pi-antigravity-rotator 1.14.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +32 -0
- package/README.md +126 -7
- package/package.json +1 -1
- package/src/account-store.ts +71 -14
- package/src/cli.ts +8 -0
- package/src/compat.ts +844 -13
- package/src/dashboard.ts +279 -10
- package/src/doctor.ts +97 -0
- package/src/index.ts +21 -37
- package/src/paths.ts +4 -0
- package/src/proxy.ts +132 -80
- package/src/rate-limit-parser.ts +126 -0
- package/src/rotator.ts +470 -51
- package/src/storage.ts +56 -0
- package/src/types.ts +101 -19
- package/src/validators.ts +19 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,38 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [2.1.0] - 2026-05-21
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- **Codex Agent Integration Support**: Out-of-the-box support for connecting agentic frameworks like Codex (executing in VS Code or CLI) by routing OpenAI Responses API payloads and enabling native reasoning streaming, function-calling translation, and strict contract validation.
|
|
9
|
+
- **OpenAI Responses API Compatibility**: Full compatibility with the OpenAI Responses endpoint family (`POST /v1/responses`, `GET /v1/responses/<id>`, `DELETE /v1/responses/<id>`, `POST /v1/responses/<id>/cancel`, and `GET /v1/responses/<id>/input_items`). Includes full support for structured inputs, in-memory conversation/responses storage, and native tool-calling/reasoning visibility, tailored for advanced agentic frameworks.
|
|
10
|
+
- **Hybrid Routing Policy**: Added optional `routingPolicy: "hybrid"` with weighted selection across timer priority, quota, tier, health, local token bucket state, and distance.
|
|
11
|
+
- **Routing Inspector**: Added a dashboard modal that explains the currently selected route, candidate scores, and why each account was excluded for a model.
|
|
12
|
+
- **Rate Limit Parser Module**: Extracted robust retry parsing into `src/rate-limit-parser.ts` with support for `Retry-After`, `x-ratelimit-reset`, `quotaResetDelay`, `quotaResetTimeStamp`, `retryDelay`, and duration strings.
|
|
13
|
+
|
|
14
|
+
### Changed
|
|
15
|
+
- **Token Bucket Guardrail**: Added optional per-account token buckets to slow repeated reuse of the same account without changing the default v2.0 routing behavior.
|
|
16
|
+
- **Attention Needed Coverage**: The dashboard now surfaces unroutable models and token-bucket exhaustion alongside existing security, cooldown, disabled, flagged, and error alerts.
|
|
17
|
+
- **Compat Hardening**: Added coverage for `cache_control` stripping, schema forwarding, missing-signature tool history, and empty SSE parsing.
|
|
18
|
+
|
|
19
|
+
## [2.0.0] - 2026-05-20
|
|
20
|
+
|
|
21
|
+
### Added
|
|
22
|
+
- **Admin Config APIs**: Added `GET /api/config`, `PUT /api/config`, `GET /api/config/export`, and `POST /api/config/import` for validated runtime config management.
|
|
23
|
+
- **Dashboard Config Editor**: Added an embedded JSON editor with load/save/import/export controls and hosted login access.
|
|
24
|
+
- **Docker Deployment**: Added `Dockerfile`, `docker-compose.yml`, and `.dockerignore` for headless deployments with persistent `/data`.
|
|
25
|
+
- **Doctor Command**: Added `pi-antigravity-rotator doctor` to validate config, inspect backups, and report missing admin auth.
|
|
26
|
+
- **Gemini-Compatible Discovery**: Added `/v1beta/models` and a minimal Gemini-style `generateContent` route family.
|
|
27
|
+
|
|
28
|
+
### Changed
|
|
29
|
+
- **Version 2.0**: Bumped package version to `2.0.0` on branch `v2.0`.
|
|
30
|
+
- **Persistence Hardening**: Config, state, and token usage now write atomically with timestamped backups.
|
|
31
|
+
- **Routing Metadata**: Added optional account `tier` plus runtime `healthScore` as timer-first tie-breakers.
|
|
32
|
+
- **Security Visibility**: Startup logs, `/api/status`, and the dashboard now warn when `PI_ROTATOR_ADMIN_TOKEN` is missing.
|
|
33
|
+
|
|
34
|
+
### Migration
|
|
35
|
+
- Existing `accounts.json` stays compatible. New defaults are `bindHost: "0.0.0.0"`, `routingPolicy: "timer-first"`, and `accounts[].tier: "unknown"`.
|
|
36
|
+
|
|
5
37
|
## [1.14.0] - 2026-05-19
|
|
6
38
|
|
|
7
39
|
### Added
|
package/README.md
CHANGED
|
@@ -1,9 +1,43 @@
|
|
|
1
|
+

|
|
2
|
+
|
|
1
3
|
# Pi Antigravity Rotator
|
|
2
4
|
|
|
3
5
|
Multi-account rotation proxy for Google Antigravity. Distributes API usage across multiple Google accounts with per-model routing, real-time quota tracking, automatic token management, and infringement detection.
|
|
4
6
|
|
|
7
|
+
> **⚠️ WARNING:** Using this proxy may put connected Google accounts at risk of Terms of Service enforcement, including restriction, suspension, or permanent bans. Use at your own risk.
|
|
8
|
+
|
|
9
|
+
<details>
|
|
10
|
+
<summary><strong>⚠️ Terms of Service Warning — Read Before Installing</strong></summary>
|
|
11
|
+
|
|
12
|
+
> [!CAUTION]
|
|
13
|
+
> This is an unofficial tool and is not endorsed by Google. Routing traffic through this proxy may violate Google's Terms of Service or trigger automated abuse or policy enforcement systems.
|
|
14
|
+
>
|
|
15
|
+
> **By using this proxy, you acknowledge:**
|
|
16
|
+
> - Your account may be restricted, suspended, shadow-banned, or permanently banned
|
|
17
|
+
> - Multi-account rotation and proxying can increase account risk compared to normal interactive usage
|
|
18
|
+
> - You assume all responsibility for the accounts and traffic routed through this tool
|
|
19
|
+
>
|
|
20
|
+
> **Recommendation:** Do not use your primary Google account. Prefer disposable or lower-risk accounts, and keep account exposure conservative.
|
|
21
|
+
|
|
22
|
+
</details>
|
|
23
|
+
|
|
24
|
+
## Support Me
|
|
25
|
+
|
|
26
|
+
If this tool has helped you optimize your API usage and save costs, consider supporting its development!
|
|
27
|
+
|
|
28
|
+
<a href="https://ko-fi.com/tuxevil" target="_blank"><img src="https://storage.ko-fi.com/cdn/kofi2.png?v=3" height="36" alt="Buy Me a Coffee at ko-fi.com" /></a>
|
|
29
|
+
|
|
30
|
+
## v2.0 Highlights
|
|
31
|
+
|
|
32
|
+
- Full dashboard config editor with import/export.
|
|
33
|
+
- Official Docker and compose deployment.
|
|
34
|
+
- `pi-antigravity-rotator doctor` for config/state validation.
|
|
35
|
+
- Optional account `tier` metadata and runtime `healthScore`.
|
|
36
|
+
- Strong security warnings when admin routes are open without `PI_ROTATOR_ADMIN_TOKEN`.
|
|
37
|
+
|
|
5
38
|
## Features
|
|
6
39
|
|
|
40
|
+
- **Compatibility Adapters** -- Includes standard OpenAI-compatible `/v1/chat/completions` and Anthropic-compatible `/v1/messages` APIs. Features comprehensive **OpenAI Responses API compatibility** (`/v1/responses`), enabling seamless integration with advanced agentic systems like Codex.
|
|
7
41
|
- **Per-model routing** -- Each model (Gemini Pro, Flash, Claude) routes to its own active account independently. Multiple agents using different models won't interfere with each other.
|
|
8
42
|
- **Real-time quota monitoring** -- Polls Google's quota API every 5 minutes to track remaining usage per model per account
|
|
9
43
|
- **Per-model timer tracking** -- Timer classification (`fresh`/`7d`/`5h`) is evaluated per model using each model's actual `resetTime` from the quota API, not a per-account estimate
|
|
@@ -49,6 +83,14 @@ npm run login
|
|
|
49
83
|
npm start
|
|
50
84
|
```
|
|
51
85
|
|
|
86
|
+
### Option C: Docker
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
docker compose up -d
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
The included compose file persists runtime data under `./docker-data` and sets `PI_ROTATOR_DIR=/data`.
|
|
93
|
+
|
|
52
94
|
## Adding Accounts
|
|
53
95
|
|
|
54
96
|
Run `npm run login` once per Google account:
|
|
@@ -89,6 +131,8 @@ If login fails at project discovery:
|
|
|
89
131
|
|
|
90
132
|
After starting the proxy, open `http://localhost:51200/dashboard` or `http://<your-server-ip>:51200/dashboard` from any machine on the same network (the proxy binds to `0.0.0.0`).
|
|
91
133
|
|
|
134
|
+
If `PI_ROTATOR_ADMIN_TOKEN` is unset, dashboard and `/api/*` access remains open for backwards compatibility. v2.0 now surfaces loud warnings about that state in startup logs, `/api/status`, and the dashboard itself.
|
|
135
|
+
|
|
92
136
|
The dashboard shows:
|
|
93
137
|
|
|
94
138
|
- **Top Status & Controls** -- Real-time routing state, uptime, requests, and PII masking toggle.
|
|
@@ -98,7 +142,8 @@ The dashboard shows:
|
|
|
98
142
|
- **Quota Forecast** -- Predictive modeling showing when each model's quota will run out based on the current requests/hour burn rate.
|
|
99
143
|
- **Searchable Request Log** -- Live feed of the last 200 requests with exact timestamps, models, masked accounts, status codes, and latency.
|
|
100
144
|
- **Account Cards** -- Sorted by total quota. Shows status (`active`, `ready`, `cooldown`, `flagged`, `disabled`), quota bars with timers, and precise error messages.
|
|
101
|
-
- **Operator Panels** -- "Attention Needed" summaries for quarantined accounts and a real-time event feed of rotator actions.
|
|
145
|
+
- **Operator Panels** -- "Attention Needed" summaries for quarantined accounts, unroutable models, token-bucket pressure, and a real-time event feed of rotator actions.
|
|
146
|
+
- **Routing Inspector** -- On-demand modal showing the active routing policy, candidate scores, local token bucket state, and rejection reasons per model.
|
|
102
147
|
|
|
103
148
|

|
|
104
149
|
|
|
@@ -220,6 +265,8 @@ export PI_ROTATOR_DIR=/path/to/config
|
|
|
220
265
|
export PI_ROTATOR_QUOTA_USER_AGENT="antigravity/1.107.0 darwin/arm64"
|
|
221
266
|
# Optional: require this token for dashboard/API access. If unset, legacy open access is preserved.
|
|
222
267
|
export PI_ROTATOR_ADMIN_TOKEN="change-me"
|
|
268
|
+
# Optional: bind the proxy to a safer local-only interface.
|
|
269
|
+
export PI_ROTATOR_BIND_HOST="127.0.0.1"
|
|
223
270
|
# Optional: max accepted proxy request body size in bytes. Default: 26214400 (25 MiB).
|
|
224
271
|
export PI_ROTATOR_MAX_BODY_BYTES=26214400
|
|
225
272
|
# Optional: log verbosity. One of debug, info, warn, error, silent. Default: info.
|
|
@@ -231,6 +278,24 @@ export PI_AI_ANTIGRAVITY_VERSION=1.107.0
|
|
|
231
278
|
pi-antigravity-rotator start --config-dir /path/to/config
|
|
232
279
|
```
|
|
233
280
|
|
|
281
|
+
New v2.0 config fields:
|
|
282
|
+
|
|
283
|
+
- `bindHost`: interface to bind on. Default: `0.0.0.0`.
|
|
284
|
+
- `routingPolicy`: current default is `timer-first`. Optional values now include `tier-first`, `quota-first`, and `hybrid`.
|
|
285
|
+
- `tokenBucketEnabled`: enables the local per-account request bucket used by `hybrid`. Default: `false`.
|
|
286
|
+
- `tokenBucketMaxTokens`: bucket capacity when enabled. Default: `50`.
|
|
287
|
+
- `tokenBucketRefillPerMinute`: refill speed when enabled. Default: `6`.
|
|
288
|
+
- `tokenBucketInitialTokens`: startup fill level when enabled. Default: `50`.
|
|
289
|
+
- `accounts[].tier`: optional `ultra`, `pro`, `free`, or `unknown`.
|
|
290
|
+
|
|
291
|
+
## Doctor
|
|
292
|
+
|
|
293
|
+
```bash
|
|
294
|
+
pi-antigravity-rotator doctor
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
This validates `accounts.json`, checks local state files, lists backups, and warns when admin auth is not configured.
|
|
298
|
+
|
|
234
299
|
`accounts.json` is created automatically by the login command.
|
|
235
300
|
Login now fails if Google does not return a project ID. No shared fallback.
|
|
236
301
|
|
|
@@ -315,6 +380,11 @@ Login now fails if Google does not return a project ID. No shared fallback.
|
|
|
315
380
|
| `POST` | `/api/self-update` | Trigger npm self-update to latest version (admin-only) |
|
|
316
381
|
| `POST` | `/v1internal:streamGenerateContent` | Native Antigravity proxy endpoint (used by pi) |
|
|
317
382
|
| `GET` | `/v1/models` | OpenAI-compatible model list |
|
|
383
|
+
| `POST` | `/v1/responses` | OpenAI Responses-compatible create endpoint |
|
|
384
|
+
| `GET` | `/v1/responses/<id>` | Retrieve stored Responses result |
|
|
385
|
+
| `DELETE` | `/v1/responses/<id>` | Delete stored Responses result |
|
|
386
|
+
| `POST` | `/v1/responses/<id>/cancel` | Cancel an in-progress stored Responses result |
|
|
387
|
+
| `GET` | `/v1/responses/<id>/input_items` | List stored input items for a Responses result |
|
|
318
388
|
| `POST` | `/v1/chat/completions` | OpenAI-compatible non-streaming chat adapter |
|
|
319
389
|
| `POST` | `/v1/messages` | Anthropic-compatible non-streaming messages adapter |
|
|
320
390
|
|
|
@@ -336,6 +406,18 @@ curl http://localhost:51200/v1/chat/completions \
|
|
|
336
406
|
}'
|
|
337
407
|
```
|
|
338
408
|
|
|
409
|
+
**OpenAI Responses-compatible example:**
|
|
410
|
+
|
|
411
|
+
```bash
|
|
412
|
+
curl http://localhost:51200/v1/responses \
|
|
413
|
+
-H 'Content-Type: application/json' \
|
|
414
|
+
-d '{
|
|
415
|
+
"model": "gemini-3-flash",
|
|
416
|
+
"input": [{"role": "user", "content": [{"type": "input_text", "text": "Say pong"}]}],
|
|
417
|
+
"stream": false
|
|
418
|
+
}'
|
|
419
|
+
```
|
|
420
|
+
|
|
339
421
|
**Anthropic-compatible example:**
|
|
340
422
|
|
|
341
423
|
```bash
|
|
@@ -353,13 +435,56 @@ curl http://localhost:51200/v1/messages \
|
|
|
353
435
|
Current adapter scope:
|
|
354
436
|
|
|
355
437
|
- Text chat/messages.
|
|
438
|
+
- **Responses API compatibility**: Supports `POST /v1/responses` plus basic in-memory retrieve/delete/cancel/input-items endpoints for Codex-style agents.
|
|
356
439
|
- **Model Role Support**: Fully supports the `"model"` role in chat message histories (e.g., from Pi or Hermes agents), validating and routing it identically to the `"assistant"` role.
|
|
357
440
|
- **Request Normalization**: Automatically normalizes loose inputs (non-array messages), legacy prompt/input fields (e.g. `prompt` strings/arrays or `input` structures), and raw native Antigravity requests (`request.contents`) into standard OpenAI/Anthropic format.
|
|
358
441
|
- **Native Reasoning visibility**: Models with thinking capabilities (Gemini 3 Pro, Gemini 3.5 Flash, Claude Sonnet 4.6 Thinking) automatically expose their interleaved thinking blocks in real-time as OpenAI `reasoning_content` or Anthropic `thinking_delta` chunks.
|
|
359
442
|
- Streaming mode is supported as compatibility SSE. The adapter buffers the upstream Antigravity stream, then emits one OpenAI/Anthropic-compatible final delta. Native token-by-token pass-through is not implemented yet.
|
|
360
443
|
- Image input is supported when sent as base64 data URL (`OpenAI image_url.url = data:image/...;base64,...`) or Anthropic base64 source (`type=image`, `source.type=base64`).
|
|
361
444
|
- **Tool/function calling is fully supported** (OpenAI `tools`/`tool_choice` format and Anthropic `tool_use`/`tool_result` via standard translation to Gemini `functionDeclarations`).
|
|
445
|
+
- Responses-compatible tool support is currently limited to `type: "function"` tools. Built-in tools like `web_search`, `file_search`, `computer`, or `code_interpreter` are rejected explicitly.
|
|
446
|
+
|
|
447
|
+
|
|
448
|
+
## Connecting Codex / VS Code Agents
|
|
449
|
+
|
|
450
|
+
`pi-antigravity-rotator` can act as the multi-account rotation backend for agentic frameworks, including **Codex** executing in VS Code or in the terminal.
|
|
451
|
+
|
|
452
|
+
Since Codex uses the standard **OpenAI Responses API**, it can seamlessly route its developer-agent workflows through the rotator.
|
|
362
453
|
|
|
454
|
+
### Configuration for Codex
|
|
455
|
+
|
|
456
|
+
To connect Codex to your local rotator:
|
|
457
|
+
|
|
458
|
+
1. **Configure the API Base URL**:
|
|
459
|
+
In Codex settings (e.g. in your `.codex` config or VS Code configuration), set the OpenAI API base URL to point to your rotator's compatibility adapter:
|
|
460
|
+
```json
|
|
461
|
+
"codex.openai.apiBase": "http://localhost:51200/v1"
|
|
462
|
+
```
|
|
463
|
+
*(Or set the environment variable `OPENAI_BASE_URL=http://localhost:51200/v1` in the workspace shell).*
|
|
464
|
+
|
|
465
|
+
2. **Set the API Key / Admin Token**:
|
|
466
|
+
If you have set a `PI_ROTATOR_ADMIN_TOKEN` for your rotator, configure that token as the API key. Otherwise, any non-empty placeholder string (e.g., `sk-antigravity`) works:
|
|
467
|
+
```json
|
|
468
|
+
"codex.openai.apiKey": "your-rotator-admin-token-here"
|
|
469
|
+
```
|
|
470
|
+
*(Or set the environment variable `OPENAI_API_KEY=...` in the shell).*
|
|
471
|
+
|
|
472
|
+
3. **Select a Supported Model**:
|
|
473
|
+
Configure Codex to target one of the following models supported by the rotator (which will be mapped to the best available Google Antigravity account/model under the hood):
|
|
474
|
+
- `gemini-3.5-flash` or `gemini-3.5-flash-high` / `gemini-3.5-flash-low` (Recommended for fast general reasoning)
|
|
475
|
+
- `gemini-3-pro` or `gemini-pro-agent` (For deep reasoning)
|
|
476
|
+
- `claude-sonnet-4-6` or `claude-3-5-sonnet` (Alternative routing fallback)
|
|
477
|
+
|
|
478
|
+
Example Codex configuration entry:
|
|
479
|
+
```json
|
|
480
|
+
"codex.model": "gemini-3.5-flash-high"
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
### Features Enabled for Codex Agents
|
|
484
|
+
|
|
485
|
+
- **Native Reasoning Visibility**: If using models with thinking enabled (e.g., `gemini-3.5-flash-high`), interleaved reasoning/thinking blocks are streamed back in real-time as OpenAI `reasoning_content` chunks. This lets Codex inspect the model's inner thoughts before it acts.
|
|
486
|
+
- **Function / Tool Routing**: Function calls emitted by Codex are fully translated to Gemini `functionCalls` and returned back to Codex safely, enabling full agentic capabilities.
|
|
487
|
+
- **Strict Validation**: The rotator strictly validates the Responses input contract and rejects unsupported tools (e.g., `web_search`) proactively to ensure Codex doesn't hit unexpected runtime exceptions.
|
|
363
488
|
|
|
364
489
|
## Development Checks
|
|
365
490
|
|
|
@@ -463,9 +588,3 @@ export PI_ROTATOR_TELEMETRY=off
|
|
|
463
588
|
```
|
|
464
589
|
|
|
465
590
|
Or use any of: `PI_ROTATOR_TELEMETRY=false`, `PI_ROTATOR_TELEMETRY=0`.
|
|
466
|
-
|
|
467
|
-
## Support Me
|
|
468
|
-
|
|
469
|
-
If this tool has helped you optimize your API usage and save costs, consider supporting its development!
|
|
470
|
-
|
|
471
|
-
<a href="https://ko-fi.com/tuxevil" target="_blank"><img src="https://storage.ko-fi.com/cdn/kofi2.png?v=3" height="36" alt="Buy Me a Coffee at ko-fi.com" /></a>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-antigravity-rotator",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"description": "Multi-account rotation proxy for Google Antigravity with per-model routing, real-time quota tracking, and infringement detection",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
package/src/account-store.ts
CHANGED
|
@@ -1,24 +1,59 @@
|
|
|
1
|
-
import { existsSync, mkdirSync, readFileSync
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync } from "node:fs";
|
|
2
2
|
import { homedir } from "node:os";
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import { getAccountsPath } from "./paths.js";
|
|
5
5
|
import type { AccountConfig, Config } from "./types.js";
|
|
6
|
+
import { backupFile, readJsonFile, writeJsonFileAtomic } from "./storage.js";
|
|
7
|
+
import { formatValidationErrors, validateConfig } from "./validators.js";
|
|
6
8
|
|
|
7
9
|
const ACCOUNTS_FILE = getAccountsPath();
|
|
8
10
|
const PI_DIR = join(homedir(), ".pi", "agent");
|
|
9
11
|
const PI_MODELS_FILE = join(PI_DIR, "models.json");
|
|
10
12
|
const PI_AUTH_FILE = join(PI_DIR, "auth.json");
|
|
13
|
+
const TOKEN_USAGE_FILE = join(join(ACCOUNTS_FILE, ".."), "token-usage.json");
|
|
11
14
|
|
|
12
|
-
export function
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
// Corrupted, start fresh
|
|
18
|
-
}
|
|
19
|
-
}
|
|
15
|
+
export function getTokenUsagePath(): string {
|
|
16
|
+
return TOKEN_USAGE_FILE;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function applyConfigDefaults(config: Config): Config {
|
|
20
20
|
return {
|
|
21
|
+
proxyPort: config.proxyPort || 51200,
|
|
22
|
+
bindHost: config.bindHost || process.env.PI_ROTATOR_BIND_HOST || "0.0.0.0",
|
|
23
|
+
routingPolicy: config.routingPolicy || "timer-first",
|
|
24
|
+
requestsPerRotation: config.requestsPerRotation || 5,
|
|
25
|
+
rotateOnQuotaDrop: config.rotateOnQuotaDrop ?? 20,
|
|
26
|
+
quotaPollIntervalMs: config.quotaPollIntervalMs || 300000,
|
|
27
|
+
maxConcurrentRequestsPerAccount: config.maxConcurrentRequestsPerAccount ?? 1,
|
|
28
|
+
maxConcurrentRequestsPerProjectModel: config.maxConcurrentRequestsPerProjectModel ?? 1,
|
|
29
|
+
projectCircuitBreaker429Threshold: config.projectCircuitBreaker429Threshold ?? 3,
|
|
30
|
+
projectCircuitBreakerWindowMs: config.projectCircuitBreakerWindowMs ?? 10 * 60 * 1000,
|
|
31
|
+
projectCircuitBreakerCooldownMs: config.projectCircuitBreakerCooldownMs ?? 60 * 60 * 1000,
|
|
32
|
+
modelCircuitBreaker429Threshold: config.modelCircuitBreaker429Threshold ?? 3,
|
|
33
|
+
modelCircuitBreakerCooldownMs: config.modelCircuitBreakerCooldownMs ?? 6 * 60 * 60 * 1000,
|
|
34
|
+
dailyAccountSlowRequests: config.dailyAccountSlowRequests ?? 250,
|
|
35
|
+
dailyAccountStopRequests: config.dailyAccountStopRequests ?? 350,
|
|
36
|
+
dailyProjectSlowRequests: config.dailyProjectSlowRequests ?? 900,
|
|
37
|
+
dailyProjectStopRequests: config.dailyProjectStopRequests ?? 1200,
|
|
38
|
+
slowModeJitterMinMs: config.slowModeJitterMinMs ?? 8_000,
|
|
39
|
+
slowModeJitterMaxMs: config.slowModeJitterMaxMs ?? 25_000,
|
|
40
|
+
protectivePauseMs: config.protectivePauseMs ?? 21600000,
|
|
41
|
+
useRequestCountRotationWhenQuotaUnknownOnly: config.useRequestCountRotationWhenQuotaUnknownOnly ?? true,
|
|
42
|
+
tokenBucketEnabled: config.tokenBucketEnabled ?? false,
|
|
43
|
+
tokenBucketMaxTokens: config.tokenBucketMaxTokens ?? 50,
|
|
44
|
+
tokenBucketRefillPerMinute: config.tokenBucketRefillPerMinute ?? 6,
|
|
45
|
+
tokenBucketInitialTokens: config.tokenBucketInitialTokens ?? (config.tokenBucketMaxTokens ?? 50),
|
|
46
|
+
accounts: config.accounts.map((account) => ({
|
|
47
|
+
...account,
|
|
48
|
+
tier: account.tier || "unknown",
|
|
49
|
+
})),
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function getDefaultConfig(): Config {
|
|
54
|
+
return applyConfigDefaults({
|
|
21
55
|
proxyPort: 51200,
|
|
56
|
+
accounts: [],
|
|
22
57
|
requestsPerRotation: 5,
|
|
23
58
|
rotateOnQuotaDrop: 20,
|
|
24
59
|
quotaPollIntervalMs: 300000,
|
|
@@ -37,12 +72,34 @@ export function loadOrCreateAccountsConfig(): Config {
|
|
|
37
72
|
slowModeJitterMaxMs: 25_000,
|
|
38
73
|
protectivePauseMs: 21600000,
|
|
39
74
|
useRequestCountRotationWhenQuotaUnknownOnly: true,
|
|
40
|
-
|
|
41
|
-
|
|
75
|
+
tokenBucketEnabled: false,
|
|
76
|
+
tokenBucketMaxTokens: 50,
|
|
77
|
+
tokenBucketRefillPerMinute: 6,
|
|
78
|
+
tokenBucketInitialTokens: 50,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function loadConfigFromDisk(): Config {
|
|
83
|
+
const parsed = readJsonFile<unknown>(ACCOUNTS_FILE);
|
|
84
|
+
if (parsed === null) return getDefaultConfig();
|
|
85
|
+
const validation = validateConfig(parsed);
|
|
86
|
+
if (!validation.ok || !validation.value) {
|
|
87
|
+
throw new Error(formatValidationErrors(validation.errors));
|
|
88
|
+
}
|
|
89
|
+
return applyConfigDefaults(validation.value);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export function loadOrCreateAccountsConfig(): Config {
|
|
93
|
+
try {
|
|
94
|
+
return loadConfigFromDisk();
|
|
95
|
+
} catch {
|
|
96
|
+
return getDefaultConfig();
|
|
97
|
+
}
|
|
42
98
|
}
|
|
43
99
|
|
|
44
100
|
export function saveAccountsConfig(config: Config): void {
|
|
45
|
-
|
|
101
|
+
backupFile(ACCOUNTS_FILE, "accounts");
|
|
102
|
+
writeJsonFileAtomic(ACCOUNTS_FILE, applyConfigDefaults(config));
|
|
46
103
|
}
|
|
47
104
|
|
|
48
105
|
export function addAccountToConfig(entry: AccountConfig): { isNew: boolean } {
|
|
@@ -83,7 +140,7 @@ export function ensurePiModelsConfig(): void {
|
|
|
83
140
|
providers["google-antigravity"] = antigravity;
|
|
84
141
|
models.providers = providers;
|
|
85
142
|
|
|
86
|
-
|
|
143
|
+
writeJsonFileAtomic(PI_MODELS_FILE, models);
|
|
87
144
|
console.log(` Updated ${PI_MODELS_FILE}`);
|
|
88
145
|
}
|
|
89
146
|
|
|
@@ -112,6 +169,6 @@ export function ensurePiAuthConfig(): void {
|
|
|
112
169
|
projectId: "proxy-managed",
|
|
113
170
|
};
|
|
114
171
|
|
|
115
|
-
|
|
172
|
+
writeJsonFileAtomic(PI_AUTH_FILE, auth);
|
|
116
173
|
console.log(` Updated ${PI_AUTH_FILE}`);
|
|
117
174
|
}
|
package/src/cli.ts
CHANGED
|
@@ -38,6 +38,13 @@ switch (command) {
|
|
|
38
38
|
}
|
|
39
39
|
break;
|
|
40
40
|
}
|
|
41
|
+
case "doctor": {
|
|
42
|
+
const { printDoctorReport, runDoctor } = await import("./doctor.js");
|
|
43
|
+
const result = runDoctor();
|
|
44
|
+
printDoctorReport(result);
|
|
45
|
+
process.exit(result.ok ? 0 : 1);
|
|
46
|
+
break;
|
|
47
|
+
}
|
|
41
48
|
default:
|
|
42
49
|
console.log("Pi Antigravity Rotator");
|
|
43
50
|
console.log();
|
|
@@ -45,6 +52,7 @@ switch (command) {
|
|
|
45
52
|
console.log(" pi-antigravity-rotator start Start the proxy (default)");
|
|
46
53
|
console.log(" pi-antigravity-rotator login Add a new Google account");
|
|
47
54
|
console.log(" pi-antigravity-rotator status Show account status (JSON)");
|
|
55
|
+
console.log(" pi-antigravity-rotator doctor Validate config and local state");
|
|
48
56
|
console.log();
|
|
49
57
|
console.log("Options:");
|
|
50
58
|
console.log(" --config-dir <path> Config directory (default: ~/.pi-antigravity-rotator/)");
|