@aporthq/aport-agent-guardrails 1.0.22 → 1.0.24
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 +5 -0
- package/bin/aport-claude-code-hook.sh +11 -1
- package/bin/aport-cursor-hook.sh +11 -1
- package/bin/frameworks/claude-code.sh +33 -6
- package/bin/frameworks/cursor.sh +33 -6
- package/bin/frameworks/generic.sh +121 -9
- package/bin/lib/guardrail-mode.sh +154 -0
- package/bin/openclaw +569 -458
- package/docs/RELEASE.md +1 -1
- package/docs/TOOL_POLICY_MAPPING.md +2 -2
- package/docs/frameworks/crewai.md +6 -0
- package/docs/frameworks/deerflow.md +3 -0
- package/docs/frameworks/langchain.md +3 -0
- package/docs/frameworks/n8n.md +3 -0
- package/docs/frameworks/openclaw.md +16 -1
- package/extensions/openclaw-aport/CHANGELOG.md +9 -0
- package/extensions/openclaw-aport/README.md +30 -2
- package/extensions/openclaw-aport/api-client.js +9 -1
- package/extensions/openclaw-aport/index.js +10 -11
- 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/extensions/openclaw-aport/tool-mapping.js +212 -25
- package/package.json +2 -1
- package/python/aport_guardrails/README.md +14 -0
- package/python/aport_guardrails/core/tool-pack-mapping.json +1 -0
package/README.md
CHANGED
|
@@ -124,6 +124,9 @@ Install via `npx @aporthq/aport-agent-guardrails <framework>` (or choose when pr
|
|
|
124
124
|
```bash
|
|
125
125
|
npx @aporthq/aport-agent-guardrails
|
|
126
126
|
# or: npx @aporthq/aport-agent-guardrails openclaw | cursor | claude-code | langchain | crewai | deerflow | n8n
|
|
127
|
+
# optional mode flags (all frameworks):
|
|
128
|
+
# --mode=api --api-url=https://api.aport.io
|
|
129
|
+
# --mode=local
|
|
127
130
|
```
|
|
128
131
|
|
|
129
132
|
**Python (LangChain, CrewAI, or DeerFlow):** Use the Python CLI directly via `uvx` or an installed package:
|
|
@@ -141,6 +144,8 @@ Then install the framework-specific Python package and follow the printed integr
|
|
|
141
144
|
|
|
142
145
|
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).
|
|
143
146
|
|
|
147
|
+
**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.
|
|
148
|
+
|
|
144
149
|
**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).
|
|
145
150
|
|
|
146
151
|
**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/`).
|
|
@@ -8,11 +8,21 @@ set -e
|
|
|
8
8
|
|
|
9
9
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
10
10
|
ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
11
|
-
GUARDRAIL="$ROOT_DIR/bin/aport-guardrail-bash.sh"
|
|
12
11
|
|
|
13
12
|
# Path resolver: probes ~/.claude, ~/.cursor, ~/.openclaw, etc.
|
|
14
13
|
# shellcheck source=bin/aport-resolve-paths.sh
|
|
15
14
|
. "$ROOT_DIR/bin/aport-resolve-paths.sh"
|
|
15
|
+
# shellcheck source=bin/lib/guardrail-mode.sh
|
|
16
|
+
. "$ROOT_DIR/bin/lib/guardrail-mode.sh"
|
|
17
|
+
load_guardrail_mode_for_hooks "${OPENCLAW_CONFIG_DIR:-$HOME/.claude}"
|
|
18
|
+
|
|
19
|
+
GUARDRAIL="$ROOT_DIR/bin/aport-guardrail-bash.sh"
|
|
20
|
+
if [ "${APORT_GUARDRAIL_MODE:-local}" = "api" ]; then
|
|
21
|
+
GUARDRAIL="$ROOT_DIR/bin/aport-guardrail-api.sh"
|
|
22
|
+
if [ -n "${APORT_API_URL:-}" ]; then
|
|
23
|
+
export APORT_API_URL
|
|
24
|
+
fi
|
|
25
|
+
fi
|
|
16
26
|
|
|
17
27
|
# Read stdin
|
|
18
28
|
INPUT=""
|
package/bin/aport-cursor-hook.sh
CHANGED
|
@@ -12,11 +12,21 @@ set -e
|
|
|
12
12
|
|
|
13
13
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
14
14
|
ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
15
|
-
GUARDRAIL="$ROOT_DIR/bin/aport-guardrail-bash.sh"
|
|
16
15
|
|
|
17
16
|
# Passport/config: resolver probes ~/.cursor, ~/.openclaw, ~/.aport/*, etc.
|
|
18
17
|
# shellcheck source=bin/aport-resolve-paths.sh
|
|
19
18
|
. "$ROOT_DIR/bin/aport-resolve-paths.sh"
|
|
19
|
+
# shellcheck source=bin/lib/guardrail-mode.sh
|
|
20
|
+
. "$ROOT_DIR/bin/lib/guardrail-mode.sh"
|
|
21
|
+
load_guardrail_mode_for_hooks "${OPENCLAW_CONFIG_DIR:-$HOME/.cursor}"
|
|
22
|
+
|
|
23
|
+
GUARDRAIL="$ROOT_DIR/bin/aport-guardrail-bash.sh"
|
|
24
|
+
if [ "${APORT_GUARDRAIL_MODE:-local}" = "api" ]; then
|
|
25
|
+
GUARDRAIL="$ROOT_DIR/bin/aport-guardrail-api.sh"
|
|
26
|
+
if [ -n "${APORT_API_URL:-}" ]; then
|
|
27
|
+
export APORT_API_URL
|
|
28
|
+
fi
|
|
29
|
+
fi
|
|
20
30
|
|
|
21
31
|
# Read stdin (single JSON object; Cursor sends one payload per invocation)
|
|
22
32
|
INPUT=""
|
|
@@ -12,21 +12,43 @@ source "$LIB/passport.sh"
|
|
|
12
12
|
source "$LIB/config.sh"
|
|
13
13
|
# shellcheck source=../lib/framework-setup.sh
|
|
14
14
|
source "$LIB/framework-setup.sh"
|
|
15
|
+
# shellcheck source=../lib/guardrail-mode.sh
|
|
16
|
+
source "$LIB/guardrail-mode.sh"
|
|
15
17
|
|
|
16
18
|
run_setup() {
|
|
19
|
+
parse_guardrail_mode_args "$@"
|
|
20
|
+
|
|
17
21
|
log_info "Setting up APort guardrails for Claude Code..."
|
|
18
22
|
config_dir="$(ensure_aport_dir_secure claude-code)"
|
|
19
23
|
|
|
20
24
|
export APORT_FRAMEWORK=claude-code
|
|
21
25
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
+
local hosted_agent_id=""
|
|
27
|
+
if [[ -n "${APORT_HOSTED_AGENT_ID_CLI:-}" ]]; then
|
|
28
|
+
hosted_agent_id="$APORT_HOSTED_AGENT_ID_CLI"
|
|
29
|
+
export APORT_AGENT_ID="$hosted_agent_id"
|
|
30
|
+
log_info "Using hosted passport (agent_id: $hosted_agent_id) — skipping wizard."
|
|
31
|
+
else
|
|
32
|
+
# Check AGENTS.md for enforcement config — skip wizard if already configured
|
|
33
|
+
# shellcheck source=../lib/agentsmd.sh
|
|
34
|
+
source "$LIB/agentsmd.sh"
|
|
35
|
+
setup_from_agentsmd_or_wizard "${APORT_FRAMEWORK_ARGS[@]}"
|
|
36
|
+
fi
|
|
26
37
|
|
|
27
38
|
# Harden permissions on passport (contains policy/capabilities)
|
|
28
39
|
[ -f "$config_dir/aport/passport.json" ] && chmod 600 "$config_dir/aport/passport.json"
|
|
29
40
|
|
|
41
|
+
if [[ -z "$hosted_agent_id" && -n "${APORT_AGENT_ID:-}" ]]; then
|
|
42
|
+
hosted_agent_id="$APORT_AGENT_ID"
|
|
43
|
+
fi
|
|
44
|
+
|
|
45
|
+
select_guardrail_mode "claude-code" "$hosted_agent_id"
|
|
46
|
+
select_guardrail_api_url "$APORT_SELECTED_GUARDRAIL_MODE"
|
|
47
|
+
if [[ "$APORT_SELECTED_GUARDRAIL_MODE" = "api" ]]; then
|
|
48
|
+
export APORT_API_URL="${APORT_SELECTED_API_URL:-$DEFAULT_APORT_API_URL}"
|
|
49
|
+
fi
|
|
50
|
+
MODE_FILE="$(write_guardrail_mode_file "$config_dir" "$APORT_SELECTED_GUARDRAIL_MODE" "${APORT_SELECTED_API_URL:-}" "$hosted_agent_id")"
|
|
51
|
+
|
|
30
52
|
# Resolve absolute path to hook script (works from repo or npx package)
|
|
31
53
|
HOOK_SCRIPT="$(resolve_hook_script_path "${APORT_CLAUDE_CODE_HOOK_SCRIPT:-}" "aport-claude-code-hook.sh" "$LIB")"
|
|
32
54
|
if [ ! -f "$HOOK_SCRIPT" ]; then
|
|
@@ -48,8 +70,13 @@ run_setup() {
|
|
|
48
70
|
echo " ─────────────────────────"
|
|
49
71
|
echo " 1. Settings written to: $SETTINGS_FILE"
|
|
50
72
|
echo " 2. Hook script: $HOOK_SCRIPT"
|
|
51
|
-
echo " 3.
|
|
52
|
-
|
|
73
|
+
echo " 3. Guardrail mode: $APORT_SELECTED_GUARDRAIL_MODE"
|
|
74
|
+
if [[ "$APORT_SELECTED_GUARDRAIL_MODE" = "api" ]]; then
|
|
75
|
+
echo " API URL: ${APORT_SELECTED_API_URL:-$DEFAULT_APORT_API_URL}"
|
|
76
|
+
fi
|
|
77
|
+
echo " 4. Mode config: $MODE_FILE"
|
|
78
|
+
echo " 5. Restart Claude Code so the PreToolUse hook is picked up."
|
|
79
|
+
echo " 6. Tool use will be checked by APort policy (exit 2 = block)."
|
|
53
80
|
echo ""
|
|
54
81
|
echo " Audit log: $config_dir/aport/audit.log"
|
|
55
82
|
echo ""
|
package/bin/frameworks/cursor.sh
CHANGED
|
@@ -12,22 +12,44 @@ source "$LIB/passport.sh"
|
|
|
12
12
|
source "$LIB/config.sh"
|
|
13
13
|
# shellcheck source=../lib/framework-setup.sh
|
|
14
14
|
source "$LIB/framework-setup.sh"
|
|
15
|
+
# shellcheck source=../lib/guardrail-mode.sh
|
|
16
|
+
source "$LIB/guardrail-mode.sh"
|
|
15
17
|
|
|
16
18
|
run_setup() {
|
|
19
|
+
parse_guardrail_mode_args "$@"
|
|
20
|
+
|
|
17
21
|
log_info "Setting up APort guardrails for Cursor..."
|
|
18
22
|
# Passport and data live under Cursor's config dir (~/.cursor/aport/ by default).
|
|
19
23
|
config_dir="$(ensure_aport_dir_secure cursor)"
|
|
20
24
|
|
|
21
25
|
export APORT_FRAMEWORK=cursor
|
|
22
26
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
+
local hosted_agent_id=""
|
|
28
|
+
if [[ -n "${APORT_HOSTED_AGENT_ID_CLI:-}" ]]; then
|
|
29
|
+
hosted_agent_id="$APORT_HOSTED_AGENT_ID_CLI"
|
|
30
|
+
export APORT_AGENT_ID="$hosted_agent_id"
|
|
31
|
+
log_info "Using hosted passport (agent_id: $hosted_agent_id) — skipping wizard."
|
|
32
|
+
else
|
|
33
|
+
# Check AGENTS.md for enforcement config — skip wizard if already configured
|
|
34
|
+
# shellcheck source=../lib/agentsmd.sh
|
|
35
|
+
source "$LIB/agentsmd.sh"
|
|
36
|
+
setup_from_agentsmd_or_wizard "${APORT_FRAMEWORK_ARGS[@]}"
|
|
37
|
+
fi
|
|
27
38
|
|
|
28
39
|
# Harden permissions on passport (contains policy/capabilities)
|
|
29
40
|
[ -f "$config_dir/aport/passport.json" ] && chmod 600 "$config_dir/aport/passport.json"
|
|
30
41
|
|
|
42
|
+
if [[ -z "$hosted_agent_id" && -n "${APORT_AGENT_ID:-}" ]]; then
|
|
43
|
+
hosted_agent_id="$APORT_AGENT_ID"
|
|
44
|
+
fi
|
|
45
|
+
|
|
46
|
+
select_guardrail_mode "cursor" "$hosted_agent_id"
|
|
47
|
+
select_guardrail_api_url "$APORT_SELECTED_GUARDRAIL_MODE"
|
|
48
|
+
if [[ "$APORT_SELECTED_GUARDRAIL_MODE" = "api" ]]; then
|
|
49
|
+
export APORT_API_URL="${APORT_SELECTED_API_URL:-$DEFAULT_APORT_API_URL}"
|
|
50
|
+
fi
|
|
51
|
+
MODE_FILE="$(write_guardrail_mode_file "$config_dir" "$APORT_SELECTED_GUARDRAIL_MODE" "${APORT_SELECTED_API_URL:-}" "$hosted_agent_id")"
|
|
52
|
+
|
|
31
53
|
# Resolve absolute path to hook script (works from repo or npx package)
|
|
32
54
|
HOOK_SCRIPT="$(resolve_hook_script_path "${APORT_CURSOR_HOOK_SCRIPT:-}" "aport-cursor-hook.sh" "$LIB")"
|
|
33
55
|
if [ ! -f "$HOOK_SCRIPT" ]; then
|
|
@@ -72,8 +94,13 @@ run_setup() {
|
|
|
72
94
|
echo " ────────────────────"
|
|
73
95
|
echo " 1. Hooks config written to: $CURSOR_HOOKS_FILE"
|
|
74
96
|
echo " 2. Hook script: $HOOK_SCRIPT"
|
|
75
|
-
echo " 3.
|
|
76
|
-
|
|
97
|
+
echo " 3. Guardrail mode: $APORT_SELECTED_GUARDRAIL_MODE"
|
|
98
|
+
if [[ "$APORT_SELECTED_GUARDRAIL_MODE" = "api" ]]; then
|
|
99
|
+
echo " API URL: ${APORT_SELECTED_API_URL:-$DEFAULT_APORT_API_URL}"
|
|
100
|
+
fi
|
|
101
|
+
echo " 4. Mode config: $MODE_FILE"
|
|
102
|
+
echo " 5. Restart Cursor (or reload window) so hooks are picked up."
|
|
103
|
+
echo " 6. Shell commands and tool use will be checked by APort policy (exit 2 = block)."
|
|
77
104
|
echo ""
|
|
78
105
|
echo " Same script works for VS Code + Copilot. For Claude Code use the dedicated integration: docs/frameworks/claude-code.md"
|
|
79
106
|
echo ""
|
|
@@ -17,31 +17,48 @@ source "$LIB/passport.sh"
|
|
|
17
17
|
source "$LIB/runtime.sh"
|
|
18
18
|
# shellcheck source=../lib/config.sh
|
|
19
19
|
source "$LIB/config.sh"
|
|
20
|
+
# shellcheck source=../lib/guardrail-mode.sh
|
|
21
|
+
source "$LIB/guardrail-mode.sh"
|
|
20
22
|
|
|
21
23
|
framework="${APORT_FRAMEWORK:?APORT_FRAMEWORK must be set by the dispatcher}"
|
|
22
24
|
crewai_integration_mode="${APORT_CREWAI_INTEGRATION_MODE:-compat}"
|
|
25
|
+
hosted_agent_id=""
|
|
23
26
|
|
|
27
|
+
parse_guardrail_mode_args "$@"
|
|
24
28
|
FORWARD_ARGS=()
|
|
25
|
-
|
|
26
|
-
|
|
29
|
+
if [[ -n "${APORT_FRAMEWORK_ARGS+x}" ]]; then
|
|
30
|
+
remaining_args=("${APORT_FRAMEWORK_ARGS[@]}")
|
|
31
|
+
else
|
|
32
|
+
remaining_args=()
|
|
33
|
+
fi
|
|
34
|
+
while [[ ${#remaining_args[@]} -gt 0 ]]; do
|
|
35
|
+
arg="${remaining_args[0]}"
|
|
36
|
+
remaining_args=("${remaining_args[@]:1}")
|
|
37
|
+
case "$arg" in
|
|
27
38
|
--integration-mode=*)
|
|
28
|
-
crewai_integration_mode="${
|
|
39
|
+
crewai_integration_mode="${arg#--integration-mode=}"
|
|
29
40
|
;;
|
|
30
41
|
--integration-mode)
|
|
31
|
-
if [[
|
|
42
|
+
if [[ ${#remaining_args[@]} -lt 1 ]]; then
|
|
32
43
|
log_error "--integration-mode requires compat or native"
|
|
33
44
|
exit 1
|
|
34
45
|
fi
|
|
35
|
-
crewai_integration_mode="$
|
|
36
|
-
|
|
46
|
+
crewai_integration_mode="${remaining_args[0]}"
|
|
47
|
+
remaining_args=("${remaining_args[@]:1}")
|
|
48
|
+
;;
|
|
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])
|
|
50
|
+
hosted_agent_id="$arg"
|
|
37
51
|
;;
|
|
38
52
|
*)
|
|
39
|
-
FORWARD_ARGS+=("$
|
|
53
|
+
FORWARD_ARGS+=("$arg")
|
|
40
54
|
;;
|
|
41
55
|
esac
|
|
42
|
-
shift
|
|
43
56
|
done
|
|
44
57
|
|
|
58
|
+
if [[ -n "${APORT_HOSTED_AGENT_ID_CLI:-}" ]]; then
|
|
59
|
+
hosted_agent_id="$APORT_HOSTED_AGENT_ID_CLI"
|
|
60
|
+
fi
|
|
61
|
+
|
|
45
62
|
case "$crewai_integration_mode" in
|
|
46
63
|
compat | native) ;;
|
|
47
64
|
*)
|
|
@@ -65,6 +82,75 @@ resolve_next_steps_file() {
|
|
|
65
82
|
echo "$FRAMEWORKS_DIR/next-steps.d/$framework.txt"
|
|
66
83
|
}
|
|
67
84
|
|
|
85
|
+
_yaml_quote() {
|
|
86
|
+
local value="${1//\'/\'\'}"
|
|
87
|
+
printf "'%s'" "$value"
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
_config_replace_or_append() {
|
|
91
|
+
local file="$1"
|
|
92
|
+
local key="$2"
|
|
93
|
+
local value="$3"
|
|
94
|
+
local tmpfile
|
|
95
|
+
|
|
96
|
+
tmpfile="$(mktemp "${file}.XXXXXX")"
|
|
97
|
+
awk -v key="$key" -v value="$value" '
|
|
98
|
+
BEGIN { done = 0 }
|
|
99
|
+
$0 ~ "^[[:space:]]*" key ":" {
|
|
100
|
+
if (!done) {
|
|
101
|
+
print key ": " value
|
|
102
|
+
done = 1
|
|
103
|
+
}
|
|
104
|
+
next
|
|
105
|
+
}
|
|
106
|
+
{ print }
|
|
107
|
+
END {
|
|
108
|
+
if (!done) {
|
|
109
|
+
print key ": " value
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
' "$file" > "$tmpfile" && mv "$tmpfile" "$file"
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
_config_remove_key() {
|
|
116
|
+
local file="$1"
|
|
117
|
+
local key="$2"
|
|
118
|
+
local tmpfile
|
|
119
|
+
|
|
120
|
+
tmpfile="$(mktemp "${file}.XXXXXX")"
|
|
121
|
+
awk -v key="$key" '$0 !~ "^[[:space:]]*" key ":" { print }' "$file" > "$tmpfile" && mv "$tmpfile" "$file"
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
persist_python_framework_config() {
|
|
125
|
+
local config_file="$1"
|
|
126
|
+
local selected_mode="$2"
|
|
127
|
+
local selected_api_url="$3"
|
|
128
|
+
local hosted_id="$4"
|
|
129
|
+
local local_passport_path="$5"
|
|
130
|
+
|
|
131
|
+
touch "$config_file"
|
|
132
|
+
_config_replace_or_append "$config_file" "framework" "$(_yaml_quote "$framework")"
|
|
133
|
+
_config_replace_or_append "$config_file" "mode" "$selected_mode"
|
|
134
|
+
|
|
135
|
+
if [[ "$selected_mode" == "api" ]]; then
|
|
136
|
+
_config_replace_or_append "$config_file" "api_url" "$(_yaml_quote "${selected_api_url:-$DEFAULT_APORT_API_URL}")"
|
|
137
|
+
if [[ -n "$hosted_id" ]]; then
|
|
138
|
+
_config_replace_or_append "$config_file" "agent_id" "$(_yaml_quote "$hosted_id")"
|
|
139
|
+
_config_remove_key "$config_file" "passport_path"
|
|
140
|
+
elif [[ -n "$local_passport_path" ]]; then
|
|
141
|
+
_config_replace_or_append "$config_file" "passport_path" "$(_yaml_quote "$local_passport_path")"
|
|
142
|
+
_config_remove_key "$config_file" "agent_id"
|
|
143
|
+
fi
|
|
144
|
+
else
|
|
145
|
+
if [[ -n "$local_passport_path" ]]; then
|
|
146
|
+
_config_replace_or_append "$config_file" "passport_path" "$(_yaml_quote "$local_passport_path")"
|
|
147
|
+
fi
|
|
148
|
+
_config_remove_key "$config_file" "agent_id"
|
|
149
|
+
fi
|
|
150
|
+
|
|
151
|
+
chmod 600 "$config_file" 2> /dev/null || true
|
|
152
|
+
}
|
|
153
|
+
|
|
68
154
|
run_setup() {
|
|
69
155
|
log_info "Setting up APort guardrails for $framework..."
|
|
70
156
|
config_dir="$(write_config_template "$framework")"
|
|
@@ -72,10 +158,15 @@ run_setup() {
|
|
|
72
158
|
chmod 700 "$config_dir/aport"
|
|
73
159
|
install_runtime_tree "$config_dir"
|
|
74
160
|
export APORT_FRAMEWORK="$framework"
|
|
161
|
+
if [[ -n "$hosted_agent_id" ]]; then
|
|
162
|
+
export APORT_AGENT_ID="$hosted_agent_id"
|
|
163
|
+
fi
|
|
75
164
|
if [[ "$framework" == "crewai" ]]; then
|
|
76
165
|
log_info "CrewAI integration mode: $crewai_integration_mode"
|
|
77
166
|
fi
|
|
78
|
-
if
|
|
167
|
+
if [[ -n "$hosted_agent_id" ]]; then
|
|
168
|
+
log_info "Using hosted passport (agent_id: $hosted_agent_id) — skipping wizard."
|
|
169
|
+
elif ((${#FORWARD_ARGS[@]} > 0)); then
|
|
79
170
|
run_passport_wizard "${FORWARD_ARGS[@]}"
|
|
80
171
|
else
|
|
81
172
|
run_passport_wizard
|
|
@@ -83,6 +174,27 @@ run_setup() {
|
|
|
83
174
|
# Harden permissions on passport (contains policy/capabilities)
|
|
84
175
|
[ -f "$config_dir/aport/passport.json" ] && chmod 600 "$config_dir/aport/passport.json"
|
|
85
176
|
log_info "Local runtime installed at: $config_dir/aport/runtime"
|
|
177
|
+
select_guardrail_mode "$framework" "$hosted_agent_id"
|
|
178
|
+
select_guardrail_api_url "$APORT_SELECTED_GUARDRAIL_MODE"
|
|
179
|
+
if [[ "$APORT_SELECTED_GUARDRAIL_MODE" = "api" ]]; then
|
|
180
|
+
export APORT_API_URL="${APORT_SELECTED_API_URL:-$DEFAULT_APORT_API_URL}"
|
|
181
|
+
fi
|
|
182
|
+
mode_file="$(write_guardrail_mode_file "$config_dir" "$APORT_SELECTED_GUARDRAIL_MODE" "${APORT_SELECTED_API_URL:-}" "$hosted_agent_id")"
|
|
183
|
+
local_passport_path=""
|
|
184
|
+
if [[ -f "$config_dir/aport/passport.json" ]]; then
|
|
185
|
+
local_passport_path="$config_dir/aport/passport.json"
|
|
186
|
+
fi
|
|
187
|
+
persist_python_framework_config \
|
|
188
|
+
"$config_dir/config.yaml" \
|
|
189
|
+
"$APORT_SELECTED_GUARDRAIL_MODE" \
|
|
190
|
+
"${APORT_SELECTED_API_URL:-}" \
|
|
191
|
+
"$hosted_agent_id" \
|
|
192
|
+
"$local_passport_path"
|
|
193
|
+
log_info "Guardrail mode: $APORT_SELECTED_GUARDRAIL_MODE"
|
|
194
|
+
if [[ "$APORT_SELECTED_GUARDRAIL_MODE" = "api" ]]; then
|
|
195
|
+
log_info "Guardrail API URL: ${APORT_SELECTED_API_URL:-$DEFAULT_APORT_API_URL}"
|
|
196
|
+
fi
|
|
197
|
+
log_info "Mode config: $mode_file"
|
|
86
198
|
|
|
87
199
|
# Print framework-specific next steps from data file
|
|
88
200
|
next_steps="$(resolve_next_steps_file)"
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Shared guardrail mode helpers for hook-based integrations (Cursor/Claude Code).
|
|
3
|
+
# Stores runtime mode under <config_dir>/aport/guardrail-mode.env so hooks can
|
|
4
|
+
# select local vs API evaluator consistently across sessions.
|
|
5
|
+
|
|
6
|
+
DEFAULT_APORT_API_URL="${DEFAULT_APORT_API_URL:-https://api.aport.io}"
|
|
7
|
+
|
|
8
|
+
parse_guardrail_mode_args() {
|
|
9
|
+
APORT_GUARDRAIL_MODE_CLI="${APORT_GUARDRAIL_MODE_CLI:-}"
|
|
10
|
+
APORT_GUARDRAIL_API_URL_CLI="${APORT_GUARDRAIL_API_URL_CLI:-}"
|
|
11
|
+
APORT_HOSTED_AGENT_ID_CLI="${APORT_HOSTED_AGENT_ID_CLI:-}"
|
|
12
|
+
APORT_FRAMEWORK_ARGS=()
|
|
13
|
+
|
|
14
|
+
while [[ $# -gt 0 ]]; do
|
|
15
|
+
case "$1" in
|
|
16
|
+
--mode=*)
|
|
17
|
+
APORT_GUARDRAIL_MODE_CLI="${1#--mode=}"
|
|
18
|
+
;;
|
|
19
|
+
--mode)
|
|
20
|
+
if [[ -z "${2:-}" ]]; then
|
|
21
|
+
echo "[aport] ERROR: --mode requires a value (local|api)" >&2
|
|
22
|
+
return 1
|
|
23
|
+
fi
|
|
24
|
+
APORT_GUARDRAIL_MODE_CLI="$2"
|
|
25
|
+
shift
|
|
26
|
+
;;
|
|
27
|
+
--api-url=*)
|
|
28
|
+
APORT_GUARDRAIL_API_URL_CLI="${1#--api-url=}"
|
|
29
|
+
;;
|
|
30
|
+
--api-url)
|
|
31
|
+
if [[ -z "${2:-}" ]]; then
|
|
32
|
+
echo "[aport] ERROR: --api-url requires a value" >&2
|
|
33
|
+
return 1
|
|
34
|
+
fi
|
|
35
|
+
APORT_GUARDRAIL_API_URL_CLI="$2"
|
|
36
|
+
shift
|
|
37
|
+
;;
|
|
38
|
+
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])
|
|
39
|
+
APORT_HOSTED_AGENT_ID_CLI="$1"
|
|
40
|
+
;;
|
|
41
|
+
*)
|
|
42
|
+
APORT_FRAMEWORK_ARGS+=("$1")
|
|
43
|
+
;;
|
|
44
|
+
esac
|
|
45
|
+
shift
|
|
46
|
+
done
|
|
47
|
+
|
|
48
|
+
export APORT_GUARDRAIL_MODE_CLI APORT_GUARDRAIL_API_URL_CLI APORT_HOSTED_AGENT_ID_CLI
|
|
49
|
+
return 0
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
select_guardrail_mode() {
|
|
53
|
+
local framework="$1"
|
|
54
|
+
local hosted_agent_id="${2:-}"
|
|
55
|
+
local noninteractive="${APORT_NONINTERACTIVE:-${CI:-}}"
|
|
56
|
+
local selected_mode="${APORT_GUARDRAIL_MODE_CLI:-${APORT_GUARDRAIL_MODE:-}}"
|
|
57
|
+
|
|
58
|
+
if [[ -n "$hosted_agent_id" ]]; then
|
|
59
|
+
APORT_SELECTED_GUARDRAIL_MODE="api"
|
|
60
|
+
export APORT_SELECTED_GUARDRAIL_MODE
|
|
61
|
+
return 0
|
|
62
|
+
fi
|
|
63
|
+
|
|
64
|
+
if [[ -n "$selected_mode" ]]; then
|
|
65
|
+
selected_mode="$(echo "$selected_mode" | tr '[:upper:]' '[:lower:]')"
|
|
66
|
+
case "$selected_mode" in
|
|
67
|
+
local | api) ;;
|
|
68
|
+
*)
|
|
69
|
+
echo "[aport] ERROR: Unsupported --mode value: $selected_mode (expected local|api)" >&2
|
|
70
|
+
return 1
|
|
71
|
+
;;
|
|
72
|
+
esac
|
|
73
|
+
APORT_SELECTED_GUARDRAIL_MODE="$selected_mode"
|
|
74
|
+
export APORT_SELECTED_GUARDRAIL_MODE
|
|
75
|
+
return 0
|
|
76
|
+
fi
|
|
77
|
+
|
|
78
|
+
if [[ -n "$noninteractive" ]]; then
|
|
79
|
+
# Keep non-interactive deterministic and offline-friendly unless explicitly overridden.
|
|
80
|
+
APORT_SELECTED_GUARDRAIL_MODE="local"
|
|
81
|
+
export APORT_SELECTED_GUARDRAIL_MODE
|
|
82
|
+
return 0
|
|
83
|
+
fi
|
|
84
|
+
|
|
85
|
+
echo ""
|
|
86
|
+
echo " Guardrail mode:"
|
|
87
|
+
echo " 1. local - Use local evaluator (offline, no network)"
|
|
88
|
+
echo " 2. api - Use APort API evaluator"
|
|
89
|
+
echo ""
|
|
90
|
+
read -r -p " Mode [1=local, 2=api]: " mode_choice
|
|
91
|
+
mode_choice="${mode_choice:-2}"
|
|
92
|
+
if [[ "$mode_choice" = "2" ]]; then
|
|
93
|
+
APORT_SELECTED_GUARDRAIL_MODE="api"
|
|
94
|
+
else
|
|
95
|
+
APORT_SELECTED_GUARDRAIL_MODE="local"
|
|
96
|
+
fi
|
|
97
|
+
export APORT_SELECTED_GUARDRAIL_MODE
|
|
98
|
+
return 0
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
select_guardrail_api_url() {
|
|
102
|
+
local mode="$1"
|
|
103
|
+
local noninteractive="${APORT_NONINTERACTIVE:-${CI:-}}"
|
|
104
|
+
local configured_url="${APORT_GUARDRAIL_API_URL_CLI:-${APORT_API_URL:-$DEFAULT_APORT_API_URL}}"
|
|
105
|
+
|
|
106
|
+
APORT_SELECTED_API_URL=""
|
|
107
|
+
if [[ "$mode" != "api" ]]; then
|
|
108
|
+
export APORT_SELECTED_API_URL
|
|
109
|
+
return 0
|
|
110
|
+
fi
|
|
111
|
+
|
|
112
|
+
if [[ -n "$noninteractive" ]]; then
|
|
113
|
+
APORT_SELECTED_API_URL="$configured_url"
|
|
114
|
+
export APORT_SELECTED_API_URL
|
|
115
|
+
return 0
|
|
116
|
+
fi
|
|
117
|
+
|
|
118
|
+
read -r -p " APort API URL [$configured_url]: " api_url_input
|
|
119
|
+
APORT_SELECTED_API_URL="${api_url_input:-$configured_url}"
|
|
120
|
+
export APORT_SELECTED_API_URL
|
|
121
|
+
return 0
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
write_guardrail_mode_file() {
|
|
125
|
+
local config_dir="$1"
|
|
126
|
+
local mode="$2"
|
|
127
|
+
local api_url="$3"
|
|
128
|
+
local hosted_agent_id="${4:-}"
|
|
129
|
+
|
|
130
|
+
local aport_dir="$config_dir/aport"
|
|
131
|
+
local mode_file="$aport_dir/guardrail-mode.env"
|
|
132
|
+
mkdir -p "$aport_dir"
|
|
133
|
+
|
|
134
|
+
{
|
|
135
|
+
echo "APORT_GUARDRAIL_MODE=$mode"
|
|
136
|
+
if [[ "$mode" = "api" ]]; then
|
|
137
|
+
echo "APORT_API_URL=${api_url:-$DEFAULT_APORT_API_URL}"
|
|
138
|
+
fi
|
|
139
|
+
if [[ -n "$hosted_agent_id" ]]; then
|
|
140
|
+
echo "APORT_AGENT_ID=$hosted_agent_id"
|
|
141
|
+
fi
|
|
142
|
+
} > "$mode_file"
|
|
143
|
+
chmod 600 "$mode_file" 2> /dev/null || true
|
|
144
|
+
echo "$mode_file"
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
load_guardrail_mode_for_hooks() {
|
|
148
|
+
local config_dir="$1"
|
|
149
|
+
local mode_file="$config_dir/aport/guardrail-mode.env"
|
|
150
|
+
if [[ -f "$mode_file" ]]; then
|
|
151
|
+
# shellcheck disable=SC1090
|
|
152
|
+
source "$mode_file"
|
|
153
|
+
fi
|
|
154
|
+
}
|