- PLATFORM_URL: replace unreachable http://platform:8080 mesh-only default with Docker-aware detection (host.docker.internal in containers, localhost for local dev) across all workspace Python modules and the git-token-helper shell script. - WORKSPACE_ID: add fail-fast validation in main.py (SystemExit if empty) consistent with coordinator.py / a2a_cli.py patterns already in place. - INCIDENT_LOG.md: replace all 3 F1088 credential types with ***REDACTED*** (sk-cp- 2x, github_pat_ 2x, ADMIN_TOKEN base64 3x). Fixes #1124, #1333. Co-authored-by: Molecule AI Dev Lead <dev-lead@agents.moleculesai.app>
120 lines
3.9 KiB
Bash
Executable File
120 lines
3.9 KiB
Bash
Executable File
#!/bin/bash
|
|
# molecule-git-token-helper.sh — git credential helper for GitHub App tokens
|
|
#
|
|
# Fetches a fresh GitHub App installation token from the Molecule AI
|
|
# platform endpoint GET /admin/github-installation-token on every git
|
|
# push/fetch, so workspace containers never use an expired GH_TOKEN after
|
|
# the ~60 min GitHub App token TTL.
|
|
#
|
|
# # Setup (called once at provision time or initial_prompt)
|
|
#
|
|
# git config --global \
|
|
# "credential.https://github.com.helper" \
|
|
# "!/workspace/scripts/molecule-git-token-helper.sh"
|
|
#
|
|
# # How git calls this helper
|
|
#
|
|
# git passes the action as the first positional arg. The protocol is:
|
|
# get → output credentials on stdout (we handle this)
|
|
# store → persist credentials (no-op — we never cache)
|
|
# erase → revoke credentials (no-op — platform manages lifecycle)
|
|
#
|
|
# On `get`, git reads key=value pairs terminated by an empty line.
|
|
# We must emit at minimum:
|
|
# username=x-access-token
|
|
# password=<token>
|
|
# (blank line)
|
|
#
|
|
# # Auth
|
|
#
|
|
# The platform endpoint requires a valid workspace bearer token. The
|
|
# token is stored at ${CONFIGS_DIR}/.auth_token (written by platform_auth.py
|
|
# on first /registry/register). Workspace env var PLATFORM_URL defaults
|
|
# to http://platform:8080.
|
|
#
|
|
# # Fallback
|
|
#
|
|
# If the platform endpoint is unreachable (e.g. network partition) or
|
|
# returns non-200, the script exits 1 without printing credentials so git
|
|
# will fall through to the next helper in the chain (if any). This
|
|
# preserves the operator's fallback PAT from .env if present.
|
|
#
|
|
# # gh CLI re-auth (30-min cron)
|
|
#
|
|
# To also fix `gh` CLI auth, run this from a workspace cron prompt:
|
|
#
|
|
# token=$(bash /workspace/scripts/molecule-git-token-helper.sh _fetch_token)
|
|
# echo "$token" | gh auth login --with-token
|
|
#
|
|
# (The _fetch_token private action returns only the raw token string.)
|
|
#
|
|
set -euo pipefail
|
|
|
|
PLATFORM_URL="${PLATFORM_URL:-http://host.docker.internal:8080}"
|
|
CONFIGS_DIR="${CONFIGS_DIR:-/configs}"
|
|
TOKEN_FILE="${CONFIGS_DIR}/.auth_token"
|
|
# #1068: use workspace-scoped path (WorkspaceAuth) instead of admin path
|
|
# (AdminAuth rejects workspace bearer tokens since PR #729).
|
|
WORKSPACE_ID="${WORKSPACE_ID:-}"
|
|
if [ -n "$WORKSPACE_ID" ]; then
|
|
ENDPOINT="${PLATFORM_URL}/workspaces/${WORKSPACE_ID}/github-installation-token"
|
|
else
|
|
ENDPOINT="${PLATFORM_URL}/admin/github-installation-token"
|
|
fi
|
|
|
|
# _fetch_token — internal helper; also callable directly from cron.
|
|
# Outputs the raw token string on success; exits non-zero on failure.
|
|
_fetch_token() {
|
|
if [ ! -f "${TOKEN_FILE}" ]; then
|
|
echo "[molecule-git-token-helper] .auth_token not found at ${TOKEN_FILE}" >&2
|
|
exit 1
|
|
fi
|
|
|
|
bearer=$(cat "${TOKEN_FILE}" | tr -d '[:space:]')
|
|
if [ -z "${bearer}" ]; then
|
|
echo "[molecule-git-token-helper] .auth_token is empty" >&2
|
|
exit 1
|
|
fi
|
|
|
|
response=$(curl -sf \
|
|
-H "Authorization: Bearer ${bearer}" \
|
|
-H "Accept: application/json" \
|
|
--max-time 10 \
|
|
"${ENDPOINT}" 2>&1) || {
|
|
echo "[molecule-git-token-helper] platform request failed: ${response}" >&2
|
|
exit 1
|
|
}
|
|
|
|
# Parse {"token":"ghs_...","expires_at":"..."} with sed (no jq dependency).
|
|
token=$(echo "${response}" | sed -n 's/.*"token":"\([^"]*\)".*/\1/p')
|
|
if [ -z "${token}" ]; then
|
|
echo "[molecule-git-token-helper] empty token in platform response: ${response}" >&2
|
|
exit 1
|
|
fi
|
|
|
|
echo "${token}"
|
|
}
|
|
|
|
ACTION="${1:-get}"
|
|
|
|
case "${ACTION}" in
|
|
get)
|
|
token=$(_fetch_token) || exit 1
|
|
# Emit git credential protocol response.
|
|
printf 'username=x-access-token\n'
|
|
printf 'password=%s\n' "${token}"
|
|
printf '\n'
|
|
;;
|
|
store|erase)
|
|
# No-op — the platform manages token lifecycle.
|
|
;;
|
|
_fetch_token)
|
|
# Private action for cron-based gh auth login --with-token.
|
|
_fetch_token
|
|
;;
|
|
*)
|
|
echo "[molecule-git-token-helper] unknown action: ${ACTION}" >&2
|
|
exit 1
|
|
;;
|
|
esac
|