summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorDax Raad <[email protected]>2025-06-24 10:42:10 -0400
committerDax Raad <[email protected]>2025-06-24 10:42:19 -0400
commit4d3d63294d7598fc70459291de874baa36966b1d (patch)
tree19561a5ba664083ca5f6efbae6581ea27efffae8
parent6bc61cbc2dab6e363b2f333c14772983dd4cd223 (diff)
downloadopencode-4d3d63294d7598fc70459291de874baa36966b1d.tar.gz
opencode-4d3d63294d7598fc70459291de874baa36966b1d.zip
externalize github copilot code
-rw-r--r--packages/opencode/src/auth/copilot.ts20
-rw-r--r--packages/opencode/src/cli/cmd/auth.ts23
-rw-r--r--packages/opencode/src/provider/provider.ts29
3 files changed, 54 insertions, 18 deletions
diff --git a/packages/opencode/src/auth/copilot.ts b/packages/opencode/src/auth/copilot.ts
new file mode 100644
index 000000000..4defdedf1
--- /dev/null
+++ b/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
+})
diff --git a/packages/opencode/src/cli/cmd/auth.ts b/packages/opencode/src/cli/cmd/auth.ts
index 71b2445d9..e5d4c5eda 100644
--- a/packages/opencode/src/cli/cmd/auth.ts
+++ b/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
}
diff --git a/packages/opencode/src/provider/provider.ts b/packages/opencode/src/provider/provider.ts
index 93acb80de..5a4cc95da 100644
--- a/packages/opencode/src/provider/provider.ts
+++ b/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"]