Skip to content

Latest commit

 

History

History
202 lines (156 loc) · 4.11 KB

ACCOUNTS.md

File metadata and controls

202 lines (156 loc) · 4.11 KB

Account Management

Storage Structure

Main Registry

Location: ~/.cligate/accounts.json

{
  "accounts": [
    {
      "email": "user@gmail.com",
      "accountId": "d41e9636-16d8-42be-91da-7ea8773bfb7e",
      "planType": "plus",
      "accessToken": "eyJhbGciOiJSUzI1NiIs...",
      "refreshToken": "rt_WpTMn1...",
      "idToken": "eyJhbGciOiJSUzI1NiIs...",
      "expiresAt": 1770886178000,
      "addedAt": "2026-02-13T04:00:00.000Z",
      "lastUsed": "2026-02-13T04:30:00.000Z",
      "quota": {
        "usage": {...},
        "account": {...},
        "lastChecked": "2026-02-14T10:00:00.000Z"
      }
    }
  ],
  "activeAccount": "user@gmail.com",
  "version": 1
}

Per-Account Tokens

Location: ~/.cligate/accounts/<email>/auth.json

{
  "auth_mode": "chatgpt",
  "OPENAI_API_KEY": null,
  "tokens": {
    "id_token": "...",
    "access_token": "...",
    "refresh_token": "...",
    "account_id": "..."
  },
  "last_refresh": "2026-02-14T10:00:00.000Z"
}

Operations

Add Account (OAuth)

curl -X POST http://localhost:8081/accounts/add

# Returns OAuth URL to open in browser

Import from Codex App

curl -X POST http://localhost:8081/accounts/import

# Imports from ~/.codex/auth.json

List Accounts

curl http://localhost:8081/accounts

# Response
{
  "accounts": [
    {
      "email": "user@gmail.com",
      "accountId": "...",
      "planType": "plus",
      "addedAt": "...",
      "lastUsed": "...",
      "isActive": true,
      "tokenExpired": false,
      "quota": {...}
    }
  ],
  "activeAccount": "user@gmail.com",
  "total": 1
}

Switch Active Account

curl -X POST http://localhost:8081/accounts/switch \
  -H "Content-Type: application/json" \
  -d '{"email":"other@gmail.com"}'

Switching:

  1. Updates activeAccount in accounts.json
  2. Updates auth file for the account
  3. Next API calls use new account's credentials

Remove Account

curl -X DELETE http://localhost:8081/accounts/user@gmail.com

Removes:

  • Account from registry
  • Per-account token directory

Refresh Tokens

# Active account
curl -X POST http://localhost:8081/accounts/refresh

# Specific account
curl -X POST http://localhost:8081/accounts/user@gmail.com/refresh

# All accounts
curl -X POST http://localhost:8081/accounts/refresh/all

Token Lifecycle

Expiration

  • Access tokens expire in ~1 hour (3600 seconds)
  • Refresh tokens are long-lived (weeks/months)

Auto-Refresh

  • Background refresh every 55 minutes
  • Startup refresh 2 seconds after server start
  • Proactive refresh 5 minutes before expiry

Token Validation

Before each API call:

  1. Check if token is expired or expiring within 5 minutes
  2. If yes, refresh using refresh token
  3. Use new access token for the call

Quota Tracking

Fetch Quota

curl http://localhost:8081/accounts/quota

# Response
{
  "success": true,
  "email": "user@gmail.com",
  "quota": {
    "usage": {
      "totalTokenUsage": 15,
      "limit": 100,
      "remaining": 85,
      "percentage": 15,
      "resetAt": "..."
    },
    "account": {...}
  },
  "cached": false
}

Web UI Quota Display Rules

  • The Accounts table displays remaining quota as a percentage.
  • Remaining percentage is normalized to 0-100 to avoid broken UI values.
  • If limitReached=true or allowed=false, UI shows quota as exhausted even when percentage data is missing.
  • If usage data is unavailable, UI shows - instead of rendering a broken bar.
  • Reset window is shown using usage.resetAt (with fallback to usage.raw.rate_limit.primary_window.reset_at).
  • UI also shows a relative countdown (e.g. Resets in 6d 13h) when reset data is available.

Refresh All Quotas

curl http://localhost:8081/accounts/quota/all

Account Persistence

On server startup:

  1. ensureAccountsPersist() loads accounts
  2. Restores active account's auth
  3. Starts auto-refresh timer

Security

  • Tokens stored locally in ~/.cligate/
  • Directory permissions: user read/write only
  • Never logged or exposed in API responses
  • Per-account isolation via separate directories