@aporthq/aport-agent-guardrails 1.0.21 → 1.0.23
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 +8 -3
- package/bin/openclaw +82 -31
- package/docs/PROVIDER.md +6 -6
- package/docs/QUICKSTART_OPENCLAW_PLUGIN.md +42 -408
- package/docs/RELEASE.md +3 -2
- package/docs/TOOL_POLICY_MAPPING.md +2 -2
- package/docs/frameworks/openclaw.md +137 -38
- package/extensions/openclaw-aport/CHANGELOG.md +14 -1
- package/extensions/openclaw-aport/MIGRATION.md +22 -375
- package/extensions/openclaw-aport/README.md +88 -350
- package/extensions/openclaw-aport/api-client.js +30 -0
- package/extensions/openclaw-aport/audit.js +32 -0
- package/extensions/openclaw-aport/decision.js +21 -0
- package/extensions/openclaw-aport/index.js +169 -592
- package/extensions/openclaw-aport/local-evaluator.js +303 -0
- package/extensions/openclaw-aport/openclaw.plugin.json +5 -5
- package/extensions/openclaw-aport/package-lock.json +2 -2
- package/extensions/openclaw-aport/package.json +27 -6
- package/extensions/openclaw-aport/tool-mapping.js +276 -0
- package/package.json +1 -1
- package/extensions/openclaw-aport/index.ts +0 -547
- package/extensions/openclaw-aport/test.js +0 -356
package/README.md
CHANGED
|
@@ -97,7 +97,7 @@ The security concern is that agent tools and skills can execute sensitive action
|
|
|
97
97
|
|
|
98
98
|
| Framework | Doc | Integration | Install |
|
|
99
99
|
|-----------|-----|--------------|--------|
|
|
100
|
-
| **OpenClaw** | [docs/frameworks/openclaw.md](docs/frameworks/openclaw.md) |
|
|
100
|
+
| **OpenClaw** | [docs/frameworks/openclaw.md](docs/frameworks/openclaw.md) | **Plugin:** `before_tool_call` via `openclaw-aport` | `npx @aporthq/aport-agent-guardrails openclaw` |
|
|
101
101
|
| **Cursor** | [docs/frameworks/cursor.md](docs/frameworks/cursor.md) | `beforeShellExecution` / `preToolUse` hooks → writes `~/.cursor/hooks.json`. **Runtime enforcement is the bash hook;** the Node package `@aporthq/aport-agent-guardrails-cursor` is a helper only (Evaluator, `getHookPath()`). | `npx @aporthq/aport-agent-guardrails cursor` |
|
|
102
102
|
| **Claude Code** | [docs/frameworks/claude-code.md](docs/frameworks/claude-code.md) | PreToolUse hook → writes `~/.claude/settings.json` (Claude Code format; not Cursor). | `npx @aporthq/aport-agent-guardrails claude-code` |
|
|
103
103
|
| **LangChain / LangGraph** | [docs/frameworks/langchain.md](docs/frameworks/langchain.md) | **Python:** `APortCallback` (`on_tool_start`) | `npx @aporthq/aport-agent-guardrails langchain` then `pip install aport-agent-guardrails-langchain` + `aport-langchain setup` |
|
|
@@ -126,13 +126,18 @@ npx @aporthq/aport-agent-guardrails
|
|
|
126
126
|
# or: npx @aporthq/aport-agent-guardrails openclaw | cursor | claude-code | langchain | crewai | deerflow | n8n
|
|
127
127
|
```
|
|
128
128
|
|
|
129
|
-
**Python (LangChain, CrewAI, or DeerFlow):**
|
|
129
|
+
**Python (LangChain, CrewAI, or DeerFlow):** Use the Python CLI directly via `uvx` or an installed package:
|
|
130
|
+
```bash
|
|
131
|
+
uvx --from aport-agent-guardrails aport setup --framework=langchain
|
|
132
|
+
# or --framework=crewai / deerflow
|
|
133
|
+
```
|
|
134
|
+
Or install the package first:
|
|
130
135
|
```bash
|
|
131
136
|
pip install aport-agent-guardrails
|
|
132
137
|
aport setup --framework=langchain
|
|
133
138
|
# or --framework=crewai / deerflow
|
|
134
139
|
```
|
|
135
|
-
Then
|
|
140
|
+
Then install the framework-specific Python package and follow the printed integration step for your framework.
|
|
136
141
|
|
|
137
142
|
This runs the **passport wizard** and writes config for your framework. Follow the **next steps** printed at the end (e.g. restart Cursor; or for CrewAI: by default install `aport-agent-guardrails-crewai` for released CrewAI, or opt into native-provider mode if your CrewAI build supports it).
|
|
138
143
|
|
package/bin/openclaw
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
2
|
# OpenClaw + APort: interactive setup
|
|
3
|
-
#
|
|
4
|
-
#
|
|
3
|
+
# Works from a repo checkout or from the published npm package via npx.
|
|
4
|
+
# One run secures OpenClaw: creates a passport or wires a hosted agent_id,
|
|
5
|
+
# installs the APort plugin, writes config, and installs guardrail wrappers.
|
|
5
6
|
# After setup, start OpenClaw with the generated config (e.g. --config ~/.openclaw/config.yaml).
|
|
6
7
|
#
|
|
7
8
|
# Usage: ./bin/openclaw [agent_id]
|
|
@@ -29,6 +30,48 @@ REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
|
29
30
|
APORT_PLUGIN_PATH="$REPO_ROOT/extensions/openclaw-aport"
|
|
30
31
|
DEFAULT_CONFIG="${OPENCLAW_HOME:-$HOME/.openclaw}"
|
|
31
32
|
|
|
33
|
+
read_local_plugin_version() {
|
|
34
|
+
if ! command -v node >/dev/null 2>&1; then
|
|
35
|
+
return 0
|
|
36
|
+
fi
|
|
37
|
+
local package_json="$APORT_PLUGIN_PATH/package.json"
|
|
38
|
+
if [ ! -f "$package_json" ]; then
|
|
39
|
+
return 0
|
|
40
|
+
fi
|
|
41
|
+
node -e '
|
|
42
|
+
const fs = require("node:fs");
|
|
43
|
+
try {
|
|
44
|
+
const pkg = JSON.parse(fs.readFileSync(process.argv[1], "utf8"));
|
|
45
|
+
if (pkg && typeof pkg.version === "string") process.stdout.write(pkg.version);
|
|
46
|
+
} catch {}
|
|
47
|
+
' "$package_json"
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
read_installed_plugin_version() {
|
|
51
|
+
if ! command -v openclaw >/dev/null 2>&1 || ! command -v node >/dev/null 2>&1; then
|
|
52
|
+
return 0
|
|
53
|
+
fi
|
|
54
|
+
openclaw plugins list --json 2>/dev/null | node -e '
|
|
55
|
+
let raw = "";
|
|
56
|
+
process.stdin.on("data", (chunk) => {
|
|
57
|
+
raw += chunk;
|
|
58
|
+
});
|
|
59
|
+
process.stdin.on("end", () => {
|
|
60
|
+
const jsonStart = raw.indexOf("{");
|
|
61
|
+
if (jsonStart < 0) return;
|
|
62
|
+
try {
|
|
63
|
+
const data = JSON.parse(raw.slice(jsonStart));
|
|
64
|
+
const plugin = Array.isArray(data.plugins)
|
|
65
|
+
? data.plugins.find((entry) => entry && entry.id === "openclaw-aport")
|
|
66
|
+
: null;
|
|
67
|
+
if (plugin && typeof plugin.version === "string") {
|
|
68
|
+
process.stdout.write(plugin.version);
|
|
69
|
+
}
|
|
70
|
+
} catch {}
|
|
71
|
+
});
|
|
72
|
+
'
|
|
73
|
+
}
|
|
74
|
+
|
|
32
75
|
# Parse command line arguments
|
|
33
76
|
HOSTED_AGENT_ID=""
|
|
34
77
|
if [ -n "$1" ]; then
|
|
@@ -209,15 +252,29 @@ if true; then
|
|
|
209
252
|
echo " Skipping plugin installation."
|
|
210
253
|
fi
|
|
211
254
|
else
|
|
255
|
+
LOCAL_PLUGIN_VERSION="$(read_local_plugin_version)"
|
|
256
|
+
INSTALLED_PLUGIN_VERSION="$(read_installed_plugin_version)"
|
|
212
257
|
echo ""
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
if openclaw plugins install -l "$APORT_PLUGIN_PATH" 2>&1; then
|
|
216
|
-
echo " ✅ Plugin installed successfully"
|
|
258
|
+
if [ -n "$LOCAL_PLUGIN_VERSION" ] && [ "$LOCAL_PLUGIN_VERSION" = "$INSTALLED_PLUGIN_VERSION" ]; then
|
|
259
|
+
echo " ✅ Plugin version $LOCAL_PLUGIN_VERSION already installed; skipping reinstall."
|
|
217
260
|
PLUGIN_INSTALLED=true
|
|
218
261
|
else
|
|
219
|
-
|
|
220
|
-
|
|
262
|
+
if [ -n "$INSTALLED_PLUGIN_VERSION" ]; then
|
|
263
|
+
echo " Existing openclaw-aport version: $INSTALLED_PLUGIN_VERSION"
|
|
264
|
+
fi
|
|
265
|
+
echo " Installing plugin from: $APORT_PLUGIN_PATH"
|
|
266
|
+
echo " (Using -l to link local directory; OpenClaw accepts only registry or --link for install.)"
|
|
267
|
+
if openclaw plugins install -l "$APORT_PLUGIN_PATH" 2>&1; then
|
|
268
|
+
echo " ✅ Plugin installed successfully"
|
|
269
|
+
PLUGIN_INSTALLED=true
|
|
270
|
+
else
|
|
271
|
+
echo " ❌ Plugin installation failed."
|
|
272
|
+
echo " OpenClaw did not accept the plugin bundle, so setup is stopping here"
|
|
273
|
+
echo " instead of writing plugin config that points at a broken install."
|
|
274
|
+
echo " Retry after fixing the bundle or install it manually:"
|
|
275
|
+
echo " openclaw plugins install -l $APORT_PLUGIN_PATH"
|
|
276
|
+
exit 1
|
|
277
|
+
fi
|
|
221
278
|
fi
|
|
222
279
|
echo ""
|
|
223
280
|
|
|
@@ -322,7 +379,7 @@ YAML
|
|
|
322
379
|
# Passport file location (in aport/ subdir)
|
|
323
380
|
passportFile: $PASSPORT_FILE
|
|
324
381
|
|
|
325
|
-
#
|
|
382
|
+
# Legacy compatibility field; current plugin versions use the built-in JS local evaluator
|
|
326
383
|
guardrailScript: $CONFIG_DIR/.skills/aport-guardrail-bash.sh
|
|
327
384
|
YAML
|
|
328
385
|
fi
|
|
@@ -433,26 +490,21 @@ YAML
|
|
|
433
490
|
'{ mode: $mode, passportFile: $pf, guardrailScript: $gs, failClosed: true, allowUnmappedTools: ($allow_unmapped == "true") } + (if $mode == "api" then { apiUrl: $api_url } else {} end)')
|
|
434
491
|
fi
|
|
435
492
|
|
|
436
|
-
if jq --argjson cfg "$CONFIG_JSON"
|
|
493
|
+
if jq --argjson cfg "$CONFIG_JSON" '
|
|
437
494
|
.plugins = (.plugins // {}) |
|
|
438
495
|
.plugins.entries = (.plugins.entries // {}) |
|
|
439
496
|
.plugins.entries["openclaw-aport"] = ((.plugins.entries["openclaw-aport"] // {}) | .enabled = true | .config = $cfg) |
|
|
440
|
-
.plugins.load = (.plugins.load // {}) |
|
|
441
|
-
.plugins.load.paths = (
|
|
442
|
-
((.plugins.load.paths // []) | map(select((type == "string" and contains("/openclaw-aport")) | not))) + [$plugin_path]
|
|
443
|
-
) |
|
|
444
497
|
.plugins.installs = (.plugins.installs // {}) |
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
| .installPath = $plugin_path)
|
|
498
|
+
del(.plugins.installs["openclaw-aport"]) |
|
|
499
|
+
.plugins.load = (.plugins.load // {}) |
|
|
500
|
+
.plugins.load.paths = ((.plugins.load.paths // []) | map(select((type == "string" and contains("/openclaw-aport")) | not)))
|
|
449
501
|
' "$OPENCLAW_JSON" > "$OPENCLAW_JSON.tmp" 2>/dev/null && { backup_file "$OPENCLAW_JSON"; mv "$OPENCLAW_JSON.tmp" "$OPENCLAW_JSON"; }; then
|
|
450
502
|
if [ "$USE_HOSTED_PASSPORT" = true ]; then
|
|
451
503
|
echo " ✅ Merged plugin config into $OPENCLAW_JSON (mode=$PLUGIN_MODE, hosted passport, allowUnmappedTools=$ALLOW_UNMAPPED)"
|
|
452
504
|
else
|
|
453
505
|
echo " ✅ Merged plugin config into $OPENCLAW_JSON (mode=$PLUGIN_MODE, allowUnmappedTools=$ALLOW_UNMAPPED)"
|
|
454
506
|
fi
|
|
455
|
-
echo " ✅
|
|
507
|
+
echo " ✅ Removed stale source-linked OpenClaw plugin paths from $OPENCLAW_JSON"
|
|
456
508
|
fi
|
|
457
509
|
fi
|
|
458
510
|
|
|
@@ -590,19 +642,18 @@ echo " OpenClaw (or your code) calls the guardrail with a tool name and contex
|
|
|
590
642
|
echo " The guardrail maps that to a policy pack in external/aport-policies:"
|
|
591
643
|
echo
|
|
592
644
|
cat << 'TABLE'
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
payment.charge → finance.payment.charge.v1
|
|
603
|
-
database.write, data.export → data.export.create.v1
|
|
645
|
+
Tool name (examples) → Policy pack
|
|
646
|
+
------------------------------------------------
|
|
647
|
+
git.create_pr, git.merge, git.* → code.repository.merge.v1
|
|
648
|
+
exec.run, system.command.* → system.command.execute.v1
|
|
649
|
+
message + send/reply/broadcast → messaging.message.send.v1
|
|
650
|
+
message + sendAttachment/react → messaging.message.send.v1
|
|
651
|
+
serverName__toolName, mcp__* → mcp.tool.execute.v1
|
|
652
|
+
read, glob, grep → data.file.read.v1
|
|
653
|
+
write, edit, multiedit → data.file.write.v1
|
|
604
654
|
TABLE
|
|
605
|
-
echo "
|
|
655
|
+
echo " Plugin mapping source: extensions/openclaw-aport/tool-mapping.js"
|
|
656
|
+
echo " Unmapped tools are allowed by default; set allowUnmappedTools: false for strict mode."
|
|
606
657
|
echo
|
|
607
658
|
|
|
608
659
|
# 5. Enforcement summary
|
package/docs/PROVIDER.md
CHANGED
|
@@ -8,7 +8,7 @@ APort ships a generic `OAPGuardrailProvider` for both Python and TypeScript. It
|
|
|
8
8
|
Framework defines interface APort implements it
|
|
9
9
|
──────────────────────── ───────────────────
|
|
10
10
|
DeerFlow: GuardrailProvider ←── Python OAPGuardrailProvider
|
|
11
|
-
|
|
11
|
+
TypeScript frameworks with a GuardrailProvider seam ←── TypeScript OAPGuardrailProvider
|
|
12
12
|
Your framework: same shape ←── Same class, same language
|
|
13
13
|
```
|
|
14
14
|
|
|
@@ -45,23 +45,23 @@ guardrails:
|
|
|
45
45
|
```typescript
|
|
46
46
|
import { OAPGuardrailProvider } from "@aporthq/aport-agent-guardrails-core";
|
|
47
47
|
|
|
48
|
-
const provider = new OAPGuardrailProvider({ framework: "
|
|
48
|
+
const provider = new OAPGuardrailProvider({ framework: "your-framework" });
|
|
49
49
|
const decision = await provider.evaluate(request); // async
|
|
50
50
|
const decision = provider.evaluateSync(request); // sync
|
|
51
51
|
```
|
|
52
52
|
|
|
53
|
-
**Supported frameworks:**
|
|
53
|
+
**Supported frameworks:** TypeScript frameworks that expose a `GuardrailProvider` interface. Current public OpenClaw uses the plugin path documented in `docs/frameworks/openclaw.md`; a native provider path would be additive when upstream support exists.
|
|
54
54
|
|
|
55
|
-
**Config:** `~/.
|
|
55
|
+
**Config:** `~/.aport/<framework>/config.yaml` or the framework-specific path your host expects
|
|
56
56
|
|
|
57
|
-
**
|
|
57
|
+
**Example TypeScript host config:**
|
|
58
58
|
```yaml
|
|
59
59
|
guardrails:
|
|
60
60
|
enabled: true
|
|
61
61
|
provider:
|
|
62
62
|
use: "@aporthq/aport-agent-guardrails-core"
|
|
63
63
|
config:
|
|
64
|
-
framework: "
|
|
64
|
+
framework: "your-framework"
|
|
65
65
|
```
|
|
66
66
|
|
|
67
67
|
## What the provider does
|