copilot-api-plus 1.0.8 → 1.0.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/dist/main.js CHANGED
@@ -1,10 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
  import { PATHS, ensurePaths } from "./paths-Ch0ixSo2.js";
3
- import { state } from "./state-DAw5jMjc.js";
4
- import { GITHUB_API_BASE_URL, copilotBaseUrl, copilotHeaders, githubHeaders } from "./get-user-DgPgvnrS.js";
3
+ import { state } from "./state-CcLGr8VN.js";
4
+ import { GITHUB_API_BASE_URL, copilotBaseUrl, copilotHeaders, githubHeaders } from "./get-user-BFf5xJXg.js";
5
5
  import { HTTPError, forwardError } from "./error-CvU5otz-.js";
6
- import { cacheModels, cacheVSCodeVersion, clearGithubToken, isNullish, setupCopilotToken, setupGitHubToken, sleep } from "./token-DYeOMeid.js";
7
- import { clearZenAuth, getZenAuthPath } from "./auth-C5zV8JbW.js";
6
+ import { cacheModels, cacheVSCodeVersion, clearGithubToken, isNullish, setupCopilotToken, setupGitHubToken, sleep } from "./token-B8crDDoA.js";
7
+ import { clearAntigravityAuth, disableCurrentAccount, getAntigravityAuthPath, getValidAccessToken, rotateAccount } from "./auth-Cm_0h9bp.js";
8
+ import { clearZenAuth, getZenAuthPath } from "./auth-Cua-c0dq.js";
9
+ import { getAntigravityModels, isThinkingModel } from "./get-models-Bbb8B5jI.js";
8
10
  import { defineCommand, runMain } from "citty";
9
11
  import consola from "consola";
10
12
  import fs from "node:fs/promises";
@@ -185,9 +187,11 @@ async function runLogout(options) {
185
187
  if (options.all) {
186
188
  await clearGithubToken();
187
189
  await clearZenAuth();
190
+ await clearAntigravityAuth();
188
191
  consola.success("Logged out from all services");
189
192
  consola.info(`GitHub token: ${PATHS.GITHUB_TOKEN_PATH}`);
190
193
  consola.info(`Zen API key: ${getZenAuthPath()}`);
194
+ consola.info(`Antigravity accounts: ${getAntigravityAuthPath()}`);
191
195
  return;
192
196
  }
193
197
  if (options.zen) {
@@ -196,26 +200,42 @@ async function runLogout(options) {
196
200
  consola.info(`Zen API key location: ${getZenAuthPath()}`);
197
201
  return;
198
202
  }
199
- const choice = await consola.prompt("Which credentials do you want to clear?", {
203
+ if (options.antigravity) {
204
+ await clearAntigravityAuth();
205
+ consola.success("Logged out from Google Antigravity");
206
+ consola.info(`Antigravity accounts location: ${getAntigravityAuthPath()}`);
207
+ return;
208
+ }
209
+ switch (await consola.prompt("Which credentials do you want to clear?", {
200
210
  type: "select",
201
211
  options: [
202
212
  "GitHub Copilot token",
203
213
  "OpenCode Zen API key",
204
- "Both (all credentials)"
214
+ "Google Antigravity accounts",
215
+ "All credentials"
205
216
  ]
206
- });
207
- if (choice === "GitHub Copilot token") {
208
- await clearGithubToken();
209
- consola.success("Logged out from GitHub Copilot");
210
- consola.info(`Token file location: ${PATHS.GITHUB_TOKEN_PATH}`);
211
- } else if (choice === "OpenCode Zen API key") {
212
- await clearZenAuth();
213
- consola.success("Logged out from OpenCode Zen");
214
- consola.info(`Zen API key location: ${getZenAuthPath()}`);
215
- } else if (choice === "Both (all credentials)") {
216
- await clearGithubToken();
217
- await clearZenAuth();
218
- consola.success("Logged out from all services");
217
+ })) {
218
+ case "GitHub Copilot token":
219
+ await clearGithubToken();
220
+ consola.success("Logged out from GitHub Copilot");
221
+ consola.info(`Token file location: ${PATHS.GITHUB_TOKEN_PATH}`);
222
+ break;
223
+ case "OpenCode Zen API key":
224
+ await clearZenAuth();
225
+ consola.success("Logged out from OpenCode Zen");
226
+ consola.info(`Zen API key location: ${getZenAuthPath()}`);
227
+ break;
228
+ case "Google Antigravity accounts":
229
+ await clearAntigravityAuth();
230
+ consola.success("Logged out from Google Antigravity");
231
+ consola.info(`Antigravity accounts location: ${getAntigravityAuthPath()}`);
232
+ break;
233
+ case "All credentials":
234
+ await clearGithubToken();
235
+ await clearZenAuth();
236
+ await clearAntigravityAuth();
237
+ consola.success("Logged out from all services");
238
+ break;
219
239
  }
220
240
  }
221
241
  const logout = defineCommand({
@@ -230,16 +250,22 @@ const logout = defineCommand({
230
250
  default: false,
231
251
  description: "Clear only OpenCode Zen API key"
232
252
  },
253
+ antigravity: {
254
+ type: "boolean",
255
+ default: false,
256
+ description: "Clear only Google Antigravity accounts"
257
+ },
233
258
  all: {
234
259
  alias: "a",
235
260
  type: "boolean",
236
261
  default: false,
237
- description: "Clear all credentials (GitHub and Zen)"
262
+ description: "Clear all credentials (GitHub, Zen, and Antigravity)"
238
263
  }
239
264
  },
240
265
  run({ args }) {
241
266
  return runLogout({
242
267
  zen: args.zen,
268
+ antigravity: args.antigravity,
243
269
  all: args.all
244
270
  });
245
271
  }
@@ -376,6 +402,700 @@ const apiKeyAuthMiddleware = async (c, next) => {
376
402
  await next();
377
403
  };
378
404
 
405
+ //#endregion
406
+ //#region src/services/antigravity/create-chat-completions.ts
407
+ const ANTIGRAVITY_API_HOST$1 = "daily-cloudcode-pa.sandbox.googleapis.com";
408
+ const ANTIGRAVITY_STREAM_URL$1 = `https://${ANTIGRAVITY_API_HOST$1}/v1internal:streamGenerateContent?alt=sse`;
409
+ const ANTIGRAVITY_NO_STREAM_URL$1 = `https://${ANTIGRAVITY_API_HOST$1}/v1internal:generateContent`;
410
+ const ANTIGRAVITY_USER_AGENT$1 = "antigravity/1.11.3 windows/amd64";
411
+ /**
412
+ * Convert OpenAI format messages to Antigravity format
413
+ */
414
+ function convertMessages$1(messages) {
415
+ const contents = [];
416
+ let systemInstruction = void 0;
417
+ for (const message of messages) {
418
+ if (message.role === "system") {
419
+ systemInstruction = {
420
+ role: "user",
421
+ parts: [{ text: typeof message.content === "string" ? message.content : message.content.map((c) => c.text || "").join("") }]
422
+ };
423
+ continue;
424
+ }
425
+ const role = message.role === "assistant" ? "model" : "user";
426
+ if (typeof message.content === "string") contents.push({
427
+ role,
428
+ parts: [{ text: message.content }]
429
+ });
430
+ else {
431
+ const parts = [];
432
+ for (const part of message.content) if (part.type === "text") parts.push({ text: part.text });
433
+ else if (part.type === "image_url" && part.image_url?.url) {
434
+ const url = part.image_url.url;
435
+ if (url.startsWith("data:")) {
436
+ const match = url.match(/^data:([^;]+);base64,(.+)$/);
437
+ if (match) parts.push({ inlineData: {
438
+ mimeType: match[1],
439
+ data: match[2]
440
+ } });
441
+ }
442
+ }
443
+ contents.push({
444
+ role,
445
+ parts
446
+ });
447
+ }
448
+ }
449
+ return {
450
+ contents,
451
+ systemInstruction
452
+ };
453
+ }
454
+ /**
455
+ * Convert tools to Antigravity format
456
+ */
457
+ function convertTools$1(tools) {
458
+ if (!tools || tools.length === 0) return;
459
+ return tools.map((tool) => {
460
+ const t = tool;
461
+ if (t.type === "function" && t.function) return { functionDeclarations: [{
462
+ name: t.function.name,
463
+ description: t.function.description || "",
464
+ parameters: t.function.parameters || {}
465
+ }] };
466
+ return tool;
467
+ });
468
+ }
469
+ /**
470
+ * Create chat completion with Antigravity
471
+ */
472
+ async function createAntigravityChatCompletion(request) {
473
+ const accessToken = await getValidAccessToken();
474
+ if (!accessToken) return new Response(JSON.stringify({ error: {
475
+ message: "No valid Antigravity access token available. Please run login first.",
476
+ type: "auth_error"
477
+ } }), {
478
+ status: 401,
479
+ headers: { "Content-Type": "application/json" }
480
+ });
481
+ const { contents, systemInstruction } = convertMessages$1(request.messages);
482
+ const tools = convertTools$1(request.tools);
483
+ const antigravityRequest = {
484
+ model: request.model,
485
+ contents,
486
+ generationConfig: {
487
+ temperature: request.temperature ?? 1,
488
+ topP: request.top_p ?? .85,
489
+ topK: request.top_k ?? 50,
490
+ maxOutputTokens: request.max_tokens ?? 8096
491
+ }
492
+ };
493
+ if (systemInstruction) antigravityRequest.systemInstruction = systemInstruction;
494
+ if (tools) antigravityRequest.tools = tools;
495
+ if (isThinkingModel(request.model)) antigravityRequest.generationConfig = {
496
+ ...antigravityRequest.generationConfig,
497
+ thinkingConfig: { includeThoughts: true }
498
+ };
499
+ const endpoint = request.stream ? ANTIGRAVITY_STREAM_URL$1 : ANTIGRAVITY_NO_STREAM_URL$1;
500
+ consola.debug(`Antigravity request to ${endpoint} with model ${request.model}`);
501
+ try {
502
+ const response = await fetch(endpoint, {
503
+ method: "POST",
504
+ headers: {
505
+ Host: ANTIGRAVITY_API_HOST$1,
506
+ "User-Agent": ANTIGRAVITY_USER_AGENT$1,
507
+ Authorization: `Bearer ${accessToken}`,
508
+ "Content-Type": "application/json",
509
+ "Accept-Encoding": "gzip"
510
+ },
511
+ body: JSON.stringify(antigravityRequest)
512
+ });
513
+ if (!response.ok) {
514
+ const errorText = await response.text();
515
+ consola.error(`Antigravity error: ${response.status} ${errorText}`);
516
+ if (response.status === 403) await disableCurrentAccount();
517
+ if (response.status === 429 || response.status === 503) await rotateAccount();
518
+ return new Response(JSON.stringify({ error: {
519
+ message: `Antigravity API error: ${response.status}`,
520
+ type: "api_error",
521
+ details: errorText
522
+ } }), {
523
+ status: response.status,
524
+ headers: { "Content-Type": "application/json" }
525
+ });
526
+ }
527
+ if (request.stream) return transformStreamResponse(response, request.model);
528
+ else return transformNonStreamResponse(response, request.model);
529
+ } catch (error) {
530
+ consola.error("Antigravity request error:", error);
531
+ return new Response(JSON.stringify({ error: {
532
+ message: `Request failed: ${error}`,
533
+ type: "request_error"
534
+ } }), {
535
+ status: 500,
536
+ headers: { "Content-Type": "application/json" }
537
+ });
538
+ }
539
+ }
540
+ /**
541
+ * Transform Antigravity stream response to OpenAI format
542
+ */
543
+ function transformStreamResponse(response, model) {
544
+ const reader = response.body?.getReader();
545
+ if (!reader) return new Response("No response body", { status: 500 });
546
+ const encoder = new TextEncoder();
547
+ const decoder = new TextDecoder();
548
+ const stream = new ReadableStream({ async start(controller) {
549
+ let buffer = "";
550
+ const requestId = `chatcmpl-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
551
+ try {
552
+ while (true) {
553
+ const { done, value } = await reader.read();
554
+ if (done) {
555
+ controller.enqueue(encoder.encode("data: [DONE]\n\n"));
556
+ controller.close();
557
+ break;
558
+ }
559
+ buffer += decoder.decode(value, { stream: true });
560
+ const lines = buffer.split("\n");
561
+ buffer = lines.pop() || "";
562
+ for (const line of lines) if (line.startsWith("data: ")) {
563
+ const data = line.slice(6).trim();
564
+ if (data === "[DONE]" || data === "") continue;
565
+ try {
566
+ const parsed = JSON.parse(data);
567
+ const candidate = (parsed.response?.candidates || parsed.candidates)?.[0];
568
+ const parts = candidate?.content?.parts || [];
569
+ for (const part of parts) {
570
+ if (part.thought && part.text) {
571
+ const chunk = {
572
+ id: requestId,
573
+ object: "chat.completion.chunk",
574
+ created: Math.floor(Date.now() / 1e3),
575
+ model,
576
+ choices: [{
577
+ index: 0,
578
+ delta: { reasoning_content: part.text },
579
+ finish_reason: null
580
+ }]
581
+ };
582
+ controller.enqueue(encoder.encode(`data: ${JSON.stringify(chunk)}\n\n`));
583
+ continue;
584
+ }
585
+ if (part.text) {
586
+ const chunk = {
587
+ id: requestId,
588
+ object: "chat.completion.chunk",
589
+ created: Math.floor(Date.now() / 1e3),
590
+ model,
591
+ choices: [{
592
+ index: 0,
593
+ delta: { content: part.text },
594
+ finish_reason: candidate?.finishReason === "STOP" ? "stop" : null
595
+ }]
596
+ };
597
+ controller.enqueue(encoder.encode(`data: ${JSON.stringify(chunk)}\n\n`));
598
+ }
599
+ if (part.functionCall) {
600
+ const chunk = {
601
+ id: requestId,
602
+ object: "chat.completion.chunk",
603
+ created: Math.floor(Date.now() / 1e3),
604
+ model,
605
+ choices: [{
606
+ index: 0,
607
+ delta: { tool_calls: [{
608
+ index: 0,
609
+ id: `call_${Date.now()}`,
610
+ type: "function",
611
+ function: {
612
+ name: part.functionCall.name,
613
+ arguments: JSON.stringify(part.functionCall.args)
614
+ }
615
+ }] },
616
+ finish_reason: null
617
+ }]
618
+ };
619
+ controller.enqueue(encoder.encode(`data: ${JSON.stringify(chunk)}\n\n`));
620
+ }
621
+ }
622
+ } catch {}
623
+ }
624
+ }
625
+ } catch (error) {
626
+ consola.error("Stream transform error:", error);
627
+ controller.error(error);
628
+ }
629
+ } });
630
+ return new Response(stream, { headers: {
631
+ "Content-Type": "text/event-stream",
632
+ "Cache-Control": "no-cache",
633
+ Connection: "keep-alive"
634
+ } });
635
+ }
636
+ /**
637
+ * Transform Antigravity non-stream response to OpenAI format
638
+ */
639
+ async function transformNonStreamResponse(response, model) {
640
+ const data = await response.json();
641
+ const candidate = data.candidates?.[0];
642
+ const parts = candidate?.content?.parts || [];
643
+ let content = "";
644
+ let reasoningContent = "";
645
+ const toolCalls = [];
646
+ for (const part of parts) {
647
+ if (part.thought && part.text) reasoningContent += part.text;
648
+ else if (part.text) content += part.text;
649
+ if (part.functionCall) toolCalls.push({
650
+ id: `call_${Date.now()}`,
651
+ type: "function",
652
+ function: {
653
+ name: part.functionCall.name,
654
+ arguments: JSON.stringify(part.functionCall.args)
655
+ }
656
+ });
657
+ }
658
+ const message = {
659
+ role: "assistant",
660
+ content: content || null
661
+ };
662
+ if (reasoningContent) message.reasoning_content = reasoningContent;
663
+ if (toolCalls.length > 0) message.tool_calls = toolCalls;
664
+ const openaiResponse = {
665
+ id: `chatcmpl-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`,
666
+ object: "chat.completion",
667
+ created: Math.floor(Date.now() / 1e3),
668
+ model,
669
+ choices: [{
670
+ index: 0,
671
+ message,
672
+ finish_reason: candidate?.finishReason === "STOP" ? "stop" : "stop"
673
+ }],
674
+ usage: {
675
+ prompt_tokens: data.usageMetadata?.promptTokenCount || 0,
676
+ completion_tokens: data.usageMetadata?.candidatesTokenCount || 0,
677
+ total_tokens: data.usageMetadata?.totalTokenCount || 0
678
+ }
679
+ };
680
+ return new Response(JSON.stringify(openaiResponse), { headers: { "Content-Type": "application/json" } });
681
+ }
682
+
683
+ //#endregion
684
+ //#region src/routes/antigravity/chat-completions/route.ts
685
+ const app$1 = new Hono();
686
+ app$1.post("/", async (c) => {
687
+ const body = await c.req.json();
688
+ const response = await createAntigravityChatCompletion(body);
689
+ const headers = new Headers(response.headers);
690
+ if (body.stream) return new Response(response.body, {
691
+ status: response.status,
692
+ headers
693
+ });
694
+ return new Response(await response.text(), {
695
+ status: response.status,
696
+ headers
697
+ });
698
+ });
699
+ const antigravityChatCompletionsRoute = app$1;
700
+
701
+ //#endregion
702
+ //#region src/services/antigravity/create-messages.ts
703
+ const ANTIGRAVITY_API_HOST = "daily-cloudcode-pa.sandbox.googleapis.com";
704
+ const ANTIGRAVITY_STREAM_URL = `https://${ANTIGRAVITY_API_HOST}/v1internal:streamGenerateContent?alt=sse`;
705
+ const ANTIGRAVITY_NO_STREAM_URL = `https://${ANTIGRAVITY_API_HOST}/v1internal:generateContent`;
706
+ const ANTIGRAVITY_USER_AGENT = "antigravity/1.11.3 windows/amd64";
707
+ /**
708
+ * Convert Anthropic messages to Antigravity format
709
+ */
710
+ function convertMessages(messages, system) {
711
+ const contents = [];
712
+ let systemInstruction = void 0;
713
+ if (system) systemInstruction = {
714
+ role: "user",
715
+ parts: [{ text: system }]
716
+ };
717
+ for (const message of messages) {
718
+ const role = message.role === "assistant" ? "model" : "user";
719
+ if (typeof message.content === "string") contents.push({
720
+ role,
721
+ parts: [{ text: message.content }]
722
+ });
723
+ else {
724
+ const parts = [];
725
+ for (const block of message.content) if (block.type === "text" && block.text) parts.push({ text: block.text });
726
+ else if (block.type === "image" && block.source) parts.push({ inlineData: {
727
+ mimeType: block.source.media_type,
728
+ data: block.source.data
729
+ } });
730
+ if (parts.length > 0) contents.push({
731
+ role,
732
+ parts
733
+ });
734
+ }
735
+ }
736
+ return {
737
+ contents,
738
+ systemInstruction
739
+ };
740
+ }
741
+ /**
742
+ * Convert tools to Antigravity format
743
+ */
744
+ function convertTools(tools) {
745
+ if (!tools || tools.length === 0) return;
746
+ return tools.map((tool) => {
747
+ const t = tool;
748
+ return { functionDeclarations: [{
749
+ name: t.name,
750
+ description: t.description || "",
751
+ parameters: t.input_schema || {}
752
+ }] };
753
+ });
754
+ }
755
+ /**
756
+ * Create Anthropic-compatible message response using Antigravity
757
+ */
758
+ async function createAntigravityMessages(request) {
759
+ const accessToken = await getValidAccessToken();
760
+ if (!accessToken) return new Response(JSON.stringify({
761
+ type: "error",
762
+ error: {
763
+ type: "authentication_error",
764
+ message: "No valid Antigravity access token available. Please run login first."
765
+ }
766
+ }), {
767
+ status: 401,
768
+ headers: { "Content-Type": "application/json" }
769
+ });
770
+ const { contents, systemInstruction } = convertMessages(request.messages, request.system);
771
+ const tools = convertTools(request.tools);
772
+ const antigravityRequest = {
773
+ model: request.model,
774
+ contents,
775
+ generationConfig: {
776
+ temperature: request.temperature ?? 1,
777
+ topP: request.top_p ?? .85,
778
+ topK: request.top_k ?? 50,
779
+ maxOutputTokens: request.max_tokens ?? 8096
780
+ }
781
+ };
782
+ if (systemInstruction) antigravityRequest.systemInstruction = systemInstruction;
783
+ if (tools) antigravityRequest.tools = tools;
784
+ if (isThinkingModel(request.model)) antigravityRequest.generationConfig = {
785
+ ...antigravityRequest.generationConfig,
786
+ thinkingConfig: { includeThoughts: true }
787
+ };
788
+ const endpoint = request.stream ? ANTIGRAVITY_STREAM_URL : ANTIGRAVITY_NO_STREAM_URL;
789
+ consola.debug(`Antigravity messages request to ${endpoint} with model ${request.model}`);
790
+ try {
791
+ const response = await fetch(endpoint, {
792
+ method: "POST",
793
+ headers: {
794
+ Host: ANTIGRAVITY_API_HOST,
795
+ "User-Agent": ANTIGRAVITY_USER_AGENT,
796
+ Authorization: `Bearer ${accessToken}`,
797
+ "Content-Type": "application/json",
798
+ "Accept-Encoding": "gzip"
799
+ },
800
+ body: JSON.stringify(antigravityRequest)
801
+ });
802
+ if (!response.ok) {
803
+ const errorText = await response.text();
804
+ consola.error(`Antigravity error: ${response.status} ${errorText}`);
805
+ if (response.status === 403) await disableCurrentAccount();
806
+ if (response.status === 429 || response.status === 503) await rotateAccount();
807
+ return new Response(JSON.stringify({
808
+ type: "error",
809
+ error: {
810
+ type: "api_error",
811
+ message: `Antigravity API error: ${response.status}`
812
+ }
813
+ }), {
814
+ status: response.status,
815
+ headers: { "Content-Type": "application/json" }
816
+ });
817
+ }
818
+ if (request.stream) return transformStreamToAnthropic(response, request.model);
819
+ else return transformNonStreamToAnthropic(response, request.model);
820
+ } catch (error) {
821
+ consola.error("Antigravity messages request error:", error);
822
+ return new Response(JSON.stringify({
823
+ type: "error",
824
+ error: {
825
+ type: "api_error",
826
+ message: `Request failed: ${error}`
827
+ }
828
+ }), {
829
+ status: 500,
830
+ headers: { "Content-Type": "application/json" }
831
+ });
832
+ }
833
+ }
834
+ /**
835
+ * Transform Antigravity stream response to Anthropic format
836
+ */
837
+ function transformStreamToAnthropic(response, model) {
838
+ const reader = response.body?.getReader();
839
+ if (!reader) return new Response("No response body", { status: 500 });
840
+ const encoder = new TextEncoder();
841
+ const decoder = new TextDecoder();
842
+ const stream = new ReadableStream({ async start(controller) {
843
+ let buffer = "";
844
+ const messageId = `msg_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`;
845
+ let inputTokens = 0;
846
+ let outputTokens = 0;
847
+ let contentBlockIndex = 0;
848
+ let thinkingBlockStarted = false;
849
+ let textBlockStarted = false;
850
+ const messageStart = {
851
+ type: "message_start",
852
+ message: {
853
+ id: messageId,
854
+ type: "message",
855
+ role: "assistant",
856
+ content: [],
857
+ model,
858
+ stop_reason: null,
859
+ stop_sequence: null,
860
+ usage: {
861
+ input_tokens: 0,
862
+ output_tokens: 0
863
+ }
864
+ }
865
+ };
866
+ controller.enqueue(encoder.encode(`event: message_start\ndata: ${JSON.stringify(messageStart)}\n\n`));
867
+ try {
868
+ while (true) {
869
+ const { done, value } = await reader.read();
870
+ if (done) {
871
+ controller.enqueue(encoder.encode(`event: message_stop\ndata: ${JSON.stringify({ type: "message_stop" })}\n\n`));
872
+ controller.close();
873
+ break;
874
+ }
875
+ buffer += decoder.decode(value, { stream: true });
876
+ const lines = buffer.split("\n");
877
+ buffer = lines.pop() || "";
878
+ for (const line of lines) if (line.startsWith("data: ")) {
879
+ const data = line.slice(6).trim();
880
+ if (data === "[DONE]" || data === "") continue;
881
+ try {
882
+ const parsed = JSON.parse(data);
883
+ const candidate = (parsed.response?.candidates || parsed.candidates)?.[0];
884
+ const parts = candidate?.content?.parts || [];
885
+ const usage = parsed.response?.usageMetadata || parsed.usageMetadata;
886
+ if (usage) {
887
+ inputTokens = usage.promptTokenCount || inputTokens;
888
+ outputTokens = usage.candidatesTokenCount || outputTokens;
889
+ }
890
+ for (const part of parts) {
891
+ if (part.thought && part.text) {
892
+ if (!thinkingBlockStarted) {
893
+ const blockStart = {
894
+ type: "content_block_start",
895
+ index: contentBlockIndex,
896
+ content_block: {
897
+ type: "thinking",
898
+ thinking: ""
899
+ }
900
+ };
901
+ controller.enqueue(encoder.encode(`event: content_block_start\ndata: ${JSON.stringify(blockStart)}\n\n`));
902
+ thinkingBlockStarted = true;
903
+ }
904
+ const thinkingDelta = {
905
+ type: "content_block_delta",
906
+ index: contentBlockIndex,
907
+ delta: {
908
+ type: "thinking_delta",
909
+ thinking: part.text
910
+ }
911
+ };
912
+ controller.enqueue(encoder.encode(`event: content_block_delta\ndata: ${JSON.stringify(thinkingDelta)}\n\n`));
913
+ continue;
914
+ }
915
+ if (part.text && !part.thought) {
916
+ if (thinkingBlockStarted && !textBlockStarted) {
917
+ const blockStop = {
918
+ type: "content_block_stop",
919
+ index: contentBlockIndex
920
+ };
921
+ controller.enqueue(encoder.encode(`event: content_block_stop\ndata: ${JSON.stringify(blockStop)}\n\n`));
922
+ contentBlockIndex++;
923
+ }
924
+ if (!textBlockStarted) {
925
+ const blockStart = {
926
+ type: "content_block_start",
927
+ index: contentBlockIndex,
928
+ content_block: {
929
+ type: "text",
930
+ text: ""
931
+ }
932
+ };
933
+ controller.enqueue(encoder.encode(`event: content_block_start\ndata: ${JSON.stringify(blockStart)}\n\n`));
934
+ textBlockStarted = true;
935
+ }
936
+ const textDelta = {
937
+ type: "content_block_delta",
938
+ index: contentBlockIndex,
939
+ delta: {
940
+ type: "text_delta",
941
+ text: part.text
942
+ }
943
+ };
944
+ controller.enqueue(encoder.encode(`event: content_block_delta\ndata: ${JSON.stringify(textDelta)}\n\n`));
945
+ }
946
+ if (part.functionCall) {
947
+ if (textBlockStarted || thinkingBlockStarted) {
948
+ const blockStop = {
949
+ type: "content_block_stop",
950
+ index: contentBlockIndex
951
+ };
952
+ controller.enqueue(encoder.encode(`event: content_block_stop\ndata: ${JSON.stringify(blockStop)}\n\n`));
953
+ contentBlockIndex++;
954
+ textBlockStarted = false;
955
+ thinkingBlockStarted = false;
956
+ }
957
+ const toolBlockStart = {
958
+ type: "content_block_start",
959
+ index: contentBlockIndex,
960
+ content_block: {
961
+ type: "tool_use",
962
+ id: `toolu_${Date.now()}`,
963
+ name: part.functionCall.name,
964
+ input: {}
965
+ }
966
+ };
967
+ controller.enqueue(encoder.encode(`event: content_block_start\ndata: ${JSON.stringify(toolBlockStart)}\n\n`));
968
+ const toolDelta = {
969
+ type: "content_block_delta",
970
+ index: contentBlockIndex,
971
+ delta: {
972
+ type: "input_json_delta",
973
+ partial_json: JSON.stringify(part.functionCall.args)
974
+ }
975
+ };
976
+ controller.enqueue(encoder.encode(`event: content_block_delta\ndata: ${JSON.stringify(toolDelta)}\n\n`));
977
+ const toolBlockStop = {
978
+ type: "content_block_stop",
979
+ index: contentBlockIndex
980
+ };
981
+ controller.enqueue(encoder.encode(`event: content_block_stop\ndata: ${JSON.stringify(toolBlockStop)}\n\n`));
982
+ contentBlockIndex++;
983
+ }
984
+ }
985
+ if (candidate?.finishReason === "STOP") {
986
+ if (textBlockStarted || thinkingBlockStarted) {
987
+ const blockStop = {
988
+ type: "content_block_stop",
989
+ index: contentBlockIndex
990
+ };
991
+ controller.enqueue(encoder.encode(`event: content_block_stop\ndata: ${JSON.stringify(blockStop)}\n\n`));
992
+ }
993
+ const messageDelta = {
994
+ type: "message_delta",
995
+ delta: {
996
+ stop_reason: "end_turn",
997
+ stop_sequence: null
998
+ },
999
+ usage: { output_tokens: outputTokens }
1000
+ };
1001
+ controller.enqueue(encoder.encode(`event: message_delta\ndata: ${JSON.stringify(messageDelta)}\n\n`));
1002
+ }
1003
+ } catch {}
1004
+ }
1005
+ }
1006
+ } catch (error) {
1007
+ consola.error("Stream transform error:", error);
1008
+ controller.error(error);
1009
+ }
1010
+ } });
1011
+ return new Response(stream, { headers: {
1012
+ "Content-Type": "text/event-stream",
1013
+ "Cache-Control": "no-cache",
1014
+ Connection: "keep-alive"
1015
+ } });
1016
+ }
1017
+ /**
1018
+ * Transform Antigravity non-stream response to Anthropic format
1019
+ */
1020
+ async function transformNonStreamToAnthropic(response, model) {
1021
+ const data = await response.json();
1022
+ const candidate = data.candidates?.[0];
1023
+ const parts = candidate?.content?.parts || [];
1024
+ const content = [];
1025
+ for (const part of parts) {
1026
+ if (part.thought && part.text) content.push({
1027
+ type: "thinking",
1028
+ thinking: part.text
1029
+ });
1030
+ else if (part.text) content.push({
1031
+ type: "text",
1032
+ text: part.text
1033
+ });
1034
+ if (part.functionCall) content.push({
1035
+ type: "tool_use",
1036
+ id: `toolu_${Date.now()}`,
1037
+ name: part.functionCall.name,
1038
+ input: part.functionCall.args
1039
+ });
1040
+ }
1041
+ const anthropicResponse = {
1042
+ id: `msg_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`,
1043
+ type: "message",
1044
+ role: "assistant",
1045
+ content,
1046
+ model,
1047
+ stop_reason: candidate?.finishReason === "STOP" ? "end_turn" : "end_turn",
1048
+ stop_sequence: null,
1049
+ usage: {
1050
+ input_tokens: data.usageMetadata?.promptTokenCount || 0,
1051
+ output_tokens: data.usageMetadata?.candidatesTokenCount || 0
1052
+ }
1053
+ };
1054
+ return new Response(JSON.stringify(anthropicResponse), { headers: { "Content-Type": "application/json" } });
1055
+ }
1056
+
1057
+ //#endregion
1058
+ //#region src/routes/antigravity/messages/route.ts
1059
+ const antigravityMessagesRoute = new Hono();
1060
+ antigravityMessagesRoute.post("/", async (c) => {
1061
+ if (!state.antigravityMode) return c.json({ error: "Antigravity mode is not enabled. Start with --antigravity flag." }, 400);
1062
+ try {
1063
+ const body = await c.req.json();
1064
+ consola.debug("Antigravity message request:", body.model);
1065
+ const response = await createAntigravityMessages(body);
1066
+ if (body.stream) {
1067
+ const headers = new Headers();
1068
+ headers.set("Content-Type", "text/event-stream");
1069
+ headers.set("Cache-Control", "no-cache");
1070
+ headers.set("Connection", "keep-alive");
1071
+ return new Response(response.body, {
1072
+ status: response.status,
1073
+ headers
1074
+ });
1075
+ }
1076
+ const data = await response.json();
1077
+ return c.json(data);
1078
+ } catch (error) {
1079
+ consola.error("Antigravity message error:", error);
1080
+ return c.json({
1081
+ type: "error",
1082
+ error: {
1083
+ type: "antigravity_error",
1084
+ message: error instanceof Error ? error.message : "Unknown error"
1085
+ }
1086
+ }, 500);
1087
+ }
1088
+ });
1089
+
1090
+ //#endregion
1091
+ //#region src/routes/antigravity/models/route.ts
1092
+ const app = new Hono();
1093
+ app.get("/", async (c) => {
1094
+ const models = await getAntigravityModels();
1095
+ return c.json(models);
1096
+ });
1097
+ const antigravityModelsRoute = app;
1098
+
379
1099
  //#endregion
380
1100
  //#region src/lib/approval.ts
381
1101
  const awaitApproval = async () => {
@@ -1312,7 +2032,7 @@ zenModelRoutes.get("/", async (c) => {
1312
2032
  if (!state.zenMode || !state.zenApiKey) return c.json({ error: "Zen mode is not enabled. Start with --zen flag." }, 400);
1313
2033
  try {
1314
2034
  if (state.zenModels) return c.json(state.zenModels);
1315
- const { getZenModels } = await import("./get-models-Hlxa1hWY.js");
2035
+ const { getZenModels } = await import("./get-models-DJfPj_Rg.js");
1316
2036
  const models = await getZenModels();
1317
2037
  state.zenModels = models;
1318
2038
  return c.json(models);
@@ -1332,33 +2052,41 @@ server.use(logger());
1332
2052
  server.use(cors());
1333
2053
  server.use(apiKeyAuthMiddleware);
1334
2054
  server.get("/", (c) => c.text("Server running"));
1335
- server.route("/chat/completions", new Hono().all("/*", async (c, next) => {
2055
+ server.route("/chat/completions", new Hono().all("/*", async (c, _next) => {
1336
2056
  if (state.zenMode) return zenCompletionRoutes.fetch(c.req.raw, c.env);
2057
+ if (state.antigravityMode) return antigravityChatCompletionsRoute.fetch(c.req.raw, c.env);
1337
2058
  return completionRoutes.fetch(c.req.raw, c.env);
1338
2059
  }));
1339
- server.route("/models", new Hono().all("/*", async (c, next) => {
2060
+ server.route("/models", new Hono().all("/*", async (c, _next) => {
1340
2061
  if (state.zenMode) return zenModelRoutes.fetch(c.req.raw, c.env);
2062
+ if (state.antigravityMode) return antigravityModelsRoute.fetch(c.req.raw, c.env);
1341
2063
  return modelRoutes.fetch(c.req.raw, c.env);
1342
2064
  }));
1343
2065
  server.route("/embeddings", embeddingRoutes);
1344
2066
  server.route("/usage", usageRoute);
1345
2067
  server.route("/token", tokenRoute);
1346
- server.route("/v1/chat/completions", new Hono().all("/*", async (c, next) => {
2068
+ server.route("/v1/chat/completions", new Hono().all("/*", async (c, _next) => {
1347
2069
  if (state.zenMode) return zenCompletionRoutes.fetch(c.req.raw, c.env);
2070
+ if (state.antigravityMode) return antigravityChatCompletionsRoute.fetch(c.req.raw, c.env);
1348
2071
  return completionRoutes.fetch(c.req.raw, c.env);
1349
2072
  }));
1350
- server.route("/v1/models", new Hono().all("/*", async (c, next) => {
2073
+ server.route("/v1/models", new Hono().all("/*", async (c, _next) => {
1351
2074
  if (state.zenMode) return zenModelRoutes.fetch(c.req.raw, c.env);
2075
+ if (state.antigravityMode) return antigravityModelsRoute.fetch(c.req.raw, c.env);
1352
2076
  return modelRoutes.fetch(c.req.raw, c.env);
1353
2077
  }));
1354
2078
  server.route("/v1/embeddings", embeddingRoutes);
1355
- server.route("/v1/messages", new Hono().all("/*", async (c, next) => {
2079
+ server.route("/v1/messages", new Hono().all("/*", async (c, _next) => {
1356
2080
  if (state.zenMode) return zenMessageRoutes.fetch(c.req.raw, c.env);
2081
+ if (state.antigravityMode) return antigravityMessagesRoute.fetch(c.req.raw, c.env);
1357
2082
  return messageRoutes.fetch(c.req.raw, c.env);
1358
2083
  }));
1359
2084
  server.route("/zen/v1/chat/completions", zenCompletionRoutes);
1360
2085
  server.route("/zen/v1/models", zenModelRoutes);
1361
2086
  server.route("/zen/v1/messages", zenMessageRoutes);
2087
+ server.route("/antigravity/v1/chat/completions", antigravityChatCompletionsRoute);
2088
+ server.route("/antigravity/v1/models", antigravityModelsRoute);
2089
+ server.route("/antigravity/v1/messages", antigravityMessagesRoute);
1362
2090
 
1363
2091
  //#endregion
1364
2092
  //#region src/start.ts
@@ -1407,23 +2135,40 @@ async function runServer(options) {
1407
2135
  state.zenApiKey = options.zenApiKey;
1408
2136
  consola.info("Using provided Zen API key");
1409
2137
  } else {
1410
- const { setupZenApiKey, loadZenAuth } = await import("./auth-KlL1W4gV.js");
2138
+ const { setupZenApiKey, loadZenAuth } = await import("./auth-BO-mwvoU.js");
1411
2139
  const existingAuth = await loadZenAuth();
1412
2140
  if (existingAuth) {
1413
2141
  state.zenApiKey = existingAuth.apiKey;
1414
2142
  consola.info("Using existing Zen API key");
1415
2143
  } else state.zenApiKey = await setupZenApiKey();
1416
2144
  }
1417
- const { cacheZenModels } = await import("./get-models-Hlxa1hWY.js");
2145
+ const { cacheZenModels } = await import("./get-models-DJfPj_Rg.js");
1418
2146
  await cacheZenModels();
1419
2147
  consola.info(`Available Zen models: \n${state.zenModels?.data.map((model) => `- ${model.id}`).join("\n")}`);
2148
+ } else if (options.antigravity) {
2149
+ consola.info("Google Antigravity mode enabled");
2150
+ state.antigravityMode = true;
2151
+ const { loadAntigravityAuth, setupAntigravity, getCurrentAccount } = await import("./auth-D2wtETTq.js");
2152
+ const existingAuth = await loadAntigravityAuth();
2153
+ if (!existingAuth || existingAuth.accounts.length === 0) {
2154
+ consola.warn("No Antigravity accounts found");
2155
+ await setupAntigravity();
2156
+ } else {
2157
+ const enabledCount = existingAuth.accounts.filter((a) => a.enable).length;
2158
+ consola.info(`Found ${existingAuth.accounts.length} Antigravity accounts (${enabledCount} enabled)`);
2159
+ }
2160
+ if (!await getCurrentAccount()) throw new Error("No enabled Antigravity accounts available");
2161
+ const { getAntigravityModels: getAntigravityModels$1 } = await import("./get-models-Dq2ZDU9m.js");
2162
+ const models = await getAntigravityModels$1();
2163
+ state.antigravityModels = models;
2164
+ consola.info(`Available Antigravity models: \n${models.data.map((model) => `- ${model.id}`).join("\n")}`);
1420
2165
  } else {
1421
2166
  await cacheVSCodeVersion();
1422
2167
  if (options.githubToken) {
1423
2168
  state.githubToken = options.githubToken;
1424
2169
  consola.info("Using provided GitHub token");
1425
2170
  try {
1426
- const { getGitHubUser } = await import("./get-user-M3sQS0U8.js");
2171
+ const { getGitHubUser } = await import("./get-user-hpkh0FzZ.js");
1427
2172
  const user = await getGitHubUser();
1428
2173
  consola.info(`Logged in as ${user.login}`);
1429
2174
  } catch (error) {
@@ -1437,7 +2182,7 @@ async function runServer(options) {
1437
2182
  const { HTTPError: HTTPError$1 } = await import("./error-CsShqJjE.js");
1438
2183
  if (error instanceof HTTPError$1 && error.response.status === 401) {
1439
2184
  consola.error("Failed to get Copilot token - GitHub token may be invalid or Copilot access revoked");
1440
- const { clearGithubToken: clearGithubToken$1 } = await import("./token-BssxOyqn.js");
2185
+ const { clearGithubToken: clearGithubToken$1 } = await import("./token-DS09XjQ5.js");
1441
2186
  await clearGithubToken$1();
1442
2187
  consola.info("Please restart to re-authenticate");
1443
2188
  }
@@ -1448,7 +2193,7 @@ async function runServer(options) {
1448
2193
  }
1449
2194
  const serverUrl = `http://localhost:${options.port}`;
1450
2195
  if (options.claudeCode) {
1451
- const models = state.zenMode ? state.zenModels : state.models;
2196
+ const models = state.zenMode ? state.zenModels : state.antigravityMode ? state.antigravityModels : state.models;
1452
2197
  invariant(models, "Models should be loaded by now");
1453
2198
  const selectedModel = await consola.prompt("Select a model to use with Claude Code", {
1454
2199
  type: "select",
@@ -1556,6 +2301,11 @@ const start = defineCommand({
1556
2301
  "zen-api-key": {
1557
2302
  type: "string",
1558
2303
  description: "OpenCode Zen API key (get from https://opencode.ai/zen)"
2304
+ },
2305
+ antigravity: {
2306
+ type: "boolean",
2307
+ default: false,
2308
+ description: "Enable Google Antigravity mode (proxy to Antigravity instead of GitHub Copilot)"
1559
2309
  }
1560
2310
  },
1561
2311
  run({ args }) {
@@ -1577,7 +2327,8 @@ const start = defineCommand({
1577
2327
  proxyEnv: args["proxy-env"],
1578
2328
  apiKeys,
1579
2329
  zen: args.zen,
1580
- zenApiKey: args["zen-api-key"]
2330
+ zenApiKey: args["zen-api-key"],
2331
+ antigravity: args.antigravity
1581
2332
  });
1582
2333
  }
1583
2334
  });