param( [string]$SessionFile = "", [string]$Workdir = "", [string]$CodexHome = "" ) $ErrorActionPreference = "SilentlyContinue" function Get-DefaultCodexHome { if ($env:CODEX_HOME) { return $env:CODEX_HOME } return (Join-Path $HOME ".codex") } function Format-Tokens([Int64]$Value) { if ($Value -ge 1000000) { return ("{0:0.#}M" -f ($Value / 1000000.0)) } if ($Value -ge 1000) { return ("{0:0.#}K" -f ($Value / 1000.0)) } return "$Value" } function Format-ResetTime($EpochSeconds) { if (-not $EpochSeconds) { return "--" } try { $dto = [DateTimeOffset]::FromUnixTimeSeconds([Int64]$EpochSeconds).ToLocalTime() return $dto.ToString("ddd HH:mm") } catch { return "--" } } function Format-Remaining($EpochSeconds) { if (-not $EpochSeconds) { return "--" } try { $remaining = [DateTimeOffset]::FromUnixTimeSeconds([Int64]$EpochSeconds) - [DateTimeOffset]::Now if ($remaining.TotalSeconds -le 0) { return "resetting" } if ($remaining.TotalDays -ge 1) { return ("{0}d {1}h" -f [int]$remaining.TotalDays, $remaining.Hours) } if ($remaining.TotalHours -ge 1) { return ("{0}h {1}m" -f [int]$remaining.TotalHours, $remaining.Minutes) } return ("{0}m" -f [Math]::Max(1, [int]$remaining.TotalMinutes)) } catch { return "--" } } function Get-Number($Value, [Int64]$Default = 0) { if ($null -eq $Value) { return $Default } try { return [Int64]$Value } catch { return $Default } } function Read-JsonLine([string]$Line) { if ([string]::IsNullOrWhiteSpace($Line)) { return $null } try { return ($Line | ConvertFrom-Json) } catch { return $null } } function Get-TokenEventFromObject($Obj) { if (-not $Obj) { return $null } if ($Obj.type -eq "event_msg" -and $Obj.payload.type -eq "token_count") { return $Obj.payload } if ($Obj.type -eq "token_count") { return $Obj } if ($Obj.info -and $Obj.rate_limits) { return $Obj } return $null } function Get-LatestSessionFile([string]$HomeDir) { $sessionsDir = Join-Path $HomeDir "sessions" if (-not (Test-Path $sessionsDir)) { return $null } return Get-ChildItem -Path $sessionsDir -Recurse -Filter "*.jsonl" -File | Where-Object { $_.Length -gt 0 } | Sort-Object LastWriteTime -Descending | Select-Object -First 1 } function Get-LatestTokenEvent([string]$Path) { if (-not $Path -or -not (Test-Path $Path)) { return $null } $lines = Get-Content -LiteralPath $Path -Tail 500 for ($i = $lines.Count - 1; $i -ge 0; $i--) { $obj = Read-JsonLine $lines[$i] $event = Get-TokenEventFromObject $obj if ($event) { return $event } } return $null } function Get-SessionMeta([string]$Path) { if (-not $Path -or -not (Test-Path $Path)) { return $null } foreach ($line in (Get-Content -LiteralPath $Path -TotalCount 100)) { $obj = Read-JsonLine $line if ($obj.type -eq "session_meta") { return $obj.payload } } return $null } function Get-ConfigModel([string]$HomeDir) { $config = Join-Path $HomeDir "config.toml" if (-not (Test-Path $config)) { return "codex" } $line = Get-Content -LiteralPath $config | Where-Object { $_ -match '^\s*model\s*=' } | Select-Object -First 1 if ($line -match '=\s*"([^"]+)"') { return $Matches[1] } return "codex" } if (-not $CodexHome) { $CodexHome = Get-DefaultCodexHome } $stdinText = @($input) -join "`n" $stdinObj = Read-JsonLine $stdinText $event = Get-TokenEventFromObject $stdinObj if (-not $SessionFile) { $latest = Get-LatestSessionFile $CodexHome if ($latest) { $SessionFile = $latest.FullName } } if (-not $event) { $event = Get-LatestTokenEvent $SessionFile } $meta = Get-SessionMeta $SessionFile if (-not $Workdir) { if ($stdinObj.workspace.current_dir) { $Workdir = $stdinObj.workspace.current_dir } elseif ($stdinObj.cwd) { $Workdir = $stdinObj.cwd } elseif ($meta.cwd) { $Workdir = $meta.cwd } else { $Workdir = (Get-Location).Path } } $model = if ($stdinObj.model.display_name) { $stdinObj.model.display_name } elseif ($stdinObj.model) { $stdinObj.model } elseif ($meta.model) { $meta.model } else { Get-ConfigModel $CodexHome } $project = Split-Path -Path $Workdir -Leaf if (-not $project) { $project = "?" } $branch = "-" if (Test-Path $Workdir) { $branchRaw = git -C $Workdir -c gc.auto=0 rev-parse --abbrev-ref HEAD 2>$null if ($LASTEXITCODE -eq 0 -and $branchRaw) { $branch = $branchRaw.Trim() $dirty = git -C $Workdir -c gc.auto=0 status --porcelain 2>$null | Select-Object -First 1 if ($dirty) { $branch = "$branch*" } } } $last = $event.info.last_token_usage $window = Get-Number $event.info.model_context_window $ctxUsed = (Get-Number $last.input_tokens) + (Get-Number $last.output_tokens) $ctxPct = if ($window -gt 0) { [Math]::Round(($ctxUsed * 100.0) / $window) } else { 0 } $total = $event.info.total_token_usage $sessionTokens = Get-Number $total.total_tokens $primary = $event.rate_limits.primary $secondary = $event.rate_limits.secondary $limitName = $event.rate_limits.limit_id if (-not $limitName) { $limitName = "codex" } $primaryPct = if ($primary.used_percent -ne $null) { [Math]::Round([double]$primary.used_percent) } else { 0 } $primaryReset = Format-ResetTime $primary.resets_at $primaryRemain = Format-Remaining $primary.resets_at $primaryWindow = if ($primary.window_minutes) { "{0}h" -f [Math]::Round([double]$primary.window_minutes / 60.0) } else { "window" } $line1 = "{0} | ctx {1}/{2} {3}% | session {4} | {5} ({6})" -f ` $model, (Format-Tokens $ctxUsed), (Format-Tokens $window), $ctxPct, (Format-Tokens $sessionTokens), $project, $branch $line2 = "limits {0} {1}%/{2} reset {3} ({4})" -f $limitName, $primaryPct, $primaryWindow, $primaryRemain, $primaryReset if ($secondary) { $secondaryPct = if ($secondary.used_percent -ne $null) { [Math]::Round([double]$secondary.used_percent) } else { 0 } $line2 += " | secondary {0}% reset {1}" -f $secondaryPct, (Format-Remaining $secondary.resets_at) } if ($event.rate_limits.plan_type) { $line2 += " | plan {0}" -f $event.rate_limits.plan_type } Write-Output $line1 Write-Output $line2