@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 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) | Native `GuardrailProvider` or plugin | `npm i @aporthq/aport-agent-guardrails-core && npx @aporthq/aport-agent-guardrails openclaw` |
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):** Run the wizard via the Node command above, then install the Python package and follow the printed next steps. Or use the Python CLI to see the exact commands:
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 run the printed `npx` command to create passport and config, and the printed Python integration step for your framework.
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
- # Run from repo root. One run secures OpenClaw: creates passport, installs the
4
- # APort plugin (deterministic enforcement), writes config and guardrail wrappers.
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
- echo " Installing plugin from: $APORT_PLUGIN_PATH"
214
- echo " (Using -l to link local directory; OpenClaw accepts only registry or --link for install.)"
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
- echo " ⚠️ Plugin installation failed. You can install manually later:"
220
- echo " openclaw plugins install -l $APORT_PLUGIN_PATH"
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
- # For local mode: path to guardrail script
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" --arg plugin_path "$APORT_PLUGIN_PATH" '
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
- .plugins.installs["openclaw-aport"] = ((.plugins.installs["openclaw-aport"] // {})
446
- | .source = "path"
447
- | .sourcePath = $plugin_path
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 " ✅ Canonicalized plugin load path $APORT_PLUGIN_PATH"
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
- Tool name (examples) → Policy pack
594
- ------------------------------------------------
595
- git.create_pr, git.merge, git.* → code.repository.merge.v1
596
- exec.run, system.command.* → system.command.execute.v1
597
- message.send, messaging.* → messaging.message.send.v1
598
- mcp.tool.*, mcp.* mcp.tool.execute.v1
599
- agent.session.*, session.* agent.session.create.v1
600
- agent.tool.*, tool.register agent.tool.register.v1
601
- payment.refund, finance.* finance.payment.refund.v1
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 " Full list: docs/TOOL_POLICY_MAPPING.md"
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
- OpenClaw: GuardrailProvider ←── TypeScript OAPGuardrailProvider
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: "openclaw" });
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:** OpenClaw, any TypeScript framework with a `GuardrailProvider` interface.
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:** `~/.openclaw/aport/config.yaml` or `~/.aport/<framework>/config.yaml`
55
+ **Config:** `~/.aport/<framework>/config.yaml` or the framework-specific path your host expects
56
56
 
57
- **OpenClaw config.yaml:**
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: "openclaw"
64
+ framework: "your-framework"
65
65
  ```
66
66
 
67
67
  ## What the provider does