@nextclaw/channel-plugin-feishu 0.2.29-beta.0 → 0.2.29-beta.1

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 (114) hide show
  1. package/dist/index.d.ts +23 -0
  2. package/dist/index.js +45 -0
  3. package/dist/src/accounts.js +141 -0
  4. package/dist/src/app-scope-checker.js +36 -0
  5. package/dist/src/async.js +34 -0
  6. package/dist/src/auth-errors.js +72 -0
  7. package/dist/src/bitable.js +495 -0
  8. package/dist/src/bot.d.ts +35 -0
  9. package/dist/src/bot.js +941 -0
  10. package/dist/src/calendar-calendar.js +54 -0
  11. package/dist/src/calendar-event-attendee.js +98 -0
  12. package/dist/src/calendar-event.js +193 -0
  13. package/dist/src/calendar-freebusy.js +40 -0
  14. package/dist/src/calendar-shared.js +23 -0
  15. package/dist/src/calendar.js +16 -0
  16. package/dist/src/card-action.js +49 -0
  17. package/dist/src/channel.d.ts +7 -0
  18. package/dist/src/channel.js +413 -0
  19. package/dist/src/chat-schema.js +25 -0
  20. package/dist/src/chat.js +87 -0
  21. package/dist/src/client.d.ts +16 -0
  22. package/dist/src/client.js +112 -0
  23. package/dist/src/config-schema.d.ts +357 -0
  24. package/dist/src/dedup.js +126 -0
  25. package/dist/src/device-flow.js +109 -0
  26. package/dist/src/directory.js +101 -0
  27. package/dist/src/doc-schema.js +148 -0
  28. package/dist/src/docx-batch-insert.js +104 -0
  29. package/dist/src/docx-color-text.js +80 -0
  30. package/dist/src/docx-table-ops.js +197 -0
  31. package/dist/src/docx.js +858 -0
  32. package/dist/src/domains.js +14 -0
  33. package/dist/src/drive-schema.js +41 -0
  34. package/dist/src/drive.js +126 -0
  35. package/dist/src/dynamic-agent.js +93 -0
  36. package/dist/src/external-keys.js +13 -0
  37. package/dist/src/feishu-fetch.js +12 -0
  38. package/dist/src/identity.js +92 -0
  39. package/dist/src/lark-ticket.js +11 -0
  40. package/dist/src/media.d.ts +75 -0
  41. package/dist/src/media.js +304 -0
  42. package/dist/src/mention.d.ts +52 -0
  43. package/dist/src/mention.js +82 -0
  44. package/dist/src/monitor.account.d.ts +1 -0
  45. package/dist/src/monitor.account.js +393 -0
  46. package/dist/src/monitor.d.ts +11 -0
  47. package/dist/src/monitor.js +58 -0
  48. package/dist/src/monitor.startup.js +24 -0
  49. package/dist/src/monitor.state.d.ts +1 -0
  50. package/dist/src/monitor.state.js +80 -0
  51. package/dist/src/monitor.transport.js +167 -0
  52. package/dist/src/nextclaw-sdk/account-id.js +15 -0
  53. package/dist/src/nextclaw-sdk/core-channel.js +150 -0
  54. package/dist/src/nextclaw-sdk/core-pairing.js +151 -0
  55. package/dist/src/nextclaw-sdk/dedupe.js +164 -0
  56. package/dist/src/nextclaw-sdk/feishu.d.ts +1 -0
  57. package/dist/src/nextclaw-sdk/feishu.js +14 -0
  58. package/dist/src/nextclaw-sdk/history.js +69 -0
  59. package/dist/src/nextclaw-sdk/network-body.js +180 -0
  60. package/dist/src/nextclaw-sdk/network-fetch.js +63 -0
  61. package/dist/src/nextclaw-sdk/network-webhook.js +126 -0
  62. package/dist/src/nextclaw-sdk/network.js +4 -0
  63. package/dist/src/nextclaw-sdk/runtime-store.js +21 -0
  64. package/dist/src/nextclaw-sdk/secrets-config.js +65 -0
  65. package/dist/src/nextclaw-sdk/secrets-core.d.ts +1 -0
  66. package/dist/src/nextclaw-sdk/secrets-core.js +68 -0
  67. package/dist/src/nextclaw-sdk/secrets-prompt.js +193 -0
  68. package/dist/src/nextclaw-sdk/secrets.d.ts +1 -0
  69. package/dist/src/nextclaw-sdk/secrets.js +4 -0
  70. package/dist/src/nextclaw-sdk/types.d.ts +242 -0
  71. package/dist/src/oauth.js +171 -0
  72. package/dist/src/onboarding.js +381 -0
  73. package/dist/src/outbound.js +150 -0
  74. package/dist/src/perm-schema.js +49 -0
  75. package/dist/src/perm.js +90 -0
  76. package/dist/src/policy.js +61 -0
  77. package/dist/src/post.js +160 -0
  78. package/dist/src/probe.d.ts +11 -0
  79. package/dist/src/probe.js +85 -0
  80. package/dist/src/raw-request.js +24 -0
  81. package/dist/src/reactions.d.ts +67 -0
  82. package/dist/src/reactions.js +91 -0
  83. package/dist/src/reply-dispatcher.js +250 -0
  84. package/dist/src/runtime.js +5 -0
  85. package/dist/src/secret-input.js +3 -0
  86. package/dist/src/send-result.js +12 -0
  87. package/dist/src/send-target.js +22 -0
  88. package/dist/src/send.d.ts +51 -0
  89. package/dist/src/send.js +265 -0
  90. package/dist/src/sheets-shared.js +193 -0
  91. package/dist/src/sheets.js +95 -0
  92. package/dist/src/streaming-card.js +263 -0
  93. package/dist/src/targets.js +39 -0
  94. package/dist/src/task-comment.js +76 -0
  95. package/dist/src/task-shared.js +13 -0
  96. package/dist/src/task-subtask.js +79 -0
  97. package/dist/src/task-task.js +144 -0
  98. package/dist/src/task-tasklist.js +136 -0
  99. package/dist/src/task.js +16 -0
  100. package/dist/src/token-store.js +154 -0
  101. package/dist/src/tool-account.js +65 -0
  102. package/dist/src/tool-result.js +18 -0
  103. package/dist/src/tool-scopes.js +62 -0
  104. package/dist/src/tools-config.js +30 -0
  105. package/dist/src/types.d.ts +43 -0
  106. package/dist/src/typing.js +145 -0
  107. package/dist/src/uat-client.js +102 -0
  108. package/dist/src/user-tool-client.js +132 -0
  109. package/dist/src/user-tool-helpers.js +110 -0
  110. package/dist/src/user-tool-result.js +10 -0
  111. package/dist/src/wiki-schema.js +45 -0
  112. package/dist/src/wiki.js +144 -0
  113. package/package.json +8 -4
  114. package/index.ts +0 -75
@@ -0,0 +1,14 @@
1
+ //#region src/domains.ts
2
+ function openPlatformDomain(domain) {
3
+ return domain === "lark" ? "https://open.larksuite.com" : "https://open.feishu.cn";
4
+ }
5
+ function wwwDomain(domain) {
6
+ return domain === "lark" ? "https://www.larksuite.com" : "https://www.feishu.cn";
7
+ }
8
+ function resolveDomainUrl(domain) {
9
+ if (domain === "feishu") return "https://open.feishu.cn";
10
+ if (domain === "lark") return "https://open.larksuite.com";
11
+ return domain.replace(/\/+$/, "");
12
+ }
13
+ //#endregion
14
+ export { openPlatformDomain, resolveDomainUrl, wwwDomain };
@@ -0,0 +1,41 @@
1
+ import { Type } from "@sinclair/typebox";
2
+ //#region src/drive-schema.ts
3
+ const FileType = Type.Union([
4
+ Type.Literal("doc"),
5
+ Type.Literal("docx"),
6
+ Type.Literal("sheet"),
7
+ Type.Literal("bitable"),
8
+ Type.Literal("folder"),
9
+ Type.Literal("file"),
10
+ Type.Literal("mindnote"),
11
+ Type.Literal("shortcut")
12
+ ]);
13
+ const FeishuDriveSchema = Type.Union([
14
+ Type.Object({
15
+ action: Type.Literal("list"),
16
+ folder_token: Type.Optional(Type.String({ description: "Folder token (optional, omit for root directory)" }))
17
+ }),
18
+ Type.Object({
19
+ action: Type.Literal("info"),
20
+ file_token: Type.String({ description: "File or folder token" }),
21
+ type: FileType
22
+ }),
23
+ Type.Object({
24
+ action: Type.Literal("create_folder"),
25
+ name: Type.String({ description: "Folder name" }),
26
+ folder_token: Type.Optional(Type.String({ description: "Parent folder token (optional, omit for root)" }))
27
+ }),
28
+ Type.Object({
29
+ action: Type.Literal("move"),
30
+ file_token: Type.String({ description: "File token to move" }),
31
+ type: FileType,
32
+ folder_token: Type.String({ description: "Target folder token" })
33
+ }),
34
+ Type.Object({
35
+ action: Type.Literal("delete"),
36
+ file_token: Type.String({ description: "File token to delete" }),
37
+ type: FileType
38
+ })
39
+ ]);
40
+ //#endregion
41
+ export { FeishuDriveSchema };
@@ -0,0 +1,126 @@
1
+ import { jsonToolResult, toolExecutionErrorResult, unknownToolActionResult } from "./tool-result.js";
2
+ import { createFeishuToolClient, resolveRegisteredFeishuToolsConfig } from "./tool-account.js";
3
+ import { FeishuDriveSchema } from "./drive-schema.js";
4
+ //#region src/drive.ts
5
+ async function getRootFolderToken(client) {
6
+ const domain = client.domain ?? "https://open.feishu.cn";
7
+ const res = await client.httpInstance.get(`${domain}/open-apis/drive/explorer/v2/root_folder/meta`);
8
+ if (res.code !== 0) throw new Error(res.msg ?? "Failed to get root folder");
9
+ const token = res.data?.token;
10
+ if (!token) throw new Error("Root folder token not found");
11
+ return token;
12
+ }
13
+ async function listFolder(client, folderToken) {
14
+ const validFolderToken = folderToken && folderToken !== "0" ? folderToken : void 0;
15
+ const res = await client.drive.file.list({ params: validFolderToken ? { folder_token: validFolderToken } : {} });
16
+ if (res.code !== 0) throw new Error(res.msg);
17
+ return {
18
+ files: res.data?.files?.map((f) => ({
19
+ token: f.token,
20
+ name: f.name,
21
+ type: f.type,
22
+ url: f.url,
23
+ created_time: f.created_time,
24
+ modified_time: f.modified_time,
25
+ owner_id: f.owner_id
26
+ })) ?? [],
27
+ next_page_token: res.data?.next_page_token
28
+ };
29
+ }
30
+ async function getFileInfo(client, fileToken, folderToken) {
31
+ const res = await client.drive.file.list({ params: folderToken ? { folder_token: folderToken } : {} });
32
+ if (res.code !== 0) throw new Error(res.msg);
33
+ const file = res.data?.files?.find((f) => f.token === fileToken);
34
+ if (!file) throw new Error(`File not found: ${fileToken}`);
35
+ return {
36
+ token: file.token,
37
+ name: file.name,
38
+ type: file.type,
39
+ url: file.url,
40
+ created_time: file.created_time,
41
+ modified_time: file.modified_time,
42
+ owner_id: file.owner_id
43
+ };
44
+ }
45
+ async function createFolder(client, name, folderToken) {
46
+ let effectiveToken = folderToken && folderToken !== "0" ? folderToken : "0";
47
+ if (effectiveToken === "0") try {
48
+ effectiveToken = await getRootFolderToken(client);
49
+ } catch {}
50
+ const res = await client.drive.file.createFolder({ data: {
51
+ name,
52
+ folder_token: effectiveToken
53
+ } });
54
+ if (res.code !== 0) throw new Error(res.msg);
55
+ return {
56
+ token: res.data?.token,
57
+ url: res.data?.url
58
+ };
59
+ }
60
+ async function moveFile(client, fileToken, type, folderToken) {
61
+ const res = await client.drive.file.move({
62
+ path: { file_token: fileToken },
63
+ data: {
64
+ type,
65
+ folder_token: folderToken
66
+ }
67
+ });
68
+ if (res.code !== 0) throw new Error(res.msg);
69
+ return {
70
+ success: true,
71
+ task_id: res.data?.task_id
72
+ };
73
+ }
74
+ async function deleteFile(client, fileToken, type) {
75
+ const res = await client.drive.file.delete({
76
+ path: { file_token: fileToken },
77
+ params: { type }
78
+ });
79
+ if (res.code !== 0) throw new Error(res.msg);
80
+ return {
81
+ success: true,
82
+ task_id: res.data?.task_id
83
+ };
84
+ }
85
+ function registerFeishuDriveTools(api) {
86
+ if (!api.config) {
87
+ api.logger.debug?.("feishu_drive: No config available, skipping drive tools");
88
+ return;
89
+ }
90
+ if (!resolveRegisteredFeishuToolsConfig(api.config).drive) {
91
+ api.logger.debug?.("feishu_drive: drive tool disabled in config");
92
+ return;
93
+ }
94
+ api.registerTool((ctx) => {
95
+ const defaultAccountId = ctx.agentAccountId;
96
+ return {
97
+ name: "feishu_drive",
98
+ label: "Feishu Drive",
99
+ description: "Feishu cloud storage operations. Actions: list, info, create_folder, move, delete",
100
+ parameters: FeishuDriveSchema,
101
+ async execute(_toolCallId, params) {
102
+ const p = params;
103
+ try {
104
+ const client = createFeishuToolClient({
105
+ api,
106
+ executeParams: p,
107
+ defaultAccountId
108
+ });
109
+ switch (p.action) {
110
+ case "list": return jsonToolResult(await listFolder(client, p.folder_token));
111
+ case "info": return jsonToolResult(await getFileInfo(client, p.file_token));
112
+ case "create_folder": return jsonToolResult(await createFolder(client, p.name, p.folder_token));
113
+ case "move": return jsonToolResult(await moveFile(client, p.file_token, p.type, p.folder_token));
114
+ case "delete": return jsonToolResult(await deleteFile(client, p.file_token, p.type));
115
+ default: return unknownToolActionResult(p.action);
116
+ }
117
+ } catch (err) {
118
+ return toolExecutionErrorResult(err);
119
+ }
120
+ }
121
+ };
122
+ }, { name: "feishu_drive" });
123
+ api.logger.info?.(`feishu_drive: Registered feishu_drive tool`);
124
+ }
125
+ //#endregion
126
+ export { registerFeishuDriveTools };
@@ -0,0 +1,93 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import os from "node:os";
4
+ //#region src/dynamic-agent.ts
5
+ /**
6
+ * Check if a dynamic agent should be created for a DM user and create it if needed.
7
+ * This creates a unique agent instance with its own workspace for each DM user.
8
+ */
9
+ async function maybeCreateDynamicAgent(params) {
10
+ const { cfg, runtime, senderOpenId, dynamicCfg, log } = params;
11
+ const existingBindings = cfg.bindings ?? [];
12
+ if (existingBindings.some((b) => b.match?.channel === "feishu" && b.match?.peer?.kind === "direct" && b.match?.peer?.id === senderOpenId)) return {
13
+ created: false,
14
+ updatedCfg: cfg
15
+ };
16
+ if (dynamicCfg.maxAgents !== void 0) {
17
+ if ((cfg.agents?.list ?? []).filter((a) => a.id.startsWith("feishu-")).length >= dynamicCfg.maxAgents) {
18
+ log(`feishu: maxAgents limit (${dynamicCfg.maxAgents}) reached, not creating agent for ${senderOpenId}`);
19
+ return {
20
+ created: false,
21
+ updatedCfg: cfg
22
+ };
23
+ }
24
+ }
25
+ const agentId = `feishu-${senderOpenId}`;
26
+ if ((cfg.agents?.list ?? []).find((a) => a.id === agentId)) {
27
+ log(`feishu: agent "${agentId}" exists, adding missing binding for ${senderOpenId}`);
28
+ const updatedCfg = {
29
+ ...cfg,
30
+ bindings: [...existingBindings, {
31
+ agentId,
32
+ match: {
33
+ channel: "feishu",
34
+ peer: {
35
+ kind: "direct",
36
+ id: senderOpenId
37
+ }
38
+ }
39
+ }]
40
+ };
41
+ await runtime.config.writeConfigFile(updatedCfg);
42
+ return {
43
+ created: true,
44
+ updatedCfg,
45
+ agentId
46
+ };
47
+ }
48
+ const workspaceTemplate = dynamicCfg.workspaceTemplate ?? "~/.openclaw/workspace-{agentId}";
49
+ const agentDirTemplate = dynamicCfg.agentDirTemplate ?? "~/.openclaw/agents/{agentId}/agent";
50
+ const workspace = resolveUserPath(workspaceTemplate.replace("{userId}", senderOpenId).replace("{agentId}", agentId));
51
+ const agentDir = resolveUserPath(agentDirTemplate.replace("{userId}", senderOpenId).replace("{agentId}", agentId));
52
+ log(`feishu: creating dynamic agent "${agentId}" for user ${senderOpenId}`);
53
+ log(` workspace: ${workspace}`);
54
+ log(` agentDir: ${agentDir}`);
55
+ await fs.promises.mkdir(workspace, { recursive: true });
56
+ await fs.promises.mkdir(agentDir, { recursive: true });
57
+ const updatedCfg = {
58
+ ...cfg,
59
+ agents: {
60
+ ...cfg.agents,
61
+ list: [...cfg.agents?.list ?? [], {
62
+ id: agentId,
63
+ workspace,
64
+ agentDir
65
+ }]
66
+ },
67
+ bindings: [...existingBindings, {
68
+ agentId,
69
+ match: {
70
+ channel: "feishu",
71
+ peer: {
72
+ kind: "direct",
73
+ id: senderOpenId
74
+ }
75
+ }
76
+ }]
77
+ };
78
+ await runtime.config.writeConfigFile(updatedCfg);
79
+ return {
80
+ created: true,
81
+ updatedCfg,
82
+ agentId
83
+ };
84
+ }
85
+ /**
86
+ * Resolve a path that may start with ~ to the user's home directory.
87
+ */
88
+ function resolveUserPath(p) {
89
+ if (p.startsWith("~/")) return path.join(os.homedir(), p.slice(2));
90
+ return p;
91
+ }
92
+ //#endregion
93
+ export { maybeCreateDynamicAgent };
@@ -0,0 +1,13 @@
1
+ //#region src/external-keys.ts
2
+ const CONTROL_CHARS_RE = /[\u0000-\u001f\u007f]/;
3
+ const MAX_EXTERNAL_KEY_LENGTH = 512;
4
+ function normalizeFeishuExternalKey(value) {
5
+ if (typeof value !== "string") return;
6
+ const normalized = value.trim();
7
+ if (!normalized || normalized.length > MAX_EXTERNAL_KEY_LENGTH) return;
8
+ if (CONTROL_CHARS_RE.test(normalized)) return;
9
+ if (normalized.includes("/") || normalized.includes("\\") || normalized.includes("..")) return;
10
+ return normalized;
11
+ }
12
+ //#endregion
13
+ export { normalizeFeishuExternalKey };
@@ -0,0 +1,12 @@
1
+ //#region src/feishu-fetch.ts
2
+ const FEISHU_USER_AGENT = "nextclaw-feishu-plugin/0.2";
3
+ function feishuFetch(url, init) {
4
+ const headers = new Headers(init?.headers);
5
+ if (!headers.has("User-Agent")) headers.set("User-Agent", FEISHU_USER_AGENT);
6
+ return fetch(url, {
7
+ ...init,
8
+ headers
9
+ });
10
+ }
11
+ //#endregion
12
+ export { feishuFetch };
@@ -0,0 +1,92 @@
1
+ import { StringEnum, assertLarkOk, createToolContext, handleInvokeError, json, registerTool } from "./user-tool-helpers.js";
2
+ import { resolveRegisteredFeishuToolsConfig } from "./tool-account.js";
3
+ import { Type } from "@sinclair/typebox";
4
+ //#region src/identity.ts
5
+ const GetUserSchema = Type.Object({
6
+ user_id: Type.Optional(Type.String({ description: "用户 ID(如 ou_xxx)。不传时获取当前消息发送者本人信息。" })),
7
+ user_id_type: Type.Optional(StringEnum([
8
+ "open_id",
9
+ "union_id",
10
+ "user_id"
11
+ ]))
12
+ });
13
+ const SearchUserSchema = Type.Object({
14
+ query: Type.String({ description: "搜索关键词,可匹配姓名、邮箱、手机号等。" }),
15
+ page_size: Type.Optional(Type.Integer({
16
+ description: "分页大小,默认 20,最大 200。",
17
+ minimum: 1,
18
+ maximum: 200
19
+ })),
20
+ page_token: Type.Optional(Type.String({ description: "翻页 token。" }))
21
+ });
22
+ function registerFeishuIdentityTools(api) {
23
+ if (!api.config) return;
24
+ if (!resolveRegisteredFeishuToolsConfig(api.config).identity) return;
25
+ registerFeishuGetUserTool(api);
26
+ registerFeishuSearchUserTool(api);
27
+ }
28
+ function registerFeishuGetUserTool(api) {
29
+ const { toolClient, log } = createToolContext(api, "feishu_get_user");
30
+ registerTool(api, {
31
+ name: "feishu_get_user",
32
+ label: "Feishu: Get User",
33
+ description: "获取飞书用户信息。不传 user_id 时默认获取当前消息发送者本人信息;传 user_id 时获取指定用户信息。",
34
+ parameters: GetUserSchema,
35
+ async execute(_toolCallId, params) {
36
+ const payload = params;
37
+ try {
38
+ const client = toolClient();
39
+ if (!payload.user_id) {
40
+ log.info("fetching current user info");
41
+ const response = await client.invoke("feishu_get_user.default", (sdk, opts) => sdk.authen.userInfo.get({}, opts), { as: "user" });
42
+ assertLarkOk(response);
43
+ return json({ user: response.data });
44
+ }
45
+ log.info(`fetching user ${payload.user_id}`);
46
+ const response = await client.invoke("feishu_get_user.default", (sdk, opts) => sdk.contact.user.get({
47
+ path: { user_id: payload.user_id },
48
+ params: { user_id_type: payload.user_id_type ?? "open_id" }
49
+ }, opts), { as: "user" });
50
+ assertLarkOk(response);
51
+ return json({ user: response.data?.user });
52
+ } catch (error) {
53
+ return handleInvokeError(error, api);
54
+ }
55
+ }
56
+ }, { name: "feishu_get_user" });
57
+ }
58
+ function registerFeishuSearchUserTool(api) {
59
+ const { toolClient, log } = createToolContext(api, "feishu_search_user");
60
+ registerTool(api, {
61
+ name: "feishu_search_user",
62
+ label: "Feishu: Search User",
63
+ description: "搜索飞书员工信息,返回姓名、部门、open_id 等结果。",
64
+ parameters: SearchUserSchema,
65
+ async execute(_toolCallId, params) {
66
+ const payload = params;
67
+ try {
68
+ const client = toolClient();
69
+ log.info(`search query="${payload.query}"`);
70
+ const response = await client.invokeByPath("feishu_search_user.default", "/open-apis/search/v1/user", {
71
+ method: "GET",
72
+ query: {
73
+ query: payload.query,
74
+ page_size: String(payload.page_size ?? 20),
75
+ ...payload.page_token ? { page_token: payload.page_token } : {}
76
+ },
77
+ as: "user"
78
+ });
79
+ assertLarkOk(response);
80
+ return json({
81
+ users: response.data?.users ?? [],
82
+ has_more: response.data?.has_more ?? false,
83
+ page_token: response.data?.page_token
84
+ });
85
+ } catch (error) {
86
+ return handleInvokeError(error, api);
87
+ }
88
+ }
89
+ }, { name: "feishu_search_user" });
90
+ }
91
+ //#endregion
92
+ export { registerFeishuIdentityTools };
@@ -0,0 +1,11 @@
1
+ import { AsyncLocalStorage } from "node:async_hooks";
2
+ //#region src/lark-ticket.ts
3
+ const store = new AsyncLocalStorage();
4
+ function withTicket(ticket, fn) {
5
+ return store.run(ticket, fn);
6
+ }
7
+ function getTicket() {
8
+ return store.getStore();
9
+ }
10
+ //#endregion
11
+ export { getTicket, withTicket };
@@ -0,0 +1,75 @@
1
+ import { ClawdbotConfig } from "./nextclaw-sdk/types.js";
2
+ //#region src/media.d.ts
3
+ type UploadImageResult = {
4
+ imageKey: string;
5
+ };
6
+ type UploadFileResult = {
7
+ fileKey: string;
8
+ };
9
+ type SendMediaResult = {
10
+ messageId: string;
11
+ chatId: string;
12
+ };
13
+ /**
14
+ * Upload an image to Feishu and get an image_key for sending.
15
+ * Supports: JPEG, PNG, WEBP, GIF, TIFF, BMP, ICO
16
+ */
17
+ declare function uploadImageFeishu(params: {
18
+ cfg: ClawdbotConfig;
19
+ image: Buffer | string;
20
+ imageType?: "message" | "avatar";
21
+ accountId?: string;
22
+ }): Promise<UploadImageResult>;
23
+ /**
24
+ * Upload a file to Feishu and get a file_key for sending.
25
+ * Max file size: 30MB
26
+ */
27
+ declare function uploadFileFeishu(params: {
28
+ cfg: ClawdbotConfig;
29
+ file: Buffer | string;
30
+ fileName: string;
31
+ fileType: "opus" | "mp4" | "pdf" | "doc" | "xls" | "ppt" | "stream";
32
+ duration?: number;
33
+ accountId?: string;
34
+ }): Promise<UploadFileResult>;
35
+ /**
36
+ * Send an image message using an image_key
37
+ */
38
+ declare function sendImageFeishu(params: {
39
+ cfg: ClawdbotConfig;
40
+ to: string;
41
+ imageKey: string;
42
+ replyToMessageId?: string;
43
+ replyInThread?: boolean;
44
+ accountId?: string;
45
+ }): Promise<SendMediaResult>;
46
+ /**
47
+ * Send a file message using a file_key
48
+ */
49
+ declare function sendFileFeishu(params: {
50
+ cfg: ClawdbotConfig;
51
+ to: string;
52
+ fileKey: string; /** Use "audio" for audio, "media" for video (mp4), "file" for documents */
53
+ msgType?: "file" | "audio" | "media";
54
+ replyToMessageId?: string;
55
+ replyInThread?: boolean;
56
+ accountId?: string;
57
+ }): Promise<SendMediaResult>;
58
+ /**
59
+ * Upload and send media (image or file) from URL, local path, or buffer.
60
+ * When mediaUrl is a local path, mediaLocalRoots (from core outbound context)
61
+ * must be passed so loadWebMedia allows the path (post CVE-2026-26321).
62
+ */
63
+ declare function sendMediaFeishu(params: {
64
+ cfg: ClawdbotConfig;
65
+ to: string;
66
+ mediaUrl?: string;
67
+ mediaBuffer?: Buffer;
68
+ fileName?: string;
69
+ replyToMessageId?: string;
70
+ replyInThread?: boolean;
71
+ accountId?: string; /** Allowed roots for local path reads; required for local filePath to work. */
72
+ mediaLocalRoots?: readonly string[];
73
+ }): Promise<SendMediaResult>;
74
+ //#endregion
75
+ export { sendFileFeishu, sendImageFeishu, sendMediaFeishu, uploadFileFeishu, uploadImageFeishu };