fix(z-ai): wire auth_token via apiKeyHelper, never via env

Mirrors the minimax fix to z-ai. Replaces bin/anthropic-compat-wrapper.sh
(which did `exec env ANTHROPIC_AUTH_TOKEN="$(cat ...)" claude "$@"`,
exposing the secret in the claude subprocess' /proc/<pid>/environ) with
init.sh that writes ~/.claude/settings.json with:

  apiKeyHelper: "cat /run/agent/secrets/z-ai/auth_token"

Claude Code routes apiKeyHelper output to `Authorization: Bearer <value>`
when ANTHROPIC_BASE_URL is non-anthropic.com — exactly what the Z.ai
proxy at api.z.ai/api/anthropic requires.

The legacy K8s Secret may still ship a `base_url` file; it is
intentionally ignored by init.sh (the base URL is not a credential and
lives in harness.yaml).

Mirrors agent-runtimes commit (z-ai apiKeyHelper). CRS serves these
harness files to dispatchers, so this repo must match.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Paul O'Reilly
2026-05-07 13:47:52 +12:00
parent cb1b411276
commit 455d8d135b
2 changed files with 46 additions and 21 deletions

View File

@@ -1,16 +1,53 @@
#!/bin/bash
# z-ai harness init — verifies ESO secret mount exists
# M22 Phase 9 Wave 4
# z-ai init — write ~/.claude/settings.json with apiKeyHelper.
#
# The Z.ai auth_token is mounted by ESO at
# /run/agent/secrets/z-ai/auth_token (mode 0400, secrets_required entry in
# harness.yaml). Claude Code's `apiKeyHelper` setting names a command that
# prints the key on stdout when the CLI needs it for an API request — the
# value never enters this process' environment, never appears in the claude
# subprocess' /proc/<pid>/environ, and is read fresh on each invocation so
# ESO secret rotations are picked up without a process restart.
#
# When ANTHROPIC_BASE_URL points at a non-anthropic.com host (set in
# harness.yaml to https://api.z.ai/api/anthropic), Claude Code routes
# apiKeyHelper output to `Authorization: Bearer <value>`, which is the
# header shape the Z.ai proxy requires.
#
# The K8s Secret may also contain a `base_url` file (legacy from the
# wrapper-script era) — it is intentionally ignored. The base URL is not a
# credential; it lives in harness.yaml.
set -euo pipefail
echo "=== z-ai/v1 init ==="
API_KEY_FILE="/run/agent/secrets/z-ai/auth_token"
SECRETS_DIR="/run/agent/secrets/z-ai"
if [ ! -d "$SECRETS_DIR" ]; then
echo "ERROR: ESO secret mount not found at $SECRETS_DIR" >&2
echo "Has the dispatcher been configured with ESO-managed secrets?" >&2
if [ ! -r "$API_KEY_FILE" ]; then
echo "ERROR: $API_KEY_FILE not readable. Check ESO ExternalSecret acct-<z-ai-id>." >&2
exit 1
fi
echo "z-ai ESO secret mount verified at $SECRETS_DIR"
echo "=== z-ai/v1 init complete ==="
CONFIG_DIR="${CLAUDE_CONFIG_DIR:-$HOME/.claude}"
mkdir -p "$CONFIG_DIR"
chmod 0700 "$CONFIG_DIR"
SETTINGS_FILE="$CONFIG_DIR/settings.json"
# Merge into an existing settings.json (from another harness layer) when
# possible; otherwise create a fresh one.
if [ -f "$SETTINGS_FILE" ] && command -v jq >/dev/null 2>&1; then
TMP=$(mktemp)
jq --arg helper "cat $API_KEY_FILE" \
'. + {apiKeyHelper: $helper}' \
"$SETTINGS_FILE" > "$TMP"
mv "$TMP" "$SETTINGS_FILE"
else
cat > "$SETTINGS_FILE" <<EOF
{
"apiKeyHelper": "cat $API_KEY_FILE"
}
EOF
fi
chmod 0600 "$SETTINGS_FILE"
echo "z-ai auth_token wired via apiKeyHelper at $SETTINGS_FILE"