Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ OpenUsage lives in your menu bar and shows you how much of your AI coding subscr
- [**Kimi Code**](docs/providers/kimi.md) / session, weekly
- [**MiniMax**](docs/providers/minimax.md) / coding plan session
- [**OpenCode Go**](docs/providers/opencode-go.md) / 5h, weekly, monthly spend limits
- [**Windsurf**](docs/providers/windsurf.md) / prompt credits, flex credits
- [**Devin**](docs/providers/devin.md) / weekly quota usage, extra usage
- [**Z.ai**](docs/providers/zai.md) / session, weekly, web searches

Community contributions welcome.
Expand Down
2 changes: 1 addition & 1 deletion docs/providers/antigravity.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

> Reverse-engineered from app bundle and language server binary. May change without notice.
Antigravity is essentially a Google-branded fork of [Windsurf](windsurf.md) — both use the same Codeium language server binary and Connect-RPC protocol. The discovery, port probing, and RPC endpoints are virtually identical. The key differences: Antigravity uses fraction-based per-model quota (not credits), and doesn't require an API key in the request metadata.
Antigravity is built on Codeium/Windsurf-derived infrastructure and uses the same Codeium language server binary and Connect-RPC protocol. The discovery, port probing, and RPC endpoints are virtually identical to that stack. The key differences: Antigravity uses fraction-based per-model quota (not credits), and doesn't require an API key in the request metadata.

## Overview

Expand Down
123 changes: 123 additions & 0 deletions docs/providers/devin.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
# Devin

> Reverse-engineered from Devin CLI credentials and Devin app state. May change without notice.

## Overview

- **Vendor:** Cognition / Devin
- **Protocol:** Connect RPC v1, JSON over HTTPS
- **Service:** `exa.seat_management_pb.SeatManagementService`
- **Auth:** local Devin session token (`devin-session-token$...`)
- **Quota:** weekly quota usage/remaining percentage
- **Extra:** overage balance in micros
- **Requires:** `devin auth login` or a signed-in Devin app

OpenUsage does not use `api.devin.ai` for this provider. Devin's public API usage and consumption endpoints are enterprise/admin APIs and do not expose the same local account quota shown in the app.

## Auth Sources

The plugin checks auth in this order:

| Order | Source | Path / key |
|---|---|---|
| 1 | Devin CLI | `~/.local/share/devin/credentials.toml` |
| 2 | Devin app | `~/Library/Application Support/Devin/User/globalStorage/state.vscdb` / `windsurfAuthStatus` |

CLI credentials:

```toml
windsurf_api_key = "devin-session-token$..."
api_server_url = "https://server.codeium.com"
devin_api_url = "https://api.devin.ai"
```

Only `windsurf_api_key` and `api_server_url` are used. If `api_server_url` is missing or invalid, the plugin uses `https://server.codeium.com`.

App SQLite:

```bash
sqlite3 ~/Library/Application\ Support/Devin/User/globalStorage/state.vscdb \
"SELECT value FROM ItemTable WHERE key = 'windsurfAuthStatus'"
```

The value is JSON:

```json
{ "apiKey": "devin-session-token$..." }
```

## GetUserStatus

```http
POST https://server.codeium.com/exa.seat_management_pb.SeatManagementService/GetUserStatus
Content-Type: application/json
Connect-Protocol-Version: 1
```

Request:

```json
{
"metadata": {
"apiKey": "devin-session-token$...",
"ideName": "devin",
"ideVersion": "1.108.2",
"extensionName": "devin",
"extensionVersion": "1.108.2",
"locale": "en"
}
}
```

Response fields used:

Observed response shape, sanitized:

```json
{
"userStatus": {
"teamsTier": "TEAMS_TIER_DEVIN_MAX",
"planStatus": {
"planInfo": {
"planName": "Max",
"teamsTier": "TEAMS_TIER_DEVIN_MAX",
"hideDailyQuota": true,
"isDevin": true,
"billingStrategy": "BILLING_STRATEGY_QUOTA"
},
"availablePromptCredits": -1,
"dailyQuotaRemainingPercent": 100,
"overageBalanceMicros": "1829587876",
"dailyQuotaResetAtUnix": "1780560000",
"weeklyQuotaResetAtUnix": "1780819200"
}
},
"planInfo": {
"planName": "Max",
"hideDailyQuota": true,
"isDevin": true
}
}
```

Response fields used:

| Response field | Display |
|---|---|
| `userStatus.planStatus.planInfo.planName` | Plan label |
| `userStatus.planStatus.dailyQuotaRemainingPercent` | Weekly quota usage percent when `hideDailyQuota` is `true` and no weekly percent is present |
| `userStatus.planStatus.weeklyQuotaRemainingPercent` | Weekly quota percent if Devin starts returning it |
| `userStatus.planStatus.dailyQuotaResetAtUnix` | Daily reset time when daily quota is visible |
| `userStatus.planStatus.weeklyQuotaResetAtUnix` | Weekly reset time |
| `userStatus.planStatus.overageBalanceMicros` | Extra usage balance |
| `userStatus.planStatus.planInfo.hideDailyQuota` | Hide daily quota line when `true` |

Devin currently returns no `weeklyQuotaRemainingPercent` field in the observed payload. When `hideDailyQuota` is `true`, OpenUsage maps `dailyQuotaRemainingPercent` as a used percentage on the visible weekly line and uses `weeklyQuotaResetAtUnix` for the reset timer. Despite the field name, `100` matches Devin's `Weekly quota usage: 100%` UI, not `100% left`.

## Plugin Strategy

1. Read CLI credentials.
2. Read Devin app SQLite auth if CLI credentials are missing or different.
3. POST `GetUserStatus` with `ideName: "devin"`.
4. Build weekly quota usage and extra balance lines. Show daily quota only if Devin does not mark it hidden.
5. If auth fails, show: `Run devin auth login or sign in to Devin and try again.`
208 changes: 0 additions & 208 deletions docs/providers/windsurf.md

This file was deleted.

5 changes: 5 additions & 0 deletions plugins/devin/icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading