opencode-google-auth 0.0.1 → 0.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/dist/main.mjs DELETED
@@ -1,716 +0,0 @@
1
- import { FetchHttpClient, HttpClient, HttpClientRequest, HttpClientResponse, HttpRouter, HttpServer, HttpServerRequest, HttpServerResponse, PlatformLogger } from "@effect/platform";
2
- import { Context, Data, Deferred, Effect, Fiber, Inspectable, Layer, LogLevel, Logger, ManagedRuntime, Ref, Schema, Stream, pipe } from "effect";
3
- import { BunFileSystem, BunHttpServer } from "@effect/platform-bun";
4
- import path from "node:path";
5
- import { OAuth2Client } from "google-auth-library";
6
- import { regex } from "arkregex";
7
- import { Retry, encoder, makeChannel } from "@effect/experimental/Sse";
8
-
9
- //#region src/lib/services/config.ts
10
- var ProviderConfig = class extends Context.Tag("ProviderConfig")() {};
11
- const CODE_ASSIST_VERSION = "v1internal";
12
- const GEMINI_CLI_MODELS = [
13
- "gemini-2.5-pro",
14
- "gemini-2.5-flash",
15
- "gemini-2.5-flash-lite",
16
- "gemini-3-pro-preview",
17
- "gemini-3-flash-preview"
18
- ];
19
- const geminiCliConfig = () => ({
20
- SERVICE_NAME: "gemini-cli",
21
- DISPLAY_NAME: "Gemini CLI",
22
- CLIENT_ID: "681255809395-oo8ft2oprdrnp9e3aqf6av3hmdib135j.apps.googleusercontent.com",
23
- CLIENT_SECRET: "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",
24
- SCOPES: [
25
- "https://www.googleapis.com/auth/cloud-platform",
26
- "https://www.googleapis.com/auth/userinfo.email",
27
- "https://www.googleapis.com/auth/userinfo.profile"
28
- ],
29
- ENDPOINTS: ["https://cloudcode-pa.googleapis.com"],
30
- HEADERS: {
31
- "User-Agent": "google-api-nodejs-client/9.15.1",
32
- "X-Goog-Api-Client": "gl-node/22.17.0",
33
- "Client-Metadata": "ideType=IDE_UNSPECIFIED,platform=PLATFORM_UNSPECIFIED,pluginType=GEMINI"
34
- },
35
- getConfig: (modelsDev) => {
36
- const provider = modelsDev.google;
37
- const filteredModels = pipe(provider.models, (models) => Object.entries(models), (entries) => entries.filter(([key]) => GEMINI_CLI_MODELS.includes(key)), (filtered) => Object.fromEntries(filtered));
38
- return {
39
- ...provider,
40
- id: geminiCliConfig().SERVICE_NAME,
41
- name: geminiCliConfig().DISPLAY_NAME,
42
- api: geminiCliConfig().ENDPOINTS.at(0),
43
- models: filteredModels
44
- };
45
- }
46
- });
47
- const antigravityConfig = () => ({
48
- SERVICE_NAME: "antigravity",
49
- DISPLAY_NAME: "Antigravity",
50
- CLIENT_ID: "1071006060591-tmhssin2h21lcre235vtolojh4g403ep.apps.googleusercontent.com",
51
- CLIENT_SECRET: "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",
52
- SCOPES: [
53
- "https://www.googleapis.com/auth/cloud-platform",
54
- "https://www.googleapis.com/auth/userinfo.email",
55
- "https://www.googleapis.com/auth/userinfo.profile",
56
- "https://www.googleapis.com/auth/cclog",
57
- "https://www.googleapis.com/auth/experimentsandconfigs"
58
- ],
59
- ENDPOINTS: [
60
- "https://daily-cloudcode-pa.sandbox.googleapis.com",
61
- "https://autopush-cloudcode-pa.sandbox.googleapis.com",
62
- "https://cloudcode-pa.googleapis.com"
63
- ],
64
- HEADERS: {
65
- "User-Agent": "antigravity/1.11.5 windows/amd64",
66
- "X-Goog-Api-Client": "google-cloud-sdk vscode_cloudshelleditor/0.1",
67
- "Client-Metadata": "{\"ideType\":\"IDE_UNSPECIFIED\",\"platform\":\"PLATFORM_UNSPECIFIED\",\"pluginType\":\"GEMINI\"}"
68
- },
69
- getConfig: (modelsDev) => {
70
- const googleProvider = modelsDev.google;
71
- const googleVertextProvider = modelsDev["google-vertex-anthropic"];
72
- const geminiFlash = googleProvider.models["gemini-3-flash-preview"];
73
- const geminiPro = googleProvider.models["gemini-3-pro-preview"];
74
- const claudeSonnet = googleVertextProvider.models["claude-sonnet-4-5@20250929"];
75
- const claudeOpus = googleVertextProvider.models["claude-opus-4-5@20251101"];
76
- const models = {
77
- "gemini-3-flash": {
78
- ...geminiFlash,
79
- id: "gemini-3-flash"
80
- },
81
- "gemini-3-pro-low": {
82
- ...geminiPro,
83
- id: "gemini-3-pro-low",
84
- name: "Gemini 3 Pro (Low)",
85
- temperature: false,
86
- options: { thinkingConfig: { thinkingLevel: "low" } }
87
- },
88
- "gemini-3-pro-high": {
89
- ...geminiPro,
90
- id: "gemini-3-pro-high",
91
- name: "Gemini 3 Pro (High)",
92
- temperature: false,
93
- options: { thinkingConfig: { thinkingLevel: "high" } }
94
- },
95
- "claude-sonnet-4-5": {
96
- ...claudeSonnet,
97
- id: "claude-sonnet-4-5",
98
- reasoning: false,
99
- options: { thinkingConfig: { includeThoughts: false } }
100
- },
101
- "claude-sonnet-4-5-thinking": {
102
- ...claudeSonnet,
103
- id: "claude-sonnet-4-5-thinking",
104
- name: "Claude Sonnet 4.5 (Reasoning)"
105
- },
106
- "claude-opus-4-5-thinking": {
107
- ...claudeOpus,
108
- id: "claude-opus-4-5-thinking",
109
- name: "Claude Opus 4.5 (Reasoning)"
110
- }
111
- };
112
- return {
113
- ...googleProvider,
114
- id: antigravityConfig().SERVICE_NAME,
115
- name: antigravityConfig().DISPLAY_NAME,
116
- api: antigravityConfig().ENDPOINTS.at(2),
117
- models
118
- };
119
- },
120
- transformRequest: Effect.fn(function* (context) {
121
- yield* Effect.log("Transforming request for: ", antigravityConfig().SERVICE_NAME);
122
- const { body, headers, url } = context;
123
- const innerRequest = body.request;
124
- let sessionId;
125
- if (innerRequest.labels && typeof innerRequest.labels === "object" && "sessionId" in innerRequest.labels) {
126
- const labels = innerRequest.labels;
127
- sessionId = labels.sessionId;
128
- delete labels.sessionId;
129
- if (Object.keys(labels).length === 0) delete innerRequest.labels;
130
- }
131
- const isClaude = body.model.toLowerCase().includes("claude");
132
- const isThinking = body.model.toLowerCase().includes("thinking");
133
- if (isClaude && body.request && typeof body.request === "object") {
134
- const generationConfig = body.request.generationConfig;
135
- innerRequest.toolConfig = { functionCallingConfig: { mode: "VALIDATED" } };
136
- if (!isThinking && generationConfig?.thinkingConfig) delete generationConfig.thinkingConfig;
137
- if (isThinking && generationConfig?.thinkingConfig) {
138
- const thinkingConfig = generationConfig.thinkingConfig;
139
- if (thinkingConfig.includeThoughts !== void 0) {
140
- thinkingConfig.include_thoughts = thinkingConfig.includeThoughts;
141
- delete thinkingConfig.includeThoughts;
142
- }
143
- if (thinkingConfig.thinkingBudget !== void 0) {
144
- thinkingConfig.thinking_budget = thinkingConfig.thinkingBudget;
145
- delete thinkingConfig.thinkingBudget;
146
- }
147
- if (thinkingConfig.thinking_budget === void 0) thinkingConfig.thinking_budget = 32768;
148
- }
149
- if (isThinking) headers.set("anthropic-beta", "interleaved-thinking-2025-05-14");
150
- }
151
- if (sessionId) {
152
- const hashedSession = yield* Effect.promise(() => hash(sessionId));
153
- innerRequest.sessionId = [
154
- `-${crypto.randomUUID()}`,
155
- body.model,
156
- body.project,
157
- `seed-${hashedSession}`
158
- ].join(":");
159
- }
160
- if (innerRequest.tools && Array.isArray(innerRequest.tools)) {
161
- const tools = innerRequest.tools;
162
- for (const tool of tools) if (tool.functionDeclarations && Array.isArray(tool.functionDeclarations)) {
163
- const functionDeclarations = tool.functionDeclarations;
164
- for (let i = 0; i < functionDeclarations.length; i++) {
165
- const declaration = functionDeclarations[i];
166
- if (declaration && declaration.name === "todoread") functionDeclarations[i] = {
167
- ...functionDeclarations[i],
168
- parameters: {
169
- type: "object",
170
- properties: { _placeholder: {
171
- type: "boolean",
172
- description: "Placeholder. Always pass true."
173
- } },
174
- required: ["_placeholder"],
175
- additionalProperties: false
176
- }
177
- };
178
- }
179
- }
180
- }
181
- if (innerRequest.systemInstruction && typeof innerRequest.systemInstruction === "object") {
182
- const systemInstruction = innerRequest.systemInstruction;
183
- systemInstruction.role = "user";
184
- }
185
- return {
186
- headers,
187
- url,
188
- body: {
189
- ...body,
190
- requestType: "agent",
191
- userAgent: "antigravity",
192
- requestId: `agent-${crypto.randomUUID()}`
193
- }
194
- };
195
- })
196
- });
197
- async function hash(str) {
198
- const data = new TextEncoder().encode(str);
199
- const hashBuffer = await crypto.subtle.digest("SHA-256", data);
200
- return Array.from(new Uint8Array(hashBuffer)).map((b) => b.toString(16).padStart(2, "0")).join("").slice(0, 16);
201
- }
202
-
203
- //#endregion
204
- //#region src/lib/services/opencode.ts
205
- var OpenCodeContext = class extends Context.Tag("OpenCodeContext")() {};
206
-
207
- //#endregion
208
- //#region src/lib/logger.ts
209
- const makeOpenCodeLogger = Effect.gen(function* () {
210
- const openCode = yield* OpenCodeContext;
211
- const config = yield* ProviderConfig;
212
- return Logger.make((log) => {
213
- let level = "debug";
214
- if (LogLevel.greaterThanEqual(log.logLevel, LogLevel.Error)) level = "error";
215
- else if (LogLevel.greaterThanEqual(log.logLevel, LogLevel.Warning)) level = "warn";
216
- else if (LogLevel.greaterThanEqual(log.logLevel, LogLevel.Info)) level = "info";
217
- const message = Inspectable.toStringUnknown(log.message);
218
- openCode.client.app.log({ body: {
219
- level,
220
- message,
221
- service: config.SERVICE_NAME
222
- } });
223
- });
224
- });
225
- const combinedLogger = Effect.gen(function* () {
226
- const openCodeLogger = yield* makeOpenCodeLogger;
227
- const fileLogger = yield* pipe(Logger.jsonLogger, PlatformLogger.toFile(path.join(import.meta.dir, "plugin.log")));
228
- return Logger.zip(openCodeLogger, fileLogger);
229
- });
230
-
231
- //#endregion
232
- //#region src/lib/services/oauth.ts
233
- /**
234
- * OAuth service
235
- *
236
- * Handles initial OAuth authentication flow only.
237
- * Token refresh is handled by the Session service.
238
- */
239
- var OAuthError = class extends Data.TaggedError("OAuthError") {};
240
- const SuccessParamsSchema = Schema.Struct({
241
- code: Schema.String,
242
- state: Schema.String
243
- });
244
- const FailureParamsSchema = Schema.Struct({
245
- error: Schema.String,
246
- error_description: Schema.optional(Schema.String),
247
- state: Schema.optional(Schema.String)
248
- });
249
- const isFailureParams = Schema.is(FailureParamsSchema);
250
- const ParamsSchema = Schema.Union(SuccessParamsSchema, FailureParamsSchema);
251
- var OAuth = class extends Effect.Service()("OAuth", { effect: Effect.gen(function* () {
252
- const config = yield* ProviderConfig;
253
- const client = new OAuth2Client({
254
- clientId: config.CLIENT_ID,
255
- clientSecret: config.CLIENT_SECRET
256
- });
257
- const ServerLive = BunHttpServer.layerServer({ port: 0 });
258
- return { authenticate: Effect.gen(function* () {
259
- yield* HttpServer.logAddress;
260
- const deferredParams = yield* Deferred.make();
261
- const redirectUri = yield* HttpServer.addressFormattedWith((address) => Effect.succeed(`${address}/oauth2callback`));
262
- const state = crypto.randomUUID();
263
- const authUrl = client.generateAuthUrl({
264
- state,
265
- redirect_uri: redirectUri,
266
- access_type: "offline",
267
- scope: config.SCOPES,
268
- prompt: "consent"
269
- });
270
- yield* Effect.log(`OAuth2 authorization URL: ${authUrl}`);
271
- const serverFiber = yield* HttpRouter.empty.pipe(HttpRouter.get("/oauth2callback", Effect.gen(function* () {
272
- const params = yield* HttpServerRequest.schemaSearchParams(ParamsSchema);
273
- if (isFailureParams(params)) yield* Deferred.fail(deferredParams, new OAuthError({
274
- reason: "callback",
275
- message: `${params.error} - ${params.error_description ?? "No additional details provided"}`
276
- }));
277
- else yield* Deferred.succeed(deferredParams, params);
278
- return yield* HttpServerResponse.text("You may now close this tab.");
279
- }).pipe(Effect.tapError(Effect.logError))), HttpServer.serveEffect(), Effect.fork);
280
- yield* Effect.log("Started OAuth2 callback server");
281
- const search = yield* Deferred.await(deferredParams);
282
- yield* Effect.log("Received OAuth2 callback with params", search);
283
- yield* Fiber.interrupt(serverFiber);
284
- if (state !== search.state) return yield* new OAuthError({
285
- reason: "state_mismatch",
286
- message: "Invalid state parameter. Possible CSRF attack."
287
- });
288
- return (yield* Effect.tryPromise({
289
- try: () => client.getToken({
290
- code: search.code,
291
- redirect_uri: redirectUri
292
- }),
293
- catch: (cause) => new OAuthError({
294
- reason: "token_exchange",
295
- message: "Failed to exchange authorization code for tokens",
296
- cause
297
- })
298
- })).tokens;
299
- }).pipe(Effect.provide(ServerLive), Effect.scoped) };
300
- }) }) {};
301
-
302
- //#endregion
303
- //#region src/lib/services/session.ts
304
- var SessionError = class extends Data.TaggedError("SessionError") {};
305
- var TokenExpiredError = class extends Data.TaggedError("TokenExpiredError") {};
306
- const CodeAssistTier = Schema.Struct({
307
- id: Schema.String,
308
- name: Schema.String,
309
- description: Schema.String,
310
- userDefinedCloudaicompanionProject: Schema.Boolean,
311
- isDefault: Schema.optional(Schema.Boolean)
312
- });
313
- const LoadCodeAssistResponse = Schema.Struct({
314
- currentTier: CodeAssistTier,
315
- allowedTiers: Schema.Array(CodeAssistTier),
316
- cloudaicompanionProject: Schema.String,
317
- gcpManaged: Schema.Boolean,
318
- manageSubscriptionUri: Schema.String
319
- });
320
- var Session = class extends Effect.Service()("Session", { effect: Effect.gen(function* () {
321
- const config = yield* ProviderConfig;
322
- const openCode = yield* OpenCodeContext;
323
- const httpClient = yield* HttpClient.HttpClient;
324
- const credentialsRef = yield* Ref.make(null);
325
- const projectRef = yield* Ref.make(null);
326
- const endpoint = config.ENDPOINTS.at(0);
327
- const oauthClient = new OAuth2Client({
328
- clientId: config.CLIENT_ID,
329
- clientSecret: config.CLIENT_SECRET
330
- });
331
- const getCredentials = Effect.gen(function* () {
332
- const current = yield* Ref.get(credentialsRef);
333
- if (!current) return yield* new SessionError({
334
- reason: "no_tokens",
335
- message: "No credentials set"
336
- });
337
- return current;
338
- });
339
- const refreshTokens = Effect.gen(function* () {
340
- const credentials = yield* getCredentials;
341
- oauthClient.setCredentials(credentials);
342
- const newCredentials = (yield* Effect.tryPromise({
343
- try: () => oauthClient.refreshAccessToken(),
344
- catch: (cause) => new SessionError({
345
- reason: "token_refresh",
346
- message: "Failed to refresh access token",
347
- cause
348
- })
349
- })).credentials;
350
- yield* Ref.set(credentialsRef, newCredentials);
351
- const accessToken = newCredentials.access_token;
352
- const refreshToken = newCredentials.refresh_token;
353
- const expiryDate = newCredentials.expiry_date;
354
- if (accessToken && refreshToken && expiryDate) yield* Effect.promise(() => openCode.client.auth.set({
355
- path: { id: config.SERVICE_NAME },
356
- body: {
357
- type: "oauth",
358
- access: accessToken,
359
- refresh: refreshToken,
360
- expires: expiryDate
361
- }
362
- }));
363
- return newCredentials;
364
- });
365
- const fetchAttempt = Effect.gen(function* () {
366
- const credentials = yield* getCredentials;
367
- const request = yield* HttpClientRequest.post(`${endpoint}/${CODE_ASSIST_VERSION}:loadCodeAssist`).pipe(HttpClientRequest.bearerToken(credentials.access_token), HttpClientRequest.bodyJson({ metadata: {
368
- ideType: "IDE_UNSPECIFIED",
369
- platform: "PLATFORM_UNSPECIFIED",
370
- pluginType: "GEMINI"
371
- } }));
372
- return yield* pipe(httpClient.execute(request), Effect.andThen(HttpClientResponse.matchStatus({
373
- "2xx": (res) => HttpClientResponse.schemaBodyJson(LoadCodeAssistResponse)(res),
374
- 401: () => new TokenExpiredError({ message: "Token expired" }),
375
- orElse: (response) => new SessionError({
376
- reason: "project_fetch",
377
- message: `HTTP error: ${response.status}`
378
- })
379
- })));
380
- });
381
- const fetchProject = fetchAttempt.pipe(Effect.catchTag("TokenExpiredError", () => pipe(Effect.log("Token expired, refreshing..."), Effect.flatMap(() => refreshTokens), Effect.flatMap(() => fetchAttempt))), Effect.catchAll((error) => {
382
- if (error instanceof SessionError) return Effect.fail(error);
383
- return Effect.fail(new SessionError({
384
- reason: "project_fetch",
385
- message: "Failed to fetch project",
386
- cause: error
387
- }));
388
- }));
389
- const ensureProject = Effect.gen(function* () {
390
- const cached = yield* Ref.get(projectRef);
391
- if (cached !== null) return cached;
392
- const project = yield* fetchProject;
393
- yield* Ref.set(projectRef, project);
394
- return project;
395
- });
396
- return {
397
- setCredentials: (credentials) => Ref.set(credentialsRef, credentials),
398
- getAccessToken: Effect.gen(function* () {
399
- const currentCreds = yield* Ref.get(credentialsRef);
400
- if (!currentCreds?.access_token) return yield* new SessionError({
401
- reason: "no_tokens",
402
- message: "No access token available"
403
- });
404
- const isExpired = (currentCreds.expiry_date ?? 0) < Date.now() + 300 * 1e3;
405
- let accessToken = currentCreds.access_token;
406
- if (isExpired) {
407
- yield* Effect.log("Access token expired, refreshing...");
408
- const refreshed = yield* refreshTokens;
409
- if (!refreshed.access_token) return yield* new SessionError({
410
- reason: "token_refresh",
411
- message: "Refresh did not return access token"
412
- });
413
- accessToken = refreshed.access_token;
414
- }
415
- yield* ensureProject;
416
- return accessToken;
417
- }),
418
- ensureProject
419
- };
420
- }) }) {};
421
-
422
- //#endregion
423
- //#region src/lib/runtime.ts
424
- const makeRuntime = ({ providerConfig, openCodeCtx }) => {
425
- const LoggerLive = Logger.replaceScoped(Logger.defaultLogger, combinedLogger);
426
- const ProviderConfigLive = Layer.succeed(ProviderConfig, providerConfig);
427
- const OpenCodeLive = Layer.succeed(OpenCodeContext, openCodeCtx);
428
- const MainLive = pipe(Layer.empty, Layer.provide(LoggerLive), Layer.provide(BunFileSystem.layer), Layer.merge(OAuth.Default), Layer.merge(Session.Default), Layer.provideMerge(OpenCodeLive), Layer.provideMerge(FetchHttpClient.layer), Layer.provideMerge(ProviderConfigLive));
429
- return ManagedRuntime.make(MainLive);
430
- };
431
-
432
- //#endregion
433
- //#region src/transform/request.ts
434
- const STREAM_ACTION = "streamGenerateContent";
435
- const PATH_PATTERN = regex("/models/(?<model>[^:]+):(?<action>\\w+)");
436
- const transformRequest = Effect.fn("transformRequest")(function* (input, init, endpoint) {
437
- const config = yield* ProviderConfig;
438
- const session = yield* Session;
439
- const accessToken = yield* session.getAccessToken;
440
- const projectId = (yield* session.ensureProject).cloudaicompanionProject;
441
- const url = new URL(input instanceof Request ? input.url : input);
442
- const endpointUrl = new URL(endpoint);
443
- url.protocol = endpointUrl.protocol;
444
- url.host = endpointUrl.host;
445
- const match = PATH_PATTERN.exec(url.pathname);
446
- if (!match) return {
447
- input: url.toString(),
448
- init: init ?? {},
449
- streaming: false
450
- };
451
- const { model, action } = match.groups;
452
- const streaming = action === STREAM_ACTION;
453
- url.pathname = `/${CODE_ASSIST_VERSION}:${action}`;
454
- if (streaming) url.searchParams.set("alt", "sse");
455
- const headers = new Headers(init?.headers);
456
- headers.delete("x-api-key");
457
- headers.set("x-opencode-tools-debug", "1");
458
- headers.set("Authorization", `Bearer ${accessToken}`);
459
- for (const [key, value] of Object.entries(config.HEADERS)) headers.set(key, value);
460
- if (streaming) headers.set("Accept", "text/event-stream");
461
- const isJson = typeof init?.body === "string";
462
- const parsedBody = yield* pipe(Effect.try(() => isJson ? JSON.parse(init.body) : null), Effect.orElseSucceed(() => null));
463
- const wrappedBody = {
464
- project: projectId,
465
- model,
466
- request: parsedBody ?? {}
467
- };
468
- const { body: transformedBody, headers: finalHeaders, url: finalUrl } = config.transformRequest ? yield* config.transformRequest({
469
- body: wrappedBody,
470
- headers,
471
- url
472
- }) : {
473
- body: wrappedBody,
474
- headers,
475
- url
476
- };
477
- const finalBody = isJson && parsedBody ? JSON.stringify(transformedBody) : init?.body;
478
- return {
479
- input: finalUrl.toString(),
480
- init: {
481
- ...init,
482
- headers: finalHeaders,
483
- body: finalBody
484
- },
485
- streaming
486
- };
487
- });
488
-
489
- //#endregion
490
- //#region src/transform/response.ts
491
- const transformNonStreamingResponse = async (response) => {
492
- if (!response.headers.get("content-type")?.includes("application/json")) return response;
493
- try {
494
- const parsed = await response.clone().json();
495
- if (parsed.response !== void 0) {
496
- const { response: responseData, ...rest } = parsed;
497
- return new Response(JSON.stringify({
498
- ...rest,
499
- ...responseData
500
- }), {
501
- status: response.status,
502
- statusText: response.statusText,
503
- headers: response.headers
504
- });
505
- }
506
- } catch {}
507
- return response;
508
- };
509
-
510
- //#endregion
511
- //#region src/transform/stream.ts
512
- const parseAndMerge = (event) => {
513
- if (!event.data) return encoder.write(event);
514
- try {
515
- const parsed = JSON.parse(event.data);
516
- if (parsed.response) {
517
- const { response, ...rest } = parsed;
518
- return encoder.write({
519
- ...event,
520
- data: JSON.stringify({
521
- ...rest,
522
- ...response
523
- })
524
- });
525
- }
526
- return encoder.write(event);
527
- } catch {
528
- return encoder.write(event);
529
- }
530
- };
531
- const parseSSE = (body) => pipe(Stream.fromReadableStream(() => body, (error) => error), Stream.decodeText, Stream.pipeThroughChannel(makeChannel()), Stream.map((event) => Retry.is(event) ? encoder.write(event) : parseAndMerge(event)), Stream.encodeText);
532
- const transformStreamingResponse = (response) => {
533
- if (!response.body) return response;
534
- const transformed = parseSSE(response.body);
535
- const readable = Stream.toReadableStream(transformed);
536
- return new Response(readable, {
537
- status: response.status,
538
- statusText: response.statusText,
539
- headers: response.headers
540
- });
541
- };
542
-
543
- //#endregion
544
- //#region src/main.ts
545
- const fetchModelsDev = Effect.gen(function* () {
546
- return yield* (yield* (yield* HttpClient.HttpClient).get("https://models.dev/api.json")).json;
547
- });
548
- const customFetch = Effect.fn(function* (input, init) {
549
- const config = yield* ProviderConfig;
550
- let lastResponse = null;
551
- for (const endpoint of config.ENDPOINTS) {
552
- const result = yield* transformRequest(input, init, endpoint);
553
- const { request, ...loggedBody } = JSON.parse(result.init.body);
554
- const generationConfig = request.generationConfig;
555
- yield* Effect.log("Transformed request (Omitting request except generationConfig) :", result.streaming, result.input, {
556
- ...loggedBody,
557
- request: { generationConfig }
558
- });
559
- const response = yield* Effect.promise(() => fetch(result.input, result.init));
560
- if (response.status === 429 || response.status === 403) {
561
- yield* Effect.log(`${response.status} on ${endpoint}, trying next...`);
562
- lastResponse = response;
563
- continue;
564
- }
565
- if (!response.ok) {
566
- const cloned = response.clone();
567
- const clonedJson = yield* Effect.promise(() => cloned.json());
568
- yield* Effect.log("Received response:", cloned.status, clonedJson, cloned.headers);
569
- }
570
- return result.streaming ? transformStreamingResponse(response) : yield* Effect.promise(() => transformNonStreamingResponse(response));
571
- }
572
- yield* Effect.logWarning("All endpoints rate limited (429)");
573
- return lastResponse;
574
- }, Effect.tapDefect(Effect.logError));
575
- const geminiCli = async (context) => {
576
- const runtime = makeRuntime({
577
- openCodeCtx: context,
578
- providerConfig: geminiCliConfig()
579
- });
580
- const config = await runtime.runPromise(Effect.gen(function* () {
581
- const providerConfig = yield* ProviderConfig;
582
- const modelsDev = yield* fetchModelsDev;
583
- return providerConfig.getConfig(modelsDev);
584
- }));
585
- return {
586
- config: async (cfg) => {
587
- cfg.provider ??= {};
588
- cfg.provider[config.id] = config;
589
- },
590
- auth: {
591
- provider: config.id,
592
- loader: async (getAuth) => {
593
- const auth = await getAuth();
594
- if (auth.type !== "oauth") return {};
595
- const credentials = {
596
- access_token: auth.access,
597
- refresh_token: auth.refresh,
598
- expiry_date: auth.expires
599
- };
600
- await runtime.runPromise(Effect.gen(function* () {
601
- yield* (yield* Session).setCredentials(credentials);
602
- }));
603
- return {
604
- apiKey: "",
605
- fetch: (async (input, init) => {
606
- return await runtime.runPromise(customFetch(input, init));
607
- })
608
- };
609
- },
610
- methods: [{
611
- type: "oauth",
612
- label: "OAuth with Google",
613
- authorize: async () => {
614
- const result = await runtime.runPromise(Effect.gen(function* () {
615
- return yield* (yield* OAuth).authenticate;
616
- }));
617
- return {
618
- url: "",
619
- method: "auto",
620
- instructions: "You are now authenticated!",
621
- callback: async () => {
622
- const accessToken = result.access_token;
623
- const refreshToken = result.refresh_token;
624
- const expiryDate = result.expiry_date;
625
- if (!accessToken || !refreshToken || !expiryDate) return { type: "failed" };
626
- return {
627
- type: "success",
628
- provider: config.id,
629
- access: accessToken,
630
- refresh: refreshToken,
631
- expires: expiryDate
632
- };
633
- }
634
- };
635
- }
636
- }]
637
- }
638
- };
639
- };
640
- const antigravity = async (context) => {
641
- const runtime = makeRuntime({
642
- openCodeCtx: context,
643
- providerConfig: antigravityConfig()
644
- });
645
- const config = await runtime.runPromise(Effect.gen(function* () {
646
- const providerConfig = yield* ProviderConfig;
647
- const modelsDev = yield* fetchModelsDev;
648
- return providerConfig.getConfig(modelsDev);
649
- }));
650
- return {
651
- config: async (cfg) => {
652
- cfg.provider ??= {};
653
- cfg.provider[config.id] = config;
654
- },
655
- auth: {
656
- provider: config.id,
657
- loader: async (getAuth) => {
658
- const auth = await getAuth();
659
- if (auth.type !== "oauth") return {};
660
- const credentials = {
661
- access_token: auth.access,
662
- refresh_token: auth.refresh,
663
- expiry_date: auth.expires
664
- };
665
- await runtime.runPromise(Effect.gen(function* () {
666
- yield* (yield* Session).setCredentials(credentials);
667
- }));
668
- return {
669
- apiKey: "",
670
- fetch: (async (input, init) => {
671
- return await runtime.runPromise(customFetch(input, init));
672
- })
673
- };
674
- },
675
- methods: [{
676
- type: "oauth",
677
- label: "OAuth with Google",
678
- authorize: async () => {
679
- const result = await runtime.runPromise(Effect.gen(function* () {
680
- return yield* (yield* OAuth).authenticate;
681
- }));
682
- return {
683
- url: "",
684
- method: "auto",
685
- instructions: "You are now authenticated!",
686
- callback: async () => {
687
- const accessToken = result.access_token;
688
- const refreshToken = result.refresh_token;
689
- const expiryDate = result.expiry_date;
690
- if (!accessToken || !refreshToken || !expiryDate) return { type: "failed" };
691
- return {
692
- type: "success",
693
- provider: config.id,
694
- access: accessToken,
695
- refresh: refreshToken,
696
- expires: expiryDate
697
- };
698
- }
699
- };
700
- }
701
- }]
702
- },
703
- "chat.params": async (input, output) => {
704
- await runtime.runPromise(Effect.log("chat.params event before:", input.model, output.options));
705
- if (input.model.providerID === config.id) output.options = {
706
- ...output.options,
707
- labels: { sessionId: input.sessionID }
708
- };
709
- await runtime.runPromise(Effect.log("chat.params event after:", input.model, output.options));
710
- }
711
- };
712
- };
713
-
714
- //#endregion
715
- export { antigravity, geminiCli };
716
- //# sourceMappingURL=main.mjs.map