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:
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user