Files
steipete--codexbar/docs/sakana.md
T
2026-07-13 12:22:33 +08:00

131 lines
8.1 KiB
Markdown

---
summary: "Sakana AI provider: manual Cookie header, billing page parser, 5-hour/weekly quota windows, and pay-as-you-go credit balance."
read_when:
- Adding or modifying the Sakana AI provider
- Debugging Sakana AI cookie import or quota parsing
- Adjusting Sakana AI menu labels or reset window display
- Debugging Sakana pay-as-you-go credit balance or usage-total parsing
---
# Sakana AI
[Sakana AI](https://sakana.ai) is a research lab focusing on foundation models and nature-inspired AI. CodexBar reads
the billing page to surface 5-hour and weekly quota windows for subscribers.
## Setup
1. Sign in at [console.sakana.ai](https://console.sakana.ai).
2. Open your browser's developer tools, navigate to the **Network** tab, and reload the billing page
(`console.sakana.ai/billing`).
3. Copy the full `Cookie:` request header value from any billing-page request.
4. In CodexBar, paste the header in **Settings → Providers → Sakana AI → Cookie header**.
The value is stored unencrypted in the [resolved config file](configuration.md#location). CodexBar sets that file's
permissions to `0600` whenever it writes the file on macOS or Linux.
Alternatively, set the environment variable `SAKANA_COOKIE` to the raw cookie header value.
## Data source
- **Auth method**: manual `Cookie:` header; no automatic browser cookie import.
- **Target page**: `https://console.sakana.ai/billing` (HTML scrape; no JSON API).
- **Source label**: `web`.
## Usage details
- The primary row shows the **5-hour quota** as a 300-minute session window and uses the reset timestamp shown on the
billing page when one is present.
- The secondary row shows the **weekly quota** as a seven-day window and uses its billing-page reset timestamp when
one is present.
- `usedPercent` for each window is parsed from the billing page's adjacent `% used` text.
- Reset dates are parsed as **UTC**, not the device's local time zone. The billing page always server-renders
"Resets on <date>" in UTC — the browser only corrects it to the viewer's local time client-side, after JS
hydration, which this HTML-only fetcher never runs. (Parsing with `TimeZone.current` instead shifted every reset
by the device's UTC offset; see [#1826](https://github.com/steipete/CodexBar/issues/1826).) The fetcher detects
`"MMMM d, yyyy 'at' h:mm a"` format strings.
- Plan name and price label (e.g. `Standard $20/mo`) are joined and surfaced as the `loginMethod` identity field for
plan display in the menu.
- Token cost tracking (`supportsTokenCost: false`): not supported; cost summary is unavailable. Sakana has no
organization-level usage/cost API to query historically, only the per-request `usage` object returned by chat
completions calls (which CodexBar never makes), so there is no local-log source to scan the way Claude/Codex are.
- Credits row (`supportsCredits: false`): not shown. The shared credits-card UI path (`MenuCardView+Costs.swift`)
has no Sakana branch and would just render the static `creditsHint` string instead of the fetched balance, so
`supportsCredits` stays off; the balance is surfaced explicitly instead (see below).
- Widget support: not currently available for Sakana AI.
## Pay-as-you-go credits
Sakana also sells prepaid credit for pay-as-you-go API usage (the model IDs `fugu` and `fugu-ultra`), separate from
the subscription quota windows above. `console.sakana.ai/billing` renders this data server-side under its
"Pay as you go" tab, but that tab's markup is only present in the HTML response when the request URL includes
`?tab=payAsYouGo` — the default `/billing` response (used for the subscription quota fetch above) does not include
it. CodexBar issues a **second, best-effort** GET to `https://console.sakana.ai/billing?tab=payAsYouGo` with the same
cookie header alongside the subscription request — skipped entirely (no request made) when
`context.includeOptionalUsage` is `false`, i.e. Settings → Advanced → "Show optional credits and extra usage" is
disabled.
- **Credit balance**: parsed from the `<h2>Credit balance</h2>` card's adjacent `tabular-nums` amount.
- **Recent usage total**: parsed from the `Usage` chart header's `Total: $…` text, covering whatever date range is
currently selected on the console (defaults to the last 30 days). React renders this text with `<!-- -->`
hydration-boundary comments splitting the label from the amount; the parser strips those before reading the value.
- **Date range label**: the raw text of the "Usage date range" picker button (e.g. `Jun 02, 2026 - Jul 01, 2026`),
kept only as context — CodexBar does not currently interpret it as start/end dates.
This second fetch never throws and never blocks the primary result: if it fails (network error, non-200, wrong
origin, empty body, or the expected markup isn't found), the pay-as-you-go fields are simply absent from that
refresh and the subscription quota windows are returned exactly as before. An account with no pay-as-you-go credit
purchased still returns a `$0.00` balance (the card is always rendered), so absence here almost always means the
request itself failed rather than "no credit."
The optional request runs concurrently with the required subscription request and has its own five-second bound.
It is cancelled when the required request fails or the caller cancels, so it cannot add a second full request
timeout or outlive the refresh that started it.
- Menu: an `Extra usage` card shows `Balance: $X.XX` and, when available, `Usage: $X.XX` alongside the quota windows.
The values are gated on Settings → Advanced → "Show optional credits and extra usage" at **both**
the fetch and the render layer: turning the setting off only rebuilds the menu without an immediate refetch, so a
previously-fetched balance would otherwise linger in the cached snapshot until the next refresh. Both the live
menu-card model and the text descriptor hide the values whenever the setting is off, independent of cached data.
- Not shown in the menu bar text: unlike some other credits-only providers, Sakana already has real 5-hour/weekly
rate windows, and the "secondary metric" preference that would otherwise select an alternate menu bar display is
the legitimate way to show the *weekly* window there. Reusing it for the PAYG balance would silently replace the
weekly percentage for anyone who picks that preference, so the balance is menu-only for now.
## CLI usage
```
codexbar usage --provider sakana
codexbar usage --provider sakana-ai # alias
```
Set the cookie via the environment variable or Settings UI:
- **Environment variable**: `SAKANA_COOKIE=<cookie-header-value> codexbar usage --provider sakana`
- **Settings UI**: Settings → Providers → Sakana AI → Cookie header
There is no `codexbar config set` command for `cookieHeader`; use one of the paths above.
## Errors
| Error | Meaning |
|-------|---------|
| `missingCookie` | No `Cookie:` header is configured and `SAKANA_COOKIE` is unset. |
| `loginRequired` | The request was unauthorized/forbidden, redirected, or ended on a different origin. |
| `apiError(Int)` | The billing page returned a non-`200` status not classified as a login failure. |
| `parseFailed(String)` | The billing response was empty or its quota data could not be parsed. |
## Related files
- `Sources/CodexBarCore/Providers/Sakana/`
- `SakanaProviderDescriptor.swift` — provider metadata, fetch plan, CLI config
- `SakanaSettingsReader.swift``SAKANA_COOKIE` env key, cookie normalizer
- `SakanaUsageFetcher.swift` — billing-page HTML fetch and quota parser; also defines
`SakanaPayAsYouGoSnapshot` and the pay-as-you-go tab fetch/parser
- `Sources/CodexBar/Providers/Sakana/`
- `SakanaProviderImplementation.swift` — settings UI, availability check
- `SakanaSettingsStore.swift``sakanaCookieHeader` settings binding
- `Sources/CodexBar/MenuCardView+Costs.swift` — live menu-card balance and usage section
- `Sources/CodexBar/MenuDescriptor.swift` — text-descriptor balance and usage rows
- `Tests/CodexBarTests/SakanaUsageFetcherTests.swift` — parser regression tests
- Dashboard: `https://console.sakana.ai/billing` (subscription tab), `https://console.sakana.ai/billing?tab=payAsYouGo`
(pay-as-you-go tab)