@openclaw/feishu 2026.3.13 → 2026.5.1-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 (188) hide show
  1. package/api.ts +31 -0
  2. package/channel-entry.ts +20 -0
  3. package/channel-plugin-api.ts +1 -0
  4. package/contract-api.ts +16 -0
  5. package/index.ts +70 -53
  6. package/openclaw.plugin.json +1653 -4
  7. package/package.json +32 -7
  8. package/runtime-api.ts +55 -0
  9. package/secret-contract-api.ts +5 -0
  10. package/security-contract-api.ts +1 -0
  11. package/session-key-api.ts +1 -0
  12. package/setup-api.ts +3 -0
  13. package/setup-entry.test.ts +14 -0
  14. package/setup-entry.ts +13 -0
  15. package/src/accounts.test.ts +95 -7
  16. package/src/accounts.ts +199 -117
  17. package/src/app-registration.ts +331 -0
  18. package/src/approval-auth.test.ts +24 -0
  19. package/src/approval-auth.ts +25 -0
  20. package/src/async.test.ts +35 -0
  21. package/src/async.ts +43 -1
  22. package/src/audio-preflight.runtime.ts +9 -0
  23. package/src/bitable.test.ts +131 -0
  24. package/src/bitable.ts +59 -22
  25. package/src/bot-content.ts +474 -0
  26. package/src/bot-group-name.test.ts +108 -0
  27. package/src/bot-runtime-api.ts +12 -0
  28. package/src/bot-sender-name.ts +125 -0
  29. package/src/bot.broadcast.test.ts +463 -0
  30. package/src/bot.card-action.test.ts +519 -5
  31. package/src/bot.checkBotMentioned.test.ts +92 -20
  32. package/src/bot.helpers.test.ts +118 -0
  33. package/src/bot.stripBotMention.test.ts +13 -21
  34. package/src/bot.test.ts +1334 -401
  35. package/src/bot.ts +778 -775
  36. package/src/card-action.ts +408 -40
  37. package/src/card-interaction.test.ts +129 -0
  38. package/src/card-interaction.ts +159 -0
  39. package/src/card-test-helpers.ts +47 -0
  40. package/src/card-ux-approval.ts +65 -0
  41. package/src/card-ux-launcher.test.ts +99 -0
  42. package/src/card-ux-launcher.ts +121 -0
  43. package/src/card-ux-shared.ts +33 -0
  44. package/src/channel-runtime-api.ts +16 -0
  45. package/src/channel.runtime.ts +47 -0
  46. package/src/channel.test.ts +914 -3
  47. package/src/channel.ts +1252 -309
  48. package/src/chat-schema.ts +5 -4
  49. package/src/chat.test.ts +84 -28
  50. package/src/chat.ts +68 -10
  51. package/src/client.test.ts +212 -103
  52. package/src/client.ts +115 -21
  53. package/src/comment-dispatcher-runtime-api.ts +6 -0
  54. package/src/comment-dispatcher.test.ts +169 -0
  55. package/src/comment-dispatcher.ts +107 -0
  56. package/src/comment-handler-runtime-api.ts +3 -0
  57. package/src/comment-handler.test.ts +486 -0
  58. package/src/comment-handler.ts +309 -0
  59. package/src/comment-reaction.test.ts +166 -0
  60. package/src/comment-reaction.ts +259 -0
  61. package/src/comment-shared.test.ts +182 -0
  62. package/src/comment-shared.ts +365 -0
  63. package/src/comment-target.ts +44 -0
  64. package/src/config-schema.test.ts +63 -1
  65. package/src/config-schema.ts +31 -4
  66. package/src/conversation-id.test.ts +18 -0
  67. package/src/conversation-id.ts +199 -0
  68. package/src/dedup-runtime-api.ts +1 -0
  69. package/src/dedup.ts +32 -94
  70. package/src/directory.static.ts +61 -0
  71. package/src/directory.test.ts +119 -20
  72. package/src/directory.ts +61 -91
  73. package/src/doc-schema.ts +1 -1
  74. package/src/docx-batch-insert.test.ts +39 -38
  75. package/src/docx-batch-insert.ts +55 -19
  76. package/src/docx-color-text.ts +9 -4
  77. package/src/docx-table-ops.test.ts +53 -0
  78. package/src/docx-table-ops.ts +52 -34
  79. package/src/docx-types.ts +38 -0
  80. package/src/docx.account-selection.test.ts +12 -3
  81. package/src/docx.test.ts +314 -74
  82. package/src/docx.ts +278 -122
  83. package/src/drive-schema.ts +47 -1
  84. package/src/drive.test.ts +1219 -0
  85. package/src/drive.ts +614 -13
  86. package/src/dynamic-agent.ts +10 -4
  87. package/src/event-types.ts +45 -0
  88. package/src/external-keys.ts +1 -1
  89. package/src/lifecycle.test-support.ts +220 -0
  90. package/src/media.test.ts +375 -26
  91. package/src/media.ts +434 -88
  92. package/src/mention-target.types.ts +5 -0
  93. package/src/mention.ts +32 -51
  94. package/src/message-action-contract.ts +13 -0
  95. package/src/monitor-state-runtime-api.ts +7 -0
  96. package/src/monitor-transport-runtime-api.ts +7 -0
  97. package/src/monitor.account.ts +218 -312
  98. package/src/monitor.acp-init-failure.lifecycle.test-support.ts +219 -0
  99. package/src/monitor.bot-identity.ts +86 -0
  100. package/src/monitor.bot-menu-handler.ts +165 -0
  101. package/src/monitor.bot-menu.lifecycle.test-support.ts +224 -0
  102. package/src/monitor.bot-menu.test.ts +178 -0
  103. package/src/monitor.broadcast.reply-once.lifecycle.test-support.ts +264 -0
  104. package/src/monitor.card-action.lifecycle.test-support.ts +373 -0
  105. package/src/monitor.cleanup.test.ts +376 -0
  106. package/src/monitor.comment-notice-handler.ts +105 -0
  107. package/src/monitor.comment.test.ts +937 -0
  108. package/src/monitor.comment.ts +1386 -0
  109. package/src/monitor.lifecycle.test.ts +4 -0
  110. package/src/monitor.message-handler.ts +339 -0
  111. package/src/monitor.reaction.lifecycle.test-support.ts +68 -0
  112. package/src/monitor.reaction.test.ts +108 -48
  113. package/src/monitor.reply-once.lifecycle.test-support.ts +190 -0
  114. package/src/monitor.startup.test.ts +11 -9
  115. package/src/monitor.startup.ts +26 -16
  116. package/src/monitor.state.ts +20 -5
  117. package/src/monitor.synthetic-error.ts +18 -0
  118. package/src/monitor.test-mocks.ts +2 -2
  119. package/src/monitor.transport.ts +220 -60
  120. package/src/monitor.ts +15 -10
  121. package/src/monitor.webhook-e2e.test.ts +65 -7
  122. package/src/monitor.webhook-security.test.ts +122 -0
  123. package/src/monitor.webhook.test-helpers.ts +44 -26
  124. package/src/outbound-runtime-api.ts +1 -0
  125. package/src/outbound.test.ts +616 -37
  126. package/src/outbound.ts +623 -81
  127. package/src/perm-schema.ts +1 -1
  128. package/src/perm.ts +1 -7
  129. package/src/pins.ts +108 -0
  130. package/src/policy.test.ts +297 -117
  131. package/src/policy.ts +142 -29
  132. package/src/post.ts +7 -6
  133. package/src/probe.test.ts +14 -9
  134. package/src/probe.ts +26 -16
  135. package/src/processing-claims.ts +59 -0
  136. package/src/qr-terminal.ts +1 -0
  137. package/src/reactions.ts +4 -34
  138. package/src/reasoning-preview.test.ts +59 -0
  139. package/src/reasoning-preview.ts +20 -0
  140. package/src/reply-dispatcher-runtime-api.ts +7 -0
  141. package/src/reply-dispatcher.test.ts +660 -29
  142. package/src/reply-dispatcher.ts +407 -154
  143. package/src/runtime.ts +6 -3
  144. package/src/secret-contract.ts +145 -0
  145. package/src/secret-input.ts +1 -13
  146. package/src/security-audit-shared.ts +69 -0
  147. package/src/security-audit.test.ts +61 -0
  148. package/src/security-audit.ts +1 -0
  149. package/src/send-result.ts +1 -1
  150. package/src/send-target.test.ts +9 -3
  151. package/src/send-target.ts +10 -4
  152. package/src/send.reply-fallback.test.ts +77 -2
  153. package/src/send.test.ts +386 -4
  154. package/src/send.ts +399 -86
  155. package/src/sequential-key.test.ts +72 -0
  156. package/src/sequential-key.ts +28 -0
  157. package/src/sequential-queue.test.ts +92 -0
  158. package/src/sequential-queue.ts +16 -0
  159. package/src/session-conversation.ts +42 -0
  160. package/src/session-route.ts +48 -0
  161. package/src/setup-core.ts +51 -0
  162. package/src/{onboarding.test.ts → setup-surface.test.ts} +52 -21
  163. package/src/setup-surface.ts +581 -0
  164. package/src/streaming-card.test.ts +138 -2
  165. package/src/streaming-card.ts +134 -18
  166. package/src/subagent-hooks.test.ts +603 -0
  167. package/src/subagent-hooks.ts +397 -0
  168. package/src/targets.ts +3 -13
  169. package/src/test-support/lifecycle-test-support.ts +479 -0
  170. package/src/thread-bindings.test.ts +143 -0
  171. package/src/thread-bindings.ts +330 -0
  172. package/src/tool-account-routing.test.ts +66 -8
  173. package/src/tool-account.test.ts +44 -0
  174. package/src/tool-account.ts +40 -17
  175. package/src/tool-factory-test-harness.ts +11 -8
  176. package/src/tool-result.ts +3 -1
  177. package/src/tools-config.ts +1 -1
  178. package/src/types.ts +16 -15
  179. package/src/typing.ts +10 -6
  180. package/src/wiki-schema.ts +1 -1
  181. package/src/wiki.ts +1 -7
  182. package/subagent-hooks-api.ts +31 -0
  183. package/tsconfig.json +16 -0
  184. package/src/feishu-command-handler.ts +0 -59
  185. package/src/onboarding.status.test.ts +0 -25
  186. package/src/onboarding.ts +0 -489
  187. package/src/send-message.ts +0 -71
  188. package/src/targets.test.ts +0 -70
@@ -1,15 +1,16 @@
1
- import { Type, type Static } from "@sinclair/typebox";
1
+ import { Type, type Static } from "typebox";
2
2
 
3
- const CHAT_ACTION_VALUES = ["members", "info"] as const;
3
+ const CHAT_ACTION_VALUES = ["members", "info", "member_info"] as const;
4
4
  const MEMBER_ID_TYPE_VALUES = ["open_id", "user_id", "union_id"] as const;
5
5
 
6
6
  export const FeishuChatSchema = Type.Object({
7
7
  action: Type.Unsafe<(typeof CHAT_ACTION_VALUES)[number]>({
8
8
  type: "string",
9
9
  enum: [...CHAT_ACTION_VALUES],
10
- description: "Action to run: members | info",
10
+ description: "Action to run: members | info | member_info",
11
11
  }),
12
- chat_id: Type.String({ description: "Chat ID (from URL or event payload)" }),
12
+ chat_id: Type.Optional(Type.String({ description: "Chat ID (from URL or event payload)" })),
13
+ member_id: Type.Optional(Type.String({ description: "Member ID for member_info lookups" })),
13
14
  page_size: Type.Optional(Type.Number({ description: "Page size (1-100, default 50)" })),
14
15
  page_token: Type.Optional(Type.String({ description: "Pagination token" })),
15
16
  member_id_type: Type.Optional(
package/src/chat.test.ts CHANGED
@@ -1,15 +1,41 @@
1
- import { beforeEach, describe, expect, it, vi } from "vitest";
2
- import { registerFeishuChatTools } from "./chat.js";
1
+ import { createTestPluginApi } from "openclaw/plugin-sdk/plugin-test-api";
2
+ import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
3
+ import type { OpenClawPluginApi, PluginRuntime } from "../runtime-api.js";
3
4
 
4
5
  const createFeishuClientMock = vi.hoisted(() => vi.fn());
6
+ const chatGetMock = vi.hoisted(() => vi.fn());
7
+ const chatMembersGetMock = vi.hoisted(() => vi.fn());
8
+ const contactUserGetMock = vi.hoisted(() => vi.fn());
5
9
 
6
10
  vi.mock("./client.js", () => ({
7
11
  createFeishuClient: createFeishuClientMock,
8
12
  }));
9
13
 
14
+ let registerFeishuChatTools: typeof import("./chat.js").registerFeishuChatTools;
15
+
16
+ function createFeishuToolRuntime(): PluginRuntime {
17
+ return {} as PluginRuntime;
18
+ }
19
+
10
20
  describe("registerFeishuChatTools", () => {
11
- const chatGetMock = vi.hoisted(() => vi.fn());
12
- const chatMembersGetMock = vi.hoisted(() => vi.fn());
21
+ function createChatToolApi(params: {
22
+ config: OpenClawPluginApi["config"];
23
+ registerTool: OpenClawPluginApi["registerTool"];
24
+ }): OpenClawPluginApi {
25
+ return createTestPluginApi({
26
+ id: "feishu-test",
27
+ name: "Feishu Test",
28
+ source: "local",
29
+ config: params.config,
30
+ runtime: createFeishuToolRuntime(),
31
+ logger: { debug: vi.fn(), info: vi.fn(), warn: vi.fn(), error: vi.fn() },
32
+ registerTool: params.registerTool,
33
+ });
34
+ }
35
+
36
+ beforeAll(async () => {
37
+ ({ registerFeishuChatTools } = await import("./chat.js"));
38
+ });
13
39
 
14
40
  beforeEach(() => {
15
41
  vi.clearAllMocks();
@@ -18,25 +44,29 @@ describe("registerFeishuChatTools", () => {
18
44
  chat: { get: chatGetMock },
19
45
  chatMembers: { get: chatMembersGetMock },
20
46
  },
47
+ contact: {
48
+ user: { get: contactUserGetMock },
49
+ },
21
50
  });
22
51
  });
23
52
 
24
53
  it("registers feishu_chat and handles info/members actions", async () => {
25
54
  const registerTool = vi.fn();
26
- registerFeishuChatTools({
27
- config: {
28
- channels: {
29
- feishu: {
30
- enabled: true,
31
- appId: "app_id",
32
- appSecret: "app_secret", // pragma: allowlist secret
33
- tools: { chat: true },
55
+ registerFeishuChatTools(
56
+ createChatToolApi({
57
+ config: {
58
+ channels: {
59
+ feishu: {
60
+ enabled: true,
61
+ appId: "app_id",
62
+ appSecret: "app_secret", // pragma: allowlist secret
63
+ tools: { chat: true },
64
+ },
34
65
  },
35
66
  },
36
- } as any,
37
- logger: { debug: vi.fn(), info: vi.fn() } as any,
38
- registerTool,
39
- } as any);
67
+ registerTool,
68
+ }),
69
+ );
40
70
 
41
71
  expect(registerTool).toHaveBeenCalledTimes(1);
42
72
  const tool = registerTool.mock.calls[0]?.[0];
@@ -66,24 +96,50 @@ describe("registerFeishuChatTools", () => {
66
96
  members: [expect.objectContaining({ member_id: "ou_1", name: "member1" })],
67
97
  }),
68
98
  );
99
+
100
+ contactUserGetMock.mockResolvedValueOnce({
101
+ code: 0,
102
+ data: {
103
+ user: {
104
+ open_id: "ou_1",
105
+ name: "member1",
106
+ email: "member1@example.com",
107
+ department_ids: ["od_1"],
108
+ },
109
+ },
110
+ });
111
+ const memberInfoResult = await tool.execute("tc_3", {
112
+ action: "member_info",
113
+ member_id: "ou_1",
114
+ });
115
+ expect(memberInfoResult.details).toEqual(
116
+ expect.objectContaining({
117
+ member_id: "ou_1",
118
+ open_id: "ou_1",
119
+ name: "member1",
120
+ email: "member1@example.com",
121
+ department_ids: ["od_1"],
122
+ }),
123
+ );
69
124
  });
70
125
 
71
126
  it("skips registration when chat tool is disabled", () => {
72
127
  const registerTool = vi.fn();
73
- registerFeishuChatTools({
74
- config: {
75
- channels: {
76
- feishu: {
77
- enabled: true,
78
- appId: "app_id",
79
- appSecret: "app_secret", // pragma: allowlist secret
80
- tools: { chat: false },
128
+ registerFeishuChatTools(
129
+ createChatToolApi({
130
+ config: {
131
+ channels: {
132
+ feishu: {
133
+ enabled: true,
134
+ appId: "app_id",
135
+ appSecret: "app_secret", // pragma: allowlist secret
136
+ tools: { chat: false },
137
+ },
81
138
  },
82
139
  },
83
- } as any,
84
- logger: { debug: vi.fn(), info: vi.fn() } as any,
85
- registerTool,
86
- } as any);
140
+ registerTool,
141
+ }),
142
+ );
87
143
  expect(registerTool).not.toHaveBeenCalled();
88
144
  });
89
145
  });
package/src/chat.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import type * as Lark from "@larksuiteoapi/node-sdk";
2
- import type { OpenClawPluginApi } from "openclaw/plugin-sdk/feishu";
2
+ import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
3
+ import type { OpenClawPluginApi } from "../runtime-api.js";
3
4
  import { listEnabledFeishuAccounts } from "./accounts.js";
4
5
  import { FeishuChatSchema, type FeishuChatParams } from "./chat-schema.js";
5
6
  import { createFeishuClient } from "./client.js";
@@ -12,7 +13,7 @@ function json(data: unknown) {
12
13
  };
13
14
  }
14
15
 
15
- async function getChatInfo(client: Lark.Client, chatId: string) {
16
+ export async function getChatInfo(client: Lark.Client, chatId: string) {
16
17
  const res = await client.im.chat.get({ path: { chat_id: chatId } });
17
18
  if (res.code !== 0) {
18
19
  throw new Error(res.msg);
@@ -36,7 +37,7 @@ async function getChatInfo(client: Lark.Client, chatId: string) {
36
37
  };
37
38
  }
38
39
 
39
- async function getChatMembers(
40
+ export async function getChatMembers(
40
41
  client: Lark.Client,
41
42
  chatId: string,
42
43
  pageSize?: number,
@@ -71,22 +72,68 @@ async function getChatMembers(
71
72
  };
72
73
  }
73
74
 
75
+ export async function getFeishuMemberInfo(
76
+ client: Lark.Client,
77
+ memberId: string,
78
+ memberIdType: "open_id" | "user_id" | "union_id" = "open_id",
79
+ ) {
80
+ const res = await client.contact.user.get({
81
+ path: { user_id: memberId },
82
+ params: {
83
+ user_id_type: memberIdType,
84
+ department_id_type: "open_department_id",
85
+ },
86
+ });
87
+
88
+ if (res.code !== 0) {
89
+ throw new Error(res.msg);
90
+ }
91
+
92
+ const user = res.data?.user;
93
+ return {
94
+ member_id: memberId,
95
+ member_id_type: memberIdType,
96
+ open_id: user?.open_id,
97
+ user_id: user?.user_id,
98
+ union_id: user?.union_id,
99
+ name: user?.name,
100
+ en_name: user?.en_name,
101
+ nickname: user?.nickname,
102
+ email: user?.email,
103
+ enterprise_email: user?.enterprise_email,
104
+ mobile: user?.mobile,
105
+ mobile_visible: user?.mobile_visible,
106
+ status: user?.status,
107
+ avatar: user?.avatar,
108
+ department_ids: user?.department_ids,
109
+ department_path: user?.department_path,
110
+ leader_user_id: user?.leader_user_id,
111
+ city: user?.city,
112
+ country: user?.country,
113
+ work_station: user?.work_station,
114
+ join_time: user?.join_time,
115
+ is_tenant_manager: user?.is_tenant_manager,
116
+ employee_no: user?.employee_no,
117
+ employee_type: user?.employee_type,
118
+ description: user?.description,
119
+ job_title: user?.job_title,
120
+ geo: user?.geo,
121
+ };
122
+ }
123
+
74
124
  export function registerFeishuChatTools(api: OpenClawPluginApi) {
75
125
  if (!api.config) {
76
- api.logger.debug?.("feishu_chat: No config available, skipping chat tools");
77
126
  return;
78
127
  }
79
128
 
80
129
  const accounts = listEnabledFeishuAccounts(api.config);
81
130
  if (accounts.length === 0) {
82
- api.logger.debug?.("feishu_chat: No Feishu accounts configured, skipping chat tools");
83
131
  return;
84
132
  }
85
133
 
86
134
  const firstAccount = accounts[0];
87
135
  const toolsCfg = resolveToolsConfig(firstAccount.config.tools);
88
136
  if (!toolsCfg.chat) {
89
- api.logger.debug?.("feishu_chat: chat tool disabled in config");
90
137
  return;
91
138
  }
92
139
 
@@ -96,7 +143,7 @@ export function registerFeishuChatTools(api: OpenClawPluginApi) {
96
143
  {
97
144
  name: "feishu_chat",
98
145
  label: "Feishu Chat",
99
- description: "Feishu chat operations. Actions: members, info",
146
+ description: "Feishu chat operations. Actions: members, info, member_info",
100
147
  parameters: FeishuChatSchema,
101
148
  async execute(_toolCallId, params) {
102
149
  const p = params as FeishuChatParams;
@@ -104,6 +151,9 @@ export function registerFeishuChatTools(api: OpenClawPluginApi) {
104
151
  const client = getClient();
105
152
  switch (p.action) {
106
153
  case "members":
154
+ if (!p.chat_id) {
155
+ return json({ error: "chat_id is required for action members" });
156
+ }
107
157
  return json(
108
158
  await getChatMembers(
109
159
  client,
@@ -114,17 +164,25 @@ export function registerFeishuChatTools(api: OpenClawPluginApi) {
114
164
  ),
115
165
  );
116
166
  case "info":
167
+ if (!p.chat_id) {
168
+ return json({ error: "chat_id is required for action info" });
169
+ }
117
170
  return json(await getChatInfo(client, p.chat_id));
171
+ case "member_info":
172
+ if (!p.member_id) {
173
+ return json({ error: "member_id is required for action member_info" });
174
+ }
175
+ return json(
176
+ await getFeishuMemberInfo(client, p.member_id, p.member_id_type ?? "open_id"),
177
+ );
118
178
  default:
119
179
  return json({ error: `Unknown action: ${String(p.action)}` });
120
180
  }
121
181
  } catch (err) {
122
- return json({ error: err instanceof Error ? err.message : String(err) });
182
+ return json({ error: formatErrorMessage(err) });
123
183
  }
124
184
  },
125
185
  },
126
186
  { name: "feishu_chat" },
127
187
  );
128
-
129
- api.logger.info?.("feishu_chat: Registered feishu_chat tool");
130
188
  }