gemini

package
v0.0.0-...-ed68b31 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 8, 2026 License: MIT Imports: 22 Imported by: 0

Documentation

Overview

Package gemini tracks Google Gemini prompt quota usage by reading conversation history via Gemini's internal web APIs.

Google provides no way to see how many Pro or Thinking prompts you've used today. This package counts your actual usage by authenticating with browser cookies, listing today's conversations, and classifying each turn by model type.

Authentication

All operations require a CookieProvider to supply Google session cookies. Several built-in providers are available:

Browser providers should be wrapped with CachedProvider to avoid repeated macOS Keychain prompts:

provider := gemini.CachedProvider(gemini.BraveCookies("Profile 2"))

One-shot usage

FetchUsage performs a single query and returns the current quota snapshot:

usage, err := gemini.FetchUsage(ctx, provider)
fmt.Printf("Pro: %d/%d remaining\n", usage.Pro.Remaining, usage.Pro.Limit)

Continuous tracking

NewTracker polls at a regular interval and delivers results via a channel and/or callback. It automatically refreshes cookies and stops when the context is cancelled:

tracker, err := gemini.NewTracker(ctx, provider,
    gemini.WithInterval(30*time.Second),
    gemini.WithCookieRefresh(6*time.Hour),
)
for usage := range tracker.Updates() {
    // ...
}

Configuration

Both NewClient and NewTracker accept functional options for customization: WithProLimit, WithThinkingLimit, WithResetTimezone, WithHTTPTimeout, WithLogger, WithInterval, WithCookieRefresh, and WithCallback.

Index

Constants

View Source
const (
	DefaultHTTPTimeout   = 30 * time.Second
	DefaultProLimit      = 100
	DefaultThinkingLimit = 300
	DefaultPollInterval  = 60 * time.Second
)

Defaults for client options.

Variables

View Source
var ErrAuthExpired = errors.New("gemini auth expired: cookies are no longer valid")

ErrAuthExpired indicates that the session cookies are no longer valid. The batchexecute endpoint returned a redirect or HTML login page instead of API data. Refresh cookies and retry.

Functions

func DefaultCacheDir

func DefaultCacheDir() string

DefaultCacheDir returns the directory used for cookie cache files.

Types

type CacheKeyer

type CacheKeyer interface {
	CacheKey() string
}

CacheKeyer is an optional interface that CookieProviders can implement to provide a unique key for cache file naming. CachedProvider uses this to derive a profile-specific default cache filename automatically.

type CacheOption

type CacheOption func(*cachedProvider)

CacheOption configures the CachedProvider.

func WithCachePath

func WithCachePath(path string) CacheOption

WithCachePath sets the file path for the cookie cache. Default: ~/.config/gemini-usage/cookies.json (via os.UserConfigDir).

func WithCacheTTL

func WithCacheTTL(d time.Duration) CacheOption

WithCacheTTL sets how long cached cookies are reused before refreshing from the inner provider. Default: 12 hours.

type ChatInfo

type ChatInfo struct {
	CID       string
	Title     string
	Timestamp int64 // Unix seconds
}

ChatInfo holds metadata about a conversation.

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client talks to Gemini's internal web APIs.

func NewClient

func NewClient(ctx context.Context, provider CookieProvider, opts ...ClientOption) (*Client, error)

NewClient creates a Client authenticated with the given CookieProvider. It fetches cookies, sets up the HTTP client, and extracts session tokens.

func (*Client) CountTurnsInChat

func (c *Client) CountTurnsInChat(ctx context.Context, cid string, since time.Time) (TurnCounts, error)

CountTurnsInChat reads a conversation and counts user turns by quota bucket. Only turns with a timestamp at or after `since` are counted; older turns in a resumed conversation are skipped.

func (*Client) CountUsageSince

func (c *Client) CountUsageSince(ctx context.Context, since time.Time) (TurnCounts, error)

CountUsageSince counts all user turns by quota bucket since the given time.

func (*Client) ListChats

func (c *Client) ListChats(ctx context.Context, limit int) ([]ChatInfo, error)

ListChats fetches the conversation list.

type ClientOption

type ClientOption func(*clientConfig)

ClientOption configures a Client.

func WithHTTPTimeout

func WithHTTPTimeout(d time.Duration) ClientOption

WithHTTPTimeout sets the HTTP client timeout. Default: 30s.

func WithLogger

func WithLogger(l *slog.Logger) ClientOption

WithLogger sets the structured logger. Default: slog.Default().

func WithProLimit

func WithProLimit(n int) ClientOption

WithProLimit sets the daily Pro prompt quota. Default: 100.

func WithResetTimezone

func WithResetTimezone(loc *time.Location) ClientOption

WithResetTimezone sets the timezone for daily quota resets. Default: system local timezone.

func WithThinkingLimit

func WithThinkingLimit(n int) ClientOption

WithThinkingLimit sets the daily Thinking prompt quota. Default: 300.

type CookieProvider

type CookieProvider interface {
	Cookies(ctx context.Context) ([]*http.Cookie, error)
}

CookieProvider retrieves Google cookies for Gemini authentication.

func BraveCookies

func BraveCookies(profile string) CookieProvider

BraveCookies returns a CookieProvider that reads cookies from Brave's encrypted cookie database. On macOS this triggers a Keychain prompt.

If profile is empty, it defaults to "Profile 1". Wrap with CachedProvider to avoid repeated Keychain prompts.

provider := gemini.CachedProvider(gemini.BraveCookies(""))

func CachedProvider

func CachedProvider(inner CookieProvider, opts ...CacheOption) CookieProvider

CachedProvider wraps another CookieProvider with on-disk caching. This avoids repeated Keychain prompts when using browser-based providers.

provider := gemini.CachedProvider(gemini.BraveCookies(""), gemini.WithCacheTTL(6*time.Hour))

func ChromeCookies

func ChromeCookies(profile string) CookieProvider

ChromeCookies returns a CookieProvider that reads cookies from Chrome's encrypted cookie database. On macOS this triggers a Keychain prompt.

If profile is empty, it defaults to "Default". Wrap with CachedProvider to avoid repeated Keychain prompts.

provider := gemini.CachedProvider(gemini.ChromeCookies(""))

func ExecCookies

func ExecCookies(binPath string, args ...string) CookieProvider

ExecCookies returns a CookieProvider that obtains cookies by executing an external binary and reading JSON from its stdout. This is useful for macOS launchd services where Keychain prompts cannot appear interactively: compile the helper binary (cmd/gemini-cookies) once, grant it Keychain access, and point ExecCookies at it. Recompiling your own program won't re-trigger Keychain prompts since the helper binary stays unchanged.

provider := gemini.CachedProvider(
    gemini.ExecCookies("/usr/local/bin/gemini-cookies", "-browser", "brave", "-profile", "Profile 2"),
)

func FileCookies

func FileCookies(path string) CookieProvider

FileCookies returns a CookieProvider that reads cookies from a JSON file. The file format matches the cookie cache format: either a cache object with metadata or a plain array of cookie objects.

provider := gemini.FileCookies("/path/to/cookies.json")

func StaticCookies

func StaticCookies(psid, psidts string) CookieProvider

StaticCookies returns a CookieProvider that always returns the same cookies. Use this when you already have cookie values from another source.

provider := gemini.StaticCookies("PSID_VALUE", "PSIDTS_VALUE")

type Invalidator

type Invalidator interface {
	Invalidate() error
}

Invalidator is an optional interface that CookieProviders can implement to allow cache invalidation (e.g. CachedProvider).

type QuotaInfo

type QuotaInfo struct {
	Used      int `json:"used"`
	Limit     int `json:"limit"`
	Remaining int `json:"remaining"`
}

QuotaInfo holds usage data for a single quota bucket.

type Tracker

type Tracker struct {
	// contains filtered or unexported fields
}

Tracker continuously polls Gemini usage and delivers results via a channel and/or callback. It automatically refreshes cookies on a configurable interval and on auth errors.

The Tracker stops when the context is cancelled.

func NewTracker

func NewTracker(ctx context.Context, provider CookieProvider, opts ...TrackerOption) (*Tracker, error)

NewTracker creates a Tracker that polls usage at a regular interval. The tracker starts immediately; consume results from Updates(). Cancel the context to stop the tracker.

ctx, cancel := context.WithCancel(context.Background())
defer cancel()
tracker, err := gemini.NewTracker(ctx, provider, gemini.WithInterval(30*time.Second))
for usage := range tracker.Updates() { ... }

func (*Tracker) Updates

func (t *Tracker) Updates() <-chan *Usage

Updates returns a channel that receives usage snapshots on each poll. The channel is closed when the tracker stops (context cancelled).

type TrackerOption

type TrackerOption func(*trackerConfig)

TrackerOption configures a Tracker.

func WithCallback

func WithCallback(fn func(*Usage)) TrackerOption

WithCallback sets a function called on each usage update. The callback runs in the tracker's goroutine; keep it fast.

func WithClientOptions

func WithClientOptions(opts ...ClientOption) TrackerOption

WithClientOptions passes ClientOption values through to the underlying Client.

func WithCookieRefresh

func WithCookieRefresh(d time.Duration) TrackerOption

WithCookieRefresh sets how often to re-extract cookies from the provider. This creates a fresh Client with new cookies on the given interval. Default: 6 hours.

func WithInterval

func WithInterval(d time.Duration) TrackerOption

WithInterval sets the polling interval. Default: 60s.

type TurnCounts

type TurnCounts struct {
	Pro      int
	Thinking int
	Flash    int
}

TurnCounts holds per-bucket turn counts for a chat.

type Usage

type Usage struct {
	Pro         QuotaInfo `json:"pro"`
	Thinking    QuotaInfo `json:"thinking"`
	Flash       int       `json:"flash"`
	LastUpdated time.Time `json:"last_updated"`
	Error       string    `json:"error,omitempty"`
}

Usage holds the complete usage snapshot.

func FetchUsage

func FetchUsage(ctx context.Context, provider CookieProvider, opts ...ClientOption) (*Usage, error)

FetchUsage performs a single usage fetch and returns the result. If authentication fails (including silent failures detected during API calls), it invalidates the cookie cache and retries with fresh cookies.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL