@xmanrui/dsh-im 3.1.0 → 3.2.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.
- package/README.en.md +15 -0
- package/README.md +15 -0
- package/lib/client.js +1653 -333
- package/lib/index.js +235 -223
- package/package.json +5 -1
- package/plugin-src/client/channels/dingtalk/api.js +3 -0
- package/plugin-src/client/channels/dingtalk/index.js +18 -5
- package/plugin-src/client/channels/feishu/api.js +3 -0
- package/plugin-src/client/channels/feishu/index.js +18 -5
- package/plugin-src/client/channels/qq/api.js +3 -0
- package/plugin-src/client/channels/qq/index.js +13 -0
- package/plugin-src/client/channels/shared/token-api.js +3 -0
- package/plugin-src/client/channels/shared/token-channel.js +13 -1
- package/plugin-src/client/channels/wecom/api.js +3 -0
- package/plugin-src/client/channels/wecom/index.js +13 -0
- package/plugin-src/client/channels/weixin/api.js +3 -0
- package/plugin-src/client/channels/weixin/index.js +19 -5
- package/plugin-src/client/channels/whatsapp/api.js +3 -0
- package/plugin-src/client/channels/whatsapp/index.js +13 -0
- package/plugin-src/client/context-enhancement.js +275 -0
- package/plugin-src/client/i18n.js +54 -3
- package/plugin-src/client/styles.js +78 -0
- package/plugin-src/client/update-panel.js +108 -7
- package/plugin-src/host/channels/dingtalk/production.mjs +1 -0
- package/plugin-src/host/channels/dingtalk/rpc.mjs +11 -0
- package/plugin-src/host/channels/feishu/production.mjs +3 -0
- package/plugin-src/host/channels/feishu/rpc.mjs +13 -0
- package/plugin-src/host/channels/qq/production.mjs +1 -0
- package/plugin-src/host/channels/qq/rpc.mjs +11 -0
- package/plugin-src/host/channels/shared/context-enhancement-rpc.mjs +17 -0
- package/plugin-src/host/channels/shared/production.mjs +1 -0
- package/plugin-src/host/channels/shared/rpc.mjs +9 -0
- package/plugin-src/host/channels/shared/workspace-rpc.mjs +1 -0
- package/plugin-src/host/channels/slack/production.mjs +1 -0
- package/plugin-src/host/channels/slack/rpc.mjs +10 -0
- package/plugin-src/host/channels/wecom/production.mjs +1 -0
- package/plugin-src/host/channels/wecom/rpc.mjs +11 -0
- package/plugin-src/host/channels/weixin/production.mjs +1 -0
- package/plugin-src/host/channels/weixin/rpc.mjs +11 -0
- package/plugin-src/host/channels/whatsapp/production.mjs +1 -0
- package/plugin-src/host/channels/whatsapp/rpc.mjs +11 -0
- package/plugin-src/host/update-runtime.mjs +3 -2
- package/plugin-src/host/update-service.mjs +2 -0
- package/scripts/verify-package.mjs +15 -2
- package/src/channels/dingtalk/dingtalk-api.mjs +39 -16
- package/src/channels/dingtalk/dingtalk-bridge.mjs +52 -23
- package/src/channels/dingtalk/dingtalk-runtime.mjs +20 -1
- package/src/channels/discord/discord-runtime.mjs +18 -4
- package/src/channels/feishu/bridge.mjs +20 -4
- package/src/channels/feishu/feishu-channel.mjs +100 -44
- package/src/channels/feishu/feishu-runtime.mjs +4 -0
- package/src/channels/qq/qq-bridge.mjs +20 -4
- package/src/channels/qq/qq-runtime.mjs +4 -0
- package/src/channels/shared/bot-workspace-store.mjs +103 -6
- package/src/channels/shared/context-enhancement.mjs +135 -0
- package/src/channels/shared/i18n-en/feishu.mjs +2 -0
- package/src/channels/shared/text-harness-bridge.mjs +19 -4
- package/src/channels/slack/slack-runtime.mjs +4 -0
- package/src/channels/telegram/telegram-runtime.mjs +24 -2
- package/src/channels/wecom/wecom-bridge.mjs +18 -3
- package/src/channels/wecom/wecom-runtime.mjs +4 -0
- package/src/channels/weixin/weixin-bridge.mjs +19 -4
- package/src/channels/weixin/weixin-runtime.mjs +4 -0
- package/src/channels/whatsapp/whatsapp-runtime.mjs +5 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import QRCode from 'qrcode';
|
|
2
|
+
import { SET_CONTEXT_ENHANCEMENT_ENDPOINT, validContextEnhancementPayload } from '../shared/context-enhancement-rpc.mjs';
|
|
2
3
|
import { resolveRpcAuthority } from '../../rpc-authority.mjs';
|
|
3
4
|
import { publicWorkspaceError, SET_WORKSPACE_ENDPOINT, validWorkspacePayload } from '../shared/workspace-rpc.mjs';
|
|
4
5
|
import { SET_AGENT_PRESET_ENDPOINT, validAgentPresetPayload } from '../shared/agent-preset-rpc.mjs';
|
|
@@ -18,6 +19,7 @@ export const DINGTALK_ENDPOINTS = Object.freeze({
|
|
|
18
19
|
deleteBot: 'bot.delete',
|
|
19
20
|
setWorkspace: SET_WORKSPACE_ENDPOINT,
|
|
20
21
|
setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
|
|
22
|
+
setContextEnhancement: SET_CONTEXT_ENHANCEMENT_ENDPOINT,
|
|
21
23
|
approveSender: 'bot.sender.approve',
|
|
22
24
|
revokeSender: 'bot.sender.revoke',
|
|
23
25
|
});
|
|
@@ -94,6 +96,10 @@ function payloadFailure(endpoint, payload) {
|
|
|
94
96
|
return validAgentPresetPayload(payload)
|
|
95
97
|
? null : '请选择 Agent Preset。';
|
|
96
98
|
}
|
|
99
|
+
if (endpoint === DINGTALK_ENDPOINTS.setContextEnhancement) {
|
|
100
|
+
return validContextEnhancementPayload(payload)
|
|
101
|
+
? null : '请提交有效的上下文增强设置。';
|
|
102
|
+
}
|
|
97
103
|
if (endpoint === DINGTALK_ENDPOINTS.approveSender) {
|
|
98
104
|
return exactKeys(payload, ['botId', 'requestId', 'confirm'])
|
|
99
105
|
&& validId(payload.botId)
|
|
@@ -251,6 +257,11 @@ export function createDingtalkRpcHandler(controller, { encodeQr = qrDataUrl } =
|
|
|
251
257
|
await controller.updateWorkspace(payload.botId, payload.workspace),
|
|
252
258
|
cachedEncode,
|
|
253
259
|
);
|
|
260
|
+
} else if (endpoint === DINGTALK_ENDPOINTS.setContextEnhancement) {
|
|
261
|
+
if (typeof controller.updateContextEnhancement !== 'function') throw new Error('Context enhancement update is unavailable');
|
|
262
|
+
value = await controller.updateContextEnhancement(
|
|
263
|
+
payload.botId, payload.config, (status) => publicStatus(status, cachedEncode),
|
|
264
|
+
);
|
|
254
265
|
} else if (endpoint === DINGTALK_ENDPOINTS.setAgentPreset) {
|
|
255
266
|
if (typeof controller.updateAgentPreset !== 'function') throw new Error('Agent preset update is unavailable');
|
|
256
267
|
value = await publicStatus(
|
|
@@ -98,6 +98,8 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|
|
98
98
|
workspaces,
|
|
99
99
|
method: 'removeBot',
|
|
100
100
|
botIdFromRemoved: (removed) => removed.id,
|
|
101
|
+
saveMethod: 'saveBot',
|
|
102
|
+
botIdFromSave: (bot) => bot?.id,
|
|
101
103
|
})
|
|
102
104
|
: configStore;
|
|
103
105
|
// State is lazy per bot. A corrupt legacy file can therefore fail only the
|
|
@@ -167,6 +169,7 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|
|
167
169
|
ownerOpenIds: botConfig.ownerOpenIds ?? [botConfig.ownerOpenId],
|
|
168
170
|
harness: workspaceScope.harness,
|
|
169
171
|
state: workspaceScope.state,
|
|
172
|
+
contextEnhancement: { botId: id, getSettings: () => workspaces.contextEnhancementFor(id) },
|
|
170
173
|
replyTimeoutMs: config.replyTimeoutMs ?? 600_000,
|
|
171
174
|
...(wsAgent ? { wsAgent } : {}),
|
|
172
175
|
logger: {
|
|
@@ -8,6 +8,8 @@ import { publicMessageFailure } from '../../../../src/channels/shared/message-fa
|
|
|
8
8
|
import { resolveRpcAuthority } from '../../rpc-authority.mjs';
|
|
9
9
|
import { publicWorkspaceError, validWorkspacePayload } from '../shared/workspace-rpc.mjs';
|
|
10
10
|
import { validAgentPresetPayload } from '../shared/agent-preset-rpc.mjs';
|
|
11
|
+
import { validContextEnhancementPayload } from '../shared/context-enhancement-rpc.mjs';
|
|
12
|
+
import { normalizeContextEnhancementConfig } from '../../../../src/channels/shared/context-enhancement.mjs';
|
|
11
13
|
import {
|
|
12
14
|
isFeishuGroupResponseMode,
|
|
13
15
|
normalizeFeishuGroupResponseMode,
|
|
@@ -276,6 +278,7 @@ function publicBotEntry(entry) {
|
|
|
276
278
|
connected,
|
|
277
279
|
configured: source.configured === true,
|
|
278
280
|
agentPreset: normalizeAgentPresetId(source.agentPreset),
|
|
281
|
+
contextEnhancement: normalizeContextEnhancementConfig(source.contextEnhancement),
|
|
279
282
|
groupResponseMode: normalizeFeishuGroupResponseMode(source.groupResponseMode),
|
|
280
283
|
groupMessagePermissionGranted: source.groupMessagePermissionGranted === true,
|
|
281
284
|
bot: publicBot(source.bot),
|
|
@@ -415,6 +418,10 @@ function validPayload(endpoint, payload) {
|
|
|
415
418
|
return validAgentPresetPayload(payload)
|
|
416
419
|
? null : '请选择 Agent Preset。';
|
|
417
420
|
}
|
|
421
|
+
if (endpoint === FEISHU_ENDPOINTS.setContextEnhancement) {
|
|
422
|
+
return validContextEnhancementPayload(payload)
|
|
423
|
+
? null : '请提交有效的上下文增强设置。';
|
|
424
|
+
}
|
|
418
425
|
if (endpoint === FEISHU_ENDPOINTS.setGroupResponseMode) {
|
|
419
426
|
return hasOnlyKeys(payload, new Set(['botId', 'groupResponseMode']))
|
|
420
427
|
&& safeOpaqueId(payload.botId)
|
|
@@ -666,6 +673,12 @@ export function createFeishuRpcHandler(controller, { encodeQr = qrCodeDataUrl }
|
|
|
666
673
|
await controller.updateWorkspace(payload.botId, payload.workspace),
|
|
667
674
|
{ encodeQr: cachedEncodeQr },
|
|
668
675
|
);
|
|
676
|
+
} else if (endpoint === FEISHU_ENDPOINTS.setContextEnhancement) {
|
|
677
|
+
if (typeof controller.updateContextEnhancement !== 'function') throw new Error('Context enhancement update is unavailable');
|
|
678
|
+
value = await controller.updateContextEnhancement(
|
|
679
|
+
payload.botId, payload.config,
|
|
680
|
+
(status) => toPublicFeishuStatus(status, { encodeQr: cachedEncodeQr }),
|
|
681
|
+
);
|
|
669
682
|
} else if (endpoint === FEISHU_ENDPOINTS.setAgentPreset) {
|
|
670
683
|
if (typeof controller.updateAgentPreset !== 'function') throw new Error('Agent preset update is unavailable');
|
|
671
684
|
value = await toPublicFeishuStatus(
|
|
@@ -100,6 +100,7 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|
|
100
100
|
appSecret,
|
|
101
101
|
harness: workspaceScope.harness,
|
|
102
102
|
state: workspaceScope.state,
|
|
103
|
+
contextEnhancement: { botId, getSettings: () => workspaces.contextEnhancementFor(botId) },
|
|
103
104
|
replyTimeoutMs: config.replyTimeoutMs ?? 600_000,
|
|
104
105
|
connectTimeoutMs: config.connectTimeoutMs ?? 20_000,
|
|
105
106
|
logger: {
|
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
connectionTestTargetUnavailable,
|
|
4
4
|
publicConnectionTestResult,
|
|
5
5
|
} from '../../../../src/channels/shared/connection-test.mjs';
|
|
6
|
+
import { SET_CONTEXT_ENHANCEMENT_ENDPOINT, validContextEnhancementPayload } from '../shared/context-enhancement-rpc.mjs';
|
|
6
7
|
import { resolveRpcAuthority } from '../../rpc-authority.mjs';
|
|
7
8
|
import { publicWorkspaceError, SET_WORKSPACE_ENDPOINT, validWorkspacePayload } from '../shared/workspace-rpc.mjs';
|
|
8
9
|
import { SET_AGENT_PRESET_ENDPOINT, validAgentPresetPayload } from '../shared/agent-preset-rpc.mjs';
|
|
@@ -18,6 +19,7 @@ export const QQ_ENDPOINTS = Object.freeze({
|
|
|
18
19
|
deleteBot: 'bot.delete',
|
|
19
20
|
setWorkspace: SET_WORKSPACE_ENDPOINT,
|
|
20
21
|
setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
|
|
22
|
+
setContextEnhancement: SET_CONTEXT_ENHANCEMENT_ENDPOINT,
|
|
21
23
|
});
|
|
22
24
|
export const QQ_RPC_ENDPOINTS = Object.freeze(Object.values(QQ_ENDPOINTS));
|
|
23
25
|
|
|
@@ -76,6 +78,10 @@ function payloadFailure(endpoint, payload) {
|
|
|
76
78
|
return validAgentPresetPayload(payload)
|
|
77
79
|
? null : '请选择 Agent Preset。';
|
|
78
80
|
}
|
|
81
|
+
if (endpoint === QQ_ENDPOINTS.setContextEnhancement) {
|
|
82
|
+
return validContextEnhancementPayload(payload)
|
|
83
|
+
? null : '请提交有效的上下文增强设置。';
|
|
84
|
+
}
|
|
79
85
|
return 'Unknown QQ endpoint.';
|
|
80
86
|
}
|
|
81
87
|
|
|
@@ -171,6 +177,11 @@ export function createQqRpcHandler(controller, { encodeQr = qrDataUrl } = {}) {
|
|
|
171
177
|
await controller.updateWorkspace(payload.botId, payload.workspace),
|
|
172
178
|
cachedEncode,
|
|
173
179
|
);
|
|
180
|
+
} else if (endpoint === QQ_ENDPOINTS.setContextEnhancement) {
|
|
181
|
+
if (typeof controller.updateContextEnhancement !== 'function') throw new Error('Context enhancement update is unavailable');
|
|
182
|
+
value = await controller.updateContextEnhancement(
|
|
183
|
+
payload.botId, payload.config, (status) => publicStatus(status, cachedEncode),
|
|
184
|
+
);
|
|
174
185
|
} else if (endpoint === QQ_ENDPOINTS.setAgentPreset) {
|
|
175
186
|
if (typeof controller.updateAgentPreset !== 'function') throw new Error('Agent preset update is unavailable');
|
|
176
187
|
value = await publicStatus(
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { validateContextEnhancementConfig } from '../../../../src/channels/shared/context-enhancement.mjs';
|
|
2
|
+
|
|
3
|
+
export const SET_CONTEXT_ENHANCEMENT_ENDPOINT = 'bot.context-enhancement.set';
|
|
4
|
+
|
|
5
|
+
export function validContextEnhancementPayload(payload) {
|
|
6
|
+
try {
|
|
7
|
+
if (!payload || typeof payload !== 'object' || Array.isArray(payload)
|
|
8
|
+
|| Reflect.ownKeys(payload).length !== 2
|
|
9
|
+
|| !Object.hasOwn(payload, 'botId') || !Object.hasOwn(payload, 'config')
|
|
10
|
+
|| typeof payload.botId !== 'string'
|
|
11
|
+
|| !/^[A-Za-z0-9_-]{1,128}$/.test(payload.botId)) return false;
|
|
12
|
+
validateContextEnhancementConfig(payload.config);
|
|
13
|
+
return true;
|
|
14
|
+
} catch {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -102,6 +102,7 @@ export async function createTokenProductionController(ctx, config, internals, de
|
|
|
102
102
|
token,
|
|
103
103
|
harness: workspaceScope.harness,
|
|
104
104
|
state: workspaceScope.state,
|
|
105
|
+
contextEnhancement: { botId, getSettings: () => workspaces.contextEnhancementFor(botId) },
|
|
105
106
|
replyTimeoutMs: config.replyTimeoutMs ?? 600_000,
|
|
106
107
|
connectTimeoutMs: config.connectTimeoutMs ?? 20_000,
|
|
107
108
|
logger: {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { SET_CONTEXT_ENHANCEMENT_ENDPOINT, validContextEnhancementPayload } from './context-enhancement-rpc.mjs';
|
|
1
2
|
import { resolveRpcAuthority } from '../../rpc-authority.mjs';
|
|
2
3
|
import { publicConnectionTestResult } from '../../../../src/channels/shared/connection-test.mjs';
|
|
3
4
|
import {
|
|
@@ -17,6 +18,7 @@ export const TOKEN_BOT_ENDPOINTS = Object.freeze({
|
|
|
17
18
|
deleteBot: 'bot.delete',
|
|
18
19
|
setWorkspace: SET_WORKSPACE_ENDPOINT,
|
|
19
20
|
setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
|
|
21
|
+
setContextEnhancement: SET_CONTEXT_ENHANCEMENT_ENDPOINT,
|
|
20
22
|
});
|
|
21
23
|
|
|
22
24
|
const ENDPOINTS = Object.freeze(Object.values(TOKEN_BOT_ENDPOINTS));
|
|
@@ -71,6 +73,10 @@ function payloadFailure(endpoint, payload) {
|
|
|
71
73
|
return validAgentPresetPayload(payload)
|
|
72
74
|
? null : '请选择 Agent Preset。';
|
|
73
75
|
}
|
|
76
|
+
if (endpoint === TOKEN_BOT_ENDPOINTS.setContextEnhancement) {
|
|
77
|
+
return validContextEnhancementPayload(payload)
|
|
78
|
+
? null : '请提交有效的上下文增强设置。';
|
|
79
|
+
}
|
|
74
80
|
return 'Unknown bot endpoint.';
|
|
75
81
|
}
|
|
76
82
|
|
|
@@ -152,6 +158,9 @@ export function createTokenBotRpcHandler(controller, { channel }) {
|
|
|
152
158
|
} else if (endpoint === TOKEN_BOT_ENDPOINTS.setWorkspace) {
|
|
153
159
|
if (typeof controller.updateWorkspace !== 'function') throw new Error('Workspace update is unavailable');
|
|
154
160
|
value = await controller.updateWorkspace(payload.botId, payload.workspace);
|
|
161
|
+
} else if (endpoint === TOKEN_BOT_ENDPOINTS.setContextEnhancement) {
|
|
162
|
+
if (typeof controller.updateContextEnhancement !== 'function') throw new Error('Context enhancement update is unavailable');
|
|
163
|
+
value = await controller.updateContextEnhancement(payload.botId, payload.config);
|
|
155
164
|
} else if (endpoint === TOKEN_BOT_ENDPOINTS.setAgentPreset) {
|
|
156
165
|
if (typeof controller.updateAgentPreset !== 'function') throw new Error('Agent preset update is unavailable');
|
|
157
166
|
value = await controller.updateAgentPreset(payload.botId, payload.agentPreset);
|
|
@@ -22,6 +22,7 @@ export function publicWorkspaceError(error) {
|
|
|
22
22
|
'workspace-bot-not-found',
|
|
23
23
|
'agent-preset-invalid',
|
|
24
24
|
'agent-preset-unavailable',
|
|
25
|
+
'context-enhancement-invalid',
|
|
25
26
|
].includes(error?.code)) return null;
|
|
26
27
|
return { code: error.code, message: error.message };
|
|
27
28
|
}
|
|
@@ -89,6 +89,7 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|
|
89
89
|
appToken,
|
|
90
90
|
harness: workspaceScope.harness,
|
|
91
91
|
state: workspaceScope.state,
|
|
92
|
+
contextEnhancement: { botId, getSettings: () => workspaces.contextEnhancementFor(botId) },
|
|
92
93
|
replyTimeoutMs: config.replyTimeoutMs ?? 600_000,
|
|
93
94
|
connectTimeoutMs: config.connectTimeoutMs ?? 20_000,
|
|
94
95
|
logger: {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { SET_CONTEXT_ENHANCEMENT_ENDPOINT, validContextEnhancementPayload } from '../shared/context-enhancement-rpc.mjs';
|
|
1
2
|
import { resolveRpcAuthority } from '../../rpc-authority.mjs';
|
|
2
3
|
import { publicConnectionTestResult } from '../../../../src/channels/shared/connection-test.mjs';
|
|
3
4
|
import {
|
|
@@ -18,6 +19,7 @@ export const SLACK_ENDPOINTS = Object.freeze({
|
|
|
18
19
|
deleteBot: 'bot.delete',
|
|
19
20
|
setWorkspace: SET_WORKSPACE_ENDPOINT,
|
|
20
21
|
setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
|
|
22
|
+
setContextEnhancement: SET_CONTEXT_ENHANCEMENT_ENDPOINT,
|
|
21
23
|
});
|
|
22
24
|
export const SLACK_RPC_ENDPOINTS = Object.freeze(Object.values(SLACK_ENDPOINTS));
|
|
23
25
|
|
|
@@ -75,6 +77,10 @@ function payloadFailure(endpoint, payload) {
|
|
|
75
77
|
return validAgentPresetPayload(payload)
|
|
76
78
|
? null : '请选择 Agent Preset。';
|
|
77
79
|
}
|
|
80
|
+
if (endpoint === SLACK_ENDPOINTS.setContextEnhancement) {
|
|
81
|
+
return validContextEnhancementPayload(payload)
|
|
82
|
+
? null : '请提交有效的上下文增强设置。';
|
|
83
|
+
}
|
|
78
84
|
return 'Unknown Slack endpoint.';
|
|
79
85
|
}
|
|
80
86
|
|
|
@@ -154,6 +160,10 @@ export function createSlackRpcHandler(controller) {
|
|
|
154
160
|
if (typeof controller.updateWorkspace !== 'function') throw new Error('Workspace update is unavailable');
|
|
155
161
|
value = await controller.updateWorkspace(payload.botId, payload.workspace);
|
|
156
162
|
}
|
|
163
|
+
else if (endpoint === SLACK_ENDPOINTS.setContextEnhancement) {
|
|
164
|
+
if (typeof controller.updateContextEnhancement !== 'function') throw new Error('Context enhancement update is unavailable');
|
|
165
|
+
value = await controller.updateContextEnhancement(payload.botId, payload.config);
|
|
166
|
+
}
|
|
157
167
|
else if (endpoint === SLACK_ENDPOINTS.setAgentPreset) {
|
|
158
168
|
if (typeof controller.updateAgentPreset !== 'function') throw new Error('Agent preset update is unavailable');
|
|
159
169
|
value = await controller.updateAgentPreset(payload.botId, payload.agentPreset);
|
|
@@ -103,6 +103,7 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|
|
103
103
|
secret,
|
|
104
104
|
harness: workspaceScope.harness,
|
|
105
105
|
state: workspaceScope.state,
|
|
106
|
+
contextEnhancement: { botId, getSettings: () => workspaces.contextEnhancementFor(botId) },
|
|
106
107
|
replyTimeoutMs: config.replyTimeoutMs ?? 600_000,
|
|
107
108
|
connectTimeoutMs: config.connectTimeoutMs ?? 20_000,
|
|
108
109
|
maxReconnectAttempts: config.maxReconnectAttempts ?? 10,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import QRCode from 'qrcode';
|
|
2
|
+
import { SET_CONTEXT_ENHANCEMENT_ENDPOINT, validContextEnhancementPayload } from '../shared/context-enhancement-rpc.mjs';
|
|
2
3
|
import { resolveRpcAuthority } from '../../rpc-authority.mjs';
|
|
3
4
|
import { publicWorkspaceError, SET_WORKSPACE_ENDPOINT, validWorkspacePayload } from '../shared/workspace-rpc.mjs';
|
|
4
5
|
import { SET_AGENT_PRESET_ENDPOINT, validAgentPresetPayload } from '../shared/agent-preset-rpc.mjs';
|
|
@@ -18,6 +19,7 @@ export const WECOM_ENDPOINTS = Object.freeze({
|
|
|
18
19
|
deleteBot: 'bot.delete',
|
|
19
20
|
setWorkspace: SET_WORKSPACE_ENDPOINT,
|
|
20
21
|
setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
|
|
22
|
+
setContextEnhancement: SET_CONTEXT_ENHANCEMENT_ENDPOINT,
|
|
21
23
|
});
|
|
22
24
|
export const WECOM_RPC_ENDPOINTS = Object.freeze(Object.values(WECOM_ENDPOINTS));
|
|
23
25
|
|
|
@@ -76,6 +78,10 @@ function payloadFailure(endpoint, payload) {
|
|
|
76
78
|
return validAgentPresetPayload(payload)
|
|
77
79
|
? null : '请选择 Agent Preset。';
|
|
78
80
|
}
|
|
81
|
+
if (endpoint === WECOM_ENDPOINTS.setContextEnhancement) {
|
|
82
|
+
return validContextEnhancementPayload(payload)
|
|
83
|
+
? null : '请提交有效的上下文增强设置。';
|
|
84
|
+
}
|
|
79
85
|
return 'Unknown Enterprise WeChat endpoint.';
|
|
80
86
|
}
|
|
81
87
|
|
|
@@ -172,6 +178,11 @@ export function createWecomRpcHandler(controller, { encodeQr = qrDataUrl } = {})
|
|
|
172
178
|
await controller.updateWorkspace(payload.botId, payload.workspace),
|
|
173
179
|
cachedEncode,
|
|
174
180
|
);
|
|
181
|
+
} else if (endpoint === WECOM_ENDPOINTS.setContextEnhancement) {
|
|
182
|
+
if (typeof controller.updateContextEnhancement !== 'function') throw new Error('Context enhancement update is unavailable');
|
|
183
|
+
value = await controller.updateContextEnhancement(
|
|
184
|
+
payload.botId, payload.config, (status) => publicStatus(status, cachedEncode),
|
|
185
|
+
);
|
|
175
186
|
} else if (endpoint === WECOM_ENDPOINTS.setAgentPreset) {
|
|
176
187
|
if (typeof controller.updateAgentPreset !== 'function') throw new Error('Agent preset update is unavailable');
|
|
177
188
|
value = await publicStatus(
|
|
@@ -107,6 +107,7 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|
|
107
107
|
token,
|
|
108
108
|
harness: workspaceScope.harness,
|
|
109
109
|
state: workspaceScope.state,
|
|
110
|
+
contextEnhancement: { botId, getSettings: () => workspaces.contextEnhancementFor(botId) },
|
|
110
111
|
replyTimeoutMs: config.replyTimeoutMs ?? 600_000,
|
|
111
112
|
maxMessageChars: config.maxMessageChars ?? DEFAULT_WEIXIN_MAX_MESSAGE_CHARS,
|
|
112
113
|
logger: {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import QRCode from 'qrcode';
|
|
2
|
+
import { SET_CONTEXT_ENHANCEMENT_ENDPOINT, validContextEnhancementPayload } from '../shared/context-enhancement-rpc.mjs';
|
|
2
3
|
import { resolveRpcAuthority } from '../../rpc-authority.mjs';
|
|
3
4
|
import {
|
|
4
5
|
publicWorkspaceError,
|
|
@@ -25,6 +26,7 @@ export const WEIXIN_ENDPOINTS = Object.freeze({
|
|
|
25
26
|
deleteBot: 'bot.delete',
|
|
26
27
|
setWorkspace: SET_WORKSPACE_ENDPOINT,
|
|
27
28
|
setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
|
|
29
|
+
setContextEnhancement: SET_CONTEXT_ENHANCEMENT_ENDPOINT,
|
|
28
30
|
});
|
|
29
31
|
export const WEIXIN_RPC_ENDPOINTS = Object.freeze(Object.values(WEIXIN_ENDPOINTS));
|
|
30
32
|
|
|
@@ -83,6 +85,10 @@ function payloadFailure(endpoint, payload) {
|
|
|
83
85
|
return validAgentPresetPayload(payload)
|
|
84
86
|
? null : '请选择 Agent Preset。';
|
|
85
87
|
}
|
|
88
|
+
if (endpoint === WEIXIN_ENDPOINTS.setContextEnhancement) {
|
|
89
|
+
return validContextEnhancementPayload(payload)
|
|
90
|
+
? null : '请提交有效的上下文增强设置。';
|
|
91
|
+
}
|
|
86
92
|
return 'Unknown Weixin endpoint.';
|
|
87
93
|
}
|
|
88
94
|
|
|
@@ -207,6 +213,11 @@ export function createWeixinRpcHandler(controller, { encodeQr = qrDataUrl } = {}
|
|
|
207
213
|
await controller.updateWorkspace(payload.botId, payload.workspace),
|
|
208
214
|
cachedEncode,
|
|
209
215
|
);
|
|
216
|
+
} else if (endpoint === WEIXIN_ENDPOINTS.setContextEnhancement) {
|
|
217
|
+
if (typeof controller.updateContextEnhancement !== 'function') throw new Error('Context enhancement update is unavailable');
|
|
218
|
+
value = await controller.updateContextEnhancement(
|
|
219
|
+
payload.botId, payload.config, (status) => publicStatus(status, cachedEncode),
|
|
220
|
+
);
|
|
210
221
|
} else if (endpoint === WEIXIN_ENDPOINTS.setAgentPreset) {
|
|
211
222
|
if (typeof controller.updateAgentPreset !== 'function') throw new Error('Agent preset update is unavailable');
|
|
212
223
|
value = await publicStatus(
|
|
@@ -106,6 +106,7 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|
|
106
106
|
authDir,
|
|
107
107
|
harness: workspaceScope.harness,
|
|
108
108
|
state: workspaceScope.state,
|
|
109
|
+
contextEnhancement: { botId, getSettings: () => workspaces.contextEnhancementFor(botId) },
|
|
109
110
|
replyTimeoutMs: config.replyTimeoutMs ?? 600_000,
|
|
110
111
|
connectTimeoutMs: config.connectTimeoutMs ?? 30_000,
|
|
111
112
|
createSession,
|
|
@@ -2,6 +2,7 @@ import QRCode from 'qrcode';
|
|
|
2
2
|
|
|
3
3
|
import { publicConnectionTestResult } from '../../../../src/channels/shared/connection-test.mjs';
|
|
4
4
|
import { normalizeWhatsappAccessPolicy } from '../../../../src/channels/whatsapp/config-store.mjs';
|
|
5
|
+
import { SET_CONTEXT_ENHANCEMENT_ENDPOINT, validContextEnhancementPayload } from '../shared/context-enhancement-rpc.mjs';
|
|
5
6
|
import { resolveRpcAuthority } from '../../rpc-authority.mjs';
|
|
6
7
|
import { publicWorkspaceError, SET_WORKSPACE_ENDPOINT, validWorkspacePayload } from '../shared/workspace-rpc.mjs';
|
|
7
8
|
import { SET_AGENT_PRESET_ENDPOINT, validAgentPresetPayload } from '../shared/agent-preset-rpc.mjs';
|
|
@@ -17,6 +18,7 @@ export const WHATSAPP_ENDPOINTS = Object.freeze({
|
|
|
17
18
|
setAccessPolicy: 'bot.access-policy.set',
|
|
18
19
|
setWorkspace: SET_WORKSPACE_ENDPOINT,
|
|
19
20
|
setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
|
|
21
|
+
setContextEnhancement: SET_CONTEXT_ENHANCEMENT_ENDPOINT,
|
|
20
22
|
});
|
|
21
23
|
export const WHATSAPP_RPC_ENDPOINTS = Object.freeze(Object.values(WHATSAPP_ENDPOINTS));
|
|
22
24
|
|
|
@@ -71,6 +73,10 @@ function payloadFailure(endpoint, payload) {
|
|
|
71
73
|
return validAgentPresetPayload(payload)
|
|
72
74
|
? null : '请选择 Agent Preset。';
|
|
73
75
|
}
|
|
76
|
+
if (endpoint === WHATSAPP_ENDPOINTS.setContextEnhancement) {
|
|
77
|
+
return validContextEnhancementPayload(payload)
|
|
78
|
+
? null : '请提交有效的上下文增强设置。';
|
|
79
|
+
}
|
|
74
80
|
return 'Unknown WhatsApp endpoint.';
|
|
75
81
|
}
|
|
76
82
|
|
|
@@ -173,6 +179,11 @@ export function createWhatsappRpcHandler(controller, { encodeQr = qrDataUrl } =
|
|
|
173
179
|
await controller.updateWorkspace(payload.botId, payload.workspace),
|
|
174
180
|
cachedEncode,
|
|
175
181
|
);
|
|
182
|
+
} else if (endpoint === WHATSAPP_ENDPOINTS.setContextEnhancement) {
|
|
183
|
+
if (typeof controller.updateContextEnhancement !== 'function') throw new Error('Context enhancement update is unavailable');
|
|
184
|
+
value = await controller.updateContextEnhancement(
|
|
185
|
+
payload.botId, payload.config, (status) => publicStatus(status, cachedEncode),
|
|
186
|
+
);
|
|
176
187
|
} else if (endpoint === WHATSAPP_ENDPOINTS.setAgentPreset) {
|
|
177
188
|
if (typeof controller.updateAgentPreset !== 'function') throw new Error('Agent preset update is unavailable');
|
|
178
189
|
value = await publicStatus(
|
|
@@ -280,6 +280,8 @@ export function createUpdateRuntime(options = {}) {
|
|
|
280
280
|
|
|
281
281
|
const profile = await packageAt(runtime.profileDir);
|
|
282
282
|
const installed = await packageAt(join(runtime.profileDir, 'node_modules', PACKAGE_NAME));
|
|
283
|
+
result.sourceInstall = !registrySpec(profile.manifest.dependencies?.[PACKAGE_NAME])
|
|
284
|
+
|| !inside(join(runtime.profileDir, 'node_modules'), installed.directory);
|
|
283
285
|
result.installedVersion = typeof installed.manifest.version === 'string' ? installed.manifest.version : null;
|
|
284
286
|
result.packageValid = await validPackage(installed);
|
|
285
287
|
const loaded = await loadedPackage;
|
|
@@ -298,8 +300,7 @@ export function createUpdateRuntime(options = {}) {
|
|
|
298
300
|
if (boundProfile !== undefined && boundProfile !== identity) result.blockedReason = 'installation-changed';
|
|
299
301
|
else if (!sameLoadedPackage && boundProfile === undefined) result.blockedReason = 'installation-changed';
|
|
300
302
|
else if (!result.packageValid) result.blockedReason = 'invalid-installation';
|
|
301
|
-
else if (
|
|
302
|
-
|| !inside(join(runtime.profileDir, 'node_modules'), installed.directory)) result.blockedReason = 'source-install';
|
|
303
|
+
else if (result.sourceInstall) result.blockedReason = 'source-install';
|
|
303
304
|
else if (runtime.blockedReason) result.blockedReason = runtime.blockedReason;
|
|
304
305
|
else if (!sameLoadedPackage) result.blockedReason = 'pending-restart';
|
|
305
306
|
else if (preflight) await checkRegistry(runtime);
|
|
@@ -201,6 +201,8 @@ export function createUpdateService({
|
|
|
201
201
|
latestVersion: checked?.release.version ?? null,
|
|
202
202
|
profileName: environment.profileName ?? null,
|
|
203
203
|
environmentKind: environment.environmentKind ?? 'cli',
|
|
204
|
+
// Preserve source protection even when restart/recovery takes status priority.
|
|
205
|
+
sourceInstall: environment.sourceInstall === true || environment.blockedReason === 'source-install',
|
|
204
206
|
canInstall,
|
|
205
207
|
blockedReason,
|
|
206
208
|
checkedAt: checked?.checkedAt ?? null,
|
|
@@ -29,6 +29,7 @@ const required = [
|
|
|
29
29
|
'plugin-src/client/channels/slack/index.js',
|
|
30
30
|
'plugin-src/client/i18n.js',
|
|
31
31
|
'plugin-src/client/update-panel.js',
|
|
32
|
+
'plugin-src/client/context-enhancement.js',
|
|
32
33
|
'plugin-src/host/update-service.mjs',
|
|
33
34
|
'plugin-src/host/update-runtime.mjs',
|
|
34
35
|
'plugin-src/host/update-rpc.mjs',
|
|
@@ -51,6 +52,7 @@ const required = [
|
|
|
51
52
|
'src/channels/discord/discord-runtime.mjs',
|
|
52
53
|
'src/channels/whatsapp/whatsapp-runtime.mjs',
|
|
53
54
|
'src/channels/whatsapp/whatsapp-web-session.mjs',
|
|
55
|
+
'src/channels/shared/context-enhancement.mjs',
|
|
54
56
|
];
|
|
55
57
|
await Promise.all(required.map((path) => access(resolve(root, path))));
|
|
56
58
|
|
|
@@ -135,8 +137,19 @@ if ((client.match(/\.slots\.inject\(\s*["']settings\.section["']/gu) ?? []).leng
|
|
|
135
137
|
if (client.includes('settings.plugins.tab') || clientSources.includes('settings.plugins.tab')) {
|
|
136
138
|
throw new Error('client source or bundle still contains the legacy Plugins-tab settings entry');
|
|
137
139
|
}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
+
// Connections still have no channel-enable toggle. Only the shared context
|
|
141
|
+
// editor owns checkable inputs: two scope switches and one mapped field input.
|
|
142
|
+
const contextEditorSource = await readFile(resolve(root, 'plugin-src/client/context-enhancement.js'), 'utf8');
|
|
143
|
+
const otherClientSources = clientSources.replace(contextEditorSource, '');
|
|
144
|
+
if (/role:\s*["']switch|type:\s*["']checkbox/.test(otherClientSources)
|
|
145
|
+
|| (client.match(/role:\s*["']switch["']/g) ?? []).length !== 2
|
|
146
|
+
|| (client.match(/type:\s*["']checkbox["']/g) ?? []).length !== 3) {
|
|
147
|
+
throw new Error('checkable inputs must be limited to the context-enhancement editor');
|
|
148
|
+
}
|
|
149
|
+
for (const marker of ['bot.context-enhancement.set', '<dsh_im_source>', '<dsh_im_source_guidance>']) {
|
|
150
|
+
if (!host.includes(marker) || !client.includes(marker)) {
|
|
151
|
+
throw new Error(`context-enhancement marker missing from Host or Client bundle: ${marker}`);
|
|
152
|
+
}
|
|
140
153
|
}
|
|
141
154
|
if (!client.includes('container-type: inline-size')
|
|
142
155
|
|| !client.includes('@container (max-width: 680px)')) {
|
|
@@ -316,16 +316,30 @@ function normalizeCardTarget(target) {
|
|
|
316
316
|
}
|
|
317
317
|
if (target?.type === 'group') {
|
|
318
318
|
const openConversationId = nonEmptyString(target.openConversationId);
|
|
319
|
-
if (openConversationId)
|
|
319
|
+
if (openConversationId) {
|
|
320
|
+
return { type: 'group', openConversationId, atUserIds: target.atUserIds };
|
|
321
|
+
}
|
|
320
322
|
}
|
|
321
323
|
throw new TypeError('DingTalk AI Card target is invalid');
|
|
322
324
|
}
|
|
323
325
|
|
|
324
|
-
function
|
|
326
|
+
function cardMarkdown(text, target) {
|
|
327
|
+
const escape = (value) => String(value).replace(/[&<>"']/g, (char) => ({
|
|
328
|
+
'&': '&', '<': '<', '>': '>', '"': '"', "'": ''',
|
|
329
|
+
})[char]);
|
|
330
|
+
const mentions = target?.type === 'group'
|
|
331
|
+
? Object.entries(target.atUserIds ?? {}).map(([userId, name]) => (
|
|
332
|
+
`<a atId="${escape(userId)}">${escape(name)}</a>`
|
|
333
|
+
)).join(' ')
|
|
334
|
+
: '';
|
|
335
|
+
return normalizeDingtalkCardMarkdown(mentions ? `${mentions}\n\n${text}` : text);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
function cardData(text, flowStatus, target) {
|
|
325
339
|
return {
|
|
326
340
|
cardParamMap: {
|
|
327
341
|
flowStatus,
|
|
328
|
-
msgContent:
|
|
342
|
+
msgContent: cardMarkdown(text, target),
|
|
329
343
|
staticMsgContent: '',
|
|
330
344
|
sys_full_json_obj: JSON.stringify({ order: ['msgContent'] }),
|
|
331
345
|
config: JSON.stringify({ autoLayout: true }),
|
|
@@ -339,7 +353,7 @@ function cardDeliverBody(cardInstanceId, target, robotCode) {
|
|
|
339
353
|
return {
|
|
340
354
|
...base,
|
|
341
355
|
openSpaceId: `dtv1.card//IM_GROUP.${target.openConversationId}`,
|
|
342
|
-
imGroupOpenDeliverModel: { robotCode },
|
|
356
|
+
imGroupOpenDeliverModel: { robotCode, atUserIds: target.atUserIds },
|
|
343
357
|
};
|
|
344
358
|
}
|
|
345
359
|
return {
|
|
@@ -575,7 +589,7 @@ export function createDingtalkApi({
|
|
|
575
589
|
}
|
|
576
590
|
}
|
|
577
591
|
|
|
578
|
-
async function failCard({ clientId, clientSecret, cardInstanceId, text, signal }) {
|
|
592
|
+
async function failCard({ clientId, clientSecret, cardInstanceId, text, target, signal }) {
|
|
579
593
|
const instanceId = nonEmptyString(cardInstanceId);
|
|
580
594
|
const content = nonEmptyString(text);
|
|
581
595
|
if (!instanceId) throw new TypeError('cardInstanceId is required');
|
|
@@ -589,7 +603,7 @@ export function createDingtalkApi({
|
|
|
589
603
|
outTrackId: instanceId,
|
|
590
604
|
guid: randomUUID(),
|
|
591
605
|
key: 'msgContent',
|
|
592
|
-
content:
|
|
606
|
+
content: cardMarkdown(content, target),
|
|
593
607
|
isFull: true,
|
|
594
608
|
isFinalize: false,
|
|
595
609
|
isError: true,
|
|
@@ -602,7 +616,7 @@ export function createDingtalkApi({
|
|
|
602
616
|
method: 'PUT',
|
|
603
617
|
body: {
|
|
604
618
|
outTrackId: instanceId,
|
|
605
|
-
cardData: cardData(content, '5'),
|
|
619
|
+
cardData: cardData(content, '5', target),
|
|
606
620
|
cardUpdateOptions: { updateCardDataByKey: true },
|
|
607
621
|
},
|
|
608
622
|
headers,
|
|
@@ -883,6 +897,9 @@ export function createDingtalkApi({
|
|
|
883
897
|
cardParamMap: { config: JSON.stringify({ autoLayout: true }) },
|
|
884
898
|
},
|
|
885
899
|
callbackType: 'STREAM',
|
|
900
|
+
cardAtUserIds: normalizedTarget.atUserIds
|
|
901
|
+
? Object.keys(normalizedTarget.atUserIds)
|
|
902
|
+
: undefined,
|
|
886
903
|
imGroupOpenSpaceModel: { supportForward: true },
|
|
887
904
|
imRobotOpenSpaceModel: { supportForward: true },
|
|
888
905
|
},
|
|
@@ -899,7 +916,7 @@ export function createDingtalkApi({
|
|
|
899
916
|
delivered = true;
|
|
900
917
|
await cardRequest('v1.0/card/instances', {
|
|
901
918
|
method: 'PUT',
|
|
902
|
-
body: { outTrackId: cardInstanceId, cardData: cardData(content, '2') },
|
|
919
|
+
body: { outTrackId: cardInstanceId, cardData: cardData(content, '2', normalizedTarget) },
|
|
903
920
|
headers,
|
|
904
921
|
signal,
|
|
905
922
|
action: 'AI Card 启动',
|
|
@@ -910,7 +927,7 @@ export function createDingtalkApi({
|
|
|
910
927
|
outTrackId: cardInstanceId,
|
|
911
928
|
guid: randomUUID(),
|
|
912
929
|
key: 'msgContent',
|
|
913
|
-
content:
|
|
930
|
+
content: cardMarkdown(content, normalizedTarget).replace(/\n+$/, ''),
|
|
914
931
|
isFull: true,
|
|
915
932
|
isFinalize: false,
|
|
916
933
|
isError: false,
|
|
@@ -927,6 +944,7 @@ export function createDingtalkApi({
|
|
|
927
944
|
clientSecret: appSecret,
|
|
928
945
|
cardInstanceId,
|
|
929
946
|
text: t('卡片已结束,请查看后续消息。'),
|
|
947
|
+
target: normalizedTarget,
|
|
930
948
|
signal: cleanupSignal,
|
|
931
949
|
}).catch(() => undefined);
|
|
932
950
|
}
|
|
@@ -935,7 +953,7 @@ export function createDingtalkApi({
|
|
|
935
953
|
return { cardInstanceId };
|
|
936
954
|
},
|
|
937
955
|
|
|
938
|
-
async updateAiCard({ clientId, clientSecret, cardInstanceId, text, signal }) {
|
|
956
|
+
async updateAiCard({ clientId, clientSecret, cardInstanceId, text, target, signal }) {
|
|
939
957
|
const instanceId = nonEmptyString(cardInstanceId);
|
|
940
958
|
const content = nonEmptyString(text);
|
|
941
959
|
if (!instanceId) throw new TypeError('cardInstanceId is required');
|
|
@@ -947,7 +965,7 @@ export function createDingtalkApi({
|
|
|
947
965
|
outTrackId: instanceId,
|
|
948
966
|
guid: randomUUID(),
|
|
949
967
|
key: 'msgContent',
|
|
950
|
-
content:
|
|
968
|
+
content: cardMarkdown(content, target).replace(/\n+$/, ''),
|
|
951
969
|
isFull: true,
|
|
952
970
|
isFinalize: false,
|
|
953
971
|
isError: false,
|
|
@@ -959,14 +977,14 @@ export function createDingtalkApi({
|
|
|
959
977
|
return true;
|
|
960
978
|
},
|
|
961
979
|
|
|
962
|
-
async finishAiCard({ clientId, clientSecret, cardInstanceId, text, signal }) {
|
|
980
|
+
async finishAiCard({ clientId, clientSecret, cardInstanceId, text, target, signal }) {
|
|
963
981
|
const instanceId = nonEmptyString(cardInstanceId);
|
|
964
982
|
const content = nonEmptyString(text);
|
|
965
983
|
if (!instanceId) throw new TypeError('cardInstanceId is required');
|
|
966
984
|
if (!content) throw new TypeError('text is required');
|
|
967
985
|
const token = await accessToken({ clientId, clientSecret, signal });
|
|
968
986
|
const headers = { 'x-acs-dingtalk-access-token': token };
|
|
969
|
-
const normalizedContent =
|
|
987
|
+
const normalizedContent = cardMarkdown(content, target);
|
|
970
988
|
await cardRequest('v1.0/card/streaming', {
|
|
971
989
|
method: 'PUT',
|
|
972
990
|
body: {
|
|
@@ -987,7 +1005,7 @@ export function createDingtalkApi({
|
|
|
987
1005
|
method: 'PUT',
|
|
988
1006
|
body: {
|
|
989
1007
|
outTrackId: instanceId,
|
|
990
|
-
cardData: cardData(content, '3'),
|
|
1008
|
+
cardData: cardData(content, '3', target),
|
|
991
1009
|
cardUpdateOptions: { updateCardDataByKey: true },
|
|
992
1010
|
},
|
|
993
1011
|
headers,
|
|
@@ -1008,13 +1026,18 @@ export function createDingtalkApi({
|
|
|
1008
1026
|
|
|
1009
1027
|
failAiCard: failCard,
|
|
1010
1028
|
|
|
1011
|
-
async sendText({ clientId, clientSecret, sessionWebhook, text, signal }) {
|
|
1029
|
+
async sendText({ clientId, clientSecret, sessionWebhook, text, at, signal }) {
|
|
1012
1030
|
const content = nonEmptyString(text);
|
|
1013
1031
|
if (!content) throw new TypeError('text is required');
|
|
1014
1032
|
const webhook = normalizeDingtalkSessionWebhook(sessionWebhook);
|
|
1015
1033
|
const token = await accessToken({ clientId, clientSecret, signal });
|
|
1034
|
+
const replyAt = at && typeof at === 'object'
|
|
1035
|
+
&& ((Array.isArray(at.atUserIds) && at.atUserIds.length > 0)
|
|
1036
|
+
|| (Array.isArray(at.atMobiles) && at.atMobiles.length > 0)
|
|
1037
|
+
|| at.isAtAll === true)
|
|
1038
|
+
? { at } : {};
|
|
1016
1039
|
const response = await requestJson(fetchImpl, webhook, {
|
|
1017
|
-
body: { msgtype: 'text', text: { content } },
|
|
1040
|
+
body: { msgtype: 'text', text: { content }, ...replyAt },
|
|
1018
1041
|
headers: { 'x-acs-dingtalk-access-token': token },
|
|
1019
1042
|
signal,
|
|
1020
1043
|
action: '消息回复',
|