@aporthq/aport-agent-guardrails 1.0.27 → 1.0.29
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 +32 -7
- package/bin/aport-claude-code-hook.sh +8 -2
- package/bin/aport-create-passport.sh +3 -2
- package/bin/aport-cursor-hook.sh +9 -3
- package/bin/aport-guardrail-api.sh +2 -0
- package/bin/aport-resolve-paths.sh +67 -40
- package/bin/frameworks/claude-code.sh +11 -1
- package/bin/frameworks/cursor.sh +6 -1
- package/bin/frameworks/generic.sh +14 -1
- package/bin/lib/framework-hook-paths.sh +96 -0
- package/bin/lib/guardrail-mode.sh +53 -4
- package/bin/lib/quick-hosted.sh +164 -0
- package/bin/openclaw +79 -48
- package/docs/ENTERPRISE_DEVICE_DEPLOYMENT.md +153 -0
- package/docs/HOSTED_PASSPORT_SETUP.md +29 -4
- package/docs/QUICKSTART.md +9 -3
- package/docs/README.md +4 -3
- package/docs/RELEASE.md +1 -1
- package/docs/VERIFICATION_METHODS.md +1 -1
- package/docs/frameworks/claude-code.md +25 -2
- package/docs/frameworks/cursor.md +4 -4
- package/extensions/openclaw-aport/CHANGELOG.md +4 -0
- package/extensions/openclaw-aport/openclaw.plugin.json +1 -1
- package/extensions/openclaw-aport/package-lock.json +2 -2
- package/extensions/openclaw-aport/package.json +1 -1
- package/package.json +1 -1
- package/src/evaluator.js +8 -2
package/README.md
CHANGED
|
@@ -46,17 +46,41 @@ From the live APort Vault adversarial testbed:
|
|
|
46
46
|
|
|
47
47
|
## Start Here
|
|
48
48
|
|
|
49
|
-
### Install in
|
|
49
|
+
### Install in 60 seconds
|
|
50
50
|
|
|
51
51
|
```bash
|
|
52
52
|
npx @aporthq/aport-agent-guardrails
|
|
53
53
|
```
|
|
54
54
|
|
|
55
55
|
- Choose your framework: `openclaw`, `cursor`, `claude-code`, `langchain`, `crewai`, `deerflow`, `n8n`
|
|
56
|
-
-
|
|
57
|
-
-
|
|
56
|
+
- Claude Code direct: `npx @aporthq/aport-agent-guardrails claude-code`
|
|
57
|
+
- Curl install URL: `curl -fsSL https://aport.io/install.sh | bash -s -- claude-code`
|
|
58
|
+
- Existing hosted passport: `npx @aporthq/aport-agent-guardrails claude-code <agent_id>`
|
|
58
59
|
- Reset a framework to a clean APort state: `npx @aporthq/aport-agent-guardrails reset claude-code --yes`
|
|
59
60
|
|
|
61
|
+
When prompted for passport setup, the choices are:
|
|
62
|
+
|
|
63
|
+
1. `Create hosted APort passport now` — recommended; creates a hosted passport and narrow setup key.
|
|
64
|
+
2. `Use existing hosted passport ID` — paste an existing `agent_id`.
|
|
65
|
+
3. `Create local passport file` — offline/local JSON passport.
|
|
66
|
+
|
|
67
|
+
For a new hosted setup, choose option `1`. The installer creates a passport, creates a narrow setup key, writes the framework hook/config, and starts sending decisions to APort. For non-interactive installs:
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
npx --yes @aporthq/aport-agent-guardrails claude-code \
|
|
71
|
+
--quick-hosted \
|
|
72
|
+
--email you@example.com \
|
|
73
|
+
--non-interactive
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Equivalent environment-variable form:
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
APORT_OWNER_EMAIL="you@example.com" \
|
|
80
|
+
APORT_QUICK_HOSTED=1 \
|
|
81
|
+
npx --yes @aporthq/aport-agent-guardrails claude-code --non-interactive
|
|
82
|
+
```
|
|
83
|
+
|
|
60
84
|
### Why Developers and teams trust APort
|
|
61
85
|
|
|
62
86
|
- **Deterministic enforcement:** runtime hook, not prompt instructions
|
|
@@ -72,6 +96,7 @@ npx @aporthq/aport-agent-guardrails
|
|
|
72
96
|
|
|
73
97
|
- **I need OpenClaw now:** [docs/QUICKSTART_OPENCLAW_PLUGIN.md](docs/QUICKSTART_OPENCLAW_PLUGIN.md)
|
|
74
98
|
- **I already have agent_id:** [docs/HOSTED_PASSPORT_SETUP.md](docs/HOSTED_PASSPORT_SETUP.md)
|
|
99
|
+
- **I’m deploying for an IT team:** [docs/ENTERPRISE_DEVICE_DEPLOYMENT.md](docs/ENTERPRISE_DEVICE_DEPLOYMENT.md)
|
|
75
100
|
- **I need framework setup docs:** [docs/frameworks](docs/frameworks)
|
|
76
101
|
- **I want Claude marketplace install:** [docs/frameworks/claude-code.md](docs/frameworks/claude-code.md#marketplace-install-claude-plugins)
|
|
77
102
|
|
|
@@ -94,7 +119,7 @@ The security concern is that agent tools and skills can execute sensitive action
|
|
|
94
119
|
|
|
95
120
|
**Two ways to use APort:** (1) **Guardrails (CLI/setup)** — run the installer to create your passport and config; (2) **Core (library)** — use the `OAPGuardrailProvider` ([docs/PROVIDER.md](docs/PROVIDER.md)) in your app so each tool call is verified. One provider per language (Python + TypeScript), works with any framework. 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).
|
|
96
121
|
|
|
97
|
-
**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.
|
|
122
|
+
**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. For IT-managed device rollout, see [Enterprise device deployment](docs/ENTERPRISE_DEVICE_DEPLOYMENT.md).
|
|
98
123
|
|
|
99
124
|
| Framework | Doc | Integration | Install |
|
|
100
125
|
|-----------|-----|--------------|--------|
|
|
@@ -159,11 +184,11 @@ aport setup --framework=langchain
|
|
|
159
184
|
```
|
|
160
185
|
Then install the framework-specific Python package and follow the printed integration step for your framework.
|
|
161
186
|
|
|
162
|
-
This runs
|
|
187
|
+
This runs setup and writes config for your framework. Choose hosted setup for passport and setup-key creation, or local setup for an on-disk passport. 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).
|
|
163
188
|
|
|
164
189
|
**Guardrail mode (local vs API)** — On the **Node** installer (`npx @aporthq/aport-agent-guardrails …` / `bin/agent-guardrails`), every framework accepts the same flags: `--mode=api` (with optional `--api-url`, default `https://api.aport.io`) or `--mode=local`, and an optional hosted `ap_<hex>` argument (API mode, no local passport). That flow writes `…/aport/guardrail-mode.env` where the hooks/generic installers need it. The **Python** `aport setup` CLI does not parse those flags yet; use the Node command above for API/local mode during setup, or set mode in your framework `config.yaml` per the framework doc.
|
|
165
190
|
|
|
166
|
-
**2. Hosted passport (optional)** — If you already have an agent_id from [aport.io](https://aport.io), use it to skip
|
|
191
|
+
**2. Hosted passport (optional)** — The installer can create a hosted passport during setup. If you already have an agent_id from [aport.io](https://aport.io), use it to skip passport creation: `npx @aporthq/aport-agent-guardrails claude-code <agent_id>`. See [Hosted passport setup](docs/HOSTED_PASSPORT_SETUP.md).
|
|
167
192
|
|
|
168
193
|
**3. Test that policy runs** — After setup, the guardrail runs automatically when your agent uses tools (Cursor hook, LangChain callback, OpenClaw plugin, etc.). To try allow/deny from the command line (any framework), use the installed `aport-guardrail` command (Node) or call the evaluator from Python; both use your existing passport from the framework config dir (e.g. `~/.cursor/aport/`, `~/.aport/langchain/aport/`).
|
|
169
194
|
|
|
@@ -445,7 +470,7 @@ Use the framework-specific doc for where config and passport live and for any ex
|
|
|
445
470
|
|
|
446
471
|
| Doc | Description |
|
|
447
472
|
|-----|-------------|
|
|
448
|
-
| [QuickStart: OpenClaw Plugin](docs/QUICKSTART_OPENCLAW_PLUGIN.md) |
|
|
473
|
+
| [QuickStart: OpenClaw Plugin](docs/QUICKSTART_OPENCLAW_PLUGIN.md) | One-command OpenClaw setup |
|
|
449
474
|
| [Hosted passport setup](docs/HOSTED_PASSPORT_SETUP.md) | Use passport from aport.io — `npx ... openclaw <agent_id>` or choose hosted in wizard |
|
|
450
475
|
| [Verification methods (local vs API)](docs/VERIFICATION_METHODS.md) | Deep dive: bash vs API evaluator |
|
|
451
476
|
| [Quick Start Guide](docs/QUICKSTART.md) | Passport wizard, copy-paste option |
|
|
@@ -9,6 +9,11 @@ set -e
|
|
|
9
9
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
10
10
|
ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
11
11
|
|
|
12
|
+
# Anchor data paths to Claude Code config before resolve (hosted/API installs may have no passport.json).
|
|
13
|
+
# shellcheck source=bin/lib/framework-hook-paths.sh
|
|
14
|
+
. "$ROOT_DIR/bin/lib/framework-hook-paths.sh"
|
|
15
|
+
aport_hook_prepare_framework_paths "claude-code" "${APORT_CLAUDE_CODE_CONFIG_DIR:-}" "$HOME/.claude"
|
|
16
|
+
|
|
12
17
|
# Path resolver: probes ~/.claude, ~/.cursor, ~/.openclaw, etc.
|
|
13
18
|
# shellcheck source=bin/aport-resolve-paths.sh
|
|
14
19
|
. "$ROOT_DIR/bin/aport-resolve-paths.sh"
|
|
@@ -16,7 +21,7 @@ ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
|
16
21
|
. "$ROOT_DIR/bin/lib/guardrail-mode.sh"
|
|
17
22
|
# shellcheck source=bin/lib/hook-read-policy.sh
|
|
18
23
|
. "$ROOT_DIR/bin/lib/hook-read-policy.sh"
|
|
19
|
-
load_guardrail_mode_for_hooks "${OPENCLAW_CONFIG_DIR:-$HOME/.claude}"
|
|
24
|
+
load_guardrail_mode_for_hooks "${APORT_CONFIG_DIR:-${OPENCLAW_CONFIG_DIR:-$HOME/.claude}}"
|
|
20
25
|
|
|
21
26
|
GUARDRAIL="$ROOT_DIR/bin/aport-guardrail-bash.sh"
|
|
22
27
|
if [ "${APORT_GUARDRAIL_MODE:-local}" = "api" ]; then
|
|
@@ -136,9 +141,10 @@ case "$TOOL_NAME_NORM" in
|
|
|
136
141
|
esac
|
|
137
142
|
|
|
138
143
|
# Use a per-invocation decision file to avoid race conditions with concurrent tool calls
|
|
139
|
-
HOOK_DECISION_FILE="${OPENCLAW_DECISION_FILE:-}"
|
|
144
|
+
HOOK_DECISION_FILE="${APORT_DECISION_FILE:-${OPENCLAW_DECISION_FILE:-}}"
|
|
140
145
|
if [ -n "$HOOK_DECISION_FILE" ]; then
|
|
141
146
|
HOOK_DECISION_FILE="${HOOK_DECISION_FILE%.json}-$$.json"
|
|
147
|
+
export APORT_DECISION_FILE="$HOOK_DECISION_FILE"
|
|
142
148
|
export OPENCLAW_DECISION_FILE="$HOOK_DECISION_FILE"
|
|
143
149
|
fi
|
|
144
150
|
|
|
@@ -45,8 +45,9 @@ fi
|
|
|
45
45
|
PASSPORT_FILE="${PASSPORT_FILE/#\~/$HOME}"
|
|
46
46
|
|
|
47
47
|
# Config dir: from env, or derived from passport path (e.g. .../aport/passport.json -> parent of aport)
|
|
48
|
-
if [ -n "${OPENCLAW_CONFIG_DIR:-}" ]; then
|
|
49
|
-
CONFIG_DIR="${OPENCLAW_CONFIG_DIR
|
|
48
|
+
if [ -n "${APORT_CONFIG_DIR:-${OPENCLAW_CONFIG_DIR:-}}" ]; then
|
|
49
|
+
CONFIG_DIR="${APORT_CONFIG_DIR:-${OPENCLAW_CONFIG_DIR:-}}"
|
|
50
|
+
CONFIG_DIR="${CONFIG_DIR/#\~/$HOME}"
|
|
50
51
|
else
|
|
51
52
|
CONFIG_DIR="$(dirname "$PASSPORT_FILE")"
|
|
52
53
|
case "$PASSPORT_FILE" in
|
package/bin/aport-cursor-hook.sh
CHANGED
|
@@ -13,6 +13,11 @@ set -e
|
|
|
13
13
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
14
14
|
ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
15
15
|
|
|
16
|
+
# Anchor data paths to Cursor config before resolve (hosted/API installs may have no passport.json).
|
|
17
|
+
# shellcheck source=bin/lib/framework-hook-paths.sh
|
|
18
|
+
. "$ROOT_DIR/bin/lib/framework-hook-paths.sh"
|
|
19
|
+
aport_hook_prepare_framework_paths "cursor" "${APORT_CURSOR_CONFIG_DIR:-}" "$HOME/.cursor"
|
|
20
|
+
|
|
16
21
|
# Passport/config: resolver probes ~/.cursor, ~/.openclaw, ~/.aport/*, etc.
|
|
17
22
|
# shellcheck source=bin/aport-resolve-paths.sh
|
|
18
23
|
. "$ROOT_DIR/bin/aport-resolve-paths.sh"
|
|
@@ -20,7 +25,7 @@ ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
|
20
25
|
. "$ROOT_DIR/bin/lib/guardrail-mode.sh"
|
|
21
26
|
# shellcheck source=bin/lib/hook-read-policy.sh
|
|
22
27
|
. "$ROOT_DIR/bin/lib/hook-read-policy.sh"
|
|
23
|
-
load_guardrail_mode_for_hooks "${OPENCLAW_CONFIG_DIR:-$HOME/.cursor}"
|
|
28
|
+
load_guardrail_mode_for_hooks "${APORT_CONFIG_DIR:-${OPENCLAW_CONFIG_DIR:-$HOME/.cursor}}"
|
|
24
29
|
|
|
25
30
|
GUARDRAIL="$ROOT_DIR/bin/aport-guardrail-bash.sh"
|
|
26
31
|
if [ "${APORT_GUARDRAIL_MODE:-local}" = "api" ]; then
|
|
@@ -164,9 +169,10 @@ else
|
|
|
164
169
|
fi
|
|
165
170
|
|
|
166
171
|
# Use a per-invocation decision file to avoid race conditions with concurrent tool calls
|
|
167
|
-
HOOK_DECISION_FILE="${OPENCLAW_DECISION_FILE:-}"
|
|
172
|
+
HOOK_DECISION_FILE="${APORT_DECISION_FILE:-${OPENCLAW_DECISION_FILE:-}}"
|
|
168
173
|
if [ -n "$HOOK_DECISION_FILE" ]; then
|
|
169
174
|
HOOK_DECISION_FILE="${HOOK_DECISION_FILE%.json}-$$.json"
|
|
175
|
+
export APORT_DECISION_FILE="$HOOK_DECISION_FILE"
|
|
170
176
|
export OPENCLAW_DECISION_FILE="$HOOK_DECISION_FILE"
|
|
171
177
|
fi
|
|
172
178
|
|
|
@@ -202,7 +208,7 @@ if [ -n "$HOOK_DECISION_FILE" ] && [ -f "$HOOK_DECISION_FILE" ]; then
|
|
|
202
208
|
fi
|
|
203
209
|
# Fallback: try common config dirs
|
|
204
210
|
if [ "$REASON" = "Policy denied this action." ]; then
|
|
205
|
-
for DEC in "${OPENCLAW_CONFIG_DIR:-$HOME/.cursor}/aport/decision.json" "$HOME/.cursor/aport/decision.json" "$HOME/.openclaw/aport/decision.json"; do
|
|
211
|
+
for DEC in "${APORT_CONFIG_DIR:-${OPENCLAW_CONFIG_DIR:-$HOME/.cursor}}/aport/decision.json" "$HOME/.cursor/aport/decision.json" "$HOME/.openclaw/aport/decision.json"; do
|
|
206
212
|
if [ -f "$DEC" ]; then
|
|
207
213
|
R="$(jq -r '.reasons[0].message // empty' "$DEC" 2> /dev/null)"
|
|
208
214
|
[ -n "$R" ] && REASON="$R" && break
|
|
@@ -63,6 +63,8 @@ if [ -n "$DEBUG_APORT" ]; then
|
|
|
63
63
|
fi
|
|
64
64
|
|
|
65
65
|
# Export environment variables for evaluator (APORT_API_URL, APORT_AGENT_ID, APORT_API_KEY passed through)
|
|
66
|
+
export APORT_PASSPORT_FILE="$PASSPORT_FILE"
|
|
67
|
+
export APORT_DECISION_FILE="$DECISION_FILE"
|
|
66
68
|
export OPENCLAW_PASSPORT_FILE="$PASSPORT_FILE"
|
|
67
69
|
export OPENCLAW_DECISION_FILE="$DECISION_FILE"
|
|
68
70
|
|
|
@@ -5,14 +5,19 @@
|
|
|
5
5
|
# - aport-guardrail-api.sh
|
|
6
6
|
# - aport-status.sh
|
|
7
7
|
# (aport-create-passport.sh uses --output; wrappers set env so children get resolved paths.)
|
|
8
|
-
# Sets
|
|
9
|
-
#
|
|
8
|
+
# Sets canonical APORT_* path variables plus legacy OPENCLAW_* aliases for compatibility.
|
|
9
|
+
# Also sets PASSPORT_FILE, DECISION_FILE, AUDIT_LOG. Passport status (active|suspended|revoked)
|
|
10
|
+
# is source of truth for suspend; no separate file.
|
|
10
11
|
# Caller must ensure APORT_DATA_DIR exists before writing (e.g. mkdir -p "$(dirname "$AUDIT_LOG")").
|
|
11
12
|
|
|
12
13
|
resolve_aport_paths() {
|
|
13
14
|
local config_dir
|
|
14
15
|
local passport_path
|
|
15
16
|
local data_dir
|
|
17
|
+
local explicit_passport="${APORT_PASSPORT_FILE:-${OPENCLAW_PASSPORT_FILE:-}}"
|
|
18
|
+
local explicit_decision="${APORT_DECISION_FILE:-${OPENCLAW_DECISION_FILE:-}}"
|
|
19
|
+
local explicit_audit="${APORT_AUDIT_LOG:-${OPENCLAW_AUDIT_LOG:-}}"
|
|
20
|
+
local explicit_config="${APORT_CONFIG_DIR:-${OPENCLAW_CONFIG_DIR:-}}"
|
|
16
21
|
|
|
17
22
|
# Source validation if available (for validate_passport_path)
|
|
18
23
|
local _resolve_script_dir
|
|
@@ -24,9 +29,14 @@ resolve_aport_paths() {
|
|
|
24
29
|
. "$_resolve_script_dir/../bin/lib/validation.sh" 2> /dev/null || true
|
|
25
30
|
fi
|
|
26
31
|
|
|
32
|
+
[ -n "$explicit_passport" ] && explicit_passport="${explicit_passport/#\~/$HOME}"
|
|
33
|
+
[ -n "$explicit_decision" ] && explicit_decision="${explicit_decision/#\~/$HOME}"
|
|
34
|
+
[ -n "$explicit_audit" ] && explicit_audit="${explicit_audit/#\~/$HOME}"
|
|
35
|
+
[ -n "$explicit_config" ] && explicit_config="${explicit_config/#\~/$HOME}"
|
|
36
|
+
|
|
27
37
|
# 0) AGENTS.md enforcement block (repo-scoped, highest priority after explicit env)
|
|
28
|
-
# Only checked when no explicit env var is set
|
|
29
|
-
if [ -z "$
|
|
38
|
+
# Only checked when no explicit passport env var is set.
|
|
39
|
+
if [ -z "$explicit_passport" ]; then
|
|
30
40
|
local _agentsmd_lib="$_resolve_script_dir/lib/agentsmd.sh"
|
|
31
41
|
[ ! -f "$_agentsmd_lib" ] && _agentsmd_lib="$_resolve_script_dir/../bin/lib/agentsmd.sh"
|
|
32
42
|
if [ -f "$_agentsmd_lib" ]; then
|
|
@@ -39,53 +49,61 @@ resolve_aport_paths() {
|
|
|
39
49
|
if [ -n "$AGENTSMD_PASSPORT" ] && [ -f "$AGENTSMD_PASSPORT" ]; then
|
|
40
50
|
passport_path="$AGENTSMD_PASSPORT"
|
|
41
51
|
data_dir="$(dirname "$AGENTSMD_PASSPORT")"
|
|
52
|
+
explicit_decision="${explicit_decision:-${data_dir}/decision.json}"
|
|
53
|
+
explicit_audit="${explicit_audit:-${data_dir}/audit.log}"
|
|
54
|
+
|
|
55
|
+
export APORT_PASSPORT_FILE="$passport_path"
|
|
42
56
|
export OPENCLAW_PASSPORT_FILE="$passport_path"
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
export OPENCLAW_AUDIT_LOG="$
|
|
57
|
+
export APORT_DECISION_FILE="$explicit_decision"
|
|
58
|
+
export OPENCLAW_DECISION_FILE="$explicit_decision"
|
|
59
|
+
export APORT_AUDIT_LOG="$explicit_audit"
|
|
60
|
+
export OPENCLAW_AUDIT_LOG="$explicit_audit"
|
|
61
|
+
|
|
47
62
|
PASSPORT_FILE="$passport_path"
|
|
48
|
-
DECISION_FILE="$
|
|
49
|
-
AUDIT_LOG="$
|
|
63
|
+
DECISION_FILE="$explicit_decision"
|
|
64
|
+
AUDIT_LOG="$explicit_audit"
|
|
50
65
|
return 0
|
|
51
66
|
fi
|
|
52
67
|
fi
|
|
53
68
|
fi
|
|
54
69
|
fi
|
|
55
70
|
|
|
56
|
-
# 1) Explicit path set and file exists
|
|
57
|
-
if [ -n "$
|
|
71
|
+
# 1) Explicit path set and file exists -> use it (plugin or wrapper)
|
|
72
|
+
if [ -n "$explicit_passport" ] && [ -f "$explicit_passport" ]; then
|
|
58
73
|
# Validate env-provided path if validator is available
|
|
59
74
|
if type validate_explicit_passport_path &> /dev/null; then
|
|
60
|
-
if ! validate_explicit_passport_path "$
|
|
61
|
-
echo "[aport] WARN:
|
|
75
|
+
if ! validate_explicit_passport_path "$explicit_passport"; then
|
|
76
|
+
echo "[aport] WARN: APORT_PASSPORT_FILE path failed validation: $explicit_passport" >&2
|
|
62
77
|
fi
|
|
63
78
|
fi
|
|
64
|
-
data_dir="$(dirname "$
|
|
65
|
-
passport_path="$
|
|
66
|
-
# 2) Explicit path set but file missing
|
|
67
|
-
elif [ -n "$
|
|
68
|
-
config_dir="$(cd "$(dirname "$
|
|
79
|
+
data_dir="$(dirname "$explicit_passport")"
|
|
80
|
+
passport_path="$explicit_passport"
|
|
81
|
+
# 2) Explicit path set but file missing -> legacy: try parent dir (e.g. .../openclaw/passport.json)
|
|
82
|
+
elif [ -n "$explicit_passport" ]; then
|
|
83
|
+
config_dir="$(cd "$(dirname "$explicit_passport")/.." 2> /dev/null && pwd)"
|
|
69
84
|
if [ -f "${config_dir}/passport.json" ]; then
|
|
70
85
|
passport_path="${config_dir}/passport.json"
|
|
71
86
|
data_dir="$config_dir"
|
|
72
87
|
else
|
|
73
|
-
passport_path="$
|
|
74
|
-
data_dir="$(dirname "$
|
|
88
|
+
passport_path="$explicit_passport"
|
|
89
|
+
data_dir="$(dirname "$explicit_passport")"
|
|
75
90
|
fi
|
|
76
|
-
# 3)
|
|
91
|
+
# 3) Probe framework paths, or honor APORT_CONFIG_DIR / OPENCLAW_CONFIG_DIR set by framework hooks.
|
|
77
92
|
else
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
93
|
+
if [ -n "$explicit_config" ]; then
|
|
94
|
+
config_dir="$explicit_config"
|
|
95
|
+
else
|
|
96
|
+
config_dir=""
|
|
97
|
+
for candidate in "$HOME/.claude" "$HOME/.cursor" "$HOME/.openclaw" "$HOME/.aport/langchain" "$HOME/.aport/crewai" "$HOME/.aport/deerflow" "$HOME/.n8n"; do
|
|
98
|
+
if [ -f "${candidate}/aport/passport.json" ]; then
|
|
99
|
+
config_dir="$candidate"
|
|
100
|
+
break
|
|
101
|
+
fi
|
|
102
|
+
done
|
|
103
|
+
if [ -z "$config_dir" ]; then
|
|
104
|
+
config_dir="$HOME/.openclaw"
|
|
83
105
|
fi
|
|
84
|
-
done
|
|
85
|
-
if [ -z "$config_dir" ]; then
|
|
86
|
-
config_dir="${OPENCLAW_CONFIG_DIR:-$HOME/.openclaw}"
|
|
87
106
|
fi
|
|
88
|
-
config_dir="${config_dir/#\~/$HOME}"
|
|
89
107
|
if [ -f "${config_dir}/aport/passport.json" ]; then
|
|
90
108
|
passport_path="${config_dir}/aport/passport.json"
|
|
91
109
|
data_dir="${config_dir}/aport"
|
|
@@ -98,19 +116,28 @@ resolve_aport_paths() {
|
|
|
98
116
|
fi
|
|
99
117
|
fi
|
|
100
118
|
|
|
119
|
+
export APORT_PASSPORT_FILE="$passport_path"
|
|
101
120
|
export OPENCLAW_PASSPORT_FILE="$passport_path"
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
121
|
+
|
|
122
|
+
# Preserve explicitly set decision/audit paths (e.g. tests); otherwise use data_dir.
|
|
123
|
+
if [ -n "$explicit_decision" ]; then
|
|
124
|
+
export APORT_DECISION_FILE="$explicit_decision"
|
|
125
|
+
else
|
|
126
|
+
export APORT_DECISION_FILE="${data_dir}/decision.json"
|
|
127
|
+
fi
|
|
128
|
+
export OPENCLAW_DECISION_FILE="$APORT_DECISION_FILE"
|
|
129
|
+
|
|
130
|
+
if [ -n "$explicit_audit" ]; then
|
|
131
|
+
export APORT_AUDIT_LOG="$explicit_audit"
|
|
105
132
|
else
|
|
106
|
-
export
|
|
133
|
+
export APORT_AUDIT_LOG="${data_dir}/audit.log"
|
|
107
134
|
fi
|
|
108
|
-
export OPENCLAW_AUDIT_LOG="$
|
|
135
|
+
export OPENCLAW_AUDIT_LOG="$APORT_AUDIT_LOG"
|
|
109
136
|
|
|
110
|
-
PASSPORT_FILE="$
|
|
111
|
-
DECISION_FILE="$
|
|
112
|
-
AUDIT_LOG="$
|
|
137
|
+
PASSPORT_FILE="$APORT_PASSPORT_FILE"
|
|
138
|
+
DECISION_FILE="$APORT_DECISION_FILE"
|
|
139
|
+
AUDIT_LOG="$APORT_AUDIT_LOG"
|
|
113
140
|
}
|
|
114
141
|
|
|
115
|
-
# When sourced, resolve immediately so callers just use the vars
|
|
142
|
+
# When sourced, resolve immediately so callers just use the vars.
|
|
116
143
|
resolve_aport_paths
|
|
@@ -14,6 +14,8 @@ source "$LIB/config.sh"
|
|
|
14
14
|
source "$LIB/framework-setup.sh"
|
|
15
15
|
# shellcheck source=../lib/guardrail-mode.sh
|
|
16
16
|
source "$LIB/guardrail-mode.sh"
|
|
17
|
+
# shellcheck source=../lib/quick-hosted.sh
|
|
18
|
+
source "$LIB/quick-hosted.sh"
|
|
17
19
|
|
|
18
20
|
run_setup() {
|
|
19
21
|
parse_guardrail_mode_args "$@"
|
|
@@ -28,6 +30,9 @@ run_setup() {
|
|
|
28
30
|
hosted_agent_id="$APORT_HOSTED_AGENT_ID_CLI"
|
|
29
31
|
export APORT_AGENT_ID="$hosted_agent_id"
|
|
30
32
|
log_info "Using hosted passport (agent_id: $hosted_agent_id) — skipping wizard."
|
|
33
|
+
elif aport_maybe_configure_hosted_passport "claude-code" "$config_dir"; then
|
|
34
|
+
hosted_agent_id="$APORT_AGENT_ID"
|
|
35
|
+
log_info "Using hosted passport (agent_id: $hosted_agent_id) — skipping wizard."
|
|
31
36
|
else
|
|
32
37
|
# Check AGENTS.md for enforcement config — skip wizard if already configured
|
|
33
38
|
# shellcheck source=../lib/agentsmd.sh
|
|
@@ -65,6 +70,11 @@ run_setup() {
|
|
|
65
70
|
_write_claude_settings "$SETTINGS_FILE" "$HOOK_SCRIPT"
|
|
66
71
|
chmod 600 "$SETTINGS_FILE"
|
|
67
72
|
|
|
73
|
+
# Audit log is appended by hooks; create empty file so the advertised path exists after install.
|
|
74
|
+
mkdir -p "$config_dir/aport"
|
|
75
|
+
: >> "$config_dir/aport/audit.log"
|
|
76
|
+
chmod 600 "$config_dir/aport/audit.log" 2> /dev/null || true
|
|
77
|
+
|
|
68
78
|
echo ""
|
|
69
79
|
echo " Next steps (Claude Code):"
|
|
70
80
|
echo " ─────────────────────────"
|
|
@@ -78,7 +88,7 @@ run_setup() {
|
|
|
78
88
|
echo " 5. Restart Claude Code so the PreToolUse hook is picked up."
|
|
79
89
|
echo " 6. Tool use will be checked by APort policy (exit 2 = block)."
|
|
80
90
|
echo ""
|
|
81
|
-
echo " Audit log: $config_dir/aport/audit.log"
|
|
91
|
+
echo " Audit log: $config_dir/aport/audit.log (entries added on each policy check)"
|
|
82
92
|
echo ""
|
|
83
93
|
echo " Note: claude --dangerously-skip-permissions bypasses ALL hooks including APort."
|
|
84
94
|
echo " See: docs/frameworks/claude-code.md"
|
package/bin/frameworks/cursor.sh
CHANGED
|
@@ -14,6 +14,8 @@ source "$LIB/config.sh"
|
|
|
14
14
|
source "$LIB/framework-setup.sh"
|
|
15
15
|
# shellcheck source=../lib/guardrail-mode.sh
|
|
16
16
|
source "$LIB/guardrail-mode.sh"
|
|
17
|
+
# shellcheck source=../lib/quick-hosted.sh
|
|
18
|
+
source "$LIB/quick-hosted.sh"
|
|
17
19
|
|
|
18
20
|
run_setup() {
|
|
19
21
|
parse_guardrail_mode_args "$@"
|
|
@@ -29,6 +31,9 @@ run_setup() {
|
|
|
29
31
|
hosted_agent_id="$APORT_HOSTED_AGENT_ID_CLI"
|
|
30
32
|
export APORT_AGENT_ID="$hosted_agent_id"
|
|
31
33
|
log_info "Using hosted passport (agent_id: $hosted_agent_id) — skipping wizard."
|
|
34
|
+
elif aport_maybe_configure_hosted_passport "cursor" "$config_dir"; then
|
|
35
|
+
hosted_agent_id="$APORT_AGENT_ID"
|
|
36
|
+
log_info "Using hosted passport (agent_id: $hosted_agent_id) — skipping wizard."
|
|
32
37
|
else
|
|
33
38
|
# Check AGENTS.md for enforcement config — skip wizard if already configured
|
|
34
39
|
# shellcheck source=../lib/agentsmd.sh
|
|
@@ -102,7 +107,7 @@ run_setup() {
|
|
|
102
107
|
echo " 5. Restart Cursor (or reload window) so hooks are picked up."
|
|
103
108
|
echo " 6. Shell commands and tool use will be checked by APort policy (exit 2 = block)."
|
|
104
109
|
echo ""
|
|
105
|
-
echo "
|
|
110
|
+
echo " For other frameworks like Claude Code, use the dedicated integration: docs/frameworks"
|
|
106
111
|
echo ""
|
|
107
112
|
}
|
|
108
113
|
|
|
@@ -19,6 +19,8 @@ source "$LIB/runtime.sh"
|
|
|
19
19
|
source "$LIB/config.sh"
|
|
20
20
|
# shellcheck source=../lib/guardrail-mode.sh
|
|
21
21
|
source "$LIB/guardrail-mode.sh"
|
|
22
|
+
# shellcheck source=../lib/quick-hosted.sh
|
|
23
|
+
source "$LIB/quick-hosted.sh"
|
|
22
24
|
|
|
23
25
|
framework="${APORT_FRAMEWORK:?APORT_FRAMEWORK must be set by the dispatcher}"
|
|
24
26
|
crewai_integration_mode="${APORT_CREWAI_INTEGRATION_MODE:-compat}"
|
|
@@ -46,7 +48,11 @@ while [[ ${#remaining_args[@]} -gt 0 ]]; do
|
|
|
46
48
|
crewai_integration_mode="${remaining_args[0]}"
|
|
47
49
|
remaining_args=("${remaining_args[@]:1}")
|
|
48
50
|
;;
|
|
49
|
-
ap_
|
|
51
|
+
ap_* | apt_* | agt_inst_* | agt_tmpl_*)
|
|
52
|
+
if ! is_aport_hosted_agent_id "$arg"; then
|
|
53
|
+
log_error "Invalid hosted passport ID format: $arg"
|
|
54
|
+
exit 1
|
|
55
|
+
fi
|
|
50
56
|
hosted_agent_id="$arg"
|
|
51
57
|
;;
|
|
52
58
|
*)
|
|
@@ -134,6 +140,9 @@ persist_python_framework_config() {
|
|
|
134
140
|
|
|
135
141
|
if [[ "$selected_mode" == "api" ]]; then
|
|
136
142
|
_config_replace_or_append "$config_file" "api_url" "$(_yaml_quote "${selected_api_url:-$DEFAULT_APORT_API_URL}")"
|
|
143
|
+
if [[ -n "${APORT_API_KEY:-}" ]]; then
|
|
144
|
+
_config_replace_or_append "$config_file" "api_key" "$(_yaml_quote "$APORT_API_KEY")"
|
|
145
|
+
fi
|
|
137
146
|
if [[ -n "$hosted_id" ]]; then
|
|
138
147
|
_config_replace_or_append "$config_file" "agent_id" "$(_yaml_quote "$hosted_id")"
|
|
139
148
|
_config_remove_key "$config_file" "passport_path"
|
|
@@ -146,6 +155,7 @@ persist_python_framework_config() {
|
|
|
146
155
|
_config_replace_or_append "$config_file" "passport_path" "$(_yaml_quote "$local_passport_path")"
|
|
147
156
|
fi
|
|
148
157
|
_config_remove_key "$config_file" "agent_id"
|
|
158
|
+
_config_remove_key "$config_file" "api_key"
|
|
149
159
|
fi
|
|
150
160
|
|
|
151
161
|
chmod 600 "$config_file" 2> /dev/null || true
|
|
@@ -166,6 +176,9 @@ run_setup() {
|
|
|
166
176
|
fi
|
|
167
177
|
if [[ -n "$hosted_agent_id" ]]; then
|
|
168
178
|
log_info "Using hosted passport (agent_id: $hosted_agent_id) — skipping wizard."
|
|
179
|
+
elif aport_maybe_configure_hosted_passport "$framework" "$config_dir"; then
|
|
180
|
+
hosted_agent_id="$APORT_AGENT_ID"
|
|
181
|
+
log_info "Using hosted passport (agent_id: $hosted_agent_id) — skipping wizard."
|
|
169
182
|
elif ((${#FORWARD_ARGS[@]} > 0)); then
|
|
170
183
|
run_passport_wizard "${FORWARD_ARGS[@]}"
|
|
171
184
|
else
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Framework hook path normalization.
|
|
3
|
+
# GUI tools can inherit stale legacy OPENCLAW_* variables from another framework
|
|
4
|
+
# session. Hooks should default to their own framework config unless the caller
|
|
5
|
+
# explicitly opts into an external passport path.
|
|
6
|
+
|
|
7
|
+
aport_hook_expand_path() {
|
|
8
|
+
local input="$1"
|
|
9
|
+
printf '%s' "${input/#\~/$HOME}"
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
aport_hook_known_config_owner() {
|
|
13
|
+
local config_dir
|
|
14
|
+
config_dir="$(aport_hook_expand_path "$1")"
|
|
15
|
+
|
|
16
|
+
case "$config_dir" in
|
|
17
|
+
"$HOME/.claude" | "$HOME/.claude/"*) echo "claude-code" ;;
|
|
18
|
+
"$HOME/.cursor" | "$HOME/.cursor/"*) echo "cursor" ;;
|
|
19
|
+
"$HOME/.openclaw" | "$HOME/.openclaw/"*) echo "openclaw" ;;
|
|
20
|
+
"$HOME/.aport/langchain" | "$HOME/.aport/langchain/"*) echo "langchain" ;;
|
|
21
|
+
"$HOME/.aport/crewai" | "$HOME/.aport/crewai/"*) echo "crewai" ;;
|
|
22
|
+
"$HOME/.aport/deerflow" | "$HOME/.aport/deerflow/"*) echo "deerflow" ;;
|
|
23
|
+
"$HOME/.n8n" | "$HOME/.n8n/"*) echo "n8n" ;;
|
|
24
|
+
*) echo "" ;;
|
|
25
|
+
esac
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
aport_hook_prepare_framework_paths() {
|
|
29
|
+
local framework="$1"
|
|
30
|
+
local framework_config_dir="${2:-}"
|
|
31
|
+
local default_config_dir="$3"
|
|
32
|
+
local selected_config_dir owner passport_path decision_path audit_path
|
|
33
|
+
|
|
34
|
+
if [ -n "$framework_config_dir" ]; then
|
|
35
|
+
selected_config_dir="$framework_config_dir"
|
|
36
|
+
else
|
|
37
|
+
selected_config_dir="${APORT_CONFIG_DIR:-${OPENCLAW_CONFIG_DIR:-}}"
|
|
38
|
+
owner="$(aport_hook_known_config_owner "$selected_config_dir")"
|
|
39
|
+
if [ -n "$owner" ] && [ "$owner" != "$framework" ]; then
|
|
40
|
+
selected_config_dir="$default_config_dir"
|
|
41
|
+
fi
|
|
42
|
+
fi
|
|
43
|
+
|
|
44
|
+
selected_config_dir="${selected_config_dir:-$default_config_dir}"
|
|
45
|
+
selected_config_dir="$(aport_hook_expand_path "$selected_config_dir")"
|
|
46
|
+
export APORT_CONFIG_DIR="$selected_config_dir"
|
|
47
|
+
export OPENCLAW_CONFIG_DIR="$selected_config_dir"
|
|
48
|
+
|
|
49
|
+
if [ -n "${APORT_PASSPORT_FILE:-${OPENCLAW_PASSPORT_FILE:-}}" ] && [ -z "${APORT_ALLOW_EXTERNAL_PASSPORT_FILE:-}" ]; then
|
|
50
|
+
passport_path="$(aport_hook_expand_path "${APORT_PASSPORT_FILE:-${OPENCLAW_PASSPORT_FILE:-}}")"
|
|
51
|
+
case "$passport_path" in
|
|
52
|
+
"$APORT_CONFIG_DIR"/*)
|
|
53
|
+
export APORT_PASSPORT_FILE="$passport_path"
|
|
54
|
+
export OPENCLAW_PASSPORT_FILE="$passport_path"
|
|
55
|
+
;;
|
|
56
|
+
*)
|
|
57
|
+
unset APORT_PASSPORT_FILE
|
|
58
|
+
unset OPENCLAW_PASSPORT_FILE
|
|
59
|
+
;;
|
|
60
|
+
esac
|
|
61
|
+
fi
|
|
62
|
+
|
|
63
|
+
if [ -n "${APORT_ALLOW_EXTERNAL_PASSPORT_FILE:-}" ] && [ -n "${APORT_PASSPORT_FILE:-${OPENCLAW_PASSPORT_FILE:-}}" ]; then
|
|
64
|
+
passport_path="$(aport_hook_expand_path "${APORT_PASSPORT_FILE:-${OPENCLAW_PASSPORT_FILE:-}}")"
|
|
65
|
+
export APORT_PASSPORT_FILE="$passport_path"
|
|
66
|
+
export OPENCLAW_PASSPORT_FILE="$passport_path"
|
|
67
|
+
fi
|
|
68
|
+
|
|
69
|
+
if [ -n "${APORT_DECISION_FILE:-${OPENCLAW_DECISION_FILE:-}}" ]; then
|
|
70
|
+
decision_path="$(aport_hook_expand_path "${APORT_DECISION_FILE:-${OPENCLAW_DECISION_FILE:-}}")"
|
|
71
|
+
case "$decision_path" in
|
|
72
|
+
"$APORT_CONFIG_DIR"/*)
|
|
73
|
+
export APORT_DECISION_FILE="$decision_path"
|
|
74
|
+
export OPENCLAW_DECISION_FILE="$decision_path"
|
|
75
|
+
;;
|
|
76
|
+
*)
|
|
77
|
+
unset APORT_DECISION_FILE
|
|
78
|
+
unset OPENCLAW_DECISION_FILE
|
|
79
|
+
;;
|
|
80
|
+
esac
|
|
81
|
+
fi
|
|
82
|
+
|
|
83
|
+
if [ -n "${APORT_AUDIT_LOG:-${OPENCLAW_AUDIT_LOG:-}}" ]; then
|
|
84
|
+
audit_path="$(aport_hook_expand_path "${APORT_AUDIT_LOG:-${OPENCLAW_AUDIT_LOG:-}}")"
|
|
85
|
+
case "$audit_path" in
|
|
86
|
+
"$APORT_CONFIG_DIR"/*)
|
|
87
|
+
export APORT_AUDIT_LOG="$audit_path"
|
|
88
|
+
export OPENCLAW_AUDIT_LOG="$audit_path"
|
|
89
|
+
;;
|
|
90
|
+
*)
|
|
91
|
+
unset APORT_AUDIT_LOG
|
|
92
|
+
unset OPENCLAW_AUDIT_LOG
|
|
93
|
+
;;
|
|
94
|
+
esac
|
|
95
|
+
fi
|
|
96
|
+
}
|