refactor(github-copilot): lazy-load provider registration

This commit is contained in:
Vincent Koc 2026-04-04 15:05:51 +09:00
parent 1c1f32e756
commit e56ffd48df
2 changed files with 104 additions and 85 deletions

View File

@ -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",

View File

@ -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";