#!/usr/bin/env bash # Credential pre-flight for the agentic reviewer, in ONE definition. # # Sourced by run.sh (production) and by preflight_test.sh (the table test), so # there is no second copy to drift. An earlier version of this change had the # logic in run.sh and a duplicate in the test reconciled by a regex diff — that # guard only covered the provider table and not the decision logic below, which # is precisely the half that had the bug. # # Why pre-flight at all, when majordomo already fails closed with a 401: # without it a missing key surfaces as five identical per-lens agent failures # that name no variable, and the operator reads a stack trace to learn which # secret they forgot to forward. # gadfly_preflight_key -> echoes "" when the run may proceed, or the # name of the environment variable the operator must set. # # Scope: the REGISTRY path only — i.e. GADFLY_BASE_URL unset. That is deliberate. # With an explicit endpoint, resolveModel constructs the client directly and the # credential is GADFLY_API_KEY, falling back to the client's own default # (OPENAI_API_KEY for the openai family) — while the built-ins' own variables are # never consulted. Checking one path's rules against the other produced a # false-pass in BOTH directions across successive fixes here, so this checks the # path whose rules it can state exactly and stays silent on the other. An # override-path config is hand-written by definition; the registry path is the # one somebody hits by adding a model id to a var and forgetting the secret. gadfly_preflight_key() { local provider="$1" key_env="" key_hint="" # Only the registry path has knowable credential rules — see above. if [ -n "${GADFLY_BASE_URL:-}" ]; then echo "" return 0 fi # A provider is absent from this table for one of TWO different reasons — do # not assume the first one and add an arm: # 1. It needs no key, or carries it in its endpoint/DSN: local ollama, # llama-swap, foreman. # 2. It needs a key but accepts more than one variable, so a single-name # check would skip a correctly-configured run. **google** is this case: # GOOGLE_API_KEY *or* GEMINI_API_KEY. Adding # `google) key_env="GOOGLE_API_KEY"` would silently skip every reviewer # configured with GEMINI_API_KEY. Pre-flighting google needs an # either-variable check, not this table's one-name shape. case "$provider" in ollama-cloud) key_env="OLLAMA_API_KEY"; key_hint="OLLAMA_CLOUD_API_KEY" ;; qwen) key_env="QWEN_API_KEY"; key_hint="QWEN_API_KEY" ;; kimi) key_env="KIMI_API_KEY"; key_hint="KIMI_API_KEY" ;; openai|openai-compatible) key_env="OPENAI_API_KEY"; key_hint="OPENAI_API_KEY" ;; anthropic) key_env="ANTHROPIC_API_KEY"; key_hint="ANTHROPIC_API_KEY" ;; esac if [ -z "$key_env" ]; then echo "" # provider needs no pre-flight return 0 fi # Indirect expansion (bash). Each majordomo built-in reads ONLY its own # variable — cross-provider fallback is refused by design — so the named hint # is always the actual fix. if [ -n "${!key_env:-}" ]; then echo "" return 0 fi echo "$key_hint" }