set -euo pipefail
readonly FEATURES_URL="https://github.com/settings/copilot/features"
readonly SESSION_PREFIX="copilot-powerline-github-usage-"
run_token() {
local started
started="$(ps -o lstart= -p "$1" 2>/dev/null)" || return 0
printf '%s' "$started" | cksum | cut -d' ' -f1
}
SESSION_NAME="${SESSION_PREFIX}$$-$(run_token $$)"
readonly SESSION_NAME
profile="${COPILOT_USAGE_BROWSER_PROFILE:-${HOME}/.copilot/github-usage-profile}"
cache_file="${COPILOT_USAGE_CACHE_FILE:-${HOME}/.copilot/github-usage.json}"
history_file="${COPILOT_USAGE_HISTORY_FILE:-${HOME}/.copilot/github-usage-history.jsonl}"
html_file=""
login=false
write_cache=true
write_history=true
usage() {
cat <<'EOF'
Usage: fetch-github-copilot-usage [--login] [--html-file FILE] [--no-cache] [--no-history]
Fetch the signed-in user's GitHub Copilot usage counter and print JSON.
--login Open a persistent browser profile for one-time GitHub login.
--html-file FILE Parse a saved page text fixture instead of opening a browser.
--no-cache Do not write the result to ~/.copilot/github-usage.json.
--no-history Do not append the result to ~/.copilot/github-usage-history.jsonl.
Environment:
COPILOT_USAGE_BROWSER_PROFILE Persistent browser profile location.
COPILOT_USAGE_CACHE_FILE Output cache location.
COPILOT_USAGE_HISTORY_FILE Append-only run history location (JSON Lines).
EOF
}
while (($#)); do
case "$1" in
--login)
login=true
;;
--html-file)
shift
html_file="${1:-}"
[[ -n "$html_file" ]] || {
echo "--html-file requires a path" >&2
exit 2
}
;;
--no-cache)
write_cache=false
;;
--no-history)
write_history=false
;;
-h|--help)
usage
exit 0
;;
*)
echo "Unknown option: $1" >&2
usage >&2
exit 2
;;
esac
shift
done
close_stale_browser_sessions() {
local session pid token
for session in $(agent-browser session list 2>/dev/null |
grep -Eo "${SESSION_PREFIX}[0-9]+-[0-9]+" || true); do
token="${session##*-}"
pid="${session#"$SESSION_PREFIX"}"
pid="${pid%-*}"
[[ "$(run_token "$pid")" == "$token" ]] && continue
agent-browser --session "$session" close >/dev/null 2>&1 || true
done
}
if "$login"; then
command -v agent-browser >/dev/null || {
echo "agent-browser is required. Install it before running --login." >&2
exit 1
}
close_stale_browser_sessions
agent-browser --session "$SESSION_NAME" --profile "$profile" --headed open "$FEATURES_URL" || {
status=$?
agent-browser --session "$SESSION_NAME" close >/dev/null 2>&1 || true
exit "$status"
}
cat <<'EOF'
Sign in to GitHub in the browser window, including SSO or two-factor authentication if required.
Close the window when finished, then run this command again without --login.
EOF
exit 0
fi
if [[ -n "$html_file" ]]; then
[[ -r "$html_file" ]] || {
echo "Cannot read page text: $html_file" >&2
exit 1
}
page_text="$(<"$html_file")"
else
command -v agent-browser >/dev/null || {
echo "agent-browser is required. Run this script with --login after installing it." >&2
exit 1
}
close_stale_browser_sessions
cleanup_browser_session() {
agent-browser --session "$SESSION_NAME" close >/dev/null 2>&1 || true
}
trap cleanup_browser_session EXIT
agent-browser --session "$SESSION_NAME" --profile "$profile" open "$FEATURES_URL" >/dev/null
page_text="$(agent-browser --session "$SESSION_NAME" read)"
agent-browser --session "$SESSION_NAME" close >/dev/null
trap - EXIT
fi
if [[ "$page_text" == *"Sign in to GitHub"* ]]; then
echo "GitHub login is required. Run: $0 --login" >&2
exit 1
fi
if ! credits="$(printf '%s\n' "$page_text" |
grep -Em1 -o '[0-9][0-9,]* AI credits used' |
tr -cd '0-9')"; then
credits=""
fi
if ! cycle="$(printf '%s\n' "$page_text" |
grep -Em1 -o '[A-Z][a-z]+ [0-9]{1,2}-[0-9]{1,2}, [0-9]{4}')"; then
cycle=""
fi
[[ -n "$credits" && -n "$cycle" ]] || {
echo "Could not find Copilot usage on the features page. GitHub may have changed the page." >&2
exit 1
}
usd="$(awk -v credits="$credits" 'BEGIN { printf "%.2f", credits / 100 }')"
result="$(printf '{"ai_credits_used":%s,"usd":"%s","cycle":"%s"}' "$credits" "$usd" "$cycle")"
if "$write_cache"; then
umask 077
mkdir -p "$(dirname "$cache_file")"
temporary_cache="$(mktemp "${cache_file}.tmp.XXXXXX")"
printf '%s\n' "$result" >"$temporary_cache"
mv "$temporary_cache" "$cache_file"
fi
if "$write_history"; then
umask 077
mkdir -p "$(dirname "$history_file")"
timestamp="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
printf '{"ts":"%s","ai_credits_used":%s,"usd":"%s","cycle":"%s"}\n' \
"$timestamp" "$credits" "$usd" "$cycle" >>"$history_file"
fi
printf '%s\n' "$result"