opencode-gemini-auth 1.3.8 → 1.3.9
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 +13 -1
- package/index.ts +1 -1
- package/package.json +2 -2
- package/src/gemini/oauth.ts +35 -47
- package/src/plugin/debug.ts +60 -0
- package/src/plugin/project.test.ts +112 -0
- package/src/plugin/project.ts +314 -121
- package/src/plugin/request-helpers.ts +249 -0
- package/src/plugin/request.ts +14 -30
- package/src/plugin/token.ts +32 -7
- package/src/plugin.ts +85 -12
package/src/plugin/project.ts
CHANGED
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
GEMINI_PROVIDER_ID,
|
|
5
5
|
} from "../constants";
|
|
6
6
|
import { formatRefreshParts, parseRefreshParts } from "./auth";
|
|
7
|
+
import { logGeminiDebugResponse, startGeminiDebugRequest } from "./debug";
|
|
7
8
|
import type {
|
|
8
9
|
OAuthAuthDetails,
|
|
9
10
|
PluginClient,
|
|
@@ -13,6 +14,9 @@ import type {
|
|
|
13
14
|
const projectContextResultCache = new Map<string, ProjectContextResult>();
|
|
14
15
|
const projectContextPendingCache = new Map<string, Promise<ProjectContextResult>>();
|
|
15
16
|
|
|
17
|
+
const FREE_TIER_ID = "free-tier";
|
|
18
|
+
const LEGACY_TIER_ID = "legacy-tier";
|
|
19
|
+
|
|
16
20
|
const CODE_ASSIST_METADATA = {
|
|
17
21
|
ideType: "IDE_UNSPECIFIED",
|
|
18
22
|
platform: "PLATFORM_UNSPECIFIED",
|
|
@@ -23,17 +27,30 @@ interface GeminiUserTier {
|
|
|
23
27
|
id?: string;
|
|
24
28
|
isDefault?: boolean;
|
|
25
29
|
userDefinedCloudaicompanionProject?: boolean;
|
|
30
|
+
name?: string;
|
|
31
|
+
description?: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
interface CloudAiCompanionProject {
|
|
35
|
+
id?: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
interface GeminiIneligibleTier {
|
|
39
|
+
reasonMessage?: string;
|
|
26
40
|
}
|
|
27
41
|
|
|
28
42
|
interface LoadCodeAssistPayload {
|
|
29
|
-
cloudaicompanionProject?: string;
|
|
43
|
+
cloudaicompanionProject?: string | CloudAiCompanionProject;
|
|
30
44
|
currentTier?: {
|
|
31
45
|
id?: string;
|
|
46
|
+
name?: string;
|
|
32
47
|
};
|
|
33
48
|
allowedTiers?: GeminiUserTier[];
|
|
49
|
+
ineligibleTiers?: GeminiIneligibleTier[];
|
|
34
50
|
}
|
|
35
51
|
|
|
36
52
|
interface OnboardUserPayload {
|
|
53
|
+
name?: string;
|
|
37
54
|
done?: boolean;
|
|
38
55
|
response?: {
|
|
39
56
|
cloudaicompanionProject?: {
|
|
@@ -48,7 +65,7 @@ class ProjectIdRequiredError extends Error {
|
|
|
48
65
|
*/
|
|
49
66
|
constructor() {
|
|
50
67
|
super(
|
|
51
|
-
"Google Gemini requires a Google Cloud project. Enable the Gemini for Google Cloud API on a project you control, then set `provider.google.options.projectId` in your Opencode config (or set OPENCODE_GEMINI_PROJECT_ID).",
|
|
68
|
+
"Google Gemini requires a Google Cloud project. Enable the Gemini for Google Cloud API on a project you control, then set `provider.google.options.projectId` in your Opencode config (or set OPENCODE_GEMINI_PROJECT_ID / GOOGLE_CLOUD_PROJECT).",
|
|
52
69
|
);
|
|
53
70
|
}
|
|
54
71
|
}
|
|
@@ -56,31 +73,94 @@ class ProjectIdRequiredError extends Error {
|
|
|
56
73
|
/**
|
|
57
74
|
* Builds metadata headers required by the Code Assist API.
|
|
58
75
|
*/
|
|
59
|
-
function buildMetadata(projectId?: string): Record<string, string> {
|
|
76
|
+
function buildMetadata(projectId?: string, includeDuetProject = true): Record<string, string> {
|
|
60
77
|
const metadata: Record<string, string> = {
|
|
61
78
|
ideType: CODE_ASSIST_METADATA.ideType,
|
|
62
79
|
platform: CODE_ASSIST_METADATA.platform,
|
|
63
80
|
pluginType: CODE_ASSIST_METADATA.pluginType,
|
|
64
81
|
};
|
|
65
|
-
if (projectId) {
|
|
82
|
+
if (projectId && includeDuetProject) {
|
|
66
83
|
metadata.duetProject = projectId;
|
|
67
84
|
}
|
|
68
85
|
return metadata;
|
|
69
86
|
}
|
|
70
87
|
|
|
88
|
+
/**
|
|
89
|
+
* Normalizes project identifiers from API payloads or config.
|
|
90
|
+
*/
|
|
91
|
+
function normalizeProjectId(value?: string | CloudAiCompanionProject): string | undefined {
|
|
92
|
+
if (!value) {
|
|
93
|
+
return undefined;
|
|
94
|
+
}
|
|
95
|
+
if (typeof value === "string") {
|
|
96
|
+
const trimmed = value.trim();
|
|
97
|
+
return trimmed ? trimmed : undefined;
|
|
98
|
+
}
|
|
99
|
+
if (typeof value === "object" && typeof value.id === "string") {
|
|
100
|
+
const trimmed = value.id.trim();
|
|
101
|
+
return trimmed ? trimmed : undefined;
|
|
102
|
+
}
|
|
103
|
+
return undefined;
|
|
104
|
+
}
|
|
105
|
+
|
|
71
106
|
/**
|
|
72
107
|
* Selects the default tier ID from the allowed tiers list.
|
|
73
108
|
*/
|
|
74
|
-
function
|
|
75
|
-
if (
|
|
109
|
+
function pickOnboardTier(allowedTiers?: GeminiUserTier[]): GeminiUserTier {
|
|
110
|
+
if (allowedTiers && allowedTiers.length > 0) {
|
|
111
|
+
for (const tier of allowedTiers) {
|
|
112
|
+
if (tier?.isDefault) {
|
|
113
|
+
return tier;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return allowedTiers[0] ?? { id: LEGACY_TIER_ID, userDefinedCloudaicompanionProject: true };
|
|
117
|
+
}
|
|
118
|
+
return { id: LEGACY_TIER_ID, userDefinedCloudaicompanionProject: true };
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Builds a concise error message from ineligible tier payloads.
|
|
123
|
+
*/
|
|
124
|
+
function buildIneligibleTierMessage(tiers?: GeminiIneligibleTier[]): string | undefined {
|
|
125
|
+
if (!tiers || tiers.length === 0) {
|
|
126
|
+
return undefined;
|
|
127
|
+
}
|
|
128
|
+
const reasons = tiers
|
|
129
|
+
.map((tier) => tier?.reasonMessage?.trim())
|
|
130
|
+
.filter((message): message is string => !!message);
|
|
131
|
+
if (reasons.length === 0) {
|
|
76
132
|
return undefined;
|
|
77
133
|
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
134
|
+
return reasons.join(", ");
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function isVpcScError(payload: unknown): boolean {
|
|
138
|
+
if (!payload || typeof payload !== "object") {
|
|
139
|
+
return false;
|
|
140
|
+
}
|
|
141
|
+
const error = (payload as { error?: unknown }).error;
|
|
142
|
+
if (!error || typeof error !== "object") {
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
145
|
+
const details = (error as { details?: unknown }).details;
|
|
146
|
+
if (!Array.isArray(details)) {
|
|
147
|
+
return false;
|
|
148
|
+
}
|
|
149
|
+
return details.some((detail) => {
|
|
150
|
+
if (!detail || typeof detail !== "object") {
|
|
151
|
+
return false;
|
|
81
152
|
}
|
|
153
|
+
const reason = (detail as { reason?: unknown }).reason;
|
|
154
|
+
return reason === "SECURITY_POLICY_VIOLATED";
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function parseJsonSafe(text: string): unknown {
|
|
159
|
+
try {
|
|
160
|
+
return JSON.parse(text);
|
|
161
|
+
} catch {
|
|
162
|
+
return null;
|
|
82
163
|
}
|
|
83
|
-
return allowedTiers[0]?.id;
|
|
84
164
|
}
|
|
85
165
|
|
|
86
166
|
/**
|
|
@@ -140,24 +220,53 @@ export async function loadManagedProject(
|
|
|
140
220
|
if (projectId) {
|
|
141
221
|
requestBody.cloudaicompanionProject = projectId;
|
|
142
222
|
}
|
|
223
|
+
const url = `${GEMINI_CODE_ASSIST_ENDPOINT}/v1internal:loadCodeAssist`;
|
|
224
|
+
const headers = {
|
|
225
|
+
"Content-Type": "application/json",
|
|
226
|
+
Authorization: `Bearer ${accessToken}`,
|
|
227
|
+
...CODE_ASSIST_HEADERS,
|
|
228
|
+
};
|
|
229
|
+
const debugContext = startGeminiDebugRequest({
|
|
230
|
+
originalUrl: url,
|
|
231
|
+
resolvedUrl: url,
|
|
232
|
+
method: "POST",
|
|
233
|
+
headers,
|
|
234
|
+
body: JSON.stringify(requestBody),
|
|
235
|
+
streaming: false,
|
|
236
|
+
projectId,
|
|
237
|
+
});
|
|
143
238
|
|
|
144
|
-
const response = await fetch(
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
239
|
+
const response = await fetch(url, {
|
|
240
|
+
method: "POST",
|
|
241
|
+
headers,
|
|
242
|
+
body: JSON.stringify(requestBody),
|
|
243
|
+
});
|
|
244
|
+
let responseBody: string | undefined;
|
|
245
|
+
if (debugContext || !response.ok) {
|
|
246
|
+
try {
|
|
247
|
+
responseBody = await response.clone().text();
|
|
248
|
+
} catch {
|
|
249
|
+
responseBody = undefined;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
if (debugContext) {
|
|
253
|
+
logGeminiDebugResponse(debugContext, response, { body: responseBody });
|
|
254
|
+
}
|
|
156
255
|
|
|
157
256
|
if (!response.ok) {
|
|
257
|
+
if (responseBody) {
|
|
258
|
+
const parsed = parseJsonSafe(responseBody);
|
|
259
|
+
if (isVpcScError(parsed)) {
|
|
260
|
+
return { currentTier: { id: "standard-tier" } };
|
|
261
|
+
}
|
|
262
|
+
}
|
|
158
263
|
return null;
|
|
159
264
|
}
|
|
160
265
|
|
|
266
|
+
if (responseBody) {
|
|
267
|
+
return parseJsonSafe(responseBody) as LoadCodeAssistPayload;
|
|
268
|
+
}
|
|
269
|
+
|
|
161
270
|
return (await response.json()) as LoadCodeAssistPayload;
|
|
162
271
|
} catch (error) {
|
|
163
272
|
console.error("Failed to load Gemini managed project:", error);
|
|
@@ -176,57 +285,198 @@ export async function onboardManagedProject(
|
|
|
176
285
|
attempts = 10,
|
|
177
286
|
delayMs = 5000,
|
|
178
287
|
): Promise<string | undefined> {
|
|
179
|
-
const
|
|
288
|
+
const isFreeTier = tierId === FREE_TIER_ID;
|
|
289
|
+
const metadata = buildMetadata(projectId, !isFreeTier);
|
|
180
290
|
const requestBody: Record<string, unknown> = {
|
|
181
291
|
tierId,
|
|
182
292
|
metadata,
|
|
183
293
|
};
|
|
184
294
|
|
|
185
|
-
if (
|
|
295
|
+
if (!isFreeTier) {
|
|
186
296
|
if (!projectId) {
|
|
187
297
|
throw new ProjectIdRequiredError();
|
|
188
298
|
}
|
|
189
299
|
requestBody.cloudaicompanionProject = projectId;
|
|
190
300
|
}
|
|
191
301
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
"Content-Type": "application/json",
|
|
200
|
-
Authorization: `Bearer ${accessToken}`,
|
|
201
|
-
...CODE_ASSIST_HEADERS,
|
|
202
|
-
},
|
|
203
|
-
body: JSON.stringify(requestBody),
|
|
204
|
-
},
|
|
205
|
-
);
|
|
206
|
-
|
|
207
|
-
if (!response.ok) {
|
|
208
|
-
return undefined;
|
|
209
|
-
}
|
|
302
|
+
const baseUrl = `${GEMINI_CODE_ASSIST_ENDPOINT}/v1internal`;
|
|
303
|
+
const onboardUrl = `${baseUrl}:onboardUser`;
|
|
304
|
+
const headers = {
|
|
305
|
+
"Content-Type": "application/json",
|
|
306
|
+
Authorization: `Bearer ${accessToken}`,
|
|
307
|
+
...CODE_ASSIST_HEADERS,
|
|
308
|
+
};
|
|
210
309
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
310
|
+
try {
|
|
311
|
+
const debugContext = startGeminiDebugRequest({
|
|
312
|
+
originalUrl: onboardUrl,
|
|
313
|
+
resolvedUrl: onboardUrl,
|
|
314
|
+
method: "POST",
|
|
315
|
+
headers,
|
|
316
|
+
body: JSON.stringify(requestBody),
|
|
317
|
+
streaming: false,
|
|
318
|
+
projectId,
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
const response = await fetch(onboardUrl, {
|
|
322
|
+
method: "POST",
|
|
323
|
+
headers,
|
|
324
|
+
body: JSON.stringify(requestBody),
|
|
325
|
+
});
|
|
326
|
+
if (debugContext) {
|
|
327
|
+
let responseBody: string | undefined;
|
|
328
|
+
try {
|
|
329
|
+
responseBody = await response.clone().text();
|
|
330
|
+
} catch {
|
|
331
|
+
responseBody = undefined;
|
|
218
332
|
}
|
|
219
|
-
|
|
220
|
-
|
|
333
|
+
logGeminiDebugResponse(debugContext, response, { body: responseBody });
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
if (!response.ok) {
|
|
221
337
|
return undefined;
|
|
222
338
|
}
|
|
223
339
|
|
|
224
|
-
await
|
|
340
|
+
let payload = (await response.json()) as OnboardUserPayload;
|
|
341
|
+
if (!payload.done && payload.name) {
|
|
342
|
+
for (let attempt = 0; attempt < attempts; attempt += 1) {
|
|
343
|
+
await wait(delayMs);
|
|
344
|
+
const operationUrl = `${baseUrl}/${payload.name}`;
|
|
345
|
+
const opDebugContext = startGeminiDebugRequest({
|
|
346
|
+
originalUrl: operationUrl,
|
|
347
|
+
resolvedUrl: operationUrl,
|
|
348
|
+
method: "GET",
|
|
349
|
+
headers,
|
|
350
|
+
streaming: false,
|
|
351
|
+
projectId,
|
|
352
|
+
});
|
|
353
|
+
const opResponse = await fetch(operationUrl, {
|
|
354
|
+
method: "GET",
|
|
355
|
+
headers,
|
|
356
|
+
});
|
|
357
|
+
if (opDebugContext) {
|
|
358
|
+
let responseBody: string | undefined;
|
|
359
|
+
try {
|
|
360
|
+
responseBody = await opResponse.clone().text();
|
|
361
|
+
} catch {
|
|
362
|
+
responseBody = undefined;
|
|
363
|
+
}
|
|
364
|
+
logGeminiDebugResponse(opDebugContext, opResponse, { body: responseBody });
|
|
365
|
+
}
|
|
366
|
+
if (!opResponse.ok) {
|
|
367
|
+
return undefined;
|
|
368
|
+
}
|
|
369
|
+
payload = (await opResponse.json()) as OnboardUserPayload;
|
|
370
|
+
if (payload.done) {
|
|
371
|
+
break;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
const managedProjectId = payload.response?.cloudaicompanionProject?.id;
|
|
377
|
+
if (payload.done && managedProjectId) {
|
|
378
|
+
return managedProjectId;
|
|
379
|
+
}
|
|
380
|
+
if (payload.done && projectId) {
|
|
381
|
+
return projectId;
|
|
382
|
+
}
|
|
383
|
+
} catch (error) {
|
|
384
|
+
console.error("Failed to onboard Gemini managed project:", error);
|
|
385
|
+
return undefined;
|
|
225
386
|
}
|
|
226
387
|
|
|
227
388
|
return undefined;
|
|
228
389
|
}
|
|
229
390
|
|
|
391
|
+
/**
|
|
392
|
+
* Resolves a project context for an access token, optionally persisting updated auth.
|
|
393
|
+
*/
|
|
394
|
+
export async function resolveProjectContextFromAccessToken(
|
|
395
|
+
auth: OAuthAuthDetails,
|
|
396
|
+
accessToken: string,
|
|
397
|
+
configuredProjectId?: string,
|
|
398
|
+
persistAuth?: (auth: OAuthAuthDetails) => Promise<void>,
|
|
399
|
+
): Promise<ProjectContextResult> {
|
|
400
|
+
const parts = parseRefreshParts(auth.refresh);
|
|
401
|
+
const effectiveConfiguredProjectId = configuredProjectId?.trim() || undefined;
|
|
402
|
+
const projectId = effectiveConfiguredProjectId ?? parts.projectId;
|
|
403
|
+
|
|
404
|
+
if (projectId || parts.managedProjectId) {
|
|
405
|
+
return {
|
|
406
|
+
auth,
|
|
407
|
+
effectiveProjectId: projectId || parts.managedProjectId || "",
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
const loadPayload = await loadManagedProject(accessToken, projectId);
|
|
412
|
+
if (!loadPayload) {
|
|
413
|
+
throw new ProjectIdRequiredError();
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
const managedProjectId = normalizeProjectId(loadPayload.cloudaicompanionProject);
|
|
417
|
+
if (managedProjectId) {
|
|
418
|
+
const updatedAuth: OAuthAuthDetails = {
|
|
419
|
+
...auth,
|
|
420
|
+
refresh: formatRefreshParts({
|
|
421
|
+
refreshToken: parts.refreshToken,
|
|
422
|
+
projectId,
|
|
423
|
+
managedProjectId,
|
|
424
|
+
}),
|
|
425
|
+
};
|
|
426
|
+
|
|
427
|
+
if (persistAuth) {
|
|
428
|
+
await persistAuth(updatedAuth);
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
return { auth: updatedAuth, effectiveProjectId: managedProjectId };
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
const currentTierId = loadPayload.currentTier?.id;
|
|
435
|
+
if (currentTierId) {
|
|
436
|
+
if (projectId) {
|
|
437
|
+
return { auth, effectiveProjectId: projectId };
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
const ineligibleMessage = buildIneligibleTierMessage(loadPayload.ineligibleTiers);
|
|
441
|
+
if (ineligibleMessage) {
|
|
442
|
+
throw new Error(ineligibleMessage);
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
throw new ProjectIdRequiredError();
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
const tier = pickOnboardTier(loadPayload.allowedTiers);
|
|
449
|
+
const tierId = tier.id ?? LEGACY_TIER_ID;
|
|
450
|
+
|
|
451
|
+
if (tierId !== FREE_TIER_ID && !projectId) {
|
|
452
|
+
throw new ProjectIdRequiredError();
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
const onboardedProjectId = await onboardManagedProject(accessToken, tierId, projectId);
|
|
456
|
+
if (onboardedProjectId) {
|
|
457
|
+
const updatedAuth: OAuthAuthDetails = {
|
|
458
|
+
...auth,
|
|
459
|
+
refresh: formatRefreshParts({
|
|
460
|
+
refreshToken: parts.refreshToken,
|
|
461
|
+
projectId,
|
|
462
|
+
managedProjectId: onboardedProjectId,
|
|
463
|
+
}),
|
|
464
|
+
};
|
|
465
|
+
|
|
466
|
+
if (persistAuth) {
|
|
467
|
+
await persistAuth(updatedAuth);
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
return { auth: updatedAuth, effectiveProjectId: onboardedProjectId };
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
if (projectId) {
|
|
474
|
+
return { auth, effectiveProjectId: projectId };
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
throw new ProjectIdRequiredError();
|
|
478
|
+
}
|
|
479
|
+
|
|
230
480
|
/**
|
|
231
481
|
* Resolves an effective project ID for the current auth state, caching results per refresh token.
|
|
232
482
|
*/
|
|
@@ -257,75 +507,18 @@ export async function ensureProjectContext(
|
|
|
257
507
|
}
|
|
258
508
|
}
|
|
259
509
|
|
|
260
|
-
const resolveContext = async (): Promise<ProjectContextResult> =>
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
const loadPayload = await loadManagedProject(accessToken, projectId);
|
|
273
|
-
if (loadPayload?.cloudaicompanionProject) {
|
|
274
|
-
const managedProjectId = loadPayload.cloudaicompanionProject;
|
|
275
|
-
const updatedAuth: OAuthAuthDetails = {
|
|
276
|
-
...auth,
|
|
277
|
-
refresh: formatRefreshParts({
|
|
278
|
-
refreshToken: parts.refreshToken,
|
|
279
|
-
projectId,
|
|
280
|
-
managedProjectId,
|
|
281
|
-
}),
|
|
282
|
-
};
|
|
283
|
-
|
|
284
|
-
await client.auth.set({
|
|
285
|
-
path: { id: GEMINI_PROVIDER_ID },
|
|
286
|
-
body: updatedAuth,
|
|
287
|
-
});
|
|
288
|
-
|
|
289
|
-
return { auth: updatedAuth, effectiveProjectId: managedProjectId };
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
if (!loadPayload) {
|
|
293
|
-
throw new ProjectIdRequiredError();
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
const currentTierId = loadPayload.currentTier?.id ?? undefined;
|
|
297
|
-
if (currentTierId && currentTierId !== "FREE") {
|
|
298
|
-
throw new ProjectIdRequiredError();
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
const defaultTierId = getDefaultTierId(loadPayload.allowedTiers);
|
|
302
|
-
const tierId = defaultTierId ?? "FREE";
|
|
303
|
-
|
|
304
|
-
if (tierId !== "FREE") {
|
|
305
|
-
throw new ProjectIdRequiredError();
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
const managedProjectId = await onboardManagedProject(accessToken, tierId, projectId);
|
|
309
|
-
if (managedProjectId) {
|
|
310
|
-
const updatedAuth: OAuthAuthDetails = {
|
|
311
|
-
...auth,
|
|
312
|
-
refresh: formatRefreshParts({
|
|
313
|
-
refreshToken: parts.refreshToken,
|
|
314
|
-
projectId,
|
|
315
|
-
managedProjectId,
|
|
316
|
-
}),
|
|
317
|
-
};
|
|
318
|
-
|
|
319
|
-
await client.auth.set({
|
|
320
|
-
path: { id: GEMINI_PROVIDER_ID },
|
|
321
|
-
body: updatedAuth,
|
|
322
|
-
});
|
|
323
|
-
|
|
324
|
-
return { auth: updatedAuth, effectiveProjectId: managedProjectId };
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
throw new ProjectIdRequiredError();
|
|
328
|
-
};
|
|
510
|
+
const resolveContext = async (): Promise<ProjectContextResult> =>
|
|
511
|
+
resolveProjectContextFromAccessToken(
|
|
512
|
+
auth,
|
|
513
|
+
accessToken,
|
|
514
|
+
configuredProjectId,
|
|
515
|
+
async (updatedAuth) => {
|
|
516
|
+
await client.auth.set({
|
|
517
|
+
path: { id: GEMINI_PROVIDER_ID },
|
|
518
|
+
body: updatedAuth,
|
|
519
|
+
});
|
|
520
|
+
},
|
|
521
|
+
);
|
|
329
522
|
|
|
330
523
|
if (!cacheKey) {
|
|
331
524
|
return resolveContext();
|