@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 +1 -1
- package/src/bot.ts +2 -0
- package/src/chat-tools/actions.ts +102 -33
- package/src/chat-tools/schemas.ts +57 -62
- package/src/doc-tools/actions.ts +63 -14
- package/src/doc-tools/schemas.ts +41 -77
- package/src/doc-write-service.ts +19 -0
- package/src/drive-tools/actions.ts +18 -6
- package/src/drive-tools/schemas.ts +45 -58
- package/src/media.ts +11 -43
- package/src/outbound.ts +2 -2
- package/src/perm-tools/actions.ts +27 -3
- package/src/perm-tools/schemas.ts +39 -45
- package/src/task-tools/actions.ts +145 -10
- package/src/task-tools/register.ts +56 -1
- package/src/task-tools/schemas.ts +23 -18
- package/src/tools-common/tool-context.ts +1 -0
- package/src/wiki-tools/actions.ts +24 -6
- package/src/wiki-tools/schemas.ts +32 -51
package/package.json
CHANGED
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
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
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(
|
|
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:
|
|
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(
|
|
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:
|
|
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(
|
|
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
|
|
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,
|
|
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,
|
|
433
|
+
return createTextBlock(client, chatId, pageBlock.block_id, content);
|
|
379
434
|
}
|
|
380
435
|
}
|
|
381
436
|
case "append_announcement": {
|
|
382
|
-
const
|
|
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" +
|
|
386
|
-
return writeDocAnnouncement(client,
|
|
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,
|
|
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(
|
|
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(
|
|
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
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
})
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
}),
|
|
31
|
-
Type.
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
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>;
|
package/src/doc-tools/actions.ts
CHANGED
|
@@ -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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
}
|
package/src/doc-tools/schemas.ts
CHANGED
|
@@ -1,85 +1,49 @@
|
|
|
1
1
|
import { Type, type Static } from "@sinclair/typebox";
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
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.
|
|
12
|
-
|
|
13
|
-
|
|
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
|
-
|
|
35
|
-
}),
|
|
36
|
-
Type.
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
}),
|
|
40
|
-
Type.
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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>;
|
package/src/doc-write-service.ts
CHANGED
|
@@ -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,
|