From 8d5a012c27a637b862e2fd3164b1c1c8546e9c7e Mon Sep 17 00:00:00 2001 From: Vincent Koc <25068+vincentkoc@users.noreply.github.com> Date: Wed, 17 Jun 2026 18:57:32 +0800 Subject: [PATCH] fix(mock): support Lark SDK capture --- CHANGELOG.md | 4 ++++ src/sdk-mock.js | 49 ++++++++++++++++++++++++++++++++++++++++++ test/inspector.test.js | 35 ++++++++++++++++++++++++++++++ 3 files changed, 88 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 28e7eec..93444f2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +### Fixed + +- Mock the Lark SDK HTTP interceptor surface during runtime capture so Feishu plugin bundles load successfully. + ## 0.3.15 - 2026-06-12 ### Fixed diff --git a/src/sdk-mock.js b/src/sdk-mock.js index 505ee63..44d7f90 100644 --- a/src/sdk-mock.js +++ b/src/sdk-mock.js @@ -530,6 +530,9 @@ export default ${options.zod ? "createZNamespace()" : 'createMockValue("default" } function externalMockModuleSource(specifier, exportNames) { + if (specifier === "@larksuiteoapi/node-sdk") { + return larkSdkMockModuleSource(exportNames); + } const names = new Set([...exportNames].filter(isValidExportName)); if (specifier === "zod") { addZodExports(names); @@ -537,6 +540,52 @@ function externalMockModuleSource(specifier, exportNames) { return dynamicMockModuleSource(names, { zod: specifier === "zod" }); } +function larkSdkMockModuleSource(exportNames) { + const larkExports = new Set([ + "AppType", + "Client", + "Domain", + "EventDispatcher", + "LoggerLevel", + "WSClient", + ...exportNames, + ]); + larkExports.delete("defaultHttpInstance"); + return `${genericMockRuntimeSource()} +const requestInterceptors = { + handlers: [], + use(handler) { + this.handlers.push(handler); + return this.handlers.length - 1; + }, +}; + +export const defaultHttpInstance = { + interceptors: { + request: requestInterceptors, + response: { + handlers: [], + use(handler) { + this.handlers.push(handler); + return this.handlers.length - 1; + }, + }, + }, + request: createMockValue("defaultHttpInstance.request"), + get: createMockValue("defaultHttpInstance.get"), + post: createMockValue("defaultHttpInstance.post"), + put: createMockValue("defaultHttpInstance.put"), + patch: createMockValue("defaultHttpInstance.patch"), + delete: createMockValue("defaultHttpInstance.delete"), + head: createMockValue("defaultHttpInstance.head"), + options: createMockValue("defaultHttpInstance.options"), +}; +${[...larkExports].filter(isValidExportName).map(genericExportStatement).join("\n")} + +export default createMockValue("default"); +`; +} + function addZodExports(names) { for (const name of ["z", "any", "array", "boolean", "enum", "literal", "number", "object", "record", "string", "unknown"]) { names.add(name); diff --git a/test/inspector.test.js b/test/inspector.test.js index 5863aeb..b36fdb0 100644 --- a/test/inspector.test.js +++ b/test/inspector.test.js @@ -330,6 +330,41 @@ test("capture entrypoint can mock OpenClaw plugin SDK imports", async () => { ); }); +test("mock capture supports the Lark SDK namespace import used by Feishu", async () => { + const dir = await mkdtemp(path.join(os.tmpdir(), "plugin-inspector-mock-sdk-lark-")); + const entrypoint = path.join(dir, "index.mjs"); + await writeFile( + entrypoint, + [ + 'import * as Lark from "@larksuiteoapi/node-sdk";', + 'import { generateChallenge } from "@larksuiteoapi/node-sdk";', + "", + "const inst = Lark.defaultHttpInstance;", + "inst.interceptors.request.handlers = [];", + "inst.interceptors.request.use((request) => request);", + "if (inst.interceptors.request.handlers.length !== 1) throw new Error('expected request interceptor');", + "if (!Lark.Client || !Lark.WSClient || !Lark.EventDispatcher) throw new Error('expected Lark SDK exports');", + "generateChallenge('fixture');", + "", + "export default {", + " register(api) {", + " api.registerTool({ name: 'lark_fixture_tool', run() {} });", + " },", + "};", + ].join("\n"), + "utf8", + ); + + const result = await captureEntrypoint("index.mjs", { + cwd: dir, + pluginRoot: dir, + mockSdk: true, + }); + + assert.equal(result.status, "captured"); + assert.deepEqual(result.captured.map((item) => `${item.kind}:${item.name}`), ["registration:registerTool"]); +}); + test("mock capture ignores type-only root SDK imports", async () => { const dir = await mkdtemp(path.join(os.tmpdir(), "plugin-inspector-mock-sdk-type-import-")); const entrypoint = path.join(dir, "index.ts");