Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 55 additions & 9 deletions daemon/claude-usage-daemon.sh
Original file line number Diff line number Diff line change
Expand Up @@ -208,27 +208,73 @@ poll() {
-d '{"model":"claude-haiku-4-5-20251001","max_tokens":1,"messages":[{"role":"user","content":"hi"}]}' \
2>/dev/null) || { log "Error: API call failed"; return 1; }

local s5h_util s5h_reset s7d_util s7d_reset status
s5h_util=$(echo "$headers" | grep -i "anthropic-ratelimit-unified-5h-utilization" | tr -d '\r' | awk '{print $2}')
s5h_reset=$(echo "$headers" | grep -i "anthropic-ratelimit-unified-5h-reset" | tr -d '\r' | awk '{print $2}')
s7d_util=$(echo "$headers" | grep -i "anthropic-ratelimit-unified-7d-utilization" | tr -d '\r' | awk '{print $2}')
s7d_reset=$(echo "$headers" | grep -i "anthropic-ratelimit-unified-7d-reset" | tr -d '\r' | awk '{print $2}')
status=$(echo "$headers" | grep -i "anthropic-ratelimit-unified-5h-status" | tr -d '\r' | awk '{print $2}')
local s5h_util s5h_reset s7d_util s7d_reset s5h_status uni_status uni_reset overage_in_use overage_util status
# grep substrings are unambiguous: "unified-status" / "unified-reset" do not
# appear inside the "-5h-", "-7d-", or "-overage-" header names.
s5h_util=$(echo "$headers" | grep -i "anthropic-ratelimit-unified-5h-utilization" | tr -d '\r' | awk '{print $2}')
s5h_reset=$(echo "$headers" | grep -i "anthropic-ratelimit-unified-5h-reset" | tr -d '\r' | awk '{print $2}')
s7d_util=$(echo "$headers" | grep -i "anthropic-ratelimit-unified-7d-utilization" | tr -d '\r' | awk '{print $2}')
s7d_reset=$(echo "$headers" | grep -i "anthropic-ratelimit-unified-7d-reset" | tr -d '\r' | awk '{print $2}')
s5h_status=$(echo "$headers" | grep -i "anthropic-ratelimit-unified-5h-status" | tr -d '\r' | awk '{print $2}')
uni_status=$(echo "$headers" | grep -i "anthropic-ratelimit-unified-status" | tr -d '\r' | awk '{print $2}')
uni_reset=$(echo "$headers" | grep -i "anthropic-ratelimit-unified-reset" | tr -d '\r' | awk '{print $2}')
overage_in_use=$(echo "$headers" | grep -i "anthropic-ratelimit-unified-overage-in-use" | tr -d '\r' | awk '{print $2}')
overage_util=$(echo "$headers" | grep -i "anthropic-ratelimit-unified-overage-utilization" | tr -d '\r' | awk '{print $2}')

# Account type (DEBT.md D-3). Computed from the ORIGINAL window headers, before
# the overage block below synthesises s5h_util/s7d_util=1.0. 5h/7d present ->
# "pro-max" (Pro and Max share the 5h/7d model). No windows but overage in use
# -> "ent" (usage-based Enterprise: permanent dollar-spend overage). Default
# "pro-max" so a blank read keeps the normal Usage screen. "oiu" is the
# overage-in-use flag as a JSON bool; the device shows the "Extra Usage" title
# on pro-max when it is true.
local acct oiu_json
if [ -n "$s5h_util" ] || [ -n "$s7d_util" ]; then
acct="pro-max"
elif [ "$overage_in_use" = "true" ]; then
acct="ent"
else
acct="pro-max"
fi
if [ "$overage_in_use" = "true" ]; then oiu_json="true"; else oiu_json="false"; fi

# Overall status: prefer the unified headline, fall back to the per-window 5h status.
status="${uni_status:-${s5h_status:-unknown}}"

# Overage mode: once the subscription allowance is spent the API drops the
# -5h-/-7d- breakdown (representative-claim == "overage"), so reading only those
# windows yields all-zeros. Represent the spent subscription windows as full.
# Ref: https://github.com/anthropics/claude-code/issues/12829
if [ "$overage_in_use" = "true" ] && [ -z "$s5h_util" ] && [ -z "$s7d_util" ]; then
s5h_util=1.0
s7d_util=1.0
s5h_reset="${s5h_reset:-$uni_reset}"
s7d_reset="${s7d_reset:-$uni_reset}"
status=overage
fi

s5h_util=${s5h_util:-0}
s5h_reset=${s5h_reset:-0}
s7d_util=${s7d_util:-0}
s7d_reset=${s7d_reset:-0}
status=${status:-unknown}
# Extra-usage (overage) spend: the real figure the device's "Extra Usage" bar
# shows. Distinct from the plan bar, which reads 100% ("allowance spent"). 0
# outside overage so the device never renders a phantom Extra Usage bar.
overage_util=${overage_util:-0}
uni_reset=${uni_reset:-0}

local payload
payload=$(awk -v u5="$s5h_util" -v r5="$s5h_reset" -v u7="$s7d_util" -v r7="$s7d_reset" -v st="$status" -v now="$now" \
payload=$(awk -v u5="$s5h_util" -v r5="$s5h_reset" -v u7="$s7d_util" -v r7="$s7d_reset" \
-v ou="$overage_util" -v ur="$uni_reset" -v st="$status" -v now="$now" \
-v oiu="$oiu_json" -v acct="$acct" \
'BEGIN {
sp = sprintf("%.0f", u5 * 100);
sr = (r5 - now) / 60; sr = sr > 0 ? sprintf("%.0f", sr) : 0;
wp = sprintf("%.0f", u7 * 100);
wr = (r7 - now) / 60; wr = wr > 0 ? sprintf("%.0f", wr) : 0;
printf "{\"s\":%s,\"sr\":%s,\"w\":%s,\"wr\":%s,\"st\":\"%s\",\"ok\":true}", sp, sr, wp, wr, st;
op = sprintf("%.0f", ou * 100);
ores = (ur - now) / 60; ores = ores > 0 ? sprintf("%.0f", ores) : 0; # "or" is an awk keyword
printf "{\"s\":%s,\"sr\":%s,\"w\":%s,\"wr\":%s,\"o\":%s,\"or\":%s,\"oiu\":%s,\"acct\":\"%s\",\"st\":\"%s\",\"ok\":true}", sp, sr, wp, wr, op, ores, oiu, acct, st;
}')

log "Sending: $payload"
Expand Down
63 changes: 52 additions & 11 deletions daemon/claude_usage_daemon.py
Original file line number Diff line number Diff line change
Expand Up @@ -284,31 +284,72 @@ async def poll_api(token: str) -> dict | None:
log(f"API HTTP {resp.status_code}: {resp.text[:200]}")
return None

def hdr(name: str, default: str = "0") -> str:
return resp.headers.get(name, default)
H = "anthropic-ratelimit-unified-"

def raw(suffix: str) -> str | None:
"""Exact header value, or None when the header is absent."""
return resp.headers.get(H + suffix)

now = time.time()

def reset_minutes(reset_ts: str) -> int:
def reset_minutes(reset_ts: str | None) -> int:
try:
r = float(reset_ts)
except ValueError:
except (TypeError, ValueError):
return 0
mins = (r - now) / 60.0
return int(round(mins)) if mins > 0 else 0

def pct(util: str) -> int:
def pct(util: str | None) -> int:
try:
return int(round(float(util) * 100))
except ValueError:
except (TypeError, ValueError):
return 0

# Unified rate-limit schema: per-window utilization for the 5-hour (session)
# and 7-day (weekly) windows, with `representative-claim` naming the binding
# window (five_hour | seven_day | overage). Once the subscription allowance is
# spent the account enters OVERAGE mode: the API omits the -5h-/-7d- breakdown
# entirely, so reading only those windows yields all-zeros. Detect overage via
# `overage-in-use` and represent the spent subscription windows as full, rather
# than silently reporting 0%. Ref:
# https://github.com/anthropics/claude-code/issues/12829
s_util = raw("5h-utilization")
w_util = raw("7d-utilization")
# Capture window presence BEFORE the overage block below synthesises s/w=100,
# because acct keys on whether the API sent any 5h/7d family at all.
has_windows = s_util is not None or w_util is not None
overage_in_use = raw("overage-in-use") == "true"
# Overall status: prefer the unified headline; fall back to the per-window
# 5h-status for older responses that predate the unified-status header.
status = raw("status") or raw("5h-status") or "unknown"

if overage_in_use and s_util is None and w_util is None:
s_util = w_util = "1.0" # subscription exhausted -> both windows full
status = "overage"

# Account type (DEBT.md D-3). 5h/7d present -> "pro-max" (Pro and Max share
# the 5h/7d subscription model). No windows but overage in use -> "ent"
# (usage-based Enterprise: permanent dollar-spend overage, no windows). Default
# "pro-max" so a blank/unrecognised read keeps the normal Usage screen rather
# than the Enterprise spend gauge. The device branches screens on this.
acct = "pro-max" if has_windows else ("ent" if overage_in_use else "pro-max")

unified_reset = raw("reset") # reset for the representative claim
# Extra-usage (overage) spend: the real figure the device's "Extra Usage" bar
# shows. Distinct from the plan bar, which reads 100% ("allowance spent") once
# the subscription is exhausted. Absent outside overage -> pct(None) == 0, so
# the device never renders a phantom Extra Usage bar.
payload = {
"s": pct(hdr("anthropic-ratelimit-unified-5h-utilization")),
"sr": reset_minutes(hdr("anthropic-ratelimit-unified-5h-reset")),
"w": pct(hdr("anthropic-ratelimit-unified-7d-utilization")),
"wr": reset_minutes(hdr("anthropic-ratelimit-unified-7d-reset")),
"st": hdr("anthropic-ratelimit-unified-5h-status", "unknown"),
"s": pct(s_util),
"sr": reset_minutes(raw("5h-reset") or unified_reset),
"w": pct(w_util),
"wr": reset_minutes(raw("7d-reset") or unified_reset),
"o": pct(raw("overage-utilization")),
"or": reset_minutes(unified_reset),
"oiu": overage_in_use,
"acct": acct,
"st": status,
"ok": True,
}
return payload
Expand Down
63 changes: 52 additions & 11 deletions daemon/claude_usage_daemon_windows.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,31 +126,72 @@ async def poll_api(token: str) -> dict | None:
log(f"API HTTP {resp.status_code}: {resp.text[:200]}")
return None

def hdr(name: str, default: str = "0") -> str:
return resp.headers.get(name, default)
H = "anthropic-ratelimit-unified-"

def raw(suffix: str) -> str | None:
"""Exact header value, or None when the header is absent."""
return resp.headers.get(H + suffix)

now = time.time()

def reset_minutes(reset_ts: str) -> int:
def reset_minutes(reset_ts: str | None) -> int:
try:
r = float(reset_ts)
except ValueError:
except (TypeError, ValueError):
return 0
mins = (r - now) / 60.0
return int(round(mins)) if mins > 0 else 0

def pct(util: str) -> int:
def pct(util: str | None) -> int:
try:
return int(round(float(util) * 100))
except ValueError:
except (TypeError, ValueError):
return 0

# Unified rate-limit schema: per-window utilization for the 5-hour (session)
# and 7-day (weekly) windows, with `representative-claim` naming the binding
# window (five_hour | seven_day | overage). Once the subscription allowance is
# spent the account enters OVERAGE mode: the API omits the -5h-/-7d- breakdown
# entirely, so reading only those windows yields all-zeros. Detect overage via
# `overage-in-use` and represent the spent subscription windows as full, rather
# than silently reporting 0%. Ref:
# https://github.com/anthropics/claude-code/issues/12829
s_util = raw("5h-utilization")
w_util = raw("7d-utilization")
# Capture window presence BEFORE the overage block below synthesises s/w=100,
# because acct keys on whether the API sent any 5h/7d family at all.
has_windows = s_util is not None or w_util is not None
overage_in_use = raw("overage-in-use") == "true"
# Overall status: prefer the unified headline; fall back to the per-window
# 5h-status for older responses that predate the unified-status header.
status = raw("status") or raw("5h-status") or "unknown"

if overage_in_use and s_util is None and w_util is None:
s_util = w_util = "1.0" # subscription exhausted -> both windows full
status = "overage"

# Account type (DEBT.md D-3). 5h/7d present -> "pro-max" (Pro and Max share
# the 5h/7d subscription model). No windows but overage in use -> "ent"
# (usage-based Enterprise: permanent dollar-spend overage, no windows). Default
# "pro-max" so a blank/unrecognised read keeps the normal Usage screen rather
# than the Enterprise spend gauge. The device branches screens on this.
acct = "pro-max" if has_windows else ("ent" if overage_in_use else "pro-max")

unified_reset = raw("reset") # reset for the representative claim
# Extra-usage (overage) spend: the real figure the device's "Extra Usage" bar
# shows. Distinct from the plan bar, which reads 100% ("allowance spent") once
# the subscription is exhausted. Absent outside overage -> pct(None) == 0, so
# the device never renders a phantom Extra Usage bar.
payload = {
"s": pct(hdr("anthropic-ratelimit-unified-5h-utilization")),
"sr": reset_minutes(hdr("anthropic-ratelimit-unified-5h-reset")),
"w": pct(hdr("anthropic-ratelimit-unified-7d-utilization")),
"wr": reset_minutes(hdr("anthropic-ratelimit-unified-7d-reset")),
"st": hdr("anthropic-ratelimit-unified-5h-status", "unknown"),
"s": pct(s_util),
"sr": reset_minutes(raw("5h-reset") or unified_reset),
"w": pct(w_util),
"wr": reset_minutes(raw("7d-reset") or unified_reset),
"o": pct(raw("overage-utilization")),
"or": reset_minutes(unified_reset),
"oiu": overage_in_use,
"acct": acct,
"st": status,
"ok": True,
}
return payload
Expand Down
Loading