mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-14 10:41:23 +00:00
auth: avoid external cli sync on profile upsert
This commit is contained in:
@@ -3,7 +3,7 @@ import { normalizeSecretInput } from "../../utils/normalize-secret-input.js";
|
||||
import { resolveProviderIdForAuth } from "../provider-auth-aliases.js";
|
||||
import { normalizeProviderId } from "../provider-id.js";
|
||||
import {
|
||||
ensureAuthProfileStore,
|
||||
ensureAuthProfileStoreForLocalUpdate,
|
||||
saveAuthProfileStore,
|
||||
updateAuthProfileStoreWithLock,
|
||||
} from "./store.js";
|
||||
@@ -59,9 +59,9 @@ export function upsertAuthProfile(params: {
|
||||
: params.credential.type === "token"
|
||||
? { ...params.credential, token: normalizeSecretInput(params.credential.token) }
|
||||
: params.credential;
|
||||
const store = ensureAuthProfileStore(params.agentDir);
|
||||
const store = ensureAuthProfileStoreForLocalUpdate(params.agentDir);
|
||||
store.profiles[params.profileId] = credential;
|
||||
saveAuthProfileStore(store, params.agentDir);
|
||||
saveAuthProfileStore(store, params.agentDir, { syncExternalCli: false });
|
||||
}
|
||||
|
||||
export async function upsertAuthProfileWithLock(params: {
|
||||
|
||||
@@ -29,6 +29,11 @@ import type { AuthProfileStore } from "./types.js";
|
||||
type LoadAuthProfileStoreOptions = {
|
||||
allowKeychainPrompt?: boolean;
|
||||
readOnly?: boolean;
|
||||
syncExternalCli?: boolean;
|
||||
};
|
||||
|
||||
type SaveAuthProfileStoreOptions = {
|
||||
syncExternalCli?: boolean;
|
||||
};
|
||||
|
||||
const runtimeAuthStoreSnapshots = new Map<string, AuthProfileStore>();
|
||||
@@ -192,6 +197,10 @@ function syncExternalCliCredentialsTimed(
|
||||
return mutated;
|
||||
}
|
||||
|
||||
function shouldSyncExternalCliCredentials(options?: { syncExternalCli?: boolean }): boolean {
|
||||
return options?.syncExternalCli !== false;
|
||||
}
|
||||
|
||||
export function loadAuthProfileStore(): AuthProfileStore {
|
||||
const asStore = loadPersistedAuthProfileStore();
|
||||
if (asStore) {
|
||||
@@ -238,7 +247,9 @@ function loadAuthProfileStoreForAgent(
|
||||
if (asStore) {
|
||||
// Runtime secret activation must remain read-only:
|
||||
// sync external CLI credentials in-memory, but never persist while readOnly.
|
||||
syncExternalCliCredentialsTimed(asStore, { log: !readOnly });
|
||||
if (shouldSyncExternalCliCredentials(options)) {
|
||||
syncExternalCliCredentialsTimed(asStore, { log: !readOnly });
|
||||
}
|
||||
if (!readOnly) {
|
||||
writeCachedAuthProfileStore({
|
||||
authPath,
|
||||
@@ -279,7 +290,9 @@ function loadAuthProfileStoreForAgent(
|
||||
|
||||
const mergedOAuth = mergeOAuthFileIntoStore(store);
|
||||
// Keep external CLI credentials visible in runtime even during read-only loads.
|
||||
syncExternalCliCredentialsTimed(store, { log: !readOnly });
|
||||
if (shouldSyncExternalCliCredentials(options)) {
|
||||
syncExternalCliCredentialsTimed(store, { log: !readOnly });
|
||||
}
|
||||
const forceReadOnly = process.env.OPENCLAW_AUTH_STORE_READONLY === "1";
|
||||
const shouldWrite = !readOnly && !forceReadOnly && (legacy !== null || mergedOAuth);
|
||||
if (shouldWrite) {
|
||||
@@ -357,6 +370,22 @@ export function ensureAuthProfileStore(
|
||||
return overlayExternalAuthProfiles(merged, { agentDir });
|
||||
}
|
||||
|
||||
export function ensureAuthProfileStoreForLocalUpdate(agentDir?: string): AuthProfileStore {
|
||||
const options: LoadAuthProfileStoreOptions = { syncExternalCli: false };
|
||||
const store = loadAuthProfileStoreForAgent(agentDir, options);
|
||||
const authPath = resolveAuthStorePath(agentDir);
|
||||
const mainAuthPath = resolveAuthStorePath();
|
||||
if (!agentDir || authPath === mainAuthPath) {
|
||||
return store;
|
||||
}
|
||||
|
||||
const mainStore = loadAuthProfileStoreForAgent(undefined, {
|
||||
readOnly: true,
|
||||
syncExternalCli: false,
|
||||
});
|
||||
return mergeAuthProfileStores(mainStore, store);
|
||||
}
|
||||
|
||||
export function hasAnyAuthProfileStoreSource(agentDir?: string): boolean {
|
||||
const runtimeStore = resolveRuntimeAuthProfileStore(agentDir);
|
||||
if (runtimeStore && Object.keys(runtimeStore.profiles).length > 0) {
|
||||
@@ -376,7 +405,11 @@ export function hasAnyAuthProfileStoreSource(agentDir?: string): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
export function saveAuthProfileStore(store: AuthProfileStore, agentDir?: string): void {
|
||||
export function saveAuthProfileStore(
|
||||
store: AuthProfileStore,
|
||||
agentDir?: string,
|
||||
options?: SaveAuthProfileStoreOptions,
|
||||
): void {
|
||||
const authPath = resolveAuthStorePath(agentDir);
|
||||
const statePath = resolveAuthStatePath(agentDir);
|
||||
const runtimeKey = resolveRuntimeStoreKey(agentDir);
|
||||
@@ -394,7 +427,9 @@ export function saveAuthProfileStore(store: AuthProfileStore, agentDir?: string)
|
||||
saveJsonFile(authPath, payload);
|
||||
savePersistedAuthProfileState(store, agentDir);
|
||||
const runtimeStore = cloneAuthProfileStore(store);
|
||||
syncExternalCliCredentialsTimed(runtimeStore, { log: false });
|
||||
if (shouldSyncExternalCliCredentials(options)) {
|
||||
syncExternalCliCredentialsTimed(runtimeStore, { log: false });
|
||||
}
|
||||
writeCachedAuthProfileStore({
|
||||
authPath,
|
||||
authMtimeMs: readAuthStoreMtimeMs(authPath),
|
||||
|
||||
@@ -36,6 +36,7 @@ vi.mock("../plugins/provider-openai-codex-oauth.js", () => ({
|
||||
}));
|
||||
|
||||
const resolvePluginProviders = vi.hoisted(() => vi.fn<() => ProviderPlugin[]>(() => []));
|
||||
const runProviderModelSelectedHook = vi.hoisted(() => vi.fn(async () => {}));
|
||||
vi.mock("../plugins/provider-auth-choice.runtime.js", async () => {
|
||||
const actual = await vi.importActual<typeof import("../plugins/provider-auth-choice.runtime.js")>(
|
||||
"../plugins/provider-auth-choice.runtime.js",
|
||||
@@ -43,6 +44,7 @@ vi.mock("../plugins/provider-auth-choice.runtime.js", async () => {
|
||||
return {
|
||||
...actual,
|
||||
resolvePluginProviders,
|
||||
runProviderModelSelectedHook,
|
||||
};
|
||||
});
|
||||
|
||||
@@ -644,6 +646,7 @@ describe("applyAuthChoice", () => {
|
||||
vi.unstubAllGlobals();
|
||||
resolvePluginProviders.mockReset();
|
||||
resolvePluginProviders.mockReturnValue(createDefaultProviderPlugins());
|
||||
runProviderModelSelectedHook.mockClear();
|
||||
detectZaiEndpoint.mockReset();
|
||||
detectZaiEndpoint.mockResolvedValue(null);
|
||||
loginOpenAICodexOAuth.mockReset();
|
||||
|
||||
Reference in New Issue
Block a user