Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
49 changes: 49 additions & 0 deletions src/sdk-mock.js
Original file line number Diff line number Diff line change
Expand Up @@ -530,13 +530,62 @@ 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);
}
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);
Expand Down
35 changes: 35 additions & 0 deletions test/inspector.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down
Loading