@xmanrui/dsh-im 1.4.0 → 2.0.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 +18 -6
- package/README.md +18 -6
- package/lib/index.js +206 -176
- package/package.json +1 -1
- package/plugin-src/host/index.mjs +2 -0
- package/src/channels/dingtalk/config-store.mjs +5 -3
- package/src/channels/dingtalk/dingtalk-api.mjs +105 -82
- package/src/channels/dingtalk/dingtalk-bridge.mjs +79 -110
- package/src/channels/dingtalk/dingtalk-card-stream.mjs +3 -1
- package/src/channels/dingtalk/dingtalk-controller.mjs +24 -23
- package/src/channels/dingtalk/dingtalk-runtime.mjs +4 -3
- package/src/channels/dingtalk/state-store.mjs +3 -1
- package/src/channels/discord/config-store.mjs +2 -1
- package/src/channels/discord/discord-api.mjs +25 -1
- package/src/channels/discord/discord-runtime.mjs +237 -11
- package/src/channels/feishu/bridge.mjs +169 -180
- package/src/channels/feishu/feishu-cards.mjs +62 -54
- package/src/channels/feishu/feishu-channel.mjs +57 -21
- package/src/channels/feishu/feishu-runtime.mjs +10 -9
- package/src/channels/feishu/message-utils.mjs +4 -3
- package/src/channels/office/office-job-executor.mjs +15 -14
- package/src/channels/office/office-runtime.mjs +6 -5
- package/src/channels/qq/config-store.mjs +3 -1
- package/src/channels/qq/markdown-reply.mjs +4 -2
- package/src/channels/qq/qq-bridge.mjs +149 -130
- package/src/channels/qq/qq-controller.mjs +17 -16
- package/src/channels/qq/qq-runtime.mjs +3 -2
- package/src/channels/shared/agent-preset.mjs +3 -1
- package/src/channels/shared/compact-command.mjs +15 -14
- package/src/channels/shared/connection-test.mjs +6 -4
- package/src/channels/shared/control-command.mjs +13 -11
- package/src/channels/shared/editable-message-stream.mjs +4 -2
- package/src/channels/shared/harness-approval.mjs +23 -21
- package/src/channels/shared/harness-client.mjs +3 -2
- package/src/channels/shared/harness-question.mjs +8 -6
- package/src/channels/shared/i18n-en/dingtalk.mjs +52 -0
- package/src/channels/shared/i18n-en/discord.mjs +8 -0
- package/src/channels/shared/i18n-en/feishu.mjs +217 -0
- package/src/channels/shared/i18n-en/office.mjs +23 -0
- package/src/channels/shared/i18n-en/qq.mjs +54 -0
- package/src/channels/shared/i18n-en/shared-a.mjs +91 -0
- package/src/channels/shared/i18n-en/shared-b.mjs +215 -0
- package/src/channels/shared/i18n-en/shared-c.mjs +99 -0
- package/src/channels/shared/i18n-en/slack.mjs +27 -0
- package/src/channels/shared/i18n-en/telegram.mjs +21 -0
- package/src/channels/shared/i18n-en/wecom.mjs +38 -0
- package/src/channels/shared/i18n-en/weixin.mjs +80 -0
- package/src/channels/shared/i18n-en/whatsapp.mjs +17 -0
- package/src/channels/shared/i18n-en.mjs +35 -0
- package/src/channels/shared/i18n.mjs +35 -0
- package/src/channels/shared/image-prompt.mjs +17 -15
- package/src/channels/shared/inbound-file.mjs +5 -3
- package/src/channels/shared/model-command.mjs +35 -31
- package/src/channels/shared/preset-command.mjs +41 -38
- package/src/channels/shared/semantic/artifact-delivery.mjs +170 -0
- package/src/channels/shared/semantic/artifact.mjs +2 -2
- package/src/channels/shared/semantic/delivery.mjs +40 -0
- package/src/channels/shared/text-harness-bridge.mjs +154 -136
- package/src/channels/shared/token-bot-controller.mjs +32 -11
- package/src/channels/shared/workspace-command.mjs +69 -58
- package/src/channels/slack/config-store.mjs +3 -1
- package/src/channels/slack/slack-api.mjs +6 -5
- package/src/channels/slack/slack-controller.mjs +10 -9
- package/src/channels/slack/slack-runtime.mjs +16 -4
- package/src/channels/telegram/config-store.mjs +2 -1
- package/src/channels/telegram/telegram-api.mjs +111 -19
- package/src/channels/telegram/telegram-rich-message.mjs +147 -0
- package/src/channels/telegram/telegram-runtime.mjs +383 -5
- package/src/channels/wecom/config-store.mjs +3 -1
- package/src/channels/wecom/wecom-bridge.mjs +154 -153
- package/src/channels/wecom/wecom-controller.mjs +20 -19
- package/src/channels/wecom/wecom-runtime.mjs +2 -1
- package/src/channels/weixin/config-store.mjs +2 -1
- package/src/channels/weixin/weixin-api.mjs +140 -107
- package/src/channels/weixin/weixin-bridge.mjs +86 -123
- package/src/channels/weixin/weixin-controller.mjs +22 -21
- package/src/channels/weixin/weixin-runtime.mjs +3 -2
- package/src/channels/whatsapp/config-store.mjs +3 -1
- package/src/channels/whatsapp/whatsapp-bridge.mjs +3 -1
- package/src/channels/whatsapp/whatsapp-controller.mjs +15 -14
- package/src/channels/whatsapp/whatsapp-runtime.mjs +47 -13
- package/src/channels/whatsapp/whatsapp-web-session.mjs +3 -1
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
// English translations (shared-c area). Keys are exact Chinese literals passed to t().
|
|
2
|
+
export default {
|
|
3
|
+
// harness-approval.mjs
|
|
4
|
+
'请精准回复「批准」或「拒绝」(也支持:同意 / 不同意 / yes / no)。':
|
|
5
|
+
'Please reply exactly with 「批准」 (approve) or 「拒绝」 (reject). Also accepted: 同意 / 不同意 / yes / no.',
|
|
6
|
+
'请先完成当前问题,再精准回复「批准」或「拒绝」。':
|
|
7
|
+
'Please finish the current question first, then reply exactly with 「批准」 (approve) or 「拒绝」 (reject).',
|
|
8
|
+
'该审批已处理,无需再次回复。':
|
|
9
|
+
'This approval has already been handled; no need to reply again.',
|
|
10
|
+
'DeepSeek Harness 需要你的审批:':
|
|
11
|
+
'DeepSeek Harness needs your approval:',
|
|
12
|
+
'工具:{tool}': 'Tool: {tool}',
|
|
13
|
+
'操作参数:': 'Operation parameters:',
|
|
14
|
+
'原因:{reason}': 'Reason: {reason}',
|
|
15
|
+
'群聊中请 @机器人 后发送审批决定。':
|
|
16
|
+
'In group chats, please @ the bot before sending your approval decision.',
|
|
17
|
+
'只有发起当前任务的用户可以处理这条审批。':
|
|
18
|
+
'Only the user who started the current task can handle this approval.',
|
|
19
|
+
'审批决定正在提交,请稍候。':
|
|
20
|
+
'Your approval decision is being submitted; please wait.',
|
|
21
|
+
'已批准,仅对本次操作有效。':
|
|
22
|
+
'Approved — valid for this operation only.',
|
|
23
|
+
'已拒绝此次操作。': 'This operation was rejected.',
|
|
24
|
+
'无法完整展示这次操作,已安全拒绝此次审批。':
|
|
25
|
+
'The operation could not be displayed in full, so this approval was safely rejected.',
|
|
26
|
+
'审批提交失败,请重新回复「批准」或「拒绝」。':
|
|
27
|
+
'Failed to submit the approval; please reply with 「批准」 (approve) or 「拒绝」 (reject) again.',
|
|
28
|
+
|
|
29
|
+
// harness-question.mjs
|
|
30
|
+
'DeepSeek Harness 需要你补充信息{progress}:':
|
|
31
|
+
'DeepSeek Harness needs more information{progress}:',
|
|
32
|
+
'请输入你的回答。': 'Please enter your answer.',
|
|
33
|
+
'请回复选项序号或文字;多选用逗号分隔,也可补充其他内容。':
|
|
34
|
+
'Reply with option numbers or text; separate multiple choices with commas, or add anything else.',
|
|
35
|
+
'请回复一个选项序号或文字,也可直接输入其他答案。':
|
|
36
|
+
'Reply with an option number or its text, or type your own answer directly.',
|
|
37
|
+
'请直接回复你的答案。': 'Please reply with your answer directly.',
|
|
38
|
+
'群聊中请 @机器人 后发送答案。':
|
|
39
|
+
'In group chats, please @ the bot before sending your answer.',
|
|
40
|
+
|
|
41
|
+
// image-prompt.mjs
|
|
42
|
+
'当前模型不支持图片,请用 /models 查看可用模型,再用 /model <序号> 切换后重发。':
|
|
43
|
+
'The current model does not support images. Use /models to list available models, switch with /model <number>, then resend.',
|
|
44
|
+
'图片超过宿主允许的大小,请压缩后重试。':
|
|
45
|
+
'The image exceeds the size allowed by the host; compress it and try again.',
|
|
46
|
+
'图片分辨率过高,请压缩后重试。':
|
|
47
|
+
'The image resolution is too high; compress it and try again.',
|
|
48
|
+
'图片内容无效或格式不受支持,请重新发送。':
|
|
49
|
+
'The image content is invalid or its format is unsupported; please resend it.',
|
|
50
|
+
'未能读取图片内容,请重新发送。':
|
|
51
|
+
'Could not read the image content; please resend it.',
|
|
52
|
+
'图片格式与实际内容不一致,请重新发送。':
|
|
53
|
+
'The image format does not match its actual content; please resend it.',
|
|
54
|
+
'一次发送的图片数量超过宿主限制,请减少后重试。':
|
|
55
|
+
'The number of images sent at once exceeds the host limit; send fewer and try again.',
|
|
56
|
+
'图片总大小超过宿主限制,请减少图片或压缩后重试。':
|
|
57
|
+
'The total image size exceeds the host limit; send fewer images or compress them and try again.',
|
|
58
|
+
'图片下载地址发生了重定向,暂时无法读取。':
|
|
59
|
+
'The image download URL redirected and cannot be read right now.',
|
|
60
|
+
'图片下载失败(HTTP {status}),请重新发送后再试。':
|
|
61
|
+
'Image download failed (HTTP {status}); please resend it and try again.',
|
|
62
|
+
'图片超过 5 MB,请压缩后重试。':
|
|
63
|
+
'The image is larger than 5 MB; compress it and try again.',
|
|
64
|
+
'一次最多只能处理 {maxImages} 张图片。':
|
|
65
|
+
'At most {maxImages} images can be processed at once.',
|
|
66
|
+
'一次发送的图片总大小过大,请减少图片数量或压缩后重试。':
|
|
67
|
+
'The total size of the images sent is too large; send fewer images or compress them and try again.',
|
|
68
|
+
'图片下载失败,请重新发送后再试。':
|
|
69
|
+
'Image download failed; please resend it and try again.',
|
|
70
|
+
'暂不支持该图片格式,请发送 JPEG、PNG、WebP 或 GIF 图片。':
|
|
71
|
+
'This image format is not supported yet; please send a JPEG, PNG, WebP, or GIF image.',
|
|
72
|
+
|
|
73
|
+
// connection-test.mjs
|
|
74
|
+
'✅ DeepSeek Harness 连接测试成功':
|
|
75
|
+
'✅ DeepSeek Harness connection test succeeded',
|
|
76
|
+
'这条消息由插件页面中的“{name}”机器人卡片发出。':
|
|
77
|
+
'This message was sent from the "{name}" bot card on the plugin page.',
|
|
78
|
+
'{channelLabel}尚未收到可用于测试的私聊消息。':
|
|
79
|
+
'The {channelLabel} has not received a direct message that can be used for testing yet.',
|
|
80
|
+
'机器人': 'bot',
|
|
81
|
+
|
|
82
|
+
// editable-message-stream.mjs / harness-client.mjs / progress text
|
|
83
|
+
'正在处理…': 'Processing…',
|
|
84
|
+
'处理完成。': 'Processing complete.',
|
|
85
|
+
'工具': 'tool',
|
|
86
|
+
'正在整理结果…': 'Gathering results…',
|
|
87
|
+
'_正在搜索网络并整理信息…_': '_Searching the web and gathering information…_',
|
|
88
|
+
'_正在使用 {name}…_': '_Using {name}…_',
|
|
89
|
+
|
|
90
|
+
// image-prompt.mjs
|
|
91
|
+
'请分析这张图片。': 'Analyze this image.',
|
|
92
|
+
|
|
93
|
+
// inbound-file.mjs
|
|
94
|
+
'文件接收失败,请重新发送后再试。': 'File reception failed. Please resend it.',
|
|
95
|
+
'文件下载失败,请重新发送后再试。': 'File download failed. Please resend it.',
|
|
96
|
+
|
|
97
|
+
// agent-preset.mjs
|
|
98
|
+
'Agent Preset 无效。': 'Invalid Agent Preset.',
|
|
99
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// English translations (slack area). Keys are exact Chinese literals passed to t().
|
|
2
|
+
export default {
|
|
3
|
+
'Slack机器人': 'Slack bot',
|
|
4
|
+
'Slack机器人凭据缺失,请移除后重新接入。':
|
|
5
|
+
'Slack bot credentials are missing. Remove the bot and connect it again.',
|
|
6
|
+
'Slack Socket Mode 连接未就绪,插件会自动重试。':
|
|
7
|
+
'The Slack Socket Mode connection is not ready yet. The plugin will retry automatically.',
|
|
8
|
+
'Slack机器人已接入,Socket Mode 连接暂未就绪。':
|
|
9
|
+
'The Slack bot is connected, but the Socket Mode connection is not ready yet.',
|
|
10
|
+
'Slack Socket Mode 连接仍未就绪,请检查两个 Token。':
|
|
11
|
+
'The Slack Socket Mode connection is still not ready. Check both tokens.',
|
|
12
|
+
'Slack机器人尚未连接': 'The Slack bot is not connected yet',
|
|
13
|
+
'Slack Socket Mode 长连接运行正常':
|
|
14
|
+
'The Slack Socket Mode long-lived connection is running normally',
|
|
15
|
+
'Slack连接未就绪,插件会自动重试':
|
|
16
|
+
'The Slack connection is not ready. The plugin will retry automatically',
|
|
17
|
+
'Slack连接当前离线': 'The Slack connection is currently offline',
|
|
18
|
+
|
|
19
|
+
'Slack 未授权机器人读取该文件。请为应用添加 files:read 后重新安装,再重新发送图片。':
|
|
20
|
+
'Slack has not granted the bot access to this file. Add the files:read scope to the app, reinstall it, then send the image again.',
|
|
21
|
+
'Slack Bot Token 必须以 xoxb- 开头。': 'The Slack Bot Token must start with xoxb-.',
|
|
22
|
+
'Slack App Token 必须以 xapp- 开头。': 'The Slack App Token must start with xapp-.',
|
|
23
|
+
'Slack Bot Token 没有返回完整的机器人身份。':
|
|
24
|
+
'The Slack Bot Token did not return a complete bot identity.',
|
|
25
|
+
'Slack App Token 无法创建 Socket Mode 连接,请确认已启用 Socket Mode 和 connections:write。':
|
|
26
|
+
'The Slack App Token could not create a Socket Mode connection. Make sure Socket Mode and connections:write are enabled.',
|
|
27
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// English translations (telegram area). Keys are exact Chinese literals passed to t().
|
|
2
|
+
export default {
|
|
3
|
+
'开启一个全新会话': 'Start a brand-new Session',
|
|
4
|
+
'压缩当前会话的较早上下文': 'Compact the earlier context of the current Session',
|
|
5
|
+
'切换工作区': 'Switch Workspace',
|
|
6
|
+
'列出工作区绝对路径': 'List Workspace absolute paths',
|
|
7
|
+
'列出会话 ID 和标题': 'List Session IDs and titles',
|
|
8
|
+
'将当前聊天绑定到指定会话': 'Bind the current chat to a specific Session',
|
|
9
|
+
'按序号列出所有可用模型': 'List all available models by number',
|
|
10
|
+
'查看或切换当前会话模型': 'View or switch the model of the current Session',
|
|
11
|
+
'列出可用 Agent Preset': 'List available Agent Presets',
|
|
12
|
+
'查看或设置新会话 Agent Preset': 'View or set the Agent Preset for new Sessions',
|
|
13
|
+
'停止当前任务': 'Stop the current task',
|
|
14
|
+
'纠偏当前任务': 'Steer the current task',
|
|
15
|
+
'检查连接状态': 'Check connection status',
|
|
16
|
+
'显示帮助': 'Show help',
|
|
17
|
+
'该 Telegram 机器人已配置 Webhook,请先在原服务中移除 Webhook 后重试。':
|
|
18
|
+
'This Telegram bot already has a Webhook configured. Remove the Webhook from the original service and try again.',
|
|
19
|
+
'Telegram机器人': 'Telegram Bot',
|
|
20
|
+
' Bot API 长轮询': ' Bot API long polling',
|
|
21
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// English translations (wecom area). Keys are exact Chinese literals passed to t().
|
|
2
|
+
export default {
|
|
3
|
+
// Help text (wecom-bridge.mjs)
|
|
4
|
+
'企业微信机器人已连接 DeepSeek Harness。': 'The Enterprise WeChat bot is connected to DeepSeek Harness.',
|
|
5
|
+
|
|
6
|
+
// Bot replies and status text (wecom-bridge.mjs)
|
|
7
|
+
|
|
8
|
+
'当前待处理图片较多,请稍后重新发送。': 'Too many images are pending. Please send them again later.',
|
|
9
|
+
|
|
10
|
+
'结果文件「{name}」已生成,但企业微信智能机器人缺少素材上传或文件消息能力,请检查机器人权限。': 'The result file "{name}" was generated, but the Enterprise WeChat bot lacks media upload or file message capability. Please check the bot permissions.',
|
|
11
|
+
'结果文件「{name}」超过当前企业微信机器人可发送的文件大小,未发送。': 'The result file "{name}" exceeds the file size this Enterprise WeChat bot can send and was not sent.',
|
|
12
|
+
'结果文件「{name}」为空,企业微信不允许发送空文件。': 'The result file "{name}" is empty. Enterprise WeChat does not allow sending empty files.',
|
|
13
|
+
'结果文件「{name}」暂时被企业微信限流,未能发送,请稍后重试。': 'The result file "{name}" was rate-limited by Enterprise WeChat and could not be sent. Please try again later.',
|
|
14
|
+
'结果文件「{name}」已生成,但企业微信拒绝了该文件或文件消息。': 'The result file "{name}" was generated, but Enterprise WeChat rejected the file or file message.',
|
|
15
|
+
'结果文件「{name}」已生成,但暂时未能通过企业微信发送,请稍后重试。': 'The result file "{name}" was generated but could not be sent via Enterprise WeChat right now. Please try again later.',
|
|
16
|
+
'任务已完成,但没有生成可显示的文本。': 'The task completed, but no displayable text was generated.',
|
|
17
|
+
'目前支持文字、图片和语音转写消息。': 'Currently only text, images, and voice transcription messages are supported.',
|
|
18
|
+
'企业微信机器人与 DeepSeek Harness 连接正常。': 'The connection between the Enterprise WeChat bot and DeepSeek Harness is working.',
|
|
19
|
+
'正在思考中…': 'Thinking…',
|
|
20
|
+
'企业微信交互问题发送失败。': 'Failed to send the Enterprise WeChat interaction question.',
|
|
21
|
+
|
|
22
|
+
// Provisioning, status, and errors (wecom-controller.mjs)
|
|
23
|
+
'企业微信机器人凭据缺失,请移除后重新扫码。': 'The Enterprise WeChat bot credentials are missing. Please remove the bot and scan the QR code again.',
|
|
24
|
+
'企业微信连接未就绪,插件会自动重试。': 'The Enterprise WeChat connection is not ready. The plugin will retry automatically.',
|
|
25
|
+
'扫码绑定已取消。': 'QR code binding has been cancelled.',
|
|
26
|
+
'无法生成企业微信二维码,请稍后重试。': 'Unable to generate the Enterprise WeChat QR code. Please try again later.',
|
|
27
|
+
'企业微信二维码已过期,请重新生成。': 'The Enterprise WeChat QR code has expired. Please generate a new one.',
|
|
28
|
+
'企业微信机器人已绑定,消息连接暂未就绪。': 'The Enterprise WeChat bot is bound, but the message connection is not ready yet.',
|
|
29
|
+
'企业微信连接仍未就绪,请稍后重试。': 'The Enterprise WeChat connection is still not ready. Please try again later.',
|
|
30
|
+
'企业微信扫码没有完成,请重新生成二维码。': 'The Enterprise WeChat QR scan was not completed. Please generate a new QR code.',
|
|
31
|
+
'企业微信扫码服务暂时不可用,请重新生成二维码。': 'The Enterprise WeChat QR service is temporarily unavailable. Please generate a new QR code.',
|
|
32
|
+
'企业微信已授权,但无法安全保存接入配置。': 'Enterprise WeChat authorization succeeded, but the connection configuration could not be saved safely.',
|
|
33
|
+
'企业微信机器人': 'Enterprise WeChat bot',
|
|
34
|
+
'企业微信机器人({botId})': 'Enterprise WeChat bot ({botId})',
|
|
35
|
+
'企业微信 WebSocket 长连接运行正常': 'The Enterprise WeChat WebSocket long connection is running normally',
|
|
36
|
+
'企业微信连接未就绪,插件会自动重试': 'The Enterprise WeChat connection is not ready. The plugin will retry automatically',
|
|
37
|
+
'企业微信连接当前离线': 'The Enterprise WeChat connection is currently offline',
|
|
38
|
+
};
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// English translations (weixin area). Keys are exact Chinese literals passed to t().
|
|
2
|
+
export default {
|
|
3
|
+
// weixin-bridge.mjs
|
|
4
|
+
'微信已连接 DeepSeek Harness。': 'WeChat is connected to DeepSeek Harness.',
|
|
5
|
+
'结果文件「{name}」已生成,但微信机器人当前没有文件消息发送权限,请检查机器人文件消息能力。': 'The result file "{name}" was generated, but the WeChat bot currently has no permission to send file messages. Please check the bot\'s file messaging capability.',
|
|
6
|
+
'结果文件「{name}」超过当前微信会话可发送的文件大小,未发送。': 'The result file "{name}" exceeds the file size limit of the current WeChat conversation and was not sent.',
|
|
7
|
+
'结果文件「{name}」暂时被微信限流,未能发送,请稍后重试。': 'The result file "{name}" was temporarily rate-limited by WeChat and was not sent. Please try again later.',
|
|
8
|
+
'结果文件「{name}」已生成,但微信拒绝了该文件消息。': 'The result file "{name}" was generated, but WeChat rejected the file message.',
|
|
9
|
+
'结果文件「{name}」已生成,但暂时未能通过微信发送,请稍后重试。': 'The result file "{name}" was generated but could not be sent via WeChat right now. Please try again later.',
|
|
10
|
+
'目前支持文字、图片,以及微信已转成文字的语音消息。': 'Currently supports text, images, and voice messages that WeChat has converted to text.',
|
|
11
|
+
'微信与 DeepSeek Harness 连接正常。': 'WeChat is connected to DeepSeek Harness normally.',
|
|
12
|
+
'微信交互问题发送失败。': 'Failed to send the WeChat interaction question.',
|
|
13
|
+
|
|
14
|
+
// weixin-controller.mjs
|
|
15
|
+
'微信已授权,但无法读取现有登录凭据。请检查 DSH 凭据存储。': 'WeChat was authorized, but the existing login credential could not be read. Please check the DSH credential store.',
|
|
16
|
+
'微信已授权,但登录凭据无法写入 DSH 凭据存储。请检查凭据存储是否可写。': 'WeChat was authorized, but the login credential could not be written to the DSH credential store. Please check that the credential store is writable.',
|
|
17
|
+
'微信已授权,但账号配置无法写入本机。请检查 DSH_HOME 目录权限。': 'WeChat was authorized, but the account configuration could not be written locally. Please check the DSH_HOME directory permissions.',
|
|
18
|
+
'微信已授权,但无法初始化账号状态或工作区。请检查 DSH_HOME 和工作区目录。': 'WeChat was authorized, but the account state or workspace could not be initialized. Please check DSH_HOME and the workspace directories.',
|
|
19
|
+
'微信已授权,但插件无法连接本机 Harness。请检查 dsh web 地址和端口。': 'WeChat was authorized, but the plugin could not connect to the local Harness. Please check the dsh web address and port.',
|
|
20
|
+
'微信已授权,但 Harness 健康检查超时。请确认 dsh web 未阻塞。': 'WeChat was authorized, but the Harness health check timed out. Please confirm dsh web is not blocked.',
|
|
21
|
+
'微信已授权,但 Harness 健康检查需要身份认证。请检查代理、网关或自定义鉴权配置。': 'WeChat was authorized, but the Harness health check requires authentication. Please check proxy, gateway, or custom auth configuration.',
|
|
22
|
+
'微信已授权,但本机 Harness 请求被代理要求认证。请让回环地址绕过代理,并检查 NO_PROXY 配置。': 'WeChat was authorized, but the local Harness request was challenged for proxy authentication. Please bypass the proxy for loopback addresses and check the NO_PROXY configuration.',
|
|
23
|
+
'微信已授权,但 Harness 异常拒绝了回环地址的健康检查。请检查 HTTP 代理、Harness 源码版本和构建产物。': 'WeChat was authorized, but Harness unexpectedly rejected the health check from a loopback address. Please check the HTTP proxy, Harness source version, and build artifacts.',
|
|
24
|
+
'微信已授权,但 Harness 的 Host 信任检查拒绝了非回环地址请求。请检查 harnessBaseUrl 与 trustedHosts 配置。': 'WeChat was authorized, but the Harness Host trust check rejected a non-loopback request. Please check the harnessBaseUrl and trustedHosts configuration.',
|
|
25
|
+
'微信已授权,但健康检查收到了非 Harness 标准的 403 拒绝响应。请检查代理或网关配置。': 'WeChat was authorized, but the health check received a non-standard Harness 403 rejection. Please check the proxy or gateway configuration.',
|
|
26
|
+
'微信已授权,但找不到 Harness 健康检查接口。请确认 Harness 与插件版本兼容。': 'WeChat was authorized, but the Harness health check endpoint was not found. Please confirm the Harness and plugin versions are compatible.',
|
|
27
|
+
'微信已授权,但 Harness 健康检查返回服务错误。请查看 dsh web 日志。': 'WeChat was authorized, but the Harness health check returned a server error. Please check the dsh web logs.',
|
|
28
|
+
'微信已授权,但 Harness 返回了无法识别的响应。请确认 Harness 与插件版本兼容。': 'WeChat was authorized, but Harness returned an unrecognized response. Please confirm the Harness and plugin versions are compatible.',
|
|
29
|
+
'微信已授权,但 Harness 拒绝了健康检查请求。请查看 dsh web 日志。': 'WeChat was authorized, but Harness rejected the health check request. Please check the dsh web logs.',
|
|
30
|
+
'微信已授权,但 Harness 健康检查发生未知错误。请查看 dsh web 日志。': 'WeChat was authorized, but the Harness health check failed with an unknown error. Please check the dsh web logs.',
|
|
31
|
+
'微信已授权,但消息连接初始化失败。请查看 dsh web 日志后重试。': 'WeChat was authorized, but the message connection failed to initialize. Please check the dsh web logs and try again.',
|
|
32
|
+
'微信已授权,但激活过程中发生未知错误。请查看 dsh web 日志。': 'WeChat was authorized, but an unknown error occurred during activation. Please check the dsh web logs.',
|
|
33
|
+
'登录凭据缺失,请移除账号后重新扫码。': 'The login credential is missing. Please remove the account and scan the QR code again.',
|
|
34
|
+
'微信连接未就绪,插件会自动重试。': 'The WeChat connection is not ready; the plugin will retry automatically.',
|
|
35
|
+
'微信连接未就绪,插件会自动重试': 'The WeChat connection is not ready; the plugin will retry automatically',
|
|
36
|
+
'无法生成微信二维码,请稍后重试。': 'Unable to generate the WeChat QR code. Please try again later.',
|
|
37
|
+
'微信连接仍未就绪,请稍后重试。': 'The WeChat connection is still not ready. Please try again later.',
|
|
38
|
+
'微信机器人': 'WeChat Bot',
|
|
39
|
+
'微信机器人({name})': 'WeChat Bot ({name})',
|
|
40
|
+
'微信消息长轮询运行正常': 'WeChat message long-polling is running normally',
|
|
41
|
+
'微信连接当前离线': 'The WeChat connection is currently offline',
|
|
42
|
+
'配对码多次错误,请重新生成二维码。': 'The pairing code was entered incorrectly too many times. Please generate a new QR code.',
|
|
43
|
+
'该微信账号已绑定,但本机没有可恢复的凭据。': 'This WeChat account is already bound, but no recoverable credential exists on this machine.',
|
|
44
|
+
'微信授权成功,但返回的账号凭据不完整。': 'WeChat authorization succeeded, but the returned account credential is incomplete.',
|
|
45
|
+
|
|
46
|
+
// weixin-runtime.mjs
|
|
47
|
+
'微信登录凭据已失效,请移除账号后重新扫码。': 'The WeChat login credential has expired. Please remove the account and scan the QR code again.',
|
|
48
|
+
'微信消息同步请求被拒绝。': 'The WeChat message sync request was rejected.',
|
|
49
|
+
|
|
50
|
+
// weixin-api.mjs
|
|
51
|
+
'微信图片的加密密钥无效。': 'The encryption key of the WeChat image is invalid.',
|
|
52
|
+
'微信图片的加密数据无效。': 'The encrypted data of the WeChat image is invalid.',
|
|
53
|
+
'微信图片解密失败。': 'Failed to decrypt the WeChat image.',
|
|
54
|
+
'微信图片没有可用的下载地址。': 'The WeChat image has no usable download URL.',
|
|
55
|
+
'微信图片的下载地址无效。': 'The download URL of the WeChat image is invalid.',
|
|
56
|
+
'微信图片的下载地址不受信任。': 'The download URL of the WeChat image is not trusted.',
|
|
57
|
+
'微信服务返回了无效的连接地址。': 'The WeChat service returned an invalid connection URL.',
|
|
58
|
+
'微信服务返回了不受信任的连接地址。': 'The WeChat service returned an untrusted connection URL.',
|
|
59
|
+
'微信服务没有返回扫码地址。': 'The WeChat service did not return a QR-code URL.',
|
|
60
|
+
'微信服务返回了无效的扫码地址。': 'The WeChat service returned an invalid QR-code URL.',
|
|
61
|
+
'微信服务返回了不受信任的扫码地址。': 'The WeChat service returned an untrusted QR-code URL.',
|
|
62
|
+
'微信服务返回了无效的文件上传地址。': 'The WeChat service returned an invalid file upload URL.',
|
|
63
|
+
'微信服务返回了不受信任的文件上传地址。': 'The WeChat service returned an untrusted file upload URL.',
|
|
64
|
+
'微信服务没有返回文件上传地址。': 'The WeChat service did not return a file upload URL.',
|
|
65
|
+
'微信文件上传被拒绝(HTTP {status})。': 'The WeChat file upload was rejected (HTTP {status}).',
|
|
66
|
+
'微信文件上传失败(HTTP {status})。': 'The WeChat file upload failed (HTTP {status}).',
|
|
67
|
+
'微信文件上传响应缺少下载参数。': 'The WeChat file upload response is missing the download parameter.',
|
|
68
|
+
'微信文件上传失败。': 'The WeChat file upload failed.',
|
|
69
|
+
'拒绝访问不受信任的微信服务地址。': 'Refusing to access an untrusted WeChat service URL.',
|
|
70
|
+
'微信服务请求失败(HTTP {status})。': 'The WeChat service request failed (HTTP {status}).',
|
|
71
|
+
'微信服务返回了无法解析的响应。': 'The WeChat service returned an unparseable response.',
|
|
72
|
+
'微信服务请求超时。': 'The WeChat service request timed out.',
|
|
73
|
+
'暂时无法访问微信服务。': 'The WeChat service is temporarily unreachable.',
|
|
74
|
+
'微信服务返回了无法识别的扫码状态。': 'The WeChat service returned an unrecognized QR-code status.',
|
|
75
|
+
'微信服务没有返回二维码令牌。': 'The WeChat service did not return a QR-code token.',
|
|
76
|
+
'微信服务拒绝了回复消息。': 'The WeChat service rejected the reply message.',
|
|
77
|
+
'微信服务拒绝了文件上传请求。': 'The WeChat service rejected the file upload request.',
|
|
78
|
+
'微信服务拒绝了文件消息。': 'The WeChat service rejected the file message.',
|
|
79
|
+
'微信账号连接启动失败。': 'Failed to start the WeChat account connection.',
|
|
80
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// English translations (whatsapp area). Keys are exact Chinese literals passed to t().
|
|
2
|
+
export default {
|
|
3
|
+
'WhatsApp 关联设备已失效,请移除后重新扫码。': 'The WhatsApp linked device is no longer valid. Remove it and scan the QR code again.',
|
|
4
|
+
'WhatsApp 连接未就绪,插件会自动重试。': 'The WhatsApp connection is not ready yet; the plugin will retry automatically.',
|
|
5
|
+
'WhatsApp 连接仍未就绪,请稍后重试。': 'The WhatsApp connection is still not ready. Please try again later.',
|
|
6
|
+
'WhatsApp机器人尚未连接': 'The WhatsApp bot is not connected yet',
|
|
7
|
+
'WhatsApp机器人': 'WhatsApp bot',
|
|
8
|
+
'WhatsApp Web 关联设备运行正常': 'The WhatsApp Web linked device is working normally',
|
|
9
|
+
'WhatsApp 连接未就绪': 'The WhatsApp connection is not ready',
|
|
10
|
+
'WhatsApp 连接当前离线': 'The WhatsApp connection is currently offline',
|
|
11
|
+
'WhatsApp 已绑定,消息连接暂未就绪。': 'WhatsApp is linked, but the messaging connection is not ready yet.',
|
|
12
|
+
'WhatsApp 已扫码,但无法保存关联设备。': 'The QR code was scanned, but the linked device could not be saved.',
|
|
13
|
+
'无法连接 WhatsApp,请重新生成二维码。': 'Unable to connect to WhatsApp. Please generate a new QR code.',
|
|
14
|
+
|
|
15
|
+
'WhatsApp账号': 'WhatsApp account',
|
|
16
|
+
' Web 关联设备': ' Web linked device',
|
|
17
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// English translations for host-side user-facing text.
|
|
2
|
+
// Keys are the exact Chinese literals passed to t() in src/channels/**.
|
|
3
|
+
// Chinese output is the identity default and needs no entries here.
|
|
4
|
+
// Entries are maintained per area in ./i18n-en/*.mjs and merged here.
|
|
5
|
+
|
|
6
|
+
import sharedA from './i18n-en/shared-a.mjs';
|
|
7
|
+
import sharedB from './i18n-en/shared-b.mjs';
|
|
8
|
+
import sharedC from './i18n-en/shared-c.mjs';
|
|
9
|
+
import feishu from './i18n-en/feishu.mjs';
|
|
10
|
+
import dingtalk from './i18n-en/dingtalk.mjs';
|
|
11
|
+
import wecom from './i18n-en/wecom.mjs';
|
|
12
|
+
import qq from './i18n-en/qq.mjs';
|
|
13
|
+
import weixin from './i18n-en/weixin.mjs';
|
|
14
|
+
import slack from './i18n-en/slack.mjs';
|
|
15
|
+
import telegram from './i18n-en/telegram.mjs';
|
|
16
|
+
import discord from './i18n-en/discord.mjs';
|
|
17
|
+
import whatsapp from './i18n-en/whatsapp.mjs';
|
|
18
|
+
import office from './i18n-en/office.mjs';
|
|
19
|
+
|
|
20
|
+
export const EN = Object.freeze(Object.assign(
|
|
21
|
+
{},
|
|
22
|
+
sharedA,
|
|
23
|
+
sharedB,
|
|
24
|
+
sharedC,
|
|
25
|
+
feishu,
|
|
26
|
+
dingtalk,
|
|
27
|
+
wecom,
|
|
28
|
+
qq,
|
|
29
|
+
weixin,
|
|
30
|
+
slack,
|
|
31
|
+
telegram,
|
|
32
|
+
discord,
|
|
33
|
+
whatsapp,
|
|
34
|
+
office,
|
|
35
|
+
));
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// Host-side i18n for dsh-im. Mirrors the conventions of the settings-UI
|
|
2
|
+
// translator in plugin-src/client/i18n.js: dictionary keys are the exact
|
|
3
|
+
// Chinese source literals, and Chinese (zh) is the identity default, so
|
|
4
|
+
// untranslated or unrecognized text always falls back to the original
|
|
5
|
+
// Chinese output unchanged.
|
|
6
|
+
//
|
|
7
|
+
// The English dictionary lives in ./i18n-en.mjs to keep this module small.
|
|
8
|
+
|
|
9
|
+
import { EN } from './i18n-en.mjs';
|
|
10
|
+
|
|
11
|
+
let language = 'zh';
|
|
12
|
+
|
|
13
|
+
// Accepts 'en', 'en-US', 'english' (any case) as English; anything else
|
|
14
|
+
// (including undefined and unrecognized values) selects Chinese.
|
|
15
|
+
export function setImHostLanguage(lang) {
|
|
16
|
+
const normalized = typeof lang === 'string' ? lang.trim().toLowerCase() : '';
|
|
17
|
+
language = normalized === 'english' || /^en(?:[-_].*)?$/u.test(normalized) ? 'en' : 'zh';
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function getImHostLanguage() {
|
|
21
|
+
return language;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Translate a user-facing Chinese literal. In zh mode (the default) this is
|
|
25
|
+
// the identity function. Optional `params` fills `{name}` placeholders in
|
|
26
|
+
// both the Chinese key and its translation, e.g.
|
|
27
|
+
// t('共 {count} 个机器人', { count: 3 })
|
|
28
|
+
export function t(text, params) {
|
|
29
|
+
if (typeof text !== 'string') return text;
|
|
30
|
+
const translated = language === 'en' ? EN[text] ?? text : text;
|
|
31
|
+
if (params == null) return translated;
|
|
32
|
+
return translated.replace(/\{(\w+)\}/g, (match, name) =>
|
|
33
|
+
Object.hasOwn(params, name) ? String(params[name]) : match,
|
|
34
|
+
);
|
|
35
|
+
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { t } from './i18n.mjs';
|
|
2
|
+
|
|
1
3
|
const DEFAULT_MAX_IMAGE_BYTES = 5 * 1024 * 1024;
|
|
2
4
|
const DEFAULT_MAX_IMAGES = 20;
|
|
3
5
|
const DEFAULT_MAX_TOTAL_IMAGE_BYTES = 20 * 1024 * 1024;
|
|
@@ -67,7 +69,7 @@ export async function fetchImageBuffer(url, {
|
|
|
67
69
|
throw new ImagePromptError(
|
|
68
70
|
'image-redirect-blocked',
|
|
69
71
|
`Image download redirect was blocked (HTTP ${response.status})`,
|
|
70
|
-
'图片下载地址发生了重定向,暂时无法读取。',
|
|
72
|
+
t('图片下载地址发生了重定向,暂时无法读取。'),
|
|
71
73
|
);
|
|
72
74
|
}
|
|
73
75
|
if (!response?.ok) {
|
|
@@ -75,7 +77,7 @@ export async function fetchImageBuffer(url, {
|
|
|
75
77
|
throw new ImagePromptError(
|
|
76
78
|
'image-http-error',
|
|
77
79
|
`Image download failed with HTTP ${response?.status ?? 'unknown'}`,
|
|
78
|
-
|
|
80
|
+
t('图片下载失败(HTTP {status}),请重新发送后再试。', { status: response?.status ?? 'unknown' }),
|
|
79
81
|
);
|
|
80
82
|
}
|
|
81
83
|
const declaredLength = Number(response.headers?.get?.('content-length'));
|
|
@@ -84,7 +86,7 @@ export async function fetchImageBuffer(url, {
|
|
|
84
86
|
throw new ImagePromptError(
|
|
85
87
|
'image-too-large',
|
|
86
88
|
`Image response declares ${declaredLength} bytes; the limit is ${maxBytes}`,
|
|
87
|
-
'图片超过 5 MB,请压缩后重试。',
|
|
89
|
+
t('图片超过 5 MB,请压缩后重试。'),
|
|
88
90
|
);
|
|
89
91
|
}
|
|
90
92
|
|
|
@@ -99,7 +101,7 @@ export async function fetchImageBuffer(url, {
|
|
|
99
101
|
throw new ImagePromptError(
|
|
100
102
|
'image-too-large',
|
|
101
103
|
`Image response exceeded ${maxBytes} bytes`,
|
|
102
|
-
'图片超过 5 MB,请压缩后重试。',
|
|
104
|
+
t('图片超过 5 MB,请压缩后重试。'),
|
|
103
105
|
);
|
|
104
106
|
}
|
|
105
107
|
chunks.push(data);
|
|
@@ -112,7 +114,7 @@ export async function fetchImageBuffer(url, {
|
|
|
112
114
|
throw new ImagePromptError(
|
|
113
115
|
'image-too-large',
|
|
114
116
|
`Image response contains ${data.length} bytes; the limit is ${maxBytes}`,
|
|
115
|
-
'图片超过 5 MB,请压缩后重试。',
|
|
117
|
+
t('图片超过 5 MB,请压缩后重试。'),
|
|
116
118
|
);
|
|
117
119
|
}
|
|
118
120
|
return data;
|
|
@@ -192,7 +194,7 @@ export async function promptContentForMessage(message, {
|
|
|
192
194
|
throw new ImagePromptError(
|
|
193
195
|
'too-many-images',
|
|
194
196
|
`Image message contains ${sources.length} images; the limit is ${maxImages}`,
|
|
195
|
-
|
|
197
|
+
t('一次最多只能处理 {maxImages} 张图片。', { maxImages }),
|
|
196
198
|
);
|
|
197
199
|
}
|
|
198
200
|
|
|
@@ -200,7 +202,7 @@ export async function promptContentForMessage(message, {
|
|
|
200
202
|
const content = [];
|
|
201
203
|
let totalImageBytes = 0;
|
|
202
204
|
if (text) content.push({ type: 'text', text });
|
|
203
|
-
else if (sources.length > 0) content.push({ type: 'text', text: DEFAULT_IMAGE_PROMPT });
|
|
205
|
+
else if (sources.length > 0) content.push({ type: 'text', text: t(DEFAULT_IMAGE_PROMPT) });
|
|
204
206
|
|
|
205
207
|
for (const [index, source] of sources.entries()) {
|
|
206
208
|
signal?.throwIfAborted();
|
|
@@ -208,14 +210,14 @@ export async function promptContentForMessage(message, {
|
|
|
208
210
|
throw new ImagePromptError(
|
|
209
211
|
'image-too-large',
|
|
210
212
|
`Image ${index + 1} declares ${source.size} bytes; the limit is ${maxImageBytes}`,
|
|
211
|
-
'图片超过 5 MB,请压缩后重试。',
|
|
213
|
+
t('图片超过 5 MB,请压缩后重试。'),
|
|
212
214
|
);
|
|
213
215
|
}
|
|
214
216
|
if (Number.isFinite(source?.size) && totalImageBytes + source.size > maxTotalImageBytes) {
|
|
215
217
|
throw new ImagePromptError(
|
|
216
218
|
'images-too-large',
|
|
217
219
|
`Images declare more than ${maxTotalImageBytes} bytes in total`,
|
|
218
|
-
'一次发送的图片总大小过大,请减少图片数量或压缩后重试。',
|
|
220
|
+
t('一次发送的图片总大小过大,请减少图片数量或压缩后重试。'),
|
|
219
221
|
);
|
|
220
222
|
}
|
|
221
223
|
|
|
@@ -230,7 +232,7 @@ export async function promptContentForMessage(message, {
|
|
|
230
232
|
throw new ImagePromptError(
|
|
231
233
|
'image-download-failed',
|
|
232
234
|
`Unable to download image ${index + 1}: ${error?.message ?? String(error)}`,
|
|
233
|
-
'图片下载失败,请重新发送后再试。',
|
|
235
|
+
t('图片下载失败,请重新发送后再试。'),
|
|
234
236
|
{ cause: error },
|
|
235
237
|
);
|
|
236
238
|
}
|
|
@@ -239,21 +241,21 @@ export async function promptContentForMessage(message, {
|
|
|
239
241
|
throw new ImagePromptError(
|
|
240
242
|
'invalid-image-data',
|
|
241
243
|
`Image ${index + 1} returned no data`,
|
|
242
|
-
'未能读取图片内容,请重新发送。',
|
|
244
|
+
t('未能读取图片内容,请重新发送。'),
|
|
243
245
|
);
|
|
244
246
|
}
|
|
245
247
|
if (loaded.data.length > maxImageBytes) {
|
|
246
248
|
throw new ImagePromptError(
|
|
247
249
|
'image-too-large',
|
|
248
250
|
`Image ${index + 1} contains ${loaded.data.length} bytes; the limit is ${maxImageBytes}`,
|
|
249
|
-
'图片超过 5 MB,请压缩后重试。',
|
|
251
|
+
t('图片超过 5 MB,请压缩后重试。'),
|
|
250
252
|
);
|
|
251
253
|
}
|
|
252
254
|
if (totalImageBytes + loaded.data.length > maxTotalImageBytes) {
|
|
253
255
|
throw new ImagePromptError(
|
|
254
256
|
'images-too-large',
|
|
255
257
|
`Images contain more than ${maxTotalImageBytes} bytes in total`,
|
|
256
|
-
'一次发送的图片总大小过大,请减少图片数量或压缩后重试。',
|
|
258
|
+
t('一次发送的图片总大小过大,请减少图片数量或压缩后重试。'),
|
|
257
259
|
);
|
|
258
260
|
}
|
|
259
261
|
totalImageBytes += loaded.data.length;
|
|
@@ -262,7 +264,7 @@ export async function promptContentForMessage(message, {
|
|
|
262
264
|
throw new ImagePromptError(
|
|
263
265
|
'unsupported-image-type',
|
|
264
266
|
`Image ${index + 1} is not JPEG, PNG, GIF, or WebP`,
|
|
265
|
-
'暂不支持该图片格式,请发送 JPEG、PNG、WebP 或 GIF 图片。',
|
|
267
|
+
t('暂不支持该图片格式,请发送 JPEG、PNG、WebP 或 GIF 图片。'),
|
|
266
268
|
);
|
|
267
269
|
}
|
|
268
270
|
content.push({
|
|
@@ -289,7 +291,7 @@ export function imagePromptDiagnostic(error) {
|
|
|
289
291
|
}
|
|
290
292
|
const reason = error.details.reason;
|
|
291
293
|
const userMessage = Object.hasOwn(HOST_ATTACHMENT_USER_MESSAGES, reason)
|
|
292
|
-
? HOST_ATTACHMENT_USER_MESSAGES[reason]
|
|
294
|
+
? t(HOST_ATTACHMENT_USER_MESSAGES[reason])
|
|
293
295
|
: null;
|
|
294
296
|
return userMessage ? { code: 'attachment-error', reason, userMessage } : null;
|
|
295
297
|
}
|
|
@@ -3,10 +3,12 @@ import { mkdir, mkdtemp, rm, writeFile } from 'node:fs/promises';
|
|
|
3
3
|
import { isAbsolute, join, relative, resolve } from 'node:path';
|
|
4
4
|
import { pipeline } from 'node:stream/promises';
|
|
5
5
|
|
|
6
|
+
import { t } from './i18n.mjs';
|
|
7
|
+
|
|
6
8
|
const FILES_DIRECTORY = join('.dsh-im', 'inbound');
|
|
7
9
|
|
|
8
10
|
export class InboundFileError extends Error {
|
|
9
|
-
constructor(code, message, userMessage = '文件接收失败,请重新发送后再试。', options = {}) {
|
|
11
|
+
constructor(code, message, userMessage = t('文件接收失败,请重新发送后再试。'), options = {}) {
|
|
10
12
|
super(message, options);
|
|
11
13
|
this.name = 'InboundFileError';
|
|
12
14
|
this.code = code;
|
|
@@ -124,7 +126,7 @@ export async function stageInboundFiles(message, {
|
|
|
124
126
|
throw new InboundFileError(
|
|
125
127
|
'inbound-file-download-failed',
|
|
126
128
|
`Unable to download inbound file ${index + 1}: ${error?.message ?? String(error)}`,
|
|
127
|
-
'文件下载失败,请重新发送后再试。',
|
|
129
|
+
t('文件下载失败,请重新发送后再试。'),
|
|
128
130
|
{ cause: error },
|
|
129
131
|
);
|
|
130
132
|
}
|
|
@@ -152,7 +154,7 @@ export async function stageInboundFiles(message, {
|
|
|
152
154
|
throw new InboundFileError(
|
|
153
155
|
'inbound-file-download-failed',
|
|
154
156
|
`Unable to stream inbound file ${index + 1}: ${error?.message ?? String(error)}`,
|
|
155
|
-
'文件下载失败,请重新发送后再试。',
|
|
157
|
+
t('文件下载失败,请重新发送后再试。'),
|
|
156
158
|
{ cause: error },
|
|
157
159
|
);
|
|
158
160
|
}
|