pi-antigravity-rotator 1.13.0 → 2.0.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 +38 -0
- package/README.md +69 -9
- package/package.json +1 -1
- package/src/account-store.ts +71 -14
- package/src/cli.ts +8 -0
- package/src/compat.ts +252 -28
- package/src/dashboard.ts +287 -9
- package/src/doctor.ts +97 -0
- package/src/index.ts +21 -37
- package/src/paths.ts +4 -0
- package/src/proxy.ts +107 -75
- package/src/rate-limit-parser.ts +126 -0
- package/src/rotator.ts +469 -51
- package/src/storage.ts +56 -0
- package/src/types.ts +118 -18
- package/src/validators.ts +19 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,43 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [Unreleased]
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- **Hybrid Routing Policy**: Added optional `routingPolicy: "hybrid"` with weighted selection across timer priority, quota, tier, health, local token bucket state, and distance.
|
|
7
|
+
- **Routing Inspector**: Added a dashboard modal that explains the currently selected route, candidate scores, and why each account was excluded for a model.
|
|
8
|
+
- **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.
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
- **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.
|
|
12
|
+
- **Attention Needed Coverage**: The dashboard now surfaces unroutable models and token-bucket exhaustion alongside existing security, cooldown, disabled, flagged, and error alerts.
|
|
13
|
+
- **Compat Hardening**: Added coverage for `cache_control` stripping, schema forwarding, missing-signature tool history, and empty SSE parsing.
|
|
14
|
+
|
|
15
|
+
## [2.0.0] - 2026-05-20
|
|
16
|
+
|
|
17
|
+
### Added
|
|
18
|
+
- **Admin Config APIs**: Added `GET /api/config`, `PUT /api/config`, `GET /api/config/export`, and `POST /api/config/import` for validated runtime config management.
|
|
19
|
+
- **Dashboard Config Editor**: Added an embedded JSON editor with load/save/import/export controls and hosted login access.
|
|
20
|
+
- **Docker Deployment**: Added `Dockerfile`, `docker-compose.yml`, and `.dockerignore` for headless deployments with persistent `/data`.
|
|
21
|
+
- **Doctor Command**: Added `pi-antigravity-rotator doctor` to validate config, inspect backups, and report missing admin auth.
|
|
22
|
+
- **Gemini-Compatible Discovery**: Added `/v1beta/models` and a minimal Gemini-style `generateContent` route family.
|
|
23
|
+
|
|
24
|
+
### Changed
|
|
25
|
+
- **Version 2.0**: Bumped package version to `2.0.0` on branch `v2.0`.
|
|
26
|
+
- **Persistence Hardening**: Config, state, and token usage now write atomically with timestamped backups.
|
|
27
|
+
- **Routing Metadata**: Added optional account `tier` plus runtime `healthScore` as timer-first tie-breakers.
|
|
28
|
+
- **Security Visibility**: Startup logs, `/api/status`, and the dashboard now warn when `PI_ROTATOR_ADMIN_TOKEN` is missing.
|
|
29
|
+
|
|
30
|
+
### Migration
|
|
31
|
+
- Existing `accounts.json` stays compatible. New defaults are `bindHost: "0.0.0.0"`, `routingPolicy: "timer-first"`, and `accounts[].tier: "unknown"`.
|
|
32
|
+
|
|
33
|
+
## [1.14.0] - 2026-05-19
|
|
34
|
+
|
|
35
|
+
### Added
|
|
36
|
+
- **Gemini 3.5 Flash Support**: Added routing and dashboard support for the new `gemini-3.5-flash` model family (including `gemini-3.5-flash-low` / `gemini-3.5-flash-medium` and `gemini-3-flash-agent` / `gemini-3.5-flash-high`).
|
|
37
|
+
- **GPT-OSS 120B Support**: Added complete support (pricing, styling, and dashboard visualization) for the `gpt-oss-120b-medium` model, mapping its quota tracking to the shared Claude pool (`claude-opus-4-6-thinking`).
|
|
38
|
+
- **Model Role Support**: Added support for the `"model"` role in compatibility layer chat completions, validating and mapping it to native Gemini model turns.
|
|
39
|
+
- **Request Normalization**: Added normalization helpers (`normalizeOpenAIChatCompletionRequest` / `normalizeAnthropicMessagesRequest`) to automatically format loose inputs, Responses-style inputs (e.g., `input`, `prompt`), and raw native Antigravity request payloads into standard OpenAI/Anthropic messages format.
|
|
40
|
+
|
|
3
41
|
## [1.13.0] - 2026-05-19
|
|
4
42
|
|
|
5
43
|
### Removed
|
package/README.md
CHANGED
|
@@ -1,7 +1,40 @@
|
|
|
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
|
|
|
7
40
|
- **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.
|
|
@@ -49,6 +82,14 @@ npm run login
|
|
|
49
82
|
npm start
|
|
50
83
|
```
|
|
51
84
|
|
|
85
|
+
### Option C: Docker
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
docker compose up -d
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
The included compose file persists runtime data under `./docker-data` and sets `PI_ROTATOR_DIR=/data`.
|
|
92
|
+
|
|
52
93
|
## Adding Accounts
|
|
53
94
|
|
|
54
95
|
Run `npm run login` once per Google account:
|
|
@@ -74,7 +115,7 @@ This package is not exclusive to Pi. It can be consumed by **any** agent or fron
|
|
|
74
115
|
2. Add a new generic/OpenAI-compatible provider
|
|
75
116
|
3. Set the API Base URL to: `http://127.0.0.1:51200/v1/`
|
|
76
117
|
4. Set the API Key to: `antigravity` (or any string, the proxy doesn't validate it)
|
|
77
|
-
5. You can now use any of the models (e.g. `gemini-3-flash`, `gemini-3.1-pro-low`, `claude-sonnet-4-6`, `claude-opus-4-6-thinking`) directly in your agent.
|
|
118
|
+
5. You can now use any of the models (e.g. `gemini-3.5-flash-low`, `gemini-3.5-flash-high`, `gemini-3.1-pro-low`, `claude-sonnet-4-6`, `claude-opus-4-6-thinking`, `gpt-oss-120b-medium`) directly in your agent.
|
|
78
119
|
|
|
79
120
|
### Activation rule
|
|
80
121
|
|
|
@@ -89,6 +130,8 @@ If login fails at project discovery:
|
|
|
89
130
|
|
|
90
131
|
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
132
|
|
|
133
|
+
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.
|
|
134
|
+
|
|
92
135
|
The dashboard shows:
|
|
93
136
|
|
|
94
137
|
- **Top Status & Controls** -- Real-time routing state, uptime, requests, and PII masking toggle.
|
|
@@ -98,7 +141,8 @@ The dashboard shows:
|
|
|
98
141
|
- **Quota Forecast** -- Predictive modeling showing when each model's quota will run out based on the current requests/hour burn rate.
|
|
99
142
|
- **Searchable Request Log** -- Live feed of the last 200 requests with exact timestamps, models, masked accounts, status codes, and latency.
|
|
100
143
|
- **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.
|
|
144
|
+
- **Operator Panels** -- "Attention Needed" summaries for quarantined accounts, unroutable models, token-bucket pressure, and a real-time event feed of rotator actions.
|
|
145
|
+
- **Routing Inspector** -- On-demand modal showing the active routing policy, candidate scores, local token bucket state, and rejection reasons per model.
|
|
102
146
|
|
|
103
147
|

|
|
104
148
|
|
|
@@ -220,6 +264,8 @@ export PI_ROTATOR_DIR=/path/to/config
|
|
|
220
264
|
export PI_ROTATOR_QUOTA_USER_AGENT="antigravity/1.107.0 darwin/arm64"
|
|
221
265
|
# Optional: require this token for dashboard/API access. If unset, legacy open access is preserved.
|
|
222
266
|
export PI_ROTATOR_ADMIN_TOKEN="change-me"
|
|
267
|
+
# Optional: bind the proxy to a safer local-only interface.
|
|
268
|
+
export PI_ROTATOR_BIND_HOST="127.0.0.1"
|
|
223
269
|
# Optional: max accepted proxy request body size in bytes. Default: 26214400 (25 MiB).
|
|
224
270
|
export PI_ROTATOR_MAX_BODY_BYTES=26214400
|
|
225
271
|
# Optional: log verbosity. One of debug, info, warn, error, silent. Default: info.
|
|
@@ -231,6 +277,24 @@ export PI_AI_ANTIGRAVITY_VERSION=1.107.0
|
|
|
231
277
|
pi-antigravity-rotator start --config-dir /path/to/config
|
|
232
278
|
```
|
|
233
279
|
|
|
280
|
+
New v2.0 config fields:
|
|
281
|
+
|
|
282
|
+
- `bindHost`: interface to bind on. Default: `0.0.0.0`.
|
|
283
|
+
- `routingPolicy`: current default is `timer-first`. Optional values now include `tier-first`, `quota-first`, and `hybrid`.
|
|
284
|
+
- `tokenBucketEnabled`: enables the local per-account request bucket used by `hybrid`. Default: `false`.
|
|
285
|
+
- `tokenBucketMaxTokens`: bucket capacity when enabled. Default: `50`.
|
|
286
|
+
- `tokenBucketRefillPerMinute`: refill speed when enabled. Default: `6`.
|
|
287
|
+
- `tokenBucketInitialTokens`: startup fill level when enabled. Default: `50`.
|
|
288
|
+
- `accounts[].tier`: optional `ultra`, `pro`, `free`, or `unknown`.
|
|
289
|
+
|
|
290
|
+
## Doctor
|
|
291
|
+
|
|
292
|
+
```bash
|
|
293
|
+
pi-antigravity-rotator doctor
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
This validates `accounts.json`, checks local state files, lists backups, and warns when admin auth is not configured.
|
|
297
|
+
|
|
234
298
|
`accounts.json` is created automatically by the login command.
|
|
235
299
|
Login now fails if Google does not return a project ID. No shared fallback.
|
|
236
300
|
|
|
@@ -353,7 +417,9 @@ curl http://localhost:51200/v1/messages \
|
|
|
353
417
|
Current adapter scope:
|
|
354
418
|
|
|
355
419
|
- Text chat/messages.
|
|
356
|
-
- **
|
|
420
|
+
- **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.
|
|
421
|
+
- **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.
|
|
422
|
+
- **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.
|
|
357
423
|
- 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.
|
|
358
424
|
- 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`).
|
|
359
425
|
- **Tool/function calling is fully supported** (OpenAI `tools`/`tool_choice` format and Anthropic `tool_use`/`tool_result` via standard translation to Gemini `functionDeclarations`).
|
|
@@ -461,9 +527,3 @@ export PI_ROTATOR_TELEMETRY=off
|
|
|
461
527
|
```
|
|
462
528
|
|
|
463
529
|
Or use any of: `PI_ROTATOR_TELEMETRY=false`, `PI_ROTATOR_TELEMETRY=0`.
|
|
464
|
-
|
|
465
|
-
## Support Me
|
|
466
|
-
|
|
467
|
-
If this tool has helped you optimize your API usage and save costs, consider supporting its development!
|
|
468
|
-
|
|
469
|
-
<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": "
|
|
3
|
+
"version": "2.0.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/)");
|