#!/usr/bin/env bash # Invoked by Claude Code via the `apiKeyHelper` setting. Prints the API key for # the active preset to stdout and nothing else. # # Only presets whose auth mode is "vault" have a secret to emit. In anthropic # mode, or for a preset using an inline placeholder token (LM Studio), this exits # silently so a token cannot leak into a context that must not have one. set -u # Claude Code surfaces a failing helper as "your apiKeyHelper script is failing" # and shows this stream under /status. Exiting 1 in silence turns several # distinct faults into one indistinguishable message, so every failure path says # which it was. Success paths stay silent - stdout carries the key and nothing else. fail() { printf 'claude-key-helper: %s\n' "$*" >&2; exit 1; } CM_ROOT="${CM_ROOT:-$HOME/.claude-mode}" # shellcheck source=/dev/null . "$CM_ROOT/bin/cm-vault.sh" PY="${CLAUDE_MODE_PYTHON:-python3}" JSON="$CM_ROOT/bin/cm-json.py" state="$CM_ROOT/state.json" [ -f "$state" ] || exit 0 mode="$("$PY" "$JSON" get "$state" mode 2>/dev/null)" [ "$mode" = "anthropic" ] && exit 0 [ -n "$mode" ] || exit 0 # `preset rename` on the active preset moves the file while this may be running # in any live session. The name read from state.json can be the old one by the # time the file is opened, so the preset is read in a single open (a removal # after that cannot change what was read), and a miss re-reads state.json once: # the rename repoints it before removing the old name, so the second read finds # the new one. A second miss is a real fault. auth='' for _attempt in 1 2; do preset_name="$("$PY" "$JSON" get "$state" preset 2>/dev/null)" [ -n "$preset_name" ] || exit 0 preset="$CM_ROOT/presets/$preset_name.json" auth="$("$PY" "$JSON" auth-of "$preset" 2>/dev/null)" && break auth='' done [ -n "$auth" ] || fail "state.json names preset '$preset_name' but $preset does not exist. Run: claude-mode presets" auth_mode="${auth%%$'\t'*}" key_ref="${auth#*$'\t'}" [ "$auth_mode" = "vault" ] || exit 0 # inline token: nothing for us to emit cm_vault_get "$key_ref" || fail "no key readable for ref '$key_ref' from $(cm_vault_backend_label 2>/dev/null || echo 'the vault'). Run: claude-mode set-key $key_ref"