@aporthq/aport-agent-guardrails 1.0.16 → 1.0.18
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 +80 -56
- package/bin/aport-claude-code-hook.sh +12 -11
- package/bin/aport-create-passport.sh +40 -14
- package/bin/frameworks/claude-code.sh +11 -12
- package/bin/frameworks/cursor.sh +17 -13
- package/bin/lib/agentsmd.sh +108 -0
- package/bin/lib/config.sh +1 -1
- package/bin/lib/detect.sh +2 -4
- package/bin/lib/framework-setup.sh +37 -0
- package/bin/lib/validation.sh +18 -7
- package/bin/openclaw +32 -3
- package/docs/HOSTED_PASSPORT_SETUP.md +3 -3
- package/docs/IMPLEMENTING_YOUR_OWN_EVALUATOR.md +64 -0
- package/docs/OPENCLAW_COMPATIBILITY.md +1 -1
- package/docs/QUICKSTART.md +14 -14
- package/docs/QUICKSTART_OPENCLAW_PLUGIN.md +3 -3
- package/docs/README.md +1 -1
- package/docs/RELEASE.md +3 -3
- package/docs/SECURITY_MODEL.md +2 -2
- package/docs/frameworks/openclaw.md +1 -1
- package/extensions/openclaw-aport/README.md +13 -13
- package/extensions/openclaw-aport/index.js +11 -2
- package/extensions/openclaw-aport/index.ts +73 -88
- package/extensions/openclaw-aport/node_modules/@types/node/README.md +3 -3
- package/extensions/openclaw-aport/node_modules/@types/node/assert/strict.d.ts +105 -2
- package/extensions/openclaw-aport/node_modules/@types/node/assert.d.ts +232 -159
- package/extensions/openclaw-aport/node_modules/@types/node/async_hooks.d.ts +74 -57
- package/extensions/openclaw-aport/node_modules/@types/node/buffer.buffer.d.ts +18 -3
- package/extensions/openclaw-aport/node_modules/@types/node/buffer.d.ts +64 -31
- package/extensions/openclaw-aport/node_modules/@types/node/child_process.d.ts +125 -102
- package/extensions/openclaw-aport/node_modules/@types/node/cluster.d.ts +21 -21
- package/extensions/openclaw-aport/node_modules/@types/node/console.d.ts +17 -17
- package/extensions/openclaw-aport/node_modules/@types/node/crypto.d.ts +575 -534
- package/extensions/openclaw-aport/node_modules/@types/node/dgram.d.ts +45 -41
- package/extensions/openclaw-aport/node_modules/@types/node/diagnostics_channel.d.ts +63 -36
- package/extensions/openclaw-aport/node_modules/@types/node/dns/promises.d.ts +52 -26
- package/extensions/openclaw-aport/node_modules/@types/node/dns.d.ts +89 -26
- package/extensions/openclaw-aport/node_modules/@types/node/domain.d.ts +5 -5
- package/extensions/openclaw-aport/node_modules/@types/node/events.d.ts +229 -116
- package/extensions/openclaw-aport/node_modules/@types/node/fs/promises.d.ts +211 -124
- package/extensions/openclaw-aport/node_modules/@types/node/fs.d.ts +467 -338
- package/extensions/openclaw-aport/node_modules/@types/node/globals.d.ts +5 -3
- package/extensions/openclaw-aport/node_modules/@types/node/globals.typedarray.d.ts +17 -0
- package/extensions/openclaw-aport/node_modules/@types/node/http.d.ts +307 -137
- package/extensions/openclaw-aport/node_modules/@types/node/http2.d.ts +93 -29
- package/extensions/openclaw-aport/node_modules/@types/node/https.d.ts +99 -69
- package/extensions/openclaw-aport/node_modules/@types/node/index.d.ts +5 -0
- package/extensions/openclaw-aport/node_modules/@types/node/inspector.d.ts +253 -0
- package/extensions/openclaw-aport/node_modules/@types/node/inspector.generated.d.ts +1763 -486
- package/extensions/openclaw-aport/node_modules/@types/node/module.d.ts +426 -38
- package/extensions/openclaw-aport/node_modules/@types/node/net.d.ts +197 -64
- package/extensions/openclaw-aport/node_modules/@types/node/os.d.ts +52 -26
- package/extensions/openclaw-aport/node_modules/@types/node/package.json +8 -8
- package/extensions/openclaw-aport/node_modules/@types/node/path.d.ts +13 -4
- package/extensions/openclaw-aport/node_modules/@types/node/perf_hooks.d.ts +150 -42
- package/extensions/openclaw-aport/node_modules/@types/node/process.d.ts +607 -155
- package/extensions/openclaw-aport/node_modules/@types/node/punycode.d.ts +4 -4
- package/extensions/openclaw-aport/node_modules/@types/node/querystring.d.ts +24 -12
- package/extensions/openclaw-aport/node_modules/@types/node/readline/promises.d.ts +58 -51
- package/extensions/openclaw-aport/node_modules/@types/node/readline.d.ts +46 -167
- package/extensions/openclaw-aport/node_modules/@types/node/repl.d.ts +25 -27
- package/extensions/openclaw-aport/node_modules/@types/node/sea.d.ts +153 -0
- package/extensions/openclaw-aport/node_modules/@types/node/sqlite.d.ts +721 -0
- package/extensions/openclaw-aport/node_modules/@types/node/stream/consumers.d.ts +2 -2
- package/extensions/openclaw-aport/node_modules/@types/node/stream/web.d.ts +98 -3
- package/extensions/openclaw-aport/node_modules/@types/node/stream.d.ts +191 -207
- package/extensions/openclaw-aport/node_modules/@types/node/string_decoder.d.ts +5 -5
- package/extensions/openclaw-aport/node_modules/@types/node/test.d.ts +1176 -221
- package/extensions/openclaw-aport/node_modules/@types/node/timers/promises.d.ts +2 -2
- package/extensions/openclaw-aport/node_modules/@types/node/timers.d.ts +7 -6
- package/extensions/openclaw-aport/node_modules/@types/node/tls.d.ts +228 -113
- package/extensions/openclaw-aport/node_modules/@types/node/trace_events.d.ts +41 -15
- package/extensions/openclaw-aport/node_modules/@types/node/ts5.6/buffer.buffer.d.ts +17 -4
- package/extensions/openclaw-aport/node_modules/@types/node/ts5.6/globals.typedarray.d.ts +15 -0
- package/extensions/openclaw-aport/node_modules/@types/node/ts5.6/index.d.ts +5 -0
- package/extensions/openclaw-aport/node_modules/@types/node/tty.d.ts +10 -8
- package/extensions/openclaw-aport/node_modules/@types/node/url.d.ts +70 -43
- package/extensions/openclaw-aport/node_modules/@types/node/util.d.ts +689 -166
- package/extensions/openclaw-aport/node_modules/@types/node/v8.d.ts +215 -48
- package/extensions/openclaw-aport/node_modules/@types/node/vm.d.ts +433 -137
- package/extensions/openclaw-aport/node_modules/@types/node/wasi.d.ts +46 -25
- package/extensions/openclaw-aport/node_modules/@types/node/web-globals/events.d.ts +16 -0
- package/extensions/openclaw-aport/node_modules/@types/node/web-globals/fetch.d.ts +17 -0
- package/extensions/openclaw-aport/node_modules/@types/node/web-globals/navigator.d.ts +22 -0
- package/extensions/openclaw-aport/node_modules/@types/node/web-globals/storage.d.ts +24 -0
- package/extensions/openclaw-aport/node_modules/@types/node/worker_threads.d.ts +140 -54
- package/extensions/openclaw-aport/node_modules/@types/node/zlib.d.ts +296 -66
- package/extensions/openclaw-aport/openclaw.plugin.json +21 -4
- package/extensions/openclaw-aport/package.json +5 -5
- package/extensions/openclaw-aport/test.js +49 -0
- package/external/aport-spec/oap/capability-registry.md +45 -0
- package/external/aport-spec/oap/oap-spec.md +7 -0
- package/package.json +1 -2
- package/docs/DEPLOYMENT_READINESS.md +0 -81
- package/docs/frameworks/GITHUB_ISSUE_PROPOSALS.md +0 -1105
- package/extensions/openclaw-aport/node_modules/undici-types/README.md +0 -6
- package/extensions/openclaw-aport/node_modules/undici-types/agent.d.ts +0 -31
- package/extensions/openclaw-aport/node_modules/undici-types/api.d.ts +0 -43
- package/extensions/openclaw-aport/node_modules/undici-types/balanced-pool.d.ts +0 -18
- package/extensions/openclaw-aport/node_modules/undici-types/cache.d.ts +0 -36
- package/extensions/openclaw-aport/node_modules/undici-types/client.d.ts +0 -97
- package/extensions/openclaw-aport/node_modules/undici-types/connector.d.ts +0 -34
- package/extensions/openclaw-aport/node_modules/undici-types/content-type.d.ts +0 -21
- package/extensions/openclaw-aport/node_modules/undici-types/cookies.d.ts +0 -28
- package/extensions/openclaw-aport/node_modules/undici-types/diagnostics-channel.d.ts +0 -67
- package/extensions/openclaw-aport/node_modules/undici-types/dispatcher.d.ts +0 -241
- package/extensions/openclaw-aport/node_modules/undici-types/errors.d.ts +0 -128
- package/extensions/openclaw-aport/node_modules/undici-types/fetch.d.ts +0 -209
- package/extensions/openclaw-aport/node_modules/undici-types/file.d.ts +0 -39
- package/extensions/openclaw-aport/node_modules/undici-types/filereader.d.ts +0 -54
- package/extensions/openclaw-aport/node_modules/undici-types/formdata.d.ts +0 -108
- package/extensions/openclaw-aport/node_modules/undici-types/global-dispatcher.d.ts +0 -9
- package/extensions/openclaw-aport/node_modules/undici-types/global-origin.d.ts +0 -7
- package/extensions/openclaw-aport/node_modules/undici-types/handlers.d.ts +0 -9
- package/extensions/openclaw-aport/node_modules/undici-types/header.d.ts +0 -4
- package/extensions/openclaw-aport/node_modules/undici-types/index.d.ts +0 -63
- package/extensions/openclaw-aport/node_modules/undici-types/interceptors.d.ts +0 -5
- package/extensions/openclaw-aport/node_modules/undici-types/mock-agent.d.ts +0 -50
- package/extensions/openclaw-aport/node_modules/undici-types/mock-client.d.ts +0 -25
- package/extensions/openclaw-aport/node_modules/undici-types/mock-errors.d.ts +0 -12
- package/extensions/openclaw-aport/node_modules/undici-types/mock-interceptor.d.ts +0 -93
- package/extensions/openclaw-aport/node_modules/undici-types/mock-pool.d.ts +0 -25
- package/extensions/openclaw-aport/node_modules/undici-types/package.json +0 -55
- package/extensions/openclaw-aport/node_modules/undici-types/patch.d.ts +0 -71
- package/extensions/openclaw-aport/node_modules/undici-types/pool-stats.d.ts +0 -19
- package/extensions/openclaw-aport/node_modules/undici-types/pool.d.ts +0 -28
- package/extensions/openclaw-aport/node_modules/undici-types/proxy-agent.d.ts +0 -30
- package/extensions/openclaw-aport/node_modules/undici-types/readable.d.ts +0 -61
- package/extensions/openclaw-aport/node_modules/undici-types/webidl.d.ts +0 -220
- package/extensions/openclaw-aport/node_modules/undici-types/websocket.d.ts +0 -131
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<div align="center">
|
|
2
2
|
|
|
3
|
-
#
|
|
3
|
+
# <img src="https://aport.io/logo.svg" alt="APort logo" width="31" /> APort Agent Guardrails
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/@aporthq/aport-agent-guardrails)
|
|
6
6
|
[](https://pypi.org/project/aport-agent-guardrails/)
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
[](tests/)
|
|
9
9
|
[](package.json)
|
|
10
10
|
[](python/aport_guardrails/pyproject.toml)
|
|
11
|
-
[](extensions/openclaw-aport/package.json)
|
|
12
12
|
|
|
13
13
|
<p>
|
|
14
14
|
<a href="https://www.npmjs.com/package/@aporthq/aport-agent-guardrails">npm</a> •
|
|
@@ -24,69 +24,75 @@
|
|
|
24
24
|
|
|
25
25
|
---
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
Deterministic pre-action authorization for AI agents. Guardrails run before tool execution, so prompt injection cannot bypass policy checks.
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
### CTF Evidence
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
From the live APort Vault adversarial testbed:
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
| Metric | Result |
|
|
34
|
+
|---|---|
|
|
35
|
+
| Total authorization decisions observed | 4,437 |
|
|
36
|
+
| Total attack sessions observed | 1,151 |
|
|
37
|
+
| Level 5 ("Vault") restrictive attempts | 879 |
|
|
38
|
+
| Level 5 ("Vault") successful breaches | 0 |
|
|
39
|
+
| **Level 5 restrictive success rate** | **0%** |
|
|
40
|
+
| Permissive baseline success rate (for comparison) | 74.6% |
|
|
34
41
|
|
|
35
|
-
|
|
42
|
+
- [CTF Results](https://vault.aport.io/results)
|
|
43
|
+
- [CTF Replay](https://vault.aport.io/replay)
|
|
44
|
+
- [CTF Leaderboard](https://vault.aport.io/leaderboard/page)
|
|
36
45
|
|
|
37
|
-
Your agent should **only do what you explicitly allow**. APort runs in the hook—not in the prompt—so enforcement is deterministic and cannot be bypassed by prompt injection. No “trust the prompt”. The guardrail runs in the hook; the model cannot skip it.
|
|
38
46
|
|
|
47
|
+
## Start Here
|
|
39
48
|
|
|
40
|
-
|
|
41
|
-
|---|----------------|---------------------|
|
|
42
|
-
| **Enforcement** | Best-effort (prompts) | Deterministic (platform hook) |
|
|
43
|
-
| **Bypass risk** | High (prompt injection) | None |
|
|
44
|
-
| **Command control** | Agent can run anything | Allowlist + blocked patterns |
|
|
45
|
-
| **Audit** | Optional / ad hoc | Every decision logged |
|
|
49
|
+
### Install in 30 seconds
|
|
46
50
|
|
|
47
|
-
|
|
51
|
+
```bash
|
|
52
|
+
npx @aporthq/aport-agent-guardrails
|
|
53
|
+
```
|
|
48
54
|
|
|
49
|
-
|
|
55
|
+
- Choose your framework: `openclaw`, `cursor`, `claude-code`, `langchain`, `crewai`, `deerflow`, `n8n`
|
|
56
|
+
- OpenClaw direct: `npx @aporthq/aport-agent-guardrails openclaw`
|
|
57
|
+
- Hosted passport: `npx @aporthq/aport-agent-guardrails openclaw <agent_id>`
|
|
50
58
|
|
|
51
|
-
|
|
52
|
-
📋 **Passport-driven** — OAP v1.0 passports define capabilities and limits (allowed commands, message caps, etc.)
|
|
53
|
-
🔌 **OpenClaw plugin** — `before_tool_call` hook; 5-minute setup, no code changes
|
|
54
|
-
🌐 **API (default) or local** — APort API (recommended, full OAP) or bash evaluator (offline / no network). Both modes now have identical behavior (exec mapping fixed); messaging runs at assurance L0 by default.
|
|
55
|
-
🔴 **Kill switch** — Suspend the agent so all tool execution is denied: local file (local mode) or **Global Suspend** (hosted passport + API; see below).
|
|
56
|
-
⚡ **Sub-100ms (API)** — Python API verification is typically **~60–65 ms** (mean); local evaluation sub-300ms. [Benchmarks](tests/performance/README.md)
|
|
57
|
-
🔄 **Framework-agnostic** — OpenClaw, IronClaw, PicoClaw, or any runtime that can call a script or API
|
|
59
|
+
### Why Developers and teams trust APort
|
|
58
60
|
|
|
59
|
-
**
|
|
61
|
+
- **Deterministic enforcement:** runtime hook, not prompt instructions
|
|
62
|
+
- **Fail-closed defaults:** verification failures block risky actions
|
|
63
|
+
- **Auditable decisions:** each allow/deny is logged with context
|
|
64
|
+
- **Open standard artifacts:** Open Agent Passport ([OAP](https://github.com/aporthq/aport-spec)) v1.0 passport and decision formats
|
|
65
|
+
- **Research-backed outcomes:** in a live adversarial testbed, permissive-policy success was 74.6% vs 0% under restrictive OAP policy (879 top-tier attempts)
|
|
66
|
+
- **Low latency at production scale:** cloud API verification p50 ~53ms at N=1,000
|
|
67
|
+
- **Security docs:** [SECURITY.md](SECURITY.md), [SECURITY_MODEL.md](docs/SECURITY_MODEL.md)
|
|
68
|
+
- **Backed by Peer-reviewed research:** [arXiv preprint (Mar 2026)](https://arxiv.org/html/2603.20953v1)
|
|
60
69
|
|
|
61
|
-
|
|
62
|
-
|--------|----------------|
|
|
63
|
-
| **system.command.execute.v1** | Shell commands — allowlist, 50+ blocked patterns (`rm -rf`, `sudo`, `nc`, `find -exec rm`, injection); passport `allowed_paths` override for path rules |
|
|
64
|
-
| **mcp.tool.execute.v1** | MCP tool calls — server allowlist, rate limits |
|
|
65
|
-
| **messaging.message.send.v1** | Message sends — rate caps, capability checks |
|
|
66
|
-
| **agent.session.create.v1** / **agent.tool.register.v1** | Sessions and tool registration |
|
|
70
|
+
### Fast path by persona
|
|
67
71
|
|
|
68
|
-
|
|
72
|
+
- **I need OpenClaw now:** [docs/QUICKSTART_OPENCLAW_PLUGIN.md](docs/QUICKSTART_OPENCLAW_PLUGIN.md)
|
|
73
|
+
- **I already have agent_id:** [docs/HOSTED_PASSPORT_SETUP.md](docs/HOSTED_PASSPORT_SETUP.md)
|
|
74
|
+
- **I need framework setup docs:** [docs/frameworks](docs/frameworks)
|
|
75
|
+
|
|
76
|
+
### Brand personality (optional)
|
|
69
77
|
|
|
70
|
-
|
|
78
|
+
Security should feel rigorous, not intimidating. Meet Porter, the APort mascot used across the product experience: [Meet Porter](https://aport.io/brand-mascot-agent/).
|
|
71
79
|
|
|
72
|
-
|
|
80
|
+
<details>
|
|
81
|
+
<summary><strong style="font-size:18pt">Deeper background (threat model, rationale, evidence)</strong></summary>
|
|
73
82
|
|
|
74
|
-
|
|
75
|
-
|------|----------------|--------|
|
|
76
|
-
| **Local** | The **passport is the source of truth**. Set passport `status` to `suspended` (e.g. edit `passport.json` or `jq '.status = "suspended"' passport.json`). The guardrail checks passport status first and denies with `oap.passport_suspended`. Set back to `active` to resume. No separate file. | Single machine / config dir only. |
|
|
77
|
-
| **Global Suspend** | Use a **hosted passport** (agent_id) with API mode. **Log in** to [APort](https://aport.io) and suspend the passport. Per the [Open Agent Passport (OAP) spec](external/aport-spec/oap/oap-spec.md): when a passport is suspended or revoked, validators MUST treat cached decisions as invalid within **≤30 seconds** globally. | **All systems** using that passport deny within <200ms. |
|
|
83
|
+
The security concern is that agent tools and skills can execute sensitive actions (files, commands, external calls). APort addresses this by verifying each tool call against a passport and policy limits before execution. This reduces prompt-injection and “agent decided wrong” risk from runtime behavior to policy configuration.
|
|
78
84
|
|
|
79
|
-
|
|
85
|
+
</details>
|
|
80
86
|
|
|
81
87
|
---
|
|
82
88
|
|
|
83
89
|
## 🔌 Supported frameworks
|
|
84
90
|
|
|
85
|
-
**APort Agent Guardrail** adapters are available per framework; the same passport and policies apply. **Node users:** `npx @aporthq/aport-agent-guardrails` (then choose framework) or `npx @aporthq/aport-agent-guardrails <framework>`. **Python users (LangChain/CrewAI):** run the same CLI for the wizard and config, then
|
|
91
|
+
**APort Agent Guardrail** adapters are available per framework; the same passport and policies apply. **Node users:** `npx @aporthq/aport-agent-guardrails` (then choose framework) or `npx @aporthq/aport-agent-guardrails <framework>`. **Python users (LangChain/CrewAI/DeerFlow):** run the same CLI for the wizard and config, then install the framework adapter/provider package shown in the framework doc.
|
|
86
92
|
|
|
87
|
-
**Two ways to use APort:** (1) **Guardrails (CLI/setup)** — run the installer to create your passport and config; (2) **Core (library)** — use the evaluator or framework callback in your app so each tool call is verified.
|
|
93
|
+
**Two ways to use APort:** (1) **Guardrails (CLI/setup)** — run the installer to create your passport and config; (2) **Core (library)** — use the evaluator or framework callback in your app so each tool call is verified. Framework docs: [OpenClaw](docs/frameworks/openclaw.md), [Cursor](docs/frameworks/cursor.md), [Claude Code](docs/frameworks/claude-code.md), [LangChain](docs/frameworks/langchain.md), [CrewAI](docs/frameworks/crewai.md), [DeerFlow](docs/frameworks/deerflow.md), [n8n](docs/frameworks/n8n.md).
|
|
88
94
|
|
|
89
|
-
**
|
|
95
|
+
**CLI-supported frameworks:** `openclaw`, `langchain`, `crewai`, `cursor`, `claude-code`, `deerflow`, `n8n`. OpenClaw/Cursor/Claude Code include runtime-specific integration scripts; DeerFlow/LangChain/CrewAI use framework docs plus generic setup output from the CLI. See [Deployment readiness](docs/DEPLOYMENT_READINESS.md).
|
|
90
96
|
|
|
91
97
|
| Framework | Doc | Integration | Install |
|
|
92
98
|
|-----------|-----|--------------|--------|
|
|
@@ -95,13 +101,14 @@ The **kill switch suspends the agent**: once active, every tool call is denied u
|
|
|
95
101
|
| **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` |
|
|
96
102
|
| **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` |
|
|
97
103
|
| **CrewAI** | [docs/frameworks/crewai.md](docs/frameworks/crewai.md) | **Python:** `@before_tool_call` hook, `register_aport_guardrail` | `npx @aporthq/aport-agent-guardrails crewai` then `pip install aport-agent-guardrails-crewai` + `aport-crewai setup` |
|
|
104
|
+
| **DeerFlow** | [docs/frameworks/deerflow.md](docs/frameworks/deerflow.md) | **Python:** generic OAP provider wiring in DeerFlow config | `npx @aporthq/aport-agent-guardrails deerflow` then follow printed `uv`/config steps |
|
|
98
105
|
| **n8n** | [docs/frameworks/n8n.md](docs/frameworks/n8n.md) | *Coming soon* — custom node and runtime in progress | — |
|
|
99
106
|
|
|
100
107
|
Install via `npx @aporthq/aport-agent-guardrails <framework>` (or choose when prompted). OpenClaw can also use the full installer flow. **For LangChain and CrewAI, the Node CLI only runs the wizard and writes config; you must then run the printed `pip install` and setup commands to install the runtime adapter.** **Python** adapters on PyPI; **Node** adapters on npm (same version as the CLI).
|
|
101
108
|
|
|
102
109
|
**Passport path:** Each framework has its own **default** passport path (where that framework stores data): e.g. Cursor → `~/.cursor/aport/passport.json`, OpenClaw → `~/.openclaw/aport/passport.json`, LangChain → `~/.aport/langchain/aport/passport.json`. The passport wizard’s **first question** is “Passport file path [default]:” — press Enter for the framework default or type a different path. In non-interactive mode (e.g. CI) use **`--output /path/to/passport.json`** to choose the path. Roadmap: [docs/FRAMEWORK_ROADMAP.md](docs/FRAMEWORK_ROADMAP.md).
|
|
103
110
|
|
|
104
|
-
**Using SDKs or middleware directly:** If you prefer to integrate with the APort API from your own app (no CLI/framework installer), use the official SDKs and middleware: **Node** — [@aporthq/sdk-node](https://www.npmjs.com/package/@aporthq/sdk-node), [@aporthq/middleware-express](https://www.npmjs.com/package/@aporthq/middleware-express); **Python** — [aporthq-sdk-python](https://pypi.org/project/aporthq-sdk-python/), [aporthq-middleware-fastapi](https://pypi.org/project/aporthq-middleware-fastapi/).
|
|
111
|
+
**Using SDKs or middleware directly:** If you prefer to integrate with the APort API from your own app (no CLI/framework installer), use the official SDKs and middleware: **Node** — [@aporthq/sdk-node](https://www.npmjs.com/package/@aporthq/sdk-node), [@aporthq/middleware-express](https://www.npmjs.com/package/@aporthq/middleware-express); **Python** — [aporthq-sdk-python](https://pypi.org/project/aporthq-sdk-python/), [aporthq-middleware-fastapi](https://pypi.org/project/aporthq-middleware-fastapi/).
|
|
105
112
|
|
|
106
113
|
---
|
|
107
114
|
|
|
@@ -114,7 +121,7 @@ Install via `npx @aporthq/aport-agent-guardrails <framework>` (or choose when pr
|
|
|
114
121
|
**Node (Cursor, OpenClaw, or to create config for any framework):**
|
|
115
122
|
```bash
|
|
116
123
|
npx @aporthq/aport-agent-guardrails
|
|
117
|
-
# or: npx @aporthq/aport-agent-guardrails cursor |
|
|
124
|
+
# or: npx @aporthq/aport-agent-guardrails openclaw | cursor | claude-code | langchain | crewai | deerflow | n8n
|
|
118
125
|
```
|
|
119
126
|
|
|
120
127
|
**Python (LangChain or CrewAI only):** Run the wizard via the Node command above, then install the Python adapter and run the framework setup (see the printed next steps). Or use the Python CLI to see the exact commands:
|
|
@@ -151,7 +158,7 @@ aport-guardrail system.command.execute '{"command":"rm -rf /"}' # DENY (blocked
|
|
|
151
158
|
|
|
152
159
|
Your framework doc (Cursor, OpenClaw, LangChain, CrewAI) describes where the config dir is and any framework-specific status commands.
|
|
153
160
|
|
|
154
|
-
📖 **Per-framework:** [OpenClaw](docs/frameworks/openclaw.md) · [Cursor](docs/frameworks/cursor.md) · [LangChain](docs/frameworks/langchain.md) · [CrewAI](docs/frameworks/crewai.md)
|
|
161
|
+
📖 **Per-framework:** [OpenClaw](docs/frameworks/openclaw.md) · [Cursor](docs/frameworks/cursor.md) · [Claude Code](docs/frameworks/claude-code.md) · [LangChain](docs/frameworks/langchain.md) · [CrewAI](docs/frameworks/crewai.md) · [DeerFlow](docs/frameworks/deerflow.md) · [n8n](docs/frameworks/n8n.md)
|
|
155
162
|
🌐 **Hosted passport:** [Use agent_id from aport.io](docs/HOSTED_PASSPORT_SETUP.md)
|
|
156
163
|
|
|
157
164
|
---
|
|
@@ -188,20 +195,23 @@ Deep dive (what each supports, comparison table): [Verification methods](docs/VE
|
|
|
188
195
|
|
|
189
196
|
## ⚡ Performance
|
|
190
197
|
|
|
191
|
-
Guardrail verification latency
|
|
198
|
+
Guardrail verification latency from the latest preprint benchmark set (**N=1,000**).
|
|
192
199
|
|
|
193
|
-
|
|
|
194
|
-
|
|
195
|
-
| API
|
|
196
|
-
| API
|
|
197
|
-
| API
|
|
198
|
-
| API
|
|
199
|
-
| Local
|
|
200
|
+
| Mode | p50 | p95 | p99 | N |
|
|
201
|
+
|------|-----|-----|-----|---|
|
|
202
|
+
| Cloud API (agent_id, pack in path) | 53ms | 63ms | 76ms | 1,000 |
|
|
203
|
+
| Cloud API (agent_id, policy in body) | 53ms | 62ms | 77ms | 1,000 |
|
|
204
|
+
| Cloud API (passport in body, pack in path) | 54ms | 63ms | 74ms | 1,000 |
|
|
205
|
+
| Cloud API (passport in body, policy in body) | 53ms | 63ms | 71ms | 1,000 |
|
|
206
|
+
| Local policy evaluation | 174ms | 243ms | 358ms | 1,000 |
|
|
200
207
|
|
|
201
|
-
|
|
208
|
+
Source: [Before the Tool Call: Deterministic Pre-Action Authorization for Autonomous AI Agents](https://arxiv.org/html/2603.20953v1)
|
|
202
209
|
|
|
203
210
|
---
|
|
204
211
|
|
|
212
|
+
<details>
|
|
213
|
+
<summary><strong>📐 How It Works (expand)</strong></summary>
|
|
214
|
+
|
|
205
215
|
## 📐 How It Works
|
|
206
216
|
|
|
207
217
|
<div align="center">
|
|
@@ -284,6 +294,11 @@ User → "Delete all log files"
|
|
|
284
294
|
|
|
285
295
|
---
|
|
286
296
|
|
|
297
|
+
</details>
|
|
298
|
+
|
|
299
|
+
<details>
|
|
300
|
+
<summary><strong>🏛️ Security model (three layers) (expand)</strong></summary>
|
|
301
|
+
|
|
287
302
|
## 🏛️ Security model (three layers)
|
|
288
303
|
|
|
289
304
|
APort enforces **identity → authorization → audit** before any tool runs. This repo implements the **plugin (Option 2)** integration: OpenClaw calls the APort extension in `before_tool_call`; the extension uses either local script or API to evaluate policy.
|
|
@@ -346,6 +361,8 @@ graph TB
|
|
|
346
361
|
|
|
347
362
|
---
|
|
348
363
|
|
|
364
|
+
</details>
|
|
365
|
+
|
|
349
366
|
## 🌐 When to use API vs local
|
|
350
367
|
|
|
351
368
|
| Use **local** when | Use **API** (default) when |
|
|
@@ -375,7 +392,7 @@ See [Verification methods](docs/VERIFICATION_METHODS.md) for a detailed comparis
|
|
|
375
392
|
|
|
376
393
|
| Command | Purpose |
|
|
377
394
|
|--------|---------|
|
|
378
|
-
| `agent-guardrails` | Main entry — prompt for framework or pass one: `agent-guardrails cursor \|
|
|
395
|
+
| `agent-guardrails` | Main entry — prompt for framework or pass one: `agent-guardrails openclaw \| cursor \| claude-code \| langchain \| crewai \| deerflow \| n8n`. Args after the framework are passed through (e.g. `agent-guardrails openclaw <agent_id>`). |
|
|
379
396
|
| `aport` | OpenClaw one-command setup (passport + plugin + wrappers). Optional: `aport <agent_id>` for hosted passport. |
|
|
380
397
|
| `aport-guardrail` | Run guardrail check from the CLI (e.g. `aport-guardrail system.command.execute '{"command":"ls"}'`). Uses passport from your framework config dir. |
|
|
381
398
|
|
|
@@ -399,7 +416,7 @@ Use the framework-specific doc for where config and passport live and for any ex
|
|
|
399
416
|
| Doc | Description |
|
|
400
417
|
|-----|-------------|
|
|
401
418
|
| [QuickStart: OpenClaw Plugin](docs/QUICKSTART_OPENCLAW_PLUGIN.md) | 5-minute OpenClaw setup |
|
|
402
|
-
| [Hosted passport setup](docs/HOSTED_PASSPORT_SETUP.md) | Use passport from aport.io — `npx ... <agent_id>` or choose hosted in wizard |
|
|
419
|
+
| [Hosted passport setup](docs/HOSTED_PASSPORT_SETUP.md) | Use passport from aport.io — `npx ... openclaw <agent_id>` or choose hosted in wizard |
|
|
403
420
|
| [Verification methods (local vs API)](docs/VERIFICATION_METHODS.md) | Deep dive: bash vs API evaluator |
|
|
404
421
|
| [Quick Start Guide](docs/QUICKSTART.md) | Passport wizard, copy-paste option |
|
|
405
422
|
| [OpenClaw Local Integration](docs/OPENCLAW_LOCAL_INTEGRATION.md) | API, Python example |
|
|
@@ -409,13 +426,18 @@ Use the framework-specific doc for where config and passport live and for any ex
|
|
|
409
426
|
| **Frameworks** | Per-framework setup and how guardrails run |
|
|
410
427
|
| → [OpenClaw](docs/frameworks/openclaw.md) | `before_tool_call` plugin |
|
|
411
428
|
| → [Cursor](docs/frameworks/cursor.md) | beforeShellExecution / preToolUse hooks, `~/.cursor/hooks.json` |
|
|
429
|
+
| → [Claude Code](docs/frameworks/claude-code.md) | PreToolUse hook, `~/.claude/settings.json` |
|
|
412
430
|
| → [LangChain / LangGraph](docs/frameworks/langchain.md) | `APortCallback` handler |
|
|
413
431
|
| → [CrewAI](docs/frameworks/crewai.md) | `@before_tool_call` hook, `register_aport_guardrail` |
|
|
432
|
+
| → [DeerFlow](docs/frameworks/deerflow.md) | Generic provider wiring via DeerFlow `config.yaml` |
|
|
414
433
|
| → [n8n](docs/frameworks/n8n.md) | Custom node, branch on allow/deny |
|
|
415
|
-
| [Framework
|
|
434
|
+
| [Framework roadmap](docs/FRAMEWORK_ROADMAP.md) | Support status and roadmap |
|
|
416
435
|
|
|
417
436
|
---
|
|
418
437
|
|
|
438
|
+
<details>
|
|
439
|
+
<summary><strong>🏗️ Architecture (expand)</strong></summary>
|
|
440
|
+
|
|
419
441
|
## 🏗️ Architecture
|
|
420
442
|
|
|
421
443
|
<div align="center">
|
|
@@ -471,6 +493,8 @@ Defense in depth: policy *before* execution, runtime safety *during* execution.
|
|
|
471
493
|
|
|
472
494
|
---
|
|
473
495
|
|
|
496
|
+
</details>
|
|
497
|
+
|
|
474
498
|
## 🤝 Contributing
|
|
475
499
|
|
|
476
500
|
Contributions welcome: policy packs, framework adapters, docs. See [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
@@ -39,6 +39,7 @@ set -e
|
|
|
39
39
|
if [ "$JQ_EXIT" -ne 0 ] || [ -z "$TOOL_NAME" ]; then
|
|
40
40
|
TOOL_NAME="unknown"
|
|
41
41
|
fi
|
|
42
|
+
TOOL_NAME_NORM="$(printf '%s' "$TOOL_NAME" | tr -d '[:space:]' | sed 's/^functions\.//' | tr '[:upper:]' '[:lower:]')"
|
|
42
43
|
set +e
|
|
43
44
|
TOOL_INPUT="$(echo "$INPUT" | jq -c '.tool_input // {}' 2> /dev/null)"
|
|
44
45
|
JQ_EXIT=$?
|
|
@@ -69,44 +70,44 @@ deny() {
|
|
|
69
70
|
GUARDRAIL_TOOL=""
|
|
70
71
|
CONTEXT_JSON="{}"
|
|
71
72
|
|
|
72
|
-
case "$
|
|
73
|
-
|
|
73
|
+
case "$TOOL_NAME_NORM" in
|
|
74
|
+
bash | shell)
|
|
74
75
|
GUARDRAIL_TOOL="bash"
|
|
75
76
|
CONTEXT_JSON="$(safe_jq "$TOOL_INPUT" '{command: (.command // "")}')"
|
|
76
77
|
;;
|
|
77
|
-
|
|
78
|
+
read | glob | ls | grep | todoread | toolsearch | askuserquestion | readfile | semanticsearch)
|
|
78
79
|
# Read-family + user-interaction tools: allow without calling evaluator
|
|
79
80
|
exit 0
|
|
80
81
|
;;
|
|
81
|
-
|
|
82
|
+
taskget | tasklist | taskoutput | cronlist)
|
|
82
83
|
# Read-only task/cron queries: allow without evaluator
|
|
83
84
|
exit 0
|
|
84
85
|
;;
|
|
85
|
-
|
|
86
|
+
enterplanmode | exitplanmode)
|
|
86
87
|
# Internal state transitions: allow without evaluator
|
|
87
88
|
exit 0
|
|
88
89
|
;;
|
|
89
|
-
|
|
90
|
+
write | edit | multiedit | notebookedit | todowrite | delete | strreplace | editnotebook)
|
|
90
91
|
GUARDRAIL_TOOL="write"
|
|
91
92
|
CONTEXT_JSON="$(safe_jq "$TOOL_INPUT" '{file_path: (.file_path // .path // "")}')"
|
|
92
93
|
;;
|
|
93
|
-
|
|
94
|
+
websearch | webfetch)
|
|
94
95
|
GUARDRAIL_TOOL="webfetch"
|
|
95
96
|
CONTEXT_JSON="$(safe_jq "$TOOL_INPUT" '{url: (.url // .query // "")}')"
|
|
96
97
|
;;
|
|
97
|
-
|
|
98
|
+
browser)
|
|
98
99
|
GUARDRAIL_TOOL="browser"
|
|
99
100
|
CONTEXT_JSON="$(safe_jq "$TOOL_INPUT" '{url: (.url // "")}')"
|
|
100
101
|
;;
|
|
101
|
-
|
|
102
|
+
task | taskcreate | taskupdate | taskstop | agent | skill | enterworktree | subagent | subagentstart)
|
|
102
103
|
GUARDRAIL_TOOL="session.create"
|
|
103
104
|
CONTEXT_JSON="$(safe_jq "$TOOL_INPUT" '{description: (.description // .prompt // "")}')"
|
|
104
105
|
;;
|
|
105
|
-
|
|
106
|
+
croncreate | crondelete)
|
|
106
107
|
GUARDRAIL_TOOL="cron"
|
|
107
108
|
CONTEXT_JSON="$(safe_jq "$TOOL_INPUT" '{description: (.description // .schedule // "")}')"
|
|
108
109
|
;;
|
|
109
|
-
mcp__*)
|
|
110
|
+
mcp__* | mcp:* | callmcptool)
|
|
110
111
|
GUARDRAIL_TOOL="mcp.tool"
|
|
111
112
|
CONTEXT_JSON="$TOOL_INPUT"
|
|
112
113
|
;;
|
|
@@ -104,15 +104,15 @@ get_identity_description() {
|
|
|
104
104
|
DEFAULT_EMAIL=$(get_default_email) || true
|
|
105
105
|
DEFAULT_EMAIL=${DEFAULT_EMAIL:-"user@example.com"}
|
|
106
106
|
DEFAULT_OWNER_TYPE="user"
|
|
107
|
+
# Agent name/description: try OpenClaw IDENTITY.md first; overridden per framework below
|
|
107
108
|
DEFAULT_AGENT_NAME=$(get_identity_name) || true
|
|
108
|
-
DEFAULT_AGENT_NAME=${DEFAULT_AGENT_NAME:-"OpenClaw Agent"}
|
|
109
109
|
DEFAULT_AGENT_DESC=$(get_identity_description) || true
|
|
110
|
-
DEFAULT_AGENT_DESC=${DEFAULT_AGENT_DESC:-"Local OpenClaw AI agent with APort guardrails"}
|
|
111
110
|
|
|
112
|
-
# Framework-aware defaults:
|
|
111
|
+
# Framework-aware defaults: capabilities and agent identity vary by framework
|
|
113
112
|
case "${APORT_FRAMEWORK:-}" in
|
|
114
113
|
claude-code)
|
|
115
|
-
|
|
114
|
+
DEFAULT_AGENT_NAME=${DEFAULT_AGENT_NAME:-"Claude Code Agent"}
|
|
115
|
+
DEFAULT_AGENT_DESC=${DEFAULT_AGENT_DESC:-"Claude Code AI agent with APort guardrails"}
|
|
116
116
|
DEFAULT_FILE_READ=y
|
|
117
117
|
DEFAULT_FILE_WRITE=y
|
|
118
118
|
DEFAULT_WEB_FETCH=y
|
|
@@ -121,7 +121,8 @@ case "${APORT_FRAMEWORK:-}" in
|
|
|
121
121
|
DEFAULT_MCP_TOOL=y
|
|
122
122
|
;;
|
|
123
123
|
cursor)
|
|
124
|
-
|
|
124
|
+
DEFAULT_AGENT_NAME=${DEFAULT_AGENT_NAME:-"Cursor Agent"}
|
|
125
|
+
DEFAULT_AGENT_DESC=${DEFAULT_AGENT_DESC:-"Cursor IDE AI agent with APort guardrails"}
|
|
125
126
|
DEFAULT_FILE_READ=y
|
|
126
127
|
DEFAULT_FILE_WRITE=y
|
|
127
128
|
DEFAULT_WEB_FETCH=y
|
|
@@ -130,7 +131,8 @@ case "${APORT_FRAMEWORK:-}" in
|
|
|
130
131
|
DEFAULT_MCP_TOOL=n
|
|
131
132
|
;;
|
|
132
133
|
crewai)
|
|
133
|
-
|
|
134
|
+
DEFAULT_AGENT_NAME=${DEFAULT_AGENT_NAME:-"CrewAI Agent"}
|
|
135
|
+
DEFAULT_AGENT_DESC=${DEFAULT_AGENT_DESC:-"CrewAI multi-agent system with APort guardrails"}
|
|
134
136
|
DEFAULT_FILE_READ=y
|
|
135
137
|
DEFAULT_FILE_WRITE=y
|
|
136
138
|
DEFAULT_WEB_FETCH=y
|
|
@@ -139,7 +141,8 @@ case "${APORT_FRAMEWORK:-}" in
|
|
|
139
141
|
DEFAULT_MCP_TOOL=y
|
|
140
142
|
;;
|
|
141
143
|
langchain)
|
|
142
|
-
|
|
144
|
+
DEFAULT_AGENT_NAME=${DEFAULT_AGENT_NAME:-"LangChain Agent"}
|
|
145
|
+
DEFAULT_AGENT_DESC=${DEFAULT_AGENT_DESC:-"LangChain/LangGraph AI agent with APort guardrails"}
|
|
143
146
|
DEFAULT_FILE_READ=y
|
|
144
147
|
DEFAULT_FILE_WRITE=y
|
|
145
148
|
DEFAULT_WEB_FETCH=y
|
|
@@ -148,7 +151,8 @@ case "${APORT_FRAMEWORK:-}" in
|
|
|
148
151
|
DEFAULT_MCP_TOOL=n
|
|
149
152
|
;;
|
|
150
153
|
n8n)
|
|
151
|
-
|
|
154
|
+
DEFAULT_AGENT_NAME=${DEFAULT_AGENT_NAME:-"n8n Agent"}
|
|
155
|
+
DEFAULT_AGENT_DESC=${DEFAULT_AGENT_DESC:-"n8n workflow agent with APort guardrails"}
|
|
152
156
|
DEFAULT_FILE_READ=y
|
|
153
157
|
DEFAULT_FILE_WRITE=y
|
|
154
158
|
DEFAULT_WEB_FETCH=y
|
|
@@ -156,8 +160,29 @@ case "${APORT_FRAMEWORK:-}" in
|
|
|
156
160
|
DEFAULT_AGENT_SESSION=n
|
|
157
161
|
DEFAULT_MCP_TOOL=n
|
|
158
162
|
;;
|
|
163
|
+
deerflow)
|
|
164
|
+
DEFAULT_AGENT_NAME=${DEFAULT_AGENT_NAME:-"DeerFlow Agent"}
|
|
165
|
+
DEFAULT_AGENT_DESC=${DEFAULT_AGENT_DESC:-"DeerFlow LangGraph agent with APort guardrails"}
|
|
166
|
+
DEFAULT_FILE_READ=y
|
|
167
|
+
DEFAULT_FILE_WRITE=y
|
|
168
|
+
DEFAULT_WEB_FETCH=y
|
|
169
|
+
DEFAULT_WEB_BROWSER=n
|
|
170
|
+
DEFAULT_AGENT_SESSION=y
|
|
171
|
+
DEFAULT_MCP_TOOL=y
|
|
172
|
+
;;
|
|
173
|
+
openclaw)
|
|
174
|
+
DEFAULT_AGENT_NAME=${DEFAULT_AGENT_NAME:-"OpenClaw Agent"}
|
|
175
|
+
DEFAULT_AGENT_DESC=${DEFAULT_AGENT_DESC:-"Local OpenClaw AI agent with APort guardrails"}
|
|
176
|
+
DEFAULT_FILE_READ=y
|
|
177
|
+
DEFAULT_FILE_WRITE=y
|
|
178
|
+
DEFAULT_WEB_FETCH=y
|
|
179
|
+
DEFAULT_WEB_BROWSER=n
|
|
180
|
+
DEFAULT_AGENT_SESSION=y
|
|
181
|
+
DEFAULT_MCP_TOOL=n
|
|
182
|
+
;;
|
|
159
183
|
*)
|
|
160
|
-
|
|
184
|
+
DEFAULT_AGENT_NAME=${DEFAULT_AGENT_NAME:-"APort Agent"}
|
|
185
|
+
DEFAULT_AGENT_DESC=${DEFAULT_AGENT_DESC:-"AI agent with APort guardrails"}
|
|
161
186
|
DEFAULT_FILE_READ=n
|
|
162
187
|
DEFAULT_FILE_WRITE=n
|
|
163
188
|
DEFAULT_WEB_FETCH=n
|
|
@@ -255,11 +280,12 @@ else
|
|
|
255
280
|
# Choose capabilities
|
|
256
281
|
echo " 🔐 Capabilities"
|
|
257
282
|
echo " ───────────────"
|
|
258
|
-
|
|
259
|
-
echo " Choose what your agent can do (y/n). Claude Code defaults: most capabilities = yes."
|
|
260
|
-
|
|
261
|
-
echo " Choose what your agent can do (y/n).
|
|
262
|
-
|
|
283
|
+
case "${APORT_FRAMEWORK:-}" in
|
|
284
|
+
claude-code) echo " Choose what your agent can do (y/n). Claude Code defaults: most capabilities = yes." ;;
|
|
285
|
+
cursor) echo " Choose what your agent can do (y/n). Cursor defaults: file ops, exec, web, sub-agents = yes." ;;
|
|
286
|
+
openclaw) echo " Choose what your agent can do (y/n). OpenClaw defaults: PRs, exec, messaging, file ops = yes." ;;
|
|
287
|
+
*) echo " Choose what your agent can do (y/n). Defaults: PRs, exec, and messaging = yes; others vary by framework." ;;
|
|
288
|
+
esac
|
|
263
289
|
echo ""
|
|
264
290
|
read -p " • Create and merge pull requests? [Y/n]: " pr_cap
|
|
265
291
|
pr_cap=${pr_cap:-y}
|
|
@@ -10,26 +10,25 @@ source "$LIB/common.sh"
|
|
|
10
10
|
source "$LIB/passport.sh"
|
|
11
11
|
# shellcheck source=../lib/config.sh
|
|
12
12
|
source "$LIB/config.sh"
|
|
13
|
+
# shellcheck source=../lib/framework-setup.sh
|
|
14
|
+
source "$LIB/framework-setup.sh"
|
|
13
15
|
|
|
14
16
|
run_setup() {
|
|
15
17
|
log_info "Setting up APort guardrails for Claude Code..."
|
|
16
|
-
config_dir="$(
|
|
17
|
-
config_dir="${config_dir/#\~/$HOME}"
|
|
18
|
-
mkdir -p "$config_dir/aport"
|
|
19
|
-
chmod 700 "$config_dir/aport"
|
|
18
|
+
config_dir="$(ensure_aport_dir_secure claude-code)"
|
|
20
19
|
|
|
21
20
|
export APORT_FRAMEWORK=claude-code
|
|
22
|
-
|
|
21
|
+
|
|
22
|
+
# Check AGENTS.md for enforcement config — skip wizard if already configured
|
|
23
|
+
# shellcheck source=../lib/agentsmd.sh
|
|
24
|
+
source "$LIB/agentsmd.sh"
|
|
25
|
+
setup_from_agentsmd_or_wizard "$@"
|
|
23
26
|
|
|
24
27
|
# Harden permissions on passport (contains policy/capabilities)
|
|
25
28
|
[ -f "$config_dir/aport/passport.json" ] && chmod 600 "$config_dir/aport/passport.json"
|
|
26
29
|
|
|
27
30
|
# Resolve absolute path to hook script (works from repo or npx package)
|
|
28
|
-
HOOK_SCRIPT="${APORT_CLAUDE_CODE_HOOK_SCRIPT:-}"
|
|
29
|
-
if [ -z "$HOOK_SCRIPT" ]; then
|
|
30
|
-
ROOT_FOR_HOOK="$(cd "$LIB/../.." && pwd)"
|
|
31
|
-
HOOK_SCRIPT="$ROOT_FOR_HOOK/bin/aport-claude-code-hook.sh"
|
|
32
|
-
fi
|
|
31
|
+
HOOK_SCRIPT="$(resolve_hook_script_path "${APORT_CLAUDE_CODE_HOOK_SCRIPT:-}" "aport-claude-code-hook.sh" "$LIB")"
|
|
33
32
|
if [ ! -f "$HOOK_SCRIPT" ]; then
|
|
34
33
|
log_warn "Hook script not found at $HOOK_SCRIPT; settings.json will reference it (create the file for hooks to work)."
|
|
35
34
|
else
|
|
@@ -67,13 +66,13 @@ _write_claude_settings() {
|
|
|
67
66
|
|
|
68
67
|
if [ -f "$file" ] && command -v jq &> /dev/null; then
|
|
69
68
|
if jq -e '.hooks' "$file" &> /dev/null; then
|
|
70
|
-
# Merge:
|
|
69
|
+
# Merge: replace stale APort hook entries (old npx paths), preserve non-APort hooks
|
|
71
70
|
local tmpfile
|
|
72
71
|
tmpfile="$(mktemp "${file}.XXXXXX")"
|
|
73
72
|
jq -c --arg cmd "$cmd" '
|
|
74
73
|
(.hooks.PreToolUse // []) as $p |
|
|
75
74
|
.hooks.PreToolUse = ($p | map(select(
|
|
76
|
-
(.hooks[0].command != $cmd)
|
|
75
|
+
((.hooks[0].command != $cmd) and (((.hooks[0].command // "") | test("aport-(cursor-hook|claude-code-hook)\\.sh$")) | not))
|
|
77
76
|
)) | . + [{"matcher":"*","hooks":[{"type":"command","command":$cmd}]}])
|
|
78
77
|
' "$file" > "$tmpfile" && mv "$tmpfile" "$file"
|
|
79
78
|
return
|
package/bin/frameworks/cursor.sh
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
|
-
# Cursor
|
|
2
|
+
# Cursor framework installer/setup.
|
|
3
3
|
# Runs passport wizard and writes ~/.cursor/hooks.json pointing at the APort hook script.
|
|
4
|
-
# Same hook script works for Cursor
|
|
4
|
+
# Same hook script works for Cursor and VS Code/Copilot-style hook payloads.
|
|
5
5
|
|
|
6
6
|
LIB="$(cd "$(dirname "${BASH_SOURCE[0]:-.}")/../lib" && pwd)"
|
|
7
7
|
# shellcheck source=../lib/common.sh
|
|
@@ -10,26 +10,26 @@ source "$LIB/common.sh"
|
|
|
10
10
|
source "$LIB/passport.sh"
|
|
11
11
|
# shellcheck source=../lib/config.sh
|
|
12
12
|
source "$LIB/config.sh"
|
|
13
|
+
# shellcheck source=../lib/framework-setup.sh
|
|
14
|
+
source "$LIB/framework-setup.sh"
|
|
13
15
|
|
|
14
16
|
run_setup() {
|
|
15
17
|
log_info "Setting up APort guardrails for Cursor..."
|
|
16
18
|
# Passport and data live under Cursor's config dir (~/.cursor/aport/ by default).
|
|
17
|
-
config_dir="$(
|
|
18
|
-
mkdir -p "$config_dir/aport"
|
|
19
|
-
chmod 700 "$config_dir/aport"
|
|
19
|
+
config_dir="$(ensure_aport_dir_secure cursor)"
|
|
20
20
|
|
|
21
21
|
export APORT_FRAMEWORK=cursor
|
|
22
|
-
|
|
22
|
+
|
|
23
|
+
# Check AGENTS.md for enforcement config — skip wizard if already configured
|
|
24
|
+
# shellcheck source=../lib/agentsmd.sh
|
|
25
|
+
source "$LIB/agentsmd.sh"
|
|
26
|
+
setup_from_agentsmd_or_wizard "$@"
|
|
23
27
|
|
|
24
28
|
# Harden permissions on passport (contains policy/capabilities)
|
|
25
29
|
[ -f "$config_dir/aport/passport.json" ] && chmod 600 "$config_dir/aport/passport.json"
|
|
26
30
|
|
|
27
31
|
# Resolve absolute path to hook script (works from repo or npx package)
|
|
28
|
-
HOOK_SCRIPT="${APORT_CURSOR_HOOK_SCRIPT:-}"
|
|
29
|
-
if [ -z "$HOOK_SCRIPT" ]; then
|
|
30
|
-
ROOT_FOR_HOOK="$(cd "$LIB/../.." && pwd)"
|
|
31
|
-
HOOK_SCRIPT="$ROOT_FOR_HOOK/bin/aport-cursor-hook.sh"
|
|
32
|
-
fi
|
|
32
|
+
HOOK_SCRIPT="$(resolve_hook_script_path "${APORT_CURSOR_HOOK_SCRIPT:-}" "aport-cursor-hook.sh" "$LIB")"
|
|
33
33
|
if [ ! -f "$HOOK_SCRIPT" ]; then
|
|
34
34
|
log_warn "Hook script not found at $HOOK_SCRIPT; hooks.json will reference it (create the file for hooks to work)."
|
|
35
35
|
else
|
|
@@ -45,9 +45,13 @@ run_setup() {
|
|
|
45
45
|
if [ -f "$CURSOR_HOOKS_FILE" ] && command -v jq &> /dev/null; then
|
|
46
46
|
EXISTING=$(cat "$CURSOR_HOOKS_FILE")
|
|
47
47
|
if echo "$EXISTING" | jq -e '.hooks' &> /dev/null; then
|
|
48
|
-
# Add APort hook to all supported lifecycle events
|
|
48
|
+
# Add APort hook to all supported lifecycle events.
|
|
49
|
+
# Replace stale APort entries (old npx paths), preserve non-APort hooks.
|
|
49
50
|
NEW_HOOKS=$(echo "$EXISTING" | jq -c --arg cmd "$HOOK_SCRIPT" '
|
|
50
|
-
def
|
|
51
|
+
def is_aport_cmd:
|
|
52
|
+
(.command? // "") | test("aport-(cursor-hook|claude-code-hook)\\.sh$");
|
|
53
|
+
def upsert_hook:
|
|
54
|
+
map(select((.command != $cmd) and (is_aport_cmd | not))) | . + [{ "command": $cmd }];
|
|
51
55
|
.hooks.beforeShellExecution = ((.hooks.beforeShellExecution // []) | upsert_hook) |
|
|
52
56
|
.hooks.preToolUse = ((.hooks.preToolUse // []) | upsert_hook) |
|
|
53
57
|
.hooks.beforeMCPExecution = ((.hooks.beforeMCPExecution // []) | upsert_hook) |
|