@xmanrui/dsh-im 0.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 (133) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +143 -0
  3. package/THIRD_PARTY_NOTICES.md +15 -0
  4. package/bin/dsh-im.mjs +99 -0
  5. package/cordis.patch.yml +3 -0
  6. package/lib/client.js +6211 -0
  7. package/lib/index.js +13339 -0
  8. package/package.json +73 -0
  9. package/plugin-src/client/build.mjs +38 -0
  10. package/plugin-src/client/channel-logos.js +107 -0
  11. package/plugin-src/client/channels/dingtalk/api.js +203 -0
  12. package/plugin-src/client/channels/dingtalk/index.js +709 -0
  13. package/plugin-src/client/channels/dingtalk/styles.js +140 -0
  14. package/plugin-src/client/channels/discord/api.js +11 -0
  15. package/plugin-src/client/channels/discord/index.js +25 -0
  16. package/plugin-src/client/channels/discord/styles.js +19 -0
  17. package/plugin-src/client/channels/feishu/api.js +325 -0
  18. package/plugin-src/client/channels/feishu/index.js +986 -0
  19. package/plugin-src/client/channels/feishu/styles.js +505 -0
  20. package/plugin-src/client/channels/qq/api.js +119 -0
  21. package/plugin-src/client/channels/qq/index.js +402 -0
  22. package/plugin-src/client/channels/qq/styles.js +19 -0
  23. package/plugin-src/client/channels/shared/token-api.js +88 -0
  24. package/plugin-src/client/channels/shared/token-channel.js +277 -0
  25. package/plugin-src/client/channels/telegram/api.js +11 -0
  26. package/plugin-src/client/channels/telegram/index.js +25 -0
  27. package/plugin-src/client/channels/telegram/styles.js +19 -0
  28. package/plugin-src/client/channels/wecom/api.js +119 -0
  29. package/plugin-src/client/channels/wecom/index.js +402 -0
  30. package/plugin-src/client/channels/wecom/styles.js +19 -0
  31. package/plugin-src/client/channels/weixin/api.js +156 -0
  32. package/plugin-src/client/channels/weixin/index.js +577 -0
  33. package/plugin-src/client/channels/weixin/styles.js +120 -0
  34. package/plugin-src/client/credential-binding.js +117 -0
  35. package/plugin-src/client/index.js +192 -0
  36. package/plugin-src/client/lifecycle.js +86 -0
  37. package/plugin-src/client/styles.js +192 -0
  38. package/plugin-src/host/build.mjs +24 -0
  39. package/plugin-src/host/channels/dingtalk/connection-supervisor.mjs +130 -0
  40. package/plugin-src/host/channels/dingtalk/index.mjs +39 -0
  41. package/plugin-src/host/channels/dingtalk/production.mjs +122 -0
  42. package/plugin-src/host/channels/dingtalk/rpc.mjs +237 -0
  43. package/plugin-src/host/channels/discord/index.mjs +30 -0
  44. package/plugin-src/host/channels/discord/production.mjs +17 -0
  45. package/plugin-src/host/channels/discord/rpc.mjs +21 -0
  46. package/plugin-src/host/channels/feishu/connection-supervisor.mjs +167 -0
  47. package/plugin-src/host/channels/feishu/controller.mjs +172 -0
  48. package/plugin-src/host/channels/feishu/credential-store.mjs +84 -0
  49. package/plugin-src/host/channels/feishu/index.mjs +73 -0
  50. package/plugin-src/host/channels/feishu/production.mjs +138 -0
  51. package/plugin-src/host/channels/feishu/rpc.mjs +454 -0
  52. package/plugin-src/host/channels/qq/connection-supervisor.mjs +124 -0
  53. package/plugin-src/host/channels/qq/index.mjs +30 -0
  54. package/plugin-src/host/channels/qq/production.mjs +109 -0
  55. package/plugin-src/host/channels/qq/rpc.mjs +146 -0
  56. package/plugin-src/host/channels/shared/connection-supervisor.mjs +133 -0
  57. package/plugin-src/host/channels/shared/production.mjs +104 -0
  58. package/plugin-src/host/channels/shared/rpc.mjs +119 -0
  59. package/plugin-src/host/channels/telegram/index.mjs +30 -0
  60. package/plugin-src/host/channels/telegram/production.mjs +17 -0
  61. package/plugin-src/host/channels/telegram/rpc.mjs +21 -0
  62. package/plugin-src/host/channels/wecom/connection-supervisor.mjs +124 -0
  63. package/plugin-src/host/channels/wecom/index.mjs +36 -0
  64. package/plugin-src/host/channels/wecom/production.mjs +113 -0
  65. package/plugin-src/host/channels/wecom/rpc.mjs +151 -0
  66. package/plugin-src/host/channels/weixin/connection-supervisor.mjs +127 -0
  67. package/plugin-src/host/channels/weixin/index.mjs +39 -0
  68. package/plugin-src/host/channels/weixin/production.mjs +119 -0
  69. package/plugin-src/host/channels/weixin/rpc.mjs +178 -0
  70. package/plugin-src/host/index.mjs +44 -0
  71. package/plugin-src/host/rpc-authority.mjs +11 -0
  72. package/scripts/verify-package.mjs +97 -0
  73. package/src/channels/dingtalk/config-store.mjs +282 -0
  74. package/src/channels/dingtalk/device-auth.mjs +237 -0
  75. package/src/channels/dingtalk/dingtalk-api.mjs +579 -0
  76. package/src/channels/dingtalk/dingtalk-bridge.mjs +281 -0
  77. package/src/channels/dingtalk/dingtalk-card-stream.mjs +233 -0
  78. package/src/channels/dingtalk/dingtalk-controller.mjs +752 -0
  79. package/src/channels/dingtalk/dingtalk-runtime.mjs +358 -0
  80. package/src/channels/dingtalk/harness-client.mjs +299 -0
  81. package/src/channels/dingtalk/state-store.mjs +212 -0
  82. package/src/channels/discord/config-store.mjs +24 -0
  83. package/src/channels/discord/discord-api.mjs +153 -0
  84. package/src/channels/discord/discord-bridge.mjs +15 -0
  85. package/src/channels/discord/discord-controller.mjs +16 -0
  86. package/src/channels/discord/discord-runtime.mjs +460 -0
  87. package/src/channels/discord/harness-client.mjs +3 -0
  88. package/src/channels/discord/state-store.mjs +3 -0
  89. package/src/channels/feishu/bridge.mjs +216 -0
  90. package/src/channels/feishu/config.mjs +71 -0
  91. package/src/channels/feishu/feishu-app.mjs +51 -0
  92. package/src/channels/feishu/feishu-channel.mjs +153 -0
  93. package/src/channels/feishu/feishu-runtime.mjs +222 -0
  94. package/src/channels/feishu/harness-client.mjs +268 -0
  95. package/src/channels/feishu/message-utils.mjs +51 -0
  96. package/src/channels/feishu/multi-bot-controller.mjs +686 -0
  97. package/src/channels/feishu/plugin-config-store.mjs +204 -0
  98. package/src/channels/feishu/plugin-controller.mjs +248 -0
  99. package/src/channels/feishu/registration-manager.mjs +345 -0
  100. package/src/channels/feishu/state-store.mjs +71 -0
  101. package/src/channels/qq/config-store.mjs +171 -0
  102. package/src/channels/qq/harness-client.mjs +3 -0
  103. package/src/channels/qq/qq-bridge.mjs +177 -0
  104. package/src/channels/qq/qq-controller.mjs +452 -0
  105. package/src/channels/qq/qq-runtime.mjs +208 -0
  106. package/src/channels/qq/qr-auth.mjs +24 -0
  107. package/src/channels/qq/state-store.mjs +96 -0
  108. package/src/channels/shared/conversation-state-store.mjs +107 -0
  109. package/src/channels/shared/editable-message-stream.mjs +81 -0
  110. package/src/channels/shared/text-harness-bridge.mjs +180 -0
  111. package/src/channels/shared/token-bot-controller.mjs +298 -0
  112. package/src/channels/shared/token-config-store.mjs +180 -0
  113. package/src/channels/telegram/config-store.mjs +24 -0
  114. package/src/channels/telegram/harness-client.mjs +3 -0
  115. package/src/channels/telegram/state-store.mjs +3 -0
  116. package/src/channels/telegram/telegram-api.mjs +120 -0
  117. package/src/channels/telegram/telegram-bridge.mjs +15 -0
  118. package/src/channels/telegram/telegram-controller.mjs +16 -0
  119. package/src/channels/telegram/telegram-runtime.mjs +278 -0
  120. package/src/channels/wecom/config-store.mjs +170 -0
  121. package/src/channels/wecom/harness-client.mjs +3 -0
  122. package/src/channels/wecom/qr-auth.mjs +95 -0
  123. package/src/channels/wecom/state-store.mjs +90 -0
  124. package/src/channels/wecom/wecom-bridge.mjs +243 -0
  125. package/src/channels/wecom/wecom-controller.mjs +474 -0
  126. package/src/channels/wecom/wecom-runtime.mjs +209 -0
  127. package/src/channels/weixin/config-store.mjs +182 -0
  128. package/src/channels/weixin/harness-client.mjs +259 -0
  129. package/src/channels/weixin/state-store.mjs +112 -0
  130. package/src/channels/weixin/weixin-api.mjs +319 -0
  131. package/src/channels/weixin/weixin-bridge.mjs +173 -0
  132. package/src/channels/weixin/weixin-controller.mjs +545 -0
  133. package/src/channels/weixin/weixin-runtime.mjs +204 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 xmanrui
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,143 @@
1
+ # dsh-im
2
+
3
+ ## 中文
4
+
5
+ 通过扫码或已有机器人凭据把 IM 机器人接入 DeepSeek Harness。一个插件、一个设置入口,统一管理飞书、微信、钉钉、企业微信、QQ、Telegram 和 Discord 机器人。
6
+
7
+ > GitHub 简介:通过扫码或机器人凭据把IM机器人接入DeepSeek Harness(支持飞书、微信、钉钉、企业微信、QQ、Telegram和Discord)。
8
+
9
+ ## 界面
10
+
11
+ ![IM机器人页面](docs/images/imbot.png)
12
+
13
+ ## 当前内置渠道
14
+
15
+ - 飞书:扫码创建机器人,或使用已有 App ID + App Secret 绑定机器人,使用长连接收发消息;
16
+ - 微信:扫码绑定微信机器人,使用腾讯 iLink 长轮询收发消息;
17
+ - 钉钉:扫码创建机器人,或使用已有 Client ID + Client Secret 绑定机器人,使用钉钉 Stream 长连接收消息,并通过 AI Card 流式显示 Harness 回答。
18
+ - 企业微信:使用企业微信 App 扫码创建智能机器人,或使用已有 Bot ID + Secret 绑定机器人,通过官方 WebSocket 长连接收消息,原生显示“正在思考中”、工具执行进度和流式回答。
19
+ - QQ:使用手机 QQ 扫码创建机器人,或使用已有 AppID + AppSecret 绑定机器人,通过 WebSocket 长连接收消息;私聊支持原生“正在输入”和流式回答,群聊在机器人被 @ 后回复。
20
+ - Telegram:使用 @BotFather 生成的 Bot Token 接入机器人,通过官方 Bot API 长轮询收消息;私聊直接回复,群聊仅在机器人被提及或收到对机器人消息的回复时响应,并通过编辑消息流式显示 Harness 回答。
21
+ - Discord:使用 Developer Portal 生成的 Bot Token 接入机器人,通过 Gateway v10 长连接收消息;私信直接回复,服务器频道仅在机器人被提及时响应,并通过编辑消息流式显示 Harness 回答。
22
+
23
+ 其他 IM 平台可继续按同一渠道适配器结构接入。
24
+
25
+ ## 安装
26
+
27
+ ```sh
28
+ npx -y github:xmanrui/dsh-im install
29
+ ```
30
+
31
+ 重启 `dsh web`,然后打开「设置 → 插件 → IM机器人」。安装器会用 `dsh-im` 替换 profile 中直接安装的 `dsh-feishu`、`dsh-weixin` 和 `dsh-dingtalk`,但不删除任何渠道数据;原有渠道凭据和扫码绑定会继续使用。
32
+
33
+ 飞书、QQ、钉钉和企业微信页面都提供两种入口:带二维码图标的蓝色「扫码接入机器人」按钮走平台官方扫码流程,右侧带钥匙图标的白色描边「手动接入」按钮连接已经创建的机器人应用。飞书和 QQ 分别填写 App ID + App Secret、AppID + AppSecret;钉钉填写官方 Client ID + Client Secret;企业微信填写官方 Bot ID + Secret。Secret 只提交给本机 Harness Host,并写入受保护的凭据存储;状态接口和机器人列表不会回传 Secret。
34
+
35
+ Telegram 和 Discord 没有官方扫码创建机器人流程,因此页面只显示带钥匙图标的「手动接入」入口,并只要求 Bot Token。Telegram Token 由 @BotFather 生成;若该机器人已经配置 Webhook,需要先由原服务移除 Webhook,Bot API 长轮询才能接管消息。Discord Token 来自 Developer Portal 的 Bot 页面;还需把机器人邀请到目标服务器,并授予查看频道、发送消息和读取历史消息权限。本插件只读取私信和明确提及机器人的服务器消息,因此不要求 Message Content 特权 Intent。
36
+
37
+ 钉钉扫码接入时,请使用已加入企业/组织且有权创建机器人的钉钉账号扫描页面二维码,再在钉钉授权页点击「一键创建新机器人」。若提示“该账号还未加入组织”,请先创建组织或换用已加入组织的账号后重新扫码。插件不设置本机二次批准流程,钉钉中的机器人可见范围就是入站访问范围,请只开放给信任的组织、群或成员。
38
+
39
+ 企业微信扫码接入时,请使用已加入企业且具有机器人创建或管理权限的企业微信账号,并在手机端确认创建智能机器人。扫码创建的是企业微信智能机器人,不是让插件直接登录个人微信账号。无论扫码还是凭据绑定,企业微信中的机器人可见范围就是入站访问范围,请只开放给信任的企业成员和群聊。
40
+
41
+ QQ 扫码接入使用腾讯 QQBot v2 官方流程。默认腾讯授权页会把接入方显示为“第三方机器人”;扫码成功后创建的是 QQ 开放平台机器人,并不是让插件直接控制个人 QQ 账号。扫码绑定只接受扫码者的消息;手动凭据无法识别扫码人,因此使用 QQ 开放平台中的机器人可见范围作为入站访问范围。
42
+
43
+ 飞书扫码绑定会把扫码者作为允许使用者;手动凭据同样无法识别扫码人,因此使用飞书应用的可见范围作为入站访问范围。请在飞书开放平台中只向信任的租户、群或成员开放应用。
44
+
45
+ ## 设计
46
+
47
+ - Harness 中只注册一个「IM机器人」设置页;
48
+ - 七个渠道的 Host、客户端与运行时源码都在本仓库维护,不依赖外部独立渠道插件;
49
+ - 左侧使用渠道 Logo 切换微信、飞书、钉钉、企业微信、QQ、Telegram 和 Discord,不使用启用/停用开关;
50
+ - 七个渠道保持独立的 RPC、凭据、连接监督和会话映射;
51
+ - 浏览器只获得二维码和脱敏状态;手动输入的 Secret 或 Bot Token 仅单向提交给本机 Host,任何 RPC 响应都不会返回 App Secret、`bot_token`、钉钉 `client_secret`、企业微信 Secret、QQ `app_secret`、Telegram/Discord Bot Token 或原始用户标识。
52
+
53
+ ## 本地开发
54
+
55
+ ```sh
56
+ npm install
57
+ npm run check
58
+ node bin/dsh-im.mjs install --source .
59
+ ```
60
+
61
+ `npm run check` 运行单元测试、构建 Host/Client 产物,并验证发布包不包含凭据或独立渠道设置页注册。
62
+
63
+ IM 管理 RPC 默认仅接受回环浏览器。如果 Web profile 在受信任的局域网内对外提供服务,可在该 profile 的 `cordis.patch.yml` 中显式开放给 Connection 已信任的 Host authority:
64
+
65
+ ```yaml
66
+ - id: xmanrui-dsh-im
67
+ config:
68
+ rpcAuthority: trusted-host
69
+ ```
70
+
71
+ `trusted-host` 只复用 Harness 的 Host/Origin 防护,不是用户认证。启用后,能访问该局域网地址的人也能查看机器人状态、扫码或提交应用凭据、重连和删除机器人;只应在可信网络中使用。
72
+
73
+ ---
74
+
75
+ ## English
76
+
77
+ Connect IM bots to DeepSeek Harness by scanning a QR code or entering existing bot credentials. One plugin and one settings entry provide unified management for Feishu, WeChat, DingTalk, WeCom, QQ, Telegram, and Discord bots.
78
+
79
+ > GitHub description: Connect IM bots to DeepSeek Harness by QR code or bot credentials (supports Feishu, WeChat, DingTalk, WeCom, QQ, Telegram, and Discord).
80
+
81
+ ## Interface
82
+
83
+ ![IM bot settings page](docs/images/imbot.png)
84
+
85
+ ## Built-in channels
86
+
87
+ - Feishu: create a bot by QR code or bind an existing bot with App ID + App Secret, then send and receive messages over a persistent connection.
88
+ - WeChat: bind a WeChat bot by scanning a QR code, then send and receive messages through Tencent iLink long polling.
89
+ - DingTalk: create a bot by QR code or bind an existing bot with Client ID + Client Secret, receive messages through DingTalk Stream, and stream Harness replies through AI Cards.
90
+ - WeCom: create an intelligent bot by QR code or bind an existing bot with Bot ID + Secret, receive messages over the official WebSocket connection, and natively show a thinking state, tool progress, and streaming replies.
91
+ - QQ: create a bot by QR code or bind an existing bot with AppID + AppSecret, receive messages over a WebSocket connection, stream private-chat replies with a native typing indicator, and reply in groups when mentioned.
92
+ - Telegram: bind a BotFather-created bot with its Bot Token, receive messages through Bot API long polling, reply directly in private chats, require a mention or reply in groups, and stream Harness output by editing the reply.
93
+ - Discord: bind a Developer Portal bot with its Bot Token, receive events through Gateway v10, reply directly in DMs, require a mention in server channels, and stream Harness output by editing the reply.
94
+
95
+ Other IM platforms can be added through the same channel-adapter structure.
96
+
97
+ ## Installation
98
+
99
+ ```sh
100
+ npx -y github:xmanrui/dsh-im install
101
+ ```
102
+
103
+ Restart `dsh web`, then open **Settings → Plugins → IM Bot**. The installer replaces directly installed `dsh-feishu`, `dsh-weixin`, and `dsh-dingtalk` entries in the profile with `dsh-im` without deleting channel data.
104
+
105
+ Feishu, QQ, DingTalk, and WeCom each provide two entry points. The blue **QR access** action uses the platform QR flow; the key-marked, outlined **Manual access** action immediately to its right connects an existing bot application. Feishu and QQ use App ID + App Secret and AppID + AppSecret respectively, DingTalk uses Client ID + Client Secret, and WeCom uses Bot ID + Secret. Secrets are sent only to the local Harness Host and stored through its protected credential provider; status responses and bot lists never return them.
106
+
107
+ Telegram and Discord do not provide an official QR flow for creating bots, so their pages expose only the key-marked **Manual access** action and request a Bot Token. Generate the Telegram token with BotFather; an existing webhook must be removed by its current service before Bot API long polling can receive updates. Generate the Discord token on the Developer Portal's Bot page, invite the bot to the target server, and grant View Channel, Send Messages, and Read Message History. The plugin reads DMs and server messages that explicitly mention the bot, so it does not request the privileged Message Content intent.
108
+
109
+ For DingTalk QR binding, scan with an account that belongs to an enterprise or organization and can create bots, then choose **Create a new bot** on the authorization page. If DingTalk reports that the account has not joined an organization, create one or switch to an account that has, then scan again. There is no second local sender-approval flow: the bot's DingTalk visibility is its inbound access scope, so restrict it to trusted organizations, groups, or members.
110
+
111
+ For WeCom QR binding, scan with an account that belongs to an enterprise and can create or manage bots, then confirm creation of the intelligent bot in the mobile app. This creates a WeCom intelligent bot; it does not sign the plugin into a personal WeChat account. For both QR and credential binding, restrict the bot's WeCom visibility to trusted enterprise members and group chats.
112
+
113
+ QQ QR binding uses Tencent's official QQBot v2 flow. Tencent's default authorization page labels the integration as a third-party bot. Scanning creates a QQ Open Platform bot; it does not give the plugin direct control of a personal QQ account. QR binding accepts only the scanner's messages. Manual credentials cannot identify a scanner, so the bot's QQ Open Platform visibility becomes its inbound access scope.
114
+
115
+ Feishu QR binding records the scanner as an allowed user. Manual credentials cannot identify a scanner, so the Feishu application's visibility becomes its inbound access scope. Restrict the application to trusted tenants, groups, or members.
116
+
117
+ ## Design
118
+
119
+ - Registers a single **IM Bot** settings page in Harness.
120
+ - Maintains all seven channel Host, client, and runtime sources in this repository without external standalone channel plugins.
121
+ - Uses channel logos for WeChat, Feishu, DingTalk, WeCom, QQ, Telegram, and Discord navigation without enable/disable switches.
122
+ - Keeps RPC endpoints, credentials, connection supervision, and session mappings isolated by channel.
123
+ - Returns only QR codes and redacted status data to the browser. Manually entered secrets and Bot Tokens travel one way to the local Host; no RPC response returns App Secrets, `bot_token`, DingTalk `client_secret`, WeCom Secrets, QQ `app_secret`, Telegram/Discord Bot Tokens, or raw user identifiers.
124
+
125
+ ## Local development
126
+
127
+ ```sh
128
+ npm install
129
+ npm run check
130
+ node bin/dsh-im.mjs install --source .
131
+ ```
132
+
133
+ `npm run check` runs unit tests, builds the Host and Client artifacts, and verifies that the published package contains neither credentials nor standalone channel settings-page registrations.
134
+
135
+ IM management RPCs accept loopback browsers by default. When a Web profile is deliberately served on a trusted LAN, opt the plugin into the Host authorities already trusted by Connection in that profile's `cordis.patch.yml`:
136
+
137
+ ```yaml
138
+ - id: xmanrui-dsh-im
139
+ config:
140
+ rpcAuthority: trusted-host
141
+ ```
142
+
143
+ `trusted-host` reuses Harness's Host/Origin fence; it is not user authentication. Anyone who can reach that LAN authority can inspect bot status, scan or submit application credentials, reconnect bots, and remove bots. Enable it only on a trusted network.
@@ -0,0 +1,15 @@
1
+ # Third-party notices
2
+
3
+ The Weixin iLink request format, QR-login states, and message fields are adapted from Tencent's [`openclaw-weixin`](https://github.com/Tencent/openclaw-weixin) project at commit `cef0bfc390393f716903e16d50408118047f87e0` (package version 2.4.6), licensed under the MIT License and copyright Tencent.
4
+
5
+ The DingTalk device-authorization request sequence and AI Card streaming protocol are adapted from DingTalk Real Team's [`dingtalk-openclaw-connector`](https://github.com/DingTalk-Real-AI/dingtalk-openclaw-connector) project at commit `b2fd6e5ea2ff99bd213faac637d3da541b2bfaf4`, licensed under the MIT License and copyright 2026 DingTalk Real Team.
6
+
7
+ The WeCom QR-authorization request sequence is adapted from the official [`@wecom/wecom-openclaw-cli`](https://www.npmjs.com/package/@wecom/wecom-openclaw-cli) 1.1.0 package, whose npm metadata declares the ISC License. No CLI source or OpenClaw runtime is bundled in this package.
8
+
9
+ This package depends on [`@larksuiteoapi/node-sdk`](https://github.com/larksuite/node-sdk) 1.73.0, [`dingtalk-stream`](https://github.com/open-dingtalk/dingtalk-stream-sdk-nodejs) 2.1.4, [`@wecom/aibot-node-sdk`](https://github.com/WecomTeam/aibot-node-sdk) 1.0.7, [`@tencent-connect/qqbot-nodejs`](https://github.com/tencent-connect/qqbot) 1.0.4, and [`qrcode`](https://github.com/soldair/node-qrcode) 1.5.4. These packages are licensed under the MIT License; `dingtalk-stream` is copyright 2023 钉钉开放平台团队.
10
+
11
+ QQ QR binding uses Tencent Connect's official [`@tencent-connect/qqbot-connector`](https://www.npmjs.com/package/@tencent-connect/qqbot-connector) 1.2.0 package as an external runtime dependency. Its npm metadata declares `UNLICENSED`; no connector source is copied into this project.
12
+
13
+ This project is an independent DeepSeek Harness integration. It does not bundle OpenClaw and is not endorsed by Tencent, WeCom, Feishu, DingTalk, QQ, Telegram, or Discord.
14
+
15
+ The WeChat, QQ, Telegram, and Discord marks use path data published by Simple Icons under the CC0 1.0 Universal license. The Feishu, DingTalk, and WeCom marks are inline vectors used for channel identification. Product names and logos remain trademarks of their respective owners.
package/bin/dsh-im.mjs ADDED
@@ -0,0 +1,99 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { readFile } from 'node:fs/promises';
4
+ import { homedir, tmpdir } from 'node:os';
5
+ import { isAbsolute, join, resolve } from 'node:path';
6
+ import { spawnSync } from 'node:child_process';
7
+
8
+ const PACKAGE_NAME = '@xmanrui/dsh-im';
9
+ const DEFAULT_SOURCE = 'github:xmanrui/dsh-im';
10
+ const LEGACY_PACKAGES = [
11
+ '@xmanrui/dsh-feishu',
12
+ '@xmanrui/dsh-weixin',
13
+ '@xmanrui/dsh-dingtalk',
14
+ ];
15
+
16
+ function usage() {
17
+ console.log(`Usage:
18
+ dsh-im install [--profile web] [--source <package-spec>]
19
+ dsh-im uninstall [--profile web]
20
+
21
+ Examples:
22
+ npx -y github:xmanrui/dsh-im install
23
+ dsh-im install --source .`);
24
+ }
25
+
26
+ function takeOption(args, name, fallback) {
27
+ const index = args.indexOf(name);
28
+ if (index < 0) return fallback;
29
+ const value = args[index + 1];
30
+ if (!value || value.startsWith('--')) throw new Error(`${name} requires a value`);
31
+ args.splice(index, 2);
32
+ return value;
33
+ }
34
+
35
+ function runDsh(args) {
36
+ const result = spawnSync('dsh', args, {
37
+ cwd: tmpdir(),
38
+ stdio: 'inherit',
39
+ shell: false,
40
+ });
41
+ if (result.error?.code === 'ENOENT') {
42
+ throw new Error('找不到 dsh,请先安装 DeepSeek Harness 并确保 dsh 在 PATH 中。');
43
+ }
44
+ if (result.error) throw result.error;
45
+ if (result.status !== 0) throw new Error(`dsh 退出,状态码 ${result.status ?? 1}`);
46
+ }
47
+
48
+ async function directProfilePackages(profile) {
49
+ const dshHome = process.env.DSH_HOME || join(homedir(), '.dsh');
50
+ const profilePackage = join(dshHome, 'profiles', profile, 'package.json');
51
+ try {
52
+ const manifest = JSON.parse(await readFile(profilePackage, 'utf8'));
53
+ const bundles = new Set(manifest.dsh?.profile?.bundles ?? []);
54
+ const dependencies = manifest.dependencies ?? {};
55
+ return new Set(LEGACY_PACKAGES.filter((name) => dependencies[name] || bundles.has(name)));
56
+ } catch (error) {
57
+ if (error?.code === 'ENOENT') return new Set();
58
+ throw new Error(`无法读取 Harness 配置 ${profilePackage}:${error.message}`);
59
+ }
60
+ }
61
+
62
+ const args = process.argv.slice(2);
63
+ const command = args.shift();
64
+
65
+ if (!command || command === '--help' || command === '-h') {
66
+ usage();
67
+ process.exit(0);
68
+ }
69
+
70
+ try {
71
+ const profile = takeOption(args, '--profile', 'web');
72
+ if (command === 'install') {
73
+ const requested = takeOption(args, '--source', DEFAULT_SOURCE);
74
+ const source = requested === '.' || requested === '..'
75
+ || requested.startsWith('./') || requested.startsWith('../')
76
+ ? resolve(process.cwd(), requested)
77
+ : (isAbsolute(requested) ? requested : requested);
78
+ if (args.length > 0) throw new Error(`无法识别的参数:${args.join(' ')}`);
79
+
80
+ const legacy = await directProfilePackages(profile);
81
+ runDsh(['plugin', '--profile', profile, 'add', '--save-exact', source]);
82
+ for (const packageName of legacy) {
83
+ runDsh(['plugin', '--profile', profile, 'remove', packageName]);
84
+ }
85
+ console.log('\nIM 机器人插件已安装。请重启 dsh web,然后打开「设置 → 插件 → IM机器人」。');
86
+ if (legacy.size > 0) {
87
+ console.log('已用 dsh-im 替换独立飞书/微信/钉钉插件;原有凭据和扫码绑定保持不变。');
88
+ }
89
+ } else if (command === 'uninstall') {
90
+ if (args.length > 0) throw new Error(`无法识别的参数:${args.join(' ')}`);
91
+ runDsh(['plugin', '--profile', profile, 'remove', PACKAGE_NAME]);
92
+ console.log('\nIM 机器人插件已卸载。请重启 dsh web。');
93
+ } else {
94
+ throw new Error(`无法识别的命令:${command}`);
95
+ }
96
+ } catch (error) {
97
+ console.error(`dsh-im: ${error.message}`);
98
+ process.exit(1);
99
+ }
@@ -0,0 +1,3 @@
1
+ - insert:
2
+ - id: xmanrui-dsh-im
3
+ name: '@xmanrui/dsh-im'