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

推荐订阅源

雷峰网
雷峰网
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
博客园 - 三生石上(FineUI控件)
博客园 - 聂微东
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Hugging Face - Blog
Hugging Face - Blog
Apple Machine Learning Research
Apple Machine Learning Research
博客园 - Franky
MyScale Blog
MyScale Blog
A
About on SuperTechFans
博客园_首页
B
Blog RSS Feed
Martin Fowler
Martin Fowler
大猫的无限游戏
大猫的无限游戏
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Vercel News
Vercel News
C
Check Point Blog
H
Hackread – Cybersecurity News, Data Breaches, AI and More
博客园 - 【当耐特】
M
MIT News - Artificial intelligence
宝玉的分享
宝玉的分享
T
Tailwind CSS Blog
I
InfoQ
罗磊的独立博客

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(e2e): bound tool search gateway proof · openclaw/open...
vincentkoc · 2026-05-27 · via Recent Commits to openclaw:main

@@ -12,6 +12,11 @@ import { startGatewayServer } from "../src/gateway/server.js";

12121313

type Lane = "normal" | "code";

141415+

type FetchJsonOptions = {

16+

fetchImpl?: (url: string, init: RequestInit) => Promise<Response>;

17+

timeoutMs?: number;

18+

};

19+1520

type LaneResult = {

1621

lane: Lane;

1722

status: string;

@@ -26,13 +31,26 @@ type LaneResult = {

2631

};

27322833

const FAKE_PLUGIN_ID = "tool-search-e2e-fixture";

34+

const DEFAULT_FETCH_TIMEOUT_MS = readPositiveInt(

35+

process.env.OPENCLAW_TOOL_SEARCH_GATEWAY_E2E_FETCH_TIMEOUT_MS,

36+

180_000,

37+

);

29383039

function assert(condition: unknown, message: string): asserts condition {

3140

if (!condition) {

3241

throw new Error(message);

3342

}

3443

}

354445+

function readPositiveInt(raw: string | undefined, fallback: number) {

46+

const parsed = Number.parseInt(raw ?? "", 10);

47+

return Number.isInteger(parsed) && parsed > 0 ? parsed : fallback;

48+

}

49+50+

function timeoutError(message: string) {

51+

return Object.assign(new Error(message), { code: "ETIMEDOUT" });

52+

}

53+3654

async function freePort(): Promise<number> {

3755

return await new Promise((resolve, reject) => {

3856

const server = net.createServer();

@@ -111,9 +129,39 @@ async function readSessionLogMentions(params: {

111129

return mentions;

112130

}

113131114-

async function fetchJson(url: string, init?: RequestInit): Promise<unknown> {

115-

const response = await fetch(url, init);

116-

const text = await response.text();

132+

export async function fetchJson(

133+

url: string,

134+

init: RequestInit = {},

135+

options: FetchJsonOptions = {},

136+

): Promise<unknown> {

137+

const timeoutMs = Math.max(1, options.timeoutMs ?? DEFAULT_FETCH_TIMEOUT_MS);

138+

const controller = new AbortController();

139+

const error = timeoutError(`HTTP request to ${url} timed out after ${timeoutMs}ms`);

140+

let timeout: NodeJS.Timeout | undefined;

141+

const timeoutPromise = new Promise<never>((_, reject) => {

142+

timeout = setTimeout(() => {

143+

controller.abort(error);

144+

reject(error);

145+

}, timeoutMs);

146+

timeout.unref?.();

147+

});

148+149+

let response: Response;

150+

let text: string;

151+

try {

152+

response = await Promise.race([

153+

(options.fetchImpl ?? fetch)(url, {

154+

...init,

155+

signal: controller.signal,

156+

}),

157+

timeoutPromise,

158+

]);

159+

text = await Promise.race([response.text(), timeoutPromise]);

160+

} finally {

161+

if (timeout) {

162+

clearTimeout(timeout);

163+

}

164+

}

117165

let parsed: unknown;

118166

try {

119167

parsed = text ? JSON.parse(text) : {};

@@ -213,6 +261,38 @@ async function writeConfig(params: {

213261

controlUiEnabled: false,

214262

providerMode: "mock-openai",

215263

});

264+

const defaults = cfg.agents?.defaults ?? {};

265+

cfg = {

266+

...cfg,

267+

plugins: {

268+

allow: [FAKE_PLUGIN_ID],

269+

slots: {

270+

...cfg.plugins?.slots,

271+

memory: "none",

272+

},

273+

entries: {

274+

[FAKE_PLUGIN_ID]: {

275+

enabled: true,

276+

},

277+

},

278+

},

279+

agents: {

280+

...cfg.agents,

281+

defaults: {

282+

...defaults,

283+

memorySearch: {

284+

...defaults.memorySearch,

285+

enabled: false,

286+

sync: {

287+

...defaults.memorySearch?.sync,

288+

onSearch: false,

289+

onSessionStart: false,

290+

watch: false,

291+

},

292+

},

293+

},

294+

},

295+

};

216296

cfg = {

217297

...cfg,

218298

tools: {

@@ -472,17 +552,18 @@ async function runLane(params: {

472552

}

473553

}

474554475-

async function main() {

555+

export async function main() {

476556

const rootDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-tool-search-"));

477-

const provider = await startQaMockOpenAiServer();

478-

const fakeTools = buildFakeTools();

479-

const fakePluginDir = await writeFakePlugin({

480-

rootDir,

481-

repoRoot: process.cwd(),

482-

fakeTools,

483-

});

484-

const targetTool = "fake_plugin_tool_17";

557+

let provider: Awaited<ReturnType<typeof startQaMockOpenAiServer>> | undefined;

485558

try {

559+

provider = await startQaMockOpenAiServer();

560+

const fakeTools = buildFakeTools();

561+

const fakePluginDir = await writeFakePlugin({

562+

rootDir,

563+

repoRoot: process.cwd(),

564+

fakeTools,

565+

});

566+

const targetTool = "fake_plugin_tool_17";

486567

const normal = await runLane({

487568

lane: "normal",

488569

rootDir,

@@ -541,8 +622,11 @@ async function main() {

541622

};

542623

process.stdout.write(`${JSON.stringify(summary, null, 2)}\n`);

543624

} finally {

544-

await provider.stop();

625+

await provider?.stop();

626+

await fs.rm(rootDir, { force: true, recursive: true });

545627

}

546628

}

547629548-

await main();

630+

if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {

631+

await main();

632+

}