From 45f958ad23b198f43c2887d10341ee3914510a7c Mon Sep 17 00:00:00 2001 From: AnnatarHe Date: Wed, 25 Feb 2026 01:24:03 +0800 Subject: [PATCH] feat(daemon): send Anthropic usage data to server for push notifications After fetching rate limit data from Anthropic, fire-and-forget POST to /api/v1/anthropic-usage so the server can schedule push notifications when rate limits reset. Co-Authored-By: Claude Opus 4.6 --- daemon/cc_info_timer.go | 46 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/daemon/cc_info_timer.go b/daemon/cc_info_timer.go index 43d7c3f..1944f22 100644 --- a/daemon/cc_info_timer.go +++ b/daemon/cc_info_timer.go @@ -432,6 +432,9 @@ func (s *CCInfoTimerService) fetchRateLimit(ctx context.Context) { s.rateLimitCache.fetchedAt = time.Now() s.rateLimitCache.mu.Unlock() + // Send usage data to server for push notification scheduling (fire-and-forget) + go s.sendAnthropicUsageToServer(ctx, usage) + slog.Debug("Anthropic rate limit updated", slog.Float64("5h", usage.FiveHourUtilization), slog.Float64("7d", usage.SevenDayUtilization)) @@ -472,6 +475,49 @@ func (s *CCInfoTimerService) fetchUserProfile(ctx context.Context) { slog.Debug("User profile fetched", slog.String("login", profile.FetchUser.Login)) } +// sendAnthropicUsageToServer sends the Anthropic usage data to the ShellTime server +// for scheduling push notifications when rate limits reset. +func (s *CCInfoTimerService) sendAnthropicUsageToServer(ctx context.Context, usage *AnthropicRateLimitData) { + if s.config.Token == "" { + return + } + + type usageBucket struct { + Utilization float64 `json:"utilization"` + ResetsAt string `json:"resets_at"` + } + type usagePayload struct { + FiveHour usageBucket `json:"five_hour"` + SevenDay usageBucket `json:"seven_day"` + } + + payload := usagePayload{ + FiveHour: usageBucket{ + Utilization: usage.FiveHourUtilization, + ResetsAt: usage.FiveHourResetsAt, + }, + SevenDay: usageBucket{ + Utilization: usage.SevenDayUtilization, + ResetsAt: usage.SevenDayResetsAt, + }, + } + + err := model.SendHTTPRequestJSON(model.HTTPRequestOptions[usagePayload, any]{ + Context: ctx, + Endpoint: model.Endpoint{ + Token: s.config.Token, + APIEndpoint: s.config.APIEndpoint, + }, + Method: "POST", + Path: "/api/v1/anthropic-usage", + Payload: payload, + Timeout: 5 * time.Second, + }) + if err != nil { + slog.Warn("Failed to send anthropic usage to server", slog.Any("err", err)) + } +} + // GetCachedRateLimit returns a copy of the cached rate limit data, or nil if not available. func (s *CCInfoTimerService) GetCachedRateLimit() *AnthropicRateLimitData { s.rateLimitCache.mu.RLock()