@max1874/feishu 0.2.21 → 0.2.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/docx.ts +94 -19
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@max1874/feishu",
3
- "version": "0.2.21",
3
+ "version": "0.2.23",
4
4
  "type": "module",
5
5
  "description": "OpenClaw Feishu/Lark channel plugin",
6
6
  "license": "MIT",
package/src/docx.ts CHANGED
@@ -508,6 +508,20 @@ async function grantFullAccess(client: Lark.Client, docToken: string, memberId:
508
508
  return res.data;
509
509
  }
510
510
 
511
+ /** Transfer document ownership to a user (open_id) */
512
+ async function transferOwner(client: Lark.Client, docToken: string, newOwnerOpenId: string) {
513
+ const res = await client.drive.permissionMember.transferOwner({
514
+ path: { token: docToken },
515
+ params: { type: "docx", need_notification: false, remove_old_owner: false },
516
+ data: {
517
+ member_type: "openid",
518
+ member_id: newOwnerOpenId,
519
+ },
520
+ });
521
+ if (res.code !== 0) throw new Error(res.msg);
522
+ return res.data;
523
+ }
524
+
511
525
  async function createDoc(
512
526
  client: Lark.Client,
513
527
  title: string,
@@ -526,16 +540,32 @@ async function createDoc(
526
540
  await setDocPermissionTenantEditable(client, docId);
527
541
  }
528
542
 
529
- // Auto-grant full_access to conversation participant (user in DM, chat group in group)
543
+ // Auto-transfer ownership to the sender, and grant group access if in group chat
544
+ let ownerTransferredTo: string | undefined;
530
545
  let grantedTo: string | undefined;
531
546
  const convCtx = getConversationContext();
532
547
  if (docId && convCtx) {
533
- const memberId = convCtx.chatType === "group" ? convCtx.chatId : convCtx.senderOpenId;
548
+ // Transfer ownership to the user who triggered this request
534
549
  try {
535
- await grantFullAccess(client, docId, memberId);
536
- grantedTo = memberId;
550
+ await transferOwner(client, docId, convCtx.senderOpenId);
551
+ ownerTransferredTo = convCtx.senderOpenId;
537
552
  } catch {
538
- // Non-fatal: doc is created, permission grant failed
553
+ // Fallback: grant full_access if transfer fails
554
+ try {
555
+ await grantFullAccess(client, docId, convCtx.senderOpenId);
556
+ grantedTo = convCtx.senderOpenId;
557
+ } catch {
558
+ // Non-fatal: doc is created, permission grant failed
559
+ }
560
+ }
561
+
562
+ // In group chats, also grant full_access to the chat group
563
+ if (convCtx.chatType === "group") {
564
+ try {
565
+ await grantFullAccess(client, docId, convCtx.chatId);
566
+ } catch {
567
+ // Non-fatal
568
+ }
539
569
  }
540
570
  }
541
571
 
@@ -544,6 +574,7 @@ async function createDoc(
544
574
  title: doc?.title,
545
575
  url: `https://feishu.cn/docx/${docId}`,
546
576
  permission: permission ?? "private",
577
+ ...(ownerTransferredTo && { owner_transferred_to: ownerTransferredTo }),
547
578
  ...(grantedTo && { granted_full_access_to: grantedTo }),
548
579
  };
549
580
  }
@@ -865,7 +896,11 @@ export function registerFeishuDocTools(api: OpenClawPluginApi) {
865
896
  {
866
897
  name: "feishu_doc_read",
867
898
  label: "Feishu Doc Read",
868
- description: "Read plain text content and metadata from a Feishu document",
899
+ description:
900
+ "Read plain text content and metadata from a Feishu document. " +
901
+ "Use this for a quick overview of document content. " +
902
+ "For structured content (tables, block details), use feishu_doc_list_blocks instead. " +
903
+ "Extract doc_token from URL: /docx/XXX or /docs/XXX.",
869
904
  parameters: DocTokenSchema,
870
905
  async execute(_toolCallId, params) {
871
906
  const { doc_token } = params as { doc_token: string };
@@ -886,7 +921,10 @@ export function registerFeishuDocTools(api: OpenClawPluginApi) {
886
921
  name: "feishu_doc_create",
887
922
  label: "Feishu Doc Create",
888
923
  description:
889
- "Create a new empty Feishu document. Automatically grants full_access (manage) permission to the current conversation participant.",
924
+ "Create a new empty Feishu document. " +
925
+ "Automatically grants full_access permission to the conversation participant so they can edit. " +
926
+ "After creating, use feishu_doc_write to populate content. " +
927
+ "Set permission='tenant_editable' to allow anyone in the organization to edit via link.",
890
928
  parameters: CreateDocSchema,
891
929
  async execute(_toolCallId, params) {
892
930
  const { title, folder_token, permission } = params as {
@@ -911,7 +949,10 @@ export function registerFeishuDocTools(api: OpenClawPluginApi) {
911
949
  name: "feishu_doc_write",
912
950
  label: "Feishu Doc Write",
913
951
  description:
914
- "Write markdown content to a Feishu document (replaces all content). Supports headings, lists, code blocks, quotes, links, images, tables, and text styling.",
952
+ "Write markdown content to a Feishu document. WARNING: This REPLACES ALL existing content. " +
953
+ "Use for: initial document population, complete rewrites. " +
954
+ "For partial edits, use feishu_doc_update_block instead. " +
955
+ "Supports: headings, lists, code blocks, quotes, links, images, tables, bold/italic/strikethrough.",
915
956
  parameters: WriteDocSchema,
916
957
  async execute(_toolCallId, params) {
917
958
  const { doc_token, content } = params as { doc_token: string; content: string };
@@ -932,7 +973,10 @@ export function registerFeishuDocTools(api: OpenClawPluginApi) {
932
973
  name: "feishu_doc_append",
933
974
  label: "Feishu Doc Append",
934
975
  description:
935
- "Append markdown content to the end of a Feishu document. Supports same markdown syntax as write.",
976
+ "Append markdown content to the END of a Feishu document without touching existing content. " +
977
+ "Use for: adding new sections, logging, incremental content building. " +
978
+ "Cannot insert in the middle - for that, use feishu_doc_update_block or delete+append. " +
979
+ "Supports same markdown syntax as feishu_doc_write.",
936
980
  parameters: AppendDocSchema,
937
981
  async execute(_toolCallId, params) {
938
982
  const { doc_token, content } = params as { doc_token: string; content: string };
@@ -952,7 +996,11 @@ export function registerFeishuDocTools(api: OpenClawPluginApi) {
952
996
  {
953
997
  name: "feishu_doc_update_block",
954
998
  label: "Feishu Doc Update Block",
955
- description: "Update the text content of a specific block in a Feishu document",
999
+ description:
1000
+ "Update the text content of a specific block without affecting other parts of the document. " +
1001
+ "WORKFLOW: First call feishu_doc_list_blocks to find the block_id, then update it here. " +
1002
+ "Use for: fixing typos, updating a paragraph, modifying specific sections. " +
1003
+ "Only works on text-based blocks (paragraphs, headings, lists). Cannot change block type.",
956
1004
  parameters: UpdateBlockSchema,
957
1005
  async execute(_toolCallId, params) {
958
1006
  const { doc_token, block_id, content } = params as {
@@ -976,7 +1024,11 @@ export function registerFeishuDocTools(api: OpenClawPluginApi) {
976
1024
  {
977
1025
  name: "feishu_doc_delete_block",
978
1026
  label: "Feishu Doc Delete Block",
979
- description: "Delete a specific block from a Feishu document",
1027
+ description:
1028
+ "Delete a specific block from a Feishu document. " +
1029
+ "WORKFLOW: First call feishu_doc_list_blocks to find the block_id, then delete it here. " +
1030
+ "Use for: removing outdated content, clearing sections before rewriting. " +
1031
+ "Combine with feishu_doc_append to replace content in the middle of a document.",
980
1032
  parameters: DeleteBlockSchema,
981
1033
  async execute(_toolCallId, params) {
982
1034
  const { doc_token, block_id } = params as { doc_token: string; block_id: string };
@@ -997,7 +1049,10 @@ export function registerFeishuDocTools(api: OpenClawPluginApi) {
997
1049
  name: "feishu_doc_list_blocks",
998
1050
  label: "Feishu Doc List Blocks",
999
1051
  description:
1000
- "List all blocks in a Feishu document with full content. Use this to read structured content like tables. Returns block_id for use with update/delete/get_block.",
1052
+ "List all blocks in a Feishu document with their block_id and full content. " +
1053
+ "ESSENTIAL for editing: returns block_id needed by update_block/delete_block/get_block. " +
1054
+ "Better than feishu_doc_read for: seeing document structure, reading tables, finding specific sections. " +
1055
+ "Block types include: text, headings (1-9), bullet/ordered lists, code, quote, table, image, divider.",
1001
1056
  parameters: DocTokenSchema,
1002
1057
  async execute(_toolCallId, params) {
1003
1058
  const { doc_token } = params as { doc_token: string };
@@ -1017,7 +1072,10 @@ export function registerFeishuDocTools(api: OpenClawPluginApi) {
1017
1072
  {
1018
1073
  name: "feishu_doc_get_block",
1019
1074
  label: "Feishu Doc Get Block",
1020
- description: "Get detailed content of a specific block by ID (from list_blocks)",
1075
+ description:
1076
+ "Get detailed content of a specific block by its block_id. " +
1077
+ "Use when feishu_doc_list_blocks output is truncated or you need fresh data for one block. " +
1078
+ "Returns full block structure including nested content (e.g., table cells).",
1021
1079
  parameters: GetBlockSchema,
1022
1080
  async execute(_toolCallId, params) {
1023
1081
  const { doc_token, block_id } = params as { doc_token: string; block_id: string };
@@ -1037,7 +1095,11 @@ export function registerFeishuDocTools(api: OpenClawPluginApi) {
1037
1095
  {
1038
1096
  name: "feishu_folder_list",
1039
1097
  label: "Feishu Folder List",
1040
- description: "List documents and subfolders in a Feishu folder",
1098
+ description:
1099
+ "List documents and subfolders in a Feishu Drive folder. " +
1100
+ "Extract folder_token from URL: /drive/folder/XXX. " +
1101
+ "Use to browse available documents or find a doc_token to read/edit. " +
1102
+ "Returns: file name, type (docx/sheet/folder/etc), token, URL, last modified time.",
1041
1103
  parameters: FolderTokenSchema,
1042
1104
  async execute(_toolCallId, params) {
1043
1105
  const { folder_token } = params as { folder_token: string };
@@ -1058,7 +1120,9 @@ export function registerFeishuDocTools(api: OpenClawPluginApi) {
1058
1120
  name: "feishu_doc_set_permission",
1059
1121
  label: "Feishu Doc Set Permission",
1060
1122
  description:
1061
- "Set document sharing permission. Use 'tenant_editable' to allow organization members to edit via link.",
1123
+ "Change document sharing permissions after creation. " +
1124
+ "Options: 'tenant_editable' = anyone in organization can edit via link; 'private' = only owner/explicitly granted users. " +
1125
+ "Note: feishu_doc_create already grants full_access to the conversation participant.",
1062
1126
  parameters: SetPermissionSchema,
1063
1127
  async execute(_toolCallId, params) {
1064
1128
  const { doc_token, permission } = params as { doc_token: string; permission: DocPermission };
@@ -1094,7 +1158,10 @@ export function registerFeishuDocTools(api: OpenClawPluginApi) {
1094
1158
  name: "feishu_wiki_read",
1095
1159
  label: "Feishu Wiki Read",
1096
1160
  description:
1097
- "Read content from a Feishu wiki page. Extract wiki_token from URL /wiki/XXX. Returns wiki metadata and document content if the underlying type is docx.",
1161
+ "Read content from a Feishu Wiki (knowledge base) page. " +
1162
+ "Extract wiki_token from URL: /wiki/XXX. " +
1163
+ "Different from feishu_doc_read - wikis have their own token format and metadata. " +
1164
+ "Returns wiki node info and document content if the underlying type is docx.",
1098
1165
  parameters: WikiTokenSchema,
1099
1166
  async execute(_toolCallId, params) {
1100
1167
  const { wiki_token } = params as { wiki_token: string };
@@ -1114,7 +1181,10 @@ export function registerFeishuDocTools(api: OpenClawPluginApi) {
1114
1181
  {
1115
1182
  name: "feishu_wiki_spaces",
1116
1183
  label: "Feishu Wiki Spaces",
1117
- description: "List available wiki spaces (knowledge bases) the app has access to.",
1184
+ description:
1185
+ "List all Wiki spaces (knowledge bases) the app has access to. " +
1186
+ "Returns space_id needed for feishu_wiki_nodes. " +
1187
+ "Use this first when browsing wikis to find the right knowledge base.",
1118
1188
  parameters: Type.Object({}),
1119
1189
  async execute() {
1120
1190
  try {
@@ -1134,7 +1204,10 @@ export function registerFeishuDocTools(api: OpenClawPluginApi) {
1134
1204
  name: "feishu_wiki_nodes",
1135
1205
  label: "Feishu Wiki Nodes",
1136
1206
  description:
1137
- "List wiki nodes (pages) in a space. Use parent_node_token to list children of a specific node.",
1207
+ "List wiki pages within a Wiki space. " +
1208
+ "WORKFLOW: First call feishu_wiki_spaces to get space_id, then list nodes here. " +
1209
+ "Use parent_node_token to browse nested pages (children of a specific node). " +
1210
+ "Returns node_token for use with feishu_wiki_read.",
1138
1211
  parameters: WikiSpaceIdSchema,
1139
1212
  async execute(_toolCallId, params) {
1140
1213
  const { space_id, parent_node_token } = params as {
@@ -1158,7 +1231,9 @@ export function registerFeishuDocTools(api: OpenClawPluginApi) {
1158
1231
  name: "feishu_app_scopes",
1159
1232
  label: "Feishu App Scopes",
1160
1233
  description:
1161
- "List current app permissions (scopes). Use to debug permission issues or check available capabilities.",
1234
+ "List the app's current permissions (scopes) granted in Feishu admin console. " +
1235
+ "Use when a tool fails with permission errors to diagnose what's missing. " +
1236
+ "Common required scopes: docx:document (read/edit docs), wiki:wiki (read wikis), drive:drive (folder access).",
1162
1237
  parameters: Type.Object({}),
1163
1238
  async execute() {
1164
1239
  try {