@@ -55,9 +55,6 @@ type CCInfoTimerService struct {
5555 // Anthropic rate limit cache
5656 rateLimitCache * anthropicRateLimitCache
5757
58- // Codex rate limit cache
59- codexRateLimitCache * codexRateLimitCache
60-
6158 // User profile cache (permanent for daemon lifetime)
6259 userLogin string
6360 userLoginFetched bool
@@ -70,8 +67,7 @@ func NewCCInfoTimerService(config *model.ShellTimeConfig) *CCInfoTimerService {
7067 cache : make (map [CCInfoTimeRange ]CCInfoCache ),
7168 activeRanges : make (map [CCInfoTimeRange ]bool ),
7269 gitCache : make (map [string ]* GitCacheEntry ),
73- rateLimitCache : & anthropicRateLimitCache {},
74- codexRateLimitCache : & codexRateLimitCache {},
70+ rateLimitCache : & anthropicRateLimitCache {},
7571 stopChan : make (chan struct {}),
7672 }
7773}
@@ -156,11 +152,6 @@ func (s *CCInfoTimerService) stopTimer() {
156152 s .rateLimitCache .fetchedAt = time.Time {}
157153 s .rateLimitCache .lastAttemptAt = time.Time {}
158154 s .rateLimitCache .mu .Unlock ()
159- s .codexRateLimitCache .mu .Lock ()
160- s .codexRateLimitCache .usage = nil
161- s .codexRateLimitCache .fetchedAt = time.Time {}
162- s .codexRateLimitCache .lastAttemptAt = time.Time {}
163- s .codexRateLimitCache .mu .Unlock ()
164155
165156 slog .Info ("CC info timer stopped due to inactivity" )
166157}
@@ -180,7 +171,6 @@ func (s *CCInfoTimerService) timerLoop() {
180171 ctx , cancel := context .WithTimeout (context .Background (), 10 * time .Second )
181172 defer cancel ()
182173 s .fetchRateLimit (ctx )
183- s .fetchCodexRateLimit (ctx )
184174 }()
185175 go s .fetchUserProfile (context .Background ())
186176
@@ -204,7 +194,6 @@ func (s *CCInfoTimerService) timerLoop() {
204194 ctx , cancel := context .WithTimeout (context .Background (), 10 * time .Second )
205195 defer cancel ()
206196 s .fetchRateLimit (ctx )
207- s .fetchCodexRateLimit (ctx )
208197 }()
209198
210199 case <- s .stopChan :
@@ -562,138 +551,6 @@ func (s *CCInfoTimerService) GetCachedRateLimitError() string {
562551 return s .rateLimitCache .lastError
563552}
564553
565- // fetchCodexRateLimit fetches Codex rate limit data if cache is stale.
566- func (s * CCInfoTimerService ) fetchCodexRateLimit (ctx context.Context ) {
567- if runtime .GOOS != "darwin" && runtime .GOOS != "linux" {
568- return
569- }
570-
571- // Check cache TTL under read lock
572- s .codexRateLimitCache .mu .RLock ()
573- sinceLastFetch := time .Since (s .codexRateLimitCache .fetchedAt )
574- sinceLastAttempt := time .Since (s .codexRateLimitCache .lastAttemptAt )
575- s .codexRateLimitCache .mu .RUnlock ()
576-
577- if sinceLastFetch < codexUsageCacheTTL || sinceLastAttempt < codexUsageCacheTTL {
578- return
579- }
580-
581- // Record attempt time
582- s .codexRateLimitCache .mu .Lock ()
583- s .codexRateLimitCache .lastAttemptAt = time .Now ()
584- s .codexRateLimitCache .mu .Unlock ()
585-
586- auth , err := loadCodexAuth ()
587- if err != nil || auth == nil {
588- slog .Debug ("Failed to load Codex auth" , slog .Any ("err" , err ))
589- s .codexRateLimitCache .mu .Lock ()
590- s .codexRateLimitCache .lastError = "auth"
591- s .codexRateLimitCache .mu .Unlock ()
592- return
593- }
594-
595- usage , err := fetchCodexUsage (ctx , auth )
596- if err != nil {
597- slog .Warn ("Failed to fetch Codex usage" , slog .Any ("err" , err ))
598- s .codexRateLimitCache .mu .Lock ()
599- s .codexRateLimitCache .lastError = shortenCodexAPIError (err )
600- s .codexRateLimitCache .mu .Unlock ()
601- return
602- }
603-
604- s .codexRateLimitCache .mu .Lock ()
605- s .codexRateLimitCache .usage = usage
606- s .codexRateLimitCache .fetchedAt = time .Now ()
607- s .codexRateLimitCache .lastError = ""
608- s .codexRateLimitCache .mu .Unlock ()
609-
610- // Send usage data to server (fire-and-forget)
611- go func () {
612- bgCtx , bgCancel := context .WithTimeout (context .Background (), 10 * time .Second )
613- defer bgCancel ()
614- s .sendCodexUsageToServer (bgCtx , usage )
615- }()
616-
617- slog .Debug ("Codex rate limit updated" ,
618- slog .String ("plan" , usage .Plan ),
619- slog .Int ("windows" , len (usage .Windows )))
620- }
621-
622- // sendCodexUsageToServer sends Codex usage data to the ShellTime server
623- // for scheduling push notifications when rate limits reset.
624- func (s * CCInfoTimerService ) sendCodexUsageToServer (ctx context.Context , usage * CodexRateLimitData ) {
625- if s .config .Token == "" {
626- return
627- }
628-
629- type usageWindow struct {
630- LimitID string `json:"limit_id"`
631- UsagePercentage float64 `json:"usage_percentage"`
632- ResetsAt string `json:"resets_at"`
633- WindowDurationMinutes int `json:"window_duration_minutes"`
634- }
635- type usagePayload struct {
636- Plan string `json:"plan"`
637- Windows []usageWindow `json:"windows"`
638- }
639-
640- windows := make ([]usageWindow , len (usage .Windows ))
641- for i , w := range usage .Windows {
642- windows [i ] = usageWindow {
643- LimitID : w .LimitID ,
644- UsagePercentage : w .UsagePercentage ,
645- ResetsAt : time .Unix (w .ResetAt , 0 ).UTC ().Format (time .RFC3339 ),
646- WindowDurationMinutes : w .WindowDurationMinutes ,
647- }
648- }
649-
650- payload := usagePayload {
651- Plan : usage .Plan ,
652- Windows : windows ,
653- }
654-
655- err := model .SendHTTPRequestJSON (model.HTTPRequestOptions [usagePayload , any ]{
656- Context : ctx ,
657- Endpoint : model.Endpoint {
658- Token : s .config .Token ,
659- APIEndpoint : s .config .APIEndpoint ,
660- },
661- Method : "POST" ,
662- Path : "/api/v1/codex-usage" ,
663- Payload : payload ,
664- Timeout : 5 * time .Second ,
665- })
666- if err != nil {
667- slog .Warn ("Failed to send codex usage to server" , slog .Any ("err" , err ))
668- }
669- }
670-
671- // GetCachedCodexRateLimit returns a copy of the cached Codex rate limit data, or nil if not available.
672- func (s * CCInfoTimerService ) GetCachedCodexRateLimit () * CodexRateLimitData {
673- s .codexRateLimitCache .mu .RLock ()
674- defer s .codexRateLimitCache .mu .RUnlock ()
675-
676- if s .codexRateLimitCache .usage == nil {
677- return nil
678- }
679-
680- // Return a copy
681- copy := * s .codexRateLimitCache .usage
682- windowsCopy := make ([]CodexRateLimitWindow , len (copy .Windows ))
683- for i , w := range copy .Windows {
684- windowsCopy [i ] = w
685- }
686- copy .Windows = windowsCopy
687- return & copy
688- }
689-
690- // GetCachedCodexRateLimitError returns the last error from Codex rate limit fetching, or empty string if none.
691- func (s * CCInfoTimerService ) GetCachedCodexRateLimitError () string {
692- s .codexRateLimitCache .mu .RLock ()
693- defer s .codexRateLimitCache .mu .RUnlock ()
694- return s .codexRateLimitCache .lastError
695- }
696-
697554// shortenAPIError converts an Anthropic usage API error into a short string for statusline display.
698555func shortenAPIError (err error ) string {
699556 msg := err .Error ()
0 commit comments