@zhin.js/adapter-dingtalk 1.0.81 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/CHANGELOG.md +617 -3
  2. package/README.md +67 -333
  3. package/adapters/dingtalk.js +33 -0
  4. package/adapters/dingtalk.ts +38 -0
  5. package/agent/PERMITS.md +19 -0
  6. package/agent/tools/add_chat_members.ts +20 -0
  7. package/agent/tools/create_chat.ts +21 -0
  8. package/agent/tools/dept_info.ts +15 -0
  9. package/agent/tools/get_dept_users.ts +16 -0
  10. package/agent/tools/get_user.ts +15 -0
  11. package/agent/tools/list_departments.ts +16 -0
  12. package/agent/tools/send_work_notice.ts +18 -0
  13. package/agent/tools/update_chat.ts +25 -0
  14. package/commands/endpoint/add/[id].js +3 -0
  15. package/commands/endpoint/add/[id].ts +3 -0
  16. package/commands/endpoint/list.js +3 -0
  17. package/commands/endpoint/list.ts +3 -0
  18. package/commands/endpoint/remove/[id].js +3 -0
  19. package/commands/endpoint/remove/[id].ts +3 -0
  20. package/lib/client.d.ts +12 -0
  21. package/lib/client.js +2 -0
  22. package/lib/dingtalk-endpoint-commands.d.ts +1 -0
  23. package/lib/dingtalk-endpoint-commands.js +18 -0
  24. package/lib/dingtalk-runtime-state.d.ts +1 -0
  25. package/lib/dingtalk-runtime-state.js +6 -0
  26. package/lib/endpoint.d.ts +72 -0
  27. package/lib/endpoint.js +353 -0
  28. package/lib/index.d.ts +5 -15
  29. package/lib/index.js +5 -214
  30. package/lib/platform-permit.d.ts +14 -0
  31. package/lib/platform-permit.js +33 -0
  32. package/lib/protocol.d.ts +134 -0
  33. package/lib/protocol.js +265 -0
  34. package/lib/webhook.d.ts +13 -0
  35. package/lib/webhook.js +47 -0
  36. package/package.json +64 -18
  37. package/plugin.js +19 -0
  38. package/schema.json +92 -0
  39. package/src/client.ts +16 -0
  40. package/src/dingtalk-endpoint-commands.ts +19 -0
  41. package/src/dingtalk-runtime-state.ts +7 -0
  42. package/src/endpoint.ts +439 -0
  43. package/src/index.ts +44 -228
  44. package/src/platform-permit.ts +48 -0
  45. package/src/protocol.ts +385 -0
  46. package/src/webhook.ts +75 -0
  47. package/lib/adapter.d.ts +0 -16
  48. package/lib/adapter.d.ts.map +0 -1
  49. package/lib/adapter.js +0 -37
  50. package/lib/adapter.js.map +0 -1
  51. package/lib/bot.d.ts +0 -46
  52. package/lib/bot.d.ts.map +0 -1
  53. package/lib/bot.js +0 -539
  54. package/lib/bot.js.map +0 -1
  55. package/lib/index.d.ts.map +0 -1
  56. package/lib/index.js.map +0 -1
  57. package/lib/types.d.ts +0 -58
  58. package/lib/types.d.ts.map +0 -1
  59. package/lib/types.js +0 -5
  60. package/lib/types.js.map +0 -1
  61. package/plugin.yml +0 -3
  62. package/src/adapter.ts +0 -44
  63. package/src/bot.ts +0 -590
  64. package/src/types.ts +0 -56
  65. /package/{skills/dingtalk/SKILL.md → agent/skills/dingtalk.md} +0 -0
package/lib/webhook.js ADDED
@@ -0,0 +1,47 @@
1
+ import { formatCompact, getLogger } from '@zhin.js/logger';
2
+ import { headerValue, readTextBody, verifySignature, } from './protocol.js';
3
+ const logger = getLogger('dingtalk');
4
+ export function registerDingTalkWebhookRoutes(http, handler) {
5
+ const path = handler.config.webhookPath;
6
+ return [
7
+ http.route('POST', path, async (request, response) => {
8
+ await handleDingTalkWebhookRequest(request, response, handler);
9
+ }, { summary: 'DingTalk robot webhook', tags: ['dingtalk'] }),
10
+ ];
11
+ }
12
+ export async function handleDingTalkWebhookRequest(request, response, handler) {
13
+ try {
14
+ // DingTalk outgoing callbacks put timestamp/sign on the URL query;
15
+ // headers are accepted as a fallback for legacy senders.
16
+ const query = new URL(request.url ?? '/', 'http://localhost').searchParams;
17
+ const timestamp = query.get('timestamp') || headerValue(request.headers, 'timestamp');
18
+ const sign = query.get('sign') || headerValue(request.headers, 'sign');
19
+ // Missing credentials must never be admitted: verify unconditionally.
20
+ if (!timestamp || !sign || !verifySignature(handler.config.appSecret, timestamp, sign)) {
21
+ logger.warn(formatCompact({ op: 'webhook', ok: false, error: 'invalid signature' }));
22
+ response.writeHead(403, { 'Content-Type': 'application/json' });
23
+ response.end(JSON.stringify({ code: -1, msg: 'Forbidden' }));
24
+ return;
25
+ }
26
+ const rawBody = await readTextBody(request);
27
+ let event;
28
+ try {
29
+ event = JSON.parse(rawBody);
30
+ }
31
+ catch {
32
+ response.writeHead(200, { 'Content-Type': 'application/json' });
33
+ response.end(JSON.stringify({ code: 0, msg: 'success' }));
34
+ return;
35
+ }
36
+ if (event.msgtype && handler.isOpen) {
37
+ handler.admit(event);
38
+ }
39
+ response.writeHead(200, { 'Content-Type': 'application/json' });
40
+ response.end(JSON.stringify({ code: 0, msg: 'success' }));
41
+ }
42
+ catch (error) {
43
+ logger.error('Webhook error:', error);
44
+ response.writeHead(500, { 'Content-Type': 'application/json' });
45
+ response.end(JSON.stringify({ code: -1, msg: 'Internal Server Error' }));
46
+ }
47
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@zhin.js/adapter-dingtalk",
3
- "version": "1.0.81",
4
- "description": "Zhin.js adapter for DingTalk (钉钉)",
3
+ "version": "1.1.0",
4
+ "description": "Zhin.js DingTalk (钉钉) adapter for Plugin Runtime (HTTP webhook)",
5
5
  "type": "module",
6
6
  "main": "./lib/index.js",
7
7
  "types": "./lib/index.d.ts",
@@ -32,39 +32,85 @@
32
32
  "type": "git",
33
33
  "directory": "plugins/adapters/dingtalk"
34
34
  },
35
- "devDependencies": {
36
- "@types/koa": "^3.0.3",
37
- "@types/node": "^25.9.1",
38
- "typescript": "^6.0.3",
39
- "zhin.js": "1.0.93"
35
+ "dependencies": {
36
+ "@zhin.js/adapter": "1.1.12",
37
+ "@zhin.js/core": "1.1.35",
38
+ "@zhin.js/feature-kit": "1.1.0",
39
+ "@zhin.js/host-http": "1.1.0",
40
+ "@zhin.js/im-contract": "1.1.0",
41
+ "@zhin.js/logger": "1.1.0"
40
42
  },
41
43
  "peerDependencies": {
42
- "@zhin.js/host-router": "0.0.3",
43
- "zhin.js": "1.0.93"
44
+ "zod": "^4.0.0",
45
+ "@zhin.js/adapter": "^1.1.12",
46
+ "@zhin.js/agent": "^1.1.23",
47
+ "@zhin.js/command": "^1.1.0",
48
+ "@zhin.js/core": "^1.1.35",
49
+ "@zhin.js/host-http": "^1.1.0",
50
+ "@zhin.js/permission": "^1.1.0",
51
+ "zhin.js": "^1.1.0"
44
52
  },
45
53
  "peerDependenciesMeta": {
46
- "@zhin.js/host-router": {
47
- "optional": false
54
+ "@zhin.js/agent": {
55
+ "optional": true
56
+ },
57
+ "@zhin.js/command": {
58
+ "optional": true
59
+ },
60
+ "zhin.js": {
61
+ "optional": true
62
+ },
63
+ "zod": {
64
+ "optional": true
48
65
  }
49
66
  },
67
+ "devDependencies": {
68
+ "@types/node": "^26.1.2",
69
+ "typescript": "^6.0.3",
70
+ "vitest": "^4.1.10",
71
+ "zod": "^4.4.3",
72
+ "@zhin.js/agent": "1.1.23",
73
+ "zhin.js": "1.1.0"
74
+ },
50
75
  "files": [
76
+ "adapters",
77
+ "commands",
78
+ "plugin.js",
79
+ "schema.json",
51
80
  "src",
52
81
  "lib",
53
- "client",
54
- "dist",
55
- "skills",
56
- "plugin.yml",
82
+ "agent",
57
83
  "README.md",
58
- "node",
59
84
  "CHANGELOG.md"
60
85
  ],
61
86
  "publishConfig": {
62
87
  "access": "public",
63
88
  "registry": "https://registry.npmjs.org"
64
89
  },
90
+ "engines": {
91
+ "node": "^20.19.0 || >=22.12.0"
92
+ },
93
+ "zhin": {
94
+ "protocol": 1,
95
+ "type": "plugin",
96
+ "entry": "./plugin.js",
97
+ "engine": "^1.0.0",
98
+ "runtime": "trusted",
99
+ "features": [
100
+ {
101
+ "package": "@zhin.js/adapter",
102
+ "api": "^1.0.0"
103
+ },
104
+ {
105
+ "package": "@zhin.js/command",
106
+ "api": "^1.0.0"
107
+ }
108
+ ],
109
+ "plugins": []
110
+ },
65
111
  "scripts": {
66
- "build": "pnpm build:node",
112
+ "build": "tsc",
67
113
  "clean": "rimraf lib",
68
- "build:node": "tsc"
114
+ "test": "NODE_OPTIONS=--experimental-strip-types vitest run --root ../../.. plugins/adapters/dingtalk/tests"
69
115
  }
70
116
  }
package/plugin.js ADDED
@@ -0,0 +1,19 @@
1
+ // Generated by build-plugin-runtime-entries.mjs. Do not edit.
2
+ import { createEndpointRuntimeState } from 'zhin.js/adapter';
3
+ import { definePlugin } from 'zhin.js';
4
+ import { permissionHostToken } from '@zhin.js/permission';
5
+ import { checkDingtalkPlatformPermit } from "./lib/platform-permit.js";
6
+ import { dingtalkRuntimeStateToken } from "./lib/dingtalk-runtime-state.js";
7
+ export default definePlugin({
8
+ name: 'dingtalk',
9
+ metadata: {
10
+ displayName: 'DingTalk (钉钉) Adapter',
11
+ },
12
+ setup(context) {
13
+ context.resources.provide(dingtalkRuntimeStateToken, createEndpointRuntimeState());
14
+ if (context.resources.has(permissionHostToken)) {
15
+ const host = context.resources.use(permissionHostToken);
16
+ return host.registerPlatform('dingtalk', checkDingtalkPlatformPermit);
17
+ }
18
+ },
19
+ });
package/schema.json ADDED
@@ -0,0 +1,92 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "type": "object",
4
+ "additionalProperties": false,
5
+ "properties": {
6
+ "apiBaseUrl": {
7
+ "type": "string",
8
+ "default": "https://oapi.dingtalk.com"
9
+ },
10
+ "master": {
11
+ "type": [
12
+ "string",
13
+ "number"
14
+ ],
15
+ "description": "框架 master(DingTalk userid;AI/工具权限、endpoint 管理)。endpoints[i].master 可逐项覆盖"
16
+ },
17
+ "trusted": {
18
+ "type": "array",
19
+ "items": {
20
+ "type": [
21
+ "string",
22
+ "number"
23
+ ],
24
+ "description": "Trusted DingTalk userid"
25
+ },
26
+ "description": "框架 trusted 用户列表(弱于 master)。endpoints[i].trusted 可逐项追加"
27
+ },
28
+ "endpoints": {
29
+ "type": "array",
30
+ "description": "多账号:一个插件实例挂多个 endpoint。每项与顶层字段同构(id 必填,其余覆盖顶层)",
31
+ "items": {
32
+ "type": "object",
33
+ "additionalProperties": true,
34
+ "properties": {
35
+ "master": {
36
+ "type": [
37
+ "string",
38
+ "number"
39
+ ],
40
+ "description": "本 endpoint 的框架 master(DingTalk userid);覆盖顶层 master"
41
+ },
42
+ "trusted": {
43
+ "type": "array",
44
+ "items": {
45
+ "type": [
46
+ "string",
47
+ "number"
48
+ ],
49
+ "description": "Trusted DingTalk userid"
50
+ },
51
+ "description": "本 endpoint 的 trusted 列表"
52
+ },
53
+ "appKey": {
54
+ "type": "string",
55
+ "description": "Dingtalk app key"
56
+ },
57
+ "appSecret": {
58
+ "type": "string",
59
+ "description": "Dingtalk app secret"
60
+ },
61
+ "webhookPath": {
62
+ "type": "string",
63
+ "description": "Dingtalk webhook path"
64
+ },
65
+ "robotCode": {
66
+ "type": "string",
67
+ "description": "Dingtalk robot code"
68
+ },
69
+ "id": {
70
+ "type": "string",
71
+ "description": "Dingtalk bot name"
72
+ }
73
+ },
74
+ "required": [
75
+ "id",
76
+ "appKey",
77
+ "appSecret",
78
+ "webhookPath",
79
+ "robotCode"
80
+ ]
81
+ }
82
+ },
83
+ "commandPrefix": {
84
+ "type": "string",
85
+ "default": "",
86
+ "description": "命令前缀(默认 '' 无前缀,任意文本按命令匹配;如 '/' 要求 / 开头)。endpoints[i] 可逐项覆盖"
87
+ }
88
+ },
89
+ "required": [
90
+ "endpoints"
91
+ ]
92
+ }
package/src/client.ts ADDED
@@ -0,0 +1,16 @@
1
+ import { defineEndpointClient } from 'zhin.js/adapter';
2
+ import type { DingTalkClient } from './endpoint.js';
3
+ import type { DingTalkEvent } from './protocol.js';
4
+
5
+ export type DingtalkClientEventMap = Record<string, DingTalkEvent>;
6
+
7
+ declare module '@zhin.js/feature-kit' {
8
+ interface AdapterClientRegistry {
9
+ readonly dingtalk: {
10
+ readonly client: DingTalkClient;
11
+ readonly events: DingtalkClientEventMap;
12
+ };
13
+ }
14
+ }
15
+
16
+ export const dingtalkClient = defineEndpointClient<DingTalkClient, DingtalkClientEventMap>('dingtalk');
@@ -0,0 +1,19 @@
1
+ /**
2
+ * `dingtalk.endpoint` 命令族:由 @zhin.js/adapter 的 createEndpointCommands 套件生成。
3
+ * commands/endpoint/ 下的 list / add / remove 直接默认导出这三项。
4
+ */
5
+ import { createEndpointCommands } from 'zhin.js/adapter';
6
+ import { defineCommand } from 'zhin.js/command';
7
+ import { dingtalkRuntimeStateToken } from './dingtalk-runtime-state.js';
8
+
9
+ export const dingtalkEndpointCommands = createEndpointCommands({
10
+ adapterKey: 'dingtalk',
11
+ adapterDisplayName: 'DingTalk',
12
+ fields: [
13
+ { key: 'appKey', required: true, env: true, description: '钉钉 AppKey' },
14
+ { key: 'appSecret', required: true, env: true, description: '钉钉 AppSecret' },
15
+ { key: 'robotCode', required: true, env: true, description: '机器人 Code(出站 senderStaffId)' },
16
+ ],
17
+ running: (use) => use(dingtalkRuntimeStateToken).endpoints.values(),
18
+ describeEntry: (entry) => `appKey: ${String(entry.appKey)}`,
19
+ }, defineCommand);
@@ -0,0 +1,7 @@
1
+ /**
2
+ * DingTalk 插件实例的运行时状态:adapter create() 注册的 endpoint 列表。
3
+ * 由 plugin.ts setup() provide,adapter create 与 `dingtalk.endpoint` 命令共享(同一 owner generation)。
4
+ */
5
+ import { defineEndpointRuntimeStateToken } from 'zhin.js/adapter';
6
+
7
+ export const dingtalkRuntimeStateToken = defineEndpointRuntimeStateToken('dingtalk');