#!/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_name="$("$PY" "$JSON" get "$state" preset 2>/dev/null)" [ -n "$preset_name" ] || exit 0 preset="$CM_ROOT/presets/$preset_name.json" [ -f "$preset" ] || fail "state.json names preset '$preset_name' but $preset does not exist. Run: claude-mode presets" auth_mode="$("$PY" "$JSON" get "$preset" auth.mode 2>/dev/null)" [ -z "$auth_mode" ] && auth_mode="vault" [ "$auth_mode" = "vault" ] || exit 0 # inline token: nothing for us to emit key_ref="$("$PY" "$JSON" get "$preset" auth.keyRef 2>/dev/null)" [ -n "$key_ref" ] || key_ref="openrouter" 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"