@m1heng-clawd/feishu 0.1.14 → 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/README.md +82 -2
- package/index.ts +7 -2
- package/package.json +5 -4
- package/skills/feishu-chat/SKILL.md +139 -0
- package/skills/feishu-urgent/SKILL.md +107 -0
- package/src/bot.ts +391 -45
- package/src/channel.ts +2 -0
- package/src/chat-tools/actions.ts +502 -0
- package/src/chat-tools/common.ts +13 -0
- package/src/chat-tools/index.ts +2 -0
- package/src/chat-tools/register.ts +72 -0
- package/src/chat-tools/schemas.ts +61 -0
- package/src/client.ts +13 -0
- package/src/config-schema.ts +6 -0
- 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 +75 -27
- package/src/drive-tools/schemas.ts +45 -58
- package/src/media-duration.ts +190 -0
- package/src/media.ts +50 -27
- package/src/mention.ts +1 -1
- package/src/monitor.ts +32 -8
- package/src/outbound.ts +2 -2
- package/src/perm-tools/actions.ts +27 -3
- package/src/perm-tools/schemas.ts +39 -45
- package/src/policy.ts +7 -3
- package/src/reply-dispatcher.ts +97 -13
- package/src/send.ts +360 -42
- package/src/streaming-card.ts +32 -4
- 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/tools-config.ts +9 -2
- package/src/types.ts +8 -1
- package/src/typing.ts +115 -5
- package/src/urgent-tools/actions.ts +84 -0
- package/src/urgent-tools/index.ts +3 -0
- package/src/urgent-tools/register.ts +64 -0
- package/src/urgent-tools/schemas.ts +25 -0
- package/src/wiki-tools/actions.ts +24 -6
- package/src/wiki-tools/schemas.ts +32 -51
package/src/config-schema.ts
CHANGED
|
@@ -4,6 +4,7 @@ export { z };
|
|
|
4
4
|
const DmPolicySchema = z.enum(["open", "pairing", "allowlist"]);
|
|
5
5
|
const GroupPolicySchema = z.enum(["open", "allowlist", "disabled"]);
|
|
6
6
|
const GroupCommandMentionBypassSchema = z.enum(["never", "single_bot", "always"]).optional();
|
|
7
|
+
const AllowMentionlessInMultiBotGroupSchema = z.boolean().optional();
|
|
7
8
|
const FeishuDomainSchema = z.union([
|
|
8
9
|
z.enum(["feishu", "lark"]),
|
|
9
10
|
z.string().url().startsWith("https://"),
|
|
@@ -88,6 +89,8 @@ const FeishuToolsConfigSchema = z
|
|
|
88
89
|
perm: z.boolean().optional(), // Permission management (default: false, sensitive)
|
|
89
90
|
scopes: z.boolean().optional(), // App scopes diagnostic (default: true)
|
|
90
91
|
task: z.boolean().optional(), // Task operations (default: true)
|
|
92
|
+
chat: z.boolean().optional(), // Chat management operations (default: true)
|
|
93
|
+
urgent: z.boolean().optional(), // Buzz/urgent notifications (default: true)
|
|
91
94
|
})
|
|
92
95
|
.strict()
|
|
93
96
|
.optional();
|
|
@@ -106,6 +109,7 @@ export const FeishuGroupSchema = z
|
|
|
106
109
|
.object({
|
|
107
110
|
requireMention: z.boolean().optional(),
|
|
108
111
|
groupCommandMentionBypass: GroupCommandMentionBypassSchema,
|
|
112
|
+
allowMentionlessInMultiBotGroup: AllowMentionlessInMultiBotGroupSchema,
|
|
109
113
|
tools: ToolPolicySchema,
|
|
110
114
|
skills: z.array(z.string()).optional(),
|
|
111
115
|
enabled: z.boolean().optional(),
|
|
@@ -140,6 +144,7 @@ export const FeishuAccountConfigSchema = z
|
|
|
140
144
|
groupAllowFrom: z.array(z.union([z.string(), z.number()])).optional(),
|
|
141
145
|
requireMention: z.boolean().optional(),
|
|
142
146
|
groupCommandMentionBypass: GroupCommandMentionBypassSchema,
|
|
147
|
+
allowMentionlessInMultiBotGroup: AllowMentionlessInMultiBotGroupSchema,
|
|
143
148
|
groups: z.record(z.string(), FeishuGroupSchema.optional()).optional(),
|
|
144
149
|
historyLimit: z.number().int().min(0).optional(),
|
|
145
150
|
dmHistoryLimit: z.number().int().min(0).optional(),
|
|
@@ -177,6 +182,7 @@ export const FeishuConfigSchema = z
|
|
|
177
182
|
groupAllowFrom: z.array(z.union([z.string(), z.number()])).optional(),
|
|
178
183
|
requireMention: z.boolean().optional().default(true),
|
|
179
184
|
groupCommandMentionBypass: GroupCommandMentionBypassSchema.default("single_bot"),
|
|
185
|
+
allowMentionlessInMultiBotGroup: AllowMentionlessInMultiBotGroupSchema.default(false),
|
|
180
186
|
groups: z.record(z.string(), FeishuGroupSchema.optional()).optional(),
|
|
181
187
|
topicSessionMode: TopicSessionModeSchema,
|
|
182
188
|
historyLimit: z.number().int().min(0).optional(),
|
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,
|
|
@@ -5,6 +5,37 @@ import type { FeishuDriveParams } from "./schemas.js";
|
|
|
5
5
|
type DriveMoveType = "doc" | "docx" | "sheet" | "bitable" | "folder" | "file" | "mindnote" | "slides";
|
|
6
6
|
type DriveDeleteType = DriveMoveType | "shortcut";
|
|
7
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
|
+
async function getFileType(client: DriveClient, fileToken: string): Promise<string> {
|
|
16
|
+
let pageToken: string | undefined;
|
|
17
|
+
|
|
18
|
+
do {
|
|
19
|
+
const listRes = await runDriveApiCall("drive.file.list", () =>
|
|
20
|
+
client.drive.file.list({
|
|
21
|
+
params: pageToken ? { page_token: pageToken } : {},
|
|
22
|
+
}),
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
const file = listRes.data?.files?.find((f: any) => f.token === fileToken);
|
|
26
|
+
if (file) {
|
|
27
|
+
if (!file.type) {
|
|
28
|
+
throw new Error(`File found but no type for ${fileToken}. Please provide the 'type' parameter explicitly.`);
|
|
29
|
+
}
|
|
30
|
+
return file.type;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
pageToken = listRes.data?.next_page_token;
|
|
34
|
+
} while (pageToken);
|
|
35
|
+
|
|
36
|
+
throw new Error(`File not found: ${fileToken}. Please provide the 'type' parameter explicitly.`);
|
|
37
|
+
}
|
|
38
|
+
|
|
8
39
|
async function getRootFolderToken(client: DriveClient): Promise<string> {
|
|
9
40
|
// Use generic HTTP client to call the root folder meta API
|
|
10
41
|
// as it's not directly exposed in the SDK.
|
|
@@ -46,27 +77,32 @@ async function listFolder(client: DriveClient, folderToken?: string) {
|
|
|
46
77
|
}
|
|
47
78
|
|
|
48
79
|
async function getFileInfo(client: DriveClient, fileToken: string, folderToken?: string) {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
80
|
+
let pageToken: string | undefined;
|
|
81
|
+
|
|
82
|
+
do {
|
|
83
|
+
const res = await runDriveApiCall("drive.file.list", () =>
|
|
84
|
+
client.drive.file.list({
|
|
85
|
+
params: folderToken ? { folder_token: folderToken, page_token: pageToken } : { page_token: pageToken },
|
|
86
|
+
}),
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
const file = res.data?.files?.find((f: any) => f.token === fileToken);
|
|
90
|
+
if (file) {
|
|
91
|
+
return {
|
|
92
|
+
token: file.token,
|
|
93
|
+
name: file.name,
|
|
94
|
+
type: file.type,
|
|
95
|
+
url: file.url,
|
|
96
|
+
created_time: file.created_time,
|
|
97
|
+
modified_time: file.modified_time,
|
|
98
|
+
owner_id: file.owner_id,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
55
101
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
throw new Error(`File not found: ${fileToken}`);
|
|
59
|
-
}
|
|
102
|
+
pageToken = res.data?.next_page_token;
|
|
103
|
+
} while (pageToken);
|
|
60
104
|
|
|
61
|
-
|
|
62
|
-
token: file.token,
|
|
63
|
-
name: file.name,
|
|
64
|
-
type: file.type,
|
|
65
|
-
url: file.url,
|
|
66
|
-
created_time: file.created_time,
|
|
67
|
-
modified_time: file.modified_time,
|
|
68
|
-
owner_id: file.owner_id,
|
|
69
|
-
};
|
|
105
|
+
throw new Error(`File not found: ${fileToken}`);
|
|
70
106
|
}
|
|
71
107
|
|
|
72
108
|
async function createFolder(client: DriveClient, name: string, folderToken?: string) {
|
|
@@ -119,12 +155,18 @@ async function moveFile(
|
|
|
119
155
|
};
|
|
120
156
|
}
|
|
121
157
|
|
|
122
|
-
async function deleteFile(client: DriveClient, fileToken: string, type
|
|
158
|
+
async function deleteFile(client: DriveClient, fileToken: string, type?: string) {
|
|
159
|
+
let effectiveType = type;
|
|
160
|
+
|
|
161
|
+
if (!effectiveType) {
|
|
162
|
+
effectiveType = await getFileType(client, fileToken);
|
|
163
|
+
}
|
|
164
|
+
|
|
123
165
|
const res = await runDriveApiCall("drive.file.delete", () =>
|
|
124
166
|
client.drive.file.delete({
|
|
125
167
|
path: { file_token: fileToken },
|
|
126
168
|
params: {
|
|
127
|
-
type:
|
|
169
|
+
type: effectiveType as DriveDeleteType,
|
|
128
170
|
},
|
|
129
171
|
}),
|
|
130
172
|
);
|
|
@@ -132,6 +174,7 @@ async function deleteFile(client: DriveClient, fileToken: string, type: string)
|
|
|
132
174
|
return {
|
|
133
175
|
success: true,
|
|
134
176
|
task_id: res.data?.task_id,
|
|
177
|
+
type_used: effectiveType,
|
|
135
178
|
};
|
|
136
179
|
}
|
|
137
180
|
|
|
@@ -160,18 +203,23 @@ export async function runDriveAction(
|
|
|
160
203
|
case "list":
|
|
161
204
|
return listFolder(client, params.folder_token);
|
|
162
205
|
case "info":
|
|
163
|
-
return getFileInfo(client, params.file_token);
|
|
206
|
+
return getFileInfo(client, requireString(params.file_token, "file_token"));
|
|
164
207
|
case "create_folder":
|
|
165
|
-
return createFolder(client, params.name, params.folder_token);
|
|
208
|
+
return createFolder(client, requireString(params.name, "name"), params.folder_token);
|
|
166
209
|
case "move":
|
|
167
|
-
return moveFile(
|
|
210
|
+
return moveFile(
|
|
211
|
+
client,
|
|
212
|
+
requireString(params.file_token, "file_token"),
|
|
213
|
+
requireString(params.type, "type"),
|
|
214
|
+
requireString(params.folder_token, "folder_token"),
|
|
215
|
+
);
|
|
168
216
|
case "delete":
|
|
169
|
-
return deleteFile(client, params.file_token, params.type);
|
|
217
|
+
return deleteFile(client, requireString(params.file_token, "file_token"), params.type);
|
|
170
218
|
case "import_document":
|
|
171
219
|
return importDocument(
|
|
172
220
|
client,
|
|
173
|
-
params.title,
|
|
174
|
-
params.content,
|
|
221
|
+
requireString(params.title, "title"),
|
|
222
|
+
requireString(params.content, "content"),
|
|
175
223
|
mediaMaxBytes,
|
|
176
224
|
params.folder_token,
|
|
177
225
|
params.doc_type || "docx",
|
|
@@ -1,67 +1,54 @@
|
|
|
1
1
|
import { Type, type Static } from "@sinclair/typebox";
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
Type.
|
|
8
|
-
|
|
9
|
-
Type.Literal("file"),
|
|
10
|
-
Type.Literal("mindnote"),
|
|
11
|
-
Type.Literal("shortcut"),
|
|
12
|
-
]);
|
|
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
|
+
}
|
|
13
9
|
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
10
|
+
const FILE_TYPE_VALUES = [
|
|
11
|
+
"doc",
|
|
12
|
+
"docx",
|
|
13
|
+
"sheet",
|
|
14
|
+
"bitable",
|
|
15
|
+
"folder",
|
|
16
|
+
"file",
|
|
17
|
+
"mindnote",
|
|
18
|
+
"shortcut",
|
|
19
|
+
] as const;
|
|
18
20
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}),
|
|
38
|
-
Type.
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
type: FileType,
|
|
42
|
-
folder_token: Type.String({ description: "Target folder token" }),
|
|
43
|
-
}),
|
|
44
|
-
Type.Object({
|
|
45
|
-
action: Type.Literal("delete"),
|
|
46
|
-
file_token: Type.String({ description: "File token to delete" }),
|
|
47
|
-
type: FileType,
|
|
48
|
-
}),
|
|
49
|
-
Type.Object({
|
|
50
|
-
action: Type.Literal("import_document"),
|
|
51
|
-
title: Type.String({
|
|
52
|
-
description: "Document title",
|
|
53
|
-
}),
|
|
54
|
-
content: Type.String({
|
|
21
|
+
const DOC_TYPE_VALUES = ["docx", "doc"] as const;
|
|
22
|
+
|
|
23
|
+
const DRIVE_ACTION_VALUES = [
|
|
24
|
+
"list",
|
|
25
|
+
"info",
|
|
26
|
+
"create_folder",
|
|
27
|
+
"move",
|
|
28
|
+
"delete",
|
|
29
|
+
"import_document",
|
|
30
|
+
] as const;
|
|
31
|
+
|
|
32
|
+
export const FeishuDriveSchema = Type.Object({
|
|
33
|
+
action: stringEnum(DRIVE_ACTION_VALUES, { description: "Drive action" }),
|
|
34
|
+
folder_token: Type.Optional(
|
|
35
|
+
Type.String({ description: "Folder token (optional, omit for root directory)" }),
|
|
36
|
+
),
|
|
37
|
+
file_token: Type.Optional(Type.String({ description: "File or folder token" })),
|
|
38
|
+
type: Type.Optional(stringEnum(FILE_TYPE_VALUES, { description: "File type" })),
|
|
39
|
+
name: Type.Optional(Type.String({ description: "Folder name (for create_folder)" })),
|
|
40
|
+
title: Type.Optional(Type.String({ description: "Document title (for import_document)" })),
|
|
41
|
+
content: Type.Optional(
|
|
42
|
+
Type.String({
|
|
55
43
|
description:
|
|
56
44
|
"Markdown content to import. Supports full Markdown syntax including tables, lists, code blocks, etc.",
|
|
57
45
|
}),
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
),
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
]);
|
|
46
|
+
),
|
|
47
|
+
doc_type: Type.Optional(
|
|
48
|
+
stringEnum(DOC_TYPE_VALUES, {
|
|
49
|
+
description: "Document type for import_document (docx default, doc legacy)",
|
|
50
|
+
}),
|
|
51
|
+
),
|
|
52
|
+
});
|
|
66
53
|
|
|
67
54
|
export type FeishuDriveParams = Static<typeof FeishuDriveSchema>;
|