opencode
opencode copied to clipboard
feat: Add (z.ai) plan usage tracking in the sidebar
Problem
When using LLM providers with quota limits (like zai-coding-plan), users have no visibility into their remaining quota or when it resets. This makes it difficult to track usage and avoid unexpected service interruptions.
Solution
Added plan usage display to the TUI sidebar that shows:
- Current usage percentage (e.g., "65% used")
- Time until quota resets (e.g., "Resets in 2h 15m")
Implementation
Core abstraction (src/provider/plan-usage.ts)
Generic PlanUsage interface that all providers implement:
export interface PlanUsage {
used: number
total: number
resetAt?: Date
percentage?: number // Provider-supplied percentage if more accurate
}
Features:
-
Registry pattern - Provider handlers register themselves via
registerProvider(id, handler) - Built-in caching - 60s TTL to avoid excessive API calls
-
Helpers -
getPercentage(),formatResetTime()
Provider implementation (src/provider/plan-usage/zai.ts)
- Fetches quota from
/api/monitor/usage/quota/limitendpoint - Handles zai-specific response format with multiple limit types
- Only displays
TOKENS_LIMIT(ignores time-based limits) - Registers itself as
zai-coding-planhandler
UI integration (sidebar.tsx)
- Shows plan usage in session sidebar below context info
- Auto-refreshes when new messages are exchanged (user or assistant)
Adding support for other providers
The pattern uses side-effect imports. The core module imports provider modules, which register themselves on load.
Step 1: Add import to src/provider/plan-usage.ts:
// Load built-in providers
import("./plan-usage/zai")
import("./plan-usage/your-provider") // ← Add this
Step 2: Create src/provider/plan-usage/your-provider.ts:
import { registerProvider, type PlanUsageHandler } from "../plan-usage"
const handler: PlanUsageHandler = async ({ token, baseURL, timeout }) => {
const response = await fetch(`${baseURL}/your-endpoint`, {
headers: { Authorization: token },
signal: AbortSignal.timeout(timeout)
})
if (!response.ok) {
return { error: `HTTP ${response.status}` }
}
const data = await response.json()
return {
planUsage: {
used: data.used,
total: data.limit,
resetAt: new Date(data.resetsAt),
}
}
}
registerProvider("your-provider-id", handler) // ← Executes on import
The handler receives:
-
token- API key from auth -
baseURL- Provider's base URL from config -
timeout- Request timeout in ms (default 5000)
And returns:
{ planUsage: PlanUsage } | { error: string }