agentradar-cli 0.1.6 → 0.1.8
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 +2 -2
- package/dist/setup.js +1 -1
- package/dist/vendor-runner/supervisor/supervise-agent.sh +21 -47
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@ npx -y agentradar-cli runner start <runner-id>
|
|
|
11
11
|
npx -y agentradar-cli skill install
|
|
12
12
|
```
|
|
13
13
|
|
|
14
|
-
The setup command prints a short approval code. Approve that code in the AgentRadar vendor portal; the CLI then receives the AgentRadar bearer in the polling response body and writes the project MCP entry plus the supported runner's private config under `~/.agentradar`, with secret-bearing files mode `0600`. It prints the paths and the exact runner start command. It never prints the bearer or puts it in a URL, command argument, or repository file. The supervisor stages credentials into private runtime files, scrubs its child environment, and gives Claude Code only the strict AgentRadar MCP config.
|
|
14
|
+
The setup command prints a short approval code. Approve that code in the AgentRadar vendor portal; the CLI then receives the AgentRadar bearer in the polling response body and writes the project MCP entry plus the supported runner's private config under `~/.agentradar`, with secret-bearing files mode `0600`. It prints the paths and the exact runner start command. It never prints the bearer or puts it in a URL, command argument, or repository file. The supervisor stages only AgentRadar and vendor-tool credentials into private runtime files, scrubs its child environment, and gives Claude Code only the strict AgentRadar MCP config.
|
|
15
15
|
|
|
16
16
|
Claude Code asks you to trust the folder on first launch. `setup` does not pre-accept that for you, and the onboarding lines above deliberately do not: the `hasTrustDialogAccepted` flag trusts everything already in the directory — existing hooks, `.claude/settings.json`, skills — not just the MCP entry setup added. Dismiss the prompt yourself.
|
|
17
17
|
|
|
@@ -19,4 +19,4 @@ There is a `--trust` flag for unattended runners, where a launch prompt would ha
|
|
|
19
19
|
|
|
20
20
|
The skill command installs only the package-bundled `agentradar-vendor` markdown skill at `.claude/skills/agentradar-vendor/SKILL.md` in the current project. It does not fetch or require access to the private AgentRadar repository.
|
|
21
21
|
|
|
22
|
-
Claude authentication stays separate.
|
|
22
|
+
Claude authentication stays separate. On macOS, the runner uses the vendor's existing Keychain-backed Claude Code login through the normal user config path; it never copies, extracts, or injects Claude credentials. `CLAUDE_CODE_OAUTH_TOKEN` is intentionally unset before launch because the environment-token contract does not authenticate the interactive TUI. `ANTHROPIC_API_KEY` is prohibited and the supervisor refuses to start when it is set. Connector isolation remains enforced by `--strict-mcp-config` with the private AgentRadar MCP file.
|
package/dist/setup.js
CHANGED
|
@@ -205,7 +205,7 @@ export async function setupVendorAgent(deps = {}) {
|
|
|
205
205
|
: `Claude Code will ask you to trust ${projectPath} on first launch — approve it there. Not pre-accepted, because that would trust everything already in the folder, not just this MCP. Re-run with --trust for unattended onboarding.`);
|
|
206
206
|
log(`Runner installed → ${runnerEnv}`);
|
|
207
207
|
log(`Start it with: agentradar runner start ${approved.runnerId}`);
|
|
208
|
-
log("
|
|
208
|
+
log("On macOS the runner uses your existing Claude Code Keychain-backed login. CLAUDE_CODE_OAUTH_TOKEN is ignored; ANTHROPIC_API_KEY is prohibited.");
|
|
209
209
|
return;
|
|
210
210
|
}
|
|
211
211
|
throw new Error("AgentRadar setup code expired; run setup again");
|
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
#
|
|
10
10
|
# 1. a tmux session ($TMUX_SESSION) running the agent:
|
|
11
11
|
# claude --continue $CLAUDE_EXTRA_FLAGS --strict-mcp-config --mcp-config=$MCP_CONFIG
|
|
12
|
-
# in $AR_BASE, with a SCRUBBED allowlist env +
|
|
13
|
-
#
|
|
12
|
+
# in $AR_BASE, with a SCRUBBED allowlist env + the macOS user's normal
|
|
13
|
+
# Keychain-backed Claude session. Strict MCP flags allow ONLY AgentRadar's config;
|
|
14
14
|
# 2. the feeder ($FEEDER_JS) OUTSIDE that session, holding $AWP_BEARER, long-
|
|
15
15
|
# polling next_event and tmux-injecting each event into the pane.
|
|
16
16
|
#
|
|
@@ -51,14 +51,8 @@ set +a
|
|
|
51
51
|
: "${AR_BASE:?AR_BASE required (the agent working dir)}"
|
|
52
52
|
: "${AWP_MCP_URL:?AWP_MCP_URL required}"
|
|
53
53
|
: "${AWP_BEARER:?AWP_BEARER required}"
|
|
54
|
-
# CLAUDE_CODE_OAUTH_TOKEN is OPTIONAL. If SET → claude uses the setup-token (bills "API Usage",
|
|
55
|
-
# subject to the org spend cap). If UNSET → claude falls through to SUBSCRIPTION OAuth (keychain login,
|
|
56
|
-
# a one-time `/login`) → draws on the Max plan's included usage. Either way it's claude.ai auth only —
|
|
57
|
-
# ANTHROPIC_API_KEY is still hard-refused below (never a pay-as-you-go key).
|
|
58
|
-
: "${CLAUDE_CODE_OAUTH_TOKEN:=}"
|
|
59
54
|
: "${MCP_CONFIG:?MCP_CONFIG required (path to awp-only.json)}"
|
|
60
55
|
TMUX_SESSION="${TMUX_SESSION:-${AGENT_NAME}}"
|
|
61
|
-
CLAUDE_CONFIG_DIR="${CLAUDE_CONFIG_DIR:-${HOME}/.agentradar/config/${AGENT_NAME}}"
|
|
62
56
|
FEEDER_JS="${FEEDER_JS:-$(cd "$(dirname "$0")/.." && pwd)/feeder.mjs}"
|
|
63
57
|
PANE_LIVE_SH="${PANE_LIVE_SH:-$(cd "$(dirname "$0")/.." && pwd)/pane-live.sh}"
|
|
64
58
|
# #133 M4 live terminal — the OPTIONAL feeder bridge (streams the tmux pane to the portals). It's an
|
|
@@ -111,6 +105,11 @@ if [ -n "${AGENT_TOOL_ENV_FILE}" ]; then
|
|
|
111
105
|
log "loaded ${#TOOL_ENV_ARGS[@]} tool-env var(s) from ${AGENT_TOOL_ENV_FILE}"
|
|
112
106
|
fi
|
|
113
107
|
|
|
108
|
+
# Interactive Claude must use the user's persisted Keychain-backed login. An isolated config or an
|
|
109
|
+
# environment OAuth token selects a different auth contract and can put the TUI back into onboarding.
|
|
110
|
+
# env -i below still scrubs everything else; strict MCP flags, not CLAUDE_CONFIG_DIR, isolate connectors.
|
|
111
|
+
unset CLAUDE_CONFIG_DIR CLAUDE_CODE_OAUTH_TOKEN
|
|
112
|
+
|
|
114
113
|
# HARD RULE — OAuth only, never a pay-as-you-go API key.
|
|
115
114
|
[ -z "${ANTHROPIC_API_KEY:-}" ] || die "ANTHROPIC_API_KEY is set — refuse to start (claude.ai OAuth only)"
|
|
116
115
|
|
|
@@ -119,15 +118,12 @@ command -v "${CLAUDE_BIN}" >/dev/null 2>&1 || die "claude CLI not found (${CLAUD
|
|
|
119
118
|
[ -f "${FEEDER_JS}" ] || die "feeder not found: ${FEEDER_JS}"
|
|
120
119
|
[ -f "${PANE_LIVE_SH}" ] || die "pane liveness check not found: ${PANE_LIVE_SH}"
|
|
121
120
|
[ -d "${AR_BASE}" ] || die "AR_BASE dir not found: ${AR_BASE}"
|
|
122
|
-
mkdir -p "${CLAUDE_CONFIG_DIR}"
|
|
123
121
|
|
|
124
122
|
# --- 2b. Credential hygiene (#152): secrets to 0600 files, OUT of argv+env ------
|
|
125
|
-
# The AWP bearer
|
|
126
|
-
#
|
|
127
|
-
#
|
|
128
|
-
#
|
|
129
|
-
# Then we UNSET the raw vars from this supervisor's env so neither the supervisor nor the feeder it
|
|
130
|
-
# spawns inherits them. (The agent's own outbound bearer already lives only in awp-only.json @0600.)
|
|
123
|
+
# The AWP bearer must not linger in any long-lived process's ARGV (`ps aux`) or ENVIRONMENT
|
|
124
|
+
# (`ps eww`), where a co-resident process on the box could read it. Stage it into a per-agent 0600
|
|
125
|
+
# file and hand the feeder only the FILE PATH. Claude authenticates directly through the user's normal
|
|
126
|
+
# macOS Keychain-backed session; no Claude credential is copied, extracted, or injected.
|
|
131
127
|
_SECRETS_DIR="${HOME}/.agentradar/run/${AGENT_NAME}"
|
|
132
128
|
( umask 077; mkdir -p "${_SECRETS_DIR}" )
|
|
133
129
|
chmod 700 "${_SECRETS_DIR}" 2>/dev/null || true
|
|
@@ -135,24 +131,12 @@ _BEARER_FILE="${_SECRETS_DIR}/awp_bearer"
|
|
|
135
131
|
( umask 077; printf '%s' "${AWP_BEARER}" > "${_BEARER_FILE}" )
|
|
136
132
|
AWP_BEARER_FILE="${_BEARER_FILE}"
|
|
137
133
|
unset AWP_BEARER # drop from the supervisor's exported env → feeder no longer inherits it; ps eww clean
|
|
138
|
-
# OAuth setup-token: same treatment. `set -a; . ENV_FILE` EXPORTS CLAUDE_CODE_OAUTH_TOKEN into the
|
|
139
|
-
# supervisor env, from which the feeder (spawned WITHOUT env -i) would inherit it → ps eww leak. So when
|
|
140
|
-
# it's set, stage it to a 0600 file and UNSET the raw var here (mirror of the bearer). ensure_agent then
|
|
141
|
-
# passes ONLY AR_OAUTH_TOKEN_FILE (the path) on the scrubbed env -i line, and the sh wrapper reads it into
|
|
142
|
-
# claude's env just before exec. When UNSET (subscription-login mode) _OAUTH_TOKEN_FILE stays empty → the
|
|
143
|
-
# claude launch is a pure no-op and falls through to subscription OAuth (no API-billing regression).
|
|
144
|
-
_OAUTH_TOKEN_FILE=""
|
|
145
|
-
if [ -n "${CLAUDE_CODE_OAUTH_TOKEN:-}" ]; then
|
|
146
|
-
_OAUTH_TOKEN_FILE="${_SECRETS_DIR}/oauth_token"
|
|
147
|
-
( umask 077; printf '%s' "${CLAUDE_CODE_OAUTH_TOKEN}" > "${_OAUTH_TOKEN_FILE}" )
|
|
148
|
-
fi
|
|
149
|
-
unset CLAUDE_CODE_OAUTH_TOKEN # out of the supervisor+feeder env (ps eww); staged to the 0600 file above
|
|
150
134
|
# Vendor MCP-tool creds (#166): the agent's OWN tool API keys (WEBFLOW_TOKEN, PERPLEXITY_API_KEY, …) were
|
|
151
135
|
# being passed INLINE as KEY=VALUE on the env -i line → stored PLAINTEXT in tmux's pane_start_command
|
|
152
136
|
# (`tmux list-panes` = any co-resident proc) and in claude's env (ps eww). Same inline-secret class as the
|
|
153
137
|
# bearer/token. Stage the already-validated entries to a 0600 file and pass ONLY AR_TOOL_ENV_FILE (a PATH);
|
|
154
138
|
# the wrapper reads+exports them just before exec, so the VALUES never appear on the pane command / argv
|
|
155
|
-
# (
|
|
139
|
+
# (tool credentials remain in Claude's environment because their MCP subprocesses require them).
|
|
156
140
|
_TOOL_ENV_FILE=""
|
|
157
141
|
if [ "${#TOOL_ENV_ARGS[@]}" -gt 0 ]; then
|
|
158
142
|
_TOOL_ENV_FILE="${_SECRETS_DIR}/tool_env"
|
|
@@ -221,31 +205,22 @@ ensure_agent() {
|
|
|
221
205
|
fi
|
|
222
206
|
# SCRUBBED launch: env -i with a strict allowlist so the agent inherits nothing from this daemon
|
|
223
207
|
# (AWP_BEARER, AWP_MCP_URL, etc. never reach the pane). CONNECTOR ISOLATION is enforced by
|
|
224
|
-
# --strict-mcp-config (the agent loads ONLY $MCP_CONFIG's servers)
|
|
225
|
-
#
|
|
226
|
-
#
|
|
227
|
-
# NOT inlined here anymore (#166): they were staged to a 0600 file in 2b and are passed only as the PATH
|
|
208
|
+
# --strict-mcp-config (the agent loads ONLY $MCP_CONFIG's servers). Claude's config override stays unset,
|
|
209
|
+
# so the normal HOME/USER Keychain-backed interactive session and conversation history remain available.
|
|
210
|
+
# The agent's own tool creds (from AGENT_TOOL_ENV_FILE) are NOT inlined here anymore (#166): they were staged to a 0600 file in 2b and are passed only as the PATH
|
|
228
211
|
# AR_TOOL_ENV_FILE, which the wrapper reads+exports before exec — so no tool VALUE appears on the env -i
|
|
229
212
|
# argv / tmux pane_start_command. Reserved keys were already rejected at tool-file parse time (2b).
|
|
230
213
|
log "starting agent in tmux '${TMUX_SESSION}' (flags: ${CLAUDE_EXTRA_FLAGS:-none}; tool-env file: ${_TOOL_ENV_FILE:-none})"
|
|
231
214
|
# SC2086: CLAUDE_EXTRA_FLAGS is intentionally UNQUOTED so "--chrome" (etc.) word-splits into flags.
|
|
232
|
-
# _tool_env
|
|
215
|
+
# _tool_env uses the ${arr[@]+"${arr[@]}"} form so an EMPTY array is safe under `set -u` (bash 3.2).
|
|
233
216
|
# First-launch handling (#153/H4 durable-feeder): `claude --continue` EXITS 1 when the cwd has NO prior
|
|
234
|
-
# conversation (a fresh engagement folder
|
|
217
|
+
# conversation (for example, a fresh engagement folder) → the pane dies → the supervisor
|
|
235
218
|
# crash-loops. So try `--continue`, and FALL BACK to a fresh `claude` (which stays up AND creates the
|
|
236
219
|
# session) when there's nothing to resume. Self-healing: once a conversation exists, --continue resumes it;
|
|
237
220
|
# if it's ever lost, we cleanly start fresh instead of exit-looping. (--continue exits 1 on no-session,
|
|
238
221
|
# verified — so the `||` fallback is reliable.) The `/bin/sh -c` wrapper keeps EXTRA_FLAGS word-splitting.
|
|
239
222
|
# shellcheck disable=SC2086
|
|
240
223
|
_launch_flags=( ${CLAUDE_EXTRA_FLAGS} --strict-mcp-config --mcp-config="${MCP_CONFIG}" )
|
|
241
|
-
# OAuth setup-token (#152): the token was already staged to a 0600 file + UNSET from this env in section
|
|
242
|
-
# 2b (so it's off both the supervisor+feeder env / ps eww). Here we only pass AR_OAUTH_TOKEN_FILE (a PATH,
|
|
243
|
-
# not the secret) on the env -i line — so the token is NOT in the `env`/claude argv (`ps aux`) either. The
|
|
244
|
-
# wrapper below reads the file into CLAUDE_CODE_OAUTH_TOKEN just before exec'ing claude. When it was UNSET
|
|
245
|
-
# (subscription-login mode) _OAUTH_TOKEN_FILE is empty → we pass NOTHING token-related and the wrapper's
|
|
246
|
-
# read is a no-op → claude falls through to subscription OAuth exactly as before. bash 3.2-safe array form.
|
|
247
|
-
_oauth_env=()
|
|
248
|
-
[ -n "${_OAUTH_TOKEN_FILE}" ] && _oauth_env=( AR_OAUTH_TOKEN_FILE="${_OAUTH_TOKEN_FILE}" )
|
|
249
224
|
# #166: pass the tool creds as a FILE PATH (AR_TOOL_ENV_FILE), never inline KEY=VALUE on env -i.
|
|
250
225
|
_tool_env=()
|
|
251
226
|
[ -n "${_TOOL_ENV_FILE}" ] && _tool_env=( AR_TOOL_ENV_FILE="${_TOOL_ENV_FILE}" )
|
|
@@ -277,8 +252,8 @@ ensure_agent() {
|
|
|
277
252
|
LANG="${LANG:-en_US.UTF-8}" LC_ALL="${LC_ALL:-}" LC_CTYPE="${AR_AGENT_LC_CTYPE:-en_US.UTF-8}" \
|
|
278
253
|
TMPDIR="${TMPDIR:-/tmp}" NODE_OPTIONS="${NODE_OPTIONS:-}" \
|
|
279
254
|
DISABLE_AUTOUPDATER=1 \
|
|
280
|
-
${_tool_env[@]+"${_tool_env[@]}"}
|
|
281
|
-
/bin/sh -c 'if [ -n "${AR_TOOL_ENV_FILE:-}" ]; then while IFS= read -r _l || [ -n "$_l" ]; do case "$_l" in ""|\#*) continue ;; esac; _k=${_l%%=*}; _v=${_l#*=}; case "$_k" in HOME|PATH|USER|LOGNAME|SHELL|TERM|COLORTERM|LANG|LC_ALL|LC_CTYPE|TMPDIR|NODE_OPTIONS|CLAUDE_CONFIG_DIR|CLAUDE_CODE_OAUTH_TOKEN|AR_OAUTH_TOKEN_FILE|AR_TOOL_ENV_FILE|ANTHROPIC_API_KEY|AWP_BEARER|AWP_BEARER_FILE|AWP_MCP_URL|TMUX_SESSION|AGENT_PID_PATTERN) continue ;; esac; export "$_k=$_v"; done < "$AR_TOOL_ENV_FILE"; unset AR_TOOL_ENV_FILE _l _k _v; fi;
|
|
255
|
+
${_tool_env[@]+"${_tool_env[@]}"} \
|
|
256
|
+
/bin/sh -c 'if [ -n "${AR_TOOL_ENV_FILE:-}" ]; then while IFS= read -r _l || [ -n "$_l" ]; do case "$_l" in ""|\#*) continue ;; esac; _k=${_l%%=*}; _v=${_l#*=}; case "$_k" in HOME|PATH|USER|LOGNAME|SHELL|TERM|COLORTERM|LANG|LC_ALL|LC_CTYPE|TMPDIR|NODE_OPTIONS|CLAUDE_CONFIG_DIR|CLAUDE_CODE_OAUTH_TOKEN|AR_OAUTH_TOKEN_FILE|AR_TOOL_ENV_FILE|ANTHROPIC_API_KEY|AWP_BEARER|AWP_BEARER_FILE|AWP_MCP_URL|TMUX_SESSION|AGENT_PID_PATTERN) continue ;; esac; export "$_k=$_v"; done < "$AR_TOOL_ENV_FILE"; unset AR_TOOL_ENV_FILE _l _k _v; fi; CB="$1"; shift; "$CB" --continue "$@" || exec "$CB" "$@"' sh "${CLAUDE_BIN}" "${_launch_flags[@]}" || {
|
|
282
257
|
log "tmux could not start agent session '${TMUX_SESSION}'"
|
|
283
258
|
return 1
|
|
284
259
|
}
|
|
@@ -293,7 +268,7 @@ FEEDER_PID=""
|
|
|
293
268
|
cleanup() {
|
|
294
269
|
[ -n "${FEEDER_PID}" ] && kill "${FEEDER_PID}" 2>/dev/null || true
|
|
295
270
|
[ -n "${BRIDGE_PID:-}" ] && kill "${BRIDGE_PID}" 2>/dev/null || true # #133 terminal bridge
|
|
296
|
-
# #152: remove the staged 0600 secret files
|
|
271
|
+
# #152: remove the staged 0600 secret files on exit.
|
|
297
272
|
[ -n "${_SECRETS_DIR:-}" ] && rm -rf "${_SECRETS_DIR}" 2>/dev/null || true
|
|
298
273
|
}
|
|
299
274
|
trap cleanup EXIT INT TERM
|
|
@@ -307,8 +282,7 @@ log "starting feeder (${FEEDER_JS}) → session '${TMUX_SESSION}'"
|
|
|
307
282
|
# #152: pass AWP_BEARER_FILE (a 0600 PATH), NEVER the raw AWP_BEARER — the feeder reads the file, so the
|
|
308
283
|
# bearer is not in the feeder process's environment (`ps eww`) or argv. AWP_MCP_URL is not a secret.
|
|
309
284
|
AWP_MCP_URL="${AWP_MCP_URL}" AWP_BEARER_FILE="${AWP_BEARER_FILE}" TMUX_SESSION="${TMUX_SESSION}" \
|
|
310
|
-
AGENT_PID_PATTERN="${AGENT_PID_PATTERN}" CLAUDE_BIN="${CLAUDE_BIN}"
|
|
311
|
-
AR_OAUTH_TOKEN_FILE="${_OAUTH_TOKEN_FILE}" node "${FEEDER_JS}" &
|
|
285
|
+
AGENT_PID_PATTERN="${AGENT_PID_PATTERN}" CLAUDE_BIN="${CLAUDE_BIN}" node "${FEEDER_JS}" &
|
|
312
286
|
FEEDER_PID=$!
|
|
313
287
|
log "feeder pid ${FEEDER_PID}"
|
|
314
288
|
|