diff --git a/extensions/feishu/src/doc-schema.ts b/extensions/feishu/src/doc-schema.ts index 7fb38b6de61..b3ecf4975ea 100644 --- a/extensions/feishu/src/doc-schema.ts +++ b/extensions/feishu/src/doc-schema.ts @@ -24,7 +24,6 @@ const FEISHU_DOC_ACTION_VALUES = [ ] as const; const tableCreationProperties = { - doc_token: Type.Optional(Type.String({ description: "Document token" })), parent_block_id: Type.Optional( Type.String({ description: "Parent block ID (default: document root)" }), ), diff --git a/extensions/feishu/src/docx.account-selection.test.ts b/extensions/feishu/src/docx.account-selection.test.ts index 11a28e69def..bc6655e21d3 100644 --- a/extensions/feishu/src/docx.account-selection.test.ts +++ b/extensions/feishu/src/docx.account-selection.test.ts @@ -88,5 +88,8 @@ describe("feishu_doc account selection", () => { expect.arrayContaining(["read", "create", "list_blocks", "upload_image"]), ); expect(schema.properties?.action?.description).toEqual(expect.stringContaining("create_table")); + expect(schema.properties?.doc_token?.description).toEqual( + expect.stringContaining("Required for all actions except create"), + ); }); }); diff --git a/extensions/feishu/src/docx.ts b/extensions/feishu/src/docx.ts index 8c6a4b6cd02..3ce7b328785 100644 --- a/extensions/feishu/src/docx.ts +++ b/extensions/feishu/src/docx.ts @@ -33,6 +33,13 @@ function json(data: unknown) { }; } +function requireParam(value: string | undefined, label: string): string { + if (value) { + return value; + } + throw new Error(`${label} is required for this action.`); +} + /** Extract image URLs from markdown content */ function extractImageUrls(markdown: string): string[] { const regex = /!\[[^\]]*\]\(([^)]+)\)/g; @@ -1276,13 +1283,13 @@ export function registerFeishuDocTools(api: OpenClawPluginApi) { const client = getClient(p, defaultAccountId); switch (p.action) { case "read": - return json(await readDoc(client, p.doc_token)); + return json(await readDoc(client, requireParam(p.doc_token, "doc_token"))); case "write": return json( await writeDoc( client, - p.doc_token, - p.content, + requireParam(p.doc_token, "doc_token"), + requireParam(p.content, "content"), getMediaMaxBytes(p, defaultAccountId), api.logger, ), @@ -1291,8 +1298,8 @@ export function registerFeishuDocTools(api: OpenClawPluginApi) { return json( await appendDoc( client, - p.doc_token, - p.content, + requireParam(p.doc_token, "doc_token"), + requireParam(p.content, "content"), getMediaMaxBytes(p, defaultAccountId), api.logger, ), @@ -1301,9 +1308,9 @@ export function registerFeishuDocTools(api: OpenClawPluginApi) { return json( await insertDoc( client, - p.doc_token, - p.content, - p.after_block_id, + requireParam(p.doc_token, "doc_token"), + requireParam(p.content, "content"), + requireParam(p.after_block_id, "after_block_id"), getMediaMaxBytes(p, defaultAccountId), api.logger, ), @@ -1316,18 +1323,37 @@ export function registerFeishuDocTools(api: OpenClawPluginApi) { }), ); case "list_blocks": - return json(await listBlocks(client, p.doc_token)); + return json(await listBlocks(client, requireParam(p.doc_token, "doc_token"))); case "get_block": - return json(await getBlock(client, p.doc_token, p.block_id)); + return json( + await getBlock( + client, + requireParam(p.doc_token, "doc_token"), + requireParam(p.block_id, "block_id"), + ), + ); case "update_block": - return json(await updateBlock(client, p.doc_token, p.block_id, p.content)); + return json( + await updateBlock( + client, + requireParam(p.doc_token, "doc_token"), + requireParam(p.block_id, "block_id"), + requireParam(p.content, "content"), + ), + ); case "delete_block": - return json(await deleteBlock(client, p.doc_token, p.block_id)); + return json( + await deleteBlock( + client, + requireParam(p.doc_token, "doc_token"), + requireParam(p.block_id, "block_id"), + ), + ); case "create_table": return json( await createTable( client, - p.doc_token, + requireParam(p.doc_token, "doc_token"), p.row_size, p.column_size, p.parent_block_id, @@ -1336,13 +1362,18 @@ export function registerFeishuDocTools(api: OpenClawPluginApi) { ); case "write_table_cells": return json( - await writeTableCells(client, p.doc_token, p.table_block_id, p.values), + await writeTableCells( + client, + requireParam(p.doc_token, "doc_token"), + requireParam(p.table_block_id, "table_block_id"), + p.values, + ), ); case "create_table_with_values": return json( await createTableWithValues( client, - p.doc_token, + requireParam(p.doc_token, "doc_token"), p.row_size, p.column_size, p.values, @@ -1354,7 +1385,7 @@ export function registerFeishuDocTools(api: OpenClawPluginApi) { return json( await uploadImageBlock( client, - p.doc_token, + requireParam(p.doc_token, "doc_token"), getMediaMaxBytes(p, defaultAccountId), p.url, p.file_path, @@ -1368,7 +1399,7 @@ export function registerFeishuDocTools(api: OpenClawPluginApi) { return json( await uploadFileBlock( client, - p.doc_token, + requireParam(p.doc_token, "doc_token"), getMediaMaxBytes(p, defaultAccountId), p.url, p.file_path, @@ -1377,19 +1408,38 @@ export function registerFeishuDocTools(api: OpenClawPluginApi) { ), ); case "color_text": - return json(await updateColorText(client, p.doc_token, p.block_id, p.content)); + return json( + await updateColorText( + client, + requireParam(p.doc_token, "doc_token"), + requireParam(p.block_id, "block_id"), + requireParam(p.content, "content"), + ), + ); case "insert_table_row": - return json(await insertTableRow(client, p.doc_token, p.block_id, p.row_index)); + return json( + await insertTableRow( + client, + requireParam(p.doc_token, "doc_token"), + requireParam(p.block_id, "block_id"), + p.row_index, + ), + ); case "insert_table_column": return json( - await insertTableColumn(client, p.doc_token, p.block_id, p.column_index), + await insertTableColumn( + client, + requireParam(p.doc_token, "doc_token"), + requireParam(p.block_id, "block_id"), + p.column_index, + ), ); case "delete_table_rows": return json( await deleteTableRows( client, - p.doc_token, - p.block_id, + requireParam(p.doc_token, "doc_token"), + requireParam(p.block_id, "block_id"), p.row_start, p.row_count, ), @@ -1398,8 +1448,8 @@ export function registerFeishuDocTools(api: OpenClawPluginApi) { return json( await deleteTableColumns( client, - p.doc_token, - p.block_id, + requireParam(p.doc_token, "doc_token"), + requireParam(p.block_id, "block_id"), p.column_start, p.column_count, ), @@ -1408,8 +1458,8 @@ export function registerFeishuDocTools(api: OpenClawPluginApi) { return json( await mergeTableCells( client, - p.doc_token, - p.block_id, + requireParam(p.doc_token, "doc_token"), + requireParam(p.block_id, "block_id"), p.row_start, p.row_end, p.column_start, diff --git a/extensions/feishu/src/wiki.ts b/extensions/feishu/src/wiki.ts index e701f57b3aa..b9f87b28400 100644 --- a/extensions/feishu/src/wiki.ts +++ b/extensions/feishu/src/wiki.ts @@ -17,6 +17,13 @@ const WIKI_ACCESS_HINT = "To grant wiki access: Open wiki space → Settings → Members → Add the bot. " + "See: https://open.feishu.cn/document/server-docs/docs/wiki-v2/wiki-qa#a40ad4ca"; +function requireParam(value: string | undefined, label: string): string { + if (value) { + return value; + } + throw new Error(`${label} is required for this action.`); +} + async function listSpaces(client: Lark.Client) { const res = await client.wiki.space.list({}); if (res.code !== 0) { @@ -192,9 +199,15 @@ export function registerFeishuWikiTools(api: OpenClawPluginApi) { case "spaces": return jsonToolResult(await listSpaces(client)); case "nodes": - return jsonToolResult(await listNodes(client, p.space_id, p.parent_node_token)); + return jsonToolResult( + await listNodes( + client, + requireParam(p.space_id, "space_id"), + p.parent_node_token, + ), + ); case "get": - return jsonToolResult(await getNode(client, p.token)); + return jsonToolResult(await getNode(client, requireParam(p.token, "token"))); case "search": return jsonToolResult({ error: @@ -202,20 +215,33 @@ export function registerFeishuWikiTools(api: OpenClawPluginApi) { }); case "create": return jsonToolResult( - await createNode(client, p.space_id, p.title, p.obj_type, p.parent_node_token), + await createNode( + client, + requireParam(p.space_id, "space_id"), + requireParam(p.title, "title"), + p.obj_type, + p.parent_node_token, + ), ); case "move": return jsonToolResult( await moveNode( client, - p.space_id, - p.node_token, + requireParam(p.space_id, "space_id"), + requireParam(p.node_token, "node_token"), p.target_space_id, p.target_parent_token, ), ); case "rename": - return jsonToolResult(await renameNode(client, p.space_id, p.node_token, p.title)); + return jsonToolResult( + await renameNode( + client, + requireParam(p.space_id, "space_id"), + requireParam(p.node_token, "node_token"), + requireParam(p.title, "title"), + ), + ); default: // eslint-disable-next-line @typescript-eslint/no-explicit-any -- exhaustive check fallback return unknownToolActionResult((p as { action?: unknown }).action);