






















@@ -24,15 +24,26 @@ let registerFeishuPermTools: typeof import("./perm.js").registerFeishuPermTools;
2424let registerFeishuWikiTools: typeof import("./wiki.js").registerFeishuWikiTools;
25252626function createConfig(params: {
27+topTools?: {
28+wiki?: boolean;
29+drive?: boolean;
30+perm?: boolean;
31+bitable?: boolean;
32+base?: boolean;
33+};
2734toolsA?: {
2835wiki?: boolean;
2936drive?: boolean;
3037perm?: boolean;
38+bitable?: boolean;
39+base?: boolean;
3140};
3241toolsB?: {
3342wiki?: boolean;
3443drive?: boolean;
3544perm?: boolean;
45+bitable?: boolean;
46+base?: boolean;
3647};
3748defaultAccount?: string;
3849}): OpenClawPluginApi["config"] {
@@ -41,6 +52,7 @@ function createConfig(params: {
4152feishu: {
4253enabled: true,
4354defaultAccount: params.defaultAccount,
55+tools: params.topTools,
4456accounts: {
4557a: {
4658appId: "app-a",
@@ -190,6 +202,21 @@ describe("feishu tool account routing", () => {
190202expect(lastClientAppId()).toBe("app-b");
191203});
192204205+test("bitable tool registers when only second account enables it and routes to agentAccountId", async () => {
206+const { api, resolveTool } = createToolFactoryHarness(
207+createConfig({
208+toolsA: { bitable: false },
209+toolsB: { bitable: true },
210+}),
211+);
212+registerFeishuBitableTools(api);
213+214+const tool = resolveTool("feishu_bitable_get_meta", { agentAccountId: "b" });
215+await tool.execute("call", { url: "invalid-url" });
216+217+expect(createFeishuClientMock.mock.calls.at(-1)?.[0]?.appId).toBe("app-b");
218+});
219+193220test("bitable tool routes to agentAccountId and allows explicit accountId override", async () => {
194221const { api, resolveTool } = createToolFactoryHarness(createConfig({}));
195222registerFeishuBitableTools(api);
@@ -202,6 +229,81 @@ describe("feishu tool account routing", () => {
202229expect(clientAppIdAt(1)).toBe("app-a");
203230});
204231232+test("bitable tools are not registered when top-level bitable config disables them", async () => {
233+const { api, registered, resolveTool } = createToolFactoryHarness(
234+createConfig({
235+topTools: { bitable: false },
236+}),
237+);
238+registerFeishuBitableTools(api);
239+240+expect(
241+registered.filter((entry) => entry.opts?.name?.startsWith("feishu_bitable_")).length,
242+).toBe(0);
243+expect(() => resolveTool("feishu_bitable_get_meta")).toThrow("Tool not registered");
244+});
245+246+test("top-level bitable disable wins over account-level bitable enable", async () => {
247+const { api, registered, resolveTool } = createToolFactoryHarness(
248+createConfig({
249+topTools: { bitable: false },
250+toolsA: { bitable: true },
251+toolsB: { bitable: true },
252+}),
253+);
254+registerFeishuBitableTools(api);
255+256+expect(
257+registered.filter((entry) => entry.opts?.name?.startsWith("feishu_bitable_")).length,
258+).toBe(0);
259+expect(() => resolveTool("feishu_bitable_get_meta")).toThrow("Tool not registered");
260+});
261+262+test("top-level base alias disable wins over account-level bitable enable", async () => {
263+const { api, registered } = createToolFactoryHarness(
264+createConfig({
265+topTools: { base: false },
266+toolsA: { bitable: true },
267+toolsB: { bitable: true },
268+}),
269+);
270+registerFeishuBitableTools(api);
271+272+expect(
273+registered.filter((entry) => entry.opts?.name?.startsWith("feishu_bitable_")).length,
274+).toBe(0);
275+});
276+277+test("bitable tools are not registered when account bitable configs disable them", async () => {
278+const { api, registered, resolveTool } = createToolFactoryHarness(
279+createConfig({
280+toolsA: { bitable: false },
281+toolsB: { bitable: false },
282+}),
283+);
284+registerFeishuBitableTools(api);
285+286+expect(
287+registered.filter((entry) => entry.opts?.name?.startsWith("feishu_bitable_")).length,
288+).toBe(0);
289+expect(() => resolveTool("feishu_bitable_get_meta")).toThrow("Tool not registered");
290+});
291+292+test("base alias disables bitable tool registration", async () => {
293+const { api, registered } = createToolFactoryHarness(
294+createConfig({
295+topTools: { base: false },
296+toolsA: { base: false },
297+toolsB: { base: false },
298+}),
299+);
300+registerFeishuBitableTools(api);
301+302+expect(
303+registered.filter((entry) => entry.opts?.name?.startsWith("feishu_bitable_")).length,
304+).toBe(0);
305+});
306+205307test("falls back to the configured Feishu default selection when agentAccountId is not a real account", async () => {
206308const { api, resolveTool } = createToolFactoryHarness(
207309createConfig({
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。