@zhin.js/adapter-lark 6.0.3 → 6.0.6

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,95 @@
1
1
  # @zhin.js/adapter-lark
2
2
 
3
+ ## 6.0.6
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [2d0a622]
8
+ - @zhin.js/command@1.0.10
9
+ - @zhin.js/adapter@1.1.7
10
+ - @zhin.js/core@1.5.5
11
+ - @zhin.js/agent@1.1.6
12
+ - zhin.js@6.0.5
13
+
14
+ ## 6.0.5
15
+
16
+ ### Patch Changes
17
+
18
+ - 51015d6: refactor(adapters): migrate platform-permit to PermissionSubject
19
+
20
+ All adapter platform-permit checkers now accept PermissionSubject
21
+ (duck-typed from Message/CommandSession) instead of Message directly.
22
+ registerPlatformPermitChecker is replaced by host.registerPlatform in
23
+ the adapter plugin setup. activity-feedback service updated for new
24
+ PermissionHost integration.
25
+
26
+ - Updated dependencies [6f9c366]
27
+ - Updated dependencies [373a56b]
28
+ - Updated dependencies [0de46a8]
29
+ - Updated dependencies [c106ecc]
30
+ - Updated dependencies [ca92e03]
31
+ - Updated dependencies [b0f37ae]
32
+ - Updated dependencies [ba08a2f]
33
+ - Updated dependencies [b08f7fe]
34
+ - Updated dependencies [daffd4c]
35
+ - Updated dependencies [36c7400]
36
+ - Updated dependencies [a9fa72e]
37
+ - Updated dependencies [c8de3ef]
38
+ - Updated dependencies [60f0fc8]
39
+ - Updated dependencies [574c990]
40
+ - Updated dependencies [d096f16]
41
+ - Updated dependencies [3f29623]
42
+ - Updated dependencies [2916852]
43
+ - Updated dependencies [d047869]
44
+ - Updated dependencies [162fa34]
45
+ - Updated dependencies [e62561e]
46
+ - Updated dependencies [3eeeb46]
47
+ - Updated dependencies [85d0f82]
48
+ - Updated dependencies [61bfc1c]
49
+ - Updated dependencies [04bad47]
50
+ - Updated dependencies [e40b048]
51
+ - Updated dependencies [5a5b1bb]
52
+ - Updated dependencies [f1708c3]
53
+ - Updated dependencies [d254a81]
54
+ - Updated dependencies [7123c47]
55
+ - Updated dependencies [05befc1]
56
+ - Updated dependencies [0663b6a]
57
+ - Updated dependencies [f28f9b3]
58
+ - Updated dependencies [9f340f7]
59
+ - Updated dependencies [e53444f]
60
+ - Updated dependencies [5eedd26]
61
+ - Updated dependencies [92b0dd7]
62
+ - Updated dependencies [f919b6f]
63
+ - Updated dependencies [098e411]
64
+ - Updated dependencies [e1b7c01]
65
+ - Updated dependencies [9b94f87]
66
+ - Updated dependencies [a7df753]
67
+ - @zhin.js/agent@1.1.5
68
+ - @zhin.js/host-http@1.0.8
69
+ - @zhin.js/permission@1.0.1
70
+ - @zhin.js/im-contract@1.0.3
71
+ - @zhin.js/adapter@1.1.7
72
+ - @zhin.js/plugin-runtime@1.1.5
73
+ - @zhin.js/command@1.0.9
74
+ - @zhin.js/core@1.5.4
75
+ - zhin.js@6.0.4
76
+
77
+ ## 6.0.4
78
+
79
+ ### Patch Changes
80
+
81
+ - f8c7a54: fix: im
82
+ - Updated dependencies [f8c7a54]
83
+ - @zhin.js/logger@1.0.76
84
+ - @zhin.js/host-http@1.0.7
85
+ - @zhin.js/adapter@1.1.6
86
+ - @zhin.js/agent@1.1.4
87
+ - @zhin.js/command@1.0.8
88
+ - @zhin.js/core@1.5.3
89
+ - @zhin.js/im-contract@1.0.2
90
+ - @zhin.js/plugin-runtime@1.1.4
91
+ - zhin.js@6.0.3
92
+
3
93
  ## 6.0.3
4
94
 
5
95
  ### Patch Changes
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.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
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,7 +98,7 @@ 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
103
  async send({ conversation, payload }) {
103
104
  const materialized = await this.#materializeOutboundMedia(payload);
@@ -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: conversation.id,
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
  }
@@ -190,14 +188,14 @@ export class LarkEndpoint {
190
188
  conversation,
191
189
  message: { conversation, id: generateMessageId(msg) },
192
190
  content: formatInboundContent(msg),
193
- sender: resolveSender(msg),
191
+ sender: { id: resolveSender(msg) },
192
+ endpointId: this.#options.config.id,
194
193
  metadata: Object.freeze({
195
194
  messageType: msg.message_type,
196
195
  chatType: resolveChatType(msg.chat_id, msg.chat_type),
197
- endpoint: this.#options.config.name,
198
196
  }),
199
197
  }).catch((err) => {
200
- logger.warn(formatCompact({
198
+ this.#logger.warn(formatCompact({
201
199
  op: 'lark_gateway_receive_failed',
202
200
  conversation: conversation.id,
203
201
  error: err instanceof Error ? err.message : String(err),
@@ -213,7 +211,7 @@ export class LarkEndpoint {
213
211
  return data.data?.user ?? null;
214
212
  }
215
213
  catch (error) {
216
- logger.error('Failed to get user info:', error);
214
+ this.#logger.error('Failed to get user info:', error);
217
215
  return null;
218
216
  }
219
217
  }
@@ -223,7 +221,7 @@ export class LarkEndpoint {
223
221
  return data.data ?? null;
224
222
  }
225
223
  catch (error) {
226
- logger.error('Failed to get chat info:', error);
224
+ this.#logger.error('Failed to get chat info:', error);
227
225
  return null;
228
226
  }
229
227
  }
@@ -247,7 +245,7 @@ export class LarkEndpoint {
247
245
  throw new Error(`Upload failed: ${data.msg}`);
248
246
  }
249
247
  catch (error) {
250
- logger.error('Failed to upload file:', error);
248
+ this.#logger.error('Failed to upload file:', error);
251
249
  return null;
252
250
  }
253
251
  }
@@ -266,7 +264,7 @@ export class LarkEndpoint {
266
264
  throw new Error(`Failed to create chat: ${data.msg}`);
267
265
  }
268
266
  catch (error) {
269
- logger.error('Failed to create chat:', error);
267
+ this.#logger.error('Failed to create chat:', error);
270
268
  return null;
271
269
  }
272
270
  }
@@ -281,7 +279,7 @@ export class LarkEndpoint {
281
279
  throw new Error(`Failed to update chat: ${data.msg}`);
282
280
  }
283
281
  catch (error) {
284
- logger.error('Failed to update chat:', error);
282
+ this.#logger.error('Failed to update chat:', error);
285
283
  return false;
286
284
  }
287
285
  }
@@ -296,7 +294,7 @@ export class LarkEndpoint {
296
294
  throw new Error(`Failed to add members: ${data.msg}`);
297
295
  }
298
296
  catch (error) {
299
- logger.error('Failed to add chat members:', error);
297
+ this.#logger.error('Failed to add chat members:', error);
300
298
  return false;
301
299
  }
302
300
  }
@@ -311,7 +309,7 @@ export class LarkEndpoint {
311
309
  throw new Error(`Failed to remove members: ${data.msg}`);
312
310
  }
313
311
  catch (error) {
314
- logger.error('Failed to remove chat members:', error);
312
+ this.#logger.error('Failed to remove chat members:', error);
315
313
  return false;
316
314
  }
317
315
  }
@@ -323,7 +321,7 @@ export class LarkEndpoint {
323
321
  throw new Error(`Failed to get members: ${data.msg}`);
324
322
  }
325
323
  catch (error) {
326
- logger.error('Failed to get chat members:', error);
324
+ this.#logger.error('Failed to get chat members:', error);
327
325
  return [];
328
326
  }
329
327
  }
@@ -335,7 +333,7 @@ export class LarkEndpoint {
335
333
  throw new Error(`Failed to dissolve chat: ${data.msg}`);
336
334
  }
337
335
  catch (error) {
338
- logger.error('Failed to dissolve chat:', error);
336
+ this.#logger.error('Failed to dissolve chat:', error);
339
337
  return false;
340
338
  }
341
339
  }
@@ -350,7 +348,7 @@ export class LarkEndpoint {
350
348
  throw new Error(`Failed to set managers: ${data.msg}`);
351
349
  }
352
350
  catch (error) {
353
- logger.error('Failed to set chat managers:', error);
351
+ this.#logger.error('Failed to set chat managers:', error);
354
352
  return false;
355
353
  }
356
354
  }
@@ -365,7 +363,7 @@ export class LarkEndpoint {
365
363
  throw new Error(`Failed to remove managers: ${data.msg}`);
366
364
  }
367
365
  catch (error) {
368
- logger.error('Failed to remove chat managers:', error);
366
+ this.#logger.error('Failed to remove chat managers:', error);
369
367
  return false;
370
368
  }
371
369
  }
@@ -450,7 +448,7 @@ export class LarkEndpoint {
450
448
  expires_in: data.expire ?? 7200,
451
449
  timestamp: Date.now(),
452
450
  };
453
- logger.debug('Access token refreshed successfully');
451
+ this.#logger.debug('Access token refreshed successfully');
454
452
  return;
455
453
  }
456
454
  throw new Error(`Failed to get access token: ${data.msg} (${data.code})`);
package/lib/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
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
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
@@ -6,7 +6,7 @@ import type { IncomingMessage } from 'node:http';
6
6
  import type { ConversationRef } from '@zhin.js/im-contract';
7
7
  /** Plugin Runtime owner config (`plugins.<instanceKey>` / schema.json). */
8
8
  export interface LarkAdapterConfig {
9
- readonly name?: string;
9
+ readonly id?: string;
10
10
  readonly appId?: string;
11
11
  readonly appSecret?: string;
12
12
  readonly encryptKey?: string;
@@ -21,7 +21,7 @@ export interface LarkAdapterConfig {
21
21
  }
22
22
  export interface ResolvedLarkConfig {
23
23
  readonly context: 'lark';
24
- readonly name: string;
24
+ readonly id: string;
25
25
  readonly appId: string;
26
26
  readonly appSecret: string;
27
27
  readonly encryptKey?: string;
@@ -102,7 +102,7 @@ export declare function resolveChatType(chatId?: string, chatType?: string): 'gr
102
102
  * p2p 单聊映射为 private;id 一律为平台 chat_id。Lark 无 guild/频道容器,
103
103
  * 不产生 `parent`。
104
104
  */
105
- export declare function larkInboundConversation(endpointId: string, msg: LarkMessage): ConversationRef;
105
+ export declare function larkInboundConversation(endpointKey: string, msg: LarkMessage): ConversationRef;
106
106
  export declare function resolveSender(msg: LarkMessage): string;
107
107
  export declare function generateMessageId(msg: LarkMessage): string;
108
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 } : {}),
@@ -54,9 +54,9 @@ export function resolveChatType(chatId, chatType) {
54
54
  * p2p 单聊映射为 private;id 一律为平台 chat_id。Lark 无 guild/频道容器,
55
55
  * 不产生 `parent`。
56
56
  */
57
- export function larkInboundConversation(endpointId, msg) {
57
+ export function larkInboundConversation(endpointKey, msg) {
58
58
  return {
59
- endpoint: { id: endpointId, adapter: endpointId.split('\0')[0] ?? endpointId },
59
+ endpoint: { id: endpointKey, adapter: endpointKey.split('\0')[0] ?? endpointKey },
60
60
  kind: resolveChatType(msg.chat_id, msg.chat_type),
61
61
  id: msg.chat_id || 'unknown',
62
62
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zhin.js/adapter-lark",
3
- "version": "6.0.3",
3
+ "version": "6.0.6",
4
4
  "description": "Zhin.js Lark/Feishu (飞书) adapter for Plugin Runtime (HTTP webhook)",
5
5
  "type": "module",
6
6
  "main": "./lib/index.js",
@@ -34,22 +34,23 @@
34
34
  "directory": "plugins/adapters/lark"
35
35
  },
36
36
  "dependencies": {
37
- "@zhin.js/adapter": "1.1.5",
38
- "@zhin.js/command": "1.0.7",
39
- "@zhin.js/core": "1.5.2",
40
- "@zhin.js/host-http": "1.0.6",
41
- "@zhin.js/im-contract": "1.0.1",
42
- "@zhin.js/logger": "1.0.75",
43
- "@zhin.js/plugin-runtime": "1.1.3"
37
+ "@zhin.js/adapter": "1.1.7",
38
+ "@zhin.js/command": "1.0.10",
39
+ "@zhin.js/core": "1.5.5",
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"
44
44
  },
45
45
  "peerDependencies": {
46
46
  "zod": "^4.0.0",
47
- "@zhin.js/adapter": "1.1.5",
48
- "@zhin.js/agent": "1.1.3",
49
- "@zhin.js/core": "1.5.2",
50
- "@zhin.js/host-http": "1.0.6",
51
- "@zhin.js/plugin-runtime": "1.1.3",
52
- "zhin.js": "6.0.2"
47
+ "@zhin.js/adapter": "1.1.7",
48
+ "@zhin.js/agent": "1.1.6",
49
+ "@zhin.js/core": "1.5.5",
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.5"
53
54
  },
54
55
  "peerDependenciesMeta": {
55
56
  "zhin.js": {
@@ -67,8 +68,8 @@
67
68
  "typescript": "^6.0.3",
68
69
  "vitest": "^4.1.10",
69
70
  "zod": "^4.4.3",
70
- "@zhin.js/agent": "1.1.3",
71
- "zhin.js": "6.0.2"
71
+ "@zhin.js/agent": "1.1.6",
72
+ "zhin.js": "6.0.5"
72
73
  },
73
74
  "files": [
74
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
@@ -6,7 +6,7 @@ import { basename } from 'node:path';
6
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 {
@@ -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,7 +149,7 @@ 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
155
  async send({ conversation, payload }: EndpointSendRequest): Promise<string> {
@@ -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: conversation.id,
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
  }
@@ -239,14 +237,14 @@ export class LarkEndpoint implements EndpointInstance {
239
237
  conversation,
240
238
  message: { conversation, id: generateMessageId(msg) },
241
239
  content: formatInboundContent(msg),
242
- sender: resolveSender(msg),
240
+ sender: { id: resolveSender(msg) },
241
+ endpointId: this.#options.config.id,
243
242
  metadata: Object.freeze({
244
243
  messageType: msg.message_type,
245
244
  chatType: resolveChatType(msg.chat_id, msg.chat_type),
246
- endpoint: this.#options.config.name,
247
245
  }),
248
246
  }).catch((err) => {
249
- logger.warn(formatCompact({
247
+ this.#logger.warn(formatCompact({
250
248
  op: 'lark_gateway_receive_failed',
251
249
  conversation: conversation.id,
252
250
  error: err instanceof Error ? err.message : String(err),
@@ -265,7 +263,7 @@ export class LarkEndpoint implements EndpointInstance {
265
263
  });
266
264
  return data.data?.user ?? null;
267
265
  } catch (error) {
268
- logger.error('Failed to get user info:', error);
266
+ this.#logger.error('Failed to get user info:', error);
269
267
  return null;
270
268
  }
271
269
  }
@@ -275,7 +273,7 @@ export class LarkEndpoint implements EndpointInstance {
275
273
  const data = await this.#request(`/im/v1/chats/${chatId}`, { method: 'GET' });
276
274
  return data.data ?? null;
277
275
  } catch (error) {
278
- logger.error('Failed to get chat info:', error);
276
+ this.#logger.error('Failed to get chat info:', error);
279
277
  return null;
280
278
  }
281
279
  }
@@ -302,7 +300,7 @@ export class LarkEndpoint implements EndpointInstance {
302
300
  }
303
301
  throw new Error(`Upload failed: ${data.msg}`);
304
302
  } catch (error) {
305
- logger.error('Failed to upload file:', error);
303
+ this.#logger.error('Failed to upload file:', error);
306
304
  return null;
307
305
  }
308
306
  }
@@ -324,7 +322,7 @@ export class LarkEndpoint implements EndpointInstance {
324
322
  if (data.code === 0) return (data.data?.chat_id as string) || null;
325
323
  throw new Error(`Failed to create chat: ${data.msg}`);
326
324
  } catch (error) {
327
- logger.error('Failed to create chat:', error);
325
+ this.#logger.error('Failed to create chat:', error);
328
326
  return null;
329
327
  }
330
328
  }
@@ -341,7 +339,7 @@ export class LarkEndpoint implements EndpointInstance {
341
339
  if (data.code === 0) return true;
342
340
  throw new Error(`Failed to update chat: ${data.msg}`);
343
341
  } catch (error) {
344
- logger.error('Failed to update chat:', error);
342
+ this.#logger.error('Failed to update chat:', error);
345
343
  return false;
346
344
  }
347
345
  }
@@ -355,7 +353,7 @@ export class LarkEndpoint implements EndpointInstance {
355
353
  if (data.code === 0) return true;
356
354
  throw new Error(`Failed to add members: ${data.msg}`);
357
355
  } catch (error) {
358
- logger.error('Failed to add chat members:', error);
356
+ this.#logger.error('Failed to add chat members:', error);
359
357
  return false;
360
358
  }
361
359
  }
@@ -369,7 +367,7 @@ export class LarkEndpoint implements EndpointInstance {
369
367
  if (data.code === 0) return true;
370
368
  throw new Error(`Failed to remove members: ${data.msg}`);
371
369
  } catch (error) {
372
- logger.error('Failed to remove chat members:', error);
370
+ this.#logger.error('Failed to remove chat members:', error);
373
371
  return false;
374
372
  }
375
373
  }
@@ -380,7 +378,7 @@ export class LarkEndpoint implements EndpointInstance {
380
378
  if (data.code === 0) return (data.data?.items as unknown[]) || [];
381
379
  throw new Error(`Failed to get members: ${data.msg}`);
382
380
  } catch (error) {
383
- logger.error('Failed to get chat members:', error);
381
+ this.#logger.error('Failed to get chat members:', error);
384
382
  return [];
385
383
  }
386
384
  }
@@ -391,7 +389,7 @@ export class LarkEndpoint implements EndpointInstance {
391
389
  if (data.code === 0) return true;
392
390
  throw new Error(`Failed to dissolve chat: ${data.msg}`);
393
391
  } catch (error) {
394
- logger.error('Failed to dissolve chat:', error);
392
+ this.#logger.error('Failed to dissolve chat:', error);
395
393
  return false;
396
394
  }
397
395
  }
@@ -405,7 +403,7 @@ export class LarkEndpoint implements EndpointInstance {
405
403
  if (data.code === 0) return true;
406
404
  throw new Error(`Failed to set managers: ${data.msg}`);
407
405
  } catch (error) {
408
- logger.error('Failed to set chat managers:', error);
406
+ this.#logger.error('Failed to set chat managers:', error);
409
407
  return false;
410
408
  }
411
409
  }
@@ -419,7 +417,7 @@ export class LarkEndpoint implements EndpointInstance {
419
417
  if (data.code === 0) return true;
420
418
  throw new Error(`Failed to remove managers: ${data.msg}`);
421
419
  } catch (error) {
422
- logger.error('Failed to remove chat managers:', error);
420
+ this.#logger.error('Failed to remove chat managers:', error);
423
421
  return false;
424
422
  }
425
423
  }
@@ -524,7 +522,7 @@ export class LarkEndpoint implements EndpointInstance {
524
522
  expires_in: data.expire ?? 7200,
525
523
  timestamp: Date.now(),
526
524
  };
527
- logger.debug('Access token refreshed successfully');
525
+ this.#logger.debug('Access token refreshed successfully');
528
526
  return;
529
527
  }
530
528
  throw new Error(`Failed to get access token: ${data.msg} (${data.code})`);
package/src/index.ts CHANGED
@@ -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
@@ -13,7 +13,7 @@ const logger = getLogger('lark');
13
13
 
14
14
  /** Plugin Runtime owner config (`plugins.<instanceKey>` / schema.json). */
15
15
  export interface LarkAdapterConfig {
16
- readonly name?: string;
16
+ readonly id?: string;
17
17
  readonly appId?: string;
18
18
  readonly appSecret?: string;
19
19
  readonly encryptKey?: string;
@@ -29,7 +29,7 @@ export interface LarkAdapterConfig {
29
29
 
30
30
  export interface ResolvedLarkConfig {
31
31
  readonly context: 'lark';
32
- readonly name: string;
32
+ readonly id: string;
33
33
  readonly appId: string;
34
34
  readonly appSecret: string;
35
35
  readonly encryptKey?: string;
@@ -119,8 +119,8 @@ export function resolveLarkConfig(config: LarkAdapterConfig = {}): ResolvedLarkC
119
119
  );
120
120
  }
121
121
  const isFeishu = config.isFeishu ?? entry?.isFeishu ?? true;
122
- const name = (typeof config.name === 'string' && config.name)
123
- || (typeof entry?.name === 'string' && entry.name)
122
+ const id = (typeof config.id === 'string' && config.id)
123
+ || (typeof entry?.id === 'string' && entry.id)
124
124
  || process.env.LARK_BOT_NAME
125
125
  || 'lark-bot';
126
126
  const webhookPath = normalizeWebhookPath(
@@ -136,7 +136,7 @@ export function resolveLarkConfig(config: LarkAdapterConfig = {}): ResolvedLarkC
136
136
  const verificationToken = config.verificationToken ?? entry?.verificationToken;
137
137
  return {
138
138
  context: 'lark',
139
- name,
139
+ id,
140
140
  appId,
141
141
  appSecret,
142
142
  ...(encryptKey ? { encryptKey } : {}),
@@ -164,9 +164,9 @@ export function resolveChatType(chatId?: string, chatType?: string): 'group' | '
164
164
  * p2p 单聊映射为 private;id 一律为平台 chat_id。Lark 无 guild/频道容器,
165
165
  * 不产生 `parent`。
166
166
  */
167
- export function larkInboundConversation(endpointId: string, msg: LarkMessage): ConversationRef {
167
+ export function larkInboundConversation(endpointKey: string, msg: LarkMessage): ConversationRef {
168
168
  return {
169
- endpoint: { id: endpointId, adapter: endpointId.split('\0')[0] ?? endpointId },
169
+ endpoint: { id: endpointKey, adapter: endpointKey.split('\0')[0] ?? endpointKey },
170
170
  kind: resolveChatType(msg.chat_id, msg.chat_type),
171
171
  id: msg.chat_id || 'unknown',
172
172
  };
File without changes
File without changes
File without changes
File without changes