@xmanrui/dsh-im 4.22.0 → 4.24.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 (148) hide show
  1. package/README.en.md +5 -4
  2. package/README.md +5 -4
  3. package/lib/client.js +2584 -1153
  4. package/lib/index.js +290 -298
  5. package/package.json +14 -2
  6. package/plugin-src/client/channel-card-meta.js +2 -0
  7. package/plugin-src/client/channel-logos.js +11 -0
  8. package/plugin-src/client/channels/dingtalk/api.js +5 -1
  9. package/plugin-src/client/channels/dingtalk/index.js +15 -13
  10. package/plugin-src/client/channels/email/api.js +52 -0
  11. package/plugin-src/client/channels/email/index.js +728 -0
  12. package/plugin-src/client/channels/email/styles.js +51 -0
  13. package/plugin-src/client/channels/feishu/api.js +4 -2
  14. package/plugin-src/client/channels/feishu/index.js +23 -7
  15. package/plugin-src/client/channels/imessage/index.js +2 -1
  16. package/plugin-src/client/channels/office/api.js +2 -0
  17. package/plugin-src/client/channels/office/index.js +6 -5
  18. package/plugin-src/client/channels/qq/api.js +7 -0
  19. package/plugin-src/client/channels/qq/index.js +16 -3
  20. package/plugin-src/client/channels/shared/token-api.js +8 -1
  21. package/plugin-src/client/channels/shared/token-channel.js +28 -3
  22. package/plugin-src/client/channels/slack/index.js +2 -1
  23. package/plugin-src/client/channels/telegram/index.js +3 -0
  24. package/plugin-src/client/channels/telegram/styles.js +12 -0
  25. package/plugin-src/client/channels/telegram/thinking-traces.js +25 -0
  26. package/plugin-src/client/channels/wecom/api.js +7 -1
  27. package/plugin-src/client/channels/wecom/index.js +16 -3
  28. package/plugin-src/client/channels/wecom-app/api.js +6 -1
  29. package/plugin-src/client/channels/wecom-app/index.js +16 -3
  30. package/plugin-src/client/channels/weixin/api.js +2 -1
  31. package/plugin-src/client/channels/weixin/connection-error.js +1 -71
  32. package/plugin-src/client/channels/whatsapp/api.js +5 -0
  33. package/plugin-src/client/channels/whatsapp/index.js +16 -3
  34. package/plugin-src/client/connection-error.js +87 -0
  35. package/plugin-src/client/global-settings.js +81 -1
  36. package/plugin-src/client/i18n.js +69 -1
  37. package/plugin-src/client/index.js +40 -4
  38. package/plugin-src/client/last-message-error.js +2 -1
  39. package/plugin-src/client/styles.js +8 -2
  40. package/plugin-src/host/build.mjs +4 -0
  41. package/plugin-src/host/channels/dingtalk/production.mjs +2 -0
  42. package/plugin-src/host/channels/dingtalk/rpc.mjs +7 -4
  43. package/plugin-src/host/channels/email/availability.mjs +60 -0
  44. package/plugin-src/host/channels/email/index.mjs +36 -0
  45. package/plugin-src/host/channels/email/production.mjs +58 -0
  46. package/plugin-src/host/channels/email/rpc.mjs +163 -0
  47. package/plugin-src/host/channels/feishu/production.mjs +3 -0
  48. package/plugin-src/host/channels/feishu/rpc.mjs +7 -4
  49. package/plugin-src/host/channels/imessage/rpc.mjs +13 -6
  50. package/plugin-src/host/channels/office/rpc.mjs +3 -1
  51. package/plugin-src/host/channels/qq/production.mjs +2 -0
  52. package/plugin-src/host/channels/qq/rpc.mjs +5 -3
  53. package/plugin-src/host/channels/shared/production.mjs +52 -5
  54. package/plugin-src/host/channels/shared/rpc.mjs +14 -3
  55. package/plugin-src/host/channels/shared/startup-error.mjs +4 -3
  56. package/plugin-src/host/channels/shared/startup.mjs +11 -6
  57. package/plugin-src/host/channels/shared/thinking-traces-rpc.mjs +11 -0
  58. package/plugin-src/host/channels/slack/production.mjs +2 -0
  59. package/plugin-src/host/channels/slack/rpc.mjs +5 -3
  60. package/plugin-src/host/channels/wecom/production.mjs +2 -0
  61. package/plugin-src/host/channels/wecom/rpc.mjs +5 -3
  62. package/plugin-src/host/channels/wecom-app/production.mjs +2 -0
  63. package/plugin-src/host/channels/wecom-app/rpc.mjs +5 -3
  64. package/plugin-src/host/channels/weixin/production.mjs +2 -0
  65. package/plugin-src/host/channels/whatsapp/production.mjs +2 -0
  66. package/plugin-src/host/channels/whatsapp/rpc.mjs +5 -3
  67. package/plugin-src/host/delivery-adapter.mjs +11 -0
  68. package/plugin-src/host/image-input-rpc.mjs +24 -0
  69. package/plugin-src/host/inbound-ttl-rpc.mjs +6 -1
  70. package/plugin-src/host/index.mjs +3 -0
  71. package/plugin-src/host/modern-harness-api.mjs +7 -2
  72. package/plugin-src/management-rpc.mjs +12 -2
  73. package/scripts/verify-package.mjs +13 -5
  74. package/src/channels/dingtalk/connection-error.mjs +5 -12
  75. package/src/channels/dingtalk/device-auth.mjs +4 -1
  76. package/src/channels/dingtalk/dingtalk-bridge.mjs +6 -4
  77. package/src/channels/dingtalk/dingtalk-controller.mjs +60 -45
  78. package/src/channels/dingtalk/dingtalk-runtime.mjs +20 -4
  79. package/src/channels/discord/discord-api.mjs +3 -3
  80. package/src/channels/discord/discord-runtime.mjs +18 -12
  81. package/src/channels/email/config-store.mjs +194 -0
  82. package/src/channels/email/email-api.mjs +20 -0
  83. package/src/channels/email/email-bridge.mjs +17 -0
  84. package/src/channels/email/email-controller.mjs +824 -0
  85. package/src/channels/email/email-runtime.mjs +699 -0
  86. package/src/channels/email/harness-client.mjs +7 -0
  87. package/src/channels/email/mail-format.mjs +91 -0
  88. package/src/channels/email/state-store.mjs +176 -0
  89. package/src/channels/email/transport.mjs +64 -0
  90. package/src/channels/email/transports/agent-mail.mjs +604 -0
  91. package/src/channels/email/transports/agently-cli.mjs +288 -0
  92. package/src/channels/email/transports/imap-smtp.mjs +165 -0
  93. package/src/channels/feishu/bridge.mjs +39 -11
  94. package/src/channels/feishu/feishu-channel.mjs +35 -0
  95. package/src/channels/feishu/feishu-runtime.mjs +15 -8
  96. package/src/channels/feishu/group-message-permission-manager.mjs +2 -1
  97. package/src/channels/feishu/live-cot.mjs +260 -0
  98. package/src/channels/feishu/message-utils.mjs +3 -2
  99. package/src/channels/feishu/multi-bot-controller.mjs +56 -47
  100. package/src/channels/feishu/registration-manager.mjs +8 -4
  101. package/src/channels/feishu/repair-manager.mjs +2 -1
  102. package/src/channels/feishu/slash-command-registry.mjs +17 -0
  103. package/src/channels/feishu/step-push-mode.mjs +10 -4
  104. package/src/channels/imessage/runtime.mjs +9 -5
  105. package/src/channels/office/office-controller.mjs +23 -12
  106. package/src/channels/office/office-runtime.mjs +6 -3
  107. package/src/channels/office/office-transport.mjs +1 -0
  108. package/src/channels/qq/qq-bridge.mjs +7 -5
  109. package/src/channels/qq/qq-controller.mjs +56 -42
  110. package/src/channels/qq/qq-runtime.mjs +16 -11
  111. package/src/channels/shared/bot-workspace-store.mjs +29 -10
  112. package/src/channels/shared/connection-error.mjs +191 -0
  113. package/src/channels/shared/connection-test.mjs +3 -1
  114. package/src/channels/shared/conversation-state-store.mjs +46 -1
  115. package/src/channels/shared/diagnostic-details.mjs +95 -0
  116. package/src/channels/shared/harness-client.mjs +287 -57
  117. package/src/channels/shared/i18n-en/diagnostics.mjs +55 -0
  118. package/src/channels/shared/i18n-en/email.mjs +25 -0
  119. package/src/channels/shared/i18n-en/image-input.mjs +17 -0
  120. package/src/channels/shared/i18n-en/shared-a.mjs +1 -0
  121. package/src/channels/shared/i18n-en/shared-c.mjs +1 -1
  122. package/src/channels/shared/i18n-en.mjs +6 -0
  123. package/src/channels/shared/image-input-policy.mjs +35 -0
  124. package/src/channels/shared/image-input-settings-store.mjs +60 -0
  125. package/src/channels/shared/image-input.mjs +124 -0
  126. package/src/channels/shared/image-prompt.mjs +15 -7
  127. package/src/channels/shared/message-failure.mjs +12 -0
  128. package/src/channels/shared/semantic/reply-reference.mjs +3 -3
  129. package/src/channels/shared/text-harness-bridge.mjs +150 -25
  130. package/src/channels/shared/token-bot-controller.mjs +52 -47
  131. package/src/channels/slack/slack-api.mjs +3 -3
  132. package/src/channels/slack/slack-controller.mjs +59 -51
  133. package/src/channels/slack/slack-runtime.mjs +18 -12
  134. package/src/channels/telegram/config-store.mjs +4 -1
  135. package/src/channels/telegram/telegram-api.mjs +2 -2
  136. package/src/channels/telegram/telegram-controller.mjs +13 -1
  137. package/src/channels/telegram/telegram-runtime.mjs +215 -14
  138. package/src/channels/wecom/wecom-bridge.mjs +15 -6
  139. package/src/channels/wecom/wecom-controller.mjs +51 -37
  140. package/src/channels/wecom/wecom-runtime.mjs +10 -6
  141. package/src/channels/wecom-app/wecom-app-bridge.mjs +7 -5
  142. package/src/channels/wecom-app/wecom-app-controller.mjs +50 -37
  143. package/src/channels/wecom-app/wecom-app-runtime.mjs +2 -1
  144. package/src/channels/weixin/connection-error.mjs +26 -92
  145. package/src/channels/weixin/diagnostic-details.mjs +1 -63
  146. package/src/channels/weixin/weixin-bridge.mjs +7 -5
  147. package/src/channels/whatsapp/whatsapp-controller.mjs +34 -17
  148. package/src/channels/whatsapp/whatsapp-runtime.mjs +35 -8
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xmanrui/dsh-im",
3
- "version": "4.22.0",
3
+ "version": "4.24.0",
4
4
  "description": "把十一种 IM 渠道和公网 AI Office 接入本机 DeepSeek Harness。 Connect eleven IM channels and a public AI Office to a local DeepSeek Harness.",
5
5
  "keywords": [
6
6
  "deepseek-harness",
@@ -110,7 +110,12 @@
110
110
  "cordis.patch.yml",
111
111
  "lib",
112
112
  "plugin-src",
113
- "scripts",
113
+ "scripts/verify-interface-language.mjs",
114
+ "scripts/verify-lan-management.mjs",
115
+ "scripts/verify-model-setting.mjs",
116
+ "scripts/verify-package.mjs",
117
+ "scripts/verify-session-channel-logos.mjs",
118
+ "scripts/verify-session-title-prefix.mjs",
114
119
  "src",
115
120
  "PROACTIVE_DELIVERY.md",
116
121
  "PROACTIVE_DELIVERY.en.md",
@@ -161,6 +166,9 @@
161
166
  "@tencent-connect/qqbot-nodejs": "1.0.4",
162
167
  "@wecom/aibot-node-sdk": "1.0.7",
163
168
  "dingtalk-stream": "2.1.4",
169
+ "imapflow": "^2.0.5",
170
+ "mailparser": "^3.9.28",
171
+ "nodemailer": "^10.0.10",
164
172
  "qrcode": "1.5.4",
165
173
  "undici": "7.29.0"
166
174
  },
@@ -179,5 +187,9 @@
179
187
  "doc": "docs",
180
188
  "lib": "lib",
181
189
  "test": "test"
190
+ },
191
+ "optionalDependencies": {
192
+ "@tencent-qqmail/agently-cli": "^1.0.18",
193
+ "sharp": "0.35.4"
182
194
  }
183
195
  }
@@ -1,3 +1,4 @@
1
+ import { ConnectionError } from './connection-error.js';
1
2
  import * as React from 'react';
2
3
 
3
4
  import { h, isEnglish } from './i18n.js';
@@ -124,6 +125,7 @@ export function LastMessageErrorSummary({ className = '', error }) {
124
125
  h('strong', null, '最近一条消息处理失败'),
125
126
  ':',
126
127
  h('span', null, error.message),
128
+ error.details ? h(ConnectionError, { error, showMessage: false }) : null,
127
129
  '(',
128
130
  h('span', null, '错误码'),
129
131
  ` ${error.code} · `,
@@ -166,3 +166,14 @@ export function OfficeLogoGlyph({ size } = {}) {
166
166
  h('path', { fill: 'currentColor', d: 'M4 3.5h10.5a2 2 0 0 1 2 2v13H4v-15Zm2.2 3v1.8h2V6.5h-2Zm4.1 0v1.8h2V6.5h-2Zm-4.1 4v1.8h2v-1.8h-2Zm4.1 0v1.8h2v-1.8h-2ZM8.4 15v3.5h3V15h-3Z' }),
167
167
  h('path', { fill: 'currentColor', d: 'M18.3 8.2h1.5v3h3v1.5h-3v3h-1.5v-3h-3v-1.5h3v-3Z' }));
168
168
  }
169
+
170
+ export function EmailLogoGlyph({ size } = {}) {
171
+ return h('svg', {
172
+ ...dimensions(size),
173
+ viewBox: '0 0 24 24',
174
+ focusable: 'false',
175
+ 'aria-hidden': 'true',
176
+ 'data-im-channel-logo': 'email',
177
+ },
178
+ h('path', { fill: 'currentColor', d: 'M3.2 5.6h17.6c.66 0 1.2.54 1.2 1.2v10.4c0 .66-.54 1.2-1.2 1.2H3.2A1.2 1.2 0 0 1 2 17.2V6.8c0-.66.54-1.2 1.2-1.2Zm.9 1.7v.28L12 12.3l7.9-4.72V7.3H4.1Zm15.8 2.02-7.42 4.43a1 1 0 0 1-1.06 0L4.1 9.32v7.78h15.8V9.32Z' }));
179
+ }
@@ -1,3 +1,5 @@
1
+ import { normalizeConnectionError as normalizeTestError } from '../../connection-error.js';
2
+ import { diagnosticFields } from '../../../../src/channels/shared/diagnostic-details.mjs';
1
3
  import { normalizeBotAlias } from '../../../../src/channels/shared/bot-alias.mjs';
2
4
  import { normalizeAgentPresetCatalog, normalizeAgentPresetId, SET_AGENT_PRESET_ENDPOINT } from '../../agent-preset.js';
3
5
  import { normalizeModelCatalog, normalizeModelSelection, SET_MODEL_ENDPOINT } from '../../model-setting.js';
@@ -99,6 +101,7 @@ function normalizeError(value, fallbackCode, fallbackMessage) {
99
101
  const hint = sanitizeMessage(value.hint, '');
100
102
  const referenceId = safeReferenceId(value.referenceId);
101
103
  return {
104
+ ...diagnosticFields(value),
102
105
  code: safeErrorCode(value.code, fallbackCode),
103
106
  message: sanitizeMessage(value.message, fallbackMessage),
104
107
  ...(hint ? { hint } : {}),
@@ -113,7 +116,7 @@ function normalizeTestMessage(value) {
113
116
  const code = value.code === 'test-target-unavailable'
114
117
  ? 'test-target-unavailable'
115
118
  : 'test-message-failed';
116
- return { sent: false, code };
119
+ return { sent: false, code, ...(value.error ? { error: normalizeTestError(value.error) } : {}) };
117
120
  }
118
121
 
119
122
  export function unwrapRpcResult(result) {
@@ -130,6 +133,7 @@ export function unwrapRpcResult(result) {
130
133
  error.code = visible.code;
131
134
  if (visible.hint) error.hint = visible.hint;
132
135
  if (visible.referenceId) error.referenceId = visible.referenceId;
136
+ Object.assign(error, diagnosticFields(result.error));
133
137
  throw error;
134
138
  }
135
139
  return result.value;
@@ -1,3 +1,4 @@
1
+ import { ConnectionError, normalizeConnectionError } from '../../connection-error.js';
1
2
  import { BotName } from '../../bot-alias.js';
2
3
  import * as React from 'react';
3
4
 
@@ -176,16 +177,7 @@ function ProgressPanel({ status, busy, onCancel }) {
176
177
  h(Button, { onClick: onCancel, disabled: busy }, '取消接入')));
177
178
  }
178
179
 
179
- function ConnectionErrorDiagnostic({ error }) {
180
- if (!error) return null;
181
- return h('div', { className: 'ddt-errorDiagnostic' },
182
- error.hint ? h('p', { className: 'ddt-errorHint' }, error.hint) : null,
183
- h('span', { className: 'ddt-errorCode' },
184
- h('span', null, '错误码'), `: ${error.code}`,
185
- error.referenceId
186
- ? h(React.Fragment, null, ' · ', h('span', null, '参考号'), `: ${error.referenceId}`)
187
- : null));
188
- }
180
+ function ConnectionErrorDiagnostic({ error }) { return error ? h(ConnectionError, { error, showMessage: false }) : null; }
189
181
 
190
182
  function ProvisionError({ provision, busy, onRetry, onClose }) {
191
183
  const error = provision.error ?? {
@@ -198,8 +190,7 @@ function ProvisionError({ provision, busy, onRetry, onClose }) {
198
190
  h('h3', null, provision.status === 'expired'
199
191
  ? '二维码已过期'
200
192
  : connectionFailed ? '机器人已保存,但连接未就绪' : '钉钉机器人没有接入完成'),
201
- h('p', null, error.message),
202
- h(ConnectionErrorDiagnostic, { error }),
193
+ h(ConnectionError, { error }),
203
194
  h('div', { className: 'ddt-actions dim-viewActions' },
204
195
  connectionFailed
205
196
  ? h(Button, { kind: 'primary', onClick: onClose, disabled: busy }, '查看已保存的机器人')
@@ -364,6 +355,7 @@ function AccountList(props) {
364
355
  const EMPTY_TOTALS = Object.freeze({ configured: 0, connected: 0 });
365
356
 
366
357
  export function DingtalkSettingsTab({ rpcCall }) {
358
+ const [operationError, setOperationError] = React.useState(null);
367
359
  const [model, setModel] = React.useState({
368
360
  phase: 'loading', bots: [], totals: EMPTY_TOTALS, revision: 0, error: null,
369
361
  agentPresetCatalog: EMPTY_AGENT_PRESET_CATALOG,
@@ -445,7 +437,16 @@ export function DingtalkSettingsTab({ rpcCall }) {
445
437
 
446
438
  const invoke = React.useCallback(async (endpoint, payload = {}, signal) => {
447
439
  if (typeof rpcCall !== 'function') throw new TypeError('钉钉设置页缺少 RPC 连接');
448
- return unwrapRpcResult(await rpcCall(endpoint, payload, signal));
440
+ const operation = !['connection.status', 'provision.poll', 'provision.begin', 'provision.cancel'].includes(endpoint);
441
+ if (operation && mountedRef.current) setOperationError(null);
442
+ try {
443
+ const value = unwrapRpcResult(await rpcCall(endpoint, payload, signal));
444
+ if (operation && mountedRef.current) setOperationError(value?.testMessage?.error ?? value?.warnings?.[0] ?? null);
445
+ return value;
446
+ } catch (error) {
447
+ if (endpoint === 'bot.delete' && mountedRef.current && !signal?.aborted && error?.name !== 'AbortError') setOperationError(normalizeConnectionError(error));
448
+ throw error;
449
+ }
449
450
  }, [rpcCall]);
450
451
 
451
452
  const loadStatus = React.useCallback(async ({
@@ -896,6 +897,7 @@ export function DingtalkSettingsTab({ rpcCall }) {
896
897
  }, h(AgentPresetCatalogContext.Provider, {
897
898
  value: model.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG,
898
899
  }, h('section', { className: 'ddt-page dim-channelPage', 'aria-label': '钉钉设置' },
900
+ operationError ? h(ConnectionError, { error: operationError }) : null,
899
901
  h(Heading, {
900
902
  totals: model.totals,
901
903
  adding: Boolean(provision),
@@ -0,0 +1,52 @@
1
+ import { TOKEN_BOT_ENDPOINTS, createTokenChannelApi } from '../shared/token-api.js';
2
+
3
+ export const EMAIL_RPC_CHANNEL = '/email';
4
+ export const EMAIL_ENDPOINTS = Object.freeze({
5
+ ...TOKEN_BOT_ENDPOINTS,
6
+ // Asks the Host whether the channel is enabled. An explicitly disabled
7
+ // channel hides its access entry point.
8
+ availability: 'channel.availability',
9
+ // The mailbox binds by address + app password rather than a single token.
10
+ bindCredentials: 'bot.bind-mailbox',
11
+ updateMailbox: 'bot.mailbox.update',
12
+ // Session binding controls (per-account settings panel).
13
+ startAuth: 'bot.auth.start',
14
+ pollAuth: 'bot.auth.poll',
15
+ getBinding: 'bot.session-binding.get',
16
+ setBinding: 'bot.session-binding.set',
17
+ listSessions: 'bot.session.list',
18
+ });
19
+ /**
20
+ * Carry the channel-specific mailbox fields through the shared snapshot
21
+ * normalizer, which keeps an explicit field list and would otherwise drop
22
+ * them — the settings form then showed an empty allowlist even though the
23
+ * Host was returning it.
24
+ */
25
+ function normalizeEmailBotExtension(value) {
26
+ const senders = Array.isArray(value?.allowedSenders) ? value.allowedSenders : [];
27
+ return {
28
+ allowedSenders: senders.filter((entry) => typeof entry === 'string' && entry),
29
+ // Without this the settings page cannot tell an Agent mailbox from an
30
+ // IMAP/SMTP one, and shows server hosts that do not apply.
31
+ ...(typeof value?.transport === 'string' && value.transport
32
+ ? { transport: value.transport } : {}),
33
+ ...(typeof value?.provider === 'string' && value.provider
34
+ ? { provider: value.provider } : {}),
35
+ ...(text(value?.imapHost) ? { imapHost: text(value.imapHost) } : {}),
36
+ ...(Number.isSafeInteger(value?.imapPort) ? { imapPort: value.imapPort } : {}),
37
+ ...(text(value?.smtpHost) ? { smtpHost: text(value.smtpHost) } : {}),
38
+ ...(Number.isSafeInteger(value?.smtpPort) ? { smtpPort: value.smtpPort } : {}),
39
+ };
40
+ }
41
+
42
+ function text(value) {
43
+ return typeof value === 'string' && value.trim() ? value.trim() : '';
44
+ }
45
+
46
+ const api = createTokenChannelApi('Email', ' IMAP/SMTP 邮箱', {
47
+ normalizeBotExtension: normalizeEmailBotExtension,
48
+ });
49
+ export { api as emailClientApi };
50
+ export const unwrapRpcResult = api.unwrapRpcResult;
51
+ export const normalizeSnapshot = api.normalizeSnapshot;
52
+ export const presentError = api.presentError;