@xmanrui/dsh-im 4.15.0 → 4.16.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 (62) hide show
  1. package/README.en.md +2 -1
  2. package/README.md +9 -5
  3. package/lib/client.js +1729 -614
  4. package/lib/index.js +303 -293
  5. package/package.json +6 -2
  6. package/plugin-src/client/access-policy-settings.js +6 -0
  7. package/plugin-src/client/channels/feishu/index.js +15 -3
  8. package/plugin-src/client/channels/wecom-app/api.js +124 -0
  9. package/plugin-src/client/channels/wecom-app/index.js +612 -0
  10. package/plugin-src/client/channels/wecom-app/styles.js +25 -0
  11. package/plugin-src/client/delivery-settings.js +7 -0
  12. package/plugin-src/client/i18n.js +63 -0
  13. package/plugin-src/client/index.js +18 -0
  14. package/plugin-src/client/model-setting.js +135 -69
  15. package/plugin-src/client/styles.js +22 -7
  16. package/plugin-src/host/channels/dingtalk/index.mjs +8 -15
  17. package/plugin-src/host/channels/discord/index.mjs +8 -10
  18. package/plugin-src/host/channels/feishu/index.mjs +8 -15
  19. package/plugin-src/host/channels/office/index.mjs +9 -5
  20. package/plugin-src/host/channels/qq/index.mjs +8 -15
  21. package/plugin-src/host/channels/shared/access-policy-production.mjs +1 -1
  22. package/plugin-src/host/channels/shared/model-setting-rpc.mjs +1 -2
  23. package/plugin-src/host/channels/shared/startup-error.mjs +53 -0
  24. package/plugin-src/host/channels/shared/startup.mjs +47 -0
  25. package/plugin-src/host/channels/shared/workspace-rpc.mjs +1 -0
  26. package/plugin-src/host/channels/slack/index.mjs +8 -10
  27. package/plugin-src/host/channels/telegram/index.mjs +8 -10
  28. package/plugin-src/host/channels/wecom/index.mjs +8 -15
  29. package/plugin-src/host/channels/wecom/rpc.mjs +6 -1
  30. package/plugin-src/host/channels/wecom-app/index.mjs +33 -0
  31. package/plugin-src/host/channels/wecom-app/production.mjs +217 -0
  32. package/plugin-src/host/channels/wecom-app/rpc.mjs +225 -0
  33. package/plugin-src/host/channels/weixin/index.mjs +8 -15
  34. package/plugin-src/host/channels/whatsapp/index.mjs +8 -15
  35. package/plugin-src/host/delivery-adapter.mjs +4 -0
  36. package/plugin-src/host/delivery-suggestions.mjs +4 -0
  37. package/plugin-src/host/index.mjs +7 -2
  38. package/scripts/verify-model-setting.mjs +54 -0
  39. package/src/channels/feishu/bridge.mjs +295 -9
  40. package/src/channels/feishu/feishu-cards.mjs +38 -0
  41. package/src/channels/feishu/feishu-channel.mjs +88 -20
  42. package/src/channels/shared/bot-workspace-store.mjs +11 -3
  43. package/src/channels/shared/command-catalog.mjs +102 -0
  44. package/src/channels/shared/i18n-en/feishu.mjs +12 -0
  45. package/src/channels/shared/i18n-en/shared-a.mjs +3 -0
  46. package/src/channels/shared/i18n-en/shared-c.mjs +9 -0
  47. package/src/channels/shared/i18n-en/wecom-app.mjs +33 -0
  48. package/src/channels/shared/i18n-en.mjs +2 -0
  49. package/src/channels/shared/model-setting.mjs +43 -8
  50. package/src/channels/shared/text-harness-bridge.mjs +2 -26
  51. package/src/channels/telegram/telegram-api.mjs +18 -6
  52. package/src/channels/telegram/telegram-runtime.mjs +34 -32
  53. package/src/channels/wecom/send-error.mjs +31 -0
  54. package/src/channels/wecom/wecom-bridge.mjs +59 -17
  55. package/src/channels/wecom-app/callback-server.mjs +471 -0
  56. package/src/channels/wecom-app/config-store.mjs +240 -0
  57. package/src/channels/wecom-app/harness-client.mjs +11 -0
  58. package/src/channels/wecom-app/state-store.mjs +107 -0
  59. package/src/channels/wecom-app/wecom-app-api.mjs +432 -0
  60. package/src/channels/wecom-app/wecom-app-bridge.mjs +1059 -0
  61. package/src/channels/wecom-app/wecom-app-controller.mjs +440 -0
  62. package/src/channels/wecom-app/wecom-app-runtime.mjs +221 -0
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@xmanrui/dsh-im",
3
- "version": "4.15.0",
4
- "description": "把九种 IM 机器人和公网 AI Office 接入本机 DeepSeek Harness。 Connect nine IM channels and a public AI Office to a local DeepSeek Harness.",
3
+ "version": "4.16.0",
4
+ "description": "把十种 IM 机器人和公网 AI Office 接入本机 DeepSeek Harness。 Connect ten IM channels and a public AI Office to a local DeepSeek Harness.",
5
5
  "keywords": [
6
6
  "deepseek-harness",
7
7
  "dsh",
@@ -35,6 +35,10 @@
35
35
  {
36
36
  "name": "Chan-0312",
37
37
  "url": "https://github.com/Chan-0312"
38
+ },
39
+ {
40
+ "name": "BuvkB",
41
+ "url": "https://github.com/BuvkB"
38
42
  }
39
43
  ],
40
44
  "license": "MIT",
@@ -33,6 +33,12 @@ export const ACCESS_CHANNEL_DEFINITIONS = Object.freeze({
33
33
  groupUserLabel: '群成员用户 ID',
34
34
  groupPlaceholder: '填写 userid',
35
35
  }),
36
+ wecomApp: Object.freeze({
37
+ directUserLabel: '企业微信用户 ID',
38
+ directPlaceholder: '填写 userid',
39
+ groupUserLabel: '群成员用户 ID',
40
+ groupPlaceholder: '填写 userid',
41
+ }),
36
42
  qq: Object.freeze({
37
43
  directUserLabel: 'QQ User Open ID',
38
44
  directPlaceholder: '填写 user_openid',
@@ -467,6 +467,7 @@ function RemoveConfirmation({ bot, busy, onConfirm, onCancel }) {
467
467
  /** Toggle row for 分步直推, mirroring the group-topic reply toggle pattern. */
468
468
  function StepPushEditor({ value = false, disabled = false, onSave }) {
469
469
  const titleId = React.useId();
470
+ const helpId = `${titleId}-help`;
470
471
  const current = value === true ? "on" : "off";
471
472
  const [saving, setSaving] = React.useState(false);
472
473
  const [error, setError] = React.useState(null);
@@ -490,7 +491,20 @@ function StepPushEditor({ value = false, disabled = false, onSave }) {
490
491
  "aria-labelledby": titleId,
491
492
  },
492
493
  h("div", { className: "dim-feishuGroupControlHeader" },
493
- h("h3", { id: titleId }, "分步直推"),
494
+ h("div", { className: "dim-presetTitle" },
495
+ h("h3", { id: titleId }, "分步直推"),
496
+ h("span", { className: "dim-presetHelp" },
497
+ h("button", {
498
+ type: "button",
499
+ className: "dim-presetHelpButton",
500
+ "aria-label": "查看分步直推说明",
501
+ "aria-describedby": helpId,
502
+ }, h("span", { "aria-hidden": "true" }, "?")),
503
+ h("span", {
504
+ id: helpId,
505
+ className: "dim-presetTooltip",
506
+ role: "tooltip",
507
+ }, "开启后逐步推送工具调用与过程说明"))),
494
508
  saving
495
509
  ? h("span", { className: "dim-feishuGroupControlStatus", role: "status" }, "保存中…")
496
510
  : null),
@@ -503,8 +517,6 @@ function StepPushEditor({ value = false, disabled = false, onSave }) {
503
517
  },
504
518
  h("option", { value: "off" }, "关闭(保持流式卡模式)"),
505
519
  h("option", { value: "on" }, "开启(逐步推送工具调用与过程说明)")),
506
- h("p", { className: "dim-feishuGroupHelp" },
507
- "开启后逐步推送工具调用与过程说明"),
508
520
  error ? h("p", {
509
521
  className: "dim-feishuGroupError",
510
522
  role: "alert",
@@ -0,0 +1,124 @@
1
+ import { normalizeAgentPresetCatalog, normalizeAgentPresetId, SET_AGENT_PRESET_ENDPOINT } from '../../agent-preset.js';
2
+ import { normalizeModelCatalog, normalizeModelSelection, SET_MODEL_ENDPOINT } from '../../model-setting.js';
3
+ import { normalizeLastMessageError } from '../../last-message-error.js';
4
+ import { normalizeAccessPolicy } from '../../../../src/channels/shared/access-policy.mjs';
5
+ import { normalizeContextEnhancementConfig } from '../../../../src/channels/shared/context-enhancement.mjs';
6
+
7
+ export const WECOM_APP_RPC_CHANNEL = '/wecom-app';
8
+
9
+ export const WECOM_APP_ENDPOINTS = Object.freeze({
10
+ status: 'connection.status',
11
+ bindApp: 'bot.bind',
12
+ updateSettings: 'bot.settings.update',
13
+ resetCallbackSecret: 'bot.callback-secret.reset',
14
+ reconnectBot: 'bot.reconnect',
15
+ deleteBot: 'bot.delete',
16
+ setWorkspace: 'bot.workspace.set',
17
+ setModel: SET_MODEL_ENDPOINT,
18
+ setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
19
+ setContextEnhancement: 'bot.context-enhancement.set',
20
+ setAccessPolicy: 'bot.access-policy.set',
21
+ });
22
+
23
+ const ACCOUNT_STATES = new Set(['connected', 'connecting', 'offline', 'error']);
24
+
25
+ function isRecord(value) {
26
+ return value !== null && typeof value === 'object' && !Array.isArray(value);
27
+ }
28
+
29
+ function text(value, fallback, max = 240) {
30
+ return typeof value === 'string' && value.trim() ? value.trim().slice(0, max) : fallback;
31
+ }
32
+
33
+ function id(value) {
34
+ const result = text(value, '', 128);
35
+ return /^[a-z\d_-]+$/i.test(result) ? result : undefined;
36
+ }
37
+
38
+ function optionalUrl(value) {
39
+ const raw = text(value, '', 512);
40
+ return /^https?:\/\//iu.test(raw) ? raw : null;
41
+ }
42
+
43
+ function timestamp(value) {
44
+ if (typeof value === 'number' && Number.isFinite(value)) return value;
45
+ const parsed = typeof value === 'string' ? Date.parse(value) : Number.NaN;
46
+ return Number.isNaN(parsed) ? undefined : parsed;
47
+ }
48
+
49
+ function normalizeTestMessage(value) {
50
+ if (!isRecord(value)) return null;
51
+ if (value.sent === true) return { sent: true };
52
+ if (value.sent !== false) return null;
53
+ const code = value.code === 'test-target-unavailable'
54
+ ? 'test-target-unavailable'
55
+ : 'test-message-failed';
56
+ return { sent: false, code };
57
+ }
58
+
59
+ export function unwrapRpcResult(result) {
60
+ if (!isRecord(result) || typeof result.ok !== 'boolean') throw new Error('企业微信应用服务返回了无法识别的响应');
61
+ if (!result.ok) {
62
+ const error = new Error(text(result.error?.message, '企业微信应用操作失败'));
63
+ error.code = text(result.error?.code, 'WECOM_APP_RPC_ERROR', 80);
64
+ throw error;
65
+ }
66
+ return result.value;
67
+ }
68
+
69
+ function normalizeBot(value) {
70
+ if (!isRecord(value) || !id(value.botId)) return undefined;
71
+ const connected = value.connected === true;
72
+ const state = ACCOUNT_STATES.has(value.state) ? value.state : 'offline';
73
+ return {
74
+ botId: id(value.botId),
75
+ connected,
76
+ state: connected ? 'connected' : state,
77
+ workspace: text(value.workspace, '', 4_096),
78
+ model: normalizeModelSelection(value.model),
79
+ agentPreset: normalizeAgentPresetId(value.agentPreset),
80
+ contextEnhancement: normalizeContextEnhancementConfig(value.contextEnhancement),
81
+ ...(Object.hasOwn(value, 'accessPolicy')
82
+ ? { accessPolicy: normalizeAccessPolicy(value.accessPolicy) }
83
+ : {}),
84
+ bot: {
85
+ name: text(value.bot?.name, '企业微信应用', 100),
86
+ corpIdMasked: text(value.bot?.corpIdMasked, '企业 ID 已保存', 140),
87
+ agentId: text(value.bot?.agentId, '', 32),
88
+ apiBaseUrl: optionalUrl(value.bot?.apiBaseUrl) ?? '',
89
+ callbackBaseUrl: optionalUrl(value.bot?.callbackBaseUrl) ?? '',
90
+ streamEnabled: value.bot?.streamEnabled !== false,
91
+ callbackUrl: optionalUrl(value.bot?.callbackUrl) ?? '',
92
+ },
93
+ health: {
94
+ summary: text(value.health?.summary, connected ? '企业微信应用回调通道就绪' : '企业微信应用连接尚未就绪'),
95
+ lastCheckedAt: timestamp(value.health?.lastCheckedAt),
96
+ },
97
+ lastMessageError: normalizeLastMessageError(value.lastMessageError),
98
+ error: isRecord(value.error) ? {
99
+ code: text(value.error.code, 'WECOM_APP_ACCOUNT_ERROR', 80),
100
+ message: text(value.error.message, '企业微信应用连接尚未就绪'),
101
+ } : null,
102
+ };
103
+ }
104
+
105
+ export function normalizeSnapshot(value) {
106
+ const source = isRecord(value?.snapshot) ? value.snapshot : value;
107
+ if (!isRecord(source) || !Array.isArray(source.bots)) throw new Error('企业微信应用服务没有返回有效的机器人列表');
108
+ const bots = source.bots.map(normalizeBot).filter(Boolean);
109
+ return {
110
+ revision: Number.isSafeInteger(source.revision) ? source.revision : 0,
111
+ bots,
112
+ totals: { configured: bots.length, connected: bots.filter((bot) => bot.connected).length },
113
+ testMessage: normalizeTestMessage(source.testMessage),
114
+ agentPresetCatalog: normalizeAgentPresetCatalog(source.agentPresetCatalog),
115
+ modelCatalog: normalizeModelCatalog(source.modelCatalog),
116
+ };
117
+ }
118
+
119
+ export function presentError(error) {
120
+ return {
121
+ code: text(error?.code, 'WECOM_APP_ERROR', 80),
122
+ message: text(error?.message, '企业微信应用操作失败,请稍后重试'),
123
+ };
124
+ }