@m1heng-clawd/feishu 0.1.10 → 0.1.12

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 (47) hide show
  1. package/README.md +280 -7
  2. package/index.ts +6 -6
  3. package/package.json +13 -3
  4. package/skills/feishu-doc/SKILL.md +64 -2
  5. package/skills/feishu-task/SKILL.md +210 -0
  6. package/src/bitable-tools/index.ts +1 -0
  7. package/src/bot.ts +182 -71
  8. package/src/channel.ts +2 -0
  9. package/src/config-schema.ts +4 -0
  10. package/src/doc-tools/actions.ts +341 -0
  11. package/src/doc-tools/common.ts +33 -0
  12. package/src/doc-tools/index.ts +2 -0
  13. package/src/doc-tools/register.ts +90 -0
  14. package/src/{doc-schema.ts → doc-tools/schemas.ts} +39 -1
  15. package/src/{docx.ts → doc-write-service.ts} +204 -351
  16. package/src/drive-tools/actions.ts +182 -0
  17. package/src/drive-tools/common.ts +18 -0
  18. package/src/drive-tools/index.ts +2 -0
  19. package/src/drive-tools/register.ts +71 -0
  20. package/src/{drive-schema.ts → drive-tools/schemas.ts} +2 -1
  21. package/src/media.ts +5 -3
  22. package/src/onboarding.ts +14 -4
  23. package/src/perm-tools/actions.ts +111 -0
  24. package/src/perm-tools/common.ts +18 -0
  25. package/src/perm-tools/index.ts +2 -0
  26. package/src/perm-tools/register.ts +65 -0
  27. package/src/policy.ts +13 -0
  28. package/src/reply-dispatcher.ts +6 -4
  29. package/src/send.ts +33 -2
  30. package/src/task-tools/actions.ts +466 -13
  31. package/src/task-tools/constants.ts +13 -0
  32. package/src/task-tools/index.ts +1 -0
  33. package/src/task-tools/register.ts +175 -13
  34. package/src/task-tools/schemas.ts +433 -4
  35. package/src/text/markdown-links.ts +104 -0
  36. package/src/types.ts +1 -0
  37. package/src/wiki-tools/actions.ts +166 -0
  38. package/src/wiki-tools/common.ts +18 -0
  39. package/src/wiki-tools/index.ts +2 -0
  40. package/src/wiki-tools/register.ts +66 -0
  41. package/src/bitable.ts +0 -1
  42. package/src/drive.ts +0 -264
  43. package/src/perm.ts +0 -165
  44. package/src/task.ts +0 -1
  45. package/src/wiki.ts +0 -223
  46. /package/src/{perm-schema.ts → perm-tools/schemas.ts} +0 -0
  47. /package/src/{wiki-schema.ts → wiki-tools/schemas.ts} +0 -0
@@ -0,0 +1,166 @@
1
+ import { runWikiApiCall, type WikiClient } from "./common.js";
2
+ import type { FeishuWikiParams } from "./schemas.js";
3
+
4
+ type ObjType = "doc" | "sheet" | "mindnote" | "bitable" | "file" | "docx" | "slides";
5
+
6
+ const WIKI_ACCESS_HINT =
7
+ "To grant wiki access: Open wiki space -> Settings -> Members -> Add the bot. " +
8
+ "See: https://open.feishu.cn/document/server-docs/docs/wiki-v2/wiki-qa#a40ad4ca";
9
+
10
+ async function listSpaces(client: WikiClient) {
11
+ const res = await runWikiApiCall("wiki.space.list", () => client.wiki.space.list({}));
12
+ const spaces =
13
+ res.data?.items?.map((s) => ({
14
+ space_id: s.space_id,
15
+ name: s.name,
16
+ description: s.description,
17
+ visibility: s.visibility,
18
+ })) ?? [];
19
+
20
+ return {
21
+ spaces,
22
+ ...(spaces.length === 0 && { hint: WIKI_ACCESS_HINT }),
23
+ };
24
+ }
25
+
26
+ async function listNodes(client: WikiClient, spaceId: string, parentNodeToken?: string) {
27
+ const res = await runWikiApiCall("wiki.spaceNode.list", () =>
28
+ client.wiki.spaceNode.list({
29
+ path: { space_id: spaceId },
30
+ params: { parent_node_token: parentNodeToken },
31
+ }),
32
+ );
33
+
34
+ return {
35
+ nodes:
36
+ res.data?.items?.map((n) => ({
37
+ node_token: n.node_token,
38
+ obj_token: n.obj_token,
39
+ obj_type: n.obj_type,
40
+ title: n.title,
41
+ has_child: n.has_child,
42
+ })) ?? [],
43
+ };
44
+ }
45
+
46
+ async function getNode(client: WikiClient, token: string) {
47
+ const res = await runWikiApiCall("wiki.space.getNode", () =>
48
+ client.wiki.space.getNode({
49
+ params: { token },
50
+ }),
51
+ );
52
+
53
+ const node = res.data?.node;
54
+ return {
55
+ node_token: node?.node_token,
56
+ space_id: node?.space_id,
57
+ obj_token: node?.obj_token,
58
+ obj_type: node?.obj_type,
59
+ title: node?.title,
60
+ parent_node_token: node?.parent_node_token,
61
+ has_child: node?.has_child,
62
+ creator: node?.creator,
63
+ create_time: node?.node_create_time,
64
+ };
65
+ }
66
+
67
+ async function createNode(
68
+ client: WikiClient,
69
+ spaceId: string,
70
+ title: string,
71
+ objType?: string,
72
+ parentNodeToken?: string,
73
+ ) {
74
+ const res = await runWikiApiCall("wiki.spaceNode.create", () =>
75
+ client.wiki.spaceNode.create({
76
+ path: { space_id: spaceId },
77
+ data: {
78
+ obj_type: (objType as ObjType) || "docx",
79
+ node_type: "origin" as const,
80
+ title,
81
+ parent_node_token: parentNodeToken,
82
+ },
83
+ }),
84
+ );
85
+
86
+ const node = res.data?.node;
87
+ return {
88
+ node_token: node?.node_token,
89
+ obj_token: node?.obj_token,
90
+ obj_type: node?.obj_type,
91
+ title: node?.title,
92
+ };
93
+ }
94
+
95
+ async function moveNode(
96
+ client: WikiClient,
97
+ spaceId: string,
98
+ nodeToken: string,
99
+ targetSpaceId?: string,
100
+ targetParentToken?: string,
101
+ ) {
102
+ const res = await runWikiApiCall("wiki.spaceNode.move", () =>
103
+ client.wiki.spaceNode.move({
104
+ path: { space_id: spaceId, node_token: nodeToken },
105
+ data: {
106
+ target_space_id: targetSpaceId || spaceId,
107
+ target_parent_token: targetParentToken,
108
+ },
109
+ }),
110
+ );
111
+
112
+ return {
113
+ success: true,
114
+ node_token: res.data?.node?.node_token,
115
+ };
116
+ }
117
+
118
+ async function renameNode(
119
+ client: WikiClient,
120
+ spaceId: string,
121
+ nodeToken: string,
122
+ title: string,
123
+ ) {
124
+ await runWikiApiCall("wiki.spaceNode.updateTitle", () =>
125
+ client.wiki.spaceNode.updateTitle({
126
+ path: { space_id: spaceId, node_token: nodeToken },
127
+ data: { title },
128
+ }),
129
+ );
130
+
131
+ return {
132
+ success: true,
133
+ node_token: nodeToken,
134
+ title,
135
+ };
136
+ }
137
+
138
+ export async function runWikiAction(client: WikiClient, params: FeishuWikiParams) {
139
+ switch (params.action) {
140
+ case "spaces":
141
+ return listSpaces(client);
142
+ case "nodes":
143
+ return listNodes(client, params.space_id, params.parent_node_token);
144
+ case "get":
145
+ return getNode(client, params.token);
146
+ case "search":
147
+ return {
148
+ error:
149
+ "Search is not available. Use feishu_wiki with action: 'nodes' to browse or action: 'get' to lookup by token.",
150
+ };
151
+ case "create":
152
+ return createNode(client, params.space_id, params.title, params.obj_type, params.parent_node_token);
153
+ case "move":
154
+ return moveNode(
155
+ client,
156
+ params.space_id,
157
+ params.node_token,
158
+ params.target_space_id,
159
+ params.target_parent_token,
160
+ );
161
+ case "rename":
162
+ return renameNode(client, params.space_id, params.node_token, params.title);
163
+ default:
164
+ return { error: `Unknown action: ${(params as any).action}` };
165
+ }
166
+ }
@@ -0,0 +1,18 @@
1
+ import { createFeishuClient } from "../client.js";
2
+ import {
3
+ errorResult,
4
+ json,
5
+ runFeishuApiCall,
6
+ type FeishuApiResponse,
7
+ } from "../tools-common/feishu-api.js";
8
+
9
+ export type WikiClient = ReturnType<typeof createFeishuClient>;
10
+
11
+ export { json, errorResult };
12
+
13
+ export async function runWikiApiCall<T extends FeishuApiResponse>(
14
+ context: string,
15
+ fn: () => Promise<T>,
16
+ ): Promise<T> {
17
+ return runFeishuApiCall(context, fn);
18
+ }
@@ -0,0 +1,2 @@
1
+ export { registerFeishuWikiTools } from "./register.js";
2
+ export { FeishuWikiSchema, type FeishuWikiParams } from "./schemas.js";
@@ -0,0 +1,66 @@
1
+ import type { TSchema } from "@sinclair/typebox";
2
+ import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
3
+ import { hasFeishuToolEnabledForAnyAccount, withFeishuToolClient } from "../tools-common/tool-exec.js";
4
+ import { runWikiAction } from "./actions.js";
5
+ import { errorResult, json, type WikiClient } from "./common.js";
6
+ import { FeishuWikiSchema, type FeishuWikiParams } from "./schemas.js";
7
+
8
+ type WikiToolSpec<P> = {
9
+ name: string;
10
+ label: string;
11
+ description: string;
12
+ parameters: TSchema;
13
+ run: (client: WikiClient, params: P) => Promise<unknown>;
14
+ };
15
+
16
+ function registerWikiTool<P>(api: OpenClawPluginApi, spec: WikiToolSpec<P>) {
17
+ api.registerTool(
18
+ {
19
+ name: spec.name,
20
+ label: spec.label,
21
+ description: spec.description,
22
+ parameters: spec.parameters,
23
+ async execute(_toolCallId, params) {
24
+ try {
25
+ return await withFeishuToolClient({
26
+ api,
27
+ toolName: spec.name,
28
+ requiredTool: "wiki",
29
+ run: async ({ client }) => json(await spec.run(client as WikiClient, params as P)),
30
+ });
31
+ } catch (err) {
32
+ return errorResult(err);
33
+ }
34
+ },
35
+ },
36
+ { name: spec.name },
37
+ );
38
+ }
39
+
40
+ export function registerFeishuWikiTools(api: OpenClawPluginApi) {
41
+ if (!api.config) {
42
+ api.logger.debug?.("feishu_wiki: No config available, skipping wiki tools");
43
+ return;
44
+ }
45
+
46
+ if (!hasFeishuToolEnabledForAnyAccount(api.config)) {
47
+ api.logger.debug?.("feishu_wiki: No Feishu accounts configured, skipping wiki tools");
48
+ return;
49
+ }
50
+
51
+ if (!hasFeishuToolEnabledForAnyAccount(api.config, "wiki")) {
52
+ api.logger.debug?.("feishu_wiki: wiki tool disabled in config");
53
+ return;
54
+ }
55
+
56
+ registerWikiTool<FeishuWikiParams>(api, {
57
+ name: "feishu_wiki",
58
+ label: "Feishu Wiki",
59
+ description:
60
+ "Feishu knowledge base operations. Actions: spaces, nodes, get, create, move, rename",
61
+ parameters: FeishuWikiSchema,
62
+ run: (client, params) => runWikiAction(client, params),
63
+ });
64
+
65
+ api.logger.debug?.("feishu_wiki: Registered feishu_wiki tool");
66
+ }
package/src/bitable.ts DELETED
@@ -1 +0,0 @@
1
- export { registerFeishuBitableTools } from "./bitable-tools/register.js";
package/src/drive.ts DELETED
@@ -1,264 +0,0 @@
1
- import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
2
- import type * as Lark from "@larksuiteoapi/node-sdk";
3
- import { FeishuDriveSchema, type FeishuDriveParams } from "./drive-schema.js";
4
- import { hasFeishuToolEnabledForAnyAccount, withFeishuToolClient } from "./tools-common/tool-exec.js";
5
- import { writeDoc } from "./docx.js";
6
-
7
- // ============ Helpers ============
8
-
9
- function json(data: unknown) {
10
- return {
11
- content: [{ type: "text" as const, text: JSON.stringify(data, null, 2) }],
12
- details: data,
13
- };
14
- }
15
-
16
- // ============ Actions ============
17
-
18
- async function getRootFolderToken(client: Lark.Client): Promise<string> {
19
- // Use generic HTTP client to call the root folder meta API
20
- // as it's not directly exposed in the SDK
21
- const domain = (client as any).domain ?? "https://open.feishu.cn";
22
- const res = (await (client as any).httpInstance.get(
23
- `${domain}/open-apis/drive/explorer/v2/root_folder/meta`,
24
- )) as { code: number; msg?: string; data?: { token?: string } };
25
- if (res.code !== 0) throw new Error(res.msg ?? "Failed to get root folder");
26
- const token = res.data?.token;
27
- if (!token) throw new Error("Root folder token not found");
28
- return token;
29
- }
30
-
31
- async function listFolder(client: Lark.Client, folderToken?: string) {
32
- // Filter out invalid folder_token values (empty, "0", etc.)
33
- const validFolderToken = folderToken && folderToken !== "0" ? folderToken : undefined;
34
- const res = await client.drive.file.list({
35
- params: validFolderToken ? { folder_token: validFolderToken } : {},
36
- });
37
- if (res.code !== 0) throw new Error(res.msg);
38
-
39
- return {
40
- files:
41
- res.data?.files?.map((f) => ({
42
- token: f.token,
43
- name: f.name,
44
- type: f.type,
45
- url: f.url,
46
- created_time: f.created_time,
47
- modified_time: f.modified_time,
48
- owner_id: f.owner_id,
49
- })) ?? [],
50
- next_page_token: res.data?.next_page_token,
51
- };
52
- }
53
-
54
- async function getFileInfo(client: Lark.Client, fileToken: string, folderToken?: string) {
55
- // Use list with folder_token to find file info
56
- const res = await client.drive.file.list({
57
- params: folderToken ? { folder_token: folderToken } : {},
58
- });
59
- if (res.code !== 0) throw new Error(res.msg);
60
-
61
- const file = res.data?.files?.find((f) => f.token === fileToken);
62
- if (!file) {
63
- throw new Error(`File not found: ${fileToken}`);
64
- }
65
-
66
- return {
67
- token: file.token,
68
- name: file.name,
69
- type: file.type,
70
- url: file.url,
71
- created_time: file.created_time,
72
- modified_time: file.modified_time,
73
- owner_id: file.owner_id,
74
- };
75
- }
76
-
77
- async function createFolder(client: Lark.Client, name: string, folderToken?: string) {
78
- // Feishu supports using folder_token="0" as the root folder.
79
- // We *try* to resolve the real root token (explorer API), but fall back to "0"
80
- // because some tenants/apps return 400 for that explorer endpoint.
81
- let effectiveToken = folderToken && folderToken !== "0" ? folderToken : "0";
82
- if (effectiveToken === "0") {
83
- try {
84
- effectiveToken = await getRootFolderToken(client);
85
- } catch {
86
- // ignore and keep "0"
87
- }
88
- }
89
-
90
- const res = await client.drive.file.createFolder({
91
- data: {
92
- name,
93
- folder_token: effectiveToken,
94
- },
95
- });
96
- if (res.code !== 0) throw new Error(res.msg);
97
-
98
- return {
99
- token: res.data?.token,
100
- url: res.data?.url,
101
- };
102
- }
103
-
104
- async function moveFile(
105
- client: Lark.Client,
106
- fileToken: string,
107
- type: string,
108
- folderToken: string,
109
- ) {
110
- const res = await client.drive.file.move({
111
- path: { file_token: fileToken },
112
- data: {
113
- type: type as "doc" | "docx" | "sheet" | "bitable" | "folder" | "file" | "mindnote" | "slides",
114
- folder_token: folderToken,
115
- },
116
- });
117
- if (res.code !== 0) throw new Error(res.msg);
118
-
119
- return {
120
- success: true,
121
- task_id: res.data?.task_id,
122
- };
123
- }
124
-
125
- async function deleteFile(client: Lark.Client, fileToken: string, type: string) {
126
- const res = await client.drive.file.delete({
127
- path: { file_token: fileToken },
128
- params: {
129
- type: type as
130
- | "doc"
131
- | "docx"
132
- | "sheet"
133
- | "bitable"
134
- | "folder"
135
- | "file"
136
- | "mindnote"
137
- | "slides"
138
- | "shortcut",
139
- },
140
- });
141
- if (res.code !== 0) throw new Error(res.msg);
142
-
143
- return {
144
- success: true,
145
- task_id: res.data?.task_id,
146
- };
147
- }
148
-
149
- // ============ Import Document Functions ============
150
-
151
- /**
152
- * Import markdown content as a new Feishu document
153
- * Uses create + write approach for reliable content import.
154
- * Note: docType parameter is accepted for API compatibility but docx is always used.
155
- */
156
- async function importDocument(
157
- client: Lark.Client,
158
- title: string,
159
- content: string,
160
- mediaMaxBytes: number,
161
- folderToken?: string,
162
- _docType?: "docx" | "doc",
163
- ) {
164
- // Step 1: Create empty document
165
- const createRes = await client.docx.document.create({
166
- data: { title, folder_token: folderToken },
167
- });
168
-
169
- if (createRes.code !== 0) {
170
- throw new Error(`Failed to create document: ${createRes.msg}`);
171
- }
172
-
173
- const docId = createRes.data?.document?.document_id;
174
- if (!docId) {
175
- throw new Error("Document created but no document_id returned");
176
- }
177
-
178
- // Step 2: Write markdown content to the document
179
- // This ensures proper structure preservation using the writeDoc function
180
- const writeResult = await writeDoc(client, docId, content, mediaMaxBytes);
181
-
182
- return {
183
- success: true,
184
- document_id: docId,
185
- title: title,
186
- url: `https://feishu.cn/docx/${docId}`,
187
- import_method: "create_and_write",
188
- blocks_added: writeResult.blocks_added,
189
- images_processed: writeResult.images_processed,
190
- ...("warning" in writeResult && { warning: writeResult.warning }),
191
- };
192
- }
193
-
194
- // ============ Tool Registration ============
195
-
196
- export function registerFeishuDriveTools(api: OpenClawPluginApi) {
197
- if (!api.config) {
198
- api.logger.debug?.("feishu_drive: No config available, skipping drive tools");
199
- return;
200
- }
201
-
202
- if (!hasFeishuToolEnabledForAnyAccount(api.config)) {
203
- api.logger.debug?.("feishu_drive: No Feishu accounts configured, skipping drive tools");
204
- return;
205
- }
206
-
207
- if (!hasFeishuToolEnabledForAnyAccount(api.config, "drive")) {
208
- api.logger.debug?.("feishu_drive: drive tool disabled in config");
209
- return;
210
- }
211
-
212
- api.registerTool(
213
- {
214
- name: "feishu_drive",
215
- label: "Feishu Drive",
216
- description:
217
- "Feishu cloud storage operations. Actions: list, info, create_folder, move, delete, import_document. Use 'import_document' to create documents from Markdown with better structure preservation than block-by-block writing.",
218
- parameters: FeishuDriveSchema,
219
- async execute(_toolCallId, params) {
220
- const p = params as FeishuDriveParams;
221
- try {
222
- return await withFeishuToolClient({
223
- api,
224
- toolName: "feishu_drive",
225
- requiredTool: "drive",
226
- run: async ({ client, account }) => {
227
- const mediaMaxBytes = (account.config?.mediaMaxMb ?? 30) * 1024 * 1024;
228
- switch (p.action) {
229
- case "list":
230
- return json(await listFolder(client, p.folder_token));
231
- case "info":
232
- return json(await getFileInfo(client, p.file_token));
233
- case "create_folder":
234
- return json(await createFolder(client, p.name, p.folder_token));
235
- case "move":
236
- return json(await moveFile(client, p.file_token, p.type, p.folder_token));
237
- case "delete":
238
- return json(await deleteFile(client, p.file_token, p.type));
239
- case "import_document":
240
- return json(
241
- await importDocument(
242
- client,
243
- p.title,
244
- p.content,
245
- mediaMaxBytes,
246
- p.folder_token,
247
- (p as any).doc_type || "docx",
248
- ),
249
- );
250
- default:
251
- return json({ error: `Unknown action: ${(p as any).action}` });
252
- }
253
- },
254
- });
255
- } catch (err) {
256
- return json({ error: err instanceof Error ? err.message : String(err) });
257
- }
258
- },
259
- },
260
- { name: "feishu_drive" },
261
- );
262
-
263
- api.logger.debug?.("feishu_drive: Registered feishu_drive tool");
264
- }
package/src/perm.ts DELETED
@@ -1,165 +0,0 @@
1
- import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
2
- import type * as Lark from "@larksuiteoapi/node-sdk";
3
- import { FeishuPermSchema, type FeishuPermParams } from "./perm-schema.js";
4
- import { hasFeishuToolEnabledForAnyAccount, withFeishuToolClient } from "./tools-common/tool-exec.js";
5
-
6
- // ============ Helpers ============
7
-
8
- function json(data: unknown) {
9
- return {
10
- content: [{ type: "text" as const, text: JSON.stringify(data, null, 2) }],
11
- details: data,
12
- };
13
- }
14
-
15
- type ListTokenType =
16
- | "doc"
17
- | "sheet"
18
- | "file"
19
- | "wiki"
20
- | "bitable"
21
- | "docx"
22
- | "mindnote"
23
- | "minutes"
24
- | "slides";
25
- type CreateTokenType =
26
- | "doc"
27
- | "sheet"
28
- | "file"
29
- | "wiki"
30
- | "bitable"
31
- | "docx"
32
- | "folder"
33
- | "mindnote"
34
- | "minutes"
35
- | "slides";
36
- type MemberType =
37
- | "email"
38
- | "openid"
39
- | "unionid"
40
- | "openchat"
41
- | "opendepartmentid"
42
- | "userid"
43
- | "groupid"
44
- | "wikispaceid";
45
- type PermType = "view" | "edit" | "full_access";
46
-
47
- // ============ Actions ============
48
-
49
- async function listMembers(client: Lark.Client, token: string, type: string) {
50
- const res = await client.drive.permissionMember.list({
51
- path: { token },
52
- params: { type: type as ListTokenType },
53
- });
54
- if (res.code !== 0) throw new Error(res.msg);
55
-
56
- return {
57
- members:
58
- res.data?.items?.map((m) => ({
59
- member_type: m.member_type,
60
- member_id: m.member_id,
61
- perm: m.perm,
62
- name: m.name,
63
- })) ?? [],
64
- };
65
- }
66
-
67
- async function addMember(
68
- client: Lark.Client,
69
- token: string,
70
- type: string,
71
- memberType: string,
72
- memberId: string,
73
- perm: string,
74
- ) {
75
- const res = await client.drive.permissionMember.create({
76
- path: { token },
77
- params: { type: type as CreateTokenType, need_notification: false },
78
- data: {
79
- member_type: memberType as MemberType,
80
- member_id: memberId,
81
- perm: perm as PermType,
82
- },
83
- });
84
- if (res.code !== 0) throw new Error(res.msg);
85
-
86
- return {
87
- success: true,
88
- member: res.data?.member,
89
- };
90
- }
91
-
92
- async function removeMember(
93
- client: Lark.Client,
94
- token: string,
95
- type: string,
96
- memberType: string,
97
- memberId: string,
98
- ) {
99
- const res = await client.drive.permissionMember.delete({
100
- path: { token, member_id: memberId },
101
- params: { type: type as CreateTokenType, member_type: memberType as MemberType },
102
- });
103
- if (res.code !== 0) throw new Error(res.msg);
104
-
105
- return {
106
- success: true,
107
- };
108
- }
109
-
110
- // ============ Tool Registration ============
111
-
112
- export function registerFeishuPermTools(api: OpenClawPluginApi) {
113
- if (!api.config) {
114
- api.logger.debug?.("feishu_perm: No config available, skipping perm tools");
115
- return;
116
- }
117
-
118
- if (!hasFeishuToolEnabledForAnyAccount(api.config)) {
119
- api.logger.debug?.("feishu_perm: No Feishu accounts configured, skipping perm tools");
120
- return;
121
- }
122
-
123
- if (!hasFeishuToolEnabledForAnyAccount(api.config, "perm")) {
124
- api.logger.debug?.("feishu_perm: perm tool disabled in config (default: false)");
125
- return;
126
- }
127
-
128
- api.registerTool(
129
- {
130
- name: "feishu_perm",
131
- label: "Feishu Perm",
132
- description: "Feishu permission management. Actions: list, add, remove",
133
- parameters: FeishuPermSchema,
134
- async execute(_toolCallId, params) {
135
- const p = params as FeishuPermParams;
136
- try {
137
- return await withFeishuToolClient({
138
- api,
139
- toolName: "feishu_perm",
140
- requiredTool: "perm",
141
- run: async ({ client }) => {
142
- switch (p.action) {
143
- case "list":
144
- return json(await listMembers(client, p.token, p.type));
145
- case "add":
146
- return json(
147
- await addMember(client, p.token, p.type, p.member_type, p.member_id, p.perm),
148
- );
149
- case "remove":
150
- return json(await removeMember(client, p.token, p.type, p.member_type, p.member_id));
151
- default:
152
- return json({ error: `Unknown action: ${(p as any).action}` });
153
- }
154
- },
155
- });
156
- } catch (err) {
157
- return json({ error: err instanceof Error ? err.message : String(err) });
158
- }
159
- },
160
- },
161
- { name: "feishu_perm" },
162
- );
163
-
164
- api.logger.debug?.("feishu_perm: Registered feishu_perm tool");
165
- }
package/src/task.ts DELETED
@@ -1 +0,0 @@
1
- export { registerFeishuTaskTools } from "./task-tools/register.js";