fix(auth): doctor-migrate anthropic oauth profiles
This commit is contained in:
@@ -304,6 +304,7 @@ async function promptAuthConfig(
|
||||
profileId,
|
||||
provider: "anthropic",
|
||||
mode: "oauth",
|
||||
email: oauthCreds.email ?? undefined,
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
|
||||
@@ -40,6 +40,10 @@ const runCommandWithTimeout = vi.fn().mockResolvedValue({
|
||||
killed: false,
|
||||
});
|
||||
|
||||
const ensureAuthProfileStore = vi
|
||||
.fn()
|
||||
.mockReturnValue({ version: 1, profiles: {} });
|
||||
|
||||
const legacyReadConfigFileSnapshot = vi.fn().mockResolvedValue({
|
||||
path: "/tmp/clawdis.json",
|
||||
exists: false,
|
||||
@@ -103,6 +107,14 @@ vi.mock("../process/exec.js", () => ({
|
||||
runCommandWithTimeout,
|
||||
}));
|
||||
|
||||
vi.mock("../agents/auth-profiles.js", async (importOriginal) => {
|
||||
const actual = await importOriginal();
|
||||
return {
|
||||
...actual,
|
||||
ensureAuthProfileStore,
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock("../daemon/service.js", () => ({
|
||||
resolveGatewayService: () => ({
|
||||
label: "LaunchAgent",
|
||||
@@ -614,4 +626,52 @@ describe("doctor", () => {
|
||||
expect(serviceRestart).not.toHaveBeenCalled();
|
||||
expect(confirm).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("migrates anthropic oauth config profile id when only email profile exists", async () => {
|
||||
readConfigFileSnapshot.mockResolvedValue({
|
||||
path: "/tmp/clawdbot.json",
|
||||
exists: true,
|
||||
raw: "{}",
|
||||
parsed: {},
|
||||
valid: true,
|
||||
config: {
|
||||
auth: {
|
||||
profiles: {
|
||||
"anthropic:default": { provider: "anthropic", mode: "oauth" },
|
||||
},
|
||||
},
|
||||
},
|
||||
issues: [],
|
||||
legacyIssues: [],
|
||||
});
|
||||
|
||||
ensureAuthProfileStore.mockReturnValueOnce({
|
||||
version: 1,
|
||||
profiles: {
|
||||
"anthropic:me@example.com": {
|
||||
type: "oauth",
|
||||
provider: "anthropic",
|
||||
access: "access",
|
||||
refresh: "refresh",
|
||||
expires: Date.now() + 60_000,
|
||||
email: "me@example.com",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const { doctorCommand } = await import("./doctor.js");
|
||||
await doctorCommand(
|
||||
{ log: vi.fn(), error: vi.fn(), exit: vi.fn() },
|
||||
{ yes: true },
|
||||
);
|
||||
|
||||
const written = writeConfigFile.mock.calls.at(-1)?.[0] as Record<
|
||||
string,
|
||||
unknown
|
||||
>;
|
||||
const profiles = (written.auth as { profiles: Record<string, unknown> })
|
||||
.profiles;
|
||||
expect(profiles["anthropic:me@example.com"]).toBeTruthy();
|
||||
expect(profiles["anthropic:default"]).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -3,7 +3,10 @@ import os from "node:os";
|
||||
import path from "node:path";
|
||||
|
||||
import { confirm, intro, note, outro, select } from "@clack/prompts";
|
||||
|
||||
import {
|
||||
ensureAuthProfileStore,
|
||||
repairOAuthProfileIdMismatch,
|
||||
} from "../agents/auth-profiles.js";
|
||||
import {
|
||||
DEFAULT_SANDBOX_BROWSER_IMAGE,
|
||||
DEFAULT_SANDBOX_COMMON_IMAGE,
|
||||
@@ -386,6 +389,28 @@ function createDoctorPrompter(params: {
|
||||
};
|
||||
}
|
||||
|
||||
async function maybeRepairAnthropicOAuthProfileId(
|
||||
cfg: ClawdbotConfig,
|
||||
prompter: DoctorPrompter,
|
||||
): Promise<ClawdbotConfig> {
|
||||
const store = ensureAuthProfileStore();
|
||||
const repair = repairOAuthProfileIdMismatch({
|
||||
cfg,
|
||||
store,
|
||||
provider: "anthropic",
|
||||
legacyProfileId: "anthropic:default",
|
||||
});
|
||||
if (!repair.migrated || repair.changes.length === 0) return cfg;
|
||||
|
||||
note(repair.changes.map((c) => `- ${c}`).join("\n"), "Auth profiles");
|
||||
const apply = await prompter.confirm({
|
||||
message: "Update Anthropic OAuth profile id in config now?",
|
||||
initialValue: true,
|
||||
});
|
||||
if (!apply) return cfg;
|
||||
return repair.config;
|
||||
}
|
||||
|
||||
const MEMORY_SYSTEM_PROMPT = [
|
||||
"Memory system not found in workspace.",
|
||||
"Paste this into your agent:",
|
||||
@@ -889,6 +914,8 @@ export async function doctorCommand(
|
||||
cfg = normalized.config;
|
||||
}
|
||||
|
||||
cfg = await maybeRepairAnthropicOAuthProfileId(cfg, prompter);
|
||||
|
||||
const legacyState = await detectLegacyStateMigrations({ cfg });
|
||||
if (legacyState.preview.length > 0) {
|
||||
note(legacyState.preview.join("\n"), "Legacy state detected");
|
||||
|
||||
Reference in New Issue
Block a user