@zhin.js/adapter-lark 4.0.2 → 4.0.3

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 (65) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/README.md +59 -88
  3. package/adapters/lark.ts +26 -0
  4. package/agent/tools/add_members.ts +2 -2
  5. package/agent/tools/create_chat.ts +2 -2
  6. package/agent/tools/dissolve_chat.ts +2 -2
  7. package/agent/tools/get_user.ts +2 -2
  8. package/agent/tools/remove_managers.ts +2 -2
  9. package/agent/tools/set_managers.ts +2 -2
  10. package/agent/tools/update_chat.ts +2 -2
  11. package/agent/tools/upload_file.ts +2 -2
  12. package/lib/endpoint.d.ts +53 -0
  13. package/lib/endpoint.js +333 -0
  14. package/lib/index.d.ts +5 -0
  15. package/lib/index.js +5 -0
  16. package/lib/lark-agent-deps.d.ts +27 -0
  17. package/lib/lark-agent-deps.js +30 -0
  18. package/lib/platform-permit.d.ts +15 -0
  19. package/lib/{src/platform-permit.js → platform-permit.js} +1 -2
  20. package/lib/protocol.d.ts +111 -0
  21. package/lib/protocol.js +204 -0
  22. package/lib/webhook.d.ts +13 -0
  23. package/lib/webhook.js +67 -0
  24. package/package.json +43 -24
  25. package/plugin.ts +12 -0
  26. package/schema.json +25 -0
  27. package/src/endpoint.ts +354 -705
  28. package/src/index.ts +45 -59
  29. package/src/lark-agent-deps.ts +53 -9
  30. package/src/platform-permit.ts +1 -1
  31. package/src/protocol.ts +332 -0
  32. package/src/webhook.ts +101 -0
  33. package/lib/agent/tools/add_members.js +0 -21
  34. package/lib/agent/tools/add_members.js.map +0 -1
  35. package/lib/agent/tools/create_chat.js +0 -20
  36. package/lib/agent/tools/create_chat.js.map +0 -1
  37. package/lib/agent/tools/dissolve_chat.js +0 -18
  38. package/lib/agent/tools/dissolve_chat.js.map +0 -1
  39. package/lib/agent/tools/get_user.js +0 -17
  40. package/lib/agent/tools/get_user.js.map +0 -1
  41. package/lib/agent/tools/remove_managers.js +0 -21
  42. package/lib/agent/tools/remove_managers.js.map +0 -1
  43. package/lib/agent/tools/set_managers.js +0 -21
  44. package/lib/agent/tools/set_managers.js.map +0 -1
  45. package/lib/agent/tools/update_chat.js +0 -22
  46. package/lib/agent/tools/update_chat.js.map +0 -1
  47. package/lib/agent/tools/upload_file.js +0 -19
  48. package/lib/agent/tools/upload_file.js.map +0 -1
  49. package/lib/src/adapter.js +0 -48
  50. package/lib/src/adapter.js.map +0 -1
  51. package/lib/src/endpoint.js +0 -677
  52. package/lib/src/endpoint.js.map +0 -1
  53. package/lib/src/index.js +0 -43
  54. package/lib/src/index.js.map +0 -1
  55. package/lib/src/lark-agent-deps.js +0 -10
  56. package/lib/src/lark-agent-deps.js.map +0 -1
  57. package/lib/src/platform-permit.js.map +0 -1
  58. package/lib/src/segment-mapper.js +0 -2
  59. package/lib/src/segment-mapper.js.map +0 -1
  60. package/lib/src/types.js +0 -2
  61. package/lib/src/types.js.map +0 -1
  62. package/plugin.yml +0 -3
  63. package/src/adapter.ts +0 -57
  64. package/src/segment-mapper.ts +0 -1
  65. package/src/types.ts +0 -58
@@ -0,0 +1,333 @@
1
+ /**
2
+ * LarkEndpoint — lifecycle, outbound, admit, OpenAPI helpers for agent tools.
3
+ */
4
+ import { readFile } from 'node:fs/promises';
5
+ import { basename } from 'node:path';
6
+ import { formatCompact, getLogger } from '@zhin.js/logger';
7
+ import { registerLarkAgentEndpoint } from './lark-agent-deps.js';
8
+ import { formatInboundContent, formatOutboundBody, generateMessageId, resolveChatType, resolveSender, resolveTarget, } from './protocol.js';
9
+ import { registerLarkWebhookRoutes } from './webhook.js';
10
+ const logger = getLogger('lark');
11
+ export class LarkEndpoint {
12
+ #options;
13
+ #fetch;
14
+ #routeReleases = [];
15
+ #accessToken = { token: '', expires_in: 0, timestamp: 0 };
16
+ #refreshPromise = null;
17
+ #open = false;
18
+ #started = false;
19
+ #unregisterAgent;
20
+ constructor(options) {
21
+ this.#options = options;
22
+ this.#fetch = options.fetch ?? globalThis.fetch;
23
+ }
24
+ /** Used by webhook handler. */
25
+ get isOpen() {
26
+ return this.#open;
27
+ }
28
+ get config() {
29
+ return this.#options.config;
30
+ }
31
+ async start() {
32
+ if (this.#started)
33
+ return;
34
+ this.#started = true;
35
+ try {
36
+ await this.#refreshAccessToken();
37
+ this.#unregisterAgent = registerLarkAgentEndpoint(this.#options.config.name, this);
38
+ this.#routeReleases.push(...registerLarkWebhookRoutes(this.#options.http, this));
39
+ logger.debug(formatCompact({
40
+ endpoint: this.#options.config.name,
41
+ op: 'webhook',
42
+ path: this.#options.config.webhookPath,
43
+ }));
44
+ }
45
+ catch (error) {
46
+ await this.stop();
47
+ logger.error('Failed to connect Lark endpoint:', error);
48
+ throw error;
49
+ }
50
+ }
51
+ open() {
52
+ this.#open = true;
53
+ }
54
+ close() {
55
+ this.#open = false;
56
+ }
57
+ async stop() {
58
+ this.#open = false;
59
+ for (const release of this.#routeReleases.splice(0))
60
+ release();
61
+ this.#unregisterAgent?.();
62
+ this.#unregisterAgent = undefined;
63
+ this.#started = false;
64
+ logger.debug(formatCompact({ op: 'disconnect', endpoint: this.#options.config.name }));
65
+ }
66
+ async send({ target, payload }) {
67
+ const content = formatOutboundBody(payload);
68
+ const data = await this.#request('/im/v1/messages', {
69
+ method: 'POST',
70
+ params: { receive_id_type: 'chat_id' },
71
+ body: {
72
+ receive_id: target,
73
+ msg_type: content.msg_type,
74
+ content: content.content,
75
+ },
76
+ });
77
+ if (data.code !== 0) {
78
+ throw new Error(`Failed to send message: ${data.msg}`);
79
+ }
80
+ const messageId = data.data?.message_id || `${Date.now()}`;
81
+ logger.debug(formatCompact({
82
+ op: 'send',
83
+ endpoint: this.#options.config.name,
84
+ to: target,
85
+ id: messageId,
86
+ }));
87
+ return messageId;
88
+ }
89
+ /** Test / internal: admit a parsed message when open (non-webhook path). */
90
+ admit(msg) {
91
+ if (!this.#open)
92
+ return;
93
+ const target = resolveTarget(msg);
94
+ void this.#options.gateway.receive({
95
+ adapter: this.#options.id,
96
+ target,
97
+ content: formatInboundContent(msg),
98
+ sender: resolveSender(msg),
99
+ id: generateMessageId(msg),
100
+ metadata: Object.freeze({
101
+ messageType: msg.message_type,
102
+ chatType: resolveChatType(msg.chat_id),
103
+ endpoint: this.#options.config.name,
104
+ }),
105
+ }).catch((err) => {
106
+ logger.warn(formatCompact({
107
+ op: 'lark_gateway_receive_failed',
108
+ target,
109
+ error: err instanceof Error ? err.message : String(err),
110
+ }));
111
+ });
112
+ }
113
+ async getUserInfo(userId, userIdType = 'open_id') {
114
+ try {
115
+ const data = await this.#request(`/contact/v3/users/${userId}`, {
116
+ method: 'GET',
117
+ params: { user_id_type: userIdType },
118
+ });
119
+ return data.data?.user ?? null;
120
+ }
121
+ catch (error) {
122
+ logger.error('Failed to get user info:', error);
123
+ return null;
124
+ }
125
+ }
126
+ async getChatInfo(chatId) {
127
+ try {
128
+ const data = await this.#request(`/im/v1/chats/${chatId}`, { method: 'GET' });
129
+ return data.data ?? null;
130
+ }
131
+ catch (error) {
132
+ logger.error('Failed to get chat info:', error);
133
+ return null;
134
+ }
135
+ }
136
+ async uploadFile(filePath, fileType = 'file') {
137
+ try {
138
+ await this.#ensureAccessToken();
139
+ const buf = await readFile(filePath);
140
+ const form = new FormData();
141
+ form.append('file', new Blob([buf]), basename(filePath));
142
+ form.append('file_type', fileType);
143
+ const url = `${this.#options.config.apiBaseUrl}/im/v1/files`;
144
+ const response = await this.#fetch(url, {
145
+ method: 'POST',
146
+ headers: { Authorization: `Bearer ${this.#accessToken.token}` },
147
+ body: form,
148
+ });
149
+ const data = await response.json();
150
+ if (data.code === 0) {
151
+ return data.data?.file_key || null;
152
+ }
153
+ throw new Error(`Upload failed: ${data.msg}`);
154
+ }
155
+ catch (error) {
156
+ logger.error('Failed to upload file:', error);
157
+ return null;
158
+ }
159
+ }
160
+ async createChat(name, userIds, ownerId) {
161
+ try {
162
+ const data = await this.#request('/im/v1/chats', {
163
+ method: 'POST',
164
+ body: {
165
+ name,
166
+ user_id_list: userIds,
167
+ ...(ownerId ? { owner_id: ownerId } : {}),
168
+ },
169
+ });
170
+ if (data.code === 0)
171
+ return data.data?.chat_id || null;
172
+ throw new Error(`Failed to create chat: ${data.msg}`);
173
+ }
174
+ catch (error) {
175
+ logger.error('Failed to create chat:', error);
176
+ return null;
177
+ }
178
+ }
179
+ async updateChatInfo(chatId, options) {
180
+ try {
181
+ const data = await this.#request(`/im/v1/chats/${chatId}`, {
182
+ method: 'PUT',
183
+ body: options,
184
+ });
185
+ if (data.code === 0)
186
+ return true;
187
+ throw new Error(`Failed to update chat: ${data.msg}`);
188
+ }
189
+ catch (error) {
190
+ logger.error('Failed to update chat:', error);
191
+ return false;
192
+ }
193
+ }
194
+ async addChatMembers(chatId, userIds) {
195
+ try {
196
+ const data = await this.#request(`/im/v1/chats/${chatId}/members`, {
197
+ method: 'POST',
198
+ body: { id_list: userIds },
199
+ });
200
+ if (data.code === 0)
201
+ return true;
202
+ throw new Error(`Failed to add members: ${data.msg}`);
203
+ }
204
+ catch (error) {
205
+ logger.error('Failed to add chat members:', error);
206
+ return false;
207
+ }
208
+ }
209
+ async removeChatMembers(chatId, userIds) {
210
+ try {
211
+ const data = await this.#request(`/im/v1/chats/${chatId}/members`, {
212
+ method: 'DELETE',
213
+ body: { id_list: userIds },
214
+ });
215
+ if (data.code === 0)
216
+ return true;
217
+ throw new Error(`Failed to remove members: ${data.msg}`);
218
+ }
219
+ catch (error) {
220
+ logger.error('Failed to remove chat members:', error);
221
+ return false;
222
+ }
223
+ }
224
+ async getChatMembers(chatId) {
225
+ try {
226
+ const data = await this.#request(`/im/v1/chats/${chatId}/members`, { method: 'GET' });
227
+ if (data.code === 0)
228
+ return data.data?.items || [];
229
+ throw new Error(`Failed to get members: ${data.msg}`);
230
+ }
231
+ catch (error) {
232
+ logger.error('Failed to get chat members:', error);
233
+ return [];
234
+ }
235
+ }
236
+ async dissolveChat(chatId) {
237
+ try {
238
+ const data = await this.#request(`/im/v1/chats/${chatId}`, { method: 'DELETE' });
239
+ if (data.code === 0)
240
+ return true;
241
+ throw new Error(`Failed to dissolve chat: ${data.msg}`);
242
+ }
243
+ catch (error) {
244
+ logger.error('Failed to dissolve chat:', error);
245
+ return false;
246
+ }
247
+ }
248
+ async setChatManagers(chatId, userIds) {
249
+ try {
250
+ const data = await this.#request(`/im/v1/chats/${chatId}/managers/add_managers`, {
251
+ method: 'POST',
252
+ body: { manager_ids: userIds },
253
+ });
254
+ if (data.code === 0)
255
+ return true;
256
+ throw new Error(`Failed to set managers: ${data.msg}`);
257
+ }
258
+ catch (error) {
259
+ logger.error('Failed to set chat managers:', error);
260
+ return false;
261
+ }
262
+ }
263
+ async removeChatManagers(chatId, userIds) {
264
+ try {
265
+ const data = await this.#request(`/im/v1/chats/${chatId}/managers/delete_managers`, {
266
+ method: 'POST',
267
+ body: { manager_ids: userIds },
268
+ });
269
+ if (data.code === 0)
270
+ return true;
271
+ throw new Error(`Failed to remove managers: ${data.msg}`);
272
+ }
273
+ catch (error) {
274
+ logger.error('Failed to remove chat managers:', error);
275
+ return false;
276
+ }
277
+ }
278
+ async #request(path, options = {}) {
279
+ await this.#ensureAccessToken();
280
+ const { method = 'GET', params = {}, body } = options;
281
+ const urlParams = new URLSearchParams(Object.fromEntries(Object.entries(params).map(([key, value]) => [key, String(value)])));
282
+ const query = urlParams.toString();
283
+ const url = `${this.#options.config.apiBaseUrl}${path}${query ? `?${query}` : ''}`;
284
+ const response = await this.#fetch(url, {
285
+ method,
286
+ headers: {
287
+ 'Content-Type': 'application/json; charset=utf-8',
288
+ Authorization: `Bearer ${this.#accessToken.token}`,
289
+ },
290
+ body: body && method !== 'GET' ? JSON.stringify(body) : undefined,
291
+ });
292
+ if (!response.ok) {
293
+ const text = await response.text().catch(() => '');
294
+ throw new Error(`Lark API error ${response.status}: ${text}`);
295
+ }
296
+ return await response.json();
297
+ }
298
+ async #ensureAccessToken() {
299
+ const now = Date.now();
300
+ if (this.#accessToken.token
301
+ && now < this.#accessToken.timestamp + (this.#accessToken.expires_in - 300) * 1000) {
302
+ return;
303
+ }
304
+ if (this.#refreshPromise) {
305
+ await this.#refreshPromise;
306
+ return;
307
+ }
308
+ this.#refreshPromise = this.#refreshAccessToken()
309
+ .then(() => this.#accessToken.token)
310
+ .finally(() => { this.#refreshPromise = null; });
311
+ await this.#refreshPromise;
312
+ }
313
+ async #refreshAccessToken() {
314
+ const { appId, appSecret, apiBaseUrl } = this.#options.config;
315
+ const url = `${apiBaseUrl}/auth/v3/tenant_access_token/internal`;
316
+ const response = await this.#fetch(url, {
317
+ method: 'POST',
318
+ headers: { 'Content-Type': 'application/json; charset=utf-8' },
319
+ body: JSON.stringify({ app_id: appId, app_secret: appSecret }),
320
+ });
321
+ const data = await response.json();
322
+ if (data.code === 0 && data.tenant_access_token) {
323
+ this.#accessToken = {
324
+ token: data.tenant_access_token,
325
+ expires_in: data.expire ?? 7200,
326
+ timestamp: Date.now(),
327
+ };
328
+ logger.debug('Access token refreshed successfully');
329
+ return;
330
+ }
331
+ throw new Error(`Failed to get access token: ${data.msg} (${data.code})`);
332
+ }
333
+ }
package/lib/index.d.ts ADDED
@@ -0,0 +1,5 @@
1
+ export { formatInboundContent, formatOutboundBody, generateMessageId, headerValue, normalizeWebhookPath, readTextBody, resolveChatType, resolveLarkConfig, resolveSender, resolveTarget, verifySignature, type AccessToken, type LarkAdapterConfig, type LarkApiResponse, type LarkEventBody, type LarkMessage, type LarkSendBody, type LarkWireSegment, type ResolvedLarkConfig, } from './protocol.js';
2
+ export { getLarkAgentDeps, registerLarkAgentEndpoint, setLarkAgentDeps, type LarkAgentDeps, type LarkAgentEndpoint, } from './lark-agent-deps.js';
3
+ export { checkLarkPlatformPermit, larkGroupPermitResolver, normalizeLarkSenderForPermit, platformPermit, registerLarkPlatformPermitChecker, } from './platform-permit.js';
4
+ export { LarkEndpoint, type LarkEndpointOptions, type LarkFetch, } from './endpoint.js';
5
+ export { registerLarkWebhookRoutes, handleLarkWebhookRequest, type LarkWebhookHandler, } from './webhook.js';
package/lib/index.js ADDED
@@ -0,0 +1,5 @@
1
+ export { formatInboundContent, formatOutboundBody, generateMessageId, headerValue, normalizeWebhookPath, readTextBody, resolveChatType, resolveLarkConfig, resolveSender, resolveTarget, verifySignature, } from './protocol.js';
2
+ export { getLarkAgentDeps, registerLarkAgentEndpoint, setLarkAgentDeps, } from './lark-agent-deps.js';
3
+ export { checkLarkPlatformPermit, larkGroupPermitResolver, normalizeLarkSenderForPermit, platformPermit, registerLarkPlatformPermitChecker, } from './platform-permit.js';
4
+ export { LarkEndpoint, } from './endpoint.js';
5
+ export { registerLarkWebhookRoutes, handleLarkWebhookRequest, } from './webhook.js';
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Agent tool deps for lark (user / chat / managers / upload).
3
+ * Endpoints register themselves on start; tools look up by endpoint id.
4
+ */
5
+ export interface LarkAgentEndpoint {
6
+ getUserInfo(userId: string, userIdType?: 'open_id' | 'user_id' | 'union_id'): Promise<unknown>;
7
+ getChatInfo(chatId: string): Promise<unknown>;
8
+ uploadFile(filePath: string, fileType?: 'image' | 'file' | 'video' | 'audio'): Promise<string | null>;
9
+ createChat(name: string, userIds: string[], ownerId?: string): Promise<string | null>;
10
+ updateChatInfo(chatId: string, options: {
11
+ name?: string;
12
+ description?: string;
13
+ }): Promise<boolean>;
14
+ addChatMembers(chatId: string, userIds: string[]): Promise<boolean>;
15
+ removeChatMembers(chatId: string, userIds: string[]): Promise<boolean>;
16
+ getChatMembers(chatId: string): Promise<unknown[]>;
17
+ dissolveChat(chatId: string): Promise<boolean>;
18
+ setChatManagers(chatId: string, userIds: string[]): Promise<boolean>;
19
+ removeChatManagers(chatId: string, userIds: string[]): Promise<boolean>;
20
+ }
21
+ export interface LarkAgentDeps {
22
+ getEndpoint: (endpointId: string) => LarkAgentEndpoint;
23
+ }
24
+ export declare function registerLarkAgentEndpoint(endpointId: string, endpoint: LarkAgentEndpoint): () => void;
25
+ /** Optional override used by tests / transitional callers. Pass `null` to clear. */
26
+ export declare function setLarkAgentDeps(deps: LarkAgentDeps | null): void;
27
+ export declare function getLarkAgentDeps(): LarkAgentDeps;
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Agent tool deps for lark (user / chat / managers / upload).
3
+ * Endpoints register themselves on start; tools look up by endpoint id.
4
+ */
5
+ const endpoints = new Map();
6
+ let override = null;
7
+ export function registerLarkAgentEndpoint(endpointId, endpoint) {
8
+ endpoints.set(endpointId, endpoint);
9
+ return () => {
10
+ if (endpoints.get(endpointId) === endpoint) {
11
+ endpoints.delete(endpointId);
12
+ }
13
+ };
14
+ }
15
+ /** Optional override used by tests / transitional callers. Pass `null` to clear. */
16
+ export function setLarkAgentDeps(deps) {
17
+ override = deps;
18
+ }
19
+ export function getLarkAgentDeps() {
20
+ if (override)
21
+ return override;
22
+ return {
23
+ getEndpoint(endpointId) {
24
+ const endpoint = endpoints.get(endpointId);
25
+ if (!endpoint)
26
+ throw new Error(`Endpoint ${endpointId} 不存在`);
27
+ return endpoint;
28
+ },
29
+ };
30
+ }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * 飞书 Lark platform permit
3
+ */
4
+ import { type Message } from '@zhin.js/core';
5
+ export declare function platformPermit(perm: string): string;
6
+ export declare function larkGroupPermitResolver(logicalPerm: string): string;
7
+ export declare function normalizeLarkSenderForPermit(input: {
8
+ isOwner?: boolean;
9
+ isAdmin?: boolean;
10
+ }): {
11
+ role?: string;
12
+ permissions?: string[];
13
+ };
14
+ export declare function checkLarkPlatformPermit(perm: string, message: Message<any>): boolean;
15
+ export declare function registerLarkPlatformPermitChecker(): () => void;
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * 飞书 Lark platform permit
3
3
  */
4
- import { registerPlatformPermitChecker } from 'zhin.js';
4
+ import { registerPlatformPermitChecker } from '@zhin.js/core';
5
5
  const ADAPTER = 'lark';
6
6
  export function platformPermit(perm) {
7
7
  return `platform(${ADAPTER},${perm})`;
@@ -41,4 +41,3 @@ export function checkLarkPlatformPermit(perm, message) {
41
41
  export function registerLarkPlatformPermitChecker() {
42
42
  return registerPlatformPermitChecker(ADAPTER, checkLarkPlatformPermit);
43
43
  }
44
- //# sourceMappingURL=platform-permit.js.map
@@ -0,0 +1,111 @@
1
+ /**
2
+ * Lark/Feishu protocol helpers — no legacy Adapter/Endpoint / segment-mapper.
3
+ * Canonicalization is owned by gateway/core before endpoint.send.
4
+ */
5
+ import type { IncomingMessage } from 'node:http';
6
+ /** Plugin Runtime owner config (`plugins.<instanceKey>` / schema.json). */
7
+ export interface LarkAdapterConfig {
8
+ readonly name?: string;
9
+ readonly appId?: string;
10
+ readonly appSecret?: string;
11
+ readonly encryptKey?: string;
12
+ readonly verificationToken?: string;
13
+ readonly webhookPath?: string;
14
+ readonly apiBaseUrl?: string;
15
+ readonly isFeishu?: boolean;
16
+ /** Transitional: legacy root `endpoints[]` with `context: lark`. */
17
+ readonly endpoints?: ReadonlyArray<Partial<ResolvedLarkConfig> & {
18
+ readonly context?: string;
19
+ }>;
20
+ }
21
+ export interface ResolvedLarkConfig {
22
+ readonly context: 'lark';
23
+ readonly name: string;
24
+ readonly appId: string;
25
+ readonly appSecret: string;
26
+ readonly encryptKey?: string;
27
+ readonly verificationToken?: string;
28
+ readonly webhookPath: string;
29
+ readonly apiBaseUrl: string;
30
+ readonly isFeishu: boolean;
31
+ }
32
+ export interface LarkMessage {
33
+ readonly message_id?: string;
34
+ readonly root_id?: string;
35
+ readonly parent_id?: string;
36
+ readonly create_time?: string;
37
+ readonly update_time?: string;
38
+ readonly chat_id?: string;
39
+ readonly sender?: {
40
+ readonly sender_id?: {
41
+ readonly user_id?: string;
42
+ readonly open_id?: string;
43
+ readonly union_id?: string;
44
+ };
45
+ readonly sender_type?: string;
46
+ readonly tenant_key?: string;
47
+ };
48
+ readonly message_type?: string;
49
+ readonly content?: string;
50
+ readonly mentions?: ReadonlyArray<{
51
+ readonly key?: string;
52
+ readonly id?: {
53
+ readonly user_id?: string;
54
+ readonly open_id?: string;
55
+ readonly union_id?: string;
56
+ };
57
+ readonly name?: string;
58
+ readonly tenant_key?: string;
59
+ }>;
60
+ }
61
+ export interface LarkEventBody {
62
+ readonly uuid?: string;
63
+ readonly token?: string;
64
+ readonly ts?: string;
65
+ readonly type?: string;
66
+ readonly challenge?: string;
67
+ readonly event?: {
68
+ readonly sender?: LarkMessage['sender'];
69
+ readonly message?: LarkMessage;
70
+ readonly [key: string]: unknown;
71
+ };
72
+ }
73
+ export interface AccessToken {
74
+ token: string;
75
+ expires_in: number;
76
+ timestamp: number;
77
+ }
78
+ export interface LarkApiResponse {
79
+ readonly code: number;
80
+ readonly msg?: string;
81
+ readonly tenant_access_token?: string;
82
+ readonly expire?: number;
83
+ readonly data?: Record<string, unknown>;
84
+ readonly [key: string]: unknown;
85
+ }
86
+ export interface LarkWireSegment {
87
+ readonly type: string;
88
+ readonly data?: Record<string, unknown>;
89
+ }
90
+ export interface LarkSendBody {
91
+ readonly msg_type: string;
92
+ readonly content: string;
93
+ }
94
+ export declare function resolveLarkConfig(config?: LarkAdapterConfig): ResolvedLarkConfig;
95
+ export declare function normalizeWebhookPath(path: string): string;
96
+ export declare function resolveChatType(chatId?: string): 'group' | 'private';
97
+ export declare function resolveTarget(msg: LarkMessage): string;
98
+ export declare function resolveSender(msg: LarkMessage): string;
99
+ export declare function generateMessageId(msg: LarkMessage): string;
100
+ /** Build inbound text for MessageGateway.receive. */
101
+ export declare function formatInboundContent(msg: LarkMessage): string;
102
+ export declare function verifySignature(encryptKey: string, timestamp: string, nonce: string, body: string, signature: string): boolean;
103
+ /**
104
+ * Wire-encode an already-rendered outbound payload into Lark im/v1 message body.
105
+ * Segment canonicalization is intentionally not done here.
106
+ */
107
+ export declare function formatOutboundBody(payload: unknown): LarkSendBody;
108
+ export declare function headerValue(headers: IncomingMessage['headers'], name: string): string;
109
+ export declare function readTextBody(request: IncomingMessage, options?: {
110
+ readonly limit?: number;
111
+ }): Promise<string>;