Something went wrong. Try again.
Configuration for my NixOS based systems and Home Manager
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209#!/usr/bin/env rcflag e +fn usage { echo 'usage: openai-usage.rcsh [-t|--token-usage]' >[1=2] echo 'Print Codex limits. Show weekly local token usage with -t.' >[1=2]}token_usage=falsewhile(! ~ $1 ()) { switch($1) { case -h --help usage exit 0 case -t --token-usage token_usage=true case * usage exit 2 } shift}codex_home=$HOME/.codexif(! ~ $CODEX_HOME ()) codex_home=$CODEX_HOMEauth=$codex_home/auth.jsonif(! test -r $auth) { echo Codex credentials not found at $auth >[1=2] echo Run 'codex login' first. >[1=2] exit 1}if(! whatis curl >/dev/null >[2=1] || ! whatis jq >/dev/null >[2=1]) { echo openai-usage.rcsh requires curl and jq. >[1=2] exit 1}token_expired=`{jq -r ' (.tokens.access_token // .access_token // empty) | split(".")[1] | @base64d | fromjson | .exp <= now' $auth}if(~ $token_expired true) { if(! whatis codex >/dev/null >[2=1]) { echo 'The Codex login has expired. Run `codex login` to refresh it.' >[1=2] exit 1 } { echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"clientInfo":{"name":"openai-usage","title":"OpenAI usage","version":"1.0.0"},"capabilities":{"experimentalApi":true}}}' sleep 1 } | codex app-server --listen stdio:// >/dev/null >[2=1]}access_token=`{jq -er '.tokens.access_token // .access_token // empty' $auth}account_id=`{jq -er '.tokens.account_id // .account_id // empty' $auth}curl_config=`{mktemp -t openai-usage.XXXXXX}response=`{mktemp -t openai-usage.XXXXXX}token_events=`{mktemp -t openai-usage.XXXXXX}terminal_columns=$COLUMNSif(~ $terminal_columns ()) terminal_columns=80if(test -t 1 && whatis tput >/dev/null >[2=1]) terminal_columns=`{tput cols}fn cleanup { rm -f $curl_config $response $token_events}fn sigexit { saved_status=$status cleanup exit $saved_status}fn sigint { cleanup exit interrupted}{ echo silent echo show-error echo fail-with-body echo 'url = "https://chatgpt.com/backend-api/wham/usage"' echo 'header = "Authorization: Bearer '^$access_token^'"' echo 'header = "ChatGPT-Account-Id: '^$account_id^'"' echo 'header = "User-Agent: codex-cli"'} >$curl_configif(! curl --config $curl_config --output $response) { echo 'Could not fetch OpenAI usage. The Codex login may have expired.' >[1=2] exit 1}# Convert each session's cumulative counters into per-request token deltas.if(~ $token_usage true && test -d $codex_home/sessions) { find $codex_home/sessions -type f -name '*.jsonl' -exec jq -Rnc ' reduce inputs as $line ({input: 0, output: 0, events: []}; ($line | try fromjson catch null) as $event | if ($event.type == "event_msg" and ($event.payload.info.total_token_usage? | type) == "object") then $event.payload.info.total_token_usage as $usage | ($usage.input_tokens // 0) as $input | ($usage.output_tokens // 0) as $output | if $input >= .input and $output >= .output then if $input > .input or $output > .output then .events += [{timestamp: $event.timestamp, input: $input - .input, output: $output - .output}] else . end else . end | .input = $input | .output = $output else . end) | .events[] ' '{}' ';' >$token_events}jqprog=`{ cat <<'jq' }def bar_width: ($terminal_columns | try tonumber catch 80) as $columns | [$columns - 3, 1] | max;def token_bar_width: ($terminal_columns | try tonumber catch 80) as $columns | [$columns - 31, 1] | max;def window_seconds: .limit_window_seconds // ((.window_minutes // 0) * 60);def reset_at: .reset_at // (now + (.reset_after_seconds // 0));def relative_time($epoch): ([$epoch - now, 0] | max | floor) as $seconds | ($seconds / 86400 | floor) as $days | (($seconds % 86400) / 3600 | floor) as $hours | (($seconds % 3600) / 60 | floor) as $minutes | if $days > 0 then "in \($days)d \($hours)h" elif $hours > 0 then "in \($hours)h \($minutes)m" else "in \($minutes)m" end; def percent: if floor == . then tostring else (. * 10 | round / 10 | tostring) end; def progress_bar($used): bar_width as $width | ([$used, 0] | max | [., 100] | min) as $bounded | ($bounded * $width / 100 | round) as $filled | "▕" + ("█" * $filled) + ("░" * ($width - $filled)) + "▏"; def tokens: if . >= 1000000 then (. / 1000000 * 10 | round / 10 | tostring) + "M" elif . >= 1000 then (. / 1000 * 10 | round / 10 | tostring) + "K" else tostring end; def token_bar($value; $maximum): token_bar_width as $width | if $maximum <= 0 then " " * $width else ($value * $width / $maximum | round) as $filled | ("█" * $filled) + ("░" * ($width - $filled)) end; def local_week: sub("\\.[0-9]+Z$"; "Z") | fromdateiso8601 | strflocaltime("%G-W%V"); def totals: reduce .[] as $event ({input: 0, output: 0}; .input += $event.input | .output += $event.output); def week_chart($events): [range(0; 8) as $offset | (now - ((7 - $offset) * 604800) | strflocaltime("%G-W%V")) as $label | ($events | map(select(.week == $label)) | totals) + {label: $label}]; def print_chart($name; $points): ($points | map([.input, .output]) | flatten | max) as $maximum | $name + " (input/output tokens)\n" + ($points | map( " \(.label) In \(.input | tokens) \(token_bar(.input; $maximum))\n" + " Out \(.output | tokens) \(token_bar(.output; $maximum))" ) | join("\n")); def print_window($name; $window): $window | reset_at as $reset | window_seconds as $duration | .used_percent as $used | "\($name) (\($duration / 3600 | percent) hours): " + "\($used | percent)% used, \([100 - $used, 0] | max | percent)% left\n" + " \(progress_bar($used))\n" + " Resets \($reset | strflocaltime("%a %Y-%m-%d %H:%M %Z")) " + "(\(relative_time($reset)))";.rate_limit as $limits| if ($limits | type) != "object" then error("usage response has no rate limits") else . end| ([$limits.primary_window, $limits.secondary_window] | map(select(type == "object"))) as $windows| ($windows | map(select(window_seconds < 604800)) | first) as $session| ($windows | map(select(window_seconds >= 604800)) | first) as $weekly| "OpenAI Codex usage (\(.plan_type // "unknown") plan)\n" + (if $session then print_window("Session"; $session) else "Session: not reported by OpenAI" end) + "\n" + (if $weekly then print_window("Weekly"; $weekly) else "Weekly: not reported by OpenAI" end) + (if $token_usage == "true" then ($token_events | map(select(.timestamp? and (.input | type) == "number" and (.output | type) == "number") | . + {week: (.timestamp | local_week)})) as $events | "\n\n" + print_chart("Weekly token usage"; week_chart($events)) else "" end)jqjq -er --slurpfile token_events $token_events --arg terminal_columns $terminal_columns --arg token_usage $token_usage $"jqprog $response