network-ai 3.1.0 → 3.1.3
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 +6 -4
- package/SKILL.md +15 -5
- package/package.json +1 -1
- package/scripts/blackboard.py +40 -7
package/README.md
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
**The plug-and-play AI agent orchestrator for TypeScript/Node.js -- connect 12 agent frameworks with zero glue code**
|
|
4
4
|
|
|
5
|
-
[](https://github.com/jovanSAPFIONEER/Network-AI/releases)
|
|
6
|
+
[](https://clawhub.ai/skills/network-ai)
|
|
6
7
|
[](https://nodejs.org)
|
|
7
8
|
[](https://typescriptlang.org)
|
|
8
9
|
[](https://python.org)
|
|
@@ -122,6 +123,7 @@ Network-AI wraps your agent swarm with **file-system mutexes**, **atomic commits
|
|
|
122
123
|
### Security Module (Defense-in-Depth)
|
|
123
124
|
- **HMAC-Signed Tokens** -- Cryptographic token generation with expiration
|
|
124
125
|
- **Input Sanitization** -- XSS, injection, path traversal, and prototype pollution prevention
|
|
126
|
+
- **Blackboard Path Safety** -- Change ID sanitization prevents directory traversal in atomic commits
|
|
125
127
|
- **Rate Limiting** -- Per-agent request throttling with lockout on failed auth
|
|
126
128
|
- **AES-256-GCM Encryption** -- Encrypt sensitive blackboard entries at rest
|
|
127
129
|
- **Privilege Escalation Prevention** -- Trust-ceiling enforcement
|
|
@@ -229,10 +231,10 @@ Copy this skill into your OpenClaw workspace:
|
|
|
229
231
|
cp -r Network-AI ~/.openclaw/workspace/skills/swarm-orchestrator
|
|
230
232
|
```
|
|
231
233
|
|
|
232
|
-
Or install via ClawHub
|
|
234
|
+
Or install via ClawHub:
|
|
233
235
|
|
|
234
236
|
```bash
|
|
235
|
-
|
|
237
|
+
clawhub install network-ai
|
|
236
238
|
```
|
|
237
239
|
|
|
238
240
|
## Usage
|
|
@@ -618,6 +620,6 @@ If you're using LangGraph, Dify, Flowise, PraisonAI, AutoGen/AG2, CrewAI, or any
|
|
|
618
620
|
<details>
|
|
619
621
|
<summary>Keywords (for search)</summary>
|
|
620
622
|
|
|
621
|
-
ai-agents, agentic-ai, multi-agent, multi-agent-systems, multi-agent-system, agent-framework, ai-agent-framework, agentic-framework, agentic-workflow, llm, llm-agents, llm-agent, large-language-models, generative-ai, genai, orchestration, ai-orchestration, swarm, swarm-intelligence, autonomous-agents, agents, ai, typescript, nodejs, mcp, model-context-protocol, a2a, agent-to-agent, function-calling, tool-integration, context-engineering, rag, ai-safety, multi-agents-collaboration, multi-agents, aiagents, aiagentframework, plug-and-play, adapter-registry, blackboard-pattern, agent-coordination, agent-handoffs, token-permissions, budget-tracking, cost-awareness, atomic-commits, hallucination-detection, content-quality-gate, OpenClaw, Clawdbot, Moltbot, Clawdbot Swarm, Moltbot Security, Moltbot multi-agent, OpenClaw skills, AgentSkills, LangChain adapter, LangGraph, AutoGen adapter, AG2, CrewAI adapter, MCP adapter, LlamaIndex adapter, Semantic Kernel adapter, OpenAI Assistants adapter, Haystack adapter, DSPy adapter, Agno adapter, Phidata adapter, Dify, Flowise, PraisonAI, custom-adapter, AES-256 encryption, HMAC tokens, rate limiting, input sanitization, privilege escalation prevention, agentic-rag, deep-research, workflow-orchestration, ai-assistant, ai-tools, developer-tools, open-source
|
|
623
|
+
ai-agents, agentic-ai, multi-agent, multi-agent-systems, multi-agent-system, agent-framework, ai-agent-framework, agentic-framework, agentic-workflow, llm, llm-agents, llm-agent, large-language-models, generative-ai, genai, orchestration, ai-orchestration, swarm, swarm-intelligence, autonomous-agents, agents, ai, typescript, nodejs, mcp, model-context-protocol, a2a, agent-to-agent, function-calling, tool-integration, context-engineering, rag, ai-safety, multi-agents-collaboration, multi-agents, aiagents, aiagentframework, plug-and-play, adapter-registry, blackboard-pattern, agent-coordination, agent-handoffs, token-permissions, budget-tracking, cost-awareness, atomic-commits, hallucination-detection, content-quality-gate, OpenClaw, Clawdbot, Moltbot, Clawdbot Swarm, Moltbot Security, Moltbot multi-agent, OpenClaw skills, AgentSkills, LangChain adapter, LangGraph, AutoGen adapter, AG2, CrewAI adapter, MCP adapter, LlamaIndex adapter, Semantic Kernel adapter, OpenAI Assistants adapter, Haystack adapter, DSPy adapter, Agno adapter, Phidata adapter, Dify, Flowise, PraisonAI, custom-adapter, AES-256 encryption, HMAC tokens, rate limiting, input sanitization, privilege escalation prevention, ClawHub, clawhub, agentic-rag, deep-research, workflow-orchestration, ai-assistant, ai-tools, developer-tools, open-source
|
|
622
624
|
|
|
623
625
|
</details>
|
package/SKILL.md
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
---
|
|
2
|
-
name:
|
|
3
|
-
description: Multi-agent swarm orchestration for complex workflows.
|
|
4
|
-
metadata:
|
|
2
|
+
name: Network-AI
|
|
3
|
+
description: Multi-agent swarm orchestration for complex workflows. Coordinates multiple agents, decomposes tasks, manages shared state via a local blackboard file, and enforces permission walls before sensitive operations. All execution is local and sandboxed.
|
|
4
|
+
metadata:
|
|
5
|
+
openclaw:
|
|
6
|
+
emoji: "\U0001F41D"
|
|
7
|
+
homepage: https://github.com/jovanSAPFIONEER/Network-AI
|
|
8
|
+
requires:
|
|
9
|
+
bins:
|
|
10
|
+
- python3
|
|
5
11
|
---
|
|
6
12
|
|
|
7
13
|
# Swarm Orchestrator Skill
|
|
@@ -363,11 +369,12 @@ Sequential processing - output of one feeds into next.
|
|
|
363
369
|
|
|
364
370
|
## Security Considerations
|
|
365
371
|
|
|
366
|
-
1. **Never bypass the permission wall** for
|
|
372
|
+
1. **Never bypass the permission wall** for gated resources
|
|
367
373
|
2. **Always include justification** explaining the business need
|
|
368
374
|
3. **Use minimal scope** - request only what you need
|
|
369
375
|
4. **Check token expiry** - tokens are valid for 5 minutes
|
|
370
|
-
5. **
|
|
376
|
+
5. **Validate tokens** - use `python {baseDir}/scripts/validate_token.py TOKEN` to verify grant tokens before use
|
|
377
|
+
6. **Audit trail** - all permission requests are logged
|
|
371
378
|
|
|
372
379
|
## 📝 Audit Trail Requirements (MANDATORY)
|
|
373
380
|
|
|
@@ -439,6 +446,9 @@ with open(audit_file, "a") as f:
|
|
|
439
446
|
Expired permission tokens are automatically tracked. Run periodic cleanup:
|
|
440
447
|
|
|
441
448
|
```bash
|
|
449
|
+
# Validate a grant token
|
|
450
|
+
python {baseDir}/scripts/validate_token.py grant_a1b2c3d4e5f6
|
|
451
|
+
|
|
442
452
|
# List expired tokens (without removing)
|
|
443
453
|
python {baseDir}/scripts/revoke_token.py --list-expired
|
|
444
454
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "network-ai",
|
|
3
|
-
"version": "3.1.
|
|
3
|
+
"version": "3.1.3",
|
|
4
4
|
"description": "AI agent orchestration framework for TypeScript/Node.js - plug-and-play multi-agent coordination with 12 frameworks (LangChain, AutoGen, CrewAI, OpenAI Assistants, LlamaIndex, Semantic Kernel, Haystack, DSPy, Agno, MCP, OpenClaw). Built-in security, swarm intelligence, and agentic workflow patterns.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
package/scripts/blackboard.py
CHANGED
|
@@ -307,7 +307,38 @@ Last Updated: {datetime.now(timezone.utc).isoformat()}
|
|
|
307
307
|
# ========================================================================
|
|
308
308
|
# ATOMIC COMMIT WORKFLOW: propose → validate → commit
|
|
309
309
|
# ========================================================================
|
|
310
|
-
|
|
310
|
+
|
|
311
|
+
@staticmethod
|
|
312
|
+
def _sanitize_change_id(change_id: str) -> str:
|
|
313
|
+
"""
|
|
314
|
+
Sanitize change_id to prevent path traversal attacks.
|
|
315
|
+
Only allows alphanumeric characters, hyphens, underscores, and dots.
|
|
316
|
+
Rejects any path separators or parent directory references.
|
|
317
|
+
"""
|
|
318
|
+
if not change_id or not isinstance(change_id, str):
|
|
319
|
+
raise ValueError("change_id must be a non-empty string")
|
|
320
|
+
# Strip whitespace
|
|
321
|
+
sanitized = change_id.strip()
|
|
322
|
+
# Reject path separators and parent directory traversal
|
|
323
|
+
if any(c in sanitized for c in ('/', '\\', '..')):
|
|
324
|
+
raise ValueError(
|
|
325
|
+
f"Invalid change_id '{change_id}': must not contain path separators or '..'"
|
|
326
|
+
)
|
|
327
|
+
# Only allow safe characters: alphanumeric, hyphen, underscore, dot
|
|
328
|
+
if not re.match(r'^[a-zA-Z0-9_\-\.]+$', sanitized):
|
|
329
|
+
raise ValueError(
|
|
330
|
+
f"Invalid change_id '{change_id}': only alphanumeric, hyphen, underscore, and dot allowed"
|
|
331
|
+
)
|
|
332
|
+
return sanitized
|
|
333
|
+
|
|
334
|
+
def _safe_pending_path(self, change_id: str, suffix: str = ".pending.json") -> Path:
|
|
335
|
+
"""Build a pending-file path and verify it stays inside pending_dir."""
|
|
336
|
+
safe_id = self._sanitize_change_id(change_id)
|
|
337
|
+
target = (self.pending_dir / f"{safe_id}{suffix}").resolve()
|
|
338
|
+
if not str(target).startswith(str(self.pending_dir.resolve())):
|
|
339
|
+
raise ValueError(f"Path traversal blocked for change_id '{change_id}'")
|
|
340
|
+
return target
|
|
341
|
+
|
|
311
342
|
def propose_change(self, change_id: str, key: str, value: Any,
|
|
312
343
|
source_agent: str = "unknown", ttl: Optional[int] = None,
|
|
313
344
|
operation: str = "write") -> dict[str, Any]:
|
|
@@ -317,7 +348,7 @@ Last Updated: {datetime.now(timezone.utc).isoformat()}
|
|
|
317
348
|
The change is written to a .pending file and must be validated
|
|
318
349
|
and committed by the orchestrator before it takes effect.
|
|
319
350
|
"""
|
|
320
|
-
pending_file = self.
|
|
351
|
+
pending_file = self._safe_pending_path(change_id)
|
|
321
352
|
|
|
322
353
|
# Check for duplicate change_id
|
|
323
354
|
if pending_file.exists():
|
|
@@ -365,7 +396,7 @@ Last Updated: {datetime.now(timezone.utc).isoformat()}
|
|
|
365
396
|
- No conflicting changes to the same key
|
|
366
397
|
- Base hash matches (data hasn't changed since proposal)
|
|
367
398
|
"""
|
|
368
|
-
pending_file = self.
|
|
399
|
+
pending_file = self._safe_pending_path(change_id)
|
|
369
400
|
|
|
370
401
|
if not pending_file.exists():
|
|
371
402
|
return {
|
|
@@ -439,7 +470,7 @@ Last Updated: {datetime.now(timezone.utc).isoformat()}
|
|
|
439
470
|
"""
|
|
440
471
|
Apply a validated change atomically (Step 3 of atomic commit).
|
|
441
472
|
"""
|
|
442
|
-
pending_file = self.
|
|
473
|
+
pending_file = self._safe_pending_path(change_id)
|
|
443
474
|
|
|
444
475
|
if not pending_file.exists():
|
|
445
476
|
return {
|
|
@@ -479,9 +510,10 @@ Last Updated: {datetime.now(timezone.utc).isoformat()}
|
|
|
479
510
|
change_set["status"] = "committed"
|
|
480
511
|
change_set["committed_at"] = datetime.now(timezone.utc).isoformat()
|
|
481
512
|
|
|
513
|
+
safe_id = self._sanitize_change_id(change_id)
|
|
482
514
|
archive_dir = self.pending_dir / "archive"
|
|
483
515
|
archive_dir.mkdir(exist_ok=True)
|
|
484
|
-
archive_file = archive_dir / f"{
|
|
516
|
+
archive_file = archive_dir / f"{safe_id}.committed.json"
|
|
485
517
|
archive_file.write_text(json.dumps(change_set, indent=2))
|
|
486
518
|
|
|
487
519
|
# Remove pending file
|
|
@@ -497,7 +529,7 @@ Last Updated: {datetime.now(timezone.utc).isoformat()}
|
|
|
497
529
|
|
|
498
530
|
def abort_change(self, change_id: str) -> dict[str, Any]:
|
|
499
531
|
"""Abort a pending change without applying it."""
|
|
500
|
-
pending_file = self.
|
|
532
|
+
pending_file = self._safe_pending_path(change_id)
|
|
501
533
|
|
|
502
534
|
if not pending_file.exists():
|
|
503
535
|
return {
|
|
@@ -510,9 +542,10 @@ Last Updated: {datetime.now(timezone.utc).isoformat()}
|
|
|
510
542
|
change_set["aborted_at"] = datetime.now(timezone.utc).isoformat()
|
|
511
543
|
|
|
512
544
|
# Archive the aborted change
|
|
545
|
+
safe_id = self._sanitize_change_id(change_id)
|
|
513
546
|
archive_dir = self.pending_dir / "archive"
|
|
514
547
|
archive_dir.mkdir(exist_ok=True)
|
|
515
|
-
archive_file = archive_dir / f"{
|
|
548
|
+
archive_file = archive_dir / f"{safe_id}.aborted.json"
|
|
516
549
|
archive_file.write_text(json.dumps(change_set, indent=2))
|
|
517
550
|
|
|
518
551
|
pending_file.unlink()
|