Accept lowercase profile types and anchor sides when reading

This commit is contained in:
Danny Kim
2026-10-20 01:53:32 +09:00
parent 13efaccb17
commit bcb0e0cc70
3 changed files with 29 additions and 2 deletions
+6 -1
View File
@@ -28,6 +28,11 @@ const VERDICTS = new Map<string, [event: string, mark: string]>([
]);
const PROFILE_TYPES = new Set<unknown>(["CLAUDE", "CODEX", "OPENCODE"]);
const FAILURE_REASONS = new Set<unknown>(["SIGN_IN_FAILED", "USAGE_EXHAUSTED", "FAILED"]);
// Anchors written before the UPPER_SNAKE switch spell sides in lowercase.
const OLD_SIDES = new Map<unknown, Anchor["side"]>([
["new", "NEW"],
["old", "OLD"],
]);
const isProfileType = (value: unknown): value is ProfileFailure["type"] => PROFILE_TYPES.has(value);
const isFailureReason = (value: unknown): value is FailureReason => FAILURE_REASONS.has(value);
const failureComment = (failures: ProfileFailure[]): string =>
@@ -56,7 +61,7 @@ function parseAnchors(text: string, sourcePath: string): Anchor[] {
const path = field(entry, "path");
const line = field(entry, "line");
const givenSide = field(entry, "side");
const side = givenSide === undefined ? "NEW" : givenSide;
const side = givenSide === undefined ? "NEW" : (OLD_SIDES.get(givenSide) ?? givenSide);
const body = field(entry, "body");
if (typeof path !== "string" || path === "") {
throw new TypeError(`${at}.path: expected a repository path`);
+11
View File
@@ -65,3 +65,14 @@ test("rejects malformed central credentials", async () => {
),
).rejects.toThrow("profiles[0].token");
});
test("reads lowercase profile types from older credentials", async () => {
const credentials = await parseCredentials(
JSON.stringify({
"reviewer-token": ENCRYPTED,
profiles: [{ type: "codex" }, { type: "claude", token: ENCRYPTED }],
}),
MASTER_KEY,
);
expect(credentials.profiles.map(({ type }) => type)).toStrictEqual(["CODEX", "CLAUDE"]);
});
+12 -1
View File
@@ -78,6 +78,17 @@ export async function encryptCredential(
return `v1:${encodeBase64Url(nonce)}:${encodeBase64Url(new Uint8Array(ciphertext))}`;
}
// profiles.json files written before the UPPER_SNAKE switch spell types in lowercase.
const OLD_PROFILE_TYPES = new Map<unknown, BotProfile["type"]>([
["claude", "CLAUDE"],
["codex", "CODEX"],
["opencode", "OPENCODE"],
]);
function currentSpelling(type: unknown): unknown {
return OLD_PROFILE_TYPES.get(type) ?? type;
}
async function parseProfile(entry: unknown, index: number, masterKey: string): Promise<BotProfile> {
const at = `profiles[${index}]`;
if (!isMapping(entry)) {
@@ -88,7 +99,7 @@ async function parseProfile(entry: unknown, index: number, masterKey: string): P
throw new Error(`${at}.${key}: unknown field`);
}
}
const type = field(entry, "type");
const type = currentSpelling(field(entry, "type"));
const model = field(entry, "model");
const token = field(entry, "token");
if (type !== "CLAUDE" && type !== "CODEX" && type !== "OPENCODE") {