pi-antigravity-rotator 1.9.1 → 1.9.2
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 +7 -0
- package/README.md +15 -3
- package/package.json +1 -1
- package/src/login.ts +4 -2
- package/src/oauth.ts +10 -5
- package/src/onboarding.ts +4 -2
- package/src/types.ts +2 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.9.2] - 2026-04-29
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
- **Project Discovery Without Shared Fallback**: Login now fails if Google does not return a companion project ID. No more shared `rising-fact-p41fc` fallback.
|
|
7
|
+
- **Activation Hint**: Login/discovery errors now tell you to open the account in Antigravity IDE and send one message first.
|
|
8
|
+
- **Activation Docs**: README now documents the first-use activation rule for new accounts.
|
|
9
|
+
|
|
3
10
|
## [1.9.1] - 2026-04-29
|
|
4
11
|
|
|
5
12
|
### Fixed
|
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@ Multi-account rotation proxy for Google Antigravity. Distributes API usage acros
|
|
|
9
9
|
- **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
|
|
10
10
|
- **Smart rotation** -- Rotates only the specific model whose quota dropped, leaving other models on their current accounts
|
|
11
11
|
- **Infringement detection** -- On 403 with infringement/abuse/suspension keywords, the account is immediately flagged and excluded from routing
|
|
12
|
-
- **
|
|
12
|
+
- **Safer 429 handling** -- On provider `429`, stops the current request and avoids cascade-burning sibling accounts
|
|
13
13
|
- **Concurrency guardrails** -- Limits each account to one in-flight request by default to avoid bursty pressure
|
|
14
14
|
- **Operator fresh-window controls** -- You can block new `fresh` window starts globally, then selectively allow specific accounts to override that policy
|
|
15
15
|
- **Protective pause** -- Pauses all routing for several hours after serious ToS/abuse-style flags so the rest of the pool is not burned
|
|
@@ -57,6 +57,7 @@ Run `npm run login` once per Google account:
|
|
|
57
57
|
2. Complete the sign-in and grant permissions
|
|
58
58
|
3. The browser redirects to a `localhost` URL that won't load -- this is expected
|
|
59
59
|
4. Copy the **full URL** from the browser's address bar and paste it into the terminal
|
|
60
|
+
5. If project discovery fails, open that same account in Antigravity IDE, send one message, then rerun login
|
|
60
61
|
|
|
61
62
|
The tool automatically:
|
|
62
63
|
|
|
@@ -66,6 +67,15 @@ The tool automatically:
|
|
|
66
67
|
|
|
67
68
|
Re-running with the same email updates the existing entry.
|
|
68
69
|
|
|
70
|
+
### Activation rule
|
|
71
|
+
|
|
72
|
+
Some accounts do not expose a discoverable `projectId` until they are used once in the Antigravity IDE.
|
|
73
|
+
If login fails at project discovery:
|
|
74
|
+
|
|
75
|
+
1. Open that exact Google account in Antigravity IDE.
|
|
76
|
+
2. Send one message.
|
|
77
|
+
3. Rerun `npm run login`.
|
|
78
|
+
|
|
69
79
|
## Dashboard
|
|
70
80
|
|
|
71
81
|
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`).
|
|
@@ -213,7 +223,8 @@ export PI_AI_ANTIGRAVITY_VERSION=1.107.0
|
|
|
213
223
|
pi-antigravity-rotator start --config-dir /path/to/config
|
|
214
224
|
```
|
|
215
225
|
|
|
216
|
-
`accounts.json` is created automatically by the login command
|
|
226
|
+
`accounts.json` is created automatically by the login command.
|
|
227
|
+
Login now fails if Google does not return a project ID. No shared fallback.
|
|
217
228
|
|
|
218
229
|
```json
|
|
219
230
|
{
|
|
@@ -273,7 +284,8 @@ pi-antigravity-rotator start --config-dir /path/to/config
|
|
|
273
284
|
|-------|-------------|
|
|
274
285
|
| `email` | Google account email (auto-filled by login) |
|
|
275
286
|
| `refreshToken` | OAuth refresh token (auto-filled by login) |
|
|
276
|
-
| `projectId` | Cloud project ID
|
|
287
|
+
| `projectId` | Cloud project ID discovered from Google during login |
|
|
288
|
+
| `projectSource` | Optional metadata: `google` when discovered from Google, `manual` if edited by hand |
|
|
277
289
|
| `label` | Display name on the dashboard (auto-filled, defaults to email username) |
|
|
278
290
|
|
|
279
291
|
## API Endpoints
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-antigravity-rotator",
|
|
3
|
-
"version": "1.9.
|
|
3
|
+
"version": "1.9.2",
|
|
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/login.ts
CHANGED
|
@@ -79,19 +79,21 @@ export async function runLogin(): Promise<void> {
|
|
|
79
79
|
const email = await getUserEmail(tokenData.accessToken);
|
|
80
80
|
|
|
81
81
|
console.log("Discovering project...");
|
|
82
|
-
const
|
|
82
|
+
const project = await discoverProject(tokenData.accessToken);
|
|
83
83
|
|
|
84
84
|
const label = email ? email.split("@")[0] : "Account";
|
|
85
85
|
const entry: AccountConfig = {
|
|
86
86
|
email: email || "unknown@gmail.com",
|
|
87
87
|
refreshToken: tokenData.refreshToken,
|
|
88
|
-
projectId,
|
|
88
|
+
projectId: project.projectId,
|
|
89
|
+
projectSource: project.source,
|
|
89
90
|
label,
|
|
90
91
|
};
|
|
91
92
|
|
|
92
93
|
console.log();
|
|
93
94
|
const { isNew } = addAccountToConfig(entry);
|
|
94
95
|
console.log(` ${isNew ? "Added" : "Updated"} ${entry.email} in ${ACCOUNTS_FILE}`);
|
|
96
|
+
console.log(` projectId=${project.projectId} (source=${project.source})`);
|
|
95
97
|
|
|
96
98
|
ensurePiModelsConfig();
|
|
97
99
|
ensurePiAuthConfig();
|
package/src/oauth.ts
CHANGED
|
@@ -11,7 +11,6 @@ export const SCOPES = [
|
|
|
11
11
|
"https://www.googleapis.com/auth/experimentsandconfigs",
|
|
12
12
|
];
|
|
13
13
|
export const AUTH_URL = "https://accounts.google.com/o/oauth2/v2/auth";
|
|
14
|
-
export const DEFAULT_PROJECT_ID = "rising-fact-p41fc";
|
|
15
14
|
|
|
16
15
|
export interface OAuthClientConfig {
|
|
17
16
|
clientId: string;
|
|
@@ -107,7 +106,13 @@ export async function exchangeAuthorizationCode(code: string, verifier: string):
|
|
|
107
106
|
};
|
|
108
107
|
}
|
|
109
108
|
|
|
110
|
-
export
|
|
109
|
+
export interface ProjectDiscoveryResult {
|
|
110
|
+
projectId: string;
|
|
111
|
+
source: "google";
|
|
112
|
+
endpoint: string;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export async function discoverProject(accessToken: string): Promise<ProjectDiscoveryResult> {
|
|
111
116
|
const headers: Record<string, string> = {
|
|
112
117
|
Authorization: `Bearer ${accessToken}`,
|
|
113
118
|
"Content-Type": "application/json",
|
|
@@ -138,14 +143,14 @@ export async function discoverProject(accessToken: string): Promise<string> {
|
|
|
138
143
|
cloudaicompanionProject?: string | { id?: string };
|
|
139
144
|
};
|
|
140
145
|
if (typeof data.cloudaicompanionProject === "string" && data.cloudaicompanionProject) {
|
|
141
|
-
return data.cloudaicompanionProject;
|
|
146
|
+
return { projectId: data.cloudaicompanionProject, source: "google", endpoint };
|
|
142
147
|
}
|
|
143
148
|
if (
|
|
144
149
|
data.cloudaicompanionProject &&
|
|
145
150
|
typeof data.cloudaicompanionProject === "object" &&
|
|
146
151
|
data.cloudaicompanionProject.id
|
|
147
152
|
) {
|
|
148
|
-
return data.cloudaicompanionProject.id;
|
|
153
|
+
return { projectId: data.cloudaicompanionProject.id, source: "google", endpoint };
|
|
149
154
|
}
|
|
150
155
|
}
|
|
151
156
|
} catch {
|
|
@@ -153,7 +158,7 @@ export async function discoverProject(accessToken: string): Promise<string> {
|
|
|
153
158
|
}
|
|
154
159
|
}
|
|
155
160
|
|
|
156
|
-
|
|
161
|
+
throw new Error("Could not discover Cloud Code companion project ID from Google. If this account is new, open it in Antigravity IDE and send one message first, then retry login. Login failed instead of falling back to a shared projectId.");
|
|
157
162
|
}
|
|
158
163
|
|
|
159
164
|
export async function getUserEmail(accessToken: string): Promise<string | undefined> {
|
package/src/onboarding.ts
CHANGED
|
@@ -203,12 +203,13 @@ export async function handleHostedCallback(
|
|
|
203
203
|
try {
|
|
204
204
|
const tokenData = await exchangeAuthorizationCode(code, session.verifier);
|
|
205
205
|
const email = await getUserEmail(tokenData.accessToken);
|
|
206
|
-
const
|
|
206
|
+
const project = await discoverProject(tokenData.accessToken);
|
|
207
207
|
const label = email ? email.split("@")[0] : "Account";
|
|
208
208
|
const entry = {
|
|
209
209
|
email: email || "unknown@gmail.com",
|
|
210
210
|
refreshToken: tokenData.refreshToken,
|
|
211
|
-
projectId,
|
|
211
|
+
projectId: project.projectId,
|
|
212
|
+
projectSource: project.source,
|
|
212
213
|
label,
|
|
213
214
|
};
|
|
214
215
|
|
|
@@ -221,6 +222,7 @@ export async function handleHostedCallback(
|
|
|
221
222
|
"Account Connected",
|
|
222
223
|
`<h1>Account Connected</h1>
|
|
223
224
|
<p><strong>${entry.email}</strong> was ${isNew ? "added" : "updated"} successfully.</p>
|
|
225
|
+
<p>Project: <span class="mono">${project.projectId}</span> via ${project.source}.</p>
|
|
224
226
|
<p>The rotator can start using this account immediately.</p>
|
|
225
227
|
<div class="note">If you ever want to stop sharing access, revoke this app's access from the Google account security settings.</div>`,
|
|
226
228
|
),
|
package/src/types.ts
CHANGED
|
@@ -6,6 +6,8 @@ export interface AccountConfig {
|
|
|
6
6
|
email: string;
|
|
7
7
|
refreshToken: string;
|
|
8
8
|
projectId: string;
|
|
9
|
+
// How the projectId was obtained.
|
|
10
|
+
projectSource?: "google" | "manual";
|
|
9
11
|
label?: string;
|
|
10
12
|
// Optional - pro/free is detected dynamically from quota API reset times
|
|
11
13
|
type?: AccountType;
|