Add shared get-github-token.sh for all agents

Single script at repo root that auto-detects GITHUB_APP_ID_* and
GITHUB_PEM_PATH_* env vars, generates a JWT, and exchanges it for a
GitHub App installation token. Contains no secrets.

Updated all heartbeats to reference the absolute path.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-15 08:58:48 -04:00
parent efaae0a266
commit 3a0fa104fd
8 changed files with 66 additions and 7 deletions
+1 -1
View File
@@ -38,7 +38,7 @@ This repo (`/paperclip/privilegedescalation`) is the canonical source of truth f
#### 4a. Authenticate with GitHub and pull latest
export GH_TOKEN=$(bash ./get-github-token.sh)
export GH_TOKEN=$(bash /paperclip/privilegedescalation/get-github-token.sh)
git -C /paperclip/privilegedescalation pull origin main
#### 4b. Detect changes since last sync
+1 -1
View File
@@ -6,7 +6,7 @@ Do these steps in order. Do not skip any. Do not ask for input.
### 0. Authenticate with GitHub
export GH_TOKEN=$(bash ./get-github-token.sh)
export GH_TOKEN=$(bash /paperclip/privilegedescalation/get-github-token.sh)
### 1. Load your operating context
+1 -1
View File
@@ -6,7 +6,7 @@ Do these steps in order. Do not skip any. Do not ask for input.
### 0. Authenticate with GitHub
export GH_TOKEN=$(bash ./get-github-token.sh)
export GH_TOKEN=$(bash /paperclip/privilegedescalation/get-github-token.sh)
### 1. Load your operating context
+1 -1
View File
@@ -6,7 +6,7 @@ Do these steps in order. Do not skip any. Do not ask for input.
### 0. Authenticate with GitHub
export GH_TOKEN=$(bash ./get-github-token.sh)
export GH_TOKEN=$(bash /paperclip/privilegedescalation/get-github-token.sh)
### 1. Load your operating context
+1 -1
View File
@@ -6,7 +6,7 @@ Do these steps in order. Do not skip any. Do not ask for input.
### 0. Authenticate with GitHub
export GH_TOKEN=$(bash ./get-github-token.sh)
export GH_TOKEN=$(bash /paperclip/privilegedescalation/get-github-token.sh)
### 1. Load your operating context
+1 -1
View File
@@ -6,7 +6,7 @@ Do these steps in order. Do not skip any. Do not ask for input.
### 0. Authenticate with GitHub
export GH_TOKEN=$(bash ./get-github-token.sh)
export GH_TOKEN=$(bash /paperclip/privilegedescalation/get-github-token.sh)
### 1. Load your operating context
+59
View File
@@ -0,0 +1,59 @@
#!/usr/bin/env bash
set -euo pipefail
#
# Generates a GitHub App installation access token.
# Reads credentials from env vars set in each agent's adapter config:
# GITHUB_APP_ID_<NAME> — the GitHub App ID
# GITHUB_PEM_PATH_<NAME> — path to the private key PEM file
#
# Usage: export GH_TOKEN=$(bash /paperclip/privilegedescalation/get-github-token.sh)
# Auto-detect credentials from env (each agent has exactly one of each)
APP_ID=$(printenv | grep '^GITHUB_APP_ID_' | head -1 | cut -d= -f2)
PEM_PATH=$(printenv | grep '^GITHUB_PEM_PATH_' | head -1 | cut -d= -f2)
if [[ -z "${APP_ID:-}" || -z "${PEM_PATH:-}" ]]; then
echo "Error: GITHUB_APP_ID_* and GITHUB_PEM_PATH_* env vars must be set" >&2
exit 1
fi
if [[ ! -f "$PEM_PATH" ]]; then
echo "Error: PEM file not found at $PEM_PATH" >&2
exit 1
fi
# --- Build JWT (RS256) ---
b64url() { openssl base64 -e -A | tr '+/' '-_' | tr -d '='; }
NOW=$(date +%s)
HEADER=$(printf '{"alg":"RS256","typ":"JWT"}' | b64url)
PAYLOAD=$(printf '{"iat":%d,"exp":%d,"iss":"%s"}' "$((NOW - 60))" "$((NOW + 600))" "$APP_ID" | b64url)
SIGNATURE=$(printf '%s.%s' "$HEADER" "$PAYLOAD" \
| openssl dgst -sha256 -sign "$PEM_PATH" | b64url)
JWT="${HEADER}.${PAYLOAD}.${SIGNATURE}"
# --- Get installation ID (first installation for this app) ---
INSTALLATION_ID=$(curl -sf \
-H "Authorization: Bearer $JWT" \
-H "Accept: application/vnd.github+json" \
https://api.github.com/app/installations \
| python3 -c "import sys,json; print(json.load(sys.stdin)[0]['id'])")
if [[ -z "$INSTALLATION_ID" ]]; then
echo "Error: Could not get installation ID for app $APP_ID" >&2
exit 1
fi
# --- Exchange for installation access token ---
TOKEN=$(curl -sf -X POST \
-H "Authorization: Bearer $JWT" \
-H "Accept: application/vnd.github+json" \
"https://api.github.com/app/installations/${INSTALLATION_ID}/access_tokens" \
| python3 -c "import sys,json; print(json.load(sys.stdin)['token'])")
if [[ -z "$TOKEN" ]]; then
echo "Error: Could not get installation access token" >&2
exit 1
fi
echo "$TOKEN"
+1 -1
View File
@@ -6,7 +6,7 @@ Do these steps in order. Do not skip any. Do not ask for input.
### 0. Authenticate with GitHub
export GH_TOKEN=$(bash ./get-github-token.sh)
export GH_TOKEN=$(bash /paperclip/privilegedescalation/get-github-token.sh)
### 1. Load your operating context