fix(feishu): route chat tool through merged account config
This commit is contained in:
parent
6a418ca784
commit
466b8ad0e6
@ -1,5 +1,6 @@
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { registerFeishuChatTools } from "./chat.js";
|
||||
import { createToolFactoryHarness } from "./tool-factory-test-harness.js";
|
||||
|
||||
const createFeishuClientMock = vi.hoisted(() => vi.fn());
|
||||
const chatGetMock = vi.hoisted(() => vi.fn());
|
||||
@ -25,8 +26,7 @@ describe("registerFeishuChatTools", () => {
|
||||
});
|
||||
|
||||
it("registers feishu_chat and handles info/members actions", async () => {
|
||||
const registerTool = vi.fn();
|
||||
registerFeishuChatTools({
|
||||
const { api, resolveTool } = createToolFactoryHarness({
|
||||
config: {
|
||||
channels: {
|
||||
feishu: {
|
||||
@ -37,13 +37,10 @@ describe("registerFeishuChatTools", () => {
|
||||
},
|
||||
},
|
||||
} as any,
|
||||
logger: { debug: vi.fn(), info: vi.fn() } as any,
|
||||
registerTool,
|
||||
} as any);
|
||||
});
|
||||
registerFeishuChatTools(api);
|
||||
|
||||
expect(registerTool).toHaveBeenCalledTimes(1);
|
||||
const tool = registerTool.mock.calls[0]?.[0];
|
||||
expect(tool?.name).toBe("feishu_chat");
|
||||
const tool = resolveTool("feishu_chat");
|
||||
|
||||
chatGetMock.mockResolvedValueOnce({
|
||||
code: 0,
|
||||
@ -97,8 +94,7 @@ describe("registerFeishuChatTools", () => {
|
||||
});
|
||||
|
||||
it("skips registration when chat tool is disabled", () => {
|
||||
const registerTool = vi.fn();
|
||||
registerFeishuChatTools({
|
||||
const { api, resolveTool } = createToolFactoryHarness({
|
||||
config: {
|
||||
channels: {
|
||||
feishu: {
|
||||
@ -109,9 +105,8 @@ describe("registerFeishuChatTools", () => {
|
||||
},
|
||||
},
|
||||
} as any,
|
||||
logger: { debug: vi.fn(), info: vi.fn() } as any,
|
||||
registerTool,
|
||||
} as any);
|
||||
expect(registerTool).not.toHaveBeenCalled();
|
||||
});
|
||||
registerFeishuChatTools(api);
|
||||
expect(() => resolveTool("feishu_chat")).toThrow("Tool not registered: feishu_chat");
|
||||
});
|
||||
});
|
||||
|
||||
@ -1,9 +1,8 @@
|
||||
import type * as Lark from "@larksuiteoapi/node-sdk";
|
||||
import type { OpenClawPluginApi } from "../runtime-api.js";
|
||||
import { listEnabledFeishuAccountConfigs, resolveFeishuAccount } from "./accounts.js";
|
||||
import { listEnabledFeishuAccountConfigs } from "./accounts.js";
|
||||
import { FeishuChatSchema, type FeishuChatParams } from "./chat-schema.js";
|
||||
import { createFeishuClient } from "./client.js";
|
||||
import { resolveToolsConfig } from "./tools-config.js";
|
||||
import { createFeishuToolClient, resolveAnyEnabledFeishuToolsConfig } from "./tool-account.js";
|
||||
|
||||
function json(data: unknown) {
|
||||
return {
|
||||
@ -132,60 +131,64 @@ export function registerFeishuChatTools(api: OpenClawPluginApi) {
|
||||
return;
|
||||
}
|
||||
|
||||
const firstAccount = accounts[0]!;
|
||||
const toolsCfg = resolveToolsConfig(firstAccount.config.tools);
|
||||
const toolsCfg = resolveAnyEnabledFeishuToolsConfig(accounts);
|
||||
if (!toolsCfg.chat) {
|
||||
api.logger.debug?.("feishu_chat: chat tool disabled in config");
|
||||
return;
|
||||
}
|
||||
|
||||
const defaultAccountId = firstAccount.accountId;
|
||||
type FeishuChatExecuteParams = FeishuChatParams & { accountId?: string };
|
||||
|
||||
api.registerTool(
|
||||
{
|
||||
name: "feishu_chat",
|
||||
label: "Feishu Chat",
|
||||
description: "Feishu chat operations. Actions: members, info, member_info",
|
||||
parameters: FeishuChatSchema,
|
||||
async execute(_toolCallId, params) {
|
||||
const p = params as FeishuChatParams;
|
||||
try {
|
||||
const client = createFeishuClient(
|
||||
resolveFeishuAccount({ cfg: api.config, accountId: defaultAccountId }),
|
||||
);
|
||||
switch (p.action) {
|
||||
case "members":
|
||||
if (!p.chat_id) {
|
||||
return json({ error: "chat_id is required for action members" });
|
||||
}
|
||||
return json(
|
||||
await getChatMembers(
|
||||
client,
|
||||
p.chat_id,
|
||||
p.page_size,
|
||||
p.page_token,
|
||||
p.member_id_type,
|
||||
),
|
||||
);
|
||||
case "info":
|
||||
if (!p.chat_id) {
|
||||
return json({ error: "chat_id is required for action info" });
|
||||
}
|
||||
return json(await getChatInfo(client, p.chat_id));
|
||||
case "member_info":
|
||||
if (!p.member_id) {
|
||||
return json({ error: "member_id is required for action member_info" });
|
||||
}
|
||||
return json(
|
||||
await getFeishuMemberInfo(client, p.member_id, p.member_id_type ?? "open_id"),
|
||||
);
|
||||
default:
|
||||
return json({ error: `Unknown action: ${String(p.action)}` });
|
||||
(ctx) => {
|
||||
const defaultAccountId = ctx.agentAccountId;
|
||||
return {
|
||||
name: "feishu_chat",
|
||||
label: "Feishu Chat",
|
||||
description: "Feishu chat operations. Actions: members, info, member_info",
|
||||
parameters: FeishuChatSchema,
|
||||
async execute(_toolCallId, params) {
|
||||
const p = params as FeishuChatExecuteParams;
|
||||
try {
|
||||
const client = createFeishuToolClient({
|
||||
api,
|
||||
executeParams: p,
|
||||
defaultAccountId,
|
||||
});
|
||||
switch (p.action) {
|
||||
case "members":
|
||||
if (!p.chat_id) {
|
||||
return json({ error: "chat_id is required for action members" });
|
||||
}
|
||||
return json(
|
||||
await getChatMembers(
|
||||
client,
|
||||
p.chat_id,
|
||||
p.page_size,
|
||||
p.page_token,
|
||||
p.member_id_type,
|
||||
),
|
||||
);
|
||||
case "info":
|
||||
if (!p.chat_id) {
|
||||
return json({ error: "chat_id is required for action info" });
|
||||
}
|
||||
return json(await getChatInfo(client, p.chat_id));
|
||||
case "member_info":
|
||||
if (!p.member_id) {
|
||||
return json({ error: "member_id is required for action member_info" });
|
||||
}
|
||||
return json(
|
||||
await getFeishuMemberInfo(client, p.member_id, p.member_id_type ?? "open_id"),
|
||||
);
|
||||
default:
|
||||
return json({ error: `Unknown action: ${String(p.action)}` });
|
||||
}
|
||||
} catch (err) {
|
||||
return json({ error: err instanceof Error ? err.message : String(err) });
|
||||
}
|
||||
} catch (err) {
|
||||
return json({ error: err instanceof Error ? err.message : String(err) });
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
{ name: "feishu_chat" },
|
||||
);
|
||||
|
||||
@ -1,13 +1,18 @@
|
||||
import type { OpenClawPluginApi } from "openclaw/plugin-sdk/feishu";
|
||||
import { beforeEach, describe, expect, test, vi } from "vitest";
|
||||
import { registerFeishuBitableTools } from "./bitable.js";
|
||||
import { registerFeishuChatTools } from "./chat.js";
|
||||
import { registerFeishuDriveTools } from "./drive.js";
|
||||
import { registerFeishuPermTools } from "./perm.js";
|
||||
import { createToolFactoryHarness } from "./tool-factory-test-harness.js";
|
||||
import { registerFeishuWikiTools } from "./wiki.js";
|
||||
|
||||
const chatGetMock = vi.fn();
|
||||
const createFeishuClientMock = vi.fn((account: { appId?: string } | undefined) => ({
|
||||
__appId: account?.appId,
|
||||
im: {
|
||||
chat: { get: chatGetMock },
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock("./client.js", () => ({
|
||||
@ -16,11 +21,13 @@ vi.mock("./client.js", () => ({
|
||||
|
||||
function createConfig(params: {
|
||||
toolsA?: {
|
||||
chat?: boolean;
|
||||
wiki?: boolean;
|
||||
drive?: boolean;
|
||||
perm?: boolean;
|
||||
};
|
||||
toolsB?: {
|
||||
chat?: boolean;
|
||||
wiki?: boolean;
|
||||
drive?: boolean;
|
||||
perm?: boolean;
|
||||
@ -100,6 +107,39 @@ describe("feishu tool account routing", () => {
|
||||
expect(createFeishuClientMock.mock.calls.at(-1)?.[0]?.appId).toBe("app-b");
|
||||
});
|
||||
|
||||
test("chat tool registers when first account disables it and routes to agentAccountId", async () => {
|
||||
chatGetMock.mockResolvedValue({ code: 0, data: { name: "chat", user_count: 1 } });
|
||||
const { api, resolveTool } = createToolFactoryHarness(
|
||||
createConfig({
|
||||
toolsA: { chat: false },
|
||||
toolsB: { chat: true },
|
||||
}),
|
||||
);
|
||||
registerFeishuChatTools(api);
|
||||
|
||||
const tool = resolveTool("feishu_chat", { agentAccountId: "b" });
|
||||
await tool.execute("call", { action: "info", chat_id: "oc_b" });
|
||||
|
||||
expect(createFeishuClientMock.mock.calls.at(-1)?.[0]?.appId).toBe("app-b");
|
||||
});
|
||||
|
||||
test("chat tool prefers configured defaultAccount over inherited default account context", async () => {
|
||||
chatGetMock.mockResolvedValue({ code: 0, data: { name: "chat", user_count: 1 } });
|
||||
const { api, resolveTool } = createToolFactoryHarness(
|
||||
createConfig({
|
||||
defaultAccount: "b",
|
||||
toolsA: { chat: true },
|
||||
toolsB: { chat: true },
|
||||
}),
|
||||
);
|
||||
registerFeishuChatTools(api);
|
||||
|
||||
const tool = resolveTool("feishu_chat", { agentAccountId: "a" });
|
||||
await tool.execute("call", { action: "info", chat_id: "oc_b" });
|
||||
|
||||
expect(createFeishuClientMock.mock.calls.at(-1)?.[0]?.appId).toBe("app-b");
|
||||
});
|
||||
|
||||
test("perm tool registers when only second account enables it and routes to agentAccountId", async () => {
|
||||
const { api, resolveTool } = createToolFactoryHarness(
|
||||
createConfig({
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user