From 1ec11649ac3e04085df50dc12e857a8feeb00ec3 Mon Sep 17 00:00:00 2001 From: arqueon <66568719+arqueon@users.noreply.github.com> Date: Fri, 26 Jun 2026 20:50:16 -0600 Subject: [PATCH 1/5] Add Antigravity quota backend --- .github/workflows/ci.yml | 36 ++- providers/get-antigravity-usage | 5 + providers/get-provider-health | 4 + providers/get-provider-usage | 209 ++++++++++++++++++ .../fixtures/antigravity-schema-changed.json | 3 + tests/fixtures/antigravity-valid.json | 28 +++ 6 files changed, 283 insertions(+), 2 deletions(-) create mode 100755 providers/get-antigravity-usage create mode 100644 tests/fixtures/antigravity-schema-changed.json create mode 100644 tests/fixtures/antigravity-valid.json diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c950e88..ba91fd6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -220,10 +220,10 @@ jobs: with: persist-credentials: false - - name: Install dependencies (jq, curl) + - name: Install dependencies (jq, curl, sqlite3) run: | sudo apt-get update -qq - sudo apt-get install -y --no-install-recommends jq curl + sudo apt-get install -y --no-install-recommends jq curl sqlite3 - name: Smoke test — get-claude-usage (no credentials) run: | @@ -263,6 +263,38 @@ jobs: } echo "get-provider-usage smoke: OK — provider=${PROVIDER}" + - name: Integration test — Antigravity quota fixture + run: | + OUT="$(ANTIGRAVITY_RESPONSE_FILE=tests/fixtures/antigravity-valid.json \ + ANTIGRAVITY_ACCOUNT_EMAIL=test@example.invalid \ + providers/get-provider-usage "antigravity" "")" + echo "$OUT" | jq -e ' + length == 1 + and .[0].provider == "antigravity" + and .[0].usage.primary.resetDescription == "Claude" + and .[0].usage.primary.usedPercent == 75 + and .[0].usage.secondary.resetDescription == "Gemini 3 Flash" + and .[0].usage.secondary.usedPercent == 50 + and .[0].usage.tertiary.resetDescription == "Gemini 3 Pro" + and .[0].usage.tertiary.usedPercent == 20 + ' >/dev/null + + AUTH_ERROR="$(ANTIGRAVITY_RESPONSE_FILE=tests/fixtures/antigravity-valid.json \ + ANTIGRAVITY_HTTP_STATUS=401 providers/get-provider-usage "antigravity" "")" + echo "$AUTH_ERROR" | jq -e '.[0].error.code == 401' >/dev/null + + RATE_ERROR="$(ANTIGRAVITY_RESPONSE_FILE=tests/fixtures/antigravity-valid.json \ + ANTIGRAVITY_HTTP_STATUS=429 providers/get-provider-usage "antigravity" "")" + echo "$RATE_ERROR" | jq -e '.[0].error.code == 429' >/dev/null + + MISSING_SESSION="$(ANTIGRAVITY_STATE_DB=/does/not/exist \ + providers/get-provider-usage "antigravity" "")" + echo "$MISSING_SESSION" | jq -e '.[0].error.code == 2' >/dev/null + + SCHEMA_ERROR="$(ANTIGRAVITY_RESPONSE_FILE=tests/fixtures/antigravity-schema-changed.json \ + providers/get-provider-usage "antigravity" "")" + echo "$SCHEMA_ERROR" | jq -e '.[0].error.message | contains("schema changed")' >/dev/null + - name: Smoke test — all stubs delegate correctly (structure check) run: | FAIL=0 diff --git a/providers/get-antigravity-usage b/providers/get-antigravity-usage new file mode 100755 index 0000000..a98a351 --- /dev/null +++ b/providers/get-antigravity-usage @@ -0,0 +1,5 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +exec "$SCRIPT_DIR/get-provider-wrapper" antigravity diff --git a/providers/get-provider-health b/providers/get-provider-health index 1cccaad..7bc48b3 100755 --- a/providers/get-provider-health +++ b/providers/get-provider-health @@ -19,6 +19,10 @@ status_for() { claude) command -v claude >/dev/null 2>&1 || [ -d "$HOME/.claude" ] || { status="missing"; detail="claude CLI or ~/.claude"; } ;; copilot) command -v gh >/dev/null 2>&1 || has_any_env COPILOT_GITHUB_TOKEN GH_TOKEN GITHUB_TOKEN || { status="missing"; detail="gh CLI or GitHub token"; } ;; gemini) command -v gemini >/dev/null 2>&1 || has_any_env GEMINI_API_KEY GOOGLE_API_KEY GOOGLE_GENERATIVE_AI_API_KEY || { status="missing"; detail="gemini CLI or API key"; } ;; + antigravity) + command -v secret-tool >/dev/null 2>&1 || command -v sqlite3 >/dev/null 2>&1 || { status="missing"; detail="secret-tool or sqlite3"; } + [ "$status" != "ready" ] || command -v secret-tool >/dev/null 2>&1 || [ -r "${XDG_CONFIG_HOME:-$HOME/.config}/Antigravity/User/globalStorage/state.vscdb" ] || { status="missing"; detail="CLI keyring or IDE state"; } + ;; 9router) [ -f "$HOME/.9router/db/data.sqlite" ] || [ -f "$HOME/.9router/usage.json" ] || { status="missing"; detail="9Router usage database"; } ;; openrouter) has_any_env OPENROUTER_API_KEY || [ -f "$HOME/.9router/db/data.sqlite" ] || { status="missing"; detail="OPENROUTER_API_KEY or 9Router data"; } ;; deepseek) has_any_env DEEPSEEK_API_KEY || { status="missing"; detail="DEEPSEEK_API_KEY"; } ;; diff --git a/providers/get-provider-usage b/providers/get-provider-usage index 5b8da62..8fe7927 100755 --- a/providers/get-provider-usage +++ b/providers/get-provider-usage @@ -205,6 +205,212 @@ fetch_gemini_native() { json_error gemini gemini-local 2 provider "Gemini is not authenticated locally. Run gemini once, or set GEMINI_API_KEY/GOOGLE_API_KEY." } +extract_antigravity_keyring_token() { + jq -r ' + def token_string: + if type == "object" then + .access_token // .accessToken // .token // .Token // empty + elif type == "string" and test("^ya29\\.") then + . + else + empty + end; + if type == "object" then + (.token | token_string) + // token_string + // empty + else + token_string + end + ' 2>/dev/null || true +} + +extract_antigravity_keyring_account() { + jq -r ' + if type == "object" then + .email // .account_email // (.token.email // .token.account_email // empty) // "Antigravity CLI" + else + "Antigravity CLI" + end + ' 2>/dev/null || printf 'Antigravity CLI' +} + +fetch_antigravity_native() { + local state_db="${ANTIGRAVITY_STATE_DB:-${XDG_CONFIG_HOME:-$HOME/.config}/Antigravity/User/globalStorage/state.vscdb}" + local project_file="${ANTIGRAVITY_PROJECT_FILE:-$HOME/.gemini/antigravity-cli/cache/default_project_id.txt}" + # Antigravity CLI 1.0.x uses the non-sandbox daily endpoint for model/quota + # discovery. Keep an override because this internal surface is not stable. + local endpoint="${ANTIGRAVITY_API_BASE_URL:-https://daily-cloudcode-pa.googleapis.com}" + local response_file="${ANTIGRAVITY_RESPONSE_FILE:-}" + local account="Antigravity" + local login_method="local OAuth session" + local tmp_body http_status auth_json token project request_body message keyring_payload service + + tmp_body="$(mktemp)" + + # A response fixture keeps parsing tests deterministic and avoids requiring + # a live Google session in CI. It is never selected during normal runtime. + if [ -n "$response_file" ]; then + if [ ! -r "$response_file" ]; then + rm -f "$tmp_body" + json_error antigravity antigravity-internal 2 runtime "Antigravity response fixture is not readable." + return 0 + fi + cp "$response_file" "$tmp_body" + http_status="${ANTIGRAVITY_HTTP_STATUS:-200}" + account="${ANTIGRAVITY_ACCOUNT_EMAIL:-Antigravity}" + else + token="" + + # Antigravity CLI keeps its current OAuth profile in the desktop keyring. + # Lookup is deliberately limited to known service names; the secret is + # captured in memory and never emitted. Standard oauth2.Token JSON and a + # plain access-token value are both accepted for forward compatibility. + if command -v secret-tool >/dev/null 2>&1; then + keyring_payload="$(timeout 3 secret-tool lookup service gemini username antigravity 2>/dev/null || true)" + if [ -n "$keyring_payload" ]; then + token="$(printf '%s' "$keyring_payload" | extract_antigravity_keyring_token)" + account="$(printf '%s' "$keyring_payload" | extract_antigravity_keyring_account)" + if [ -z "$token" ] && [[ "$keyring_payload" == ya29.* ]]; then + token="$keyring_payload" + account="Antigravity CLI" + fi + [ -z "$token" ] || login_method="CLI keyring OAuth" + fi + + for service in "${ANTIGRAVITY_KEYRING_SERVICE:-Antigravity CLI}" "antigravity-cli" "Antigravity"; do + [ -z "$token" ] || break + keyring_payload="$(timeout 3 secret-tool lookup service "$service" 2>/dev/null || true)" + [ -n "$keyring_payload" ] || continue + token="$(printf '%s' "$keyring_payload" | extract_antigravity_keyring_token)" + account="$(printf '%s' "$keyring_payload" | extract_antigravity_keyring_account)" + if [ -z "$token" ] && [[ "$keyring_payload" == ya29.* ]]; then + token="$keyring_payload" + account="Antigravity CLI" + fi + if [ -n "$token" ]; then + login_method="CLI keyring OAuth" + break + fi + done + fi + + # Older IDE builds also leave an access token in state.vscdb. Keep it as a + # fallback, but prefer the keyring because the database copy can be stale. + if [ -z "$token" ] && command -v sqlite3 >/dev/null 2>&1 && [ -r "$state_db" ]; then + auth_json="$(sqlite3 "$state_db" "select value from ItemTable where key='antigravityAuthStatus' limit 1;" 2>/dev/null || true)" + token="$(printf '%s' "$auth_json" | jq -r '.apiKey // empty' 2>/dev/null || true)" + account="$(printf '%s' "$auth_json" | jq -r '.email // "Antigravity IDE"' 2>/dev/null || printf 'Antigravity IDE')" + login_method="IDE state OAuth" + fi + + if [ -z "$token" ]; then + rm -f "$tmp_body" + json_error antigravity antigravity-local 2 provider "No usable Antigravity session was found in the CLI keyring or IDE state. Run agy or open Antigravity and sign in." + return 0 + fi + + project="" + [ ! -r "$project_file" ] || project="$(sed -n '1p' "$project_file")" + request_body="$(jq -cn --arg project "$project" 'if $project == "" then {} else {project:$project} end')" + + # Feed the bearer header through a pipe-backed config descriptor and the + # request body through stdin. Neither token nor project appears in the + # process argument list, stdout, stderr, or a temporary file. + http_status="$( + curl -sS --max-time 10 \ + --config <(printf 'header = "Authorization: Bearer %s"\n' "$token") \ + -o "$tmp_body" -w '%{http_code}' \ + -X POST \ + -H "Content-Type: application/json" \ + -H "User-Agent: antigravity/linux/amd64" \ + --data-binary @- \ + "$endpoint/v1internal:fetchAvailableModels" 2>/dev/null \ + <<< "$request_body" \ + || true + )" + fi + + if [ "$http_status" != "200" ]; then + message="$(jq -r '.error.message // empty' "$tmp_body" 2>/dev/null || true)" + rm -f "$tmp_body" + case "$http_status" in + 401|403) + json_error antigravity antigravity-internal "$http_status" provider "Antigravity session expired. Open Antigravity and sign in again." + ;; + 429) + [ -n "$message" ] || message="Antigravity quota endpoint is rate-limited. Retry after its reset time." + json_error antigravity antigravity-internal 429 provider "$message" + ;; + *) + [ -n "$message" ] || message="Antigravity quota request returned HTTP ${http_status:-0}." + json_error antigravity antigravity-internal "${http_status:-1}" provider "$message" + ;; + esac + return 0 + fi + + if ! jq -e '.models | type == "object"' "$tmp_body" >/dev/null 2>&1; then + rm -f "$tmp_body" + json_error antigravity antigravity-internal 1 provider "Antigravity quota response schema changed: models object is missing." + return 0 + fi + + jq -c --arg account "$account" --arg login_method "$login_method" ' + def clamp: + if type != "number" then null + elif . < 0 then 0 + elif . > 1 then 1 + else . end; + def family($key; $entry): + (($key + " " + ($entry.displayName // "") + " " + ($entry.modelName // "")) | ascii_downcase) as $name + | if ($name | contains("claude")) then "Claude" + elif (($name | test("gemini[- ]?3")) and ($name | contains("flash"))) then "Gemini 3 Flash" + elif ($name | test("gemini[- ]?3")) then "Gemini 3 Pro" + else null end; + def window($group): { + usedPercent: (((1 - $group.remaining) * 10000 | round) / 100), + windowMinutes: null, + resetsAt: $group.resetsAt, + resetDescription: $group.name, + displayValue: (((1 - $group.remaining) * 100 | round | tostring) + "% used") + }; + [ + .models | to_entries[] + | . as $model + | (family(.key; .value)) as $family + | (.value.quotaInfo.remainingFraction | clamp) as $remaining + | select($family != null and $remaining != null) + | {name:$family, remaining:$remaining, resetsAt:(.value.quotaInfo.resetTime // null)} + ] + | group_by(.name) + | map({ + name: .[0].name, + remaining: (map(.remaining) | min), + resetsAt: (map(.resetsAt | select(. != null)) | sort | first // null) + }) + | sort_by(.remaining) + | map(window(.)) as $windows + | if ($windows | length) == 0 then + error("no supported quota entries") + else { + provider:"antigravity", + source:"antigravity-internal", + usage:{ + identity:{providerID:"antigravity",accountEmail:$account,loginMethod:$login_method}, + accountEmail:$account, + loginMethod:$login_method, + primary:$windows[0], + secondary:($windows[1] // null), + tertiary:($windows[2] // null), + updatedAt:(now|todateiso8601) + }, + credits:{remaining:"per-model quota"} + } end + ' "$tmp_body" 2>/dev/null || json_error antigravity antigravity-internal 1 provider "Antigravity quota response contains no supported model quota entries." + rm -f "$tmp_body" +} + fetch_openrouter_native() { local key="${OPENROUTER_API_KEY:-}" if [ -z "$key" ]; then @@ -1303,6 +1509,9 @@ fetch_provider() { gemini) fetch_gemini_native ;; + antigravity) + fetch_antigravity_native + ;; 9router) fetch_9router_native 9router 9router ;; diff --git a/tests/fixtures/antigravity-schema-changed.json b/tests/fixtures/antigravity-schema-changed.json new file mode 100644 index 0000000..1206964 --- /dev/null +++ b/tests/fixtures/antigravity-schema-changed.json @@ -0,0 +1,3 @@ +{ + "availableModels": [] +} diff --git a/tests/fixtures/antigravity-valid.json b/tests/fixtures/antigravity-valid.json new file mode 100644 index 0000000..8d426ee --- /dev/null +++ b/tests/fixtures/antigravity-valid.json @@ -0,0 +1,28 @@ +{ + "models": { + "claude-sonnet-4-6": { + "displayName": "Claude Sonnet 4.6", + "quotaInfo": { + "remainingFraction": 0.25, + "resetTime": "2026-06-27T03:00:00Z" + } + }, + "gemini-3-pro-high": { + "displayName": "Gemini 3 Pro High", + "quotaInfo": { + "remainingFraction": 0.8, + "resetTime": "2026-06-27T04:00:00Z" + } + }, + "gemini-3-flash": { + "displayName": "Gemini 3 Flash", + "quotaInfo": { + "remainingFraction": 0.5, + "resetTime": "2026-06-27T05:00:00Z" + } + }, + "unrelated-model": { + "displayName": "Unrelated model" + } + } +} From 346d1f55f602d102c147596c85b35ccc9ecbf296 Mon Sep 17 00:00:00 2001 From: arqueon <66568719+arqueon@users.noreply.github.com> Date: Fri, 26 Jun 2026 20:50:24 -0600 Subject: [PATCH 2/5] Expose Antigravity in the widget --- AiOverviewControlSettings.qml | 1 + AiOverviewControlWidget.qml | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/AiOverviewControlSettings.qml b/AiOverviewControlSettings.qml index a3e11dd..256c3b2 100644 --- a/AiOverviewControlSettings.qml +++ b/AiOverviewControlSettings.qml @@ -60,6 +60,7 @@ PluginSettings { { id:"codex", name:"Codex", icon:"terminal", mode:"telemetry", requirement:"codex CLI", envVar:"", note:"Official app-server rate limits" }, { id:"claude", name:"Claude", icon:"psychology", mode:"telemetry", requirement:"claude CLI or ~/.claude", envVar:"", note:"Local analytics and authenticated usage" }, { id:"copilot", name:"Copilot", icon:"code", mode:"telemetry", requirement:"gh CLI or GitHub token", envVar:"COPILOT_GITHUB_TOKEN", note:"Authenticated Copilot quota from the GitHub session" }, + { id:"antigravity", name:"Antigravity", icon:"rocket_launch", mode:"telemetry", requirement:"Antigravity CLI keyring or IDE session", envVar:"", note:"Per-model quota and reset times from the internal read-only endpoint" }, { id:"gemini", name:"Gemini", icon:"star", mode:"telemetry", requirement:"gemini CLI or API key", envVar:"GEMINI_API_KEY", note:"Authentication status; quota remains in AI Studio" }, { id:"9router", name:"9Router", icon:"share", mode:"telemetry", requirement:"local 9Router database", envVar:"", note:"Local requests, tokens and cost" }, { id:"openrouter", name:"OpenRouter", icon:"route", mode:"telemetry", requirement:"API key or 9Router data", envVar:"OPENROUTER_API_KEY", note:"Official key usage and limits" }, diff --git a/AiOverviewControlWidget.qml b/AiOverviewControlWidget.qml index 6019615..9fcde5f 100644 --- a/AiOverviewControlWidget.qml +++ b/AiOverviewControlWidget.qml @@ -134,6 +134,7 @@ PluginComponent { "codex", "claude", "copilot", + "antigravity", "gemini", "9router", "openrouter", @@ -526,6 +527,7 @@ PluginComponent { codex: "Codex", claude: "Claude", copilot: "Copilot", + antigravity: "Antigravity", cursor: "Cursor", gemini: "Gemini", openrouter: "OpenRouter", @@ -722,6 +724,7 @@ PluginComponent { if (providerId === "codex") return "data_object"; if (providerId === "claude") return "psychology"; if (providerId === "copilot") return "hub"; + if (providerId === "antigravity") return "rocket_launch"; if (providerId === "gemini") return "auto_awesome"; if (providerId === "openrouter") return "route"; if (providerId === "9router") return "share"; @@ -758,6 +761,7 @@ PluginComponent { if (providerId === "claude") return Theme.warning; if (providerId === "codex") return Theme.success; if (providerId === "copilot") return Theme.primary; + if (providerId === "antigravity") return Theme.primary; if (providerId === "gemini") return Theme.secondary; if (providerId === "openrouter") return Theme.primary; if (providerId === "9router") return Theme.secondary; @@ -816,6 +820,10 @@ PluginComponent { const windowData = primaryUsageWindow(provider); if (windowData && windowData.displayValue && String(windowData.displayValue).length > 0) { const label = windowData.resetDescription || t("status.usage", "usage"); + const reset = provider.provider === "antigravity" ? formatTimeUntil(windowData.resetsAt) : ""; + if (reset && reset !== "—") { + return `${source} · ${label} · ${windowData.displayValue} · ${t("status.reset", "reset")} ${reset}`; + } return `${source} · ${label} · ${windowData.displayValue}`; } const reset = providerReset(provider); @@ -973,6 +981,7 @@ PluginComponent { claude: "https://claude.ai/settings/usage", codex: "https://chatgpt.com/codex/settings/usage", copilot: "https://github.com/settings/copilot/features", + antigravity: "", gemini: "https://aistudio.google.com/usage", openrouter: "https://openrouter.ai/activity", deepseek: "https://platform.deepseek.com/usage", From 84e94166315d11dece326057cd4923a1f46bb63a Mon Sep 17 00:00:00 2001 From: arqueon <66568719+arqueon@users.noreply.github.com> Date: Fri, 26 Jun 2026 20:50:29 -0600 Subject: [PATCH 3/5] Document Antigravity quota support --- README.md | 9 +++++---- docs/README.pt-BR.md | 5 +++-- docs/architecture.md | 5 +++++ docs/configuration.md | 2 ++ docs/providers.md | 13 ++++++++++++- 5 files changed, 27 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 535ccfd..825bf2d 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ billing, authentication, and local usage telemetry — right in your DankBar. [](https://github.com/bernardopg/AiOverviewControl/actions/workflows/ci.yml) [](https://github.com/bernardopg/AiOverviewControl/releases/latest) [](./LICENSE) -[](./docs/providers.md) +[](./docs/providers.md) [](./docs/i18n-crowdin.md) [Install](#installation) · [Screenshots](#screenshots) · [Providers](./docs/providers.md) · @@ -48,7 +48,7 @@ it does not. No dashboard scraping. No fabricated percentages. Ever. | | | | --- | --- | -| 📊 **Unified dashboard** | 33 AI providers and developer tools in one place. | +| 📊 **Unified dashboard** | 34 AI providers and developer tools in one place. | | 🛰️ **Fleet overview** | Cross-provider rollup in the hero — quota-only average load, hottest provider, how many are near their cap, and the soonest reset. | | ⏱️ **Official Codex windows** | Rate-limit windows straight from `codex app-server`. | | 🤖 **Deep Claude analytics** | Quota plus local token, session, model, project, and cost analytics. | @@ -84,7 +84,7 @@ Provider cards use one of these honest coverage levels: | Coverage | Meaning | | --- | --- | -| **Quota** | Returns real rate-limit/spend windows and used percentage (Codex, Copilot, OpenRouter, Z.ai, GLM). | +| **Quota** | Returns real rate-limit/spend windows and used percentage (Codex, Copilot, Antigravity, OpenRouter, Z.ai, GLM). | | **Balance** | Returns remaining prepaid balance or credits in real currency (Kimi, DeepSeek, Together). | | **Analytics** | Reads consumption counters or provider-owned local data (Cloudflare GraphQL, 9Router, Claude, Ollama). | | **Authentication** | Verifies credentials via a read-only endpoint without stable quota data (Gemini, Mistral, NVIDIA, MiniMax, Qwen, xAI, and more). | @@ -97,6 +97,7 @@ Notable integrations: | Codex | Official `codex app-server` account and rate-limit methods. | | Claude Code | OAuth quota plus local `~/.claude/projects` analytics. | | GitHub Copilot | Authenticated GitHub/Copilot quota snapshot. | +| Antigravity | Per-model quota and reset times from the local IDE session and internal read-only endpoint. | | 9Router | Local SQLite or JSON usage data, including routed-model telemetry. | | OpenRouter | Key limits, spend, balance, and 30-day model activity. | | Kimi (Moonshot) | `GET /v1/users/me/balance` — available, voucher, and cash balance (USD/CNY). | @@ -114,7 +115,7 @@ The full matrix, credentials, and upstream references are documented in - DankMaterialShell running on Quickshell. - `bash`, `jq`, and `curl`. -- Provider-specific CLIs or credentials only for providers you enable. +- Provider-specific CLIs or credentials only for providers you enable; Antigravity additionally requires `sqlite3`. Recommended baseline for the default provider set: diff --git a/docs/README.pt-BR.md b/docs/README.pt-BR.md index f71cf05..7ca3e84 100644 --- a/docs/README.pt-BR.md +++ b/docs/README.pt-BR.md @@ -12,7 +12,7 @@ billing, autenticação e telemetria local de uso de IA — direto na sua DankBa [](https://github.com/bernardopg/AiOverviewControl/actions/workflows/ci.yml) [](https://github.com/bernardopg/AiOverviewControl/releases/latest) [](../LICENSE) -[](./providers.md) +[](./providers.md) [](./i18n-crowdin.md) [Instalação](#instalação) · [Screenshots](#screenshots) · [Provedores](./providers.md) · @@ -97,6 +97,7 @@ Integrações medidas notáveis: | Codex | Métodos oficiais de conta e rate-limit do `codex app-server`. | | Claude Code | Cota OAuth mais analytics local de `~/.claude/projects`. | | GitHub Copilot | Snapshot autenticado de cota GitHub/Copilot. | +| Antigravity | Cotas por modelo e horários de reset da sessão local do IDE via endpoint interno somente leitura. | | 9Router | Dados locais de uso em SQLite ou JSON, incluindo telemetria por modelo roteado. | | OpenRouter | Limites de chave, gasto, saldo e atividade de modelos em 30 dias. | | DeepSeek, Kimi, Together | APIs de saldo ou crédito do provedor. | @@ -112,7 +113,7 @@ A matriz completa, credenciais e referências upstream estão documentadas em - DankMaterialShell rodando sobre Quickshell. - `bash`, `jq` e `curl`. -- CLIs ou credenciais específicas apenas para os provedores habilitados. +- CLIs ou credenciais específicas apenas para os provedores habilitados; Antigravity também requer `sqlite3`. Linha de base recomendada para o conjunto padrão de provedores: diff --git a/docs/architecture.md b/docs/architecture.md index dfe3de4..8a428a4 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -11,6 +11,7 @@ providers/get-provider-health Prerequisite checks for settings providers/get-codex-usage Codex app-server protocol bridge providers/get-claude-usage Claude local analytics and quota bridge providers/get-copilot-usage Authenticated GitHub Copilot quota bridge +providers/get-antigravity-usage Local Antigravity session quota bridge providers/get-provider-wrapper Single-provider wrapper providers/get-*-usage Canonical provider entrypoints ``` @@ -62,6 +63,10 @@ Errors return: `get-codex-usage` starts `codex app-server`, sends `initialize`, `account/read`, and `account/rateLimits/read`, then maps the official response to the common schema. The bridge uses a bounded process lifetime and never reads browser state. +## Antigravity protocol + +`get-antigravity-usage` reads the current Antigravity CLI OAuth profile from the desktop keyring, falling back to the IDE SQLite state used by older builds, and calls the read-only internal `v1internal:fetchAvailableModels` endpoint. It groups model quotas into Claude, Gemini 3 Pro, and Gemini 3 Flash windows, ordered by highest consumption. The bearer token is supplied to curl over stdin and is never printed or placed in process arguments. This is an internal Google endpoint and may change without notice. + ## Settings keys | Key | Default | Purpose | diff --git a/docs/configuration.md b/docs/configuration.md index 115de9d..c4ff2c3 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -29,6 +29,8 @@ codex,claude,copilot Add API providers only after their required environment variables are available to the DMS process. +Antigravity does not use an environment variable. It prefers the signed-in CLI profile from Linux Secret Service via `secret-tool`, then falls back to `${XDG_CONFIG_HOME:-~/.config}/Antigravity/User/globalStorage/state.vscdb` via `sqlite3` for older IDE builds. + ## Environment variables | Provider | Variables | diff --git a/docs/providers.md b/docs/providers.md index c8b1f78..aead00c 100644 --- a/docs/providers.md +++ b/docs/providers.md @@ -21,7 +21,7 @@ Every provider maps to exactly one coverage level. The level dictates what the w | Level | Meaning | Example providers | | --- | --- | --- | -| **Quota** | Real `usedPercent` + reset window from an official protocol/API. | `codex`, `copilot`, `openrouter`, `zai`, `glm` | +| **Quota** | Real `usedPercent` + reset window from a protocol/API. | `codex`, `copilot`, `antigravity`, `openrouter`, `zai`, `glm` | | **Balance** | Remaining prepaid balance / credits in real currency. | `kimi`, `deepseek`, `together` | | **Analytics** | Consumption counters (requests/tokens/neurons/cost) with no remaining-quota value. | `cloudflare` (GraphQL), `9router`, `claude` (local) | | **Auth** | Read-only key validation only — no usage numbers. | `gemini`, `mistral`, `nvidia`, `qwen`, `byteplus`, `groq`, `cohere`, `replicate`, `fireworks`, `minimax`, `xai`, `kilo` | @@ -83,6 +83,17 @@ The matrix below summarises the **authentication/billing surface** for every sup
antigravity~/.config/Antigravityv1internal:fetchAvailableModelsgeminiGET /v1beta/modelsantigravity~/.config/Antigravityv1internal:fetchAvailableModelsv1internal:fetchAvailableModels on cloudcode-pa.googleapis.comgemini