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

推荐订阅源

Engineering at Meta
Engineering at Meta
博客园_首页
J
Java Code Geeks
Jina AI
Jina AI
B
Blog RSS Feed
量子位
有赞技术团队
有赞技术团队
M
MIT News - Artificial intelligence
L
LangChain Blog
Microsoft Security Blog
Microsoft Security Blog
小众软件
小众软件
博客园 - 聂微东
月光博客
月光博客
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
博客园 - 三生石上(FineUI控件)
Last Week in AI
Last Week in AI
MongoDB | Blog
MongoDB | Blog
I
InfoQ
罗磊的独立博客
H
Hackread – Cybersecurity News, Data Breaches, AI and More
爱范儿
爱范儿
Y
Y Combinator Blog
Vercel News
Vercel News
雷峰网
雷峰网

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
test: fix plugin registry CI contracts · openclaw/opencla...
steipete · 2026-04-27 · via Recent Commits to openclaw:main

@@ -1,7 +1,11 @@

11

import fs from "node:fs";

2+

import path from "node:path";

3+

import { resolveCompatibilityHostVersion } from "../version.js";

4+

import { resolveBundledPluginsDir } from "./bundled-dir.js";

25

import {

36

inspectPersistedInstalledPluginIndex,

47

readPersistedInstalledPluginIndexSync,

8+

resolveInstalledPluginIndexStorePath,

59

refreshPersistedInstalledPluginIndex,

610

type InstalledPluginIndexStoreInspection,

711

type InstalledPluginIndexStoreOptions,

@@ -18,6 +22,7 @@ import {

1822

type LoadInstalledPluginIndexParams,

1923

type RefreshInstalledPluginIndexParams,

2024

} from "./installed-plugin-index.js";

25+

import { resolvePluginCacheInputs } from "./roots.js";

21262227

export type PluginRegistrySnapshot = InstalledPluginIndex;

2328

export type PluginRegistryRecord = InstalledPluginIndexRecord;

@@ -41,6 +46,16 @@ export type PluginRegistrySnapshotResult = {

4146

diagnostics: readonly PluginRegistrySnapshotDiagnostic[];

4247

};

434849+

const DERIVED_SNAPSHOT_CACHE_MS = 1000;

50+

const derivedSnapshotCache = new Map<

51+

string,

52+

{ expiresAt: number; result: PluginRegistrySnapshotResult }

53+

>();

54+55+

export function clearPluginRegistrySnapshotCache(): void {

56+

derivedSnapshotCache.clear();

57+

}

58+4459

export const DISABLE_PERSISTED_PLUGIN_REGISTRY_ENV = "OPENCLAW_DISABLE_PERSISTED_PLUGIN_REGISTRY";

45604661

function formatDeprecatedPersistedRegistryDisableWarning(): string {

@@ -76,6 +91,68 @@ function hasMissingPersistedPluginSource(index: InstalledPluginIndex): boolean {

7691

});

7792

}

789394+

function resolveComparablePath(filePath: string): string {

95+

try {

96+

return fs.realpathSync(filePath);

97+

} catch {

98+

return path.resolve(filePath);

99+

}

100+

}

101+102+

function isPathInsideOrEqual(childPath: string, parentPath: string): boolean {

103+

const relative = path.relative(

104+

resolveComparablePath(parentPath),

105+

resolveComparablePath(childPath),

106+

);

107+

return relative === "" || (!relative.startsWith("..") && !path.isAbsolute(relative));

108+

}

109+110+

function hasMismatchedPersistedBundledPluginRoot(

111+

index: InstalledPluginIndex,

112+

env: NodeJS.ProcessEnv,

113+

): boolean {

114+

const bundledPluginsDir = resolveBundledPluginsDir(env);

115+

if (!bundledPluginsDir) {

116+

return false;

117+

}

118+

return index.plugins.some(

119+

(plugin) =>

120+

plugin.origin === "bundled" && !isPathInsideOrEqual(plugin.rootDir, bundledPluginsDir),

121+

);

122+

}

123+124+

function resolveDerivedSnapshotCacheKey(

125+

params: LoadPluginRegistryParams,

126+

env: NodeJS.ProcessEnv,

127+

): string | null {

128+

if (

129+

params.cache === false ||

130+

params.preferPersisted === false ||

131+

params.config ||

132+

params.workspaceDir ||

133+

params.stateDir ||

134+

params.filePath ||

135+

params.pluginIndexFilePath ||

136+

params.installRecords ||

137+

params.candidates ||

138+

params.diagnostics ||

139+

params.now

140+

) {

141+

return null;

142+

}

143+144+

const { roots, loadPaths } = resolvePluginCacheInputs({ env });

145+

return JSON.stringify({

146+

persistedStore: resolveInstalledPluginIndexStorePath({ env }),

147+

roots,

148+

loadPaths,

149+

hostContractVersion: resolveCompatibilityHostVersion(env),

150+

disablePersisted: env[DISABLE_PERSISTED_PLUGIN_REGISTRY_ENV] ?? "",

151+

disableBundled: env.OPENCLAW_DISABLE_BUNDLED_PLUGINS ?? "",

152+

vitest: env.VITEST ?? "",

153+

});

154+

}

155+79156

export function loadPluginRegistrySnapshotWithMetadata(

80157

params: LoadPluginRegistryParams = {},

81158

): PluginRegistrySnapshotResult {

@@ -93,6 +170,15 @@ export function loadPluginRegistrySnapshotWithMetadata(

93170

const disabledByEnv = hasEnvFlag(env, DISABLE_PERSISTED_PLUGIN_REGISTRY_ENV);

94171

const persistedReadsEnabled = !disabledByCaller && !disabledByEnv;

95172

const persistedInstallRecordReadsEnabled = !disabledByEnv;

173+

const derivedCacheKey = persistedReadsEnabled

174+

? resolveDerivedSnapshotCacheKey(params, env)

175+

: null;

176+

if (derivedCacheKey) {

177+

const cached = derivedSnapshotCache.get(derivedCacheKey);

178+

if (cached && cached.expiresAt > Date.now()) {

179+

return cached.result;

180+

}

181+

}

96182

let persistedIndex: InstalledPluginIndex | null = null;

97183

if (persistedInstallRecordReadsEnabled) {

98184

persistedIndex = readPersistedInstalledPluginIndexSync(params);

@@ -114,6 +200,13 @@ export function loadPluginRegistrySnapshotWithMetadata(

114200

message:

115201

"Persisted plugin registry points at missing plugin files; using derived plugin index. Run `openclaw plugins registry --refresh` to update the persisted registry.",

116202

});

203+

} else if (hasMismatchedPersistedBundledPluginRoot(persistedIndex, env)) {

204+

diagnostics.push({

205+

level: "warn",

206+

code: "persisted-registry-stale-source",

207+

message:

208+

"Persisted plugin registry points at a different bundled plugin tree; using derived plugin index. Run `openclaw plugins registry --refresh` to update the persisted registry.",

209+

});

117210

} else {

118211

return {

119212

snapshot: persistedIndex,

@@ -138,7 +231,7 @@ export function loadPluginRegistrySnapshotWithMetadata(

138231

});

139232

}

140233141-

return {

234+

const result: PluginRegistrySnapshotResult = {

142235

snapshot: loadInstalledPluginIndex({

143236

...params,

144237

installRecords:

@@ -148,6 +241,13 @@ export function loadPluginRegistrySnapshotWithMetadata(

148241

source: "derived",

149242

diagnostics,

150243

};

244+

if (derivedCacheKey) {

245+

derivedSnapshotCache.set(derivedCacheKey, {

246+

expiresAt: Date.now() + DERIVED_SNAPSHOT_CACHE_MS,

247+

result,

248+

});

249+

}

250+

return result;

151251

}

152252153253

function resolveSnapshot(params: LoadPluginRegistryParams = {}): PluginRegistrySnapshot {