
























@@ -192,6 +192,138 @@ describe("PlaywrightDiffScreenshotter", () => {
192192});
193193194194describe("diffs plugin registration", () => {
195+it("uses live runtime tool config through the registered tool factory", async () => {
196+type RegisteredTool = {
197+execute?: (toolCallId: string, params: Record<string, unknown>) => Promise<unknown>;
198+};
199+type HttpRouteHandler = (
200+req: IncomingMessage,
201+res: ServerResponse,
202+) => boolean | Promise<boolean>;
203+type RegisteredHttpRouteParams = Parameters<OpenClawPluginApi["registerHttpRoute"]>[0];
204+205+let registeredToolFactory:
206+| ((ctx: OpenClawPluginToolContext) => RegisteredTool | RegisteredTool[] | null | undefined)
207+| undefined;
208+let registeredHttpRouteHandler: HttpRouteHandler | undefined;
209+let configFile: OpenClawConfig = {
210+gateway: {
211+port: 18789,
212+bind: "loopback",
213+},
214+plugins: {
215+entries: {
216+diffs: {
217+config: {
218+viewerBaseUrl: "https://startup.example.com/openclaw",
219+defaults: {
220+mode: "view",
221+theme: "light",
222+background: false,
223+layout: "split",
224+showLineNumbers: false,
225+diffIndicators: "classic",
226+lineSpacing: 2,
227+},
228+},
229+},
230+},
231+},
232+} as OpenClawConfig;
233+234+const api = createTestPluginApi({
235+id: "diffs",
236+name: "Diffs",
237+description: "Diffs",
238+source: "test",
239+config: {
240+gateway: {
241+port: 18789,
242+bind: "loopback",
243+},
244+},
245+pluginConfig: {
246+viewerBaseUrl: "https://startup.example.com/openclaw",
247+defaults: {
248+mode: "view",
249+theme: "light",
250+background: false,
251+layout: "split",
252+showLineNumbers: false,
253+diffIndicators: "classic",
254+lineSpacing: 2,
255+},
256+},
257+runtime: {
258+config: {
259+loadConfig: () => configFile,
260+},
261+} as never,
262+registerTool(tool: Parameters<OpenClawPluginApi["registerTool"]>[0]) {
263+registeredToolFactory = typeof tool === "function" ? tool : () => tool;
264+},
265+registerHttpRoute(params: RegisteredHttpRouteParams) {
266+registeredHttpRouteHandler = params.handler as HttpRouteHandler;
267+},
268+on: vi.fn(),
269+});
270+271+registerDiffsPlugin(api as unknown as OpenClawPluginApi);
272+273+configFile = {
274+ ...configFile,
275+plugins: {
276+entries: {
277+diffs: {
278+config: {
279+viewerBaseUrl: "https://live.example.com/gateway",
280+defaults: {
281+mode: "view",
282+theme: "dark",
283+background: true,
284+layout: "unified",
285+showLineNumbers: true,
286+diffIndicators: "bars",
287+lineSpacing: 1.6,
288+},
289+},
290+},
291+},
292+},
293+} as OpenClawConfig;
294+295+const registeredTool = registeredToolFactory?.({
296+agentId: "main",
297+sessionId: "session-456",
298+messageChannel: "discord",
299+agentAccountId: "default",
300+}) as RegisteredTool | undefined;
301+const result = await registeredTool?.execute?.("tool-1", {
302+before: "one\n",
303+after: "two\n",
304+});
305+const details = (result as { details?: Record<string, unknown> } | undefined)?.details;
306+const viewerPath = String(details?.viewerPath);
307+const res = createMockServerResponse();
308+const handled = await registeredHttpRouteHandler?.(
309+localReq({
310+method: "GET",
311+url: viewerPath,
312+}),
313+res,
314+);
315+316+expect(handled).toBe(true);
317+expect(String(details?.viewerUrl)).toContain("https://live.example.com/gateway");
318+expect(res.statusCode).toBe(200);
319+expect(String(res.body)).toContain('body data-theme="dark"');
320+expect(String(res.body)).toContain('"backgroundEnabled":true');
321+expect(String(res.body)).toContain('"diffStyle":"unified"');
322+expect(String(res.body)).toContain('"disableLineNumbers":false');
323+expect(String(res.body)).toContain('"diffIndicators":"bars"');
324+expect(String(res.body)).toContain("--diffs-line-height: 24px;");
325+});
326+195327it("uses live runtime viewer-access config through the registered HTTP handler", async () => {
196328type RegisteredTool = {
197329execute?: (toolCallId: string, params: Record<string, unknown>) => Promise<unknown>;
@@ -299,12 +431,6 @@ describe("diffs plugin registration", () => {
299431300432expect(handled).toBe(true);
301433expect(res.statusCode).toBe(200);
302-expect(String(res.body)).toContain('body data-theme="light"');
303-expect(String(res.body)).toContain('"backgroundEnabled":false');
304-expect(String(res.body)).toContain('"diffStyle":"split"');
305-expect(String(res.body)).toContain('"disableLineNumbers":true');
306-expect(String(res.body)).toContain('"diffIndicators":"classic"');
307-expect(String(res.body)).toContain("--diffs-line-height: 30px;");
308434expect((result as { details?: Record<string, unknown> } | undefined)?.details?.context).toEqual(
309435{
310436agentId: "main",
@@ -344,6 +470,107 @@ describe("diffs plugin registration", () => {
344470expect(proxiedHandled).toBe(true);
345471expect(proxiedRes.statusCode).toBe(404);
346472});
473+474+it("fails closed for remote viewer access when the live diffs plugin entry is removed", async () => {
475+type RegisteredTool = {
476+execute?: (toolCallId: string, params: Record<string, unknown>) => Promise<unknown>;
477+};
478+type HttpRouteHandler = (
479+req: IncomingMessage,
480+res: ServerResponse,
481+) => boolean | Promise<boolean>;
482+type RegisteredHttpRouteParams = Parameters<OpenClawPluginApi["registerHttpRoute"]>[0];
483+484+let registeredToolFactory:
485+| ((ctx: OpenClawPluginToolContext) => RegisteredTool | RegisteredTool[] | null | undefined)
486+| undefined;
487+let registeredHttpRouteHandler: HttpRouteHandler | undefined;
488+let configFile: OpenClawConfig = {
489+gateway: {
490+port: 18789,
491+bind: "loopback",
492+},
493+plugins: {
494+entries: {
495+diffs: {
496+config: {
497+security: {
498+allowRemoteViewer: true,
499+},
500+},
501+},
502+},
503+},
504+} as OpenClawConfig;
505+506+const api = createTestPluginApi({
507+id: "diffs",
508+name: "Diffs",
509+description: "Diffs",
510+source: "test",
511+config: {
512+gateway: {
513+port: 18789,
514+bind: "loopback",
515+},
516+},
517+pluginConfig: {
518+security: {
519+allowRemoteViewer: true,
520+},
521+},
522+runtime: {
523+config: {
524+loadConfig: () => configFile,
525+},
526+} as never,
527+registerTool(tool: Parameters<OpenClawPluginApi["registerTool"]>[0]) {
528+registeredToolFactory = typeof tool === "function" ? tool : () => tool;
529+},
530+registerHttpRoute(params: RegisteredHttpRouteParams) {
531+registeredHttpRouteHandler = params.handler as HttpRouteHandler;
532+},
533+on: vi.fn(),
534+});
535+536+registerDiffsPlugin(api as unknown as OpenClawPluginApi);
537+538+const registeredTool = registeredToolFactory?.({
539+agentId: "main",
540+sessionId: "session-789",
541+messageChannel: "discord",
542+agentAccountId: "default",
543+}) as RegisteredTool | undefined;
544+const result = await registeredTool?.execute?.("tool-1", {
545+before: "one\n",
546+after: "two\n",
547+});
548+const viewerPath = String(
549+(result as { details?: Record<string, unknown> } | undefined)?.details?.viewerPath,
550+);
551+552+configFile = {
553+ ...configFile,
554+plugins: {
555+entries: {},
556+},
557+} as OpenClawConfig;
558+559+const proxiedRes = createMockServerResponse();
560+const proxiedHandled = await registeredHttpRouteHandler?.(
561+localReq({
562+method: "GET",
563+url: viewerPath,
564+headers: {
565+"x-forwarded-for": "203.0.113.10",
566+},
567+}),
568+proxiedRes,
569+);
570+571+expect(proxiedHandled).toBe(true);
572+expect(proxiedRes.statusCode).toBe(404);
573+});
347574});
348575349576function createConfig(): OpenClawConfig {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。