agentradar-cli 0.3.3 → 0.3.5
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/dist/secrets.js
CHANGED
|
@@ -17,7 +17,8 @@ import { join } from "node:path";
|
|
|
17
17
|
import { awpRequest, envValue } from "./tool-run.js";
|
|
18
18
|
export const SECRETS_FILE = join(".agentradar", "secrets.enc");
|
|
19
19
|
export const SECRETS_USAGE = "Usage: agentradar secrets pull|renew|env <runner-id> | agentradar secrets exec <runner-id> -- <command> [args...]"
|
|
20
|
-
+ " | agentradar secrets upload <runner-id> --env-file <path> | --from-env <NAME,...> | --prompt <NAME> [--only <NAME,...>] [--replace --approval-details '<json>'] [--token <token>]"
|
|
20
|
+
+ " | agentradar secrets upload <runner-id> --env-file <path> | --from-env <NAME,...> | --prompt <NAME> [--only <NAME,...>] [--replace --approval-details '<json>'] [--token <token>]"
|
|
21
|
+
+ " | agentradar secrets rotate <runner-id> --env-file <path> | --from-env <NAME,...> | --prompt <NAME> [--only <NAME,...>] [--token <token>]";
|
|
21
22
|
/** The names the supervisor reserves for itself (supervise-agent.sh); a role secret never overrides one. */
|
|
22
23
|
const RESERVED = /^(HOME|PATH|USER|LOGNAME|SHELL|TERM|COLORTERM|LANG|LC_[A-Z]+|TMPDIR|NODE_OPTIONS|CLAUDE_[A-Z_]+|CODEX_HOME|AR_[A-Z_]+|ANTHROPIC_API_KEY|AWP_[A-Z_]+|TMUX_SESSION|AGENT_PID_PATTERN|DISABLE_AUTOUPDATER)$/;
|
|
23
24
|
/** The runner's own origin and key: its env file, else a hosted agent's run env (as tool-run.ts awpRequest). */
|
|
@@ -161,8 +162,9 @@ async function promptHidden(name) {
|
|
|
161
162
|
/**
|
|
162
163
|
* Vadym 11074: `agentradar secrets upload`. Values come from this machine (a .env file, the environment, a hidden prompt)
|
|
163
164
|
* and never from the command line. Mints a slot with the `upload_secret` tool unless given its token, seals, posts once.
|
|
165
|
+
* SECRETS-PERMS (Vadym 11295): `agentradar secrets rotate` is the same with `rotate_secret`, for existing company secrets.
|
|
164
166
|
*/
|
|
165
|
-
export async function secretsUpload(runnerId, flags, deps = {}) {
|
|
167
|
+
export async function secretsUpload(runnerId, flags, deps = {}, tool = "upload_secret") {
|
|
166
168
|
const opts = new Map();
|
|
167
169
|
for (let i = 0; i < flags.length; i++) {
|
|
168
170
|
const flag = flags[i];
|
|
@@ -200,8 +202,8 @@ export async function secretsUpload(runnerId, flags, deps = {}) {
|
|
|
200
202
|
let token = opts.get("token");
|
|
201
203
|
if (!token) {
|
|
202
204
|
const approvalDetails = opts.has("approval-details") ? JSON.parse(opts.get("approval-details")) : undefined;
|
|
203
|
-
const said = await awpRequest(runnerId, "tools/call", { name: "
|
|
204
|
-
|
|
205
|
+
const said = await awpRequest(runnerId, "tools/call", { name: tool, arguments: tool === "rotate_secret" ? { names: Object.keys(values) }
|
|
206
|
+
: { names: Object.keys(values), replace: opts.has("replace") || undefined, approvalDetails } }, deps);
|
|
205
207
|
const minted = JSON.parse(said?.result?.content?.[0]?.text ?? "{}");
|
|
206
208
|
if (!minted.ok || !minted.token)
|
|
207
209
|
throw new Error(minted.error ?? "AgentRadar did not open an upload.");
|
|
@@ -237,6 +239,8 @@ export async function secretsOverCli(args, deps = {}) {
|
|
|
237
239
|
return await secretsExec(runnerId, command, deps);
|
|
238
240
|
if (verb === "upload" && runnerId)
|
|
239
241
|
return await secretsUpload(runnerId, args.slice(2), deps);
|
|
242
|
+
if (verb === "rotate" && runnerId)
|
|
243
|
+
return await secretsUpload(runnerId, args.slice(2), deps, "rotate_secret");
|
|
240
244
|
}
|
|
241
245
|
catch (error) {
|
|
242
246
|
(deps.logError ?? console.error)(error instanceof Error ? error.message : String(error));
|
|
@@ -89,6 +89,6 @@ else
|
|
|
89
89
|
echo "LOADED: agentradar-agent@${AGENT_NAME} (could not enable linger; run 'loginctl enable-linger ${USER:-$(id -un)}' for boot-survival)"
|
|
90
90
|
fi
|
|
91
91
|
else
|
|
92
|
-
echo "To load: install -m 644 '${UNIT}' ~/.config/systemd/user/ && systemctl --user daemon-reload && systemctl --user enable --now agentradar-agent@${AGENT_NAME} (+ loginctl enable-linger ${USER})"
|
|
92
|
+
echo "To load: install -m 644 '${UNIT}' ~/.config/systemd/user/ && systemctl --user daemon-reload && systemctl --user enable --now agentradar-agent@${AGENT_NAME} (+ loginctl enable-linger ${USER:-$(id -un)})"
|
|
93
93
|
fi
|
|
94
94
|
fi
|
|
@@ -138,6 +138,13 @@ else
|
|
|
138
138
|
# `agentradar join` pins `--resume <id>` / `--session-id <id>` so the service never resumes the vendor's
|
|
139
139
|
# own conversation in the same folder; an env file that sets nothing keeps `--continue`.
|
|
140
140
|
AR_RESUME_FLAGS="${AR_RESUME_FLAGS:---continue}"
|
|
141
|
+
# A heal (below) wrote its new session id next to the env file; a restart honors it, so it never resumes the poisoned id.
|
|
142
|
+
# Only while newer than the env file: a re-join rewrites the env file and its pin wins again.
|
|
143
|
+
_SID_FILE="${ENV_FILE}.session-id"
|
|
144
|
+
if [ -f "${_SID_FILE}" ] && [ "${_SID_FILE}" -nt "${ENV_FILE}" ] && IFS= read -r _saved_sid < "${_SID_FILE}" \
|
|
145
|
+
&& [[ "${_saved_sid}" =~ ^[0-9a-fA-F-]{36}$ ]]; then
|
|
146
|
+
AR_RESUME_FLAGS="--resume ${_saved_sid}"; AR_FRESH_FLAGS="--session-id ${_saved_sid}"
|
|
147
|
+
fi
|
|
141
148
|
: "${MCP_CONFIG:?MCP_CONFIG required (path to awp-only.json)}"
|
|
142
149
|
# SC2086: CLAUDE_EXTRA_FLAGS is intentionally UNQUOTED so "--chrome" (etc.) word-splits into flags.
|
|
143
150
|
# shellcheck disable=SC2086
|
|
@@ -332,7 +339,8 @@ ensure_agent() {
|
|
|
332
339
|
case "${AR_AGENT_HARNESS}:${AR_RESUME_FLAGS} ${AR_FRESH_FLAGS}" in
|
|
333
340
|
claude:*--resume\ *|claude:*--session-id\ *)
|
|
334
341
|
_new_sid="$(node -e 'process.stdout.write(require("crypto").randomUUID())')" || die "could not generate a fresh session id"
|
|
335
|
-
_resume_now="--session-id ${_new_sid}"; AR_RESUME_FLAGS="--resume ${_new_sid}"; AR_FRESH_FLAGS="--session-id ${_new_sid}"
|
|
342
|
+
_resume_now="--session-id ${_new_sid}"; AR_RESUME_FLAGS="--resume ${_new_sid}"; AR_FRESH_FLAGS="--session-id ${_new_sid}"
|
|
343
|
+
printf '%s\n' "${_new_sid}" > "${_SID_FILE}.tmp" && mv "${_SID_FILE}.tmp" "${_SID_FILE}" || log "WARN could not save the new session id to ${_SID_FILE}" ;;
|
|
336
344
|
esac
|
|
337
345
|
_FORCE_FRESH=0
|
|
338
346
|
fi
|
|
@@ -392,6 +400,14 @@ ensure_agent() {
|
|
|
392
400
|
# would run as a separate, UNISOLATED command in the supervisor's own shell. `bash -n` passes on
|
|
393
401
|
# that — it is semantic, not syntactic — and a colour gate cannot see it either, because a pane that
|
|
394
402
|
# never starts has nothing to colour. Caught by disc counting the args tmux actually received.
|
|
403
|
+
#
|
|
404
|
+
# The tmux CLIENT runs under `env -i` too. When this new-session starts the user's tmux server (first
|
|
405
|
+
# tmux after a reboot), the client's environment becomes the server's GLOBAL environment, inherited by
|
|
406
|
+
# every session anyone creates on that server later. Unscrubbed, the daemon's CLAUDE_CONFIG_DIR /
|
|
407
|
+
# CLAUDE_SETTINGS / plugin cache leaked into an unrelated always-on Claude session on the same Mac
|
|
408
|
+
# (2026-10-07): it read this agent's config dir, lost its trust state and transcript, and crash-looped.
|
|
409
|
+
env -i HOME="${HOME}" PATH="${PATH}" USER="${USER:-}" LANG="${LANG:-en_US.UTF-8}" \
|
|
410
|
+
LC_CTYPE="${AR_AGENT_LC_CTYPE:-en_US.UTF-8}" TMUX_TMPDIR="${TMUX_TMPDIR:-}" \
|
|
395
411
|
"${TMUX_BIN}" new-session -d -s "${TMUX_SESSION}" -c "${AR_BASE}" \
|
|
396
412
|
env -i \
|
|
397
413
|
HOME="${HOME}" PATH="${PATH}" USER="${USER:-}" LOGNAME="${LOGNAME:-}" SHELL="${SHELL:-/bin/sh}" \
|