main
Kyle Bergstedt Publish 1.1.112 8d0a92c 6d ago
#!/usr/bin/env python3
"""Codex usage in the title bar: the plan's rate-limit window as percent used, plus a
secondary window when the plan has one. Reads the Codex sign-in in ~/.codex/auth.json and asks
the usage endpoint the Codex CLI shows in /status. Asks the provider at most once every three minutes, pauses while the window is hidden, retries at 5, 15, 45 s on errors. Click the slot to cycle the views (rows, one number, a ring, the pair, both as numbers or rings)."""
import json, os, sys
from importlib.machinery import SourceFileLoader
marks = SourceFileLoader('hwmarks', os.path.join(os.path.dirname(os.path.realpath(__file__)), 'hydrogen-widget-marks.py')).load_module()
lib = SourceFileLoader('hwlib', os.path.join(os.path.dirname(os.path.realpath(__file__)), 'hydrogen-widget-lib.py')).load_module()

def data():
    if not os.path.exists(os.path.expanduser('~/.codex/auth.json')):
        return {'error': 'sign in to Codex in this workspace'}
    a = lib.read_json_if_changed('~/.codex/auth.json')
    t = a.get('tokens') or {}
    tok = t.get('access_token'); acct = t.get('account_id') or ''
    if not tok: return {'error': 'not signed in to Codex'}
    u = lib.http_json('https://chatgpt.com/backend-api/wham/usage', {'Authorization': 'Bearer ' + tok, 'ChatGPT-Account-Id': acct, 'User-Agent': 'hydrogen-widget-codex-usage', 'Accept': 'application/json'})
    rl = u.get('rate_limit') or {}
    p = rl.get('primary_window') or {}; s = rl.get('secondary_window') or {}
    def label(w):
        secs = w.get('limit_window_seconds') or 0
        return 'week' if secs >= 6*86400 else ('5h' if secs >= 4*3600 else f"{max(1, secs//3600)}h")
    items = [{'label': label(p), 'pct': p.get('used_percent', 0) or 0, 'severity': 'normal'}]
    if s: items.append({'label': label(s), 'pct': s.get('used_percent', 0) or 0, 'severity': 'normal'})
    return {'plan': u.get('plan_type'), 'items': items}

RENDER = r"""(d.error?`<span class="err">${d.error}</span>`:(d.items||[]).map(b=>bar(b.label,b.pct,b.severity)).join(''))"""
lib.serve('Codex usage', data, RENDER, 180000, mark=marks.MARKS['codex'], default_view='nums')