opencode-antigravity-auth 1.0.2 → 1.0.3
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 +1 -1
- package/package.json +44 -11
- package/index.ts +0 -14
- package/src/antigravity/oauth.ts +0 -250
- package/src/constants.ts +0 -75
- package/src/plugin/auth.ts +0 -38
- package/src/plugin/cache.ts +0 -65
- package/src/plugin/cli.ts +0 -15
- package/src/plugin/debug.ts +0 -198
- package/src/plugin/project.ts +0 -334
- package/src/plugin/request-helpers.ts +0 -197
- package/src/plugin/request.ts +0 -535
- package/src/plugin/server.ts +0 -247
- package/src/plugin/token.test.ts +0 -74
- package/src/plugin/token.ts +0 -164
- package/src/plugin/types.ts +0 -76
- package/src/plugin.ts +0 -349
- package/src/shims.d.ts +0 -8
package/src/plugin/request.ts
DELETED
|
@@ -1,535 +0,0 @@
|
|
|
1
|
-
import crypto from "node:crypto";
|
|
2
|
-
import {
|
|
3
|
-
ANTIGRAVITY_HEADERS,
|
|
4
|
-
ANTIGRAVITY_ENDPOINT,
|
|
5
|
-
} from "../constants";
|
|
6
|
-
import { logAntigravityDebugResponse, type AntigravityDebugContext } from "./debug";
|
|
7
|
-
import {
|
|
8
|
-
extractUsageFromSsePayload,
|
|
9
|
-
extractUsageMetadata,
|
|
10
|
-
normalizeThinkingConfig,
|
|
11
|
-
parseAntigravityApiBody,
|
|
12
|
-
rewriteAntigravityPreviewAccessError,
|
|
13
|
-
type AntigravityApiBody,
|
|
14
|
-
} from "./request-helpers";
|
|
15
|
-
|
|
16
|
-
function generateSyntheticProjectId(): string {
|
|
17
|
-
const adjectives = ["useful", "bright", "swift", "calm", "bold"];
|
|
18
|
-
const nouns = ["fuze", "wave", "spark", "flow", "core"];
|
|
19
|
-
const adj = adjectives[Math.floor(Math.random() * adjectives.length)];
|
|
20
|
-
const noun = nouns[Math.floor(Math.random() * nouns.length)];
|
|
21
|
-
const randomPart = crypto.randomUUID().slice(0, 5).toLowerCase();
|
|
22
|
-
return `${adj}-${noun}-${randomPart}`;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const STREAM_ACTION = "streamGenerateContent";
|
|
26
|
-
/**
|
|
27
|
-
* Detects requests headed to the Google Generative Language API so we can intercept them.
|
|
28
|
-
*/
|
|
29
|
-
export function isGenerativeLanguageRequest(input: RequestInfo): input is string {
|
|
30
|
-
return typeof input === "string" && input.includes("generativelanguage.googleapis.com");
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Rewrites SSE payloads so downstream consumers see only the inner `response` objects.
|
|
35
|
-
*/
|
|
36
|
-
function transformStreamingPayload(payload: string): string {
|
|
37
|
-
return payload
|
|
38
|
-
.split("\n")
|
|
39
|
-
.map((line) => {
|
|
40
|
-
if (!line.startsWith("data:")) {
|
|
41
|
-
return line;
|
|
42
|
-
}
|
|
43
|
-
const json = line.slice(5).trim();
|
|
44
|
-
if (!json) {
|
|
45
|
-
return line;
|
|
46
|
-
}
|
|
47
|
-
try {
|
|
48
|
-
const parsed = JSON.parse(json) as { response?: unknown };
|
|
49
|
-
if (parsed.response !== undefined) {
|
|
50
|
-
return `data: ${JSON.stringify(parsed.response)}`;
|
|
51
|
-
}
|
|
52
|
-
} catch (_) {}
|
|
53
|
-
return line;
|
|
54
|
-
})
|
|
55
|
-
.join("\n");
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Rewrites OpenAI-style requests into Antigravity shape, normalizing model, headers,
|
|
60
|
-
* optional cached_content, and thinking config. Also toggles streaming mode for SSE actions.
|
|
61
|
-
*/
|
|
62
|
-
export function prepareAntigravityRequest(
|
|
63
|
-
input: RequestInfo,
|
|
64
|
-
init: RequestInit | undefined,
|
|
65
|
-
accessToken: string,
|
|
66
|
-
projectId: string,
|
|
67
|
-
endpointOverride?: string,
|
|
68
|
-
): { request: RequestInfo; init: RequestInit; streaming: boolean; requestedModel?: string; effectiveModel?: string; projectId?: string; endpoint?: string; toolDebugMissing?: number; toolDebugSummary?: string; toolDebugPayload?: string } {
|
|
69
|
-
const baseInit: RequestInit = { ...init };
|
|
70
|
-
const headers = new Headers(init?.headers ?? {});
|
|
71
|
-
let resolvedProjectId = projectId?.trim() || "";
|
|
72
|
-
let toolDebugMissing = 0;
|
|
73
|
-
const toolDebugSummaries: string[] = [];
|
|
74
|
-
let toolDebugPayload: string | undefined;
|
|
75
|
-
|
|
76
|
-
if (!isGenerativeLanguageRequest(input)) {
|
|
77
|
-
return {
|
|
78
|
-
request: input,
|
|
79
|
-
init: { ...baseInit, headers },
|
|
80
|
-
streaming: false,
|
|
81
|
-
};
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
headers.set("Authorization", `Bearer ${accessToken}`);
|
|
85
|
-
headers.delete("x-api-key");
|
|
86
|
-
|
|
87
|
-
const match = input.match(/\/models\/([^:]+):(\w+)/);
|
|
88
|
-
if (!match) {
|
|
89
|
-
return {
|
|
90
|
-
request: input,
|
|
91
|
-
init: { ...baseInit, headers },
|
|
92
|
-
streaming: false,
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
const [, rawModel = "", rawAction = ""] = match;
|
|
97
|
-
const effectiveModel = rawModel;
|
|
98
|
-
const upstreamModel = rawModel;
|
|
99
|
-
const streaming = rawAction === STREAM_ACTION;
|
|
100
|
-
const baseEndpoint = endpointOverride ?? ANTIGRAVITY_ENDPOINT;
|
|
101
|
-
const transformedUrl = `${baseEndpoint}/v1internal:${rawAction}${
|
|
102
|
-
streaming ? "?alt=sse" : ""
|
|
103
|
-
}`;
|
|
104
|
-
const isClaudeModel = upstreamModel.toLowerCase().includes("claude");
|
|
105
|
-
|
|
106
|
-
let body = baseInit.body;
|
|
107
|
-
if (typeof baseInit.body === "string" && baseInit.body) {
|
|
108
|
-
try {
|
|
109
|
-
const parsedBody = JSON.parse(baseInit.body) as Record<string, unknown>;
|
|
110
|
-
const isWrapped = typeof parsedBody.project === "string" && "request" in parsedBody;
|
|
111
|
-
|
|
112
|
-
if (isWrapped) {
|
|
113
|
-
const wrappedBody = {
|
|
114
|
-
...parsedBody,
|
|
115
|
-
model: effectiveModel,
|
|
116
|
-
} as Record<string, unknown>;
|
|
117
|
-
body = JSON.stringify(wrappedBody);
|
|
118
|
-
} else {
|
|
119
|
-
const requestPayload: Record<string, unknown> = { ...parsedBody };
|
|
120
|
-
|
|
121
|
-
const rawGenerationConfig = requestPayload.generationConfig as Record<string, unknown> | undefined;
|
|
122
|
-
const normalizedThinking = normalizeThinkingConfig(rawGenerationConfig?.thinkingConfig);
|
|
123
|
-
if (normalizedThinking) {
|
|
124
|
-
if (rawGenerationConfig) {
|
|
125
|
-
rawGenerationConfig.thinkingConfig = normalizedThinking;
|
|
126
|
-
requestPayload.generationConfig = rawGenerationConfig;
|
|
127
|
-
} else {
|
|
128
|
-
requestPayload.generationConfig = { thinkingConfig: normalizedThinking };
|
|
129
|
-
}
|
|
130
|
-
} else if (rawGenerationConfig?.thinkingConfig) {
|
|
131
|
-
delete rawGenerationConfig.thinkingConfig;
|
|
132
|
-
requestPayload.generationConfig = rawGenerationConfig;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
if ("system_instruction" in requestPayload) {
|
|
136
|
-
requestPayload.systemInstruction = requestPayload.system_instruction;
|
|
137
|
-
delete requestPayload.system_instruction;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
const cachedContentFromExtra =
|
|
141
|
-
typeof requestPayload.extra_body === "object" && requestPayload.extra_body
|
|
142
|
-
? (requestPayload.extra_body as Record<string, unknown>).cached_content ??
|
|
143
|
-
(requestPayload.extra_body as Record<string, unknown>).cachedContent
|
|
144
|
-
: undefined;
|
|
145
|
-
const cachedContent =
|
|
146
|
-
(requestPayload.cached_content as string | undefined) ??
|
|
147
|
-
(requestPayload.cachedContent as string | undefined) ??
|
|
148
|
-
(cachedContentFromExtra as string | undefined);
|
|
149
|
-
if (cachedContent) {
|
|
150
|
-
requestPayload.cachedContent = cachedContent;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
delete requestPayload.cached_content;
|
|
154
|
-
delete requestPayload.cachedContent;
|
|
155
|
-
if (requestPayload.extra_body && typeof requestPayload.extra_body === "object") {
|
|
156
|
-
delete (requestPayload.extra_body as Record<string, unknown>).cached_content;
|
|
157
|
-
delete (requestPayload.extra_body as Record<string, unknown>).cachedContent;
|
|
158
|
-
if (Object.keys(requestPayload.extra_body as Record<string, unknown>).length === 0) {
|
|
159
|
-
delete requestPayload.extra_body;
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
// Normalize tools. For Claude models, keep full function declarations (names + schemas).
|
|
164
|
-
if (Array.isArray(requestPayload.tools)) {
|
|
165
|
-
if (isClaudeModel) {
|
|
166
|
-
const functionDeclarations: any[] = [];
|
|
167
|
-
const passthroughTools: any[] = [];
|
|
168
|
-
|
|
169
|
-
const normalizeSchema = (schema: any) => {
|
|
170
|
-
if (schema && typeof schema === "object") {
|
|
171
|
-
return schema;
|
|
172
|
-
}
|
|
173
|
-
toolDebugMissing += 1;
|
|
174
|
-
return { type: "object", properties: {} };
|
|
175
|
-
};
|
|
176
|
-
|
|
177
|
-
requestPayload.tools.forEach((tool: any, idx: number) => {
|
|
178
|
-
const pushDeclaration = (decl: any, source: string) => {
|
|
179
|
-
const schema =
|
|
180
|
-
decl?.parameters ||
|
|
181
|
-
decl?.input_schema ||
|
|
182
|
-
decl?.inputSchema ||
|
|
183
|
-
tool.parameters ||
|
|
184
|
-
tool.input_schema ||
|
|
185
|
-
tool.inputSchema ||
|
|
186
|
-
tool.function?.parameters ||
|
|
187
|
-
tool.function?.input_schema ||
|
|
188
|
-
tool.function?.inputSchema ||
|
|
189
|
-
tool.custom?.parameters ||
|
|
190
|
-
tool.custom?.input_schema;
|
|
191
|
-
|
|
192
|
-
const name =
|
|
193
|
-
decl?.name ||
|
|
194
|
-
tool.name ||
|
|
195
|
-
tool.function?.name ||
|
|
196
|
-
tool.custom?.name ||
|
|
197
|
-
`tool-${functionDeclarations.length}`;
|
|
198
|
-
|
|
199
|
-
const description =
|
|
200
|
-
decl?.description ||
|
|
201
|
-
tool.description ||
|
|
202
|
-
tool.function?.description ||
|
|
203
|
-
tool.custom?.description ||
|
|
204
|
-
"";
|
|
205
|
-
|
|
206
|
-
functionDeclarations.push({
|
|
207
|
-
name,
|
|
208
|
-
description,
|
|
209
|
-
parameters: normalizeSchema(schema),
|
|
210
|
-
});
|
|
211
|
-
|
|
212
|
-
toolDebugSummaries.push(
|
|
213
|
-
`decl=${name},src=${source},hasSchema=${schema ? "y" : "n"}`,
|
|
214
|
-
);
|
|
215
|
-
};
|
|
216
|
-
|
|
217
|
-
if (Array.isArray(tool.functionDeclarations) && tool.functionDeclarations.length > 0) {
|
|
218
|
-
tool.functionDeclarations.forEach((decl: any) => pushDeclaration(decl, "functionDeclarations"));
|
|
219
|
-
return;
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
// Fall back to function/custom style definitions.
|
|
223
|
-
if (
|
|
224
|
-
tool.function ||
|
|
225
|
-
tool.custom ||
|
|
226
|
-
tool.parameters ||
|
|
227
|
-
tool.input_schema ||
|
|
228
|
-
tool.inputSchema
|
|
229
|
-
) {
|
|
230
|
-
pushDeclaration(tool.function ?? tool.custom ?? tool, "function/custom");
|
|
231
|
-
return;
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
// Preserve any non-function tool entries (e.g., codeExecution) untouched.
|
|
235
|
-
passthroughTools.push(tool);
|
|
236
|
-
});
|
|
237
|
-
|
|
238
|
-
const finalTools: any[] = [];
|
|
239
|
-
if (functionDeclarations.length > 0) {
|
|
240
|
-
finalTools.push({ functionDeclarations });
|
|
241
|
-
}
|
|
242
|
-
requestPayload.tools = finalTools.concat(passthroughTools);
|
|
243
|
-
} else {
|
|
244
|
-
// Default normalization for non-Claude models
|
|
245
|
-
requestPayload.tools = requestPayload.tools.map((tool: any, toolIndex: number) => {
|
|
246
|
-
const newTool = { ...tool };
|
|
247
|
-
|
|
248
|
-
const schemaCandidates = [
|
|
249
|
-
newTool.function?.input_schema,
|
|
250
|
-
newTool.function?.parameters,
|
|
251
|
-
newTool.function?.inputSchema,
|
|
252
|
-
newTool.custom?.input_schema,
|
|
253
|
-
newTool.custom?.parameters,
|
|
254
|
-
newTool.parameters,
|
|
255
|
-
newTool.input_schema,
|
|
256
|
-
newTool.inputSchema,
|
|
257
|
-
].filter(Boolean);
|
|
258
|
-
const schema = schemaCandidates[0];
|
|
259
|
-
|
|
260
|
-
const nameCandidate =
|
|
261
|
-
newTool.name ||
|
|
262
|
-
newTool.function?.name ||
|
|
263
|
-
newTool.custom?.name ||
|
|
264
|
-
`tool-${toolIndex}`;
|
|
265
|
-
|
|
266
|
-
if (newTool.function && !newTool.function.input_schema && schema) {
|
|
267
|
-
newTool.function.input_schema = schema;
|
|
268
|
-
}
|
|
269
|
-
if (newTool.custom && !newTool.custom.input_schema && schema) {
|
|
270
|
-
newTool.custom.input_schema = schema;
|
|
271
|
-
}
|
|
272
|
-
if (!newTool.custom && newTool.function) {
|
|
273
|
-
newTool.custom = {
|
|
274
|
-
name: newTool.function.name || nameCandidate,
|
|
275
|
-
description: newTool.function.description,
|
|
276
|
-
input_schema: schema ?? { type: "object", properties: {} },
|
|
277
|
-
};
|
|
278
|
-
}
|
|
279
|
-
if (!newTool.custom && !newTool.function) {
|
|
280
|
-
newTool.custom = {
|
|
281
|
-
name: nameCandidate,
|
|
282
|
-
description: newTool.description,
|
|
283
|
-
input_schema: schema ?? { type: "object", properties: {} },
|
|
284
|
-
};
|
|
285
|
-
}
|
|
286
|
-
if (newTool.custom && !newTool.custom.input_schema) {
|
|
287
|
-
newTool.custom.input_schema = { type: "object", properties: {} };
|
|
288
|
-
toolDebugMissing += 1;
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
toolDebugSummaries.push(
|
|
292
|
-
`idx=${toolIndex}, hasCustom=${!!newTool.custom}, customSchema=${!!newTool.custom?.input_schema}, hasFunction=${!!newTool.function}, functionSchema=${!!newTool.function?.input_schema}`,
|
|
293
|
-
);
|
|
294
|
-
|
|
295
|
-
// Strip custom wrappers for Gemini; only function-style is accepted.
|
|
296
|
-
if (newTool.custom) {
|
|
297
|
-
delete newTool.custom;
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
return newTool;
|
|
301
|
-
});
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
try {
|
|
305
|
-
toolDebugPayload = JSON.stringify(requestPayload.tools);
|
|
306
|
-
} catch {
|
|
307
|
-
toolDebugPayload = undefined;
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
// For Claude models, ensure functionCall/tool use parts carry IDs (required by Anthropic).
|
|
312
|
-
if (isClaudeModel && Array.isArray(requestPayload.contents)) {
|
|
313
|
-
let toolCallCounter = 0;
|
|
314
|
-
const lastCallIdByName = new Map<string, string>();
|
|
315
|
-
|
|
316
|
-
requestPayload.contents = requestPayload.contents.map((content: any) => {
|
|
317
|
-
if (!content || !Array.isArray(content.parts)) {
|
|
318
|
-
return content;
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
const newParts = content.parts.map((part: any) => {
|
|
322
|
-
if (part && typeof part === "object" && part.functionCall) {
|
|
323
|
-
const call = { ...part.functionCall };
|
|
324
|
-
if (!call.id) {
|
|
325
|
-
call.id = `tool-call-${++toolCallCounter}`;
|
|
326
|
-
}
|
|
327
|
-
const nameKey = typeof call.name === "string" ? call.name : `tool-${toolCallCounter}`;
|
|
328
|
-
lastCallIdByName.set(nameKey, call.id);
|
|
329
|
-
return { ...part, functionCall: call };
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
if (part && typeof part === "object" && part.functionResponse) {
|
|
333
|
-
const resp = { ...part.functionResponse };
|
|
334
|
-
if (!resp.id && typeof resp.name === "string") {
|
|
335
|
-
const linkedId = lastCallIdByName.get(resp.name);
|
|
336
|
-
if (linkedId) {
|
|
337
|
-
resp.id = linkedId;
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
return { ...part, functionResponse: resp };
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
return part;
|
|
344
|
-
});
|
|
345
|
-
|
|
346
|
-
return { ...content, parts: newParts };
|
|
347
|
-
});
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
if ("model" in requestPayload) {
|
|
351
|
-
delete requestPayload.model;
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
const effectiveProjectId = projectId?.trim() || generateSyntheticProjectId();
|
|
355
|
-
resolvedProjectId = effectiveProjectId;
|
|
356
|
-
|
|
357
|
-
const wrappedBody = {
|
|
358
|
-
project: effectiveProjectId,
|
|
359
|
-
model: upstreamModel,
|
|
360
|
-
request: requestPayload,
|
|
361
|
-
};
|
|
362
|
-
|
|
363
|
-
// Add additional Antigravity fields
|
|
364
|
-
Object.assign(wrappedBody, {
|
|
365
|
-
userAgent: "antigravity",
|
|
366
|
-
requestId: "agent-" + crypto.randomUUID(),
|
|
367
|
-
});
|
|
368
|
-
if (wrappedBody.request && typeof wrappedBody.request === 'object') {
|
|
369
|
-
(wrappedBody.request as any).sessionId = "-" + Math.floor(Math.random() * 9000000000000000000).toString();
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
body = JSON.stringify(wrappedBody);
|
|
373
|
-
}
|
|
374
|
-
} catch (error) {
|
|
375
|
-
throw error;
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
if (streaming) {
|
|
380
|
-
headers.set("Accept", "text/event-stream");
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
headers.set("User-Agent", ANTIGRAVITY_HEADERS["User-Agent"]);
|
|
384
|
-
headers.set("X-Goog-Api-Client", ANTIGRAVITY_HEADERS["X-Goog-Api-Client"]);
|
|
385
|
-
headers.set("Client-Metadata", ANTIGRAVITY_HEADERS["Client-Metadata"]);
|
|
386
|
-
// Optional debug header to observe tool normalization on the backend if surfaced
|
|
387
|
-
if (toolDebugMissing > 0) {
|
|
388
|
-
headers.set("X-Opencode-Tools-Debug", String(toolDebugMissing));
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
return {
|
|
392
|
-
request: transformedUrl,
|
|
393
|
-
init: {
|
|
394
|
-
...baseInit,
|
|
395
|
-
headers,
|
|
396
|
-
body,
|
|
397
|
-
},
|
|
398
|
-
streaming,
|
|
399
|
-
requestedModel: rawModel,
|
|
400
|
-
effectiveModel: upstreamModel,
|
|
401
|
-
projectId: resolvedProjectId,
|
|
402
|
-
endpoint: transformedUrl,
|
|
403
|
-
toolDebugMissing,
|
|
404
|
-
toolDebugSummary: toolDebugSummaries.slice(0, 20).join(" | "),
|
|
405
|
-
toolDebugPayload,
|
|
406
|
-
};
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
/**
|
|
410
|
-
* Normalizes Antigravity responses: applies retry headers, extracts cache usage into headers,
|
|
411
|
-
* rewrites preview errors, flattens streaming payloads, and logs debug metadata.
|
|
412
|
-
*/
|
|
413
|
-
export async function transformAntigravityResponse(
|
|
414
|
-
response: Response,
|
|
415
|
-
streaming: boolean,
|
|
416
|
-
debugContext?: AntigravityDebugContext | null,
|
|
417
|
-
requestedModel?: string,
|
|
418
|
-
projectId?: string,
|
|
419
|
-
endpoint?: string,
|
|
420
|
-
effectiveModel?: string,
|
|
421
|
-
toolDebugMissing?: number,
|
|
422
|
-
toolDebugSummary?: string,
|
|
423
|
-
toolDebugPayload?: string,
|
|
424
|
-
): Promise<Response> {
|
|
425
|
-
const contentType = response.headers.get("content-type") ?? "";
|
|
426
|
-
const isJsonResponse = contentType.includes("application/json");
|
|
427
|
-
const isEventStreamResponse = contentType.includes("text/event-stream");
|
|
428
|
-
|
|
429
|
-
if (!isJsonResponse && !isEventStreamResponse) {
|
|
430
|
-
logAntigravityDebugResponse(debugContext, response, {
|
|
431
|
-
note: "Non-JSON response (body omitted)",
|
|
432
|
-
});
|
|
433
|
-
return response;
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
try {
|
|
437
|
-
const text = await response.text();
|
|
438
|
-
const headers = new Headers(response.headers);
|
|
439
|
-
|
|
440
|
-
if (!response.ok) {
|
|
441
|
-
let errorBody;
|
|
442
|
-
try {
|
|
443
|
-
errorBody = JSON.parse(text);
|
|
444
|
-
} catch {
|
|
445
|
-
errorBody = { error: { message: text } };
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
// Inject Debug Info
|
|
449
|
-
if (errorBody?.error) {
|
|
450
|
-
const debugInfo = `\n\n[Debug Info]\nRequested Model: ${requestedModel || "Unknown"}\nEffective Model: ${effectiveModel || "Unknown"}\nProject: ${projectId || "Unknown"}\nEndpoint: ${endpoint || "Unknown"}\nStatus: ${response.status}\nRequest ID: ${headers.get('x-request-id') || "N/A"}${toolDebugMissing !== undefined ? `\nTool Debug Missing: ${toolDebugMissing}` : ""}${toolDebugSummary ? `\nTool Debug Summary: ${toolDebugSummary}` : ""}${toolDebugPayload ? `\nTool Debug Payload: ${toolDebugPayload}` : ""}`;
|
|
451
|
-
errorBody.error.message = (errorBody.error.message || "Unknown error") + debugInfo;
|
|
452
|
-
|
|
453
|
-
return new Response(JSON.stringify(errorBody), {
|
|
454
|
-
status: response.status,
|
|
455
|
-
statusText: response.statusText,
|
|
456
|
-
headers
|
|
457
|
-
});
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
if (errorBody?.error?.details && Array.isArray(errorBody.error.details)) {
|
|
461
|
-
const retryInfo = errorBody.error.details.find(
|
|
462
|
-
(detail: any) => detail['@type'] === 'type.googleapis.com/google.rpc.RetryInfo'
|
|
463
|
-
);
|
|
464
|
-
|
|
465
|
-
if (retryInfo?.retryDelay) {
|
|
466
|
-
const match = retryInfo.retryDelay.match(/^([\d.]+)s$/);
|
|
467
|
-
if (match && match[1]) {
|
|
468
|
-
const retrySeconds = parseFloat(match[1]);
|
|
469
|
-
if (!isNaN(retrySeconds) && retrySeconds > 0) {
|
|
470
|
-
const retryAfterSec = Math.ceil(retrySeconds).toString();
|
|
471
|
-
const retryAfterMs = Math.ceil(retrySeconds * 1000).toString();
|
|
472
|
-
headers.set('Retry-After', retryAfterSec);
|
|
473
|
-
headers.set('retry-after-ms', retryAfterMs);
|
|
474
|
-
}
|
|
475
|
-
}
|
|
476
|
-
}
|
|
477
|
-
}
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
const init = {
|
|
481
|
-
status: response.status,
|
|
482
|
-
statusText: response.statusText,
|
|
483
|
-
headers,
|
|
484
|
-
};
|
|
485
|
-
|
|
486
|
-
const usageFromSse = streaming && isEventStreamResponse ? extractUsageFromSsePayload(text) : null;
|
|
487
|
-
const parsed: AntigravityApiBody | null = !streaming || !isEventStreamResponse ? parseAntigravityApiBody(text) : null;
|
|
488
|
-
const patched = parsed ? rewriteAntigravityPreviewAccessError(parsed, response.status, requestedModel) : null;
|
|
489
|
-
const effectiveBody = patched ?? parsed ?? undefined;
|
|
490
|
-
|
|
491
|
-
const usage = usageFromSse ?? (effectiveBody ? extractUsageMetadata(effectiveBody) : null);
|
|
492
|
-
if (usage?.cachedContentTokenCount !== undefined) {
|
|
493
|
-
headers.set("x-antigravity-cached-content-token-count", String(usage.cachedContentTokenCount));
|
|
494
|
-
if (usage.totalTokenCount !== undefined) {
|
|
495
|
-
headers.set("x-antigravity-total-token-count", String(usage.totalTokenCount));
|
|
496
|
-
}
|
|
497
|
-
if (usage.promptTokenCount !== undefined) {
|
|
498
|
-
headers.set("x-antigravity-prompt-token-count", String(usage.promptTokenCount));
|
|
499
|
-
}
|
|
500
|
-
if (usage.candidatesTokenCount !== undefined) {
|
|
501
|
-
headers.set("x-antigravity-candidates-token-count", String(usage.candidatesTokenCount));
|
|
502
|
-
}
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
logAntigravityDebugResponse(debugContext, response, {
|
|
506
|
-
body: text,
|
|
507
|
-
note: streaming ? "Streaming SSE payload" : undefined,
|
|
508
|
-
headersOverride: headers,
|
|
509
|
-
});
|
|
510
|
-
|
|
511
|
-
if (streaming && response.ok && isEventStreamResponse) {
|
|
512
|
-
return new Response(transformStreamingPayload(text), init);
|
|
513
|
-
}
|
|
514
|
-
|
|
515
|
-
if (!parsed) {
|
|
516
|
-
return new Response(text, init);
|
|
517
|
-
}
|
|
518
|
-
|
|
519
|
-
if (effectiveBody?.response !== undefined) {
|
|
520
|
-
return new Response(JSON.stringify(effectiveBody.response), init);
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
if (patched) {
|
|
524
|
-
return new Response(JSON.stringify(patched), init);
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
return new Response(text, init);
|
|
528
|
-
} catch (error) {
|
|
529
|
-
logAntigravityDebugResponse(debugContext, response, {
|
|
530
|
-
error,
|
|
531
|
-
note: "Failed to transform Antigravity response",
|
|
532
|
-
});
|
|
533
|
-
return response;
|
|
534
|
-
}
|
|
535
|
-
}
|