@zhin.js/adapter-lark 6.0.14 → 7.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +36 -0
- package/README.md +11 -2
- package/adapters/lark.js +1 -3
- package/adapters/lark.ts +1 -3
- package/agent/tools/add_members.ts +4 -6
- package/agent/tools/create_chat.ts +4 -6
- package/agent/tools/dissolve_chat.ts +4 -6
- package/agent/tools/get_user.ts +4 -6
- package/agent/tools/remove_managers.ts +4 -6
- package/agent/tools/set_managers.ts +4 -6
- package/agent/tools/update_chat.ts +4 -6
- package/agent/tools/upload_file.ts +4 -8
- package/lib/client.d.ts +12 -0
- package/lib/client.js +2 -0
- package/lib/endpoint.d.ts +41 -5
- package/lib/endpoint.js +48 -7
- package/lib/index.d.ts +2 -2
- package/lib/index.js +2 -2
- package/lib/lark-endpoint-commands.d.ts +1 -1
- package/lib/protocol.d.ts +1 -1
- package/lib/protocol.js +1 -1
- package/lib/webhook.d.ts +1 -0
- package/lib/webhook.js +6 -0
- package/package.json +15 -14
- package/src/client.ts +16 -0
- package/src/endpoint.ts +70 -11
- package/src/index.ts +3 -7
- package/src/protocol.ts +1 -1
- package/src/webhook.ts +8 -0
- package/lib/lark-agent-deps.d.ts +0 -27
- package/lib/lark-agent-deps.js +0 -30
- package/src/lark-agent-deps.ts +0 -62
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,41 @@
|
|
|
1
1
|
# @zhin.js/adapter-lark
|
|
2
2
|
|
|
3
|
+
## 7.0.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 1fc78bc: Unify native platform Client access behind the literal `adapter` discriminant. Handlers infer both native events and Clients, while command, inbound/outbound middleware, and both Agent tool authoring surfaces expose the exact operation-scoped Client through a lazy `$client` getter. Definitions without `adapter` keep `$client` typed as `unknown`, and runtime dispatch rejects adapter mismatches before resolving the Client. Bundled platform tools now use this single path instead of model-provided endpoint ids and adapter-specific dependency wrappers. Every adapter registers one Client/EventMap contract, and protocol adapters including NapCat, Milky, OneBot and Satori now produce transport-independent Client objects rather than letting Endpoint instances impersonate Clients.
|
|
8
|
+
- Updated dependencies [e9c6a73]
|
|
9
|
+
- Updated dependencies [4e8117c]
|
|
10
|
+
- Updated dependencies [902fa35]
|
|
11
|
+
- Updated dependencies [54bfd6b]
|
|
12
|
+
- Updated dependencies [12025ee]
|
|
13
|
+
- Updated dependencies [09b14d6]
|
|
14
|
+
- Updated dependencies [1fc78bc]
|
|
15
|
+
- @zhin.js/agent@1.1.16
|
|
16
|
+
- @zhin.js/adapter@1.2.1
|
|
17
|
+
- @zhin.js/core@1.5.14
|
|
18
|
+
- @zhin.js/host-http@1.0.13
|
|
19
|
+
- @zhin.js/command@1.0.16
|
|
20
|
+
- @zhin.js/logger@1.0.77
|
|
21
|
+
- zhin.js@6.0.14
|
|
22
|
+
- @zhin.js/feature-kit@1.0.13
|
|
23
|
+
- @zhin.js/permission@1.0.4
|
|
24
|
+
|
|
25
|
+
## 7.0.0
|
|
26
|
+
|
|
27
|
+
### Patch Changes
|
|
28
|
+
|
|
29
|
+
- f2c532f: Expose exact per-Endpoint message operations through one validated Adapter capability model, route Core control calls through declared active capabilities, and connect existing recall, edit, reaction, and typing implementations across platform adapters.
|
|
30
|
+
- Updated dependencies [b10d058]
|
|
31
|
+
- Updated dependencies [f2c532f]
|
|
32
|
+
- Updated dependencies [3dbf990]
|
|
33
|
+
- @zhin.js/host-http@1.0.12
|
|
34
|
+
- @zhin.js/adapter@1.2.0
|
|
35
|
+
- @zhin.js/core@1.5.13
|
|
36
|
+
- @zhin.js/agent@1.1.15
|
|
37
|
+
- zhin.js@6.0.13
|
|
38
|
+
|
|
3
39
|
## 6.0.14
|
|
4
40
|
|
|
5
41
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -20,7 +20,7 @@ pnpm add @zhin.js/adapter-lark
|
|
|
20
20
|
## Plugin Runtime
|
|
21
21
|
|
|
22
22
|
- `@zhin.js/adapter` — 约定式 `adapters/lark.ts`(`defineAdapter`)
|
|
23
|
-
- `@zhin.js/core` — `
|
|
23
|
+
- `@zhin.js/core` — `Endpoint.emit(...)` 入站、`outboundMessageToken` 出站
|
|
24
24
|
- `@zhin.js/host-http` — `httpHostToken` 注册 Webhook 路由(**非** legacy host-router/Koa)
|
|
25
25
|
- `zhin.js` — `plugin.ts`(`definePlugin`)
|
|
26
26
|
- 配置经插件 `schema.json` 落到 `plugins.<instanceKey>`
|
|
@@ -79,7 +79,7 @@ plugins:
|
|
|
79
79
|
|
|
80
80
|
## Agent 工具
|
|
81
81
|
|
|
82
|
-
`agent/` 目录保留(get_user
|
|
82
|
+
`agent/` 目录保留(get_user、群聊、管理员、上传文件等)。工具声明 `adapter: 'lark'` 后,通过惰性的 `context.$client` 自动取得当前操作的 `LarkClient`;无需把 Endpoint id 暴露给模型。
|
|
83
83
|
|
|
84
84
|
## 平台权限(platform permit)
|
|
85
85
|
|
|
@@ -91,3 +91,12 @@ plugins:
|
|
|
91
91
|
pnpm --filter @zhin.js/adapter-lark build
|
|
92
92
|
pnpm --filter @zhin.js/adapter-lark test
|
|
93
93
|
```
|
|
94
|
+
|
|
95
|
+
## 故障排查
|
|
96
|
+
|
|
97
|
+
| 现象 | 排查 |
|
|
98
|
+
| --- | --- |
|
|
99
|
+
| URL 验证失败 | 检查公网 HTTPS、`webhookPath`、verification token 与 encrypt key |
|
|
100
|
+
| Tenant Token 获取失败 | 检查 `appId`、`appSecret` 与应用版本是否已发布 |
|
|
101
|
+
| 群里 @机器人不触发 AI | 当前无法可靠判定 bot open_id;使用显式 AI 前缀 |
|
|
102
|
+
| 能收不能发 | 检查机器人消息权限、可见范围与应用是否已加入群聊 |
|
package/adapters/lark.js
CHANGED
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
* Convention entry: discover `adapters/lark.ts` → defineAdapter.
|
|
4
4
|
*/
|
|
5
5
|
import { defineAdapter } from 'zhin.js/adapter';
|
|
6
|
-
import { messageGatewayToken, sideEventGatewayToken } from '@zhin.js/core/runtime';
|
|
7
6
|
import { httpHostToken } from '@zhin.js/host-http';
|
|
8
7
|
import { LarkEndpoint } from "../lib/endpoint.js";
|
|
9
8
|
import { resolveLarkConfig, } from "../lib/protocol.js";
|
|
@@ -11,6 +10,7 @@ import { larkRuntimeStateToken } from "../lib/lark-runtime-state.js";
|
|
|
11
10
|
export { LarkEndpoint } from "../lib/endpoint.js";
|
|
12
11
|
export default defineAdapter({
|
|
13
12
|
capabilities: ['inbound', 'outbound'],
|
|
13
|
+
operations: ['recall'],
|
|
14
14
|
// image 段 canonical MediaRef 经 /im/v1/images 物化为 image_key
|
|
15
15
|
// (base64 解码 / path 读盘 / url 下载后上传,kind=file 平台引用直通);
|
|
16
16
|
// canonical Markdown 由 endpoint codec 转为 lark_md 消息卡片;
|
|
@@ -29,8 +29,6 @@ export default defineAdapter({
|
|
|
29
29
|
});
|
|
30
30
|
return new LarkEndpoint({
|
|
31
31
|
id: context.id,
|
|
32
|
-
gateway: context.use(messageGatewayToken),
|
|
33
|
-
sideEvents: context.use(sideEventGatewayToken),
|
|
34
32
|
http: context.use(httpHostToken),
|
|
35
33
|
config,
|
|
36
34
|
});
|
package/adapters/lark.ts
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
* Convention entry: discover `adapters/lark.ts` → defineAdapter.
|
|
3
3
|
*/
|
|
4
4
|
import { defineAdapter } from 'zhin.js/adapter';
|
|
5
|
-
import { messageGatewayToken, sideEventGatewayToken } from '@zhin.js/core/runtime';
|
|
6
5
|
import { httpHostToken } from '@zhin.js/host-http';
|
|
7
6
|
import { LarkEndpoint } from '../src/endpoint.js';
|
|
8
7
|
import {
|
|
@@ -16,6 +15,7 @@ export type { LarkEndpointOptions, LarkFetch } from '../src/endpoint.js';
|
|
|
16
15
|
|
|
17
16
|
export default defineAdapter<LarkAdapterConfig>({
|
|
18
17
|
capabilities: ['inbound', 'outbound'],
|
|
18
|
+
operations: ['recall'],
|
|
19
19
|
// image 段 canonical MediaRef 经 /im/v1/images 物化为 image_key
|
|
20
20
|
// (base64 解码 / path 读盘 / url 下载后上传,kind=file 平台引用直通);
|
|
21
21
|
// canonical Markdown 由 endpoint codec 转为 lark_md 消息卡片;
|
|
@@ -34,8 +34,6 @@ export default defineAdapter<LarkAdapterConfig>({
|
|
|
34
34
|
});
|
|
35
35
|
return new LarkEndpoint({
|
|
36
36
|
id: context.id,
|
|
37
|
-
gateway: context.use(messageGatewayToken),
|
|
38
|
-
sideEvents: context.use(sideEventGatewayToken),
|
|
39
37
|
http: context.use(httpHostToken),
|
|
40
38
|
config,
|
|
41
39
|
});
|
|
@@ -1,20 +1,18 @@
|
|
|
1
1
|
import { defineAgentTool } from '@zhin.js/agent/tools';
|
|
2
2
|
import { z } from 'zod';
|
|
3
|
-
import { getLarkAgentDeps } from '../../src/lark-agent-deps.js';
|
|
4
3
|
import { platformPermit } from '../../src/platform-permit.js';
|
|
5
4
|
|
|
6
|
-
export default defineAgentTool<{
|
|
5
|
+
export default defineAgentTool<{ chat_id: string; user_ids: string }>({
|
|
7
6
|
description: '添加飞书群成员',
|
|
8
7
|
inputSchema: z.object({
|
|
9
|
-
endpoint_id: z.string().describe('Endpoint 名称'),
|
|
10
8
|
chat_id: z.string().describe('群聊 ID'),
|
|
11
9
|
user_ids: z.string().describe('用户 open_id 列表,逗号分隔'),
|
|
12
10
|
}),
|
|
13
|
-
|
|
11
|
+
adapter: 'lark',
|
|
14
12
|
tags: ['lark'],
|
|
15
13
|
permissions: [platformPermit('chat_admin')],
|
|
16
|
-
async execute({
|
|
17
|
-
const endpoint =
|
|
14
|
+
async execute({ chat_id, user_ids }: { chat_id: string; user_ids: string }, context) {
|
|
15
|
+
const endpoint = context.$client;
|
|
18
16
|
const success = await endpoint.addChatMembers(chat_id, user_ids.split(','));
|
|
19
17
|
return { success, message: success ? '成员添加成功' : '添加失败' };
|
|
20
18
|
},
|
|
@@ -1,18 +1,16 @@
|
|
|
1
1
|
import { defineAgentTool } from '@zhin.js/agent/tools';
|
|
2
2
|
import { z } from 'zod';
|
|
3
|
-
|
|
4
|
-
export default defineAgentTool<{ endpoint_id: string; name: string; members: string; owner?: string }>({
|
|
3
|
+
export default defineAgentTool<{ name: string; members: string; owner?: string }>({
|
|
5
4
|
description: '创建飞书群聊',
|
|
6
5
|
inputSchema: z.object({
|
|
7
|
-
endpoint_id: z.string().describe('Endpoint 名称'),
|
|
8
6
|
name: z.string().describe('群名'),
|
|
9
7
|
members: z.string().describe('成员 open_id 列表,逗号分隔'),
|
|
10
8
|
owner: z.string().optional().describe('群主 open_id(可选)'),
|
|
11
9
|
}),
|
|
12
|
-
|
|
10
|
+
adapter: 'lark',
|
|
13
11
|
tags: ['lark'],
|
|
14
|
-
async execute({
|
|
15
|
-
const endpoint =
|
|
12
|
+
async execute({ name, members, owner }: { name: string; members: string; owner?: string }, context) {
|
|
13
|
+
const endpoint = context.$client;
|
|
16
14
|
const chatId = await endpoint.createChat(name, members.split(','), owner);
|
|
17
15
|
return { success: !!chatId, chat_id: chatId, message: chatId ? `群聊创建成功: ${chatId}` : '创建失败' };
|
|
18
16
|
},
|
|
@@ -1,16 +1,14 @@
|
|
|
1
1
|
import { defineAgentTool } from '@zhin.js/agent/tools';
|
|
2
2
|
import { z } from 'zod';
|
|
3
|
-
|
|
4
|
-
export default defineAgentTool<{ endpoint_id: string; chat_id: string }>({
|
|
3
|
+
export default defineAgentTool<{ chat_id: string }>({
|
|
5
4
|
description: '解散飞书群聊(需要群主权限)',
|
|
6
5
|
inputSchema: z.object({
|
|
7
|
-
endpoint_id: z.string().describe('Endpoint 名称'),
|
|
8
6
|
chat_id: z.string().describe('群聊 ID'),
|
|
9
7
|
}),
|
|
10
|
-
|
|
8
|
+
adapter: 'lark',
|
|
11
9
|
tags: ['lark'],
|
|
12
|
-
async execute({
|
|
13
|
-
const endpoint =
|
|
10
|
+
async execute({ chat_id }: { chat_id: string }, context) {
|
|
11
|
+
const endpoint = context.$client;
|
|
14
12
|
const success = await endpoint.dissolveChat(chat_id);
|
|
15
13
|
return { success, message: success ? '群聊已解散' : '解散失败' };
|
|
16
14
|
},
|
package/agent/tools/get_user.ts
CHANGED
|
@@ -1,17 +1,15 @@
|
|
|
1
1
|
import { defineAgentTool } from '@zhin.js/agent/tools';
|
|
2
2
|
import { z } from 'zod';
|
|
3
|
-
import { getLarkAgentDeps } from '../../src/lark-agent-deps.js';
|
|
4
3
|
|
|
5
|
-
export default defineAgentTool<{
|
|
4
|
+
export default defineAgentTool<{ user_id: string }>({
|
|
6
5
|
description: '获取飞书用户信息',
|
|
7
6
|
inputSchema: z.object({
|
|
8
|
-
endpoint_id: z.string().describe('Endpoint 名称'),
|
|
9
7
|
user_id: z.string().describe('用户 ID (open_id)'),
|
|
10
8
|
}),
|
|
11
|
-
|
|
9
|
+
adapter: 'lark',
|
|
12
10
|
tags: ['lark'],
|
|
13
|
-
async execute({
|
|
14
|
-
const endpoint =
|
|
11
|
+
async execute({ user_id }: { user_id: string }, context) {
|
|
12
|
+
const endpoint = context.$client;
|
|
15
13
|
return await endpoint.getUserInfo(user_id);
|
|
16
14
|
},
|
|
17
15
|
});
|
|
@@ -1,20 +1,18 @@
|
|
|
1
1
|
import { defineAgentTool } from '@zhin.js/agent/tools';
|
|
2
2
|
import { z } from 'zod';
|
|
3
|
-
import { getLarkAgentDeps } from '../../src/lark-agent-deps.js';
|
|
4
3
|
import { platformPermit } from '../../src/platform-permit.js';
|
|
5
4
|
|
|
6
|
-
export default defineAgentTool<{
|
|
5
|
+
export default defineAgentTool<{ chat_id: string; user_ids: string }>({
|
|
7
6
|
description: '移除飞书群管理员',
|
|
8
7
|
inputSchema: z.object({
|
|
9
|
-
endpoint_id: z.string().describe('Endpoint 名称'),
|
|
10
8
|
chat_id: z.string().describe('群聊 ID'),
|
|
11
9
|
user_ids: z.string().describe('用户 open_id 列表,逗号分隔'),
|
|
12
10
|
}),
|
|
13
|
-
|
|
11
|
+
adapter: 'lark',
|
|
14
12
|
tags: ['lark'],
|
|
15
13
|
permissions: [platformPermit('manage_managers')],
|
|
16
|
-
async execute({
|
|
17
|
-
const endpoint =
|
|
14
|
+
async execute({ chat_id, user_ids }: { chat_id: string; user_ids: string }, context) {
|
|
15
|
+
const endpoint = context.$client;
|
|
18
16
|
const success = await endpoint.removeChatManagers(chat_id, user_ids.split(','));
|
|
19
17
|
return { success, message: success ? '管理员移除成功' : '移除失败' };
|
|
20
18
|
},
|
|
@@ -1,20 +1,18 @@
|
|
|
1
1
|
import { defineAgentTool } from '@zhin.js/agent/tools';
|
|
2
2
|
import { z } from 'zod';
|
|
3
|
-
import { getLarkAgentDeps } from '../../src/lark-agent-deps.js';
|
|
4
3
|
import { platformPermit } from '../../src/platform-permit.js';
|
|
5
4
|
|
|
6
|
-
export default defineAgentTool<{
|
|
5
|
+
export default defineAgentTool<{ chat_id: string; user_ids: string }>({
|
|
7
6
|
description: '设置飞书群管理员',
|
|
8
7
|
inputSchema: z.object({
|
|
9
|
-
endpoint_id: z.string().describe('Endpoint 名称'),
|
|
10
8
|
chat_id: z.string().describe('群聊 ID'),
|
|
11
9
|
user_ids: z.string().describe('用户 open_id 列表,逗号分隔'),
|
|
12
10
|
}),
|
|
13
|
-
|
|
11
|
+
adapter: 'lark',
|
|
14
12
|
tags: ['lark'],
|
|
15
13
|
permissions: [platformPermit('manage_managers')],
|
|
16
|
-
async execute({
|
|
17
|
-
const endpoint =
|
|
14
|
+
async execute({ chat_id, user_ids }: { chat_id: string; user_ids: string }, context) {
|
|
15
|
+
const endpoint = context.$client;
|
|
18
16
|
const success = await endpoint.setChatManagers(chat_id, user_ids.split(','));
|
|
19
17
|
return { success, message: success ? '管理员设置成功' : '设置失败' };
|
|
20
18
|
},
|
|
@@ -1,21 +1,19 @@
|
|
|
1
1
|
import { defineAgentTool } from '@zhin.js/agent/tools';
|
|
2
2
|
import { z } from 'zod';
|
|
3
|
-
import { getLarkAgentDeps } from '../../src/lark-agent-deps.js';
|
|
4
3
|
import { platformPermit } from '../../src/platform-permit.js';
|
|
5
4
|
|
|
6
|
-
export default defineAgentTool<{
|
|
5
|
+
export default defineAgentTool<{ chat_id: string; name?: string; description?: string }>({
|
|
7
6
|
description: '更新飞书群聊信息(群名、描述)',
|
|
8
7
|
inputSchema: z.object({
|
|
9
|
-
endpoint_id: z.string().describe('Endpoint 名称'),
|
|
10
8
|
chat_id: z.string().describe('群聊 ID'),
|
|
11
9
|
name: z.string().optional().describe('新群名(可选)'),
|
|
12
10
|
description: z.string().optional().describe('新描述(可选)'),
|
|
13
11
|
}),
|
|
14
|
-
|
|
12
|
+
adapter: 'lark',
|
|
15
13
|
tags: ['lark'],
|
|
16
14
|
permissions: [platformPermit('chat_admin')],
|
|
17
|
-
async execute({
|
|
18
|
-
const endpoint =
|
|
15
|
+
async execute({ chat_id, name, description }: { chat_id: string; name?: string; description?: string }, context) {
|
|
16
|
+
const endpoint = context.$client;
|
|
19
17
|
const success = await endpoint.updateChatInfo(chat_id, { name, description });
|
|
20
18
|
return { success, message: success ? '群信息更新成功' : '更新失败' };
|
|
21
19
|
},
|
|
@@ -1,26 +1,22 @@
|
|
|
1
1
|
import { defineAgentTool } from '@zhin.js/agent/tools';
|
|
2
2
|
import { z } from 'zod';
|
|
3
|
-
import { getLarkAgentDeps } from '../../src/lark-agent-deps.js';
|
|
4
3
|
|
|
5
4
|
export default defineAgentTool<{
|
|
6
|
-
endpoint_id: string;
|
|
7
5
|
file_path: string;
|
|
8
6
|
file_type: 'image' | 'file' | 'video' | 'audio';
|
|
9
7
|
}>({
|
|
10
8
|
description: '上传文件到飞书(image/file/video/audio)',
|
|
11
9
|
inputSchema: z.object({
|
|
12
|
-
endpoint_id: z.string().describe('Endpoint 名称'),
|
|
13
10
|
file_path: z.string().describe('本地文件路径'),
|
|
14
11
|
file_type: z.enum(['image', 'file', 'video', 'audio']).default('file').describe('飞书文件类型'),
|
|
15
12
|
}),
|
|
16
|
-
|
|
13
|
+
adapter: 'lark',
|
|
17
14
|
tags: ['lark'],
|
|
18
|
-
async execute({
|
|
19
|
-
endpoint_id: string;
|
|
15
|
+
async execute({ file_path, file_type }: {
|
|
20
16
|
file_path: string;
|
|
21
17
|
file_type: 'image' | 'file' | 'video' | 'audio';
|
|
22
|
-
}) {
|
|
23
|
-
const endpoint =
|
|
18
|
+
}, context) {
|
|
19
|
+
const endpoint = context.$client;
|
|
24
20
|
const result = await endpoint.uploadFile(file_path, file_type);
|
|
25
21
|
return { success: true, file_key: result, message: `文件已上传,file_key: ${result}` };
|
|
26
22
|
},
|
package/lib/client.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { LarkClient } from './endpoint.js';
|
|
2
|
+
import type { LarkEventBody } from './protocol.js';
|
|
3
|
+
export type LarkClientEventMap = Record<string, LarkEventBody>;
|
|
4
|
+
declare module '@zhin.js/feature-kit' {
|
|
5
|
+
interface AdapterClientRegistry {
|
|
6
|
+
readonly lark: {
|
|
7
|
+
readonly client: LarkClient;
|
|
8
|
+
readonly events: LarkClientEventMap;
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
export declare const larkClient: import("@zhin.js/adapter").EndpointClientToken<LarkClient, LarkClientEventMap>;
|
package/lib/client.js
ADDED
package/lib/endpoint.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import
|
|
2
|
-
import type
|
|
1
|
+
import { Endpoint } from 'zhin.js/adapter';
|
|
2
|
+
import { type EndpointControl, type EndpointManagement, type EndpointSendRequest } from 'zhin.js/adapter';
|
|
3
3
|
import type { HttpHost } from '@zhin.js/host-http';
|
|
4
4
|
import type { CapabilityId } from 'zhin.js';
|
|
5
5
|
import { resolveMediaBinary } from './media-upload.js';
|
|
@@ -17,14 +17,48 @@ export type LarkFetch = (url: string, init?: {
|
|
|
17
17
|
}>;
|
|
18
18
|
export interface LarkEndpointOptions {
|
|
19
19
|
readonly id: CapabilityId;
|
|
20
|
-
readonly gateway: MessageGateway;
|
|
21
|
-
readonly sideEvents?: SideEventGateway;
|
|
22
20
|
readonly http: HttpHost;
|
|
23
21
|
readonly config: ResolvedLarkConfig;
|
|
24
22
|
readonly fetch?: LarkFetch;
|
|
25
23
|
}
|
|
26
|
-
export
|
|
24
|
+
export interface LarkClientApi {
|
|
25
|
+
getUserInfo(userId: string, userIdType?: 'open_id' | 'user_id' | 'union_id'): Promise<unknown>;
|
|
26
|
+
getChatInfo(chatId: string): Promise<unknown>;
|
|
27
|
+
uploadFile(filePath: string, fileType?: 'image' | 'file' | 'video' | 'audio'): Promise<string | null>;
|
|
28
|
+
createChat(name: string, userIds: string[], ownerId?: string): Promise<string | null>;
|
|
29
|
+
updateChatInfo(chatId: string, options: {
|
|
30
|
+
name?: string;
|
|
31
|
+
description?: string;
|
|
32
|
+
}): Promise<boolean>;
|
|
33
|
+
addChatMembers(chatId: string, userIds: string[]): Promise<boolean>;
|
|
34
|
+
removeChatMembers(chatId: string, userIds: string[]): Promise<boolean>;
|
|
35
|
+
getChatMembers(chatId: string): Promise<unknown[]>;
|
|
36
|
+
dissolveChat(chatId: string): Promise<boolean>;
|
|
37
|
+
setChatManagers(chatId: string, userIds: string[]): Promise<boolean>;
|
|
38
|
+
removeChatManagers(chatId: string, userIds: string[]): Promise<boolean>;
|
|
39
|
+
}
|
|
40
|
+
/** Feishu/Lark OpenAPI client surface carried by every Endpoint event. */
|
|
41
|
+
export declare class LarkClient implements LarkClientApi {
|
|
42
|
+
readonly api: LarkClientApi;
|
|
43
|
+
constructor(api: LarkClientApi);
|
|
44
|
+
getUserInfo: (id: string, type?: "open_id" | "user_id" | "union_id") => Promise<unknown>;
|
|
45
|
+
getChatInfo: (id: string) => Promise<unknown>;
|
|
46
|
+
uploadFile: (path: string, type?: "image" | "file" | "video" | "audio") => Promise<string | null>;
|
|
47
|
+
createChat: (name: string, users: string[], owner?: string) => Promise<string | null>;
|
|
48
|
+
updateChatInfo: (id: string, options: {
|
|
49
|
+
name?: string;
|
|
50
|
+
description?: string;
|
|
51
|
+
}) => Promise<boolean>;
|
|
52
|
+
addChatMembers: (id: string, users: string[]) => Promise<boolean>;
|
|
53
|
+
removeChatMembers: (id: string, users: string[]) => Promise<boolean>;
|
|
54
|
+
getChatMembers: (id: string) => Promise<unknown[]>;
|
|
55
|
+
dissolveChat: (id: string) => Promise<boolean>;
|
|
56
|
+
setChatManagers: (id: string, users: string[]) => Promise<boolean>;
|
|
57
|
+
removeChatManagers: (id: string, users: string[]) => Promise<boolean>;
|
|
58
|
+
}
|
|
59
|
+
export declare class LarkEndpoint extends Endpoint<LarkClient> {
|
|
27
60
|
#private;
|
|
61
|
+
readonly client: LarkClient;
|
|
28
62
|
/**
|
|
29
63
|
* Console 社交面语义端口。Lark OpenAPI 仅接读取类列表能力:
|
|
30
64
|
* - listGroups:GET /im/v1/chats(bot 所在群列表,分页归一为 {group_id: chat_id, name})
|
|
@@ -33,6 +67,7 @@ export declare class LarkEndpoint implements EndpointInstance {
|
|
|
33
67
|
* listChannels(无频道/guild 概念)、写操作(审批/踢人/禁言等本轮不在范围内)。
|
|
34
68
|
*/
|
|
35
69
|
readonly management: EndpointManagement;
|
|
70
|
+
readonly control: EndpointControl;
|
|
36
71
|
constructor(options: LarkEndpointOptions);
|
|
37
72
|
/** Used by webhook handler. */
|
|
38
73
|
get isOpen(): boolean;
|
|
@@ -47,6 +82,7 @@ export declare class LarkEndpoint implements EndpointInstance {
|
|
|
47
82
|
uploadImage(media: Parameters<typeof resolveMediaBinary>[0]): Promise<string | null>;
|
|
48
83
|
/** Test / internal: admit a parsed message when open (non-webhook path). */
|
|
49
84
|
admit(msg: LarkMessage): void;
|
|
85
|
+
admitPlatform(name: string, event: unknown): void;
|
|
50
86
|
getUserInfo(userId: string, userIdType?: 'open_id' | 'user_id' | 'union_id'): Promise<unknown>;
|
|
51
87
|
getChatInfo(chatId: string): Promise<unknown>;
|
|
52
88
|
uploadFile(filePath: string, fileType?: 'image' | 'file' | 'video' | 'audio'): Promise<string | null>;
|
package/lib/endpoint.js
CHANGED
|
@@ -1,16 +1,48 @@
|
|
|
1
|
+
import { Endpoint } from 'zhin.js/adapter';
|
|
1
2
|
/**
|
|
2
3
|
* LarkEndpoint — lifecycle, outbound, admit, OpenAPI helpers for agent tools.
|
|
3
4
|
*/
|
|
4
5
|
import { readFile } from 'node:fs/promises';
|
|
5
6
|
import { basename } from 'node:path';
|
|
7
|
+
import { createRecallEndpointControl, } from 'zhin.js/adapter';
|
|
6
8
|
import { formatCompact, getAdapterLogger } from '@zhin.js/logger';
|
|
7
|
-
import { registerLarkAgentEndpoint } from './lark-agent-deps.js';
|
|
8
9
|
import { buildImageUploadForm, readOutboundImageMedia, resolveMediaBinary, } from './media-upload.js';
|
|
9
10
|
import { formatInboundContent, formatOutboundBody, generateMessageId, larkInboundConversation, resolveChatType, resolveSender, } from './protocol.js';
|
|
10
11
|
import { registerLarkWebhookRoutes } from './webhook.js';
|
|
11
12
|
/** 出站 HTTP 调用统一 30s 超时。 */
|
|
12
13
|
const OUTBOUND_TIMEOUT_MS = 30_000;
|
|
13
|
-
|
|
14
|
+
/** Feishu/Lark OpenAPI client surface carried by every Endpoint event. */
|
|
15
|
+
export class LarkClient {
|
|
16
|
+
api;
|
|
17
|
+
constructor(api) {
|
|
18
|
+
this.api = api;
|
|
19
|
+
}
|
|
20
|
+
getUserInfo = (id, type) => this.api.getUserInfo(id, type);
|
|
21
|
+
getChatInfo = (id) => this.api.getChatInfo(id);
|
|
22
|
+
uploadFile = (path, type) => this.api.uploadFile(path, type);
|
|
23
|
+
createChat = (name, users, owner) => this.api.createChat(name, users, owner);
|
|
24
|
+
updateChatInfo = (id, options) => this.api.updateChatInfo(id, options);
|
|
25
|
+
addChatMembers = (id, users) => this.api.addChatMembers(id, users);
|
|
26
|
+
removeChatMembers = (id, users) => this.api.removeChatMembers(id, users);
|
|
27
|
+
getChatMembers = (id) => this.api.getChatMembers(id);
|
|
28
|
+
dissolveChat = (id) => this.api.dissolveChat(id);
|
|
29
|
+
setChatManagers = (id, users) => this.api.setChatManagers(id, users);
|
|
30
|
+
removeChatManagers = (id, users) => this.api.removeChatManagers(id, users);
|
|
31
|
+
}
|
|
32
|
+
export class LarkEndpoint extends Endpoint {
|
|
33
|
+
client = new LarkClient({
|
|
34
|
+
getUserInfo: (id, type) => this.getUserInfo(id, type),
|
|
35
|
+
getChatInfo: (id) => this.getChatInfo(id),
|
|
36
|
+
uploadFile: (path, type) => this.uploadFile(path, type),
|
|
37
|
+
createChat: (name, users, owner) => this.createChat(name, users, owner),
|
|
38
|
+
updateChatInfo: (id, options) => this.updateChatInfo(id, options),
|
|
39
|
+
addChatMembers: (id, users) => this.addChatMembers(id, users),
|
|
40
|
+
removeChatMembers: (id, users) => this.removeChatMembers(id, users),
|
|
41
|
+
getChatMembers: (id) => this.getChatMembers(id),
|
|
42
|
+
dissolveChat: (id) => this.dissolveChat(id),
|
|
43
|
+
setChatManagers: (id, users) => this.setChatManagers(id, users),
|
|
44
|
+
removeChatManagers: (id, users) => this.removeChatManagers(id, users),
|
|
45
|
+
});
|
|
14
46
|
#logger;
|
|
15
47
|
#options;
|
|
16
48
|
#fetch;
|
|
@@ -38,13 +70,14 @@ export class LarkEndpoint {
|
|
|
38
70
|
},
|
|
39
71
|
listGroupMembers: (groupId) => this.#listAllPages(`/im/v1/chats/${encodeURIComponent(groupId)}/members`, { member_id_type: 'open_id' }),
|
|
40
72
|
});
|
|
73
|
+
control = createRecallEndpointControl((id) => this.recallMessage(id));
|
|
41
74
|
#routeReleases = [];
|
|
42
75
|
#accessToken = { token: '', expires_in: 0, timestamp: 0 };
|
|
43
76
|
#refreshPromise = null;
|
|
44
77
|
#open = false;
|
|
45
78
|
#started = false;
|
|
46
|
-
#unregisterAgent;
|
|
47
79
|
constructor(options) {
|
|
80
|
+
super();
|
|
48
81
|
this.#logger = getAdapterLogger('lark', options.config.id);
|
|
49
82
|
this.#options = options;
|
|
50
83
|
this.#fetch = options.fetch ?? globalThis.fetch;
|
|
@@ -71,7 +104,6 @@ export class LarkEndpoint {
|
|
|
71
104
|
}));
|
|
72
105
|
}
|
|
73
106
|
await this.#refreshAccessToken();
|
|
74
|
-
this.#unregisterAgent = registerLarkAgentEndpoint(this.#options.config.id, this);
|
|
75
107
|
this.#routeReleases.push(...registerLarkWebhookRoutes(this.#options.http, this));
|
|
76
108
|
this.#logger.debug(formatCompact({
|
|
77
109
|
endpoint: this.#options.config.id,
|
|
@@ -95,8 +127,6 @@ export class LarkEndpoint {
|
|
|
95
127
|
this.#open = false;
|
|
96
128
|
for (const release of this.#routeReleases.splice(0))
|
|
97
129
|
release();
|
|
98
|
-
this.#unregisterAgent?.();
|
|
99
|
-
this.#unregisterAgent = undefined;
|
|
100
130
|
this.#started = false;
|
|
101
131
|
this.#logger.debug(formatCompact({ op: 'disconnect' }));
|
|
102
132
|
}
|
|
@@ -184,7 +214,7 @@ export class LarkEndpoint {
|
|
|
184
214
|
if (!this.#open)
|
|
185
215
|
return;
|
|
186
216
|
const conversation = larkInboundConversation(String(this.#options.id), msg);
|
|
187
|
-
void this
|
|
217
|
+
void this.emit('message.receive', {
|
|
188
218
|
conversation,
|
|
189
219
|
message: { conversation, id: generateMessageId(msg) },
|
|
190
220
|
content: formatInboundContent(msg),
|
|
@@ -202,6 +232,17 @@ export class LarkEndpoint {
|
|
|
202
232
|
}));
|
|
203
233
|
});
|
|
204
234
|
}
|
|
235
|
+
admitPlatform(name, event) {
|
|
236
|
+
if (!this.#open)
|
|
237
|
+
return;
|
|
238
|
+
void this.emitPlatform(name, event).catch((error) => {
|
|
239
|
+
this.#logger.warn(formatCompact({
|
|
240
|
+
op: 'lark_platform_event_failed',
|
|
241
|
+
event: name,
|
|
242
|
+
error: error instanceof Error ? error.message : String(error),
|
|
243
|
+
}));
|
|
244
|
+
});
|
|
245
|
+
}
|
|
205
246
|
async getUserInfo(userId, userIdType = 'open_id') {
|
|
206
247
|
try {
|
|
207
248
|
const data = await this.#request(`/contact/v3/users/${userId}`, {
|
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
|
-
export {
|
|
2
|
+
export { larkClient, type LarkClientEventMap } from './client.js';
|
|
3
3
|
export { checkLarkPlatformPermit, larkGroupPermitResolver, normalizeLarkSenderForPermit, platformPermit, } from './platform-permit.js';
|
|
4
|
-
export { LarkEndpoint, type LarkEndpointOptions, type LarkFetch, } from './endpoint.js';
|
|
4
|
+
export { LarkClient, LarkEndpoint, type LarkClientApi, 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
|
-
export {
|
|
2
|
+
export { larkClient } from './client.js';
|
|
3
3
|
export { checkLarkPlatformPermit, larkGroupPermitResolver, normalizeLarkSenderForPermit, platformPermit, } from './platform-permit.js';
|
|
4
|
-
export { LarkEndpoint, } from './endpoint.js';
|
|
4
|
+
export { LarkClient, LarkEndpoint, } from './endpoint.js';
|
|
5
5
|
export { buildImageUploadForm, readOutboundImageMedia, resolveMediaBinary, } from './media-upload.js';
|
|
6
6
|
export { registerLarkWebhookRoutes, handleLarkWebhookRequest, } from './webhook.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const larkEndpointCommands: import("@zhin.js/adapter").EndpointCommands<Readonly<import("@zhin.js/command").CommandDefinition<unknown, unknown, import("@zhin.js/command").CommandMessage>>>;
|
|
1
|
+
export declare const larkEndpointCommands: import("@zhin.js/adapter").EndpointCommands<Readonly<import("@zhin.js/command").CommandDefinition<unknown, unknown, import("@zhin.js/command").CommandMessage, string | undefined>>>;
|
package/lib/protocol.d.ts
CHANGED
|
@@ -105,7 +105,7 @@ export declare function resolveChatType(chatId?: string, chatType?: string): 'gr
|
|
|
105
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
|
-
/** Build inbound text for
|
|
108
|
+
/** Build inbound text for OutboundMessageService.receive. */
|
|
109
109
|
export declare function formatInboundContent(msg: LarkMessage): string;
|
|
110
110
|
/** 验签时间戳时效窗口(防重放)。 */
|
|
111
111
|
export declare const LARK_SIGNATURE_MAX_AGE_MS: number;
|
package/lib/protocol.js
CHANGED
|
@@ -69,7 +69,7 @@ export function resolveSender(msg) {
|
|
|
69
69
|
export function generateMessageId(msg) {
|
|
70
70
|
return msg.message_id || `${msg.create_time ?? Date.now()}`;
|
|
71
71
|
}
|
|
72
|
-
/** Build inbound text for
|
|
72
|
+
/** Build inbound text for OutboundMessageService.receive. */
|
|
73
73
|
export function formatInboundContent(msg) {
|
|
74
74
|
if (!msg.content || !msg.message_type)
|
|
75
75
|
return '';
|
package/lib/webhook.d.ts
CHANGED
|
@@ -7,6 +7,7 @@ import { type LarkMessage, type ResolvedLarkConfig } from './protocol.js';
|
|
|
7
7
|
export interface LarkWebhookHandler {
|
|
8
8
|
readonly config: ResolvedLarkConfig;
|
|
9
9
|
readonly isOpen: boolean;
|
|
10
|
+
admitPlatform(name: string, event: unknown): void;
|
|
10
11
|
admit(msg: LarkMessage): void;
|
|
11
12
|
}
|
|
12
13
|
export declare function registerLarkWebhookRoutes(http: HttpHost, handler: LarkWebhookHandler): HttpRouteRegistration[];
|
package/lib/webhook.js
CHANGED
|
@@ -44,6 +44,12 @@ export async function handleLarkWebhookRequest(request, response, handler) {
|
|
|
44
44
|
response.end(JSON.stringify({ code: 0, msg: 'success' }));
|
|
45
45
|
return;
|
|
46
46
|
}
|
|
47
|
+
if (handler.isOpen) {
|
|
48
|
+
const eventName = event.type === 'event_callback'
|
|
49
|
+
? String(event.event?.type ?? event.type)
|
|
50
|
+
: String(event.type ?? 'event');
|
|
51
|
+
handler.admitPlatform(eventName, event);
|
|
52
|
+
}
|
|
47
53
|
if (event.type === 'url_verification') {
|
|
48
54
|
response.writeHead(200, { 'Content-Type': 'application/json' });
|
|
49
55
|
response.end(JSON.stringify({ challenge: event.challenge }));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zhin.js/adapter-lark",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "7.0.1",
|
|
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,22 @@
|
|
|
34
34
|
"directory": "plugins/adapters/lark"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@zhin.js/adapter": "1.1
|
|
38
|
-
"@zhin.js/core": "1.5.
|
|
39
|
-
"@zhin.js/
|
|
37
|
+
"@zhin.js/adapter": "1.2.1",
|
|
38
|
+
"@zhin.js/core": "1.5.14",
|
|
39
|
+
"@zhin.js/feature-kit": "1.0.13",
|
|
40
|
+
"@zhin.js/host-http": "1.0.13",
|
|
40
41
|
"@zhin.js/im-contract": "1.0.4",
|
|
41
|
-
"@zhin.js/logger": "1.0.
|
|
42
|
+
"@zhin.js/logger": "1.0.77"
|
|
42
43
|
},
|
|
43
44
|
"peerDependencies": {
|
|
44
45
|
"zod": "^4.0.0",
|
|
45
|
-
"@zhin.js/adapter": "1.1
|
|
46
|
-
"@zhin.js/agent": "1.1.
|
|
47
|
-
"@zhin.js/command": "1.0.
|
|
48
|
-
"@zhin.js/core": "1.5.
|
|
49
|
-
"@zhin.js/host-http": "1.0.
|
|
50
|
-
"@zhin.js/permission": "1.0.
|
|
51
|
-
"zhin.js": "6.0.
|
|
46
|
+
"@zhin.js/adapter": "1.2.1",
|
|
47
|
+
"@zhin.js/agent": "1.1.16",
|
|
48
|
+
"@zhin.js/command": "1.0.16",
|
|
49
|
+
"@zhin.js/core": "1.5.14",
|
|
50
|
+
"@zhin.js/host-http": "1.0.13",
|
|
51
|
+
"@zhin.js/permission": "1.0.4",
|
|
52
|
+
"zhin.js": "6.0.14"
|
|
52
53
|
},
|
|
53
54
|
"peerDependenciesMeta": {
|
|
54
55
|
"@zhin.js/agent": {
|
|
@@ -69,8 +70,8 @@
|
|
|
69
70
|
"typescript": "^6.0.3",
|
|
70
71
|
"vitest": "^4.1.10",
|
|
71
72
|
"zod": "^4.4.3",
|
|
72
|
-
"@zhin.js/agent": "1.1.
|
|
73
|
-
"zhin.js": "6.0.
|
|
73
|
+
"@zhin.js/agent": "1.1.16",
|
|
74
|
+
"zhin.js": "6.0.14"
|
|
74
75
|
},
|
|
75
76
|
"files": [
|
|
76
77
|
"adapters",
|
package/src/client.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { defineEndpointClient } from 'zhin.js/adapter';
|
|
2
|
+
import type { LarkClient } from './endpoint.js';
|
|
3
|
+
import type { LarkEventBody } from './protocol.js';
|
|
4
|
+
|
|
5
|
+
export type LarkClientEventMap = Record<string, LarkEventBody>;
|
|
6
|
+
|
|
7
|
+
declare module '@zhin.js/feature-kit' {
|
|
8
|
+
interface AdapterClientRegistry {
|
|
9
|
+
readonly lark: {
|
|
10
|
+
readonly client: LarkClient;
|
|
11
|
+
readonly events: LarkClientEventMap;
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const larkClient = defineEndpointClient<LarkClient, LarkClientEventMap>('lark');
|
package/src/endpoint.ts
CHANGED
|
@@ -1,14 +1,19 @@
|
|
|
1
|
+
import { Endpoint } from 'zhin.js/adapter';
|
|
1
2
|
/**
|
|
2
3
|
* LarkEndpoint — lifecycle, outbound, admit, OpenAPI helpers for agent tools.
|
|
3
4
|
*/
|
|
4
5
|
import { readFile } from 'node:fs/promises';
|
|
5
6
|
import { basename } from 'node:path';
|
|
6
|
-
import
|
|
7
|
-
|
|
7
|
+
import {
|
|
8
|
+
createRecallEndpointControl,
|
|
9
|
+
type EndpointControl,
|
|
10
|
+
type EndpointGroup,
|
|
11
|
+
type EndpointManagement,
|
|
12
|
+
type EndpointSendRequest,
|
|
13
|
+
} from 'zhin.js/adapter';
|
|
8
14
|
import type { HttpHost, HttpRouteRegistration } from '@zhin.js/host-http';
|
|
9
15
|
import { formatCompact, getAdapterLogger } from '@zhin.js/logger';
|
|
10
16
|
import type { CapabilityId } from 'zhin.js';
|
|
11
|
-
import { registerLarkAgentEndpoint } from './lark-agent-deps.js';
|
|
12
17
|
import {
|
|
13
18
|
buildImageUploadForm,
|
|
14
19
|
readOutboundImageMedia,
|
|
@@ -48,14 +53,59 @@ export type LarkFetch = (
|
|
|
48
53
|
|
|
49
54
|
export interface LarkEndpointOptions {
|
|
50
55
|
readonly id: CapabilityId;
|
|
51
|
-
readonly gateway: MessageGateway;
|
|
52
|
-
readonly sideEvents?: SideEventGateway;
|
|
53
56
|
readonly http: HttpHost;
|
|
54
57
|
readonly config: ResolvedLarkConfig;
|
|
55
58
|
readonly fetch?: LarkFetch;
|
|
56
59
|
}
|
|
57
60
|
|
|
58
|
-
export
|
|
61
|
+
export interface LarkClientApi {
|
|
62
|
+
getUserInfo(userId: string, userIdType?: 'open_id' | 'user_id' | 'union_id'): Promise<unknown>;
|
|
63
|
+
getChatInfo(chatId: string): Promise<unknown>;
|
|
64
|
+
uploadFile(filePath: string, fileType?: 'image' | 'file' | 'video' | 'audio'): Promise<string | null>;
|
|
65
|
+
createChat(name: string, userIds: string[], ownerId?: string): Promise<string | null>;
|
|
66
|
+
updateChatInfo(chatId: string, options: { name?: string; description?: string }): Promise<boolean>;
|
|
67
|
+
addChatMembers(chatId: string, userIds: string[]): Promise<boolean>;
|
|
68
|
+
removeChatMembers(chatId: string, userIds: string[]): Promise<boolean>;
|
|
69
|
+
getChatMembers(chatId: string): Promise<unknown[]>;
|
|
70
|
+
dissolveChat(chatId: string): Promise<boolean>;
|
|
71
|
+
setChatManagers(chatId: string, userIds: string[]): Promise<boolean>;
|
|
72
|
+
removeChatManagers(chatId: string, userIds: string[]): Promise<boolean>;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** Feishu/Lark OpenAPI client surface carried by every Endpoint event. */
|
|
76
|
+
export class LarkClient implements LarkClientApi {
|
|
77
|
+
constructor(readonly api: LarkClientApi) {}
|
|
78
|
+
getUserInfo = (id: string, type?: 'open_id' | 'user_id' | 'union_id') =>
|
|
79
|
+
this.api.getUserInfo(id, type);
|
|
80
|
+
getChatInfo = (id: string) => this.api.getChatInfo(id);
|
|
81
|
+
uploadFile = (path: string, type?: 'image' | 'file' | 'video' | 'audio') =>
|
|
82
|
+
this.api.uploadFile(path, type);
|
|
83
|
+
createChat = (name: string, users: string[], owner?: string) =>
|
|
84
|
+
this.api.createChat(name, users, owner);
|
|
85
|
+
updateChatInfo = (id: string, options: { name?: string; description?: string }) =>
|
|
86
|
+
this.api.updateChatInfo(id, options);
|
|
87
|
+
addChatMembers = (id: string, users: string[]) => this.api.addChatMembers(id, users);
|
|
88
|
+
removeChatMembers = (id: string, users: string[]) => this.api.removeChatMembers(id, users);
|
|
89
|
+
getChatMembers = (id: string) => this.api.getChatMembers(id);
|
|
90
|
+
dissolveChat = (id: string) => this.api.dissolveChat(id);
|
|
91
|
+
setChatManagers = (id: string, users: string[]) => this.api.setChatManagers(id, users);
|
|
92
|
+
removeChatManagers = (id: string, users: string[]) => this.api.removeChatManagers(id, users);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export class LarkEndpoint extends Endpoint<LarkClient> {
|
|
96
|
+
readonly client = new LarkClient({
|
|
97
|
+
getUserInfo: (id, type) => this.getUserInfo(id, type),
|
|
98
|
+
getChatInfo: (id) => this.getChatInfo(id),
|
|
99
|
+
uploadFile: (path, type) => this.uploadFile(path, type),
|
|
100
|
+
createChat: (name, users, owner) => this.createChat(name, users, owner),
|
|
101
|
+
updateChatInfo: (id, options) => this.updateChatInfo(id, options),
|
|
102
|
+
addChatMembers: (id, users) => this.addChatMembers(id, users),
|
|
103
|
+
removeChatMembers: (id, users) => this.removeChatMembers(id, users),
|
|
104
|
+
getChatMembers: (id) => this.getChatMembers(id),
|
|
105
|
+
dissolveChat: (id) => this.dissolveChat(id),
|
|
106
|
+
setChatManagers: (id, users) => this.setChatManagers(id, users),
|
|
107
|
+
removeChatManagers: (id, users) => this.removeChatManagers(id, users),
|
|
108
|
+
});
|
|
59
109
|
readonly #logger!: ReturnType<typeof getAdapterLogger>;
|
|
60
110
|
|
|
61
111
|
readonly #options: LarkEndpointOptions;
|
|
@@ -86,14 +136,15 @@ export class LarkEndpoint implements EndpointInstance {
|
|
|
86
136
|
{ member_id_type: 'open_id' },
|
|
87
137
|
),
|
|
88
138
|
});
|
|
139
|
+
readonly control: EndpointControl = createRecallEndpointControl((id) => this.recallMessage(id));
|
|
89
140
|
#routeReleases: HttpRouteRegistration[] = [];
|
|
90
141
|
#accessToken: AccessToken = { token: '', expires_in: 0, timestamp: 0 };
|
|
91
142
|
#refreshPromise: Promise<string> | null = null;
|
|
92
143
|
#open = false;
|
|
93
144
|
#started = false;
|
|
94
|
-
#unregisterAgent?: () => void;
|
|
95
145
|
|
|
96
146
|
constructor(options: LarkEndpointOptions) {
|
|
147
|
+
super();
|
|
97
148
|
this.#logger = getAdapterLogger('lark', options.config.id);
|
|
98
149
|
this.#options = options;
|
|
99
150
|
this.#fetch = options.fetch ?? globalThis.fetch;
|
|
@@ -122,7 +173,6 @@ export class LarkEndpoint implements EndpointInstance {
|
|
|
122
173
|
}));
|
|
123
174
|
}
|
|
124
175
|
await this.#refreshAccessToken();
|
|
125
|
-
this.#unregisterAgent = registerLarkAgentEndpoint(this.#options.config.id, this);
|
|
126
176
|
this.#routeReleases.push(...registerLarkWebhookRoutes(this.#options.http, this));
|
|
127
177
|
this.#logger.debug(formatCompact({
|
|
128
178
|
endpoint: this.#options.config.id,
|
|
@@ -147,8 +197,6 @@ export class LarkEndpoint implements EndpointInstance {
|
|
|
147
197
|
async stop(): Promise<void> {
|
|
148
198
|
this.#open = false;
|
|
149
199
|
for (const release of this.#routeReleases.splice(0)) release();
|
|
150
|
-
this.#unregisterAgent?.();
|
|
151
|
-
this.#unregisterAgent = undefined;
|
|
152
200
|
this.#started = false;
|
|
153
201
|
this.#logger.debug(formatCompact({ op: 'disconnect' }));
|
|
154
202
|
}
|
|
@@ -234,7 +282,7 @@ export class LarkEndpoint implements EndpointInstance {
|
|
|
234
282
|
admit(msg: LarkMessage): void {
|
|
235
283
|
if (!this.#open) return;
|
|
236
284
|
const conversation = larkInboundConversation(String(this.#options.id), msg);
|
|
237
|
-
void this
|
|
285
|
+
void this.emit('message.receive', {
|
|
238
286
|
conversation,
|
|
239
287
|
message: { conversation, id: generateMessageId(msg) },
|
|
240
288
|
content: formatInboundContent(msg),
|
|
@@ -253,6 +301,17 @@ export class LarkEndpoint implements EndpointInstance {
|
|
|
253
301
|
});
|
|
254
302
|
}
|
|
255
303
|
|
|
304
|
+
admitPlatform(name: string, event: unknown): void {
|
|
305
|
+
if (!this.#open) return;
|
|
306
|
+
void this.emitPlatform(name, event).catch((error) => {
|
|
307
|
+
this.#logger.warn(formatCompact({
|
|
308
|
+
op: 'lark_platform_event_failed',
|
|
309
|
+
event: name,
|
|
310
|
+
error: error instanceof Error ? error.message : String(error),
|
|
311
|
+
}));
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
|
|
256
315
|
async getUserInfo(
|
|
257
316
|
userId: string,
|
|
258
317
|
userIdType: 'open_id' | 'user_id' | 'union_id' = 'open_id',
|
package/src/index.ts
CHANGED
|
@@ -20,13 +20,7 @@ export {
|
|
|
20
20
|
type ResolvedLarkConfig,
|
|
21
21
|
} from './protocol.js';
|
|
22
22
|
|
|
23
|
-
export {
|
|
24
|
-
getLarkAgentDeps,
|
|
25
|
-
registerLarkAgentEndpoint,
|
|
26
|
-
setLarkAgentDeps,
|
|
27
|
-
type LarkAgentDeps,
|
|
28
|
-
type LarkAgentEndpoint,
|
|
29
|
-
} from './lark-agent-deps.js';
|
|
23
|
+
export { larkClient, type LarkClientEventMap } from './client.js';
|
|
30
24
|
|
|
31
25
|
export {
|
|
32
26
|
checkLarkPlatformPermit,
|
|
@@ -36,7 +30,9 @@ export {
|
|
|
36
30
|
} from './platform-permit.js';
|
|
37
31
|
|
|
38
32
|
export {
|
|
33
|
+
LarkClient,
|
|
39
34
|
LarkEndpoint,
|
|
35
|
+
type LarkClientApi,
|
|
40
36
|
type LarkEndpointOptions,
|
|
41
37
|
type LarkFetch,
|
|
42
38
|
} from './endpoint.js';
|
package/src/protocol.ts
CHANGED
|
@@ -182,7 +182,7 @@ export function generateMessageId(msg: LarkMessage): string {
|
|
|
182
182
|
return msg.message_id || `${msg.create_time ?? Date.now()}`;
|
|
183
183
|
}
|
|
184
184
|
|
|
185
|
-
/** Build inbound text for
|
|
185
|
+
/** Build inbound text for OutboundMessageService.receive. */
|
|
186
186
|
export function formatInboundContent(msg: LarkMessage): string {
|
|
187
187
|
if (!msg.content || !msg.message_type) return '';
|
|
188
188
|
try {
|
package/src/webhook.ts
CHANGED
|
@@ -19,6 +19,7 @@ const logger = getLogger('lark');
|
|
|
19
19
|
export interface LarkWebhookHandler {
|
|
20
20
|
readonly config: ResolvedLarkConfig;
|
|
21
21
|
readonly isOpen: boolean;
|
|
22
|
+
admitPlatform(name: string, event: unknown): void;
|
|
22
23
|
admit(msg: LarkMessage): void;
|
|
23
24
|
}
|
|
24
25
|
|
|
@@ -78,6 +79,13 @@ export async function handleLarkWebhookRequest(
|
|
|
78
79
|
return;
|
|
79
80
|
}
|
|
80
81
|
|
|
82
|
+
if (handler.isOpen) {
|
|
83
|
+
const eventName = event.type === 'event_callback'
|
|
84
|
+
? String(event.event?.type ?? event.type)
|
|
85
|
+
: String(event.type ?? 'event');
|
|
86
|
+
handler.admitPlatform(eventName, event);
|
|
87
|
+
}
|
|
88
|
+
|
|
81
89
|
if (event.type === 'url_verification') {
|
|
82
90
|
response.writeHead(200, { 'Content-Type': 'application/json' });
|
|
83
91
|
response.end(JSON.stringify({ challenge: event.challenge }));
|
package/lib/lark-agent-deps.d.ts
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Agent tool deps for lark (user / chat / managers / upload).
|
|
3
|
-
* Endpoints register themselves on start; tools look up by endpoint id.
|
|
4
|
-
*/
|
|
5
|
-
export interface LarkAgentEndpoint {
|
|
6
|
-
getUserInfo(userId: string, userIdType?: 'open_id' | 'user_id' | 'union_id'): Promise<unknown>;
|
|
7
|
-
getChatInfo(chatId: string): Promise<unknown>;
|
|
8
|
-
uploadFile(filePath: string, fileType?: 'image' | 'file' | 'video' | 'audio'): Promise<string | null>;
|
|
9
|
-
createChat(name: string, userIds: string[], ownerId?: string): Promise<string | null>;
|
|
10
|
-
updateChatInfo(chatId: string, options: {
|
|
11
|
-
name?: string;
|
|
12
|
-
description?: string;
|
|
13
|
-
}): Promise<boolean>;
|
|
14
|
-
addChatMembers(chatId: string, userIds: string[]): Promise<boolean>;
|
|
15
|
-
removeChatMembers(chatId: string, userIds: string[]): Promise<boolean>;
|
|
16
|
-
getChatMembers(chatId: string): Promise<unknown[]>;
|
|
17
|
-
dissolveChat(chatId: string): Promise<boolean>;
|
|
18
|
-
setChatManagers(chatId: string, userIds: string[]): Promise<boolean>;
|
|
19
|
-
removeChatManagers(chatId: string, userIds: string[]): Promise<boolean>;
|
|
20
|
-
}
|
|
21
|
-
export interface LarkAgentDeps {
|
|
22
|
-
getEndpoint: (endpointKey: string) => LarkAgentEndpoint;
|
|
23
|
-
}
|
|
24
|
-
export declare function registerLarkAgentEndpoint(endpointKey: string, endpoint: LarkAgentEndpoint): () => void;
|
|
25
|
-
/** Optional override used by tests / transitional callers. Pass `null` to clear. */
|
|
26
|
-
export declare function setLarkAgentDeps(deps: LarkAgentDeps | null): void;
|
|
27
|
-
export declare function getLarkAgentDeps(): LarkAgentDeps;
|
package/lib/lark-agent-deps.js
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Agent tool deps for lark (user / chat / managers / upload).
|
|
3
|
-
* Endpoints register themselves on start; tools look up by endpoint id.
|
|
4
|
-
*/
|
|
5
|
-
const endpoints = new Map();
|
|
6
|
-
let override = null;
|
|
7
|
-
export function registerLarkAgentEndpoint(endpointKey, endpoint) {
|
|
8
|
-
endpoints.set(endpointKey, endpoint);
|
|
9
|
-
return () => {
|
|
10
|
-
if (endpoints.get(endpointKey) === endpoint) {
|
|
11
|
-
endpoints.delete(endpointKey);
|
|
12
|
-
}
|
|
13
|
-
};
|
|
14
|
-
}
|
|
15
|
-
/** Optional override used by tests / transitional callers. Pass `null` to clear. */
|
|
16
|
-
export function setLarkAgentDeps(deps) {
|
|
17
|
-
override = deps;
|
|
18
|
-
}
|
|
19
|
-
export function getLarkAgentDeps() {
|
|
20
|
-
if (override)
|
|
21
|
-
return override;
|
|
22
|
-
return {
|
|
23
|
-
getEndpoint(endpointKey) {
|
|
24
|
-
const endpoint = endpoints.get(endpointKey);
|
|
25
|
-
if (!endpoint)
|
|
26
|
-
throw new Error(`Endpoint ${endpointKey} 不存在`);
|
|
27
|
-
return endpoint;
|
|
28
|
-
},
|
|
29
|
-
};
|
|
30
|
-
}
|
package/src/lark-agent-deps.ts
DELETED
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Agent tool deps for lark (user / chat / managers / upload).
|
|
3
|
-
* Endpoints register themselves on start; tools look up by endpoint id.
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
export interface LarkAgentEndpoint {
|
|
7
|
-
getUserInfo(
|
|
8
|
-
userId: string,
|
|
9
|
-
userIdType?: 'open_id' | 'user_id' | 'union_id',
|
|
10
|
-
): Promise<unknown>;
|
|
11
|
-
getChatInfo(chatId: string): Promise<unknown>;
|
|
12
|
-
uploadFile(
|
|
13
|
-
filePath: string,
|
|
14
|
-
fileType?: 'image' | 'file' | 'video' | 'audio',
|
|
15
|
-
): Promise<string | null>;
|
|
16
|
-
createChat(name: string, userIds: string[], ownerId?: string): Promise<string | null>;
|
|
17
|
-
updateChatInfo(
|
|
18
|
-
chatId: string,
|
|
19
|
-
options: { name?: string; description?: string },
|
|
20
|
-
): Promise<boolean>;
|
|
21
|
-
addChatMembers(chatId: string, userIds: string[]): Promise<boolean>;
|
|
22
|
-
removeChatMembers(chatId: string, userIds: string[]): Promise<boolean>;
|
|
23
|
-
getChatMembers(chatId: string): Promise<unknown[]>;
|
|
24
|
-
dissolveChat(chatId: string): Promise<boolean>;
|
|
25
|
-
setChatManagers(chatId: string, userIds: string[]): Promise<boolean>;
|
|
26
|
-
removeChatManagers(chatId: string, userIds: string[]): Promise<boolean>;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export interface LarkAgentDeps {
|
|
30
|
-
getEndpoint: (endpointKey: string) => LarkAgentEndpoint;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
const endpoints = new Map<string, LarkAgentEndpoint>();
|
|
34
|
-
let override: LarkAgentDeps | null = null;
|
|
35
|
-
|
|
36
|
-
export function registerLarkAgentEndpoint(
|
|
37
|
-
endpointKey: string,
|
|
38
|
-
endpoint: LarkAgentEndpoint,
|
|
39
|
-
): () => void {
|
|
40
|
-
endpoints.set(endpointKey, endpoint);
|
|
41
|
-
return () => {
|
|
42
|
-
if (endpoints.get(endpointKey) === endpoint) {
|
|
43
|
-
endpoints.delete(endpointKey);
|
|
44
|
-
}
|
|
45
|
-
};
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/** Optional override used by tests / transitional callers. Pass `null` to clear. */
|
|
49
|
-
export function setLarkAgentDeps(deps: LarkAgentDeps | null): void {
|
|
50
|
-
override = deps;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
export function getLarkAgentDeps(): LarkAgentDeps {
|
|
54
|
-
if (override) return override;
|
|
55
|
-
return {
|
|
56
|
-
getEndpoint(endpointKey) {
|
|
57
|
-
const endpoint = endpoints.get(endpointKey);
|
|
58
|
-
if (!endpoint) throw new Error(`Endpoint ${endpointKey} 不存在`);
|
|
59
|
-
return endpoint;
|
|
60
|
-
},
|
|
61
|
-
};
|
|
62
|
-
}
|