refactor(feishu): unify private tool registration setup (#130522)

This commit is contained in:
Peter Steinberger
2026-08-26 18:05:15 -07:00
committed by GitHub
parent 801cbbd665
commit fc51e97e60
9 changed files with 71 additions and 118 deletions
+14 -54
View File
@@ -6,54 +6,6 @@ import {
import type { OpenClawPluginApi } from "openclaw/plugin-sdk/channel-entry-contract";
import { registerFeishuSubagentHooks } from "./subagent-hooks-api.js";
function registerFeishuDocTools(api: OpenClawPluginApi) {
const register = loadBundledEntryExportSync<(api: OpenClawPluginApi) => void>(import.meta.url, {
specifier: "./api.js",
exportName: "registerFeishuDocTools",
});
register(api);
}
function registerFeishuChatTools(api: OpenClawPluginApi) {
const register = loadBundledEntryExportSync<(api: OpenClawPluginApi) => void>(import.meta.url, {
specifier: "./api.js",
exportName: "registerFeishuChatTools",
});
register(api);
}
function registerFeishuWikiTools(api: OpenClawPluginApi) {
const register = loadBundledEntryExportSync<(api: OpenClawPluginApi) => void>(import.meta.url, {
specifier: "./api.js",
exportName: "registerFeishuWikiTools",
});
register(api);
}
function registerFeishuDriveTools(api: OpenClawPluginApi) {
const register = loadBundledEntryExportSync<(api: OpenClawPluginApi) => void>(import.meta.url, {
specifier: "./api.js",
exportName: "registerFeishuDriveTools",
});
register(api);
}
function registerFeishuPermTools(api: OpenClawPluginApi) {
const register = loadBundledEntryExportSync<(api: OpenClawPluginApi) => void>(import.meta.url, {
specifier: "./api.js",
exportName: "registerFeishuPermTools",
});
register(api);
}
function registerFeishuBitableTools(api: OpenClawPluginApi) {
const register = loadBundledEntryExportSync<(api: OpenClawPluginApi) => void>(import.meta.url, {
specifier: "./api.js",
exportName: "registerFeishuBitableTools",
});
register(api);
}
export default defineBundledChannelEntry({
id: "feishu",
name: "Feishu",
@@ -73,11 +25,19 @@ export default defineBundledChannelEntry({
},
registerFull(api) {
registerFeishuSubagentHooks(api);
registerFeishuDocTools(api);
registerFeishuChatTools(api);
registerFeishuWikiTools(api);
registerFeishuDriveTools(api);
registerFeishuPermTools(api);
registerFeishuBitableTools(api);
for (const exportName of [
"registerFeishuDocTools",
"registerFeishuChatTools",
"registerFeishuWikiTools",
"registerFeishuDriveTools",
"registerFeishuPermTools",
"registerFeishuBitableTools",
]) {
const register = loadBundledEntryExportSync<(api: OpenClawPluginApi) => void>(
import.meta.url,
{ specifier: "./api.js", exportName },
);
register(api);
}
},
});
+1 -7
View File
@@ -5,7 +5,6 @@ import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
import { readPositiveIntegerParam } from "openclaw/plugin-sdk/param-readers";
import { Type, type TSchema } from "typebox";
import type { OpenClawPluginApi } from "../runtime-api.js";
import { listEnabledFeishuAccounts } from "./accounts.js";
import { createFeishuToolClient, resolveAnyEnabledFeishuToolsConfig } from "./tool-account.js";
import { feishuExternalToolResult as json } from "./tool-result.js";
@@ -586,12 +585,7 @@ export function registerFeishuBitableTools(api: OpenClawPluginApi) {
return;
}
const accounts = listEnabledFeishuAccounts(api.config);
if (accounts.length === 0) {
return;
}
const toolsCfg = resolveAnyEnabledFeishuToolsConfig(accounts);
const toolsCfg = resolveAnyEnabledFeishuToolsConfig(api.config);
if (!toolsCfg.bitable) {
return;
}
+1 -7
View File
@@ -3,7 +3,6 @@ import type * as Lark from "@larksuiteoapi/node-sdk";
import { readPositiveIntegerParam } from "openclaw/plugin-sdk/param-readers";
import type { OpenClawPluginToolContext } from "openclaw/plugin-sdk/plugin-entry";
import type { OpenClawPluginApi } from "../runtime-api.js";
import { listEnabledFeishuAccounts } from "./accounts.js";
import { FeishuChatSchema, type FeishuChatParams } from "./chat-schema.js";
import { resolveFeishuChatType } from "./chat-type.js";
import { createFeishuClient } from "./client.js";
@@ -244,12 +243,7 @@ export function registerFeishuChatTools(api: OpenClawPluginApi) {
}
const cfg = api.config;
const accounts = listEnabledFeishuAccounts(cfg);
if (accounts.length === 0) {
return;
}
const toolsCfg = resolveAnyEnabledFeishuToolsConfig(accounts);
const toolsCfg = resolveAnyEnabledFeishuToolsConfig(cfg);
if (!toolsCfg.chat) {
return;
}
+10 -28
View File
@@ -5,7 +5,6 @@ import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
import { normalizeOptionalString, uniqueStrings } from "openclaw/plugin-sdk/string-coerce-runtime";
import { Type } from "typebox";
import type { OpenClawPluginApi } from "../runtime-api.js";
import { listEnabledFeishuAccounts } from "./accounts.js";
import { resolveConfiguredHttpTimeoutMs } from "./client-timeout.js";
import { FeishuDocSchema, type FeishuDocParams } from "./doc-schema.js";
import { BATCH_SIZE, insertBlocksInBatches } from "./docx-batch-insert.js";
@@ -76,28 +75,20 @@ const BLOCK_TYPE_NAMES: Record<number, string> = {
// Block types that cannot be created via documentBlockChildren.create API
const UNSUPPORTED_CREATE_TYPES = new Set([31, 32]);
/** Clean blocks for insertion (remove unsupported types and read-only fields) */
/** Remove block types unsupported by the children insertion API. */
function cleanBlocksForInsert(blocks: FeishuDocxBlock[]): {
cleaned: FeishuDocxBlock[];
skipped: string[];
} {
const skipped: string[] = [];
const cleaned = blocks
.filter((block) => {
if (UNSUPPORTED_CREATE_TYPES.has(block.block_type)) {
const typeName = BLOCK_TYPE_NAMES[block.block_type] || `type_${block.block_type}`;
skipped.push(typeName);
return false;
}
return true;
})
.map((block) => {
if (block.block_type === 31 && block.table?.merge_info) {
const { merge_info: _merge_info, ...tableRest } = block.table;
return Object.assign({}, block, { table: tableRest });
}
return block;
});
const cleaned = blocks.filter((block) => {
if (UNSUPPORTED_CREATE_TYPES.has(block.block_type)) {
const typeName = BLOCK_TYPE_NAMES[block.block_type] || `type_${block.block_type}`;
skipped.push(typeName);
return false;
}
return true;
});
return { cleaned, skipped };
}
@@ -1200,16 +1191,9 @@ export function registerFeishuDocTools(api: OpenClawPluginApi) {
return;
}
// Check if any account is configured
const accounts = listEnabledFeishuAccounts(api.config);
if (accounts.length === 0) {
return;
}
// Register if enabled on any account; account routing is resolved per execution.
const toolsCfg = resolveAnyEnabledFeishuToolsConfig(accounts);
const toolsCfg = resolveAnyEnabledFeishuToolsConfig(api.config);
const registered: string[] = [];
type FeishuDocExecuteParams = FeishuDocParams & { accountId?: string };
const getClient = (params: { accountId?: string } | undefined, defaultAccountId?: string) =>
@@ -1434,7 +1418,6 @@ export function registerFeishuDocTools(api: OpenClawPluginApi) {
},
{ name: "feishu_doc" },
);
registered.push("feishu_doc");
}
// Keep feishu_app_scopes as independent tool
@@ -1464,7 +1447,6 @@ export function registerFeishuDocTools(api: OpenClawPluginApi) {
}),
{ name: "feishu_app_scopes" },
);
registered.push("feishu_app_scopes");
}
}
/* oxlint-disable max-lines -- TODO: split this grandfathered oversized file. */
+1 -7
View File
@@ -4,7 +4,6 @@ import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
import { readPositiveIntegerParam } from "openclaw/plugin-sdk/param-readers";
import { isRecord, readStringValue as readString } from "openclaw/plugin-sdk/string-coerce-runtime";
import type { OpenClawPluginApi } from "../runtime-api.js";
import { listEnabledFeishuAccounts } from "./accounts.js";
import { cleanupAmbientCommentTypingReaction } from "./comment-reaction.js";
import { encodeQuery, extractReplyText, formatFeishuApiError } from "./comment-shared.js";
import { parseFeishuCommentTarget, type CommentFileType } from "./comment-target.js";
@@ -798,12 +797,7 @@ export function registerFeishuDriveTools(api: OpenClawPluginApi) {
return;
}
const accounts = listEnabledFeishuAccounts(api.config);
if (accounts.length === 0) {
return;
}
const toolsCfg = resolveAnyEnabledFeishuToolsConfig(accounts);
const toolsCfg = resolveAnyEnabledFeishuToolsConfig(api.config);
if (!toolsCfg.drive) {
return;
}
+1 -7
View File
@@ -1,7 +1,6 @@
// Feishu plugin module implements perm behavior.
import type * as Lark from "@larksuiteoapi/node-sdk";
import type { OpenClawPluginApi } from "../runtime-api.js";
import { listEnabledFeishuAccounts } from "./accounts.js";
import { FeishuPermSchema, type FeishuPermParams } from "./perm-schema.js";
import { createFeishuToolClient, resolveAnyEnabledFeishuToolsConfig } from "./tool-account.js";
import {
@@ -118,12 +117,7 @@ export function registerFeishuPermTools(api: OpenClawPluginApi) {
return;
}
const accounts = listEnabledFeishuAccounts(api.config);
if (accounts.length === 0) {
return;
}
const toolsCfg = resolveAnyEnabledFeishuToolsConfig(accounts);
const toolsCfg = resolveAnyEnabledFeishuToolsConfig(api.config);
if (!toolsCfg.perm) {
return;
}
@@ -20,6 +20,8 @@ vi.mock("./client.js", () => ({
}));
let registerFeishuBitableTools: typeof import("./bitable.js").registerFeishuBitableTools;
let registerFeishuChatTools: typeof import("./chat.js").registerFeishuChatTools;
let registerFeishuDocTools: typeof import("./docx.js").registerFeishuDocTools;
let registerFeishuDriveTools: typeof import("./drive.js").registerFeishuDriveTools;
let registerFeishuPermTools: typeof import("./perm.js").registerFeishuPermTools;
let registerFeishuWikiTools: typeof import("./wiki.js").registerFeishuWikiTools;
@@ -92,6 +94,8 @@ describe("feishu tool account routing", () => {
}),
));
({ registerFeishuWikiTools } = await import("./wiki.js"));
({ registerFeishuChatTools } = await import("./chat.js"));
({ registerFeishuDocTools } = await import("./docx.js"));
});
afterAll(() => {
@@ -103,6 +107,41 @@ describe("feishu tool account routing", () => {
vi.clearAllMocks();
});
test.each([
["missing channel", {}],
["unconfigured account", { channels: { feishu: { accounts: { a: { enabled: true } } } } }],
[
"disabled channel",
{ channels: { feishu: { ...createConfig({}).channels?.feishu, enabled: false } } },
],
[
"disabled accounts",
{
channels: {
feishu: {
accounts: {
a: { enabled: false, appId: "app-a", appSecret: "sec-a" }, // pragma: allowlist secret
},
},
},
},
],
])("does not register workplace tools for %s", (_label, config) => {
const { api, registered } = createToolFactoryHarness(config);
for (const register of [
registerFeishuDocTools,
registerFeishuChatTools,
registerFeishuWikiTools,
registerFeishuDriveTools,
registerFeishuPermTools,
registerFeishuBitableTools,
]) {
register(api);
}
expect(registered).toEqual([]);
expect(createFeishuClientMock).not.toHaveBeenCalled();
});
test("wiki tool registers when first account disables it and routes to agentAccountId", async () => {
const { api, resolveTool } = createToolFactoryHarness(
createConfig({
+3 -1
View File
@@ -4,6 +4,7 @@ import { normalizeOptionalAccountId } from "openclaw/plugin-sdk/account-resoluti
import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
import type { OpenClawPluginApi } from "../runtime-api.js";
import {
listEnabledFeishuAccounts,
listFeishuAccountIds,
resolveDefaultFeishuAccountId,
resolveFeishuAccount,
@@ -128,8 +129,9 @@ export function createFeishuToolClient(params: {
}
export function resolveAnyEnabledFeishuToolsConfig(
accounts: ResolvedFeishuAccount[],
config: OpenClawPluginApi["config"],
): Required<FeishuToolsConfig> {
const accounts = listEnabledFeishuAccounts(config);
const merged: Required<FeishuToolsConfig> = {
doc: false,
chat: false,
+1 -7
View File
@@ -2,7 +2,6 @@
import type * as Lark from "@larksuiteoapi/node-sdk";
import { readPositiveIntegerParam } from "openclaw/plugin-sdk/param-readers";
import type { OpenClawPluginApi } from "../runtime-api.js";
import { listEnabledFeishuAccounts } from "./accounts.js";
import { createFeishuToolClient, resolveAnyEnabledFeishuToolsConfig } from "./tool-account.js";
import {
feishuExternalToolResult as jsonResult,
@@ -209,12 +208,7 @@ export function registerFeishuWikiTools(api: OpenClawPluginApi) {
return;
}
const accounts = listEnabledFeishuAccounts(api.config);
if (accounts.length === 0) {
return;
}
const toolsCfg = resolveAnyEnabledFeishuToolsConfig(accounts);
const toolsCfg = resolveAnyEnabledFeishuToolsConfig(api.config);
if (!toolsCfg.wiki) {
return;
}