@zhin.js/adapter-lark 6.0.2 → 6.0.5

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/CHANGELOG.md CHANGED
@@ -1,5 +1,100 @@
1
1
  # @zhin.js/adapter-lark
2
2
 
3
+ ## 6.0.5
4
+
5
+ ### Patch Changes
6
+
7
+ - 51015d6: refactor(adapters): migrate platform-permit to PermissionSubject
8
+
9
+ All adapter platform-permit checkers now accept PermissionSubject
10
+ (duck-typed from Message/CommandSession) instead of Message directly.
11
+ registerPlatformPermitChecker is replaced by host.registerPlatform in
12
+ the adapter plugin setup. activity-feedback service updated for new
13
+ PermissionHost integration.
14
+
15
+ - Updated dependencies [6f9c366]
16
+ - Updated dependencies [373a56b]
17
+ - Updated dependencies [0de46a8]
18
+ - Updated dependencies [c106ecc]
19
+ - Updated dependencies [ca92e03]
20
+ - Updated dependencies [b0f37ae]
21
+ - Updated dependencies [ba08a2f]
22
+ - Updated dependencies [b08f7fe]
23
+ - Updated dependencies [daffd4c]
24
+ - Updated dependencies [36c7400]
25
+ - Updated dependencies [a9fa72e]
26
+ - Updated dependencies [c8de3ef]
27
+ - Updated dependencies [60f0fc8]
28
+ - Updated dependencies [574c990]
29
+ - Updated dependencies [d096f16]
30
+ - Updated dependencies [3f29623]
31
+ - Updated dependencies [2916852]
32
+ - Updated dependencies [d047869]
33
+ - Updated dependencies [162fa34]
34
+ - Updated dependencies [e62561e]
35
+ - Updated dependencies [3eeeb46]
36
+ - Updated dependencies [85d0f82]
37
+ - Updated dependencies [61bfc1c]
38
+ - Updated dependencies [04bad47]
39
+ - Updated dependencies [e40b048]
40
+ - Updated dependencies [5a5b1bb]
41
+ - Updated dependencies [f1708c3]
42
+ - Updated dependencies [d254a81]
43
+ - Updated dependencies [7123c47]
44
+ - Updated dependencies [05befc1]
45
+ - Updated dependencies [0663b6a]
46
+ - Updated dependencies [f28f9b3]
47
+ - Updated dependencies [9f340f7]
48
+ - Updated dependencies [e53444f]
49
+ - Updated dependencies [5eedd26]
50
+ - Updated dependencies [92b0dd7]
51
+ - Updated dependencies [f919b6f]
52
+ - Updated dependencies [098e411]
53
+ - Updated dependencies [e1b7c01]
54
+ - Updated dependencies [9b94f87]
55
+ - Updated dependencies [a7df753]
56
+ - @zhin.js/agent@1.1.5
57
+ - @zhin.js/host-http@1.0.8
58
+ - @zhin.js/permission@1.0.1
59
+ - @zhin.js/im-contract@1.0.3
60
+ - @zhin.js/adapter@1.1.7
61
+ - @zhin.js/plugin-runtime@1.1.5
62
+ - @zhin.js/command@1.0.9
63
+ - @zhin.js/core@1.5.4
64
+ - zhin.js@6.0.4
65
+
66
+ ## 6.0.4
67
+
68
+ ### Patch Changes
69
+
70
+ - f8c7a54: fix: im
71
+ - Updated dependencies [f8c7a54]
72
+ - @zhin.js/logger@1.0.76
73
+ - @zhin.js/host-http@1.0.7
74
+ - @zhin.js/adapter@1.1.6
75
+ - @zhin.js/agent@1.1.4
76
+ - @zhin.js/command@1.0.8
77
+ - @zhin.js/core@1.5.3
78
+ - @zhin.js/im-contract@1.0.2
79
+ - @zhin.js/plugin-runtime@1.1.4
80
+ - zhin.js@6.0.3
81
+
82
+ ## 6.0.3
83
+
84
+ ### Patch Changes
85
+
86
+ - Updated dependencies [afc0e66]
87
+ - Updated dependencies [2e41ad5]
88
+ - Updated dependencies [9f57124]
89
+ - @zhin.js/core@1.5.2
90
+ - @zhin.js/adapter@1.1.5
91
+ - @zhin.js/im-contract@1.0.1
92
+ - @zhin.js/plugin-runtime@1.1.3
93
+ - @zhin.js/command@1.0.7
94
+ - @zhin.js/host-http@1.0.6
95
+ - @zhin.js/agent@1.1.3
96
+ - zhin.js@6.0.2
97
+
3
98
  ## 6.0.2
4
99
 
5
100
  ### Patch Changes
package/README.md CHANGED
@@ -24,8 +24,8 @@ pnpm add @zhin.js/adapter-lark
24
24
  - `@zhin.js/plugin-runtime` — `plugin.ts`(`definePlugin`)
25
25
  - 配置经插件 `schema.json` 落到 `plugins.<instanceKey>`
26
26
 
27
- 入站:`gateway.receive({ adapter, target: chat_id, content: text, sender, metadata })`
28
- 出站:`send({ target, payload })` → `im/v1/messages`
27
+ 入站:`gateway.receive({ conversation, message: { conversation, id }, content: text, sender, metadata })`
28
+ 出站:`send({ conversation, payload })` → `im/v1/messages`
29
29
 
30
30
  入站 `metadata.mentioned`:**未接线**。消息事件的 `mentions[]` 元素含 `id.open_id`,但本适配器拿不到 bot 自身的 open_id——配置(`appId` / `appSecret` / `name` 等)不含 bot open_id,代码也未调用 `bot/v3/info` 获取应用信息,故无可靠判据比对 mentions。
31
31
 
package/adapters/lark.js CHANGED
@@ -21,8 +21,8 @@ export default defineAdapter({
21
21
  create(context) {
22
22
  const config = resolveLarkConfig(context.config);
23
23
  // 注册到插件运行时状态(lark.endpoint list 的"运行中"数据源)
24
- context.use(larkRuntimeStateToken).endpoints.set(config.name, {
25
- name: config.name,
24
+ context.use(larkRuntimeStateToken).endpoints.set(config.id, {
25
+ id: config.id,
26
26
  mode: 'webhook',
27
27
  });
28
28
  return new LarkEndpoint({
package/adapters/lark.ts CHANGED
@@ -26,8 +26,8 @@ export default defineAdapter<LarkAdapterConfig>({
26
26
  create(context) {
27
27
  const config = resolveLarkConfig(context.config);
28
28
  // 注册到插件运行时状态(lark.endpoint list 的"运行中"数据源)
29
- context.use(larkRuntimeStateToken).endpoints.set(config.name, {
30
- name: config.name,
29
+ context.use(larkRuntimeStateToken).endpoints.set(config.id, {
30
+ id: config.id,
31
31
  mode: 'webhook',
32
32
  });
33
33
  return new LarkEndpoint({
package/lib/endpoint.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { EndpointInstance, EndpointManagement } from '@zhin.js/adapter';
1
+ import type { EndpointInstance, EndpointManagement, EndpointSendRequest } from '@zhin.js/adapter';
2
2
  import type { MessageGateway } from '@zhin.js/core/runtime';
3
3
  import type { HttpHost } from '@zhin.js/host-http';
4
4
  import type { CapabilityId } from '@zhin.js/plugin-runtime';
@@ -40,10 +40,7 @@ export declare class LarkEndpoint implements EndpointInstance {
40
40
  open(): void;
41
41
  close(): void;
42
42
  stop(): Promise<void>;
43
- send({ target, payload }: {
44
- readonly target: string;
45
- readonly payload: unknown;
46
- }): Promise<string>;
43
+ send({ conversation, payload }: EndpointSendRequest): Promise<string>;
47
44
  recallMessage(messageId: string): Promise<void>;
48
45
  /** POST /im/v1/images(image_type=message),返回 image_key;失败返回 null。 */
49
46
  uploadImage(media: Parameters<typeof resolveMediaBinary>[0]): Promise<string | null>;
package/lib/endpoint.js CHANGED
@@ -3,15 +3,15 @@
3
3
  */
4
4
  import { readFile } from 'node:fs/promises';
5
5
  import { basename } from 'node:path';
6
- import { formatCompact, getLogger } from '@zhin.js/logger';
6
+ import { formatCompact, getAdapterLogger } from '@zhin.js/logger';
7
7
  import { registerLarkAgentEndpoint } from './lark-agent-deps.js';
8
8
  import { buildImageUploadForm, readOutboundImageMedia, resolveMediaBinary, } from './media-upload.js';
9
- import { formatInboundContent, formatOutboundBody, generateMessageId, resolveChatType, resolveSender, resolveTarget, } from './protocol.js';
9
+ import { formatInboundContent, formatOutboundBody, generateMessageId, larkInboundConversation, resolveChatType, resolveSender, } from './protocol.js';
10
10
  import { registerLarkWebhookRoutes } from './webhook.js';
11
- const logger = getLogger('lark');
12
11
  /** 出站 HTTP 调用统一 30s 超时。 */
13
12
  const OUTBOUND_TIMEOUT_MS = 30_000;
14
13
  export class LarkEndpoint {
14
+ #logger;
15
15
  #options;
16
16
  #fetch;
17
17
  /**
@@ -45,6 +45,7 @@ export class LarkEndpoint {
45
45
  #started = false;
46
46
  #unregisterAgent;
47
47
  constructor(options) {
48
+ this.#logger = getAdapterLogger('lark', options.config.id);
48
49
  this.#options = options;
49
50
  this.#fetch = options.fetch ?? globalThis.fetch;
50
51
  }
@@ -62,25 +63,25 @@ export class LarkEndpoint {
62
63
  try {
63
64
  if (!this.#options.config.encryptKey && !this.#options.config.verificationToken) {
64
65
  // encryptKey / verificationToken 都未配置时 webhook 完全无鉴权,任何人可伪造事件
65
- logger.warn(formatCompact({
66
- endpoint: this.#options.config.name,
66
+ this.#logger.warn(formatCompact({
67
+ endpoint: this.#options.config.id,
67
68
  op: 'webhook',
68
69
  ok: false,
69
70
  error: 'neither encryptKey nor verificationToken configured; webhook is unauthenticated',
70
71
  }));
71
72
  }
72
73
  await this.#refreshAccessToken();
73
- this.#unregisterAgent = registerLarkAgentEndpoint(this.#options.config.name, this);
74
+ this.#unregisterAgent = registerLarkAgentEndpoint(this.#options.config.id, this);
74
75
  this.#routeReleases.push(...registerLarkWebhookRoutes(this.#options.http, this));
75
- logger.debug(formatCompact({
76
- endpoint: this.#options.config.name,
76
+ this.#logger.debug(formatCompact({
77
+ endpoint: this.#options.config.id,
77
78
  op: 'webhook',
78
79
  path: this.#options.config.webhookPath,
79
80
  }));
80
81
  }
81
82
  catch (error) {
82
83
  await this.stop();
83
- logger.error('Failed to connect Lark endpoint:', error);
84
+ this.#logger.error('Failed to connect Lark endpoint:', error);
84
85
  throw error;
85
86
  }
86
87
  }
@@ -97,16 +98,16 @@ export class LarkEndpoint {
97
98
  this.#unregisterAgent?.();
98
99
  this.#unregisterAgent = undefined;
99
100
  this.#started = false;
100
- logger.debug(formatCompact({ op: 'disconnect', endpoint: this.#options.config.name }));
101
+ this.#logger.debug(formatCompact({ op: 'disconnect' }));
101
102
  }
102
- async send({ target, payload }) {
103
+ async send({ conversation, payload }) {
103
104
  const materialized = await this.#materializeOutboundMedia(payload);
104
105
  const content = formatOutboundBody(materialized);
105
106
  const data = await this.#request('/im/v1/messages', {
106
107
  method: 'POST',
107
108
  params: { receive_id_type: 'chat_id' },
108
109
  body: {
109
- receive_id: target,
110
+ receive_id: conversation.id,
110
111
  msg_type: content.msg_type,
111
112
  content: content.content,
112
113
  },
@@ -115,10 +116,7 @@ export class LarkEndpoint {
115
116
  throw new Error(`Failed to send message: ${data.msg}`);
116
117
  }
117
118
  const messageId = data.data?.message_id || `${Date.now()}`;
118
- logger.debug(formatCompact({
119
- op: 'send',
120
- endpoint: this.#options.config.name,
121
- to: target,
119
+ this.#logger.debug(formatCompact({ op: 'send', to: conversation.id,
122
120
  id: messageId,
123
121
  }));
124
122
  return messageId;
@@ -177,7 +175,7 @@ export class LarkEndpoint {
177
175
  throw new Error(`Image upload failed: ${data.msg}`);
178
176
  }
179
177
  catch (error) {
180
- logger.error('Failed to upload image:', error);
178
+ this.#logger.error('Failed to upload image:', error);
181
179
  return null;
182
180
  }
183
181
  }
@@ -185,22 +183,21 @@ export class LarkEndpoint {
185
183
  admit(msg) {
186
184
  if (!this.#open)
187
185
  return;
188
- const target = resolveTarget(msg);
186
+ const conversation = larkInboundConversation(String(this.#options.id), msg);
189
187
  void this.#options.gateway.receive({
190
- adapter: this.#options.id,
191
- target,
188
+ conversation,
189
+ message: { conversation, id: generateMessageId(msg) },
192
190
  content: formatInboundContent(msg),
193
- sender: resolveSender(msg),
194
- id: generateMessageId(msg),
191
+ sender: { id: resolveSender(msg) },
192
+ endpointId: this.#options.config.id,
195
193
  metadata: Object.freeze({
196
194
  messageType: msg.message_type,
197
195
  chatType: resolveChatType(msg.chat_id, msg.chat_type),
198
- endpoint: this.#options.config.name,
199
196
  }),
200
197
  }).catch((err) => {
201
- logger.warn(formatCompact({
198
+ this.#logger.warn(formatCompact({
202
199
  op: 'lark_gateway_receive_failed',
203
- target,
200
+ conversation: conversation.id,
204
201
  error: err instanceof Error ? err.message : String(err),
205
202
  }));
206
203
  });
@@ -214,7 +211,7 @@ export class LarkEndpoint {
214
211
  return data.data?.user ?? null;
215
212
  }
216
213
  catch (error) {
217
- logger.error('Failed to get user info:', error);
214
+ this.#logger.error('Failed to get user info:', error);
218
215
  return null;
219
216
  }
220
217
  }
@@ -224,7 +221,7 @@ export class LarkEndpoint {
224
221
  return data.data ?? null;
225
222
  }
226
223
  catch (error) {
227
- logger.error('Failed to get chat info:', error);
224
+ this.#logger.error('Failed to get chat info:', error);
228
225
  return null;
229
226
  }
230
227
  }
@@ -248,7 +245,7 @@ export class LarkEndpoint {
248
245
  throw new Error(`Upload failed: ${data.msg}`);
249
246
  }
250
247
  catch (error) {
251
- logger.error('Failed to upload file:', error);
248
+ this.#logger.error('Failed to upload file:', error);
252
249
  return null;
253
250
  }
254
251
  }
@@ -267,7 +264,7 @@ export class LarkEndpoint {
267
264
  throw new Error(`Failed to create chat: ${data.msg}`);
268
265
  }
269
266
  catch (error) {
270
- logger.error('Failed to create chat:', error);
267
+ this.#logger.error('Failed to create chat:', error);
271
268
  return null;
272
269
  }
273
270
  }
@@ -282,7 +279,7 @@ export class LarkEndpoint {
282
279
  throw new Error(`Failed to update chat: ${data.msg}`);
283
280
  }
284
281
  catch (error) {
285
- logger.error('Failed to update chat:', error);
282
+ this.#logger.error('Failed to update chat:', error);
286
283
  return false;
287
284
  }
288
285
  }
@@ -297,7 +294,7 @@ export class LarkEndpoint {
297
294
  throw new Error(`Failed to add members: ${data.msg}`);
298
295
  }
299
296
  catch (error) {
300
- logger.error('Failed to add chat members:', error);
297
+ this.#logger.error('Failed to add chat members:', error);
301
298
  return false;
302
299
  }
303
300
  }
@@ -312,7 +309,7 @@ export class LarkEndpoint {
312
309
  throw new Error(`Failed to remove members: ${data.msg}`);
313
310
  }
314
311
  catch (error) {
315
- logger.error('Failed to remove chat members:', error);
312
+ this.#logger.error('Failed to remove chat members:', error);
316
313
  return false;
317
314
  }
318
315
  }
@@ -324,7 +321,7 @@ export class LarkEndpoint {
324
321
  throw new Error(`Failed to get members: ${data.msg}`);
325
322
  }
326
323
  catch (error) {
327
- logger.error('Failed to get chat members:', error);
324
+ this.#logger.error('Failed to get chat members:', error);
328
325
  return [];
329
326
  }
330
327
  }
@@ -336,7 +333,7 @@ export class LarkEndpoint {
336
333
  throw new Error(`Failed to dissolve chat: ${data.msg}`);
337
334
  }
338
335
  catch (error) {
339
- logger.error('Failed to dissolve chat:', error);
336
+ this.#logger.error('Failed to dissolve chat:', error);
340
337
  return false;
341
338
  }
342
339
  }
@@ -351,7 +348,7 @@ export class LarkEndpoint {
351
348
  throw new Error(`Failed to set managers: ${data.msg}`);
352
349
  }
353
350
  catch (error) {
354
- logger.error('Failed to set chat managers:', error);
351
+ this.#logger.error('Failed to set chat managers:', error);
355
352
  return false;
356
353
  }
357
354
  }
@@ -366,7 +363,7 @@ export class LarkEndpoint {
366
363
  throw new Error(`Failed to remove managers: ${data.msg}`);
367
364
  }
368
365
  catch (error) {
369
- logger.error('Failed to remove chat managers:', error);
366
+ this.#logger.error('Failed to remove chat managers:', error);
370
367
  return false;
371
368
  }
372
369
  }
@@ -451,7 +448,7 @@ export class LarkEndpoint {
451
448
  expires_in: data.expire ?? 7200,
452
449
  timestamp: Date.now(),
453
450
  };
454
- logger.debug('Access token refreshed successfully');
451
+ this.#logger.debug('Access token refreshed successfully');
455
452
  return;
456
453
  }
457
454
  throw new Error(`Failed to get access token: ${data.msg} (${data.code})`);
package/lib/index.d.ts CHANGED
@@ -1,6 +1,6 @@
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';
1
+ export { formatInboundContent, formatOutboundBody, generateMessageId, headerValue, larkInboundConversation, normalizeWebhookPath, readTextBody, resolveChatType, resolveLarkConfig, resolveSender, verifySignature, type AccessToken, type LarkAdapterConfig, type LarkApiResponse, type LarkEventBody, type LarkMessage, type LarkSendBody, type LarkWireSegment, type ResolvedLarkConfig, } from './protocol.js';
2
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';
3
+ export { checkLarkPlatformPermit, larkGroupPermitResolver, normalizeLarkSenderForPermit, platformPermit, } from './platform-permit.js';
4
4
  export { LarkEndpoint, type LarkEndpointOptions, type LarkFetch, } from './endpoint.js';
5
5
  export { buildImageUploadForm, readOutboundImageMedia, resolveMediaBinary, type MediaBinary, } from './media-upload.js';
6
6
  export { registerLarkWebhookRoutes, handleLarkWebhookRequest, type LarkWebhookHandler, } from './webhook.js';
package/lib/index.js CHANGED
@@ -1,6 +1,6 @@
1
- export { formatInboundContent, formatOutboundBody, generateMessageId, headerValue, normalizeWebhookPath, readTextBody, resolveChatType, resolveLarkConfig, resolveSender, resolveTarget, verifySignature, } from './protocol.js';
1
+ export { formatInboundContent, formatOutboundBody, generateMessageId, headerValue, larkInboundConversation, normalizeWebhookPath, readTextBody, resolveChatType, resolveLarkConfig, resolveSender, verifySignature, } from './protocol.js';
2
2
  export { getLarkAgentDeps, registerLarkAgentEndpoint, setLarkAgentDeps, } from './lark-agent-deps.js';
3
- export { checkLarkPlatformPermit, larkGroupPermitResolver, normalizeLarkSenderForPermit, platformPermit, registerLarkPlatformPermitChecker, } from './platform-permit.js';
3
+ export { checkLarkPlatformPermit, larkGroupPermitResolver, normalizeLarkSenderForPermit, platformPermit, } from './platform-permit.js';
4
4
  export { LarkEndpoint, } from './endpoint.js';
5
5
  export { buildImageUploadForm, readOutboundImageMedia, resolveMediaBinary, } from './media-upload.js';
6
6
  export { registerLarkWebhookRoutes, handleLarkWebhookRequest, } from './webhook.js';
@@ -19,9 +19,9 @@ export interface LarkAgentEndpoint {
19
19
  removeChatManagers(chatId: string, userIds: string[]): Promise<boolean>;
20
20
  }
21
21
  export interface LarkAgentDeps {
22
- getEndpoint: (endpointId: string) => LarkAgentEndpoint;
22
+ getEndpoint: (endpointKey: string) => LarkAgentEndpoint;
23
23
  }
24
- export declare function registerLarkAgentEndpoint(endpointId: string, endpoint: LarkAgentEndpoint): () => void;
24
+ export declare function registerLarkAgentEndpoint(endpointKey: string, endpoint: LarkAgentEndpoint): () => void;
25
25
  /** Optional override used by tests / transitional callers. Pass `null` to clear. */
26
26
  export declare function setLarkAgentDeps(deps: LarkAgentDeps | null): void;
27
27
  export declare function getLarkAgentDeps(): LarkAgentDeps;
@@ -4,11 +4,11 @@
4
4
  */
5
5
  const endpoints = new Map();
6
6
  let override = null;
7
- export function registerLarkAgentEndpoint(endpointId, endpoint) {
8
- endpoints.set(endpointId, endpoint);
7
+ export function registerLarkAgentEndpoint(endpointKey, endpoint) {
8
+ endpoints.set(endpointKey, endpoint);
9
9
  return () => {
10
- if (endpoints.get(endpointId) === endpoint) {
11
- endpoints.delete(endpointId);
10
+ if (endpoints.get(endpointKey) === endpoint) {
11
+ endpoints.delete(endpointKey);
12
12
  }
13
13
  };
14
14
  }
@@ -20,10 +20,10 @@ export function getLarkAgentDeps() {
20
20
  if (override)
21
21
  return override;
22
22
  return {
23
- getEndpoint(endpointId) {
24
- const endpoint = endpoints.get(endpointId);
23
+ getEndpoint(endpointKey) {
24
+ const endpoint = endpoints.get(endpointKey);
25
25
  if (!endpoint)
26
- throw new Error(`Endpoint ${endpointId} 不存在`);
26
+ throw new Error(`Endpoint ${endpointKey} 不存在`);
27
27
  return endpoint;
28
28
  },
29
29
  };
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * 飞书 Lark platform permit
3
3
  */
4
- import { type Message } from '@zhin.js/core';
4
+ import type { PermissionSubject } from '@zhin.js/permission';
5
5
  export declare function platformPermit(perm: string): string;
6
6
  export declare function larkGroupPermitResolver(logicalPerm: string): string;
7
7
  export declare function normalizeLarkSenderForPermit(input: {
@@ -11,5 +11,4 @@ export declare function normalizeLarkSenderForPermit(input: {
11
11
  role?: string;
12
12
  permissions?: string[];
13
13
  };
14
- export declare function checkLarkPlatformPermit(perm: string, message: Message<any>): boolean;
15
- export declare function registerLarkPlatformPermitChecker(): () => void;
14
+ export declare function checkLarkPlatformPermit(perm: string, subject: PermissionSubject): boolean;
@@ -1,7 +1,3 @@
1
- /**
2
- * 飞书 Lark platform permit
3
- */
4
- import { registerPlatformPermitChecker } from '@zhin.js/core';
5
1
  const ADAPTER = 'lark';
6
2
  export function platformPermit(perm) {
7
3
  return `platform(${ADAPTER},${perm})`;
@@ -22,10 +18,9 @@ export function normalizeLarkSenderForPermit(input) {
22
18
  }
23
19
  return { role: 'member', permissions: [] };
24
20
  }
25
- export function checkLarkPlatformPermit(perm, message) {
26
- const sender = message.$sender;
27
- const permissions = sender.permissions ?? [];
28
- const role = sender.role;
21
+ export function checkLarkPlatformPermit(perm, subject) {
22
+ const role = subject.sender?.role?.[0];
23
+ const permissions = subject.sender?.permissions ?? [];
29
24
  const has = (t) => permissions.includes(t);
30
25
  switch (perm) {
31
26
  case 'chat_owner':
@@ -38,6 +33,3 @@ export function checkLarkPlatformPermit(perm, message) {
38
33
  return false;
39
34
  }
40
35
  }
41
- export function registerLarkPlatformPermitChecker() {
42
- return registerPlatformPermitChecker(ADAPTER, checkLarkPlatformPermit);
43
- }
package/lib/protocol.d.ts CHANGED
@@ -3,9 +3,10 @@
3
3
  * Canonicalization is owned by gateway/core before endpoint.send.
4
4
  */
5
5
  import type { IncomingMessage } from 'node:http';
6
+ import type { ConversationRef } from '@zhin.js/im-contract';
6
7
  /** Plugin Runtime owner config (`plugins.<instanceKey>` / schema.json). */
7
8
  export interface LarkAdapterConfig {
8
- readonly name?: string;
9
+ readonly id?: string;
9
10
  readonly appId?: string;
10
11
  readonly appSecret?: string;
11
12
  readonly encryptKey?: string;
@@ -20,7 +21,7 @@ export interface LarkAdapterConfig {
20
21
  }
21
22
  export interface ResolvedLarkConfig {
22
23
  readonly context: 'lark';
23
- readonly name: string;
24
+ readonly id: string;
24
25
  readonly appId: string;
25
26
  readonly appSecret: string;
26
27
  readonly encryptKey?: string;
@@ -96,7 +97,12 @@ export interface LarkSendBody {
96
97
  export declare function resolveLarkConfig(config?: LarkAdapterConfig): ResolvedLarkConfig;
97
98
  export declare function normalizeWebhookPath(path: string): string;
98
99
  export declare function resolveChatType(chatId?: string, chatType?: string): 'group' | 'private';
99
- export declare function resolveTarget(msg: LarkMessage): string;
100
+ /**
101
+ * 入站归一化 → ConversationRef:`chat_type` 为 'group' 的会话映射为 group,
102
+ * p2p 单聊映射为 private;id 一律为平台 chat_id。Lark 无 guild/频道容器,
103
+ * 不产生 `parent`。
104
+ */
105
+ export declare function larkInboundConversation(endpointKey: string, msg: LarkMessage): ConversationRef;
100
106
  export declare function resolveSender(msg: LarkMessage): string;
101
107
  export declare function generateMessageId(msg: LarkMessage): string;
102
108
  /** Build inbound text for MessageGateway.receive. */
package/lib/protocol.js CHANGED
@@ -14,8 +14,8 @@ export function resolveLarkConfig(config = {}) {
14
14
  throw new TypeError('Lark adapter requires appId + appSecret (plugins.<key> or endpoints with context: lark)');
15
15
  }
16
16
  const isFeishu = config.isFeishu ?? entry?.isFeishu ?? true;
17
- const name = (typeof config.name === 'string' && config.name)
18
- || (typeof entry?.name === 'string' && entry.name)
17
+ const id = (typeof config.id === 'string' && config.id)
18
+ || (typeof entry?.id === 'string' && entry.id)
19
19
  || process.env.LARK_BOT_NAME
20
20
  || 'lark-bot';
21
21
  const webhookPath = normalizeWebhookPath(config.webhookPath ?? entry?.webhookPath ?? '/lark/webhook');
@@ -27,7 +27,7 @@ export function resolveLarkConfig(config = {}) {
27
27
  const verificationToken = config.verificationToken ?? entry?.verificationToken;
28
28
  return {
29
29
  context: 'lark',
30
- name,
30
+ id,
31
31
  appId,
32
32
  appSecret,
33
33
  ...(encryptKey ? { encryptKey } : {}),
@@ -49,8 +49,17 @@ export function resolveChatType(chatId, chatType) {
49
49
  return 'private';
50
50
  return chatId?.startsWith('oc_') ? 'group' : 'private';
51
51
  }
52
- export function resolveTarget(msg) {
53
- return msg.chat_id || 'unknown';
52
+ /**
53
+ * 入站归一化 → ConversationRef:`chat_type` 为 'group' 的会话映射为 group,
54
+ * p2p 单聊映射为 private;id 一律为平台 chat_id。Lark 无 guild/频道容器,
55
+ * 不产生 `parent`。
56
+ */
57
+ export function larkInboundConversation(endpointKey, msg) {
58
+ return {
59
+ endpoint: { id: endpointKey, adapter: endpointKey.split('\0')[0] ?? endpointKey },
60
+ kind: resolveChatType(msg.chat_id, msg.chat_type),
61
+ id: msg.chat_id || 'unknown',
62
+ };
54
63
  }
55
64
  export function resolveSender(msg) {
56
65
  return msg.sender?.sender_id?.open_id
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zhin.js/adapter-lark",
3
- "version": "6.0.2",
3
+ "version": "6.0.5",
4
4
  "description": "Zhin.js Lark/Feishu (飞书) adapter for Plugin Runtime (HTTP webhook)",
5
5
  "type": "module",
6
6
  "main": "./lib/index.js",
@@ -34,21 +34,23 @@
34
34
  "directory": "plugins/adapters/lark"
35
35
  },
36
36
  "dependencies": {
37
- "@zhin.js/adapter": "1.1.4",
38
- "@zhin.js/command": "1.0.6",
39
- "@zhin.js/core": "1.5.1",
40
- "@zhin.js/host-http": "1.0.5",
41
- "@zhin.js/logger": "1.0.75",
42
- "@zhin.js/plugin-runtime": "1.1.2"
37
+ "@zhin.js/adapter": "1.1.7",
38
+ "@zhin.js/command": "1.0.9",
39
+ "@zhin.js/core": "1.5.4",
40
+ "@zhin.js/host-http": "1.0.8",
41
+ "@zhin.js/im-contract": "1.0.3",
42
+ "@zhin.js/logger": "1.0.76",
43
+ "@zhin.js/plugin-runtime": "1.1.5"
43
44
  },
44
45
  "peerDependencies": {
45
46
  "zod": "^4.0.0",
46
- "@zhin.js/adapter": "1.1.4",
47
- "@zhin.js/agent": "1.1.2",
48
- "@zhin.js/core": "1.5.1",
49
- "@zhin.js/host-http": "1.0.5",
50
- "@zhin.js/plugin-runtime": "1.1.2",
51
- "zhin.js": "6.0.1"
47
+ "@zhin.js/adapter": "1.1.7",
48
+ "@zhin.js/agent": "1.1.5",
49
+ "@zhin.js/core": "1.5.4",
50
+ "@zhin.js/host-http": "1.0.8",
51
+ "@zhin.js/permission": "1.0.1",
52
+ "@zhin.js/plugin-runtime": "1.1.5",
53
+ "zhin.js": "6.0.4"
52
54
  },
53
55
  "peerDependenciesMeta": {
54
56
  "zhin.js": {
@@ -66,8 +68,8 @@
66
68
  "typescript": "^6.0.3",
67
69
  "vitest": "^4.1.10",
68
70
  "zod": "^4.4.3",
69
- "@zhin.js/agent": "1.1.2",
70
- "zhin.js": "6.0.1"
71
+ "@zhin.js/agent": "1.1.5",
72
+ "zhin.js": "6.0.4"
71
73
  },
72
74
  "files": [
73
75
  "adapters",
package/plugin.js CHANGED
@@ -1,7 +1,8 @@
1
1
  // Generated by build-plugin-runtime-entries.mjs. Do not edit.
2
2
  import { createEndpointRuntimeState } from '@zhin.js/adapter';
3
3
  import { definePlugin } from '@zhin.js/plugin-runtime';
4
- import { registerLarkPlatformPermitChecker } from "./lib/platform-permit.js";
4
+ import { permissionHostToken } from '@zhin.js/permission';
5
+ import { checkLarkPlatformPermit } from "./lib/platform-permit.js";
5
6
  import { larkRuntimeStateToken } from "./lib/lark-runtime-state.js";
6
7
  export default definePlugin({
7
8
  name: 'lark',
@@ -9,8 +10,10 @@ export default definePlugin({
9
10
  displayName: 'Lark/Feishu (飞书) Adapter',
10
11
  },
11
12
  setup(context) {
12
- // 运行中 endpoint 注册表(lark.endpoint list 的"运行中"数据源)
13
13
  context.resources.provide(larkRuntimeStateToken, createEndpointRuntimeState());
14
- return registerLarkPlatformPermitChecker();
14
+ if (context.resources.has(permissionHostToken)) {
15
+ const host = context.resources.use(permissionHostToken);
16
+ return host.registerPlatform('lark', checkLarkPlatformPermit);
17
+ }
15
18
  },
16
19
  });
package/schema.json CHANGED
@@ -14,16 +14,48 @@
14
14
  "type": "boolean",
15
15
  "default": true
16
16
  },
17
+ "master": {
18
+ "type": [
19
+ "string",
20
+ "number"
21
+ ],
22
+ "description": "框架 master(Lark/Feishu open_id or user_id;AI/工具权限、endpoint 管理)。endpoints[i].master 可逐项覆盖"
23
+ },
24
+ "trusted": {
25
+ "type": "array",
26
+ "items": {
27
+ "type": [
28
+ "string",
29
+ "number"
30
+ ],
31
+ "description": "Trusted Lark/Feishu open_id or user_id"
32
+ },
33
+ "description": "框架 trusted 用户列表(弱于 master)。endpoints[i].trusted 可逐项追加"
34
+ },
17
35
  "endpoints": {
18
36
  "type": "array",
19
- "description": "多账号:一个插件实例挂多个 endpoint。每项与顶层字段同构(name 必填,其余覆盖顶层)",
37
+ "description": "多账号:一个插件实例挂多个 endpoint。每项与顶层字段同构(id 必填,其余覆盖顶层)",
20
38
  "items": {
21
39
  "type": "object",
22
40
  "additionalProperties": true,
23
41
  "properties": {
24
- "name": {
25
- "type": "string",
26
- "description": "Lark bot name"
42
+ "master": {
43
+ "type": [
44
+ "string",
45
+ "number"
46
+ ],
47
+ "description": "本 endpoint 的框架 master(Lark/Feishu open_id or user_id);覆盖顶层 master"
48
+ },
49
+ "trusted": {
50
+ "type": "array",
51
+ "items": {
52
+ "type": [
53
+ "string",
54
+ "number"
55
+ ],
56
+ "description": "Trusted Lark/Feishu open_id or user_id"
57
+ },
58
+ "description": "本 endpoint 的 trusted 列表"
27
59
  },
28
60
  "appId": {
29
61
  "type": "string",
@@ -40,10 +72,14 @@
40
72
  "verificationToken": {
41
73
  "type": "string",
42
74
  "description": "Lark verification token"
75
+ },
76
+ "id": {
77
+ "type": "string",
78
+ "description": "Lark bot name"
43
79
  }
44
80
  },
45
81
  "required": [
46
- "name",
82
+ "id",
47
83
  "appId",
48
84
  "appSecret"
49
85
  ]
package/src/endpoint.ts CHANGED
@@ -3,10 +3,10 @@
3
3
  */
4
4
  import { readFile } from 'node:fs/promises';
5
5
  import { basename } from 'node:path';
6
- import type { EndpointGroup, EndpointInstance, EndpointManagement } from '@zhin.js/adapter';
6
+ import type { EndpointGroup, EndpointInstance, EndpointManagement, EndpointSendRequest } from '@zhin.js/adapter';
7
7
  import type { MessageGateway } from '@zhin.js/core/runtime';
8
8
  import type { HttpHost, HttpRouteRegistration } from '@zhin.js/host-http';
9
- import { formatCompact, getLogger } from '@zhin.js/logger';
9
+ import { formatCompact, getAdapterLogger } from '@zhin.js/logger';
10
10
  import type { CapabilityId } from '@zhin.js/plugin-runtime';
11
11
  import { registerLarkAgentEndpoint } from './lark-agent-deps.js';
12
12
  import {
@@ -18,9 +18,9 @@ import {
18
18
  formatInboundContent,
19
19
  formatOutboundBody,
20
20
  generateMessageId,
21
+ larkInboundConversation,
21
22
  resolveChatType,
22
23
  resolveSender,
23
- resolveTarget,
24
24
  type AccessToken,
25
25
  type LarkApiResponse,
26
26
  type LarkMessage,
@@ -28,8 +28,6 @@ import {
28
28
  } from './protocol.js';
29
29
  import { registerLarkWebhookRoutes } from './webhook.js';
30
30
 
31
- const logger = getLogger('lark');
32
-
33
31
  /** 出站 HTTP 调用统一 30s 超时。 */
34
32
  const OUTBOUND_TIMEOUT_MS = 30_000;
35
33
 
@@ -57,6 +55,8 @@ export interface LarkEndpointOptions {
57
55
  }
58
56
 
59
57
  export class LarkEndpoint implements EndpointInstance {
58
+ readonly #logger!: ReturnType<typeof getAdapterLogger>;
59
+
60
60
  readonly #options: LarkEndpointOptions;
61
61
  readonly #fetch: LarkFetch;
62
62
  /**
@@ -93,6 +93,7 @@ export class LarkEndpoint implements EndpointInstance {
93
93
  #unregisterAgent?: () => void;
94
94
 
95
95
  constructor(options: LarkEndpointOptions) {
96
+ this.#logger = getAdapterLogger('lark', options.config.id);
96
97
  this.#options = options;
97
98
  this.#fetch = options.fetch ?? globalThis.fetch;
98
99
  }
@@ -112,24 +113,24 @@ export class LarkEndpoint implements EndpointInstance {
112
113
  try {
113
114
  if (!this.#options.config.encryptKey && !this.#options.config.verificationToken) {
114
115
  // encryptKey / verificationToken 都未配置时 webhook 完全无鉴权,任何人可伪造事件
115
- logger.warn(formatCompact({
116
- endpoint: this.#options.config.name,
116
+ this.#logger.warn(formatCompact({
117
+ endpoint: this.#options.config.id,
117
118
  op: 'webhook',
118
119
  ok: false,
119
120
  error: 'neither encryptKey nor verificationToken configured; webhook is unauthenticated',
120
121
  }));
121
122
  }
122
123
  await this.#refreshAccessToken();
123
- this.#unregisterAgent = registerLarkAgentEndpoint(this.#options.config.name, this);
124
+ this.#unregisterAgent = registerLarkAgentEndpoint(this.#options.config.id, this);
124
125
  this.#routeReleases.push(...registerLarkWebhookRoutes(this.#options.http, this));
125
- logger.debug(formatCompact({
126
- endpoint: this.#options.config.name,
126
+ this.#logger.debug(formatCompact({
127
+ endpoint: this.#options.config.id,
127
128
  op: 'webhook',
128
129
  path: this.#options.config.webhookPath,
129
130
  }));
130
131
  } catch (error) {
131
132
  await this.stop();
132
- logger.error('Failed to connect Lark endpoint:', error);
133
+ this.#logger.error('Failed to connect Lark endpoint:', error);
133
134
  throw error;
134
135
  }
135
136
  }
@@ -148,17 +149,17 @@ export class LarkEndpoint implements EndpointInstance {
148
149
  this.#unregisterAgent?.();
149
150
  this.#unregisterAgent = undefined;
150
151
  this.#started = false;
151
- logger.debug(formatCompact({ op: 'disconnect', endpoint: this.#options.config.name }));
152
+ this.#logger.debug(formatCompact({ op: 'disconnect' }));
152
153
  }
153
154
 
154
- async send({ target, payload }: { readonly target: string; readonly payload: unknown }): Promise<string> {
155
+ async send({ conversation, payload }: EndpointSendRequest): Promise<string> {
155
156
  const materialized = await this.#materializeOutboundMedia(payload);
156
157
  const content = formatOutboundBody(materialized);
157
158
  const data = await this.#request('/im/v1/messages', {
158
159
  method: 'POST',
159
160
  params: { receive_id_type: 'chat_id' },
160
161
  body: {
161
- receive_id: target,
162
+ receive_id: conversation.id,
162
163
  msg_type: content.msg_type,
163
164
  content: content.content,
164
165
  },
@@ -167,10 +168,7 @@ export class LarkEndpoint implements EndpointInstance {
167
168
  throw new Error(`Failed to send message: ${data.msg}`);
168
169
  }
169
170
  const messageId = (data.data?.message_id as string) || `${Date.now()}`;
170
- logger.debug(formatCompact({
171
- op: 'send',
172
- endpoint: this.#options.config.name,
173
- to: target,
171
+ this.#logger.debug(formatCompact({ op: 'send', to: conversation.id,
174
172
  id: messageId,
175
173
  }));
176
174
  return messageId;
@@ -226,7 +224,7 @@ export class LarkEndpoint implements EndpointInstance {
226
224
  }
227
225
  throw new Error(`Image upload failed: ${data.msg}`);
228
226
  } catch (error) {
229
- logger.error('Failed to upload image:', error);
227
+ this.#logger.error('Failed to upload image:', error);
230
228
  return null;
231
229
  }
232
230
  }
@@ -234,22 +232,21 @@ export class LarkEndpoint implements EndpointInstance {
234
232
  /** Test / internal: admit a parsed message when open (non-webhook path). */
235
233
  admit(msg: LarkMessage): void {
236
234
  if (!this.#open) return;
237
- const target = resolveTarget(msg);
235
+ const conversation = larkInboundConversation(String(this.#options.id), msg);
238
236
  void this.#options.gateway.receive({
239
- adapter: this.#options.id,
240
- target,
237
+ conversation,
238
+ message: { conversation, id: generateMessageId(msg) },
241
239
  content: formatInboundContent(msg),
242
- sender: resolveSender(msg),
243
- id: generateMessageId(msg),
240
+ sender: { id: resolveSender(msg) },
241
+ endpointId: this.#options.config.id,
244
242
  metadata: Object.freeze({
245
243
  messageType: msg.message_type,
246
244
  chatType: resolveChatType(msg.chat_id, msg.chat_type),
247
- endpoint: this.#options.config.name,
248
245
  }),
249
246
  }).catch((err) => {
250
- logger.warn(formatCompact({
247
+ this.#logger.warn(formatCompact({
251
248
  op: 'lark_gateway_receive_failed',
252
- target,
249
+ conversation: conversation.id,
253
250
  error: err instanceof Error ? err.message : String(err),
254
251
  }));
255
252
  });
@@ -266,7 +263,7 @@ export class LarkEndpoint implements EndpointInstance {
266
263
  });
267
264
  return data.data?.user ?? null;
268
265
  } catch (error) {
269
- logger.error('Failed to get user info:', error);
266
+ this.#logger.error('Failed to get user info:', error);
270
267
  return null;
271
268
  }
272
269
  }
@@ -276,7 +273,7 @@ export class LarkEndpoint implements EndpointInstance {
276
273
  const data = await this.#request(`/im/v1/chats/${chatId}`, { method: 'GET' });
277
274
  return data.data ?? null;
278
275
  } catch (error) {
279
- logger.error('Failed to get chat info:', error);
276
+ this.#logger.error('Failed to get chat info:', error);
280
277
  return null;
281
278
  }
282
279
  }
@@ -303,7 +300,7 @@ export class LarkEndpoint implements EndpointInstance {
303
300
  }
304
301
  throw new Error(`Upload failed: ${data.msg}`);
305
302
  } catch (error) {
306
- logger.error('Failed to upload file:', error);
303
+ this.#logger.error('Failed to upload file:', error);
307
304
  return null;
308
305
  }
309
306
  }
@@ -325,7 +322,7 @@ export class LarkEndpoint implements EndpointInstance {
325
322
  if (data.code === 0) return (data.data?.chat_id as string) || null;
326
323
  throw new Error(`Failed to create chat: ${data.msg}`);
327
324
  } catch (error) {
328
- logger.error('Failed to create chat:', error);
325
+ this.#logger.error('Failed to create chat:', error);
329
326
  return null;
330
327
  }
331
328
  }
@@ -342,7 +339,7 @@ export class LarkEndpoint implements EndpointInstance {
342
339
  if (data.code === 0) return true;
343
340
  throw new Error(`Failed to update chat: ${data.msg}`);
344
341
  } catch (error) {
345
- logger.error('Failed to update chat:', error);
342
+ this.#logger.error('Failed to update chat:', error);
346
343
  return false;
347
344
  }
348
345
  }
@@ -356,7 +353,7 @@ export class LarkEndpoint implements EndpointInstance {
356
353
  if (data.code === 0) return true;
357
354
  throw new Error(`Failed to add members: ${data.msg}`);
358
355
  } catch (error) {
359
- logger.error('Failed to add chat members:', error);
356
+ this.#logger.error('Failed to add chat members:', error);
360
357
  return false;
361
358
  }
362
359
  }
@@ -370,7 +367,7 @@ export class LarkEndpoint implements EndpointInstance {
370
367
  if (data.code === 0) return true;
371
368
  throw new Error(`Failed to remove members: ${data.msg}`);
372
369
  } catch (error) {
373
- logger.error('Failed to remove chat members:', error);
370
+ this.#logger.error('Failed to remove chat members:', error);
374
371
  return false;
375
372
  }
376
373
  }
@@ -381,7 +378,7 @@ export class LarkEndpoint implements EndpointInstance {
381
378
  if (data.code === 0) return (data.data?.items as unknown[]) || [];
382
379
  throw new Error(`Failed to get members: ${data.msg}`);
383
380
  } catch (error) {
384
- logger.error('Failed to get chat members:', error);
381
+ this.#logger.error('Failed to get chat members:', error);
385
382
  return [];
386
383
  }
387
384
  }
@@ -392,7 +389,7 @@ export class LarkEndpoint implements EndpointInstance {
392
389
  if (data.code === 0) return true;
393
390
  throw new Error(`Failed to dissolve chat: ${data.msg}`);
394
391
  } catch (error) {
395
- logger.error('Failed to dissolve chat:', error);
392
+ this.#logger.error('Failed to dissolve chat:', error);
396
393
  return false;
397
394
  }
398
395
  }
@@ -406,7 +403,7 @@ export class LarkEndpoint implements EndpointInstance {
406
403
  if (data.code === 0) return true;
407
404
  throw new Error(`Failed to set managers: ${data.msg}`);
408
405
  } catch (error) {
409
- logger.error('Failed to set chat managers:', error);
406
+ this.#logger.error('Failed to set chat managers:', error);
410
407
  return false;
411
408
  }
412
409
  }
@@ -420,7 +417,7 @@ export class LarkEndpoint implements EndpointInstance {
420
417
  if (data.code === 0) return true;
421
418
  throw new Error(`Failed to remove managers: ${data.msg}`);
422
419
  } catch (error) {
423
- logger.error('Failed to remove chat managers:', error);
420
+ this.#logger.error('Failed to remove chat managers:', error);
424
421
  return false;
425
422
  }
426
423
  }
@@ -525,7 +522,7 @@ export class LarkEndpoint implements EndpointInstance {
525
522
  expires_in: data.expire ?? 7200,
526
523
  timestamp: Date.now(),
527
524
  };
528
- logger.debug('Access token refreshed successfully');
525
+ this.#logger.debug('Access token refreshed successfully');
529
526
  return;
530
527
  }
531
528
  throw new Error(`Failed to get access token: ${data.msg} (${data.code})`);
package/src/index.ts CHANGED
@@ -3,12 +3,12 @@ export {
3
3
  formatOutboundBody,
4
4
  generateMessageId,
5
5
  headerValue,
6
+ larkInboundConversation,
6
7
  normalizeWebhookPath,
7
8
  readTextBody,
8
9
  resolveChatType,
9
10
  resolveLarkConfig,
10
11
  resolveSender,
11
- resolveTarget,
12
12
  verifySignature,
13
13
  type AccessToken,
14
14
  type LarkAdapterConfig,
@@ -33,7 +33,6 @@ export {
33
33
  larkGroupPermitResolver,
34
34
  normalizeLarkSenderForPermit,
35
35
  platformPermit,
36
- registerLarkPlatformPermitChecker,
37
36
  } from './platform-permit.js';
38
37
 
39
38
  export {
@@ -27,20 +27,20 @@ export interface LarkAgentEndpoint {
27
27
  }
28
28
 
29
29
  export interface LarkAgentDeps {
30
- getEndpoint: (endpointId: string) => LarkAgentEndpoint;
30
+ getEndpoint: (endpointKey: string) => LarkAgentEndpoint;
31
31
  }
32
32
 
33
33
  const endpoints = new Map<string, LarkAgentEndpoint>();
34
34
  let override: LarkAgentDeps | null = null;
35
35
 
36
36
  export function registerLarkAgentEndpoint(
37
- endpointId: string,
37
+ endpointKey: string,
38
38
  endpoint: LarkAgentEndpoint,
39
39
  ): () => void {
40
- endpoints.set(endpointId, endpoint);
40
+ endpoints.set(endpointKey, endpoint);
41
41
  return () => {
42
- if (endpoints.get(endpointId) === endpoint) {
43
- endpoints.delete(endpointId);
42
+ if (endpoints.get(endpointKey) === endpoint) {
43
+ endpoints.delete(endpointKey);
44
44
  }
45
45
  };
46
46
  }
@@ -53,9 +53,9 @@ export function setLarkAgentDeps(deps: LarkAgentDeps | null): void {
53
53
  export function getLarkAgentDeps(): LarkAgentDeps {
54
54
  if (override) return override;
55
55
  return {
56
- getEndpoint(endpointId) {
57
- const endpoint = endpoints.get(endpointId);
58
- if (!endpoint) throw new Error(`Endpoint ${endpointId} 不存在`);
56
+ getEndpoint(endpointKey) {
57
+ const endpoint = endpoints.get(endpointKey);
58
+ if (!endpoint) throw new Error(`Endpoint ${endpointKey} 不存在`);
59
59
  return endpoint;
60
60
  },
61
61
  };
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * 飞书 Lark platform permit
3
3
  */
4
- import { registerPlatformPermitChecker, type Message } from '@zhin.js/core';
4
+ import type { PermissionSubject } from '@zhin.js/permission';
5
5
 
6
6
  const ADAPTER = 'lark';
7
7
 
@@ -31,10 +31,9 @@ export function normalizeLarkSenderForPermit(input: {
31
31
  return { role: 'member', permissions: [] };
32
32
  }
33
33
 
34
- export function checkLarkPlatformPermit(perm: string, message: Message<any>): boolean {
35
- const sender = message.$sender as { role?: string; permissions?: string[] };
36
- const permissions = sender.permissions ?? [];
37
- const role = sender.role;
34
+ export function checkLarkPlatformPermit(perm: string, subject: PermissionSubject): boolean {
35
+ const role = subject.sender?.role?.[0];
36
+ const permissions = subject.sender?.permissions ?? [];
38
37
  const has = (t: string) => permissions.includes(t);
39
38
 
40
39
  switch (perm) {
@@ -49,6 +48,3 @@ export function checkLarkPlatformPermit(perm: string, message: Message<any>): bo
49
48
  }
50
49
  }
51
50
 
52
- export function registerLarkPlatformPermitChecker(): () => void {
53
- return registerPlatformPermitChecker(ADAPTER, checkLarkPlatformPermit);
54
- }
package/src/protocol.ts CHANGED
@@ -6,13 +6,14 @@
6
6
  import { createHash, timingSafeEqual } from 'node:crypto';
7
7
  import type { IncomingMessage } from 'node:http';
8
8
  import { isMediaRef } from '@zhin.js/core';
9
+ import type { ConversationRef } from '@zhin.js/im-contract';
9
10
  import { formatCompact, getLogger } from '@zhin.js/logger';
10
11
 
11
12
  const logger = getLogger('lark');
12
13
 
13
14
  /** Plugin Runtime owner config (`plugins.<instanceKey>` / schema.json). */
14
15
  export interface LarkAdapterConfig {
15
- readonly name?: string;
16
+ readonly id?: string;
16
17
  readonly appId?: string;
17
18
  readonly appSecret?: string;
18
19
  readonly encryptKey?: string;
@@ -28,7 +29,7 @@ export interface LarkAdapterConfig {
28
29
 
29
30
  export interface ResolvedLarkConfig {
30
31
  readonly context: 'lark';
31
- readonly name: string;
32
+ readonly id: string;
32
33
  readonly appId: string;
33
34
  readonly appSecret: string;
34
35
  readonly encryptKey?: string;
@@ -118,8 +119,8 @@ export function resolveLarkConfig(config: LarkAdapterConfig = {}): ResolvedLarkC
118
119
  );
119
120
  }
120
121
  const isFeishu = config.isFeishu ?? entry?.isFeishu ?? true;
121
- const name = (typeof config.name === 'string' && config.name)
122
- || (typeof entry?.name === 'string' && entry.name)
122
+ const id = (typeof config.id === 'string' && config.id)
123
+ || (typeof entry?.id === 'string' && entry.id)
123
124
  || process.env.LARK_BOT_NAME
124
125
  || 'lark-bot';
125
126
  const webhookPath = normalizeWebhookPath(
@@ -135,7 +136,7 @@ export function resolveLarkConfig(config: LarkAdapterConfig = {}): ResolvedLarkC
135
136
  const verificationToken = config.verificationToken ?? entry?.verificationToken;
136
137
  return {
137
138
  context: 'lark',
138
- name,
139
+ id,
139
140
  appId,
140
141
  appSecret,
141
142
  ...(encryptKey ? { encryptKey } : {}),
@@ -158,8 +159,17 @@ export function resolveChatType(chatId?: string, chatType?: string): 'group' | '
158
159
  return chatId?.startsWith('oc_') ? 'group' : 'private';
159
160
  }
160
161
 
161
- export function resolveTarget(msg: LarkMessage): string {
162
- return msg.chat_id || 'unknown';
162
+ /**
163
+ * 入站归一化 → ConversationRef:`chat_type` 为 'group' 的会话映射为 group,
164
+ * p2p 单聊映射为 private;id 一律为平台 chat_id。Lark 无 guild/频道容器,
165
+ * 不产生 `parent`。
166
+ */
167
+ export function larkInboundConversation(endpointKey: string, msg: LarkMessage): ConversationRef {
168
+ return {
169
+ endpoint: { id: endpointKey, adapter: endpointKey.split('\0')[0] ?? endpointKey },
170
+ kind: resolveChatType(msg.chat_id, msg.chat_type),
171
+ id: msg.chat_id || 'unknown',
172
+ };
163
173
  }
164
174
 
165
175
  export function resolveSender(msg: LarkMessage): string {