mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 07:50:43 +00:00
fix(onboard): address copilot review feedback
This commit is contained in:
@@ -214,6 +214,92 @@ describe("github-copilot plugin", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("falls back to GH_TOKEN during non-interactive onboarding", async () => {
|
||||
const provider = registerProviderWithPluginConfig({});
|
||||
const method = provider.auth[0];
|
||||
const agentDir = await createAgentDir();
|
||||
const runtime = { error: vi.fn(), exit: vi.fn() };
|
||||
const resolveApiKey = vi.fn(async ({ envVar }: { envVar?: string }) =>
|
||||
envVar === "GH_TOKEN"
|
||||
? {
|
||||
key: "ghu_from_gh_token",
|
||||
source: "env" as const,
|
||||
envVarName: "GH_TOKEN",
|
||||
}
|
||||
: null,
|
||||
);
|
||||
|
||||
const result = await method.runNonInteractive({
|
||||
authChoice: "github-copilot",
|
||||
config: {},
|
||||
baseConfig: {},
|
||||
opts: {},
|
||||
runtime,
|
||||
agentDir,
|
||||
resolveApiKey,
|
||||
toApiKeyCredential: vi.fn(),
|
||||
});
|
||||
|
||||
expect(runtime.error).not.toHaveBeenCalled();
|
||||
expect(resolveApiKey).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ envVar: "COPILOT_GITHUB_TOKEN" }),
|
||||
);
|
||||
expect(resolveApiKey).toHaveBeenCalledWith(expect.objectContaining({ envVar: "GH_TOKEN" }));
|
||||
expect(result?.auth?.profiles?.["github-copilot:github"]).toEqual({
|
||||
provider: "github-copilot",
|
||||
mode: "token",
|
||||
});
|
||||
|
||||
const profile = ensureAuthProfileStore(agentDir).profiles["github-copilot:github"];
|
||||
expect(profile).toEqual({
|
||||
type: "token",
|
||||
provider: "github-copilot",
|
||||
token: "ghu_from_gh_token",
|
||||
});
|
||||
});
|
||||
|
||||
it("preserves an existing primary model during non-interactive onboarding", async () => {
|
||||
const provider = registerProviderWithPluginConfig({});
|
||||
const method = provider.auth[0];
|
||||
const agentDir = await createAgentDir();
|
||||
const runtime = { error: vi.fn(), exit: vi.fn() };
|
||||
|
||||
const result = await method.runNonInteractive({
|
||||
authChoice: "github-copilot",
|
||||
config: {
|
||||
agents: {
|
||||
defaults: {
|
||||
model: {
|
||||
primary: "github-copilot/gpt-5.4",
|
||||
fallbacks: ["openai/gpt-5.4"],
|
||||
},
|
||||
models: {
|
||||
"github-copilot/gpt-5.4": { label: "Existing" },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
baseConfig: {},
|
||||
opts: { githubCopilotToken: "ghu_test" },
|
||||
runtime,
|
||||
agentDir,
|
||||
resolveApiKey: vi.fn(async () => ({
|
||||
key: "ghu_test",
|
||||
source: "flag" as const,
|
||||
})),
|
||||
toApiKeyCredential: vi.fn(),
|
||||
});
|
||||
|
||||
expect(runtime.error).not.toHaveBeenCalled();
|
||||
expect(result?.agents?.defaults?.model).toEqual({
|
||||
primary: "github-copilot/gpt-5.4",
|
||||
fallbacks: ["openai/gpt-5.4"],
|
||||
});
|
||||
expect(result?.agents?.defaults?.models).toEqual({
|
||||
"github-copilot/gpt-5.4": { label: "Existing" },
|
||||
});
|
||||
});
|
||||
|
||||
it("reuses an existing token profile during non-interactive onboarding", async () => {
|
||||
const provider = registerProviderWithPluginConfig({});
|
||||
const method = provider.auth[0];
|
||||
@@ -267,16 +353,17 @@ describe("github-copilot plugin", () => {
|
||||
},
|
||||
runtime,
|
||||
agentDir,
|
||||
resolveApiKey: vi.fn(async () => {
|
||||
runtime.error("resolver error");
|
||||
runtime.exit(1);
|
||||
return null;
|
||||
}),
|
||||
resolveApiKey: vi.fn(async () => null),
|
||||
toApiKeyCredential: vi.fn(),
|
||||
});
|
||||
|
||||
expect(result).toBeNull();
|
||||
expect(runtime.error).toHaveBeenCalledTimes(1);
|
||||
expect(runtime.error).toHaveBeenCalledWith("resolver error");
|
||||
expect(runtime.error).toHaveBeenCalledWith(
|
||||
[
|
||||
"--github-copilot-token cannot be used with --secret-input-mode ref unless COPILOT_GITHUB_TOKEN, GH_TOKEN, or GITHUB_TOKEN is set in env.",
|
||||
"Set one of those env vars and omit --github-copilot-token, or use --secret-input-mode plaintext.",
|
||||
].join("\n"),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -38,6 +38,15 @@ async function loadGithubCopilotRuntime() {
|
||||
function applyCopilotDefaultModel(cfg: OpenClawConfig): OpenClawConfig {
|
||||
const defaults = cfg.agents?.defaults;
|
||||
const existingModel = defaults?.model;
|
||||
const existingPrimary =
|
||||
typeof existingModel === "string"
|
||||
? existingModel.trim()
|
||||
: typeof existingModel === "object" && typeof existingModel?.primary === "string"
|
||||
? existingModel.primary.trim()
|
||||
: "";
|
||||
if (existingPrimary) {
|
||||
return cfg;
|
||||
}
|
||||
const fallbacks =
|
||||
typeof existingModel === "object" && existingModel !== null && "fallbacks" in existingModel
|
||||
? (existingModel as { fallbacks?: string[] }).fallbacks
|
||||
@@ -76,20 +85,79 @@ function resolveExistingCopilotTokenProfileId(agentDir?: string): string | undef
|
||||
});
|
||||
}
|
||||
|
||||
async function resolveCopilotNonInteractiveToken(
|
||||
ctx: ProviderAuthMethodNonInteractiveContext,
|
||||
flagValue: string | undefined,
|
||||
) {
|
||||
const resolveFromEnvChain = async () => {
|
||||
for (const envVar of COPILOT_ENV_VARS) {
|
||||
const resolved = await ctx.resolveApiKey({
|
||||
provider: PROVIDER_ID,
|
||||
flagName: "--github-copilot-token",
|
||||
envVar,
|
||||
envVarName: envVar,
|
||||
allowProfile: false,
|
||||
required: false,
|
||||
});
|
||||
if (resolved) {
|
||||
return resolved;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
if (ctx.opts.secretInputMode === "ref") {
|
||||
const resolved = await resolveFromEnvChain();
|
||||
if (resolved) {
|
||||
return resolved;
|
||||
}
|
||||
if (flagValue) {
|
||||
ctx.runtime.error(
|
||||
[
|
||||
"--github-copilot-token cannot be used with --secret-input-mode ref unless COPILOT_GITHUB_TOKEN, GH_TOKEN, or GITHUB_TOKEN is set in env.",
|
||||
"Set one of those env vars and omit --github-copilot-token, or use --secret-input-mode plaintext.",
|
||||
].join("\n"),
|
||||
);
|
||||
ctx.runtime.exit(1);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
const primary = await ctx.resolveApiKey({
|
||||
provider: PROVIDER_ID,
|
||||
flagValue,
|
||||
flagName: "--github-copilot-token",
|
||||
envVar: COPILOT_ENV_VARS[0],
|
||||
envVarName: COPILOT_ENV_VARS[0],
|
||||
allowProfile: false,
|
||||
required: false,
|
||||
});
|
||||
if (primary || flagValue) {
|
||||
return primary;
|
||||
}
|
||||
|
||||
for (const envVar of COPILOT_ENV_VARS.slice(1)) {
|
||||
const resolved = await ctx.resolveApiKey({
|
||||
provider: PROVIDER_ID,
|
||||
flagName: "--github-copilot-token",
|
||||
envVar,
|
||||
envVarName: envVar,
|
||||
allowProfile: false,
|
||||
required: false,
|
||||
});
|
||||
if (resolved) {
|
||||
return resolved;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
async function runGitHubCopilotNonInteractiveAuth(
|
||||
ctx: ProviderAuthMethodNonInteractiveContext,
|
||||
): Promise<OpenClawConfig | null> {
|
||||
const opts = ctx.opts as Record<string, unknown> | undefined;
|
||||
const flagValue = normalizeOptionalSecretInput(opts?.githubCopilotToken);
|
||||
const resolved = await ctx.resolveApiKey({
|
||||
provider: PROVIDER_ID,
|
||||
flagValue,
|
||||
flagName: "--github-copilot-token",
|
||||
envVar: "COPILOT_GITHUB_TOKEN",
|
||||
envVarName: "COPILOT_GITHUB_TOKEN",
|
||||
allowProfile: false,
|
||||
required: false,
|
||||
});
|
||||
const resolved = await resolveCopilotNonInteractiveToken(ctx, flagValue);
|
||||
|
||||
let profileId = DEFAULT_COPILOT_PROFILE_ID;
|
||||
if (resolved) {
|
||||
|
||||
Reference in New Issue
Block a user