Rewrite in Go: static binaries, zero runtime dependencies
Some checks failed
Release / build (push) Failing after 21s
Some checks failed
Release / build (push) Failing after 21s
Replace Node.js + Python codebase with three Go binaries: - claude-statusline: CLI status bar for Claude Code - claude-fetcher: standalone cron job for API usage - claude-widget: system tray icon (fyne-io/systray + fogleman/gg) All CGO-free for trivial cross-compilation. Add nfpm .deb packaging with autostart and cron. CI pipeline produces Linux + Windows binaries, .deb, .tar.gz, and .zip release assets. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
85
internal/fetcher/cache.go
Normal file
85
internal/fetcher/cache.go
Normal file
@@ -0,0 +1,85 @@
|
||||
package fetcher
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"runtime"
|
||||
"time"
|
||||
)
|
||||
|
||||
// CacheData represents the JSON structure written to the cache file.
|
||||
// It mirrors the raw API response (five_hour, seven_day) plus error fields.
|
||||
type CacheData struct {
|
||||
FiveHour *UsageWindow `json:"five_hour,omitempty"`
|
||||
SevenDay *UsageWindow `json:"seven_day,omitempty"`
|
||||
Error string `json:"_error,omitempty"`
|
||||
Status int `json:"_status,omitempty"`
|
||||
Message string `json:"_message,omitempty"`
|
||||
}
|
||||
|
||||
// UsageWindow represents a single usage window from the API.
|
||||
type UsageWindow struct {
|
||||
Utilization float64 `json:"utilization"`
|
||||
ResetsAt string `json:"resets_at"`
|
||||
}
|
||||
|
||||
// CachePath returns the cache file path.
|
||||
func CachePath() string {
|
||||
if v := os.Getenv("CLAUDE_USAGE_CACHE"); v != "" {
|
||||
return v
|
||||
}
|
||||
if runtime.GOOS == "windows" {
|
||||
if tmp := os.Getenv("TEMP"); tmp != "" {
|
||||
return tmp + `\claude_usage.json`
|
||||
}
|
||||
return os.TempDir() + `\claude_usage.json`
|
||||
}
|
||||
return "/tmp/claude_usage.json"
|
||||
}
|
||||
|
||||
// WriteCache writes data to the cache file as JSON.
|
||||
func WriteCache(data *CacheData) error {
|
||||
b, err := json.MarshalIndent(data, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return os.WriteFile(CachePath(), b, 0o644)
|
||||
}
|
||||
|
||||
// ReadCache reads and parses the cache file. Returns data and file age.
|
||||
// Returns nil if file doesn't exist or can't be parsed.
|
||||
func ReadCache() (*CacheData, time.Duration) {
|
||||
path := CachePath()
|
||||
info, err := os.Stat(path)
|
||||
if err != nil {
|
||||
return nil, 0
|
||||
}
|
||||
age := time.Since(info.ModTime())
|
||||
|
||||
raw, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, 0
|
||||
}
|
||||
|
||||
var data CacheData
|
||||
if err := json.Unmarshal(raw, &data); err != nil {
|
||||
return nil, 0
|
||||
}
|
||||
return &data, age
|
||||
}
|
||||
|
||||
// ReadCacheIfFresh reads cache only if it's younger than maxAge.
|
||||
// Error caches are always returned regardless of age.
|
||||
func ReadCacheIfFresh(maxAge time.Duration) *CacheData {
|
||||
data, age := ReadCache()
|
||||
if data == nil {
|
||||
return nil
|
||||
}
|
||||
if data.Error != "" {
|
||||
return data
|
||||
}
|
||||
if age > maxAge {
|
||||
return nil
|
||||
}
|
||||
return data
|
||||
}
|
||||
Reference in New Issue
Block a user