1
0
Fork 0
gibby/.opencode/plans/1775725199925-glowing-orchid.md
2026-04-20 23:41:44 +03:00

188 lines
4.9 KiB
Markdown

# Plan: rewrite token selection around a simple disk-first state model
## Goal
Throw away the current layered selection/cooldown/state model and replace it with a small implementation that:
- reads the main JSON file on every `/token` request
- keeps only the minimum necessary account fields on disk
- decides from file state first
- refreshes usage only when missing or stale
- validates the selected token before returning it
- moves invalid accounts to `failed.json`
- does not touch the helper scripts in this pass
## Required file model
### Main state file
`accounts.json`
```json
{
"active_account": "user@example.com",
"accounts": [
{
"email": "user@example.com",
"access_token": "...",
"refresh_token": "...",
"token_refresh_at": 1710000000,
"usage": {
"primary": {
"used_percent": 72,
"reset_at": 1710018000
},
"secondary": {
"used_percent": 18,
"reset_at": 1710600000
}
},
"usage_checked_at": 1710000000,
"disabled": false
}
]
}
```
Only these fields should exist for account state.
### Failed state file
`failed.json`
```json
{
"accounts": [
{
"email": "bad@example.com",
"access_token": "...",
"refresh_token": "...",
"token_refresh_at": 1710000000,
"usage": {
"primary": {
"used_percent": 100,
"reset_at": 1710018000
},
"secondary": {
"used_percent": 100,
"reset_at": 1710600000
}
},
"usage_checked_at": 1710000000,
"disabled": false
}
]
}
```
Top-level must contain only `accounts`.
## Selection rules
### Active account first
For each `/token` request:
1. Read `accounts.json` fresh from disk.
2. Resolve `active_account` by email.
3. Evaluate active first.
### When an account is usable
An account is usable when:
- `disabled == false`
- `secondary.used_percent < 100`
- `primary.used_percent < GIBBY_EXHAUSTED_USAGE_THRESHOLD`
Default threshold remains `95`.
### Usage freshness
Usage must be refreshed only when missing or stale.
Add env:
- `GIBBY_USAGE_STALE_SECONDS`, default `3600`
Usage is stale when:
- `usage` is missing
- `usage_checked_at` is missing
- `now - usage_checked_at > GIBBY_USAGE_STALE_SECONDS`
If active account usage is stale or missing, refresh usage for that account before deciding if it is usable.
### Fallback selection
If active account cannot be used, choose the next account by:
- filtering to usable accounts
- sorting by highest primary `used_percent`
- using file order as the tie-breaker
If a new account is chosen, write its email into `active_account` in `accounts.json`.
## Token flow
For the chosen account:
1. Ensure token is fresh enough.
2. If `token_refresh_at` says refresh is needed, refresh token and persist new values.
3. After selection decisions are finished and the token is ready, validate it by calling:
`https://chatgpt.com/backend-api/codex/models`
4. Only return the token if validation returns `200`.
## Invalid account handling
If refresh, usage auth, or final validation shows the token/account is invalid:
1. Read current main state.
2. Remove that full account object from `accounts.json`.
3. Append the same full account object to `failed.json.accounts`.
4. If it was the active account, clear `active_account` before reselection.
5. Persist both files atomically.
No `failed.txt` in the rewritten core flow.
## Files to rewrite
- `/home/wzray/AI/gibby/src/gibby/settings.py`
- keep only env needed for the new flow
- `/home/wzray/AI/gibby/src/gibby/store.py`
- rewrite as simple JSON read/write helpers for `accounts.json` and `failed.json`
- `/home/wzray/AI/gibby/src/gibby/client.py`
- keep only token refresh, usage fetch, and token validation calls
- `/home/wzray/AI/gibby/src/gibby/manager.py`
- rewrite into one small service for `/token`
- `/home/wzray/AI/gibby/src/gibby/app.py`
- keep thin FastAPI wiring for `/health` and `/token`
## Files to remove or stop using
- `/home/wzray/AI/gibby/src/gibby/models.py`
- `/home/wzray/AI/gibby/src/gibby/account_ops.py`
Their logic should be folded into the new minimal data model and service flow instead of preserved.
## Out of scope for this pass
- do not touch `scripts/oauth_helper.py`
- do not touch `scripts/refresh_limits.py`
- do not preserve old cooldown, failed.txt, dual-state, or derived snapshot machinery unless absolutely required to keep app booting during rewrite
## Verification
- `uv run pytest -q`
- API tests for:
- `/health` returns `ok`
- `/token` returns `503` when file has no usable accounts
- `/token` prefers active account when usable
- `/token` rereads the file between requests
- stale usage triggers a refresh before decision
- fresh usage skips refresh
- invalid token moves full account object to `failed.json`
- fallback chooses highest primary usage among usable non-disabled accounts
- direct file tests for exact `accounts.json` and `failed.json` schema