165 lines
6.1 KiB
PowerShell
165 lines
6.1 KiB
PowerShell
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
|