diff --git a/extensions/github-copilot/index.ts b/extensions/github-copilot/index.ts index 459f0812fea..224a3125103 100644 --- a/extensions/github-copilot/index.ts +++ b/extensions/github-copilot/index.ts @@ -1,14 +1,4 @@ import { definePluginEntry, type ProviderAuthContext } from "openclaw/plugin-sdk/plugin-entry"; -import { - coerceSecretRef, - ensureAuthProfileStore, - listProfilesForProvider, -} from "openclaw/plugin-sdk/provider-auth"; -import { githubCopilotLoginCommand } from "openclaw/plugin-sdk/provider-auth-login"; -import { PROVIDER_ID, resolveCopilotForwardCompatModel } from "./models.js"; -import { wrapCopilotAnthropicStream } from "./stream.js"; -import { DEFAULT_COPILOT_API_BASE_URL, resolveCopilotApiToken } from "./token.js"; -import { fetchCopilotUsage } from "./usage.js"; const COPILOT_ENV_VARS = ["COPILOT_GITHUB_TOKEN", "GH_TOKEN", "GITHUB_TOKEN"]; const COPILOT_XHIGH_MODEL_IDS = ["gpt-5.2", "gpt-5.2-codex"] as const; @@ -21,85 +11,104 @@ function buildGithubCopilotReplayPolicy(modelId?: string) { : {}; } -function resolveFirstGithubToken(params: { agentDir?: string; env: NodeJS.ProcessEnv }): { - githubToken: string; - hasProfile: boolean; -} { - const authStore = ensureAuthProfileStore(params.agentDir, { - allowKeychainPrompt: false, - }); - const hasProfile = listProfilesForProvider(authStore, PROVIDER_ID).length > 0; - const envToken = - params.env.COPILOT_GITHUB_TOKEN ?? params.env.GH_TOKEN ?? params.env.GITHUB_TOKEN ?? ""; - const githubToken = envToken.trim(); - if (githubToken || !hasProfile) { - return { githubToken, hasProfile }; - } - - const profileId = listProfilesForProvider(authStore, PROVIDER_ID)[0]; - const profile = profileId ? authStore.profiles[profileId] : undefined; - if (profile?.type !== "token") { - return { githubToken: "", hasProfile }; - } - const directToken = profile.token?.trim() ?? ""; - if (directToken) { - return { githubToken: directToken, hasProfile }; - } - const tokenRef = coerceSecretRef(profile.tokenRef); - if (tokenRef?.source === "env" && tokenRef.id.trim()) { - return { - githubToken: (params.env[tokenRef.id] ?? process.env[tokenRef.id] ?? "").trim(), - hasProfile, - }; - } - return { githubToken: "", hasProfile }; -} - -async function runGitHubCopilotAuth(ctx: ProviderAuthContext) { - await ctx.prompter.note( - [ - "This will open a GitHub device login to authorize Copilot.", - "Requires an active GitHub Copilot subscription.", - ].join("\n"), - "GitHub Copilot", - ); - - if (!process.stdin.isTTY) { - await ctx.prompter.note("GitHub Copilot login requires an interactive TTY.", "GitHub Copilot"); - return { profiles: [] }; - } - - try { - await githubCopilotLoginCommand({ yes: true, profileId: "github-copilot:github" }, ctx.runtime); - } catch (err) { - await ctx.prompter.note(`GitHub Copilot login failed: ${String(err)}`, "GitHub Copilot"); - return { profiles: [] }; - } - - const authStore = ensureAuthProfileStore(undefined, { - allowKeychainPrompt: false, - }); - const credential = authStore.profiles["github-copilot:github"]; - if (!credential || credential.type !== "token") { - return { profiles: [] }; - } - - return { - profiles: [ - { - profileId: "github-copilot:github", - credential, - }, - ], - defaultModel: "github-copilot/gpt-4o", - }; -} - export default definePluginEntry({ id: "github-copilot", name: "GitHub Copilot Provider", description: "Bundled GitHub Copilot provider plugin", - register(api) { + async register(api) { + const { + coerceSecretRef, + DEFAULT_COPILOT_API_BASE_URL, + ensureAuthProfileStore, + fetchCopilotUsage, + githubCopilotLoginCommand, + listProfilesForProvider, + PROVIDER_ID, + resolveCopilotApiToken, + resolveCopilotForwardCompatModel, + wrapCopilotAnthropicStream, + } = await import("./register.runtime.js"); + + function resolveFirstGithubToken(params: { agentDir?: string; env: NodeJS.ProcessEnv }): { + githubToken: string; + hasProfile: boolean; + } { + const authStore = ensureAuthProfileStore(params.agentDir, { + allowKeychainPrompt: false, + }); + const hasProfile = listProfilesForProvider(authStore, PROVIDER_ID).length > 0; + const envToken = + params.env.COPILOT_GITHUB_TOKEN ?? params.env.GH_TOKEN ?? params.env.GITHUB_TOKEN ?? ""; + const githubToken = envToken.trim(); + if (githubToken || !hasProfile) { + return { githubToken, hasProfile }; + } + + const profileId = listProfilesForProvider(authStore, PROVIDER_ID)[0]; + const profile = profileId ? authStore.profiles[profileId] : undefined; + if (profile?.type !== "token") { + return { githubToken: "", hasProfile }; + } + const directToken = profile.token?.trim() ?? ""; + if (directToken) { + return { githubToken: directToken, hasProfile }; + } + const tokenRef = coerceSecretRef(profile.tokenRef); + if (tokenRef?.source === "env" && tokenRef.id.trim()) { + return { + githubToken: (params.env[tokenRef.id] ?? process.env[tokenRef.id] ?? "").trim(), + hasProfile, + }; + } + return { githubToken: "", hasProfile }; + } + + async function runGitHubCopilotAuth(ctx: ProviderAuthContext) { + await ctx.prompter.note( + [ + "This will open a GitHub device login to authorize Copilot.", + "Requires an active GitHub Copilot subscription.", + ].join("\n"), + "GitHub Copilot", + ); + + if (!process.stdin.isTTY) { + await ctx.prompter.note( + "GitHub Copilot login requires an interactive TTY.", + "GitHub Copilot", + ); + return { profiles: [] }; + } + + try { + await githubCopilotLoginCommand( + { yes: true, profileId: "github-copilot:github" }, + ctx.runtime, + ); + } catch (err) { + await ctx.prompter.note(`GitHub Copilot login failed: ${String(err)}`, "GitHub Copilot"); + return { profiles: [] }; + } + + const authStore = ensureAuthProfileStore(undefined, { + allowKeychainPrompt: false, + }); + const credential = authStore.profiles["github-copilot:github"]; + if (!credential || credential.type !== "token") { + return { profiles: [] }; + } + + return { + profiles: [ + { + profileId: "github-copilot:github", + credential, + }, + ], + defaultModel: "github-copilot/gpt-4o", + }; + } + api.registerProvider({ id: PROVIDER_ID, label: "GitHub Copilot", diff --git a/extensions/github-copilot/register.runtime.ts b/extensions/github-copilot/register.runtime.ts new file mode 100644 index 00000000000..c31c14c75ad --- /dev/null +++ b/extensions/github-copilot/register.runtime.ts @@ -0,0 +1,10 @@ +export { + coerceSecretRef, + ensureAuthProfileStore, + listProfilesForProvider, +} from "openclaw/plugin-sdk/provider-auth"; +export { githubCopilotLoginCommand } from "openclaw/plugin-sdk/provider-auth-login"; +export { PROVIDER_ID, resolveCopilotForwardCompatModel } from "./models.js"; +export { wrapCopilotAnthropicStream } from "./stream.js"; +export { DEFAULT_COPILOT_API_BASE_URL, resolveCopilotApiToken } from "./token.js"; +export { fetchCopilotUsage } from "./usage.js";