opencode-antigravity-auth 1.6.5-beta.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/README.md +34 -694
- package/dist/index.d.ts +10 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -2
- package/dist/index.js.map +1 -1
- package/dist/src/antigravity/oauth.d.ts +0 -28
- package/dist/src/antigravity/oauth.d.ts.map +1 -1
- package/dist/src/antigravity/oauth.js +25 -103
- package/dist/src/antigravity/oauth.js.map +1 -1
- package/dist/src/constants.d.ts +53 -32
- package/dist/src/constants.d.ts.map +1 -1
- package/dist/src/constants.js +64 -56
- package/dist/src/constants.js.map +1 -1
- package/dist/src/hooks/auto-update-checker/cache.js +1 -1
- package/dist/src/hooks/auto-update-checker/checker.d.ts +1 -1
- package/dist/src/hooks/auto-update-checker/checker.js +2 -2
- package/dist/src/hooks/auto-update-checker/index.d.ts +4 -4
- package/dist/src/hooks/auto-update-checker/index.js +6 -6
- package/dist/src/hooks/auto-update-checker/logging.js +1 -1
- package/dist/src/plugin/account-management.d.ts +67 -0
- package/dist/src/plugin/account-management.d.ts.map +1 -0
- package/dist/src/plugin/account-management.js +174 -0
- package/dist/src/plugin/account-management.js.map +1 -0
- package/dist/src/plugin/account-rpc.d.ts +70 -0
- package/dist/src/plugin/account-rpc.d.ts.map +1 -0
- package/dist/src/plugin/account-rpc.js +28 -0
- package/dist/src/plugin/account-rpc.js.map +1 -0
- package/dist/src/plugin/accounts.d.ts +32 -19
- package/dist/src/plugin/accounts.d.ts.map +1 -1
- package/dist/src/plugin/accounts.js +203 -34
- package/dist/src/plugin/accounts.js.map +1 -1
- package/dist/src/plugin/auth.d.ts +1 -1
- package/dist/src/plugin/cache/index.d.ts +1 -1
- package/dist/src/plugin/cache/index.js +1 -1
- package/dist/src/plugin/cache/signature-cache.d.ts +1 -1
- package/dist/src/plugin/cache/signature-cache.js +1 -1
- package/dist/src/plugin/cache.d.ts +7 -5
- package/dist/src/plugin/cache.d.ts.map +1 -1
- package/dist/src/plugin/cache.js +20 -3
- package/dist/src/plugin/cache.js.map +1 -1
- package/dist/src/plugin/cli.d.ts +4 -6
- package/dist/src/plugin/cli.d.ts.map +1 -1
- package/dist/src/plugin/cli.js +5 -11
- package/dist/src/plugin/cli.js.map +1 -1
- package/dist/src/plugin/config/index.d.ts +2 -2
- package/dist/src/plugin/config/index.js +2 -2
- package/dist/src/plugin/config/loader.d.ts +1 -1
- package/dist/src/plugin/config/loader.js +2 -2
- package/dist/src/plugin/config/models-v2.d.ts +54 -0
- package/dist/src/plugin/config/models-v2.d.ts.map +1 -0
- package/dist/src/plugin/config/models-v2.js +62 -0
- package/dist/src/plugin/config/models-v2.js.map +1 -0
- package/dist/src/plugin/config/models.d.ts +1 -1
- package/dist/src/plugin/config/updater.d.ts +16 -3
- package/dist/src/plugin/config/updater.d.ts.map +1 -1
- package/dist/src/plugin/config/updater.js +53 -10
- package/dist/src/plugin/config/updater.js.map +1 -1
- package/dist/src/plugin/core/streaming/index.d.ts +2 -2
- package/dist/src/plugin/core/streaming/index.js +2 -2
- package/dist/src/plugin/core/streaming/transformer.d.ts +1 -1
- package/dist/src/plugin/core/streaming/transformer.js +1 -1
- package/dist/src/plugin/debug.d.ts +1 -1
- package/dist/src/plugin/debug.js +2 -2
- package/dist/src/plugin/fingerprint.d.ts +8 -5
- package/dist/src/plugin/fingerprint.d.ts.map +1 -1
- package/dist/src/plugin/fingerprint.js +23 -41
- package/dist/src/plugin/fingerprint.js.map +1 -1
- package/dist/src/plugin/logger.d.ts +1 -1
- package/dist/src/plugin/logger.js +2 -2
- package/dist/src/plugin/project.d.ts +4 -4
- package/dist/src/plugin/project.d.ts.map +1 -1
- package/dist/src/plugin/project.js +95 -94
- package/dist/src/plugin/project.js.map +1 -1
- package/dist/src/plugin/quota.d.ts +35 -3
- package/dist/src/plugin/quota.d.ts.map +1 -1
- package/dist/src/plugin/quota.js +54 -25
- package/dist/src/plugin/quota.js.map +1 -1
- package/dist/src/plugin/recovery/index.d.ts +3 -3
- package/dist/src/plugin/recovery/index.js +3 -3
- package/dist/src/plugin/recovery/storage.d.ts +1 -1
- package/dist/src/plugin/recovery/storage.js +1 -1
- package/dist/src/plugin/recovery.d.ts +3 -3
- package/dist/src/plugin/recovery.js +3 -3
- package/dist/src/plugin/refresh-queue.d.ts +6 -3
- package/dist/src/plugin/refresh-queue.d.ts.map +1 -1
- package/dist/src/plugin/refresh-queue.js +23 -6
- package/dist/src/plugin/refresh-queue.js.map +1 -1
- package/dist/src/plugin/request-helpers.d.ts +1 -1
- package/dist/src/plugin/request-helpers.js +5 -5
- package/dist/src/plugin/request.d.ts +9 -6
- package/dist/src/plugin/request.d.ts.map +1 -1
- package/dist/src/plugin/request.js +23 -23
- package/dist/src/plugin/request.js.map +1 -1
- package/dist/src/plugin/search.js +7 -7
- package/dist/src/plugin/search.js.map +1 -1
- package/dist/src/plugin/server.js +1 -1
- package/dist/src/plugin/storage.d.ts +18 -5
- package/dist/src/plugin/storage.d.ts.map +1 -1
- package/dist/src/plugin/storage.js +117 -69
- package/dist/src/plugin/storage.js.map +1 -1
- package/dist/src/plugin/stores/signature-store.d.ts +1 -1
- package/dist/src/plugin/token.d.ts +2 -4
- package/dist/src/plugin/token.d.ts.map +1 -1
- package/dist/src/plugin/token.js +48 -8
- package/dist/src/plugin/token.js.map +1 -1
- package/dist/src/plugin/transform/claude.d.ts +1 -1
- package/dist/src/plugin/transform/claude.js +1 -1
- package/dist/src/plugin/transform/cross-model-sanitizer.js +2 -2
- package/dist/src/plugin/transform/gemini.d.ts +1 -1
- package/dist/src/plugin/transform/index.d.ts +9 -9
- package/dist/src/plugin/transform/index.js +4 -4
- package/dist/src/plugin/transform/model-resolver.d.ts +9 -1
- package/dist/src/plugin/transform/model-resolver.d.ts.map +1 -1
- package/dist/src/plugin/transform/model-resolver.js +34 -4
- package/dist/src/plugin/transform/model-resolver.js.map +1 -1
- package/dist/src/plugin/transform/types.d.ts +1 -1
- package/dist/src/plugin/types.d.ts +31 -2
- package/dist/src/plugin/types.d.ts.map +1 -1
- package/dist/src/plugin/ui/auth-menu.d.ts +1 -4
- package/dist/src/plugin/ui/auth-menu.d.ts.map +1 -1
- package/dist/src/plugin/ui/auth-menu.js +8 -16
- package/dist/src/plugin/ui/auth-menu.js.map +1 -1
- package/dist/src/plugin/ui/confirm.js +1 -1
- package/dist/src/plugin/ui/select.js +1 -1
- package/dist/src/plugin/version.d.ts +35 -6
- package/dist/src/plugin/version.d.ts.map +1 -1
- package/dist/src/plugin/version.js +138 -42
- package/dist/src/plugin/version.js.map +1 -1
- package/dist/src/plugin.d.ts +11 -8
- package/dist/src/plugin.d.ts.map +1 -1
- package/dist/src/plugin.js +170 -291
- package/dist/src/plugin.js.map +1 -1
- package/dist/src/tui.d.ts +64 -0
- package/dist/src/tui.d.ts.map +1 -0
- package/dist/src/tui.js +124 -0
- package/dist/src/tui.js.map +1 -0
- package/dist/src/v2.d.ts +4 -0
- package/dist/src/v2.d.ts.map +1 -0
- package/dist/src/v2.js +672 -0
- package/dist/src/v2.js.map +1 -0
- package/index.js +4 -0
- package/package.json +21 -7
- package/tui.js +1 -0
package/dist/src/v2.js
ADDED
|
@@ -0,0 +1,672 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
|
+
import { createRequire } from "node:module";
|
|
3
|
+
import { pathToFileURL } from "node:url";
|
|
4
|
+
import { createGoogleGenerativeAI } from "@ai-sdk/google";
|
|
5
|
+
import { Credential, Integration, Model, Plugin, Provider } from "@opencode/plugin";
|
|
6
|
+
import { authorizeAntigravity, exchangeAntigravity } from "./antigravity/oauth.js";
|
|
7
|
+
import { ANTIGRAVITY_REDIRECT_URI } from "./constants.js";
|
|
8
|
+
import { createAntigravityPlugin } from "./plugin.js";
|
|
9
|
+
import { accessTokenExpired, formatRefreshParts, parseRefreshParts } from "./plugin/auth.js";
|
|
10
|
+
import { OPENCODE_V2_MODEL_DEFINITIONS, buildAccountModelDefinitions, ModelCatalogCacheSchema } from "./plugin/config/models-v2.js";
|
|
11
|
+
import { accountID, accountPoolIdentity, accountCredentialsIdentity, loadManagedAccounts, registerAccountManagement } from "./plugin/account-management.js";
|
|
12
|
+
import { checkAccountsQuota } from "./plugin/quota.js";
|
|
13
|
+
import { createLogger } from "./plugin/logger.js";
|
|
14
|
+
import { ensureProjectContext } from "./plugin/project.js";
|
|
15
|
+
import { executeSearch } from "./plugin/search.js";
|
|
16
|
+
import { startOAuthListener } from "./plugin/server.js";
|
|
17
|
+
import { loadAccounts, matchesAccountToken, saveAccountCredentials, updateAccountCredentials, updateAccounts } from "./plugin/storage.js";
|
|
18
|
+
import { AntigravityTokenRefreshError, refreshAccessToken } from "./plugin/token.js";
|
|
19
|
+
import { resolveModelWithTier } from "./plugin/transform/model-resolver.js";
|
|
20
|
+
const PROVIDER_ID = "google";
|
|
21
|
+
const METHOD_ID = "antigravity";
|
|
22
|
+
// Core rewrites the standard Google SDK package to its native transport after
|
|
23
|
+
// every provider/model update. Its local module URL avoids that conversion and
|
|
24
|
+
// lets Core's earlier generic SDK loader use our installed dependency directly.
|
|
25
|
+
const SDK_PACKAGE = typeof import.meta.resolve === "function"
|
|
26
|
+
? import.meta.resolve("@ai-sdk/google")
|
|
27
|
+
: pathToFileURL(createRequire(import.meta.url).resolve("@ai-sdk/google")).href;
|
|
28
|
+
const GOOGLE_PACKAGE = `aisdk:${SDK_PACKAGE}`;
|
|
29
|
+
const API_KEY_SENTINEL = "antigravity-oauth";
|
|
30
|
+
const ownsModel = (id) => id.startsWith("antigravity-");
|
|
31
|
+
function mergeBody(base, overlay) {
|
|
32
|
+
const result = { ...base };
|
|
33
|
+
for (const [key, value] of Object.entries(overlay)) {
|
|
34
|
+
const previous = result[key];
|
|
35
|
+
if (value === undefined)
|
|
36
|
+
continue;
|
|
37
|
+
result[key] = value && typeof value === "object" && !Array.isArray(value)
|
|
38
|
+
&& previous && typeof previous === "object" && !Array.isArray(previous)
|
|
39
|
+
? mergeBody(previous, value)
|
|
40
|
+
: value;
|
|
41
|
+
}
|
|
42
|
+
return result;
|
|
43
|
+
}
|
|
44
|
+
// V2 owns credentials, notifications and session repair. The legacy transport
|
|
45
|
+
// only needs these methods to be awaitable; it never writes V2 session files.
|
|
46
|
+
function transportClient() {
|
|
47
|
+
const unusedSession = async () => {
|
|
48
|
+
throw new Error("Legacy session recovery is unavailable in OpenCode V2");
|
|
49
|
+
};
|
|
50
|
+
return {
|
|
51
|
+
app: { log: async () => undefined },
|
|
52
|
+
auth: { set: async () => undefined },
|
|
53
|
+
tui: { showToast: async () => undefined },
|
|
54
|
+
session: { prompt: unusedSession, abort: unusedSession, messages: unusedSession },
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
function oauthCredential(auth, metadata) {
|
|
58
|
+
return {
|
|
59
|
+
type: "oauth",
|
|
60
|
+
methodID: Integration.MethodID.make(METHOD_ID),
|
|
61
|
+
refresh: auth.refresh,
|
|
62
|
+
access: auth.access ?? "",
|
|
63
|
+
expires: auth.expires ?? 0,
|
|
64
|
+
...(metadata ? { metadata } : {}),
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
function isAntigravityCredential(value) {
|
|
68
|
+
return value?.type === "oauth" && value.methodID === METHOD_ID && !!value.refresh;
|
|
69
|
+
}
|
|
70
|
+
/** OpenCode 2.1's source transforms and AI SDK 3 transport adapter. */
|
|
71
|
+
export const AntigravityV2Plugin = Plugin.define({
|
|
72
|
+
id: "opencode-antigravity-auth",
|
|
73
|
+
async setup(ctx) {
|
|
74
|
+
const client = transportClient();
|
|
75
|
+
const outbound = new AsyncLocalStorage();
|
|
76
|
+
let modelAccounts = {};
|
|
77
|
+
let accountCatalogLoaded = false;
|
|
78
|
+
const transport = await createAntigravityPlugin(PROVIDER_ID)({
|
|
79
|
+
client,
|
|
80
|
+
directory: ctx.location.directory,
|
|
81
|
+
runtimeVersion: 2,
|
|
82
|
+
canUseAccount: (account, model) => {
|
|
83
|
+
const eligible = modelAccounts[`antigravity-${resolveModelWithTier(model).actualModel}`];
|
|
84
|
+
return !accountCatalogLoaded || !!eligible?.includes(accountID(account));
|
|
85
|
+
},
|
|
86
|
+
fetch: (input, init) => {
|
|
87
|
+
const send = outbound.getStore() ?? globalThis.fetch;
|
|
88
|
+
// Core's wrapper applies model.body to string init.body. It has already
|
|
89
|
+
// been applied to the Google request before interception, so a Request
|
|
90
|
+
// prevents applying that overlay again to the Antigravity envelope.
|
|
91
|
+
const request = new Request(input, init);
|
|
92
|
+
request.headers.delete("x-goog-api-key");
|
|
93
|
+
return send(request, { signal: init?.signal });
|
|
94
|
+
},
|
|
95
|
+
});
|
|
96
|
+
const registrations = [];
|
|
97
|
+
const saved = await loadManagedAccounts();
|
|
98
|
+
let hasSavedAccounts = saved?.accounts.some((account) => account.enabled !== false && !!account.refreshToken);
|
|
99
|
+
const log = createLogger("v2");
|
|
100
|
+
const cachedResult = ModelCatalogCacheSchema.safeParse(await ctx.storage.get("model-catalog"));
|
|
101
|
+
const cached = cachedResult.success ? cachedResult.data : undefined;
|
|
102
|
+
const validCache = cached?.identity === accountPoolIdentity(saved?.accounts ?? []) && cached.models;
|
|
103
|
+
let modelDefinitions = validCache ? buildAccountModelDefinitions(validCache) : OPENCODE_V2_MODEL_DEFINITIONS;
|
|
104
|
+
accountCatalogLoaded = !!validCache;
|
|
105
|
+
if (validCache)
|
|
106
|
+
modelAccounts = cached.accounts;
|
|
107
|
+
let discoveryGeneration = 0;
|
|
108
|
+
let catalogSnapshot;
|
|
109
|
+
const catalogWrites = new Set();
|
|
110
|
+
const discoveries = new Set();
|
|
111
|
+
const writeCatalog = async () => {
|
|
112
|
+
let pending = catalogSnapshot;
|
|
113
|
+
while (pending !== undefined) {
|
|
114
|
+
await ctx.storage.set("model-catalog", pending);
|
|
115
|
+
if (pending === catalogSnapshot)
|
|
116
|
+
return;
|
|
117
|
+
pending = catalogSnapshot;
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
const discoverModels = async () => {
|
|
121
|
+
const discovery = ++discoveryGeneration;
|
|
122
|
+
await resolveAuth();
|
|
123
|
+
const stored = await loadManagedAccounts();
|
|
124
|
+
const accounts = stored?.accounts.filter(account => account.enabled !== false) ?? [];
|
|
125
|
+
hasSavedAccounts = accounts.length > 0;
|
|
126
|
+
if (!accounts.length)
|
|
127
|
+
throw new Error("No enabled Google accounts. Add or enable an account first.");
|
|
128
|
+
const results = await checkAccountsQuota(accounts, client, PROVIDER_ID, {
|
|
129
|
+
onAccountUpdated: saveAccountCredentials,
|
|
130
|
+
signal: lifetime.signal,
|
|
131
|
+
});
|
|
132
|
+
const successful = results.filter(result => result.quota?.models && Object.keys(result.quota.models).length);
|
|
133
|
+
const models = {};
|
|
134
|
+
const eligibleAccounts = {};
|
|
135
|
+
if (discovery === discoveryGeneration) {
|
|
136
|
+
invalidateLoader();
|
|
137
|
+
await transport.flush?.();
|
|
138
|
+
}
|
|
139
|
+
const latest = await updateAccounts(latest => {
|
|
140
|
+
for (const result of results) {
|
|
141
|
+
const original = accounts[result.index];
|
|
142
|
+
if (!original)
|
|
143
|
+
continue;
|
|
144
|
+
const account = latest.accounts.find(item => accountID(item) === accountID(original));
|
|
145
|
+
if (!account)
|
|
146
|
+
continue;
|
|
147
|
+
if (result.updatedAccount && account.refreshToken === original.refreshToken) {
|
|
148
|
+
updateAccountCredentials(account, result.updatedAccount);
|
|
149
|
+
}
|
|
150
|
+
if (account.enabled === false)
|
|
151
|
+
continue;
|
|
152
|
+
if (!result.quota?.models)
|
|
153
|
+
continue;
|
|
154
|
+
for (const id of Object.keys(buildAccountModelDefinitions(result.quota.models))) {
|
|
155
|
+
(eligibleAccounts[id] ??= []).push(accountID(account));
|
|
156
|
+
}
|
|
157
|
+
for (const [id, model] of Object.entries(result.quota?.models ?? {})) {
|
|
158
|
+
const usable = (value) => !value.disabled && value.selectable !== false;
|
|
159
|
+
if (!models[id] || !usable(models[id]) || usable(model))
|
|
160
|
+
models[id] = model;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
// Credentials remain valuable after a concurrent pool change, but only
|
|
165
|
+
// the newest complete discovery for this pool may replace the catalog.
|
|
166
|
+
if (disposed || discovery !== discoveryGeneration || accountPoolIdentity(latest?.accounts ?? []) !== accountPoolIdentity(accounts))
|
|
167
|
+
return Object.keys(modelDefinitions).length;
|
|
168
|
+
if (!successful.length)
|
|
169
|
+
throw new Error(results.find(result => result.error)?.error ?? "Google did not return a model catalog. Check account access and try again.");
|
|
170
|
+
modelDefinitions = buildAccountModelDefinitions(models);
|
|
171
|
+
modelAccounts = eligibleAccounts;
|
|
172
|
+
accountCatalogLoaded = true;
|
|
173
|
+
catalogSnapshot = JSON.parse(JSON.stringify({ version: 3, identity: accountPoolIdentity(latest?.accounts ?? []), updatedAt: Date.now(), models, accounts: eligibleAccounts }));
|
|
174
|
+
const writing = writeCatalog();
|
|
175
|
+
catalogWrites.add(writing);
|
|
176
|
+
try {
|
|
177
|
+
await writing;
|
|
178
|
+
}
|
|
179
|
+
finally {
|
|
180
|
+
catalogWrites.delete(writing);
|
|
181
|
+
}
|
|
182
|
+
await ctx.provider.reload();
|
|
183
|
+
return Object.keys(modelDefinitions).length;
|
|
184
|
+
};
|
|
185
|
+
const refreshModels = () => {
|
|
186
|
+
if (disposed)
|
|
187
|
+
return Promise.reject(new Error("Antigravity plugin has been disposed"));
|
|
188
|
+
const discovering = discoverModels();
|
|
189
|
+
discoveries.add(discovering);
|
|
190
|
+
void discovering.then(() => discoveries.delete(discovering), () => discoveries.delete(discovering));
|
|
191
|
+
return discovering;
|
|
192
|
+
};
|
|
193
|
+
const listeners = new Set();
|
|
194
|
+
const lifetime = new AbortController();
|
|
195
|
+
let requestLifetime = new AbortController();
|
|
196
|
+
let disposed = false;
|
|
197
|
+
const credentialOperations = new Set();
|
|
198
|
+
const runCredentialOperation = (operation) => {
|
|
199
|
+
if (disposed)
|
|
200
|
+
return Promise.reject(new Error("Antigravity plugin has been disposed"));
|
|
201
|
+
const running = operation();
|
|
202
|
+
credentialOperations.add(running);
|
|
203
|
+
void running.then(() => credentialOperations.delete(running), () => credentialOperations.delete(running));
|
|
204
|
+
return running;
|
|
205
|
+
};
|
|
206
|
+
let loader;
|
|
207
|
+
let loaderIdentity;
|
|
208
|
+
let generation = 0;
|
|
209
|
+
let observedIdentity;
|
|
210
|
+
let loading;
|
|
211
|
+
const observeAuth = (resolved) => {
|
|
212
|
+
if (observedIdentity !== resolved?.identity) {
|
|
213
|
+
observedIdentity = resolved?.identity;
|
|
214
|
+
generation += 1;
|
|
215
|
+
loader = undefined;
|
|
216
|
+
loaderIdentity = undefined;
|
|
217
|
+
}
|
|
218
|
+
return resolved;
|
|
219
|
+
};
|
|
220
|
+
const resolveAuth = async () => {
|
|
221
|
+
const connection = await ctx.integration.connection.active(PROVIDER_ID);
|
|
222
|
+
let poolSelection = await ctx.storage.get("pool-selection");
|
|
223
|
+
const connectionID = connection?.type === "credential" ? connection.id : "pool";
|
|
224
|
+
if (poolSelection && poolSelection !== connectionID) {
|
|
225
|
+
await ctx.storage.remove("pool-selection");
|
|
226
|
+
poolSelection = undefined;
|
|
227
|
+
}
|
|
228
|
+
if (connection) {
|
|
229
|
+
const value = await ctx.integration.connection.resolve(connection);
|
|
230
|
+
const stored = await loadAccounts();
|
|
231
|
+
const bindingKey = connection.type === "credential" ? `connection-account:${connection.id}` : undefined;
|
|
232
|
+
const boundID = bindingKey ? await ctx.storage.get(bindingKey) : undefined;
|
|
233
|
+
const migrated = value?.type === "oauth" && value.methodID === "oauth"
|
|
234
|
+
&& (typeof boundID === "string" || stored?.accounts.some((account) => matchesAccountToken(account, parseRefreshParts(value.refresh).refreshToken)));
|
|
235
|
+
if (!isAntigravityCredential(value) && !migrated)
|
|
236
|
+
return observeAuth(undefined);
|
|
237
|
+
if (value?.type !== "oauth")
|
|
238
|
+
return observeAuth(undefined);
|
|
239
|
+
// V1 credentials are imported with a generic method ID. Only matching
|
|
240
|
+
// this plugin's existing pool establishes their Antigravity ownership.
|
|
241
|
+
const metadataID = value.metadata?.antigravityAccountId;
|
|
242
|
+
const account = stored?.accounts.find(item => matchesAccountToken(item, parseRefreshParts(value.refresh).refreshToken))
|
|
243
|
+
?? stored?.accounts.find(item => (typeof metadataID === "string" && item.id === metadataID) || (typeof boundID === "string" && item.id === boundID) || (typeof value.metadata?.email === "string" && item.email === value.metadata.email));
|
|
244
|
+
if (account && bindingKey)
|
|
245
|
+
await ctx.storage.set(bindingKey, accountID(account));
|
|
246
|
+
const knownAccountRemoved = (typeof metadataID === "string" || typeof boundID === "string") && !account;
|
|
247
|
+
if (account?.enabled !== false && poolSelection !== "pool" && (connection.type !== "credential" || poolSelection !== connection.id)) {
|
|
248
|
+
if (!knownAccountRemoved) {
|
|
249
|
+
const auth = account ? { ...value, refresh: formatRefreshParts(account) } : value;
|
|
250
|
+
return observeAuth({ auth, identity: `credential:${parseRefreshParts(auth.refresh).refreshToken}:${accountCredentialsIdentity(stored?.accounts ?? [])}`, select: true, accountID: account && accountID(account) });
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
// The independent pool also supports V1 migration without a connection.
|
|
255
|
+
const stored = await loadAccounts();
|
|
256
|
+
const active = stored?.accounts[stored.activeIndex];
|
|
257
|
+
const account = active?.enabled !== false && active?.refreshToken
|
|
258
|
+
? active
|
|
259
|
+
: stored?.accounts.find((item) => item.enabled !== false && item.refreshToken);
|
|
260
|
+
return observeAuth(account ? {
|
|
261
|
+
auth: { type: "oauth", refresh: formatRefreshParts(account) },
|
|
262
|
+
// Cursor changes preserve rotation state; credential rotation rebuilds
|
|
263
|
+
// a manager before it can attempt to refresh an obsolete token.
|
|
264
|
+
identity: `account-pool:${accountCredentialsIdentity(stored?.accounts ?? [])}`,
|
|
265
|
+
select: false,
|
|
266
|
+
accountID: accountID(account),
|
|
267
|
+
} : undefined);
|
|
268
|
+
};
|
|
269
|
+
const currentAuth = async () => (await resolveAuth())?.auth;
|
|
270
|
+
const invalidateLoader = (abortRequests = true) => {
|
|
271
|
+
generation += 1;
|
|
272
|
+
if (abortRequests) {
|
|
273
|
+
requestLifetime.abort(new Error("Antigravity accounts changed"));
|
|
274
|
+
requestLifetime = new AbortController();
|
|
275
|
+
}
|
|
276
|
+
loader = undefined;
|
|
277
|
+
loaderIdentity = undefined;
|
|
278
|
+
};
|
|
279
|
+
const rememberAuth = async (auth, email, previous) => {
|
|
280
|
+
const parts = parseRefreshParts(auth.refresh);
|
|
281
|
+
if (previous) {
|
|
282
|
+
let remembered = false;
|
|
283
|
+
const originalToken = parseRefreshParts(previous.refresh).refreshToken;
|
|
284
|
+
const saved = await updateAccounts(storage => {
|
|
285
|
+
const account = storage.accounts.find(item => matchesAccountToken(item, originalToken) || (typeof previous.metadata?.antigravityAccountId === "string" && item.id === previous.metadata.antigravityAccountId));
|
|
286
|
+
if (!account)
|
|
287
|
+
return;
|
|
288
|
+
remembered = true;
|
|
289
|
+
if (account.refreshToken === originalToken) {
|
|
290
|
+
updateAccountCredentials(account, parts);
|
|
291
|
+
}
|
|
292
|
+
});
|
|
293
|
+
if (remembered)
|
|
294
|
+
return saved?.accounts.find(item => item.refreshToken === parts.refreshToken || item.id === previous.metadata?.antigravityAccountId);
|
|
295
|
+
// A saved native credential may outlive an explicitly deleted pool entry.
|
|
296
|
+
if (previous.metadata?.antigravityAccountId)
|
|
297
|
+
return undefined;
|
|
298
|
+
}
|
|
299
|
+
const saved = await updateAccounts(storage => {
|
|
300
|
+
const account = storage.accounts.find(item => matchesAccountToken(item, parts.refreshToken) || (!!email && item.email === email));
|
|
301
|
+
if (account) {
|
|
302
|
+
updateAccountCredentials(account, parts);
|
|
303
|
+
account.email = email ?? account.email;
|
|
304
|
+
}
|
|
305
|
+
else {
|
|
306
|
+
storage.accounts.push({ id: accountID({ refreshToken: parts.refreshToken }), refreshToken: parts.refreshToken, projectId: parts.projectId, managedProjectId: parts.managedProjectId, email, enabled: true, addedAt: Date.now(), lastUsed: 0 });
|
|
307
|
+
}
|
|
308
|
+
});
|
|
309
|
+
return saved?.accounts.find(item => item.refreshToken === parts.refreshToken);
|
|
310
|
+
};
|
|
311
|
+
const getLoader = async () => {
|
|
312
|
+
if (disposed)
|
|
313
|
+
throw new Error("Antigravity plugin has been disposed");
|
|
314
|
+
const resolved = await resolveAuth();
|
|
315
|
+
if (!resolved)
|
|
316
|
+
throw new Error("Connect Google with Antigravity OAuth to use this model");
|
|
317
|
+
const { auth, identity } = resolved;
|
|
318
|
+
if (loader && loaderIdentity === identity)
|
|
319
|
+
return loader;
|
|
320
|
+
if (loading) {
|
|
321
|
+
await loading;
|
|
322
|
+
return getLoader();
|
|
323
|
+
}
|
|
324
|
+
const expectedGeneration = generation;
|
|
325
|
+
loading = (async () => {
|
|
326
|
+
// Finish an older manager's pending save before selecting a new
|
|
327
|
+
// connection, so it cannot overwrite the newly selected cursors.
|
|
328
|
+
await transport.flush?.();
|
|
329
|
+
if (generation !== expectedGeneration)
|
|
330
|
+
return undefined;
|
|
331
|
+
const token = parseRefreshParts(auth.refresh).refreshToken;
|
|
332
|
+
let stored = await loadAccounts();
|
|
333
|
+
if (generation !== expectedGeneration)
|
|
334
|
+
return undefined;
|
|
335
|
+
if (!stored?.accounts.some((account) => account.refreshToken === token)) {
|
|
336
|
+
// A resolved pool member can disappear or rotate during I/O. Only a
|
|
337
|
+
// previously unknown native credential may add a new pool member.
|
|
338
|
+
if (resolved.accountID)
|
|
339
|
+
return undefined;
|
|
340
|
+
const latest = await resolveAuth();
|
|
341
|
+
if (generation !== expectedGeneration || latest?.identity !== identity)
|
|
342
|
+
return undefined;
|
|
343
|
+
await rememberAuth(auth);
|
|
344
|
+
return undefined;
|
|
345
|
+
}
|
|
346
|
+
if (resolved.select && stored) {
|
|
347
|
+
const index = stored.accounts.findIndex((account) => account.refreshToken === token && account.enabled !== false);
|
|
348
|
+
if (index >= 0) {
|
|
349
|
+
stored.activeIndex = index;
|
|
350
|
+
stored.activeIndexByFamily = { claude: index, gemini: index };
|
|
351
|
+
await updateAccounts(latest => {
|
|
352
|
+
const selected = latest.accounts.findIndex(account => account.refreshToken === token && account.enabled !== false);
|
|
353
|
+
if (selected >= 0) {
|
|
354
|
+
latest.activeIndex = selected;
|
|
355
|
+
latest.activeIndexByFamily = { claude: selected, gemini: selected };
|
|
356
|
+
}
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
let result;
|
|
361
|
+
try {
|
|
362
|
+
result = await transport.auth.loader(async () => await currentAuth() ?? { type: "none" }, {});
|
|
363
|
+
}
|
|
364
|
+
catch (error) {
|
|
365
|
+
if (!disposed && generation !== expectedGeneration)
|
|
366
|
+
return undefined;
|
|
367
|
+
throw error;
|
|
368
|
+
}
|
|
369
|
+
if (typeof result.fetch !== "function")
|
|
370
|
+
throw new Error("Antigravity OAuth is unavailable");
|
|
371
|
+
if (disposed)
|
|
372
|
+
throw new Error("Antigravity plugin has been disposed");
|
|
373
|
+
if (generation !== expectedGeneration)
|
|
374
|
+
return undefined;
|
|
375
|
+
loader = result;
|
|
376
|
+
loaderIdentity = identity;
|
|
377
|
+
return loader;
|
|
378
|
+
})();
|
|
379
|
+
let result;
|
|
380
|
+
try {
|
|
381
|
+
result = await loading;
|
|
382
|
+
}
|
|
383
|
+
finally {
|
|
384
|
+
loading = undefined;
|
|
385
|
+
}
|
|
386
|
+
return result ?? getLoader();
|
|
387
|
+
};
|
|
388
|
+
const scopedFetch = (fallback, body) => (input, init) => runCredentialOperation(async () => {
|
|
389
|
+
if (disposed)
|
|
390
|
+
throw new Error("Antigravity plugin has been disposed");
|
|
391
|
+
const original = typeof fallback === "function" ? fallback : globalThis.fetch;
|
|
392
|
+
if (!await currentAuth())
|
|
393
|
+
throw new Error("Connect Google with Antigravity OAuth and enable an account to use this model");
|
|
394
|
+
if (body && typeof init?.body === "string") {
|
|
395
|
+
init = { ...init, body: JSON.stringify(mergeBody(JSON.parse(init.body), body)) };
|
|
396
|
+
}
|
|
397
|
+
const interceptor = await getLoader();
|
|
398
|
+
const signal = AbortSignal.any([
|
|
399
|
+
lifetime.signal,
|
|
400
|
+
requestLifetime.signal,
|
|
401
|
+
...(init?.signal ? [init.signal] : input instanceof Request ? [input.signal] : []),
|
|
402
|
+
]);
|
|
403
|
+
return outbound.run(original, () => interceptor.fetch(input instanceof URL ? input.toString() : input, { ...init, signal }));
|
|
404
|
+
});
|
|
405
|
+
let cleaning;
|
|
406
|
+
const cleanup = () => {
|
|
407
|
+
if (cleaning)
|
|
408
|
+
return cleaning;
|
|
409
|
+
disposed = true;
|
|
410
|
+
cleaning = Promise.resolve().then(async () => {
|
|
411
|
+
lifetime.abort(new Error("Antigravity plugin disposed"));
|
|
412
|
+
await Promise.allSettled([...listeners].map((listener) => listener.close()));
|
|
413
|
+
await Promise.allSettled([...credentialOperations]);
|
|
414
|
+
await Promise.allSettled([...discoveries]);
|
|
415
|
+
await Promise.allSettled([...catalogWrites]);
|
|
416
|
+
await Promise.allSettled(registrations.reverse().map((registration) => registration.dispose()));
|
|
417
|
+
await transport.dispose?.();
|
|
418
|
+
loader = undefined;
|
|
419
|
+
});
|
|
420
|
+
return cleaning;
|
|
421
|
+
};
|
|
422
|
+
try {
|
|
423
|
+
registrations.push(await ctx.integration.transform((editor) => {
|
|
424
|
+
editor.method.update({
|
|
425
|
+
integrationID: PROVIDER_ID,
|
|
426
|
+
method: {
|
|
427
|
+
id: METHOD_ID,
|
|
428
|
+
type: "oauth",
|
|
429
|
+
label: "Google OAuth (Antigravity)",
|
|
430
|
+
form: [
|
|
431
|
+
{ key: "projectId", type: "string", title: "Google Cloud project ID (optional)" },
|
|
432
|
+
{ key: "manual", type: "boolean", title: "Paste an authorization code or callback URL", default: true },
|
|
433
|
+
],
|
|
434
|
+
},
|
|
435
|
+
async authorize(answer) {
|
|
436
|
+
return runCredentialOperation(async () => {
|
|
437
|
+
const auth = await authorizeAntigravity(typeof answer.projectId === "string" ? answer.projectId : "");
|
|
438
|
+
if (disposed)
|
|
439
|
+
throw new Error("Antigravity plugin has been disposed");
|
|
440
|
+
const state = new URL(auth.url).searchParams.get("state");
|
|
441
|
+
if (!state)
|
|
442
|
+
throw new Error("Antigravity authorization did not provide OAuth state");
|
|
443
|
+
const complete = (input) => runCredentialOperation(async () => {
|
|
444
|
+
let code = input.trim();
|
|
445
|
+
if (/^https?:\/\//i.test(code)) {
|
|
446
|
+
const callback = new URL(code);
|
|
447
|
+
const redirect = new URL(ANTIGRAVITY_REDIRECT_URI);
|
|
448
|
+
if (callback.origin !== redirect.origin || callback.pathname !== redirect.pathname || callback.searchParams.get("state") !== state) {
|
|
449
|
+
throw new Error("Invalid Antigravity OAuth callback URL or state");
|
|
450
|
+
}
|
|
451
|
+
code = callback.searchParams.get("code") ?? "";
|
|
452
|
+
}
|
|
453
|
+
if (!code)
|
|
454
|
+
throw new Error("Missing Antigravity authorization code");
|
|
455
|
+
const result = await exchangeAntigravity(code, state);
|
|
456
|
+
if (result.type !== "success")
|
|
457
|
+
throw new Error("Antigravity authorization failed; please try signing in again");
|
|
458
|
+
const account = await rememberAuth({ ...result, type: "oauth" }, result.email);
|
|
459
|
+
await updateAccounts(accounts => {
|
|
460
|
+
const current = accounts.accounts.find(item => item.id === account?.id);
|
|
461
|
+
if (!current)
|
|
462
|
+
return;
|
|
463
|
+
current.enabled = true;
|
|
464
|
+
current.verificationRequired = false;
|
|
465
|
+
delete current.verificationRequiredAt;
|
|
466
|
+
delete current.verificationRequiredReason;
|
|
467
|
+
delete current.verificationUrl;
|
|
468
|
+
});
|
|
469
|
+
await ctx.storage.remove("pool-selection");
|
|
470
|
+
invalidateLoader();
|
|
471
|
+
return oauthCredential({ ...result, type: "oauth" }, { ...(result.email ? { email: result.email } : {}), ...(account?.id ? { antigravityAccountId: account.id } : {}) });
|
|
472
|
+
});
|
|
473
|
+
if (answer.manual !== false) {
|
|
474
|
+
return { url: auth.url, instructions: "Open this URL, then paste the authorization code or full callback URL.", mode: "code", callback: complete };
|
|
475
|
+
}
|
|
476
|
+
const listener = await startOAuthListener();
|
|
477
|
+
listeners.add(listener);
|
|
478
|
+
const callback = listener.waitForCallback()
|
|
479
|
+
.then((url) => complete(url.toString()))
|
|
480
|
+
.finally(async () => {
|
|
481
|
+
listeners.delete(listener);
|
|
482
|
+
await listener.close();
|
|
483
|
+
});
|
|
484
|
+
// The host consumes this promise after returning the authorization.
|
|
485
|
+
void callback.catch(() => undefined);
|
|
486
|
+
if (disposed) {
|
|
487
|
+
await listener.close();
|
|
488
|
+
throw new Error("Antigravity plugin has been disposed");
|
|
489
|
+
}
|
|
490
|
+
return { url: auth.url, instructions: "Open this URL in your browser to connect Antigravity.", mode: "auto", callback };
|
|
491
|
+
});
|
|
492
|
+
},
|
|
493
|
+
async refresh(credential) {
|
|
494
|
+
return runCredentialOperation(async () => {
|
|
495
|
+
const sourceConnection = await ctx.integration.connection.active(PROVIDER_ID);
|
|
496
|
+
const stored = await loadManagedAccounts();
|
|
497
|
+
const original = stored?.accounts.find(account => matchesAccountToken(account, parseRefreshParts(credential.refresh).refreshToken) || (typeof credential.metadata?.antigravityAccountId === "string" && account.id === credential.metadata.antigravityAccountId));
|
|
498
|
+
const previous = original?.id ? { ...credential, refresh: formatRefreshParts(original), metadata: { ...credential.metadata, antigravityAccountId: original.id } } : credential;
|
|
499
|
+
try {
|
|
500
|
+
const refreshed = await refreshAccessToken(previous, client, PROVIDER_ID);
|
|
501
|
+
if (!refreshed?.access)
|
|
502
|
+
throw new Error("Antigravity token refresh temporarily unavailable");
|
|
503
|
+
const account = await rememberAuth(refreshed, original?.email, previous);
|
|
504
|
+
invalidateLoader(false);
|
|
505
|
+
return oauthCredential(account ? { ...refreshed, refresh: formatRefreshParts(account) } : refreshed, { ...credential.metadata, ...(account?.id ? { antigravityAccountId: account.id } : {}) });
|
|
506
|
+
}
|
|
507
|
+
catch (error) {
|
|
508
|
+
if (error instanceof AntigravityTokenRefreshError && error.code === "invalid_grant") {
|
|
509
|
+
let revoked = false;
|
|
510
|
+
await updateAccounts(stored => {
|
|
511
|
+
const account = stored.accounts.find(item => item.refreshToken === parseRefreshParts(previous.refresh).refreshToken);
|
|
512
|
+
if (account) {
|
|
513
|
+
account.enabled = false;
|
|
514
|
+
revoked = true;
|
|
515
|
+
}
|
|
516
|
+
});
|
|
517
|
+
const boundID = sourceConnection?.type === "credential" ? await ctx.storage.get(`connection-account:${sourceConnection.id}`) : undefined;
|
|
518
|
+
if (revoked && sourceConnection && typeof boundID === "string" && boundID === original?.id)
|
|
519
|
+
await ctx.integration.connection.status({ integrationID: PROVIDER_ID, connection: sourceConnection, status: { status: "needs_auth", message: "Google revoked this account; sign in again." } });
|
|
520
|
+
invalidateLoader();
|
|
521
|
+
}
|
|
522
|
+
// Core resolves credentials before model construction. Keep that
|
|
523
|
+
// step available so the transport can rotate to another account.
|
|
524
|
+
// Six minutes clears Core's five-minute refresh buffer briefly.
|
|
525
|
+
return { ...credential, access: "", expires: Date.now() + 6 * 60_000 };
|
|
526
|
+
}
|
|
527
|
+
});
|
|
528
|
+
},
|
|
529
|
+
label: (credential) => typeof credential.metadata?.email === "string" ? credential.metadata.email : undefined,
|
|
530
|
+
});
|
|
531
|
+
}));
|
|
532
|
+
registrations.push(await ctx.provider.transform((editor) => {
|
|
533
|
+
// Keep the native Google provider and every unrelated model intact.
|
|
534
|
+
// Only a missing provider (e.g. a minimal host) needs a source template.
|
|
535
|
+
if (!editor.get(PROVIDER_ID)) {
|
|
536
|
+
editor.add({
|
|
537
|
+
info: { ...Provider.Info.empty(Provider.ID.google), name: "Google", package: GOOGLE_PACKAGE },
|
|
538
|
+
models: [],
|
|
539
|
+
});
|
|
540
|
+
}
|
|
541
|
+
if (hasSavedAccounts) {
|
|
542
|
+
editor.update(PROVIDER_ID, (provider) => {
|
|
543
|
+
if (provider.activation === "auto")
|
|
544
|
+
provider.activation = "enabled";
|
|
545
|
+
});
|
|
546
|
+
}
|
|
547
|
+
if (accountCatalogLoaded) {
|
|
548
|
+
for (const id of editor.get(PROVIDER_ID)?.models.keys() ?? []) {
|
|
549
|
+
if (ownsModel(id) && !modelDefinitions[id])
|
|
550
|
+
editor.models.remove(PROVIDER_ID, id);
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
for (const [id, definition] of Object.entries(modelDefinitions)) {
|
|
554
|
+
editor.models.update(PROVIDER_ID, id, (model) => {
|
|
555
|
+
Object.assign(model, structuredClone(definition), { package: GOOGLE_PACKAGE });
|
|
556
|
+
model.variants = structuredClone(definition.variants).map((variant) => ({ ...variant, id: Model.VariantID.make(variant.id) }));
|
|
557
|
+
});
|
|
558
|
+
}
|
|
559
|
+
}));
|
|
560
|
+
registrations.push(await ctx.model.transform((editor) => {
|
|
561
|
+
for (const model of editor.list(PROVIDER_ID)) {
|
|
562
|
+
const id = String(model.id);
|
|
563
|
+
if (!ownsModel(id))
|
|
564
|
+
continue;
|
|
565
|
+
if (accountCatalogLoaded && !modelDefinitions[id]) {
|
|
566
|
+
// Configured models are raw overrides. Removing an override can
|
|
567
|
+
// reveal its native Google route; disable it explicitly instead.
|
|
568
|
+
editor.update(PROVIDER_ID, id, target => {
|
|
569
|
+
target.enabled = false;
|
|
570
|
+
target.package = GOOGLE_PACKAGE;
|
|
571
|
+
});
|
|
572
|
+
continue;
|
|
573
|
+
}
|
|
574
|
+
editor.update(PROVIDER_ID, id, (target) => {
|
|
575
|
+
// A native provider override can otherwise bypass the SDK hook.
|
|
576
|
+
target.package = GOOGLE_PACKAGE;
|
|
577
|
+
});
|
|
578
|
+
}
|
|
579
|
+
}));
|
|
580
|
+
registrations.push(await ctx.aisdk.hook("sdk", async (event) => {
|
|
581
|
+
if (!ownsModel(event.model.id))
|
|
582
|
+
return;
|
|
583
|
+
if (accountCatalogLoaded && !modelDefinitions[event.model.id]) {
|
|
584
|
+
throw new Error("This model is unavailable to the enabled Antigravity accounts. Refresh available models from /antigravity.");
|
|
585
|
+
}
|
|
586
|
+
if (event.package !== SDK_PACKAGE)
|
|
587
|
+
return;
|
|
588
|
+
event.sdk = createGoogleGenerativeAI({
|
|
589
|
+
...event.options,
|
|
590
|
+
fetch: scopedFetch(event.options.fetch, event.model.body),
|
|
591
|
+
// SDK 3 checks for a nonempty key before invoking custom fetch. The
|
|
592
|
+
// interceptor replaces this sentinel with account-specific OAuth.
|
|
593
|
+
apiKey: event.options.apiKey ?? API_KEY_SENTINEL,
|
|
594
|
+
baseURL: event.options.baseURL ?? "https://generativelanguage.googleapis.com/v1beta",
|
|
595
|
+
});
|
|
596
|
+
}, { providerID: PROVIDER_ID }));
|
|
597
|
+
registrations.push(await ctx.tool.transform((editor) => {
|
|
598
|
+
editor.add({
|
|
599
|
+
name: "google_search",
|
|
600
|
+
description: "Search Google for current information and analyze URLs. Include URLs from the question in the urls argument.",
|
|
601
|
+
input: {
|
|
602
|
+
type: "object",
|
|
603
|
+
properties: {
|
|
604
|
+
query: { type: "string", minLength: 1 },
|
|
605
|
+
urls: { type: "array", items: { type: "string" } },
|
|
606
|
+
thinking: { type: "boolean", default: true },
|
|
607
|
+
},
|
|
608
|
+
required: ["query"],
|
|
609
|
+
additionalProperties: false,
|
|
610
|
+
},
|
|
611
|
+
async execute(input, context) {
|
|
612
|
+
return runCredentialOperation(async () => {
|
|
613
|
+
const signal = AbortSignal.any([context.signal, lifetime.signal, requestLifetime.signal]);
|
|
614
|
+
signal.throwIfAborted();
|
|
615
|
+
const args = input;
|
|
616
|
+
if (!args || typeof args.query !== "string" || !args.query.trim())
|
|
617
|
+
throw new Error("A search query is required");
|
|
618
|
+
const resolved = await resolveAuth();
|
|
619
|
+
let auth = resolved?.auth;
|
|
620
|
+
if (!auth)
|
|
621
|
+
return { content: "Connect Google with Antigravity OAuth to use Google Search." };
|
|
622
|
+
const metadata = resolved?.accountID ? { antigravityAccountId: resolved.accountID } : undefined;
|
|
623
|
+
if (!auth.access || accessTokenExpired(auth)) {
|
|
624
|
+
const previous = oauthCredential(auth, metadata);
|
|
625
|
+
auth = await refreshAccessToken(auth, client, PROVIDER_ID);
|
|
626
|
+
if (auth)
|
|
627
|
+
await rememberAuth(auth, undefined, previous);
|
|
628
|
+
}
|
|
629
|
+
if (!auth?.access)
|
|
630
|
+
throw new Error("Antigravity token refresh failed; please sign in again");
|
|
631
|
+
signal.throwIfAborted();
|
|
632
|
+
const project = await ensureProjectContext(auth);
|
|
633
|
+
if (project.auth.refresh !== auth.refresh)
|
|
634
|
+
await rememberAuth(project.auth, undefined, oauthCredential(auth, metadata));
|
|
635
|
+
signal.throwIfAborted();
|
|
636
|
+
return { content: await executeSearch(args, auth.access, project.effectiveProjectId, signal) };
|
|
637
|
+
});
|
|
638
|
+
},
|
|
639
|
+
});
|
|
640
|
+
}));
|
|
641
|
+
registrations.push(await registerAccountManagement(ctx, client, async () => {
|
|
642
|
+
invalidateLoader();
|
|
643
|
+
await transport.flush?.();
|
|
644
|
+
}, refreshModels, async () => {
|
|
645
|
+
hasSavedAccounts = (await loadAccounts())?.accounts.some(account => account.enabled !== false);
|
|
646
|
+
if (hasSavedAccounts && ctx.options.discoverModels !== false) {
|
|
647
|
+
try {
|
|
648
|
+
await refreshModels();
|
|
649
|
+
}
|
|
650
|
+
catch (error) {
|
|
651
|
+
log.warn("Model refresh unavailable", { error: String(error) });
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
await ctx.provider.reload();
|
|
655
|
+
}, runCredentialOperation));
|
|
656
|
+
if (hasSavedAccounts && ctx.options.discoverModels !== false && (!validCache || Date.now() - (cached?.updatedAt ?? 0) > 3600_000)) {
|
|
657
|
+
try {
|
|
658
|
+
await refreshModels();
|
|
659
|
+
}
|
|
660
|
+
catch (error) {
|
|
661
|
+
log.warn("Model discovery unavailable; using cached or fallback models", { error: String(error) });
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
return cleanup;
|
|
665
|
+
}
|
|
666
|
+
catch (error) {
|
|
667
|
+
await cleanup();
|
|
668
|
+
throw error;
|
|
669
|
+
}
|
|
670
|
+
},
|
|
671
|
+
});
|
|
672
|
+
//# sourceMappingURL=v2.js.map
|