@xmanrui/dsh-im 2.3.0 → 2.5.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 +9 -4
- package/README.md +9 -4
- package/lib/client.js +411 -156
- package/lib/index.js +218 -210
- package/package.json +1 -1
- package/plugin-src/client/channel-card-meta.js +36 -1
- package/plugin-src/client/channels/dingtalk/api.js +2 -0
- package/plugin-src/client/channels/dingtalk/index.js +9 -1
- package/plugin-src/client/channels/feishu/api.js +3 -0
- package/plugin-src/client/channels/feishu/index.js +46 -28
- package/plugin-src/client/channels/feishu/styles.js +26 -1
- package/plugin-src/client/channels/qq/api.js +2 -0
- package/plugin-src/client/channels/qq/index.js +9 -1
- package/plugin-src/client/channels/shared/token-api.js +2 -0
- package/plugin-src/client/channels/shared/token-channel.js +9 -1
- package/plugin-src/client/channels/wecom/api.js +2 -0
- package/plugin-src/client/channels/wecom/index.js +9 -1
- package/plugin-src/client/channels/weixin/api.js +2 -10
- package/plugin-src/client/channels/weixin/index.js +9 -5
- package/plugin-src/client/channels/whatsapp/api.js +2 -0
- package/plugin-src/client/channels/whatsapp/index.js +9 -1
- package/plugin-src/client/i18n.js +31 -25
- package/plugin-src/client/index.js +16 -2
- package/plugin-src/client/last-message-error.js +17 -0
- package/plugin-src/client/styles.js +5 -1
- package/plugin-src/host/channels/feishu/rpc.mjs +3 -0
- package/src/channels/dingtalk/dingtalk-api.mjs +1 -1
- package/src/channels/dingtalk/dingtalk-bridge.mjs +50 -16
- package/src/channels/dingtalk/dingtalk-card-stream.mjs +2 -2
- package/src/channels/dingtalk/dingtalk-controller.mjs +2 -0
- package/src/channels/feishu/bridge.mjs +148 -70
- package/src/channels/feishu/feishu-cards.mjs +10 -6
- package/src/channels/feishu/feishu-runtime.mjs +0 -1
- package/src/channels/feishu/message-utils.mjs +1 -1
- package/src/channels/feishu/multi-bot-controller.mjs +3 -17
- package/src/channels/feishu/repair-manager.mjs +7 -2
- package/src/channels/qq/qq-bridge.mjs +84 -28
- package/src/channels/qq/qq-controller.mjs +2 -0
- package/src/channels/shared/control-command.mjs +11 -1
- package/src/channels/shared/harness-client.mjs +40 -4
- package/src/channels/shared/i18n-en/feishu.mjs +27 -25
- package/src/channels/shared/i18n-en/shared-a.mjs +78 -0
- package/src/channels/shared/i18n-en/shared-b.mjs +1 -0
- package/src/channels/shared/i18n-en/telegram.mjs +1 -0
- package/src/channels/shared/message-failure.mjs +244 -0
- package/src/channels/shared/semantic/artifact-delivery.mjs +9 -2
- package/src/channels/shared/text-harness-bridge.mjs +66 -60
- package/src/channels/shared/token-bot-controller.mjs +2 -0
- package/src/channels/slack/slack-controller.mjs +2 -0
- package/src/channels/telegram/telegram-runtime.mjs +1 -0
- package/src/channels/wecom/wecom-bridge.mjs +50 -15
- package/src/channels/wecom/wecom-controller.mjs +2 -0
- package/src/channels/weixin/weixin-api.mjs +47 -0
- package/src/channels/weixin/weixin-bridge.mjs +262 -43
- package/src/channels/weixin/weixin-controller.mjs +2 -15
- package/src/channels/weixin/weixin-runtime.mjs +1 -0
- package/src/channels/whatsapp/whatsapp-controller.mjs +2 -0
package/package.json
CHANGED
|
@@ -1,6 +1,20 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
|
|
3
|
-
import { h } from './i18n.js';
|
|
3
|
+
import { h, isEnglish } from './i18n.js';
|
|
4
|
+
|
|
5
|
+
function messageErrorTime(value) {
|
|
6
|
+
try {
|
|
7
|
+
return new Intl.DateTimeFormat(isEnglish() ? 'en-US' : 'zh-CN', {
|
|
8
|
+
year: 'numeric',
|
|
9
|
+
month: '2-digit',
|
|
10
|
+
day: '2-digit',
|
|
11
|
+
hour: '2-digit',
|
|
12
|
+
minute: '2-digit',
|
|
13
|
+
}).format(new Date(value));
|
|
14
|
+
} catch {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
4
18
|
|
|
5
19
|
export function ChannelListHeading({ className = '', id, title, connectionLabel }) {
|
|
6
20
|
const helpId = React.useId();
|
|
@@ -46,3 +60,24 @@ export function BotStatusMeta({
|
|
|
46
60
|
h('span', null, '最近检查'),
|
|
47
61
|
h('span', null, formatCheckedTime(lastCheckedAt))));
|
|
48
62
|
}
|
|
63
|
+
|
|
64
|
+
export function LastMessageErrorSummary({ className = '', error }) {
|
|
65
|
+
if (!error) return null;
|
|
66
|
+
const occurredAt = messageErrorTime(error.at);
|
|
67
|
+
return h('div', {
|
|
68
|
+
className: `${className} dim-cardSummary`.trim(),
|
|
69
|
+
role: 'status',
|
|
70
|
+
},
|
|
71
|
+
h('strong', null, '最近一条消息处理失败'),
|
|
72
|
+
':',
|
|
73
|
+
h('span', null, error.message),
|
|
74
|
+
'(',
|
|
75
|
+
h('span', null, '错误码'),
|
|
76
|
+
` ${error.code} · `,
|
|
77
|
+
h('span', null, '参考号'),
|
|
78
|
+
` ${error.referenceId}`,
|
|
79
|
+
occurredAt ? h(React.Fragment, null,
|
|
80
|
+
' · ',
|
|
81
|
+
h('time', { dateTime: new Date(error.at).toISOString() }, occurredAt)) : null,
|
|
82
|
+
')');
|
|
83
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { normalizeAgentPresetCatalog, normalizeAgentPresetId, SET_AGENT_PRESET_ENDPOINT } from '../../agent-preset.js';
|
|
2
|
+
import { normalizeLastMessageError } from '../../last-message-error.js';
|
|
2
3
|
|
|
3
4
|
export const DINGTALK_RPC_CHANNEL = '/dingtalk';
|
|
4
5
|
|
|
@@ -178,6 +179,7 @@ function normalizeBot(value) {
|
|
|
178
179
|
messagesReceived: nonNegativeInteger(stats.messagesReceived),
|
|
179
180
|
messagesReplied: nonNegativeInteger(stats.messagesReplied),
|
|
180
181
|
},
|
|
182
|
+
lastMessageError: normalizeLastMessageError(value.lastMessageError),
|
|
181
183
|
error: normalizeError(value.error, 'DINGTALK_ACCOUNT_ERROR', '钉钉连接尚未就绪') ?? null,
|
|
182
184
|
};
|
|
183
185
|
}
|
|
@@ -9,7 +9,11 @@ import {
|
|
|
9
9
|
EMPTY_AGENT_PRESET_CATALOG,
|
|
10
10
|
} from '../../agent-preset.js';
|
|
11
11
|
import { useWorkspaceSnapshotFence } from '../../workspace-snapshot-fence.js';
|
|
12
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
BotStatusMeta,
|
|
14
|
+
ChannelListHeading,
|
|
15
|
+
LastMessageErrorSummary,
|
|
16
|
+
} from '../../channel-card-meta.js';
|
|
13
17
|
import {
|
|
14
18
|
DINGTALK_ENDPOINTS,
|
|
15
19
|
DINGTALK_RPC_CHANNEL,
|
|
@@ -262,6 +266,10 @@ export function AccountCard({
|
|
|
262
266
|
h(Button, { className: 'dim-cardAction', kind: 'danger', onClick: onRequestRemove, disabled: Boolean(busy) },
|
|
263
267
|
'移除接入')),
|
|
264
268
|
summary ? h('div', { className: 'ddt-summary dim-cardSummary' }, summary) : null,
|
|
269
|
+
account.lastMessageError ? h(LastMessageErrorSummary, {
|
|
270
|
+
className: 'ddt-summary',
|
|
271
|
+
error: account.lastMessageError,
|
|
272
|
+
}) : null,
|
|
265
273
|
feedback ? h('div', {
|
|
266
274
|
className: 'ddt-summary dim-cardFeedback',
|
|
267
275
|
role: 'status',
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import { normalizeAgentPresetCatalog, normalizeAgentPresetId } from "../../agent-preset.js";
|
|
10
|
+
import { normalizeLastMessageError } from "../../last-message-error.js";
|
|
10
11
|
|
|
11
12
|
export const FEISHU_RPC_CHANNEL = "/feishu";
|
|
12
13
|
|
|
@@ -206,6 +207,7 @@ export function normalizeBotConnection(value, fallbackBotId) {
|
|
|
206
207
|
groupMessagePermissionGranted: value.groupMessagePermissionGranted === true,
|
|
207
208
|
bot: normalizeBot(value.bot),
|
|
208
209
|
health: normalizeHealth(value.health, connected),
|
|
210
|
+
lastMessageError: normalizeLastMessageError(value.lastMessageError),
|
|
209
211
|
error: normalizeError(value.error),
|
|
210
212
|
};
|
|
211
213
|
}
|
|
@@ -226,6 +228,7 @@ export function normalizeBotsSnapshot(value) {
|
|
|
226
228
|
configured: true,
|
|
227
229
|
bot: value.bot,
|
|
228
230
|
health: value.health,
|
|
231
|
+
lastMessageError: value.lastMessageError,
|
|
229
232
|
error: value.error,
|
|
230
233
|
}];
|
|
231
234
|
}
|
|
@@ -23,7 +23,11 @@ import {
|
|
|
23
23
|
EMPTY_AGENT_PRESET_CATALOG,
|
|
24
24
|
} from "../../agent-preset.js";
|
|
25
25
|
import { useWorkspaceSnapshotFence } from "../../workspace-snapshot-fence.js";
|
|
26
|
-
import {
|
|
26
|
+
import {
|
|
27
|
+
BotStatusMeta,
|
|
28
|
+
ChannelListHeading,
|
|
29
|
+
LastMessageErrorSummary,
|
|
30
|
+
} from "../../channel-card-meta.js";
|
|
27
31
|
import { installFeishuStyles } from "./styles.js";
|
|
28
32
|
|
|
29
33
|
export const name = "feishu-settings";
|
|
@@ -265,7 +269,7 @@ function QrPane({ provision, now, onRefresh, onCancel, busy }) {
|
|
|
265
269
|
? h("img", {
|
|
266
270
|
src: qrSource,
|
|
267
271
|
alt: repairing
|
|
268
|
-
?
|
|
272
|
+
? `用于为${botName}补全权限与回调的一次性授权二维码`
|
|
269
273
|
: grantingGroupMessages
|
|
270
274
|
? `用于为${botName}开通群消息权限的一次性授权二维码`
|
|
271
275
|
: "用于新增 DeepSeek Harness 飞书机器人的一次性授权二维码",
|
|
@@ -293,31 +297,31 @@ function QrPane({ provision, now, onRefresh, onCancel, busy }) {
|
|
|
293
297
|
h("div", { className: "bxf-stateLabel dim-stateLabel" },
|
|
294
298
|
h("span", { className: "bxf-dot dim-stateDot", "data-tone": "warning" }),
|
|
295
299
|
h("span", null, repairing
|
|
296
|
-
?
|
|
300
|
+
? `正在为「${botName}」补全权限与回调`
|
|
297
301
|
: grantingGroupMessages
|
|
298
302
|
? `正在为「${botName}」开通群消息权限`
|
|
299
303
|
: "正在添加新机器人")),
|
|
300
304
|
h("h3", null, expired
|
|
301
305
|
? "刷新二维码后继续"
|
|
302
306
|
: repairing
|
|
303
|
-
? "
|
|
307
|
+
? "使用飞书扫码补全权限"
|
|
304
308
|
: grantingGroupMessages
|
|
305
309
|
? "使用飞书确认群消息权限"
|
|
306
310
|
: "使用飞书扫码创建机器人"),
|
|
307
311
|
h("p", null, repairing
|
|
308
|
-
? "
|
|
312
|
+
? "扫码会更新现有飞书应用,最多增量补充卡片按钮回调、读取用户消息内图片或文件所需的 im:message:readonly(飞书显示为“获取单聊、群组消息”),以及上传机器人图片或文件所需的 im:resource;不会创建新应用。确认页只显示当前缺少项,完成后此机器人会短暂重连,其他机器人不受影响。"
|
|
309
313
|
: grantingGroupMessages
|
|
310
314
|
? "扫码会更新现有飞书应用,只增量开通“获取群组中所有消息”权限;不会创建新应用。确认后会自动启用“响应所有群消息”,其他机器人不受影响。"
|
|
311
315
|
: "扫码只会新增一个机器人,已接入的机器人会继续正常收发消息。"),
|
|
312
316
|
h("ol", { className: "bxf-steps dim-steps" },
|
|
313
317
|
h("li", null, "打开飞书移动端,使用扫一扫读取二维码"),
|
|
314
318
|
h("li", null, repairing
|
|
315
|
-
? "
|
|
319
|
+
? "核对现有应用名称,并确认只新增当前缺少的上述配置"
|
|
316
320
|
: grantingGroupMessages
|
|
317
321
|
? "核对现有应用,并确认“获取群组中所有消息”权限"
|
|
318
322
|
: "核对应用名称与权限范围,并确认创建"),
|
|
319
323
|
h("li", null, repairing
|
|
320
|
-
? "
|
|
324
|
+
? "保持本页打开,等待权限与回调补全完成"
|
|
321
325
|
: grantingGroupMessages
|
|
322
326
|
? "保持本页打开,等待权限生效并自动切换响应方式"
|
|
323
327
|
: "保持本页打开,等待新机器人的长连接就绪")),
|
|
@@ -336,7 +340,7 @@ function QrPane({ provision, now, onRefresh, onCancel, busy }) {
|
|
|
336
340
|
? h(Button, { onClick: onRefresh, disabled: busy }, "换一个二维码")
|
|
337
341
|
: null,
|
|
338
342
|
h(Button, { onClick: onCancel, disabled: busy }, repairing
|
|
339
|
-
? "
|
|
343
|
+
? "取消补全"
|
|
340
344
|
: grantingGroupMessages ? "取消授权" : "取消添加")),
|
|
341
345
|
),
|
|
342
346
|
),
|
|
@@ -354,16 +358,16 @@ function ProvisionProgress({ phase, provision, onCancel, busy }) {
|
|
|
354
358
|
h("div", { className: "dim-spinner", "aria-hidden": "true" }),
|
|
355
359
|
h("h3", null, connecting
|
|
356
360
|
? repairing
|
|
357
|
-
? "
|
|
361
|
+
? "已确认,正在完成权限与回调配置"
|
|
358
362
|
: grantingGroupMessages
|
|
359
363
|
? "已确认,正在启用全部消息模式"
|
|
360
364
|
: "已确认,正在连接新机器人"
|
|
361
365
|
: repairing
|
|
362
|
-
? "
|
|
366
|
+
? "正在准备权限补全二维码"
|
|
363
367
|
: grantingGroupMessages ? "正在准备权限授权二维码" : "正在准备授权二维码"),
|
|
364
368
|
h("p", null, connecting
|
|
365
369
|
? repairing
|
|
366
|
-
? "
|
|
370
|
+
? "配置已提交,正在保存权限、验证卡片回调并重连此机器人;此阶段无法取消,其他机器人不会中断。"
|
|
367
371
|
: grantingGroupMessages
|
|
368
372
|
? "权限配置已提交,正在保存设置并重连此机器人;此阶段无法取消,其他机器人不会中断。"
|
|
369
373
|
: "正在安全保存凭据并检查新机器人的消息通道,其他机器人不会中断。"
|
|
@@ -375,7 +379,7 @@ function ProvisionProgress({ phase, provision, onCancel, busy }) {
|
|
|
375
379
|
connecting && onCancel
|
|
376
380
|
? h("div", { className: "bxf-actions dim-viewActions", style: { justifyContent: "center" } },
|
|
377
381
|
h(Button, { onClick: onCancel, disabled: busy }, repairing
|
|
378
|
-
? "
|
|
382
|
+
? "取消补全"
|
|
379
383
|
: grantingGroupMessages ? "取消授权" : "取消添加"))
|
|
380
384
|
: null,
|
|
381
385
|
);
|
|
@@ -388,7 +392,7 @@ function ProvisionError({ error, provision, onRetry, onCancel, busy }) {
|
|
|
388
392
|
h("div", { className: "bxf-inlineError dim-inlineError", role: "alert" },
|
|
389
393
|
h("div", null,
|
|
390
394
|
h("h3", null, repairing
|
|
391
|
-
? "
|
|
395
|
+
? "权限与回调没有补全完成"
|
|
392
396
|
: grantingGroupMessages ? "群消息权限没有开通完成" : "新机器人没有添加完成"),
|
|
393
397
|
h("p", null, error.message),
|
|
394
398
|
error.code ? h("span", { className: "bxf-errorCode" }, error.code) : null,
|
|
@@ -564,6 +568,7 @@ export function BotCard({
|
|
|
564
568
|
removeButtonRef,
|
|
565
569
|
}) {
|
|
566
570
|
const { bot, health, state, connected } = connection;
|
|
571
|
+
const repairTooltipId = React.useId();
|
|
567
572
|
const stateForDisplay = busy === "reconnect"
|
|
568
573
|
? "connecting"
|
|
569
574
|
: state;
|
|
@@ -637,13 +642,22 @@ export function BotCard({
|
|
|
637
642
|
disabled: Boolean(busy), "aria-busy": busy === "reconnect" ? "true" : undefined,
|
|
638
643
|
"aria-label": `${connected ? "检查连接" : "重试连接"}${bot.name}`,
|
|
639
644
|
}, busy === "reconnect" ? (connected ? "检查中…" : "正在连接…") : connected ? "检查连接" : "重试连接"),
|
|
640
|
-
h(
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
645
|
+
h("span", { className: "bxf-repairAction" },
|
|
646
|
+
h(Button, {
|
|
647
|
+
className: "bxf-repairButton dim-cardAction",
|
|
648
|
+
onClick: onRepairCallback,
|
|
649
|
+
disabled: Boolean(busy) || repairDisabled,
|
|
650
|
+
"aria-busy": busy === "callback-repair" ? "true" : undefined,
|
|
651
|
+
"aria-label": `为${bot.name}补全权限与回调`,
|
|
652
|
+
"aria-describedby": repairTooltipId,
|
|
653
|
+
}, busy === "callback-repair" ? "等待扫码…" : "补全权限"),
|
|
654
|
+
h("span", {
|
|
655
|
+
id: repairTooltipId,
|
|
656
|
+
className: "bxf-repairTooltip",
|
|
657
|
+
role: "tooltip",
|
|
658
|
+
},
|
|
659
|
+
h("strong", null, "补全范围"),
|
|
660
|
+
h("span", null, "最多增量添加卡片回调 card.action.trigger、读取消息内图片或文件所需的 im:message:readonly(飞书显示为“获取单聊、群组消息”),以及上传机器人图片或文件所需的 im:resource;确认页只显示当前缺少项,不会创建新应用。"))),
|
|
647
661
|
h(Button, {
|
|
648
662
|
className: "dim-cardAction", kind: "danger", onClick: onRequestRemove,
|
|
649
663
|
disabled: Boolean(busy), ref: removeButtonRef,
|
|
@@ -651,6 +665,10 @@ export function BotCard({
|
|
|
651
665
|
}, "移除接入")),
|
|
652
666
|
summary ? h("div", { className: "bxf-healthSummary dim-cardSummary", "data-error": actionError || connection.error ? "true" : undefined },
|
|
653
667
|
summary) : null,
|
|
668
|
+
connection.lastMessageError ? h(LastMessageErrorSummary, {
|
|
669
|
+
className: "bxf-healthSummary",
|
|
670
|
+
error: connection.lastMessageError,
|
|
671
|
+
}) : null,
|
|
654
672
|
testNotice ? h("div", {
|
|
655
673
|
className: "bxf-healthSummary dim-cardFeedback",
|
|
656
674
|
role: "status",
|
|
@@ -933,7 +951,7 @@ export function FeishuSettingsTab({ rpcCall }) {
|
|
|
933
951
|
&& (provision.operation !== operation || provision.botId !== botId)) {
|
|
934
952
|
throw new Error(grantingGroupMessages
|
|
935
953
|
? "飞书服务返回了不匹配的群消息权限二维码"
|
|
936
|
-
: "
|
|
954
|
+
: "飞书服务返回了不匹配的权限补全二维码");
|
|
937
955
|
}
|
|
938
956
|
const timestamp = Date.now();
|
|
939
957
|
setNow(timestamp);
|
|
@@ -948,7 +966,7 @@ export function FeishuSettingsTab({ rpcCall }) {
|
|
|
948
966
|
},
|
|
949
967
|
}));
|
|
950
968
|
announce(repairing
|
|
951
|
-
? `${botName ?? "机器人"}
|
|
969
|
+
? `${botName ?? "机器人"}的权限补全二维码已生成,请使用飞书扫码。`
|
|
952
970
|
: grantingGroupMessages
|
|
953
971
|
? `${botName ?? "机器人"}的群消息权限二维码已生成,请使用飞书确认。`
|
|
954
972
|
: "授权二维码已生成,请使用飞书扫码。");
|
|
@@ -1032,7 +1050,7 @@ export function FeishuSettingsTab({ rpcCall }) {
|
|
|
1032
1050
|
: current);
|
|
1033
1051
|
announce(grantingGroupMessages
|
|
1034
1052
|
? "权限配置已提交,正在启用全部消息模式并重连此机器人;此阶段无法取消,其他机器人不会中断。"
|
|
1035
|
-
: "
|
|
1053
|
+
: "配置已提交,正在保存权限、验证卡片回调并重连此机器人;此阶段无法取消,其他机器人不会中断。");
|
|
1036
1054
|
return;
|
|
1037
1055
|
}
|
|
1038
1056
|
if (result.status === "connected") {
|
|
@@ -1040,7 +1058,7 @@ export function FeishuSettingsTab({ rpcCall }) {
|
|
|
1040
1058
|
setModel((current) => ({ ...current, provisioning: null }));
|
|
1041
1059
|
announce(grantingGroupMessages
|
|
1042
1060
|
? `${targetBotName}已开通群消息权限,并启用“响应所有群消息”。`
|
|
1043
|
-
: `${targetBotName}
|
|
1061
|
+
: `${targetBotName}的权限与回调已补全。`);
|
|
1044
1062
|
if (activeProvision.botId) setFocusBotId(activeProvision.botId);
|
|
1045
1063
|
await loadStatus({ silent: true, restoreProvisioning: false });
|
|
1046
1064
|
return;
|
|
@@ -1048,7 +1066,7 @@ export function FeishuSettingsTab({ rpcCall }) {
|
|
|
1048
1066
|
}
|
|
1049
1067
|
setModel((current) => ({ ...current, provisioning: null }));
|
|
1050
1068
|
announce(repairing
|
|
1051
|
-
? "
|
|
1069
|
+
? "已取消补全权限与回调。"
|
|
1052
1070
|
: grantingGroupMessages ? "已取消群消息权限授权。" : "已取消添加机器人。");
|
|
1053
1071
|
await loadStatus({ silent: true, restoreProvisioning: false });
|
|
1054
1072
|
scheduleAnimationFrame(() => {
|
|
@@ -1117,7 +1135,7 @@ export function FeishuSettingsTab({ rpcCall }) {
|
|
|
1117
1135
|
const targetBot = snapshot?.bots.find((bot) => bot.botId === result.botId);
|
|
1118
1136
|
if (!snapshot) {
|
|
1119
1137
|
throw new Error(isCallbackRepair(provision)
|
|
1120
|
-
? "
|
|
1138
|
+
? "权限与回调已更新,但暂时无法确认机器人连接状态"
|
|
1121
1139
|
: isGroupMessagePermission(provision)
|
|
1122
1140
|
? "群消息权限已更新,但暂时无法确认机器人连接状态"
|
|
1123
1141
|
: "机器人已经创建,但暂时无法确认连接状态");
|
|
@@ -1130,7 +1148,7 @@ export function FeishuSettingsTab({ rpcCall }) {
|
|
|
1130
1148
|
}
|
|
1131
1149
|
setModel((current) => ({ ...current, provisioning: null }));
|
|
1132
1150
|
announce(isCallbackRepair(provision)
|
|
1133
|
-
? `${targetBot.bot.name}
|
|
1151
|
+
? `${targetBot.bot.name}的权限与回调已补全。`
|
|
1134
1152
|
: isGroupMessagePermission(provision)
|
|
1135
1153
|
? `${targetBot.bot.name}已开通群消息权限,并启用“响应所有群消息”。`
|
|
1136
1154
|
: targetBot
|
|
@@ -1142,7 +1160,7 @@ export function FeishuSettingsTab({ rpcCall }) {
|
|
|
1142
1160
|
if (result.status === "failed") {
|
|
1143
1161
|
const error = new Error(result.message
|
|
1144
1162
|
?? (isCallbackRepair(provision)
|
|
1145
|
-
? "
|
|
1163
|
+
? "飞书权限与回调补全失败"
|
|
1146
1164
|
: isGroupMessagePermission(provision)
|
|
1147
1165
|
? "飞书群消息权限开通失败"
|
|
1148
1166
|
: "飞书应用创建失败"));
|
|
@@ -450,10 +450,35 @@ const CSS = String.raw`
|
|
|
450
450
|
.bxf-connectedFooter { display: flex; align-items: center; justify-content: space-between; gap: 15px; margin-top: 20px; padding-top: 16px; border-top: 1px solid var(--dsw-alias-border-l1, #eef0f3); }
|
|
451
451
|
.bxf-healthSummary { min-width: 0; color: var(--dsw-alias-label-secondary, #646a73); font-size: 12px; line-height: 18px; }
|
|
452
452
|
.bxf-healthSummary[data-error="true"] { color: var(--bxf-error); }
|
|
453
|
-
.bxf-botActions { flex: none; width: 100%; flex-wrap: wrap; gap: 8px; margin-top: 0; justify-content: flex-end; }
|
|
453
|
+
.bxf-botActions { position: relative; flex: none; width: 100%; flex-wrap: wrap; gap: 8px; margin-top: 0; justify-content: flex-end; }
|
|
454
454
|
.bxf-botActions .bxf-button { flex: none; white-space: nowrap; }
|
|
455
455
|
.bxf-botActions .bxf-repairButton { color: var(--bxf-accent); border-color: color-mix(in srgb, var(--bxf-accent) 35%, var(--dsw-alias-border-l2, #dee0e3)); }
|
|
456
456
|
.bxf-botActions .bxf-repairButton:hover:not(:disabled) { background: color-mix(in srgb, var(--bxf-accent) 7%, transparent); }
|
|
457
|
+
.bxf-repairAction { display: inline-flex; }
|
|
458
|
+
.bxf-repairTooltip {
|
|
459
|
+
position: absolute;
|
|
460
|
+
right: 0;
|
|
461
|
+
bottom: calc(100% + 8px);
|
|
462
|
+
z-index: 40;
|
|
463
|
+
width: min(330px, 100%);
|
|
464
|
+
display: grid;
|
|
465
|
+
gap: 3px;
|
|
466
|
+
padding: 9px 10px;
|
|
467
|
+
border: 1px solid var(--dsw-alias-border-l2, #dfe1e5);
|
|
468
|
+
border-radius: 8px;
|
|
469
|
+
color: var(--dsw-alias-label-primary, #1f2329);
|
|
470
|
+
background: var(--dsw-alias-bg-layer-3, #fff);
|
|
471
|
+
box-shadow: 0 10px 28px rgb(31 35 41 / 16%);
|
|
472
|
+
opacity: 0;
|
|
473
|
+
visibility: hidden;
|
|
474
|
+
transform: translateY(3px);
|
|
475
|
+
pointer-events: none;
|
|
476
|
+
transition: opacity .15s ease, transform .15s ease, visibility .15s ease;
|
|
477
|
+
}
|
|
478
|
+
.bxf-repairTooltip strong { font-size: 12px; line-height: 17px; font-weight: 650; }
|
|
479
|
+
.bxf-repairTooltip > span { color: var(--dsw-alias-label-secondary, #646a73); font-size: 11px; line-height: 17px; font-weight: 400; overflow-wrap: anywhere; }
|
|
480
|
+
.bxf-repairAction:hover .bxf-repairTooltip,
|
|
481
|
+
.bxf-repairAction:focus-within .bxf-repairTooltip { opacity: 1; visibility: visible; transform: translateY(0); }
|
|
457
482
|
|
|
458
483
|
.bxf-confirm {
|
|
459
484
|
border-top: 1px solid var(--dsw-alias-border-l2, #dee0e3);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { normalizeAgentPresetCatalog, normalizeAgentPresetId, SET_AGENT_PRESET_ENDPOINT } from '../../agent-preset.js';
|
|
2
|
+
import { normalizeLastMessageError } from '../../last-message-error.js';
|
|
2
3
|
|
|
3
4
|
export const QQ_RPC_CHANNEL = '/qq';
|
|
4
5
|
|
|
@@ -94,6 +95,7 @@ function normalizeBot(value) {
|
|
|
94
95
|
summary: text(value.health?.summary, connected ? 'QQ WebSocket 长连接运行正常' : 'QQ 连接尚未就绪'),
|
|
95
96
|
lastCheckedAt: timestamp(value.health?.lastCheckedAt),
|
|
96
97
|
},
|
|
98
|
+
lastMessageError: normalizeLastMessageError(value.lastMessageError),
|
|
97
99
|
error: isRecord(value.error) ? {
|
|
98
100
|
code: text(value.error.code, 'QQ_ACCOUNT_ERROR', 80),
|
|
99
101
|
message: text(value.error.message, 'QQ 连接尚未就绪'),
|
|
@@ -10,7 +10,11 @@ import {
|
|
|
10
10
|
EMPTY_AGENT_PRESET_CATALOG,
|
|
11
11
|
} from '../../agent-preset.js';
|
|
12
12
|
import { useWorkspaceSnapshotFence } from '../../workspace-snapshot-fence.js';
|
|
13
|
-
import {
|
|
13
|
+
import {
|
|
14
|
+
BotStatusMeta,
|
|
15
|
+
ChannelListHeading,
|
|
16
|
+
LastMessageErrorSummary,
|
|
17
|
+
} from '../../channel-card-meta.js';
|
|
14
18
|
import { installDingtalkStyles } from '../dingtalk/styles.js';
|
|
15
19
|
import {
|
|
16
20
|
QQ_ENDPOINTS,
|
|
@@ -201,6 +205,10 @@ export function AccountCard({
|
|
|
201
205
|
h(Button, { className: 'dim-cardAction', onClick: onReconnect, disabled: Boolean(busy) }, busy === 'reconnect' ? '检查中…' : account.connected ? '检查连接' : '重试连接'),
|
|
202
206
|
h(Button, { className: 'dim-cardAction', kind: 'danger', onClick: onRequestRemove, disabled: Boolean(busy) }, '移除接入')),
|
|
203
207
|
summary ? h('div', { className: 'ddt-summary dim-cardSummary' }, summary) : null,
|
|
208
|
+
account.lastMessageError ? h(LastMessageErrorSummary, {
|
|
209
|
+
className: 'ddt-summary',
|
|
210
|
+
error: account.lastMessageError,
|
|
211
|
+
}) : null,
|
|
204
212
|
feedback ? h('div', {
|
|
205
213
|
className: 'ddt-summary dim-cardFeedback',
|
|
206
214
|
role: 'status',
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { normalizeAgentPresetCatalog, normalizeAgentPresetId, SET_AGENT_PRESET_ENDPOINT } from '../../agent-preset.js';
|
|
2
|
+
import { normalizeLastMessageError } from '../../last-message-error.js';
|
|
2
3
|
|
|
3
4
|
const ACCOUNT_STATES = new Set(['connected', 'connecting', 'offline', 'error']);
|
|
4
5
|
|
|
@@ -68,6 +69,7 @@ export function createTokenChannelApi(channel, connectionSummary, {
|
|
|
68
69
|
),
|
|
69
70
|
lastCheckedAt: timestamp(value.health?.lastCheckedAt),
|
|
70
71
|
},
|
|
72
|
+
lastMessageError: normalizeLastMessageError(value.lastMessageError),
|
|
71
73
|
error: isRecord(value.error) ? {
|
|
72
74
|
code: text(value.error.code, `${channel.toUpperCase()}_ACCOUNT_ERROR`, 80),
|
|
73
75
|
message: text(value.error.message, `${channel}连接尚未就绪`),
|
|
@@ -10,7 +10,11 @@ import {
|
|
|
10
10
|
EMPTY_AGENT_PRESET_CATALOG,
|
|
11
11
|
} from '../../agent-preset.js';
|
|
12
12
|
import { useWorkspaceSnapshotFence } from '../../workspace-snapshot-fence.js';
|
|
13
|
-
import {
|
|
13
|
+
import {
|
|
14
|
+
BotStatusMeta,
|
|
15
|
+
ChannelListHeading,
|
|
16
|
+
LastMessageErrorSummary,
|
|
17
|
+
} from '../../channel-card-meta.js';
|
|
14
18
|
|
|
15
19
|
const Button = React.forwardRef(function Button(
|
|
16
20
|
{ children, kind = 'secondary', className = '', ...props },
|
|
@@ -121,6 +125,10 @@ export function createTokenChannelSettings(definition) {
|
|
|
121
125
|
disabled: Boolean(busy),
|
|
122
126
|
}, '移除接入')),
|
|
123
127
|
summary ? h('div', { className: 'ddt-summary dim-cardSummary' }, summary) : null,
|
|
128
|
+
account.lastMessageError ? h(LastMessageErrorSummary, {
|
|
129
|
+
className: 'ddt-summary',
|
|
130
|
+
error: account.lastMessageError,
|
|
131
|
+
}) : null,
|
|
124
132
|
testNotice ? h('div', {
|
|
125
133
|
className: 'ddt-summary dim-cardFeedback',
|
|
126
134
|
role: 'status',
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { normalizeAgentPresetCatalog, normalizeAgentPresetId, SET_AGENT_PRESET_ENDPOINT } from '../../agent-preset.js';
|
|
2
|
+
import { normalizeLastMessageError } from '../../last-message-error.js';
|
|
2
3
|
|
|
3
4
|
export const WECOM_RPC_CHANNEL = '/wecom';
|
|
4
5
|
|
|
@@ -103,6 +104,7 @@ function normalizeBot(value) {
|
|
|
103
104
|
summary: text(value.health?.summary, connected ? '企业微信 WebSocket 长连接运行正常' : '企业微信连接尚未就绪'),
|
|
104
105
|
lastCheckedAt: timestamp(value.health?.lastCheckedAt),
|
|
105
106
|
},
|
|
107
|
+
lastMessageError: normalizeLastMessageError(value.lastMessageError),
|
|
106
108
|
error: isRecord(value.error) ? {
|
|
107
109
|
code: text(value.error.code, 'WECOM_ACCOUNT_ERROR', 80),
|
|
108
110
|
message: text(value.error.message, '企业微信连接尚未就绪'),
|
|
@@ -10,7 +10,11 @@ import {
|
|
|
10
10
|
EMPTY_AGENT_PRESET_CATALOG,
|
|
11
11
|
} from '../../agent-preset.js';
|
|
12
12
|
import { useWorkspaceSnapshotFence } from '../../workspace-snapshot-fence.js';
|
|
13
|
-
import {
|
|
13
|
+
import {
|
|
14
|
+
BotStatusMeta,
|
|
15
|
+
ChannelListHeading,
|
|
16
|
+
LastMessageErrorSummary,
|
|
17
|
+
} from '../../channel-card-meta.js';
|
|
14
18
|
import { installDingtalkStyles } from '../dingtalk/styles.js';
|
|
15
19
|
import {
|
|
16
20
|
WECOM_ENDPOINTS,
|
|
@@ -200,6 +204,10 @@ export function AccountCard({
|
|
|
200
204
|
h(Button, { className: 'dim-cardAction', onClick: onReconnect, disabled: Boolean(busy) }, busy === 'reconnect' ? '检查中…' : account.connected ? '检查连接' : '重试连接'),
|
|
201
205
|
h(Button, { className: 'dim-cardAction', kind: 'danger', onClick: onRequestRemove, disabled: Boolean(busy) }, '移除接入')),
|
|
202
206
|
summary ? h('div', { className: 'ddt-summary dim-cardSummary' }, summary) : null,
|
|
207
|
+
account.lastMessageError ? h(LastMessageErrorSummary, {
|
|
208
|
+
className: 'ddt-summary',
|
|
209
|
+
error: account.lastMessageError,
|
|
210
|
+
}) : null,
|
|
203
211
|
feedback ? h('div', {
|
|
204
212
|
className: 'ddt-summary dim-cardFeedback',
|
|
205
213
|
role: 'status',
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { normalizeAgentPresetCatalog, normalizeAgentPresetId, SET_AGENT_PRESET_ENDPOINT } from '../../agent-preset.js';
|
|
2
|
+
import { normalizeLastMessageError } from '../../last-message-error.js';
|
|
2
3
|
|
|
3
4
|
export const WEIXIN_RPC_CHANNEL = '/weixin';
|
|
4
5
|
export const WEIXIN_ENDPOINTS = Object.freeze({
|
|
@@ -48,15 +49,6 @@ function normalizeTestMessage(value) {
|
|
|
48
49
|
return { sent: false, code };
|
|
49
50
|
}
|
|
50
51
|
|
|
51
|
-
function normalizeMessageError(value) {
|
|
52
|
-
if (!isRecord(value)) return null;
|
|
53
|
-
const code = string(value.code).slice(0, 64);
|
|
54
|
-
const reason = string(value.reason).slice(0, 128);
|
|
55
|
-
const message = string(value.message).slice(0, 500);
|
|
56
|
-
const at = timestamp(value.at);
|
|
57
|
-
return code && reason && message && at !== null ? { code, reason, message, at } : null;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
52
|
export function unwrapRpcResult(result) {
|
|
61
53
|
if (!isRecord(result) || typeof result.ok !== 'boolean') {
|
|
62
54
|
throw new Error('微信服务返回了无法识别的响应');
|
|
@@ -141,7 +133,7 @@ function normalizeBot(value) {
|
|
|
141
133
|
messagesReceived: Math.max(0, Number(value.stats?.messagesReceived) || 0),
|
|
142
134
|
messagesReplied: Math.max(0, Number(value.stats?.messagesReplied) || 0),
|
|
143
135
|
},
|
|
144
|
-
lastMessageError:
|
|
136
|
+
lastMessageError: normalizeLastMessageError(value.lastMessageError),
|
|
145
137
|
error: isRecord(value.error)
|
|
146
138
|
? {
|
|
147
139
|
code: string(value.error.code, 'WEIXIN_ACCOUNT_ERROR'),
|
|
@@ -22,7 +22,11 @@ import {
|
|
|
22
22
|
EMPTY_AGENT_PRESET_CATALOG,
|
|
23
23
|
} from '../../agent-preset.js';
|
|
24
24
|
import { useWorkspaceSnapshotFence } from '../../workspace-snapshot-fence.js';
|
|
25
|
-
import {
|
|
25
|
+
import {
|
|
26
|
+
BotStatusMeta,
|
|
27
|
+
ChannelListHeading,
|
|
28
|
+
LastMessageErrorSummary,
|
|
29
|
+
} from '../../channel-card-meta.js';
|
|
26
30
|
import { installWeixinStyles } from './styles.js';
|
|
27
31
|
|
|
28
32
|
export const name = 'weixin-settings';
|
|
@@ -247,10 +251,10 @@ export function AccountCard({
|
|
|
247
251
|
busy === 'reconnect' ? '检查中…' : account.connected ? '检查连接' : '重试连接'),
|
|
248
252
|
h(Button, { className: 'dim-cardAction', kind: 'danger', onClick: onRequestRemove, disabled: Boolean(busy) }, '移除接入')),
|
|
249
253
|
summary ? h('div', { className: 'dxw-summary dim-cardSummary' }, summary) : null,
|
|
250
|
-
account.lastMessageError ? h(
|
|
251
|
-
className: 'dxw-summary
|
|
252
|
-
|
|
253
|
-
}
|
|
254
|
+
account.lastMessageError ? h(LastMessageErrorSummary, {
|
|
255
|
+
className: 'dxw-summary',
|
|
256
|
+
error: account.lastMessageError,
|
|
257
|
+
}) : null,
|
|
254
258
|
feedback ? h('div', {
|
|
255
259
|
className: 'dxw-summary dim-cardFeedback',
|
|
256
260
|
role: 'status',
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { normalizeAgentPresetCatalog, normalizeAgentPresetId, SET_AGENT_PRESET_ENDPOINT } from '../../agent-preset.js';
|
|
2
|
+
import { normalizeLastMessageError } from '../../last-message-error.js';
|
|
2
3
|
|
|
3
4
|
export const WHATSAPP_RPC_CHANNEL = '/whatsapp';
|
|
4
5
|
|
|
@@ -106,6 +107,7 @@ function normalizeBot(value) {
|
|
|
106
107
|
? 'WhatsApp Web 关联设备运行正常' : 'WhatsApp 连接尚未就绪'),
|
|
107
108
|
lastCheckedAt: timestamp(value.health?.lastCheckedAt),
|
|
108
109
|
},
|
|
110
|
+
lastMessageError: normalizeLastMessageError(value.lastMessageError),
|
|
109
111
|
error: isRecord(value.error) ? {
|
|
110
112
|
code: text(value.error.code, 'WHATSAPP_ACCOUNT_ERROR', 80),
|
|
111
113
|
message: text(value.error.message, 'WhatsApp 连接尚未就绪'),
|
|
@@ -10,7 +10,11 @@ import {
|
|
|
10
10
|
EMPTY_AGENT_PRESET_CATALOG,
|
|
11
11
|
} from '../../agent-preset.js';
|
|
12
12
|
import { useWorkspaceSnapshotFence } from '../../workspace-snapshot-fence.js';
|
|
13
|
-
import {
|
|
13
|
+
import {
|
|
14
|
+
BotStatusMeta,
|
|
15
|
+
ChannelListHeading,
|
|
16
|
+
LastMessageErrorSummary,
|
|
17
|
+
} from '../../channel-card-meta.js';
|
|
14
18
|
import { installDingtalkStyles } from '../dingtalk/styles.js';
|
|
15
19
|
import {
|
|
16
20
|
WHATSAPP_ENDPOINTS,
|
|
@@ -345,6 +349,10 @@ export function WhatsappAccountCard({
|
|
|
345
349
|
className: 'dim-cardAction', kind: 'danger', onClick: onRequestRemove, disabled: Boolean(busy),
|
|
346
350
|
}, '移除接入')),
|
|
347
351
|
summary ? h('div', { className: 'ddt-summary dim-cardSummary' }, summary) : null,
|
|
352
|
+
account.lastMessageError ? h(LastMessageErrorSummary, {
|
|
353
|
+
className: 'ddt-summary',
|
|
354
|
+
error: account.lastMessageError,
|
|
355
|
+
}) : null,
|
|
348
356
|
testNotice ? h('div', {
|
|
349
357
|
className: 'ddt-summary dim-cardFeedback',
|
|
350
358
|
role: 'status',
|