惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

爱范儿
爱范儿
H
Help Net Security
Jina AI
Jina AI
T
The Blog of Author Tim Ferriss
宝玉的分享
宝玉的分享
博客园 - 叶小钗
Y
Y Combinator Blog
罗磊的独立博客
大猫的无限游戏
大猫的无限游戏
WordPress大学
WordPress大学
C
Check Point Blog
Recent Announcements
Recent Announcements
IT之家
IT之家
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
美团技术团队
云风的 BLOG
云风的 BLOG
雷峰网
雷峰网
H
Hackread – Cybersecurity News, Data Breaches, AI and More
S
SegmentFault 最新的问题
MyScale Blog
MyScale Blog
Apple Machine Learning Research
Apple Machine Learning Research
Microsoft Azure Blog
Microsoft Azure Blog
V
Visual Studio Blog
B
Blog

Recent Commits to openclaw:main

test: merge chat side-result checks · openclaw/openclaw@ddd2c2a test: merge cron history checks · openclaw/openclaw@f7eb746 test: merge responsive navigation shell checks · openclaw/openclaw@c2e4b47 docs(changelog): add codex oauth fixes · openclaw/openclaw@628e6cd test: merge navigation routing cases · openclaw/openclaw@5d8cecb Tests: mock channel registry bundled fallback · openclaw/openclaw@2b08233 Secrets: avoid broad web search discovery for single plugin config · openclaw/openclaw@a464f59 test: merge config view browser checks · openclaw/openclaw@20cf511 fix(status): align oauth health with runtime · openclaw/openclaw@eed7116 feat: add macOS screen snapshots for monitor preview (#67954) thanks … · openclaw/openclaw@f377db1 fix: report shared auth scopes in hello-ok (#67810) thanks @BunsDev · openclaw/openclaw@0b6c39b Auto-reply: avoid eager bundled route fallback · openclaw/openclaw@3ea1bf4 Tests: narrow session binding contract setup · openclaw/openclaw@54e4e16 fix(macOS): enable undo/redo in webchat composer text input (#34962) · openclaw/openclaw@00951dc Tests: speed up channel setup promotion · openclaw/openclaw@82b529a Docs: refresh agent instructions · openclaw/openclaw@5775fe2 fix(auth): serialize OAuth refresh across agents to fix #26322 (#67876) · openclaw/openclaw@8e79080 test: allow ollama public surface boundary test · openclaw/openclaw@7d4f1a6 Docs: add test performance guardrails · openclaw/openclaw@89706d3 Tests: restore context-engine usage proof · openclaw/openclaw@e4c4f95 Tests: slim context engine runtime coverage · openclaw/openclaw@74c198f ci: retry failed custom checkouts · openclaw/openclaw@0ee5baf test: trim duplicate provider auth onboarding cases · openclaw/openclaw@1ffc02e matrix: fix sessions_spawn --thread subagent session spawning (#67643) · openclaw/openclaw@1ce2596 test: reduce auth choice fixture churn · openclaw/openclaw@857b9cd test: mock health status config boundaries · openclaw/openclaw@9d5ab4a test: mock onboard config io boundary · openclaw/openclaw@299694d test: mock legacy state plugin boundaries · openclaw/openclaw@2713089 test: mock channel install boundaries · openclaw/openclaw@b945248 test: mock doctor preview channel boundaries · openclaw/openclaw@b1a3ad4
fix: serialize stale auth shadow repair · openclaw/opencl...
steipete · 2026-05-15 · via Recent Commits to openclaw:main

@@ -5,6 +5,7 @@ import {

55

resolveDefaultAgentDir,

66

listAgentEntries,

77

} from "../../../agents/agent-scope.js";

8+

import { AUTH_STORE_LOCK_OPTIONS } from "../../../agents/auth-profiles/constants.js";

89

import {

910

areOAuthCredentialsEquivalent,

1011

hasUsableOAuthCredential,

@@ -16,6 +17,7 @@ import { saveAuthProfileStore } from "../../../agents/auth-profiles/store.js";

1617

import type { AuthProfileStore, OAuthCredential } from "../../../agents/auth-profiles/types.js";

1718

import { resolveStateDir } from "../../../config/paths.js";

1819

import type { OpenClawConfig } from "../../../config/types.openclaw.js";

20+

import { withFileLock } from "../../../infra/file-lock.js";

1921

import { shortenHomePath } from "../../../utils.js";

20222123

type StaleOAuthProfileShadow = {

@@ -126,31 +128,84 @@ export async function scanStaleOAuthProfileShadows(params: {

126128

return hits;

127129

}

128130129-

function removeProfilesFromStore(

130-

store: AuthProfileStore,

131-

profileIds: Set<string>,

132-

): AuthProfileStore {

133-

const profiles = { ...store.profiles };

134-

const usageStats = store.usageStats ? { ...store.usageStats } : undefined;

135-

for (const profileId of profileIds) {

131+

function removeStaleProfilesFromStore(params: {

132+

store: AuthProfileStore;

133+

mainStore: AuthProfileStore;

134+

profileIds: Set<string>;

135+

now: number;

136+

}): { store: AuthProfileStore; removedProfileIds: string[] } {

137+

const removedProfileIds: string[] = [];

138+

const profiles = { ...params.store.profiles };

139+

const usageStats = params.store.usageStats ? { ...params.store.usageStats } : undefined;

140+

for (const profileId of params.profileIds) {

141+

const local = profiles[profileId];

142+

const main = params.mainStore.profiles[profileId];

143+

if (

144+

local?.type !== "oauth" ||

145+

!shouldRemoveLocalOAuthShadow({

146+

local,

147+

main: main?.type === "oauth" ? main : undefined,

148+

now: params.now,

149+

})

150+

) {

151+

continue;

152+

}

136153

delete profiles[profileId];

137154

if (usageStats) {

138155

delete usageStats[profileId];

139156

}

157+

removedProfileIds.push(profileId);

140158

}

141159

return {

142-

...store,

143-

profiles,

144-

...(usageStats && Object.keys(usageStats).length > 0

145-

? { usageStats }

146-

: { usageStats: undefined }),

160+

store: {

161+

...params.store,

162+

profiles,

163+

...(usageStats && Object.keys(usageStats).length > 0

164+

? { usageStats }

165+

: { usageStats: undefined }),

166+

},

167+

removedProfileIds,

147168

};

148169

}

149170150171

function formatProfileList(profileIds: string[]): string {

151172

return profileIds.length === 1 ? profileIds[0] : `${profileIds.length} profiles`;

152173

}

153174175+

async function repairStaleOAuthProfilesForAgent(params: {

176+

agentDir: string;

177+

mainStore: AuthProfileStore;

178+

profileIds: Set<string>;

179+

now: number;

180+

}): Promise<

181+

{ status: "changed"; removedProfileIds: string[] } | { status: "missing" | "unchanged" }

182+

> {

183+

return await withFileLock(

184+

resolveAuthStorePath(params.agentDir),

185+

AUTH_STORE_LOCK_OPTIONS,

186+

async () => {

187+

const store = loadPersistedAuthProfileStore(params.agentDir);

188+

if (!store) {

189+

return { status: "missing" };

190+

}

191+

const result = removeStaleProfilesFromStore({

192+

store,

193+

mainStore: params.mainStore,

194+

profileIds: params.profileIds,

195+

now: params.now,

196+

});

197+

if (result.removedProfileIds.length === 0) {

198+

return { status: "unchanged" };

199+

}

200+

saveAuthProfileStore(result.store, params.agentDir);

201+

return {

202+

status: "changed",

203+

removedProfileIds: result.removedProfileIds,

204+

};

205+

},

206+

);

207+

}

208+154209

export function collectStaleOAuthProfileShadowWarnings(params: {

155210

hits: StaleOAuthProfileShadow[];

156211

doctorFixCommand: string;

@@ -166,7 +221,9 @@ export async function repairStaleOAuthProfileShadows(params: {

166221

env?: NodeJS.ProcessEnv;

167222

now?: number;

168223

}): Promise<{ changes: string[]; warnings: string[] }> {

169-

const hits = await scanStaleOAuthProfileShadows(params);

224+

const env = params.env ?? process.env;

225+

const now = params.now ?? Date.now();

226+

const hits = await scanStaleOAuthProfileShadows({ ...params, env, now });

170227

const changes: string[] = [];

171228

const warnings: string[] = [];

172229

const byAgentDir = new Map<string, StaleOAuthProfileShadow[]>();

@@ -176,18 +233,25 @@ export async function repairStaleOAuthProfileShadows(params: {

176233

byAgentDir.set(hit.agentDir, existing);

177234

}

178235

for (const [agentDir, agentHits] of byAgentDir) {

179-

const store = loadPersistedAuthProfileStore(agentDir);

180-

if (!store) {

236+

const mainStore = loadPersistedAuthProfileStore(resolveDefaultAgentDir({}, env));

237+

if (!mainStore) {

181238

continue;

182239

}

183240

const profileIds = new Set(agentHits.map((hit) => hit.profileId));

184241

try {

185-

saveAuthProfileStore(removeProfilesFromStore(store, profileIds), agentDir);

186-

changes.push(

187-

`Removed stale OAuth auth profile shadow ${formatProfileList(

188-

[...profileIds].toSorted(),

189-

)} from ${shortenHomePath(resolveAuthStorePath(agentDir))}; this agent now inherits main auth.`,

190-

);

242+

const repair = await repairStaleOAuthProfilesForAgent({

243+

agentDir,

244+

mainStore,

245+

profileIds,

246+

now,

247+

});

248+

if (repair.status === "changed") {

249+

changes.push(

250+

`Removed stale OAuth auth profile shadow ${formatProfileList(

251+

repair.removedProfileIds.toSorted(),

252+

)} from ${shortenHomePath(resolveAuthStorePath(agentDir))}; this agent now inherits main auth.`,

253+

);

254+

}

191255

} catch (error) {

192256

warnings.push(

193257

`Failed to remove stale OAuth auth profile shadow from ${shortenHomePath(

@@ -200,5 +264,7 @@ export async function repairStaleOAuthProfileShadows(params: {

200264

}

201265202266

export const __testing = {

267+

removeStaleProfilesFromStore,

268+

repairStaleOAuthProfilesForAgent,

203269

shouldRemoveLocalOAuthShadow,

204270

};