mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-16 20:40:45 +00:00
142 lines
4.5 KiB
TypeScript
142 lines
4.5 KiB
TypeScript
import {
|
|
emptyPluginConfigSchema,
|
|
type OpenClawPluginApi,
|
|
type ProviderResolveDynamicModelContext,
|
|
type ProviderRuntimeModel,
|
|
} from "openclaw/plugin-sdk/core";
|
|
import { listProfilesForProvider } from "../../src/agents/auth-profiles/profiles.js";
|
|
import { ensureAuthProfileStore } from "../../src/agents/auth-profiles/store.js";
|
|
import { normalizeModelCompat } from "../../src/agents/model-compat.js";
|
|
import { coerceSecretRef } from "../../src/config/types.secrets.js";
|
|
import { fetchCopilotUsage } from "../../src/infra/provider-usage.fetch.js";
|
|
import {
|
|
DEFAULT_COPILOT_API_BASE_URL,
|
|
resolveCopilotApiToken,
|
|
} from "../../src/providers/github-copilot-token.js";
|
|
|
|
const PROVIDER_ID = "github-copilot";
|
|
const COPILOT_ENV_VARS = ["COPILOT_GITHUB_TOKEN", "GH_TOKEN", "GITHUB_TOKEN"];
|
|
const CODEX_GPT_53_MODEL_ID = "gpt-5.3-codex";
|
|
const CODEX_TEMPLATE_MODEL_IDS = ["gpt-5.2-codex"] as const;
|
|
|
|
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 };
|
|
}
|
|
|
|
function resolveCopilotForwardCompatModel(
|
|
ctx: ProviderResolveDynamicModelContext,
|
|
): ProviderRuntimeModel | undefined {
|
|
const trimmedModelId = ctx.modelId.trim();
|
|
if (trimmedModelId.toLowerCase() !== CODEX_GPT_53_MODEL_ID) {
|
|
return undefined;
|
|
}
|
|
for (const templateId of CODEX_TEMPLATE_MODEL_IDS) {
|
|
const template = ctx.modelRegistry.find(PROVIDER_ID, templateId) as ProviderRuntimeModel | null;
|
|
if (!template) {
|
|
continue;
|
|
}
|
|
return normalizeModelCompat({
|
|
...template,
|
|
id: trimmedModelId,
|
|
name: trimmedModelId,
|
|
} as ProviderRuntimeModel);
|
|
}
|
|
return undefined;
|
|
}
|
|
|
|
const githubCopilotPlugin = {
|
|
id: "github-copilot",
|
|
name: "GitHub Copilot Provider",
|
|
description: "Bundled GitHub Copilot provider plugin",
|
|
configSchema: emptyPluginConfigSchema(),
|
|
register(api: OpenClawPluginApi) {
|
|
api.registerProvider({
|
|
id: PROVIDER_ID,
|
|
label: "GitHub Copilot",
|
|
docsPath: "/providers/models",
|
|
envVars: COPILOT_ENV_VARS,
|
|
auth: [],
|
|
catalog: {
|
|
order: "late",
|
|
run: async (ctx) => {
|
|
const { githubToken, hasProfile } = resolveFirstGithubToken({
|
|
agentDir: ctx.agentDir,
|
|
env: ctx.env,
|
|
});
|
|
if (!hasProfile && !githubToken) {
|
|
return null;
|
|
}
|
|
let baseUrl = DEFAULT_COPILOT_API_BASE_URL;
|
|
if (githubToken) {
|
|
try {
|
|
const token = await resolveCopilotApiToken({
|
|
githubToken,
|
|
env: ctx.env,
|
|
});
|
|
baseUrl = token.baseUrl;
|
|
} catch {
|
|
baseUrl = DEFAULT_COPILOT_API_BASE_URL;
|
|
}
|
|
}
|
|
return {
|
|
provider: {
|
|
baseUrl,
|
|
models: [],
|
|
},
|
|
};
|
|
},
|
|
},
|
|
resolveDynamicModel: (ctx) => resolveCopilotForwardCompatModel(ctx),
|
|
capabilities: {
|
|
dropThinkingBlockModelHints: ["claude"],
|
|
},
|
|
prepareRuntimeAuth: async (ctx) => {
|
|
const token = await resolveCopilotApiToken({
|
|
githubToken: ctx.apiKey,
|
|
env: ctx.env,
|
|
});
|
|
return {
|
|
apiKey: token.token,
|
|
baseUrl: token.baseUrl,
|
|
expiresAt: token.expiresAt,
|
|
};
|
|
},
|
|
resolveUsageAuth: async (ctx) => await ctx.resolveOAuthToken(),
|
|
fetchUsageSnapshot: async (ctx) =>
|
|
await fetchCopilotUsage(ctx.token, ctx.timeoutMs, ctx.fetchFn),
|
|
});
|
|
},
|
|
};
|
|
|
|
export default githubCopilotPlugin;
|