Просмотр исходного кода

externalize github copilot code

Dax Raad 8 месяцев назад
Родитель
Сommit
4d3d63294d

+ 20 - 0
packages/opencode/src/auth/copilot.ts

@@ -0,0 +1,20 @@
+import { Global } from "../global"
+import { lazy } from "../util/lazy"
+import path from "path"
+
+export const AuthCopilot = lazy(async () => {
+  const file = Bun.file(path.join(Global.Path.cache, "copilot.ts"))
+  const response = fetch(
+    "https://raw.githubusercontent.com/sst/opencode-github-copilot/refs/heads/main/auth.ts",
+  )
+    .then((x) => Bun.write(file, x))
+    .catch(() => {})
+
+  if (!file.exists()) {
+    const worked = await response
+    if (!worked) return
+  }
+  const result = await import(file.name!).catch(() => {})
+  if (!result) return
+  return result.AuthCopilot
+})

+ 15 - 8
packages/opencode/src/cli/cmd/auth.ts

@@ -1,5 +1,5 @@
 import { AuthAnthropic } from "../../auth/anthropic"
-import { AuthGithubCopilot } from "../../auth/github-copilot"
+import { AuthCopilot } from "../../auth/copilot"
 import { Auth } from "../../auth"
 import { cmd } from "./cmd"
 import * as prompts from "@clack/prompts"
@@ -17,7 +17,7 @@ export const AuthCommand = cmd({
       .command(AuthLogoutCommand)
       .command(AuthListCommand)
       .demandCommand(),
-  async handler() { },
+  async handler() {},
 })
 
 export const AuthListCommand = cmd({
@@ -148,9 +148,10 @@ export const AuthLoginCommand = cmd({
       }
     }
 
-    if (provider === "github-copilot") {
+    const copilot = await AuthCopilot()
+    if (provider === "github-copilot" && copilot) {
       await new Promise((resolve) => setTimeout(resolve, 10))
-      const deviceInfo = await AuthGithubCopilot.authorize()
+      const deviceInfo = await copilot.authorize()
 
       prompts.note(
         `Please visit: ${deviceInfo.verification}\nEnter code: ${deviceInfo.user}`,
@@ -163,13 +164,19 @@ export const AuthLoginCommand = cmd({
         await new Promise((resolve) =>
           setTimeout(resolve, deviceInfo.interval * 1000),
         )
-        const status = await AuthGithubCopilot.poll(deviceInfo.device)
-        if (status === "pending") continue
-        if (status === "complete") {
+        const response = await copilot.poll(deviceInfo.device)
+        if (response.status === "pending") continue
+        if (response.status === "success") {
+          await Auth.set("github-copilot", {
+            type: "oauth",
+            refresh: response.refresh,
+            access: response.access,
+            expires: response.expires,
+          })
           spinner.stop("Login successful")
           break
         }
-        if (status === "failed") {
+        if (response.status === "failed") {
           spinner.stop("Failed to authorize", 1)
           break
         }

+ 19 - 10
packages/opencode/src/provider/provider.ts

@@ -19,7 +19,7 @@ import type { Tool } from "../tool/tool"
 import { WriteTool } from "../tool/write"
 import { TodoReadTool, TodoWriteTool } from "../tool/todo"
 import { AuthAnthropic } from "../auth/anthropic"
-import { AuthGithubCopilot } from "../auth/github-copilot"
+import { AuthCopilot } from "../auth/copilot"
 import { ModelsDev } from "./models"
 import { NamedError } from "../util/error"
 import { Auth } from "../auth"
@@ -68,8 +68,10 @@ export namespace Provider {
       }
     },
     "github-copilot": async (provider) => {
-      const info = await AuthGithubCopilot.access()
-      if (!info) return false
+      const copilot = await AuthCopilot()
+      if (!copilot) return false
+      let info = await Auth.get("github-copilot")
+      if (!info || info.type !== "oauth") return false
 
       if (provider && provider.models) {
         for (const model of Object.values(provider.models)) {
@@ -84,15 +86,22 @@ export namespace Provider {
         options: {
           apiKey: "",
           async fetch(input: any, init: any) {
-            const token = await AuthGithubCopilot.access()
-            if (!token) throw new Error("GitHub Copilot authentication expired")
+            let info = await Auth.get("github-copilot")
+            if (!info || info.type !== "oauth") return
+            if (!info.access || info.expires < Date.now()) {
+              const tokens = await copilot.access(info.refresh)
+              if (!tokens)
+                throw new Error("GitHub Copilot authentication expired")
+              info = {
+                type: "oauth",
+                ...tokens,
+              }
+              await Auth.set("github-copilot", info)
+            }
             const headers = {
               ...init.headers,
-              Authorization: `Bearer ${token}`,
-              "User-Agent": "GitHubCopilotChat/0.26.7",
-              "Editor-Version": "vscode/1.99.3",
-              "Editor-Plugin-Version": "copilot-chat/0.26.7",
-              "Copilot-Integration-Id": "vscode-chat",
+              ...copilot.HEADERS,
+              Authorization: `Bearer ${info.access}`,
               "Openai-Intent": "conversation-edits",
             }
             delete headers["x-api-key"]