@zhin.js/adapter-lark 1.1.25 → 1.1.27

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 (42) hide show
  1. package/CHANGELOG.md +146 -0
  2. package/LICENSE +21 -0
  3. package/PERMITS.md +11 -11
  4. package/README.md +7 -3
  5. package/adapters/lark/index.js +3 -0
  6. package/adapters/lark/index.ts +3 -0
  7. package/agents/lark/conversation.js +15 -0
  8. package/agents/lark/conversation.ts +17 -0
  9. package/agents/lark/messages.js +4 -0
  10. package/agents/lark/messages.ts +4 -0
  11. package/agents/lark/skills/lark/tools/upload_file/index.js +3 -1
  12. package/agents/lark/skills/lark/tools/upload_file/index.ts +3 -1
  13. package/agents/lark/skills/lark-group-admin/tools/add_members/index.js +5 -6
  14. package/agents/lark/skills/lark-group-admin/tools/add_members/index.ts +6 -7
  15. package/agents/lark/skills/lark-group-admin/tools/dissolve_chat/index.js +6 -6
  16. package/agents/lark/skills/lark-group-admin/tools/dissolve_chat/index.ts +7 -7
  17. package/agents/lark/skills/lark-group-admin/tools/remove_managers/index.js +5 -6
  18. package/agents/lark/skills/lark-group-admin/tools/remove_managers/index.ts +6 -7
  19. package/agents/lark/skills/lark-group-admin/tools/set_managers/index.js +5 -6
  20. package/agents/lark/skills/lark-group-admin/tools/set_managers/index.ts +6 -7
  21. package/agents/lark/skills/lark-group-admin/tools/update_chat/index.js +5 -6
  22. package/agents/lark/skills/lark-group-admin/tools/update_chat/index.ts +6 -7
  23. package/lib/client.js +49 -16
  24. package/lib/console-spec.d.ts +1 -1
  25. package/lib/console-spec.js +71 -7
  26. package/lib/index.d.ts +2 -2
  27. package/lib/index.js +2 -2
  28. package/lib/platform-permit.d.ts +4 -11
  29. package/lib/platform-permit.js +7 -33
  30. package/lib/protocol.d.ts +10 -5
  31. package/lib/protocol.js +17 -20
  32. package/lib/webhook.d.ts +2 -3
  33. package/lib/webhook.js +35 -28
  34. package/package.json +25 -26
  35. package/plugin.js +0 -6
  36. package/schema.json +0 -56
  37. package/src/client.ts +44 -19
  38. package/src/console-spec.ts +71 -7
  39. package/src/index.ts +2 -7
  40. package/src/platform-permit.ts +5 -45
  41. package/src/protocol.ts +21 -24
  42. package/src/webhook.ts +38 -33
package/src/webhook.ts CHANGED
@@ -1,13 +1,13 @@
1
1
  /**
2
- * Lark/Feishu webhook HTTP: verification → signature → challenge/event → admit.
2
+ * Lark/Feishu webhook HTTP: signature → decrypt → verification token → challenge/event → admit.
3
3
  */
4
4
  import type { LarkCardAction } from './cards.js';
5
- import type { IncomingMessage, ServerResponse } from 'node:http';
6
5
  import type { HttpHost, HttpRouteRegistration } from '@zhin.js/host-http';
6
+ import { readTextBody } from '@zhin.js/host-http-contract';
7
7
  import { formatCompact, getLogger } from '@zhin.js/logger';
8
8
  import {
9
+ decryptLarkEvent,
9
10
  headerValue,
10
- readTextBody,
11
11
  verifySignature,
12
12
  verifyToken,
13
13
  type LarkEventBody,
@@ -31,17 +31,14 @@ export function registerLarkWebhookRoutes(
31
31
  ): HttpRouteRegistration[] {
32
32
  const path = handler.config.webhookPath;
33
33
  return [
34
- http.route('POST', path, async (request, response) => {
35
- await handleLarkWebhookRequest(request, response, handler);
36
- }, { summary: 'Lark/Feishu event webhook', tags: ['lark'] }),
34
+ http.route('POST', path, (request) => handleLarkWebhookRequest(request, handler), { summary: 'Lark/Feishu event webhook', tags: ['lark'] }),
37
35
  ];
38
36
  }
39
37
 
40
38
  export async function handleLarkWebhookRequest(
41
- request: IncomingMessage,
42
- response: ServerResponse,
39
+ request: Request,
43
40
  handler: LarkWebhookHandler,
44
- ): Promise<void> {
41
+ ): Promise<Response> {
45
42
  try {
46
43
  const rawBody = await readTextBody(request);
47
44
 
@@ -49,22 +46,10 @@ export async function handleLarkWebhookRequest(
49
46
  try {
50
47
  event = JSON.parse(rawBody) as LarkEventBody;
51
48
  } catch {
52
- response.writeHead(200, { 'Content-Type': 'application/json' });
53
- response.end(JSON.stringify({ code: 0, msg: 'success' }));
54
- return;
55
- }
56
-
57
- if (handler.config.verificationToken) {
58
- const bodyToken = (event as LarkEventBody & { header?: { token?: unknown } }).header?.token ?? event.token;
59
- const token = typeof bodyToken === 'string' ? bodyToken : headerValue(request.headers, 'x-lark-request-token');
60
- if (!verifyToken(token, handler.config.verificationToken)) {
61
- logger.warn(formatCompact({ op: 'webhook', ok: false, error: 'invalid verification token' }));
62
- response.writeHead(403, { 'Content-Type': 'text/plain' });
63
- response.end('Forbidden');
64
- return;
65
- }
49
+ return new Response(JSON.stringify({ code: 0, msg: 'success' }), { status: 200, headers: { 'Content-Type': 'application/json' } });
66
50
  }
67
51
 
52
+ // Signature first: it covers the raw (possibly encrypted) body.
68
53
  if (handler.config.encryptKey) {
69
54
  const timestamp = headerValue(request.headers, 'x-lark-request-timestamp');
70
55
  const nonce = headerValue(request.headers, 'x-lark-request-nonce');
@@ -76,12 +61,36 @@ export async function handleLarkWebhookRequest(
76
61
  || !verifySignature(handler.config.encryptKey, timestamp, nonce, rawBody, signature)
77
62
  ) {
78
63
  logger.warn(formatCompact({ op: 'webhook', ok: false, error: 'invalid signature' }));
79
- response.writeHead(403, { 'Content-Type': 'text/plain' });
80
- response.end('Forbidden');
81
- return;
64
+ return new Response('Forbidden', { status: 403, headers: { 'Content-Type': 'text/plain' } });
82
65
  }
83
66
  }
84
67
 
68
+ if (typeof event?.encrypt === 'string') {
69
+ if (!handler.config.encryptKey) {
70
+ logger.warn(formatCompact({ op: 'webhook', ok: false, error: 'encrypted event received but encryptKey is not configured' }));
71
+ return new Response(JSON.stringify({ code: -1, msg: 'Encrypt key is not configured' }), { status: 400, headers: { 'Content-Type': 'application/json' } });
72
+ }
73
+ try {
74
+ event = JSON.parse(decryptLarkEvent(handler.config.encryptKey, event.encrypt)) as LarkEventBody;
75
+ } catch {
76
+ logger.warn(formatCompact({ op: 'webhook', ok: false, error: 'event decryption failed' }));
77
+ return new Response(JSON.stringify({ code: -1, msg: 'Decryption failed' }), { status: 400, headers: { 'Content-Type': 'application/json' } });
78
+ }
79
+ }
80
+
81
+ if (!event || typeof event !== 'object') {
82
+ return new Response(JSON.stringify({ code: 0, msg: 'success' }), { status: 200, headers: { 'Content-Type': 'application/json' } });
83
+ }
84
+
85
+ // Token and challenge live inside the decrypted event.
86
+ if (handler.config.verificationToken) {
87
+ const bodyToken = (event as LarkEventBody & { header?: { token?: unknown } }).header?.token ?? event.token;
88
+ const token = typeof bodyToken === 'string' ? bodyToken : headerValue(request.headers, 'x-lark-request-token');
89
+ if (!verifyToken(token, handler.config.verificationToken)) {
90
+ logger.warn(formatCompact({ op: 'webhook', ok: false, error: 'invalid verification token' }));
91
+ return new Response('Forbidden', { status: 403, headers: { 'Content-Type': 'text/plain' } });
92
+ }
93
+ }
85
94
 
86
95
  if (handler.isOpen) {
87
96
  const eventName = event.type === 'event_callback'
@@ -91,9 +100,7 @@ export async function handleLarkWebhookRequest(
91
100
  }
92
101
 
93
102
  if (event.type === 'url_verification') {
94
- response.writeHead(200, { 'Content-Type': 'application/json' });
95
- response.end(JSON.stringify({ challenge: event.challenge }));
96
- return;
103
+ return new Response(JSON.stringify({ challenge: event.challenge }), { status: 200, headers: { 'Content-Type': 'application/json' } });
97
104
  }
98
105
 
99
106
  const headerEventType = (event as LarkEventBody & { header?: { event_type?: string } }).header?.event_type;
@@ -107,11 +114,9 @@ export async function handleLarkWebhookRequest(
107
114
  handler.admit(message, event);
108
115
  }
109
116
 
110
- response.writeHead(200, { 'Content-Type': 'application/json' });
111
- response.end(JSON.stringify({ code: 0, msg: 'success' }));
117
+ return new Response(JSON.stringify({ code: 0, msg: 'success' }), { status: 200, headers: { 'Content-Type': 'application/json' } });
112
118
  } catch (error) {
113
119
  logger.error('Webhook error:', error);
114
- response.writeHead(500, { 'Content-Type': 'application/json' });
115
- response.end(JSON.stringify({ code: -1, msg: 'Internal Server Error' }));
120
+ return new Response(JSON.stringify({ code: -1, msg: 'Internal Server Error' }), { status: 500, headers: { 'Content-Type': 'application/json' } });
116
121
  }
117
122
  }