@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 CHANGED
@@ -46,17 +46,41 @@ From the live APort Vault adversarial testbed:
46
46
 
47
47
  ## Start Here
48
48
 
49
- ### Install in 30 seconds
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
- - OpenClaw direct: `npx @aporthq/aport-agent-guardrails openclaw`
57
- - Hosted passport: `npx @aporthq/aport-agent-guardrails openclaw <agent_id>`
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. See [Deployment readiness](docs/DEPLOYMENT_READINESS.md).
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 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).
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 the wizard: `npx @aporthq/aport-agent-guardrails openclaw <agent_id>`. See [Hosted passport setup](docs/HOSTED_PASSPORT_SETUP.md).
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) | 5-minute OpenClaw setup |
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/#\~/$HOME}"
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
@@ -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: OPENCLAW_PASSPORT_FILE, OPENCLAW_DECISION_FILE, OPENCLAW_AUDIT_LOG
9
- # and PASSPORT_FILE, DECISION_FILE, AUDIT_LOG. Passport status (active|suspended|revoked) is source of truth for suspend; no separate file.
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 — OPENCLAW_PASSPORT_FILE always wins.
29
- if [ -z "${OPENCLAW_PASSPORT_FILE:-}" ]; then
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
- if [ -z "${OPENCLAW_DECISION_FILE:-}" ]; then
44
- export OPENCLAW_DECISION_FILE="${data_dir}/decision.json"
45
- fi
46
- export OPENCLAW_AUDIT_LOG="${data_dir}/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="${OPENCLAW_DECISION_FILE}"
49
- AUDIT_LOG="${data_dir}/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 use it (plugin or wrapper)
57
- if [ -n "${OPENCLAW_PASSPORT_FILE:-}" ] && [ -f "$OPENCLAW_PASSPORT_FILE" ]; then
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 "$OPENCLAW_PASSPORT_FILE"; then
61
- echo "[aport] WARN: OPENCLAW_PASSPORT_FILE path failed validation: $OPENCLAW_PASSPORT_FILE" >&2
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 "$OPENCLAW_PASSPORT_FILE")"
65
- passport_path="$OPENCLAW_PASSPORT_FILE"
66
- # 2) Explicit path set but file missing legacy: try parent dir (e.g. .../openclaw/passport.json)
67
- elif [ -n "${OPENCLAW_PASSPORT_FILE:-}" ]; then
68
- config_dir="$(cd "$(dirname "$OPENCLAW_PASSPORT_FILE")/.." 2> /dev/null && pwd)"
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="$OPENCLAW_PASSPORT_FILE"
74
- data_dir="$(dirname "$OPENCLAW_PASSPORT_FILE")"
88
+ passport_path="$explicit_passport"
89
+ data_dir="$(dirname "$explicit_passport")"
75
90
  fi
76
- # 3) No env → probe framework-specific default paths (where each framework stores data), then OpenClaw legacy
91
+ # 3) Probe framework paths, or honor APORT_CONFIG_DIR / OPENCLAW_CONFIG_DIR set by framework hooks.
77
92
  else
78
- config_dir=""
79
- for candidate in "$HOME/.claude" "$HOME/.cursor" "$HOME/.openclaw" "$HOME/.aport/langchain" "$HOME/.aport/crewai" "$HOME/.aport/deerflow" "$HOME/.n8n"; do
80
- if [ -f "${candidate}/aport/passport.json" ]; then
81
- config_dir="$candidate"
82
- break
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
- # Preserve explicitly set decision path (e.g. tests set OPENCLAW_DECISION_FILE); otherwise use data_dir
103
- if [ -n "${OPENCLAW_DECISION_FILE:-}" ]; then
104
- export OPENCLAW_DECISION_FILE="$OPENCLAW_DECISION_FILE"
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 OPENCLAW_DECISION_FILE="${data_dir}/decision.json"
133
+ export APORT_AUDIT_LOG="${data_dir}/audit.log"
107
134
  fi
108
- export OPENCLAW_AUDIT_LOG="${data_dir}/audit.log"
135
+ export OPENCLAW_AUDIT_LOG="$APORT_AUDIT_LOG"
109
136
 
110
- PASSPORT_FILE="$OPENCLAW_PASSPORT_FILE"
111
- DECISION_FILE="$OPENCLAW_DECISION_FILE"
112
- AUDIT_LOG="$OPENCLAW_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"
@@ -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 " Same script works for VS Code + Copilot. For Claude Code use the dedicated integration: docs/frameworks/claude-code.md"
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_[a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9])
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
+ }