@m1heng-clawd/feishu 0.1.15 → 0.1.16

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@m1heng-clawd/feishu",
3
- "version": "0.1.15",
3
+ "version": "0.1.16",
4
4
  "type": "module",
5
5
  "description": "OpenClaw Feishu/Lark channel plugin",
6
6
  "scripts": {
package/src/bot.ts CHANGED
@@ -1488,6 +1488,7 @@ export async function handleFeishuMessage(params: {
1488
1488
  channel: "feishu",
1489
1489
  accountId: account.accountId,
1490
1490
  sessionKey: route.sessionKey,
1491
+ senderOpenId: ctx.senderOpenId,
1491
1492
  },
1492
1493
  // Keep account context available while the agent executes plugin tools.
1493
1494
  () =>
@@ -1574,6 +1575,7 @@ export async function handleFeishuMessage(params: {
1574
1575
  channel: "feishu",
1575
1576
  accountId: account.accountId,
1576
1577
  sessionKey: route.sessionKey,
1578
+ senderOpenId: ctx.senderOpenId,
1577
1579
  },
1578
1580
  // Tool calls produced by this turn should resolve to the same inbound account.
1579
1581
  () =>
@@ -2,6 +2,23 @@ import type { ChatClient } from "./common.js";
2
2
  import type { FeishuChatParams } from "./schemas.js";
3
3
  import { runChatApiCall } from "./common.js";
4
4
 
5
+ type UserIdType = "open_id" | "user_id" | "union_id";
6
+ type MemberIdType = UserIdType | "app_id";
7
+
8
+ function requireString(value: unknown, field: string): string {
9
+ if (typeof value !== "string" || value.trim().length === 0) {
10
+ throw new Error(`${field} is required`);
11
+ }
12
+ return value;
13
+ }
14
+
15
+ function requireStringArray(value: unknown, field: string): string[] {
16
+ if (!Array.isArray(value) || value.length === 0 || value.some((item) => typeof item !== "string")) {
17
+ throw new Error(`${field} must be a non-empty string array`);
18
+ }
19
+ return value;
20
+ }
21
+
5
22
  const BLOCK_TYPE_NAMES: Record<number, string> = {
6
23
  1: "Page",
7
24
  2: "Text",
@@ -117,15 +134,35 @@ async function writeDocAnnouncement(client: ChatClient, chatId: string, content:
117
134
  }),
118
135
  );
119
136
 
120
- const res = await runChatApiCall("im.chatAnnouncement.patch", () =>
121
- (client as any).im.chatAnnouncement.patch({
122
- path: { chat_id: chatId },
123
- data: {
124
- content,
125
- revision: (current as any).data?.revision,
126
- },
127
- }),
128
- );
137
+ const revision = (current as any).data?.revision;
138
+ if (!revision) {
139
+ throw new Error("Failed to load current announcement revision");
140
+ }
141
+
142
+ let res: unknown;
143
+ try {
144
+ // Prefer SDK current payload shape (revision + requests[]).
145
+ res = await runChatApiCall("im.chatAnnouncement.patch", () =>
146
+ (client as any).im.chatAnnouncement.patch({
147
+ path: { chat_id: chatId },
148
+ data: {
149
+ revision,
150
+ requests: [content],
151
+ },
152
+ }),
153
+ );
154
+ } catch {
155
+ // Backward compatibility for tenants still accepting legacy content payload.
156
+ res = await runChatApiCall("im.chatAnnouncement.patch", () =>
157
+ (client as any).im.chatAnnouncement.patch({
158
+ path: { chat_id: chatId },
159
+ data: {
160
+ content,
161
+ revision,
162
+ },
163
+ }),
164
+ );
165
+ }
129
166
 
130
167
  return {
131
168
  success: true,
@@ -210,7 +247,13 @@ async function batchUpdateAnnouncementBlocks(
210
247
 
211
248
  // ============== New Chat Management Functions ==============
212
249
 
213
- async function createChat(client: ChatClient, name: string, userIds?: string[], description?: string) {
250
+ async function createChat(
251
+ client: ChatClient,
252
+ name: string,
253
+ userIds?: string[],
254
+ description?: string,
255
+ userIdType: UserIdType = "open_id",
256
+ ) {
214
257
  const data: any = { name };
215
258
  if (userIds && userIds.length > 0) {
216
259
  data.user_id_list = userIds;
@@ -222,7 +265,7 @@ async function createChat(client: ChatClient, name: string, userIds?: string[],
222
265
  const res = await runChatApiCall("im.chat.create", () =>
223
266
  (client as any).im.chat.create({
224
267
  data,
225
- params: { user_id_type: "open_id" },
268
+ params: { user_id_type: userIdType },
226
269
  }),
227
270
  );
228
271
 
@@ -233,11 +276,16 @@ async function createChat(client: ChatClient, name: string, userIds?: string[],
233
276
  };
234
277
  }
235
278
 
236
- async function addMembers(client: ChatClient, chatId: string, userIds: string[]) {
279
+ async function addMembers(
280
+ client: ChatClient,
281
+ chatId: string,
282
+ userIds: string[],
283
+ memberIdType: MemberIdType = "open_id",
284
+ ) {
237
285
  const res = await runChatApiCall("im.chatMembers.create", () =>
238
286
  (client as any).im.chatMembers.create({
239
287
  path: { chat_id: chatId },
240
- params: { member_id_type: "open_id" },
288
+ params: { member_id_type: memberIdType },
241
289
  data: { id_list: userIds },
242
290
  }),
243
291
  );
@@ -300,9 +348,10 @@ async function createSessionChat(
300
348
  userIds: string[],
301
349
  greeting?: string,
302
350
  description?: string,
351
+ userIdType: UserIdType = "open_id",
303
352
  ) {
304
353
  // Step 1: Create the chat
305
- const createResult = await createChat(client, name, userIds, description);
354
+ const createResult = await createChat(client, name, userIds, description, userIdType);
306
355
  const chatId = createResult.chat_id;
307
356
 
308
357
  if (!chatId) {
@@ -358,15 +407,21 @@ export async function runChatAction(client: ChatClient, params: FeishuChatParams
358
407
  switch (params.action) {
359
408
  case "get_announcement_info":
360
409
  case "get_announcement":
361
- return getAnnouncement(client, params.chat_id);
410
+ return getAnnouncement(client, requireString(params.chat_id, "chat_id"));
362
411
  case "list_announcement_blocks":
363
- return listAnnouncementBlocks(client, params.chat_id);
412
+ return listAnnouncementBlocks(client, requireString(params.chat_id, "chat_id"));
364
413
  case "get_announcement_block":
365
- return getAnnouncementBlock(client, params.chat_id, params.block_id);
414
+ return getAnnouncementBlock(
415
+ client,
416
+ requireString(params.chat_id, "chat_id"),
417
+ requireString(params.block_id, "block_id"),
418
+ );
366
419
  case "write_announcement": {
367
- const current = await getAnnouncement(client, params.chat_id);
420
+ const chatId = requireString(params.chat_id, "chat_id");
421
+ const content = requireString(params.content, "content");
422
+ const current = await getAnnouncement(client, chatId);
368
423
  if (current.announcement_type === "doc") {
369
- return writeDocAnnouncement(client, params.chat_id, params.content);
424
+ return writeDocAnnouncement(client, chatId, content);
370
425
  } else {
371
426
  // For docx announcements, append a text block under the Page root block.
372
427
  // Full replacement is not supported via API; use update_announcement_block to edit existing blocks.
@@ -375,15 +430,17 @@ export async function runChatAction(client: ChatClient, params: FeishuChatParams
375
430
  if (!pageBlock?.block_id) {
376
431
  return { error: "Could not find the Page root block for docx announcement. Use list_announcement_blocks to inspect the structure." };
377
432
  }
378
- return createTextBlock(client, params.chat_id, pageBlock.block_id, params.content);
433
+ return createTextBlock(client, chatId, pageBlock.block_id, content);
379
434
  }
380
435
  }
381
436
  case "append_announcement": {
382
- const current = await getAnnouncement(client, params.chat_id);
437
+ const chatId = requireString(params.chat_id, "chat_id");
438
+ const content = requireString(params.content, "content");
439
+ const current = await getAnnouncement(client, chatId);
383
440
  if (current.announcement_type === "doc") {
384
441
  const existingContent = (current as any).content || "";
385
- const newContent = existingContent + "\n" + params.content;
386
- return writeDocAnnouncement(client, params.chat_id, newContent);
442
+ const newContent = existingContent + "\n" + content;
443
+ return writeDocAnnouncement(client, chatId, newContent);
387
444
  } else {
388
445
  // For docx format, the parent block must be the Page root block (block_type: 1)
389
446
  const blocks: any[] = (current as any).blocks ?? [];
@@ -391,40 +448,52 @@ export async function runChatAction(client: ChatClient, params: FeishuChatParams
391
448
  if (!pageBlock?.block_id) {
392
449
  return { error: "Could not find the Page root block for docx announcement. Use list_announcement_blocks to inspect the structure." };
393
450
  }
394
- return createTextBlock(client, params.chat_id, pageBlock.block_id, params.content);
451
+ return createTextBlock(client, chatId, pageBlock.block_id, content);
395
452
  }
396
453
  }
397
454
  case "update_announcement_block": {
398
455
  const requests = [
399
456
  {
400
- block_id: params.block_id,
457
+ block_id: requireString(params.block_id, "block_id"),
401
458
  update_text_elements: {
402
- elements: [{ text_run: { content: params.content } }],
459
+ elements: [{ text_run: { content: requireString(params.content, "content") } }],
403
460
  },
404
461
  },
405
462
  ];
406
- return batchUpdateAnnouncementBlocks(client, params.chat_id, requests);
463
+ return batchUpdateAnnouncementBlocks(client, requireString(params.chat_id, "chat_id"), requests);
407
464
  }
408
465
  // ============== New Chat Management Actions ==============
409
466
  case "create_chat": {
410
- return createChat(client, params.name, params.user_ids, params.description);
467
+ return createChat(
468
+ client,
469
+ requireString(params.name, "name"),
470
+ params.user_ids,
471
+ params.description,
472
+ params.user_id_type,
473
+ );
411
474
  }
412
475
  case "add_members": {
413
- return addMembers(client, params.chat_id, params.user_ids);
476
+ return addMembers(
477
+ client,
478
+ requireString(params.chat_id, "chat_id"),
479
+ requireStringArray(params.user_ids, "user_ids"),
480
+ params.member_id_type,
481
+ );
414
482
  }
415
483
  case "check_bot_in_chat": {
416
- return checkBotInChat(client, params.chat_id);
484
+ return checkBotInChat(client, requireString(params.chat_id, "chat_id"));
417
485
  }
418
486
  case "delete_chat": {
419
- return deleteChat(client, params.chat_id);
487
+ return deleteChat(client, requireString(params.chat_id, "chat_id"));
420
488
  }
421
489
  case "create_session_chat": {
422
490
  return createSessionChat(
423
491
  client,
424
- params.name,
425
- params.user_ids,
492
+ requireString(params.name, "name"),
493
+ requireStringArray(params.user_ids, "user_ids"),
426
494
  params.greeting,
427
495
  params.description,
496
+ params.user_id_type,
428
497
  );
429
498
  }
430
499
  default:
@@ -1,66 +1,61 @@
1
1
  import { Type, type Static } from "@sinclair/typebox";
2
2
 
3
- export const FeishuChatSchema = Type.Union([
4
- Type.Object({
5
- action: Type.Literal("get_announcement_info"),
6
- chat_id: Type.String({ description: "Chat ID to get announcement from" }),
7
- }),
8
- Type.Object({
9
- action: Type.Literal("get_announcement"),
10
- chat_id: Type.String({ description: "Chat ID to get announcement from" }),
11
- }),
12
- Type.Object({
13
- action: Type.Literal("write_announcement"),
14
- chat_id: Type.String({ description: "Chat ID to write announcement to" }),
15
- content: Type.String({ description: "Content to write. For doc format: replaces the entire announcement. For docx format: appends a new text block under the page root." }),
16
- }),
17
- Type.Object({
18
- action: Type.Literal("append_announcement"),
19
- chat_id: Type.String({ description: "Chat ID to append announcement to" }),
20
- content: Type.String({ description: "Markdown content to append to announcement" }),
21
- }),
22
- Type.Object({
23
- action: Type.Literal("list_announcement_blocks"),
24
- chat_id: Type.String({ description: "Chat ID to list announcement blocks from" }),
25
- }),
26
- Type.Object({
27
- action: Type.Literal("get_announcement_block"),
28
- chat_id: Type.String({ description: "Chat ID to get announcement block from" }),
29
- block_id: Type.String({ description: "Block ID (from list_announcement_blocks)" }),
30
- }),
31
- Type.Object({
32
- action: Type.Literal("update_announcement_block"),
33
- chat_id: Type.String({ description: "Chat ID to update announcement block in" }),
34
- block_id: Type.String({ description: "Block ID (from list_announcement_blocks)" }),
35
- content: Type.String({ description: "New text content" }),
36
- }),
37
- // ============== New Chat Management Actions ==============
38
- Type.Object({
39
- action: Type.Literal("create_chat"),
40
- name: Type.String({ description: "Group chat name" }),
41
- user_ids: Type.Optional(Type.Array(Type.String(), { description: "List of user IDs to add to the group" })),
42
- description: Type.Optional(Type.String({ description: "Group chat description" })),
43
- }),
44
- Type.Object({
45
- action: Type.Literal("add_members"),
46
- chat_id: Type.String({ description: "Chat ID to add members to" }),
47
- user_ids: Type.Array(Type.String(), { description: "List of user IDs to add" }),
48
- }),
49
- Type.Object({
50
- action: Type.Literal("check_bot_in_chat"),
51
- chat_id: Type.String({ description: "Chat ID to check" }),
52
- }),
53
- Type.Object({
54
- action: Type.Literal("delete_chat"),
55
- chat_id: Type.String({ description: "Chat ID to delete/dismiss" }),
56
- }),
57
- Type.Object({
58
- action: Type.Literal("create_session_chat"),
59
- name: Type.String({ description: "Session group name" }),
60
- user_ids: Type.Array(Type.String(), { description: "List of user IDs to invite" }),
61
- greeting: Type.Optional(Type.String({ description: "Greeting message to send (default: Hello! I've created this group chat for us to collaborate.)" })),
62
- description: Type.Optional(Type.String({ description: "Group description" })),
63
- }),
64
- ]);
3
+ function stringEnum<T extends readonly string[]>(
4
+ values: T,
5
+ options: { description?: string; default?: T[number] } = {},
6
+ ) {
7
+ return Type.Unsafe<T[number]>({ type: "string", enum: [...values], ...options });
8
+ }
9
+
10
+ const CHAT_ACTION_VALUES = [
11
+ "get_announcement_info",
12
+ "get_announcement",
13
+ "write_announcement",
14
+ "append_announcement",
15
+ "list_announcement_blocks",
16
+ "get_announcement_block",
17
+ "update_announcement_block",
18
+ "create_chat",
19
+ "add_members",
20
+ "check_bot_in_chat",
21
+ "delete_chat",
22
+ "create_session_chat",
23
+ ] as const;
24
+
25
+ const USER_ID_TYPE_VALUES = ["open_id", "user_id", "union_id"] as const;
26
+ const MEMBER_ID_TYPE_VALUES = ["open_id", "user_id", "union_id", "app_id"] as const;
27
+
28
+ export const FeishuChatSchema = Type.Object({
29
+ action: stringEnum(CHAT_ACTION_VALUES, { description: "Chat action" }),
30
+ chat_id: Type.Optional(Type.String({ description: "Chat ID" })),
31
+ content: Type.Optional(Type.String({ description: "Announcement content / block content" })),
32
+ block_id: Type.Optional(Type.String({ description: "Announcement block ID" })),
33
+
34
+ name: Type.Optional(Type.String({ description: "Group chat name" })),
35
+ user_ids: Type.Optional(
36
+ Type.Array(Type.String(), {
37
+ description: "User/member IDs used by create_chat/add_members/create_session_chat",
38
+ }),
39
+ ),
40
+ user_id_type: Type.Optional(
41
+ stringEnum(USER_ID_TYPE_VALUES, {
42
+ description: "ID type for user_ids in create_chat/create_session_chat",
43
+ default: "open_id",
44
+ }),
45
+ ),
46
+ member_id_type: Type.Optional(
47
+ stringEnum(MEMBER_ID_TYPE_VALUES, {
48
+ description: "ID type for add_members (supports app_id for bots)",
49
+ default: "open_id",
50
+ }),
51
+ ),
52
+ greeting: Type.Optional(
53
+ Type.String({
54
+ description:
55
+ "Greeting message for create_session_chat (default: Hello! I've created this group chat for us to collaborate.)",
56
+ }),
57
+ ),
58
+ description: Type.Optional(Type.String({ description: "Group chat description" })),
59
+ });
65
60
 
66
61
  export type FeishuChatParams = Static<typeof FeishuChatSchema>;
@@ -44,6 +44,13 @@ function normalizePageSize(pageSize?: number) {
44
44
  return pageSize;
45
45
  }
46
46
 
47
+ function requireString(value: unknown, field: string): string {
48
+ if (typeof value !== "string" || value.trim().length === 0) {
49
+ throw new Error(`${field} is required`);
50
+ }
51
+ return value;
52
+ }
53
+
47
54
  function omitUndefined<T extends Record<string, unknown>>(obj: T): T {
48
55
  return Object.fromEntries(Object.entries(obj).filter(([, value]) => value !== undefined)) as T;
49
56
  }
@@ -304,37 +311,79 @@ export async function runDocAction(
304
311
  ) {
305
312
  switch (params.action) {
306
313
  case "read":
307
- return readDoc(client, params.doc_token);
314
+ return readDoc(client, requireString(params.doc_token, "doc_token"));
308
315
  case "write":
309
- return writeDoc(client, params.doc_token, params.content, mediaMaxBytes);
316
+ return writeDoc(
317
+ client,
318
+ requireString(params.doc_token, "doc_token"),
319
+ requireString(params.content, "content"),
320
+ mediaMaxBytes,
321
+ );
310
322
  case "append":
311
- return appendDoc(client, params.doc_token, params.content, mediaMaxBytes);
323
+ return appendDoc(
324
+ client,
325
+ requireString(params.doc_token, "doc_token"),
326
+ requireString(params.content, "content"),
327
+ mediaMaxBytes,
328
+ );
312
329
  case "create":
313
- return createDoc(client, params.title, params.folder_token);
330
+ return createDoc(client, requireString(params.title, "title"), params.folder_token);
314
331
  case "create_and_write":
315
332
  return createAndWriteDoc(
316
333
  client,
317
- params.title,
318
- params.content,
334
+ requireString(params.title, "title"),
335
+ requireString(params.content, "content"),
319
336
  mediaMaxBytes,
320
337
  params.folder_token,
321
338
  );
322
339
  case "list_blocks":
323
- return listBlocks(client, params.doc_token);
340
+ return listBlocks(client, requireString(params.doc_token, "doc_token"));
324
341
  case "get_block":
325
- return getBlock(client, params.doc_token, params.block_id);
342
+ return getBlock(
343
+ client,
344
+ requireString(params.doc_token, "doc_token"),
345
+ requireString(params.block_id, "block_id"),
346
+ );
326
347
  case "update_block":
327
- return updateBlock(client, params.doc_token, params.block_id, params.content);
348
+ return updateBlock(
349
+ client,
350
+ requireString(params.doc_token, "doc_token"),
351
+ requireString(params.block_id, "block_id"),
352
+ requireString(params.content, "content"),
353
+ );
328
354
  case "delete_block":
329
- return deleteBlock(client, params.doc_token, params.block_id);
355
+ return deleteBlock(
356
+ client,
357
+ requireString(params.doc_token, "doc_token"),
358
+ requireString(params.block_id, "block_id"),
359
+ );
330
360
  case "list_comments":
331
- return listComments(client, params.doc_token, params.page_token, params.page_size);
361
+ return listComments(
362
+ client,
363
+ requireString(params.doc_token, "doc_token"),
364
+ params.page_token,
365
+ params.page_size,
366
+ );
332
367
  case "create_comment":
333
- return createComment(client, params.doc_token, params.content);
368
+ return createComment(
369
+ client,
370
+ requireString(params.doc_token, "doc_token"),
371
+ requireString(params.content, "content"),
372
+ );
334
373
  case "get_comment":
335
- return getComment(client, params.doc_token, params.comment_id);
374
+ return getComment(
375
+ client,
376
+ requireString(params.doc_token, "doc_token"),
377
+ requireString(params.comment_id, "comment_id"),
378
+ );
336
379
  case "list_comment_replies":
337
- return listCommentReplies(client, params.doc_token, params.comment_id, params.page_token, params.page_size);
380
+ return listCommentReplies(
381
+ client,
382
+ requireString(params.doc_token, "doc_token"),
383
+ requireString(params.comment_id, "comment_id"),
384
+ params.page_token,
385
+ params.page_size,
386
+ );
338
387
  default:
339
388
  return { error: `Unknown action: ${(params as any).action}` };
340
389
  }
@@ -1,85 +1,49 @@
1
1
  import { Type, type Static } from "@sinclair/typebox";
2
2
 
3
- export const FeishuDocSchema = Type.Union([
4
- Type.Object({
5
- action: Type.Literal("read"),
6
- doc_token: Type.String({
3
+ function stringEnum<T extends readonly string[]>(
4
+ values: T,
5
+ options: { description?: string; default?: T[number] } = {},
6
+ ) {
7
+ return Type.Unsafe<T[number]>({ type: "string", enum: [...values], ...options });
8
+ }
9
+
10
+ const DOC_ACTION_VALUES = [
11
+ "read",
12
+ "write",
13
+ "append",
14
+ "create",
15
+ "create_and_write",
16
+ "list_blocks",
17
+ "get_block",
18
+ "update_block",
19
+ "delete_block",
20
+ "list_comments",
21
+ "create_comment",
22
+ "get_comment",
23
+ "list_comment_replies",
24
+ ] as const;
25
+
26
+ export const FeishuDocSchema = Type.Object({
27
+ action: stringEnum(DOC_ACTION_VALUES, { description: "Document action" }),
28
+ doc_token: Type.Optional(
29
+ Type.String({
7
30
  description:
8
31
  "Document token (extract from URL /docx/XXX or /docs/XXX). Supports both new (docx) and legacy (doc) formats.",
9
32
  }),
10
- }),
11
- Type.Object({
12
- action: Type.Literal("write"),
13
- doc_token: Type.String({ description: "Document token" }),
14
- content: Type.String({
15
- description: "Markdown content to write (replaces entire document content)",
16
- }),
17
- }),
18
- Type.Object({
19
- action: Type.Literal("append"),
20
- doc_token: Type.String({ description: "Document token" }),
21
- content: Type.String({ description: "Markdown content to append to end of document" }),
22
- }),
23
- Type.Object({
24
- action: Type.Literal("create"),
25
- title: Type.String({ description: "Document title" }),
26
- folder_token: Type.Optional(Type.String({ description: "Target folder token (optional)" })),
27
- }),
28
- Type.Object({
29
- action: Type.Literal("create_and_write"),
30
- title: Type.String({ description: "Document title" }),
31
- content: Type.String({
32
- description: "Markdown content to write immediately after document creation",
33
+ ),
34
+ content: Type.Optional(
35
+ Type.String({
36
+ description: "Markdown content for write/append/comment/update operations",
33
37
  }),
34
- folder_token: Type.Optional(Type.String({ description: "Target folder token (optional)" })),
35
- }),
36
- Type.Object({
37
- action: Type.Literal("list_blocks"),
38
- doc_token: Type.String({ description: "Document token" }),
39
- }),
40
- Type.Object({
41
- action: Type.Literal("get_block"),
42
- doc_token: Type.String({ description: "Document token" }),
43
- block_id: Type.String({ description: "Block ID (from list_blocks)" }),
44
- }),
45
- Type.Object({
46
- action: Type.Literal("update_block"),
47
- doc_token: Type.String({ description: "Document token" }),
48
- block_id: Type.String({ description: "Block ID (from list_blocks)" }),
49
- content: Type.String({ description: "New text content" }),
50
- }),
51
- Type.Object({
52
- action: Type.Literal("delete_block"),
53
- doc_token: Type.String({ description: "Document token" }),
54
- block_id: Type.String({ description: "Block ID" }),
55
- }),
56
- Type.Object({
57
- action: Type.Literal("list_comments"),
58
- doc_token: Type.String({ description: "Document token" }),
59
- page_token: Type.Optional(Type.String({ description: "Page token for pagination" })),
60
- page_size: Type.Optional(
61
- Type.Integer({ minimum: 1, description: "Page size, default 50 (positive integer)" }),
62
- ),
63
- }),
64
- Type.Object({
65
- action: Type.Literal("create_comment"),
66
- doc_token: Type.String({ description: "Document token" }),
67
- content: Type.String({ description: "Comment content" }),
68
- }),
69
- Type.Object({
70
- action: Type.Literal("get_comment"),
71
- doc_token: Type.String({ description: "Document token" }),
72
- comment_id: Type.String({ description: "Comment ID" }),
73
- }),
74
- Type.Object({
75
- action: Type.Literal("list_comment_replies"),
76
- doc_token: Type.String({ description: "Document token" }),
77
- comment_id: Type.String({ description: "Comment ID" }),
78
- page_token: Type.Optional(Type.String({ description: "Page token for pagination" })),
79
- page_size: Type.Optional(
80
- Type.Integer({ minimum: 1, description: "Page size, default 50 (positive integer)" }),
81
- ),
82
- }),
83
- ]);
38
+ ),
39
+ title: Type.Optional(Type.String({ description: "Document title (for create/create_and_write)" })),
40
+ folder_token: Type.Optional(Type.String({ description: "Target folder token (optional)" })),
41
+ block_id: Type.Optional(Type.String({ description: "Block ID (from list_blocks)" })),
42
+ comment_id: Type.Optional(Type.String({ description: "Comment ID" })),
43
+ page_token: Type.Optional(Type.String({ description: "Page token for pagination" })),
44
+ page_size: Type.Optional(
45
+ Type.Integer({ minimum: 1, description: "Page size, default 50 (positive integer)" }),
46
+ ),
47
+ });
84
48
 
85
49
  export type FeishuDocParams = Static<typeof FeishuDocSchema>;
@@ -1,6 +1,7 @@
1
1
  import type * as Lark from "@larksuiteoapi/node-sdk";
2
2
  import { Readable } from "stream";
3
3
  import { getFeishuRuntime } from "./runtime.js";
4
+ import { getCurrentFeishuToolContext } from "./tools-common/tool-context.js";
4
5
 
5
6
  const BLOCK_TYPE_NAMES: Record<number, string> = {
6
7
  1: "Page",
@@ -572,6 +573,24 @@ export async function createDoc(
572
573
  });
573
574
  if (res.code !== 0) throw new Error(res.msg);
574
575
  const doc = res.data?.document;
576
+
577
+ // Auto-share with the requesting user so they can edit the document.
578
+ const senderOpenId = getCurrentFeishuToolContext()?.senderOpenId;
579
+ if (doc?.document_id && senderOpenId) {
580
+ try {
581
+ const permRes = await client.drive.permissionMember.create({
582
+ path: { token: doc.document_id },
583
+ params: { type: "docx", need_notification: false },
584
+ data: { member_type: "openid", member_id: senderOpenId, perm: "edit" },
585
+ });
586
+ if (permRes.code !== 0) {
587
+ console.warn(`[feishu_doc] Failed to auto-share doc ${doc.document_id}: ${permRes.msg}`);
588
+ }
589
+ } catch (err) {
590
+ console.warn(`[feishu_doc] Failed to auto-share doc ${doc.document_id}:`, err);
591
+ }
592
+ }
593
+
575
594
  return {
576
595
  document_id: doc?.document_id,
577
596
  title: doc?.title,