fix(auth): read the Anthropic credential preference from the resolved global dir
Global settings live in settings.json under the resolved global dir, which falls back to the pre-rename ~/.pi/fusion and ~/.pi/kb dirs for installs that never migrated. The reader hardcoded ~/.fusion, so on those installs the operator's preference was silently ignored and resolution fell back to raw-key precedence -- the same silent fallback the preference exists to remove. Mirror the legacy-aware lookup getModelRegistryModelsPath already does for models.json rather than importing core's resolver, which throws under VITEST when called without an explicit dir. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -131,6 +131,27 @@ describe("Anthropic credential precedence (anthropicAuthPreference)", () => {
|
||||
await expect(storage.getApiKey("anthropic")).resolves.toBe(SUBSCRIPTION_ACCESS_TOKEN);
|
||||
});
|
||||
|
||||
it("honors the preference stored in a legacy global settings dir", async () => {
|
||||
writeAuth(homeDir, {
|
||||
anthropic: { type: "api_key", key: RAW_API_KEY },
|
||||
"anthropic-subscription": liveSubscriptionCredential(),
|
||||
});
|
||||
/*
|
||||
Operators who never migrated off the pre-rename global dir keep settings.json under
|
||||
~/.pi/fusion. Reading only ~/.fusion would silently ignore their choice and fall back to
|
||||
raw-key precedence — the exact silent fallback this preference exists to remove.
|
||||
*/
|
||||
mkdirSync(join(homeDir, ".pi", "fusion"), { recursive: true });
|
||||
writeFileSync(
|
||||
join(homeDir, ".pi", "fusion", "settings.json"),
|
||||
JSON.stringify({ anthropicAuthPreference: "subscription" }),
|
||||
);
|
||||
|
||||
const storage = createFusionAuthStorage();
|
||||
|
||||
await expect(storage.getApiKey("anthropic")).resolves.toBe(SUBSCRIPTION_ACCESS_TOKEN);
|
||||
});
|
||||
|
||||
it("falls back to the historical precedence when the settings file is unreadable", async () => {
|
||||
writeAuth(homeDir, {
|
||||
anthropic: { type: "api_key", key: RAW_API_KEY },
|
||||
|
||||
@@ -421,9 +421,26 @@ function resolveStoredCredentialApiKey(providerId: string, credential: StoredCre
|
||||
* can return them as a fallback when neither Fusion auth nor legacy auth.json
|
||||
* contains a key for the provider.
|
||||
*/
|
||||
/** Global settings file that carries the operator's Anthropic credential preference. */
|
||||
/*
|
||||
FNXC:ProviderAuth 2026-07-24-18:20:
|
||||
Global settings live in `settings.json` under the resolved GLOBAL dir — not in Postgres
|
||||
(Postgres holds project settings, which are merged over the global ones by
|
||||
`settings-ops.ts`, and the immutable configuration-revision journal). The global dir is
|
||||
`~/.fusion` for current installs but falls back to the pre-rename `~/.pi/fusion` and
|
||||
`~/.pi/kb` dirs for operators who never migrated (see core `resolveGlobalDirForHome`).
|
||||
Hardcoding `~/.fusion` would silently ignore the operator's preference on those installs and
|
||||
fall back to raw-key precedence — the same silent-fallback failure this preference exists to
|
||||
remove. Mirror the legacy-aware lookup `getModelRegistryModelsPath` already does for
|
||||
models.json rather than importing core's resolver, which throws under VITEST when called
|
||||
without an explicit dir.
|
||||
*/
|
||||
export function getFusionGlobalSettingsPath(home = getHomeDir()): string {
|
||||
return join(home, ".fusion", "settings.json");
|
||||
const candidates = [
|
||||
join(home, ".fusion", "settings.json"),
|
||||
join(home, ".pi", "fusion", "settings.json"),
|
||||
join(home, ".pi", "kb", "settings.json"),
|
||||
];
|
||||
return candidates.find((candidate) => existsSync(candidate)) ?? candidates[0];
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
Reference in New Issue
Block a user