mirror of
https://github.com/cline/cline.git
synced 2026-09-16 21:01:52 +08:00
fix(core): preserve oauth auth when saving provider settings
This commit is contained in:
@@ -337,6 +337,87 @@ describe("ProviderSettingsManager", () => {
|
||||
expect(manager.read().providers["openai-codex"]?.tokenSource).toBe("oauth");
|
||||
});
|
||||
|
||||
it("preserves OAuth auth when updating only Cline model settings", () => {
|
||||
const tempDir = mkdtempSync(
|
||||
path.join(os.tmpdir(), "core-provider-settings-"),
|
||||
);
|
||||
tempDirs.push(tempDir);
|
||||
const filePath = path.join(tempDir, "provider-settings.json");
|
||||
const manager = new ProviderSettingsManager({ filePath });
|
||||
|
||||
manager.saveProviderSettings(
|
||||
{
|
||||
provider: "cline",
|
||||
model: "anthropic/claude-sonnet-4.6",
|
||||
auth: {
|
||||
accessToken: "workos:access-old",
|
||||
refreshToken: "refresh-old",
|
||||
expiresAt: 4_000_000_000_000,
|
||||
accountId: "acct-old",
|
||||
},
|
||||
},
|
||||
{ tokenSource: "oauth" },
|
||||
);
|
||||
|
||||
manager.saveProviderSettings({
|
||||
provider: "cline",
|
||||
model: "anthropic/claude-haiku-4.5",
|
||||
reasoning: { enabled: false },
|
||||
});
|
||||
|
||||
expect(manager.getProviderSettings("cline")).toEqual({
|
||||
provider: "cline",
|
||||
model: "anthropic/claude-haiku-4.5",
|
||||
reasoning: { enabled: false },
|
||||
auth: {
|
||||
accessToken: "workos:access-old",
|
||||
refreshToken: "refresh-old",
|
||||
expiresAt: 4_000_000_000_000,
|
||||
accountId: "acct-old",
|
||||
},
|
||||
});
|
||||
expect(manager.read().providers.cline?.tokenSource).toBe("oauth");
|
||||
});
|
||||
|
||||
it("merges partial OAuth auth updates with existing refresh metadata", () => {
|
||||
const tempDir = mkdtempSync(
|
||||
path.join(os.tmpdir(), "core-provider-settings-"),
|
||||
);
|
||||
tempDirs.push(tempDir);
|
||||
const filePath = path.join(tempDir, "provider-settings.json");
|
||||
const manager = new ProviderSettingsManager({ filePath });
|
||||
|
||||
manager.saveProviderSettings(
|
||||
{
|
||||
provider: "openai-codex",
|
||||
auth: {
|
||||
accessToken: "access-old",
|
||||
refreshToken: "refresh-old",
|
||||
expiresAt: 4_000_000_000_000,
|
||||
accountId: "acct-old",
|
||||
},
|
||||
},
|
||||
{ tokenSource: "oauth" },
|
||||
);
|
||||
|
||||
manager.saveProviderSettings(
|
||||
{
|
||||
provider: "openai-codex",
|
||||
auth: {
|
||||
accessToken: "access-new",
|
||||
},
|
||||
},
|
||||
{ tokenSource: "oauth" },
|
||||
);
|
||||
|
||||
expect(manager.getProviderSettings("openai-codex")?.auth).toEqual({
|
||||
accessToken: "access-new",
|
||||
refreshToken: "refresh-old",
|
||||
expiresAt: 4_000_000_000_000,
|
||||
accountId: "acct-old",
|
||||
});
|
||||
});
|
||||
|
||||
it("ignores invalid persisted JSON and falls back to empty state", () => {
|
||||
const tempDir = mkdtempSync(
|
||||
path.join(os.tmpdir(), "core-provider-settings-"),
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
writeFileSync,
|
||||
} from "node:fs";
|
||||
import { basename, dirname } from "node:path";
|
||||
import { isOAuthProviderId } from "@cline/shared";
|
||||
import { resolveProviderSettingsPath } from "@cline/shared/storage";
|
||||
import { getLiveModelsCatalog } from "../..";
|
||||
import {
|
||||
@@ -50,6 +51,25 @@ function inferLegacyDataDir(filePath: string): string | undefined {
|
||||
return dirname(settingsDir);
|
||||
}
|
||||
|
||||
function preserveOAuthAuthFields(
|
||||
next: ProviderSettings,
|
||||
previous: ProviderSettings | undefined,
|
||||
): ProviderSettings {
|
||||
if (!isOAuthProviderId(next.provider) || !previous?.auth) {
|
||||
return next;
|
||||
}
|
||||
if (!next.auth) {
|
||||
return { ...next, auth: previous.auth };
|
||||
}
|
||||
return {
|
||||
...next,
|
||||
auth: {
|
||||
...previous.auth,
|
||||
...next.auth,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export class ProviderSettingsManager {
|
||||
private readonly filePath: string;
|
||||
private readonly dataDir?: string;
|
||||
@@ -124,11 +144,15 @@ export class ProviderSettingsManager {
|
||||
settings: unknown,
|
||||
options: SaveProviderSettingsOptions = {},
|
||||
): StoredProviderSettings {
|
||||
const validatedSettings = ProviderSettingsSchema.parse(settings);
|
||||
const parsedSettings = ProviderSettingsSchema.parse(settings);
|
||||
const previous = this.read();
|
||||
const providerId = validatedSettings.provider;
|
||||
const providerId = parsedSettings.provider;
|
||||
const shouldSetLastUsed = options.setLastUsed !== false;
|
||||
const previousEntry = previous.providers[providerId];
|
||||
const validatedSettings = preserveOAuthAuthFields(
|
||||
parsedSettings,
|
||||
previousEntry?.settings,
|
||||
);
|
||||
const tokenSource =
|
||||
options.tokenSource ?? previousEntry?.tokenSource ?? "manual";
|
||||
const next: StoredProviderSettings = {
|
||||
|
||||
Reference in New Issue
Block a user