opencode-gemini-auth 1.3.8 → 1.3.10
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 +24 -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 +321 -121
- package/src/plugin/request-helpers.ts +265 -0
- package/src/plugin/request.ts +14 -30
- package/src/plugin/token.ts +32 -7
- package/src/plugin.ts +207 -18
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,100 @@ 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) {
|
|
76
126
|
return undefined;
|
|
77
127
|
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
128
|
+
const reasons = tiers
|
|
129
|
+
.map((tier) => tier?.reasonMessage?.trim())
|
|
130
|
+
.filter((message): message is string => !!message);
|
|
131
|
+
if (reasons.length === 0) {
|
|
132
|
+
return undefined;
|
|
133
|
+
}
|
|
134
|
+
return reasons.join(", ");
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Detects VPC-SC errors from Cloud Code responses.
|
|
139
|
+
*/
|
|
140
|
+
function isVpcScError(payload: unknown): boolean {
|
|
141
|
+
if (!payload || typeof payload !== "object") {
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
const error = (payload as { error?: unknown }).error;
|
|
145
|
+
if (!error || typeof error !== "object") {
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
const details = (error as { details?: unknown }).details;
|
|
149
|
+
if (!Array.isArray(details)) {
|
|
150
|
+
return false;
|
|
151
|
+
}
|
|
152
|
+
return details.some((detail) => {
|
|
153
|
+
if (!detail || typeof detail !== "object") {
|
|
154
|
+
return false;
|
|
81
155
|
}
|
|
156
|
+
const reason = (detail as { reason?: unknown }).reason;
|
|
157
|
+
return reason === "SECURITY_POLICY_VIOLATED";
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Safely parses JSON, returning null on failure.
|
|
163
|
+
*/
|
|
164
|
+
function parseJsonSafe(text: string): unknown {
|
|
165
|
+
try {
|
|
166
|
+
return JSON.parse(text);
|
|
167
|
+
} catch {
|
|
168
|
+
return null;
|
|
82
169
|
}
|
|
83
|
-
return allowedTiers[0]?.id;
|
|
84
170
|
}
|
|
85
171
|
|
|
86
172
|
/**
|
|
@@ -140,24 +226,53 @@ export async function loadManagedProject(
|
|
|
140
226
|
if (projectId) {
|
|
141
227
|
requestBody.cloudaicompanionProject = projectId;
|
|
142
228
|
}
|
|
229
|
+
const url = `${GEMINI_CODE_ASSIST_ENDPOINT}/v1internal:loadCodeAssist`;
|
|
230
|
+
const headers = {
|
|
231
|
+
"Content-Type": "application/json",
|
|
232
|
+
Authorization: `Bearer ${accessToken}`,
|
|
233
|
+
...CODE_ASSIST_HEADERS,
|
|
234
|
+
};
|
|
235
|
+
const debugContext = startGeminiDebugRequest({
|
|
236
|
+
originalUrl: url,
|
|
237
|
+
resolvedUrl: url,
|
|
238
|
+
method: "POST",
|
|
239
|
+
headers,
|
|
240
|
+
body: JSON.stringify(requestBody),
|
|
241
|
+
streaming: false,
|
|
242
|
+
projectId,
|
|
243
|
+
});
|
|
143
244
|
|
|
144
|
-
const response = await fetch(
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
245
|
+
const response = await fetch(url, {
|
|
246
|
+
method: "POST",
|
|
247
|
+
headers,
|
|
248
|
+
body: JSON.stringify(requestBody),
|
|
249
|
+
});
|
|
250
|
+
let responseBody: string | undefined;
|
|
251
|
+
if (debugContext || !response.ok) {
|
|
252
|
+
try {
|
|
253
|
+
responseBody = await response.clone().text();
|
|
254
|
+
} catch {
|
|
255
|
+
responseBody = undefined;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
if (debugContext) {
|
|
259
|
+
logGeminiDebugResponse(debugContext, response, { body: responseBody });
|
|
260
|
+
}
|
|
156
261
|
|
|
157
262
|
if (!response.ok) {
|
|
263
|
+
if (responseBody) {
|
|
264
|
+
const parsed = parseJsonSafe(responseBody);
|
|
265
|
+
if (isVpcScError(parsed)) {
|
|
266
|
+
return { currentTier: { id: "standard-tier" } };
|
|
267
|
+
}
|
|
268
|
+
}
|
|
158
269
|
return null;
|
|
159
270
|
}
|
|
160
271
|
|
|
272
|
+
if (responseBody) {
|
|
273
|
+
return parseJsonSafe(responseBody) as LoadCodeAssistPayload;
|
|
274
|
+
}
|
|
275
|
+
|
|
161
276
|
return (await response.json()) as LoadCodeAssistPayload;
|
|
162
277
|
} catch (error) {
|
|
163
278
|
console.error("Failed to load Gemini managed project:", error);
|
|
@@ -176,57 +291,199 @@ export async function onboardManagedProject(
|
|
|
176
291
|
attempts = 10,
|
|
177
292
|
delayMs = 5000,
|
|
178
293
|
): Promise<string | undefined> {
|
|
179
|
-
const
|
|
294
|
+
const isFreeTier = tierId === FREE_TIER_ID;
|
|
295
|
+
const metadata = buildMetadata(projectId, !isFreeTier);
|
|
180
296
|
const requestBody: Record<string, unknown> = {
|
|
181
297
|
tierId,
|
|
182
298
|
metadata,
|
|
183
299
|
};
|
|
184
300
|
|
|
185
|
-
if (
|
|
301
|
+
if (!isFreeTier) {
|
|
186
302
|
if (!projectId) {
|
|
187
303
|
throw new ProjectIdRequiredError();
|
|
188
304
|
}
|
|
189
305
|
requestBody.cloudaicompanionProject = projectId;
|
|
190
306
|
}
|
|
191
307
|
|
|
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
|
-
}
|
|
308
|
+
const baseUrl = `${GEMINI_CODE_ASSIST_ENDPOINT}/v1internal`;
|
|
309
|
+
const onboardUrl = `${baseUrl}:onboardUser`;
|
|
310
|
+
const headers = {
|
|
311
|
+
"Content-Type": "application/json",
|
|
312
|
+
Authorization: `Bearer ${accessToken}`,
|
|
313
|
+
...CODE_ASSIST_HEADERS,
|
|
314
|
+
};
|
|
210
315
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
316
|
+
try {
|
|
317
|
+
const debugContext = startGeminiDebugRequest({
|
|
318
|
+
originalUrl: onboardUrl,
|
|
319
|
+
resolvedUrl: onboardUrl,
|
|
320
|
+
method: "POST",
|
|
321
|
+
headers,
|
|
322
|
+
body: JSON.stringify(requestBody),
|
|
323
|
+
streaming: false,
|
|
324
|
+
projectId,
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
const response = await fetch(onboardUrl, {
|
|
328
|
+
method: "POST",
|
|
329
|
+
headers,
|
|
330
|
+
body: JSON.stringify(requestBody),
|
|
331
|
+
});
|
|
332
|
+
if (debugContext) {
|
|
333
|
+
let responseBody: string | undefined;
|
|
334
|
+
try {
|
|
335
|
+
responseBody = await response.clone().text();
|
|
336
|
+
} catch {
|
|
337
|
+
responseBody = undefined;
|
|
218
338
|
}
|
|
219
|
-
|
|
220
|
-
|
|
339
|
+
logGeminiDebugResponse(debugContext, response, { body: responseBody });
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
if (!response.ok) {
|
|
221
343
|
return undefined;
|
|
222
344
|
}
|
|
223
345
|
|
|
224
|
-
await
|
|
346
|
+
let payload = (await response.json()) as OnboardUserPayload;
|
|
347
|
+
if (!payload.done && payload.name) {
|
|
348
|
+
for (let attempt = 0; attempt < attempts; attempt += 1) {
|
|
349
|
+
await wait(delayMs);
|
|
350
|
+
const operationUrl = `${baseUrl}/${payload.name}`;
|
|
351
|
+
const opDebugContext = startGeminiDebugRequest({
|
|
352
|
+
originalUrl: operationUrl,
|
|
353
|
+
resolvedUrl: operationUrl,
|
|
354
|
+
method: "GET",
|
|
355
|
+
headers,
|
|
356
|
+
streaming: false,
|
|
357
|
+
projectId,
|
|
358
|
+
});
|
|
359
|
+
const opResponse = await fetch(operationUrl, {
|
|
360
|
+
method: "GET",
|
|
361
|
+
headers,
|
|
362
|
+
});
|
|
363
|
+
if (opDebugContext) {
|
|
364
|
+
let responseBody: string | undefined;
|
|
365
|
+
try {
|
|
366
|
+
responseBody = await opResponse.clone().text();
|
|
367
|
+
} catch {
|
|
368
|
+
responseBody = undefined;
|
|
369
|
+
}
|
|
370
|
+
logGeminiDebugResponse(opDebugContext, opResponse, { body: responseBody });
|
|
371
|
+
}
|
|
372
|
+
if (!opResponse.ok) {
|
|
373
|
+
return undefined;
|
|
374
|
+
}
|
|
375
|
+
payload = (await opResponse.json()) as OnboardUserPayload;
|
|
376
|
+
if (payload.done) {
|
|
377
|
+
break;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
const managedProjectId = payload.response?.cloudaicompanionProject?.id;
|
|
383
|
+
if (payload.done && managedProjectId) {
|
|
384
|
+
return managedProjectId;
|
|
385
|
+
}
|
|
386
|
+
if (payload.done && projectId) {
|
|
387
|
+
return projectId;
|
|
388
|
+
}
|
|
389
|
+
} catch (error) {
|
|
390
|
+
console.error("Failed to onboard Gemini managed project:", error);
|
|
391
|
+
return undefined;
|
|
225
392
|
}
|
|
226
393
|
|
|
227
394
|
return undefined;
|
|
228
395
|
}
|
|
229
396
|
|
|
397
|
+
/**
|
|
398
|
+
* Resolves a project context for an access token, optionally persisting updated auth.
|
|
399
|
+
*/
|
|
400
|
+
export async function resolveProjectContextFromAccessToken(
|
|
401
|
+
auth: OAuthAuthDetails,
|
|
402
|
+
accessToken: string,
|
|
403
|
+
configuredProjectId?: string,
|
|
404
|
+
persistAuth?: (auth: OAuthAuthDetails) => Promise<void>,
|
|
405
|
+
): Promise<ProjectContextResult> {
|
|
406
|
+
const parts = parseRefreshParts(auth.refresh);
|
|
407
|
+
const effectiveConfiguredProjectId = configuredProjectId?.trim() || undefined;
|
|
408
|
+
const projectId = effectiveConfiguredProjectId ?? parts.projectId;
|
|
409
|
+
|
|
410
|
+
if (projectId || parts.managedProjectId) {
|
|
411
|
+
const effectiveProjectId = projectId || parts.managedProjectId || "";
|
|
412
|
+
return {
|
|
413
|
+
auth,
|
|
414
|
+
effectiveProjectId,
|
|
415
|
+
};
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
const loadPayload = await loadManagedProject(accessToken, projectId);
|
|
419
|
+
if (!loadPayload) {
|
|
420
|
+
throw new ProjectIdRequiredError();
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
const managedProjectId = normalizeProjectId(loadPayload.cloudaicompanionProject);
|
|
424
|
+
if (managedProjectId) {
|
|
425
|
+
const updatedAuth: OAuthAuthDetails = {
|
|
426
|
+
...auth,
|
|
427
|
+
refresh: formatRefreshParts({
|
|
428
|
+
refreshToken: parts.refreshToken,
|
|
429
|
+
projectId,
|
|
430
|
+
managedProjectId,
|
|
431
|
+
}),
|
|
432
|
+
};
|
|
433
|
+
|
|
434
|
+
if (persistAuth) {
|
|
435
|
+
await persistAuth(updatedAuth);
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
return { auth: updatedAuth, effectiveProjectId: managedProjectId };
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
const currentTierId = loadPayload.currentTier?.id;
|
|
442
|
+
if (currentTierId) {
|
|
443
|
+
if (projectId) {
|
|
444
|
+
return { auth, effectiveProjectId: projectId };
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
const ineligibleMessage = buildIneligibleTierMessage(loadPayload.ineligibleTiers);
|
|
448
|
+
if (ineligibleMessage) {
|
|
449
|
+
throw new Error(ineligibleMessage);
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
throw new ProjectIdRequiredError();
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
const tier = pickOnboardTier(loadPayload.allowedTiers);
|
|
456
|
+
const tierId = tier.id ?? LEGACY_TIER_ID;
|
|
457
|
+
|
|
458
|
+
if (tierId !== FREE_TIER_ID && !projectId) {
|
|
459
|
+
throw new ProjectIdRequiredError();
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
const onboardedProjectId = await onboardManagedProject(accessToken, tierId, projectId);
|
|
463
|
+
if (onboardedProjectId) {
|
|
464
|
+
const updatedAuth: OAuthAuthDetails = {
|
|
465
|
+
...auth,
|
|
466
|
+
refresh: formatRefreshParts({
|
|
467
|
+
refreshToken: parts.refreshToken,
|
|
468
|
+
projectId,
|
|
469
|
+
managedProjectId: onboardedProjectId,
|
|
470
|
+
}),
|
|
471
|
+
};
|
|
472
|
+
|
|
473
|
+
if (persistAuth) {
|
|
474
|
+
await persistAuth(updatedAuth);
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
return { auth: updatedAuth, effectiveProjectId: onboardedProjectId };
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
if (projectId) {
|
|
481
|
+
return { auth, effectiveProjectId: projectId };
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
throw new ProjectIdRequiredError();
|
|
485
|
+
}
|
|
486
|
+
|
|
230
487
|
/**
|
|
231
488
|
* Resolves an effective project ID for the current auth state, caching results per refresh token.
|
|
232
489
|
*/
|
|
@@ -257,75 +514,18 @@ export async function ensureProjectContext(
|
|
|
257
514
|
}
|
|
258
515
|
}
|
|
259
516
|
|
|
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
|
-
};
|
|
517
|
+
const resolveContext = async (): Promise<ProjectContextResult> =>
|
|
518
|
+
resolveProjectContextFromAccessToken(
|
|
519
|
+
auth,
|
|
520
|
+
accessToken,
|
|
521
|
+
configuredProjectId,
|
|
522
|
+
async (updatedAuth) => {
|
|
523
|
+
await client.auth.set({
|
|
524
|
+
path: { id: GEMINI_PROVIDER_ID },
|
|
525
|
+
body: updatedAuth,
|
|
526
|
+
});
|
|
527
|
+
},
|
|
528
|
+
);
|
|
329
529
|
|
|
330
530
|
if (!cacheKey) {
|
|
331
531
|
return resolveContext();
|