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

推荐订阅源

爱范儿
爱范儿
腾讯CDC
博客园 - 司徒正美
A
About on SuperTechFans
H
Help Net Security
J
Java Code Geeks
C
Check Point Blog
B
Blog RSS Feed
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
MongoDB | Blog
MongoDB | Blog
U
Unit 42
Hugging Face - Blog
Hugging Face - Blog
Last Week in AI
Last Week in AI
MyScale Blog
MyScale Blog
V
Visual Studio Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
I
InfoQ
H
Hackread – Cybersecurity News, Data Breaches, AI and More
F
Fortinet All Blogs
博客园 - 聂微东
酷 壳 – CoolShell
酷 壳 – CoolShell
GbyAI
GbyAI
博客园 - 【当耐特】
雷峰网
雷峰网

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(test): avoid scanning bundled plugin naming guardrail...
vincentkoc · 2026-05-16 · via Recent Commits to openclaw:main

@@ -1,6 +1,7 @@

1+

import { spawnSync } from "node:child_process";

12

import fs from "node:fs";

23

import path from "node:path";

3-

import { describe, expect, it } from "vitest";

4+

import { describe, expect, it, vi } from "vitest";

4556

type PluginManifestShape = {

67

id?: unknown;

@@ -54,10 +55,100 @@ function normalizeText(value: unknown): string | undefined {

5455

return trimmed || undefined;

5556

}

565758+

function listBundledPluginDirs(): string[] {

59+

const externalDirs = listExternalBundledPluginDirs();

60+

if (externalDirs) {

61+

return externalDirs;

62+

}

63+

return fs.readdirSync(EXTENSIONS_ROOT).toSorted();

64+

}

65+66+

function listExternalBundledPluginDirs(): string[] | null {

67+

const files =

68+

listGitPluginMetadataFiles() ??

69+

listFindPluginMetadataFiles();

70+

if (!files) {

71+

return null;

72+

}

73+74+

const metadataByDir = new Map<string, Set<string>>();

75+

for (const file of files) {

76+

const match = /^extensions\/([^/]+)\/(openclaw\.plugin\.json|package\.json)$/u.exec(file);

77+

if (!match) {

78+

continue;

79+

}

80+

const [, dirName, fileName] = match;

81+

const metadataFiles = metadataByDir.get(dirName) ?? new Set<string>();

82+

metadataFiles.add(fileName);

83+

metadataByDir.set(dirName, metadataFiles);

84+

}

85+86+

return [...metadataByDir.entries()]

87+

.filter(([, metadataFiles]) =>

88+

metadataFiles.has("package.json") && metadataFiles.has("openclaw.plugin.json"),

89+

)

90+

.map(([dirName]) => dirName)

91+

.toSorted();

92+

}

93+94+

function listGitPluginMetadataFiles(): string[] | null {

95+

const result = spawnSync(

96+

"git",

97+

["ls-files", "--", "extensions/*/package.json", "extensions/*/openclaw.plugin.json"],

98+

{

99+

cwd: process.cwd(),

100+

encoding: "utf8",

101+

maxBuffer: 1024 * 1024,

102+

stdio: ["ignore", "pipe", "ignore"],

103+

},

104+

);

105+

if (result.status !== 0) {

106+

return null;

107+

}

108+

return result.stdout

109+

.split("\n")

110+

.map((line) => line.trim())

111+

.filter((line) => line.length > 0)

112+

.toSorted();

113+

}

114+115+

function listFindPluginMetadataFiles(): string[] | null {

116+

const result = spawnSync(

117+

"find",

118+

[

119+

EXTENSIONS_ROOT,

120+

"-maxdepth",

121+

"2",

122+

"-type",

123+

"f",

124+

"(",

125+

"-name",

126+

"package.json",

127+

"-o",

128+

"-name",

129+

"openclaw.plugin.json",

130+

")",

131+

],

132+

{

133+

cwd: process.cwd(),

134+

encoding: "utf8",

135+

maxBuffer: 1024 * 1024,

136+

stdio: ["ignore", "pipe", "ignore"],

137+

},

138+

);

139+

if (result.status !== 0) {

140+

return null;

141+

}

142+

return result.stdout

143+

.split("\n")

144+

.map((line) => line.trim())

145+

.filter((line) => line.length > 0)

146+

.map((file) => path.relative(process.cwd(), file).split(path.sep).join("/"))

147+

.toSorted();

148+

}

149+57150

function readBundledPluginRecords(): BundledPluginRecord[] {

58-

return fs

59-

.readdirSync(EXTENSIONS_ROOT)

60-

.toSorted()

151+

return listBundledPluginDirs()

61152

.flatMap((dirName) => {

62153

const rootDir = path.join(EXTENSIONS_ROOT, dirName);

63154

const packagePath = path.join(rootDir, "package.json");

@@ -108,6 +199,19 @@ function expectNoBundledPluginNamingMismatches(params: {

108199

}

109200110201

describe("bundled plugin naming guardrails", () => {

202+

it("lists bundled plugin metadata without scanning extension directories in-process", () => {

203+

const readDir = vi.spyOn(fs, "readdirSync");

204+

try {

205+

const records = readBundledPluginRecords();

206+207+

expect(records.length).toBeGreaterThan(0);

208+

expect(records.every((record) => record.dirName.length > 0)).toBe(true);

209+

expect(readDir).not.toHaveBeenCalled();

210+

} finally {

211+

readDir.mockRestore();

212+

}

213+

});

214+111215

it.each([

112216

{

113217

name: "keeps bundled workspace package names anchored to the plugin id",