@xmanrui/dsh-im 0.18.0 → 1.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 +12 -1
- package/README.md +12 -1
- package/lib/client.js +341 -37
- package/lib/index.js +162 -156
- package/package.json +1 -1
- package/plugin-src/client/channels/feishu/api.js +23 -5
- package/plugin-src/client/channels/feishu/index.js +291 -39
- package/plugin-src/client/channels/feishu/styles.js +46 -0
- package/plugin-src/client/i18n.js +50 -0
- package/plugin-src/host/channels/dingtalk/production.mjs +5 -2
- package/plugin-src/host/channels/feishu/production.mjs +7 -2
- package/plugin-src/host/channels/feishu/rpc.mjs +61 -7
- package/plugin-src/host/channels/qq/production.mjs +5 -2
- package/plugin-src/host/channels/shared/production.mjs +5 -2
- package/plugin-src/host/channels/slack/production.mjs +5 -2
- package/plugin-src/host/channels/wecom/production.mjs +5 -2
- package/plugin-src/host/channels/weixin/production.mjs +5 -2
- package/plugin-src/host/channels/whatsapp/production.mjs +5 -2
- package/src/channels/dingtalk/dingtalk-bridge.mjs +11 -1
- package/src/channels/discord/discord-api.mjs +1 -1
- package/src/channels/feishu/bridge.mjs +40 -5
- package/src/channels/feishu/feishu-cards.mjs +4 -0
- package/src/channels/feishu/feishu-runtime.mjs +14 -0
- package/src/channels/feishu/group-message-permission-manager.mjs +71 -0
- package/src/channels/feishu/group-response-mode.mjs +15 -0
- package/src/channels/feishu/multi-bot-controller.mjs +258 -9
- package/src/channels/feishu/plugin-config-store.mjs +3 -0
- package/src/channels/feishu/repair-manager.mjs +17 -3
- package/src/channels/qq/qq-bridge.mjs +11 -1
- package/src/channels/shared/bot-workspace-store.mjs +75 -4
- package/src/channels/shared/preset-command.mjs +305 -0
- package/src/channels/shared/text-harness-bridge.mjs +11 -1
- package/src/channels/telegram/telegram-api.mjs +31 -0
- package/src/channels/telegram/telegram-runtime.mjs +27 -1
- package/src/channels/wecom/wecom-bridge.mjs +11 -1
- package/src/channels/weixin/weixin-api.mjs +1 -1
- package/src/channels/weixin/weixin-bridge.mjs +11 -1
|
@@ -407,6 +407,50 @@ const CSS = String.raw`
|
|
|
407
407
|
.bxf-metric dt { color: var(--dsw-alias-label-tertiary, #8f959e); font-size: 11px; line-height: 17px; }
|
|
408
408
|
.bxf-metric dd { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--dsw-alias-label-primary, #1f2329); font-size: 12px; line-height: 18px; font-weight: 550; margin: 3px 0 0; }
|
|
409
409
|
|
|
410
|
+
.bxf-responseMode { min-width: 0; display: grid; grid-template-columns: minmax(0, 1fr) max-content; align-items: center; column-gap: 10px; row-gap: 5px; margin-top: 6px; padding: 8px 10px; border: 1px solid var(--dsw-alias-border-l1, #eef0f3); border-radius: 9px; background: var(--dsw-alias-bg-module-platform, #f7f8fa); }
|
|
411
|
+
.bxf-responseModeHeader { display: contents; color: var(--dsw-alias-label-secondary, #646a73); font-size: 12px; line-height: normal; }
|
|
412
|
+
.bxf-responseModeHeader > span:first-child { grid-column: 1; grid-row: 1; white-space: nowrap; }
|
|
413
|
+
.bxf-responseModeStatus { grid-column: 2; grid-row: 1; color: var(--dsw-alias-label-tertiary, #8f959e); font-size: 12px; white-space: nowrap; }
|
|
414
|
+
.bxf-responseModeSelect { min-width: 0; width: 100%; grid-column: 1 / -1; grid-row: 2; height: 32px; padding: 0 30px 0 9px; border: 1px solid var(--dsw-alias-border-l2, #dfe1e5); border-radius: 7px; color: var(--dsw-alias-label-primary, #1f2329); background-color: var(--dsw-alias-bg-layer-1, #fff); font: inherit; font-size: 12px; cursor: pointer; transition: border-color .16s ease, box-shadow .16s ease, background-color .16s ease; }
|
|
415
|
+
.bxf-responseModeSelect:hover:not(:disabled) { border-color: color-mix(in srgb, var(--bxf-accent) 45%, var(--dsw-alias-border-l2, #dfe1e5)); }
|
|
416
|
+
.bxf-responseModeSelect:focus-visible { outline: none; border-color: var(--bxf-accent); box-shadow: 0 0 0 2px color-mix(in srgb, var(--bxf-accent) 16%, transparent); }
|
|
417
|
+
.bxf-responseModeSelect:disabled { cursor: not-allowed; opacity: .55; }
|
|
418
|
+
.bxf-responseModeHelp { grid-column: 1 / -1; grid-row: 3; color: var(--dsw-alias-label-tertiary, #8f959e); font-size: 11px; line-height: 1.45; }
|
|
419
|
+
.bxf-responseModePermissionAction { grid-column: 1 / -1; grid-row: 4; display: flex; justify-content: flex-start; margin-top: 2px; }
|
|
420
|
+
.bxf-responseModePermissionButton { min-height: 28px; padding: 3px 9px; color: var(--bxf-accent); border-color: color-mix(in srgb, var(--bxf-accent) 30%, var(--dsw-alias-border-l2, #dfe1e5)); background: var(--dsw-alias-bg-layer-1, #fff); }
|
|
421
|
+
.bxf-responseModePermissionButton:hover:not(:disabled) { background: color-mix(in srgb, var(--bxf-accent) 7%, transparent); }
|
|
422
|
+
.bxf-responseModeError { grid-column: 1 / -1; grid-row: 5; color: var(--bxf-error); font-size: 12px; line-height: 1.4; margin: 0; }
|
|
423
|
+
|
|
424
|
+
.bxf-botProvision {
|
|
425
|
+
position: relative;
|
|
426
|
+
margin-top: 14px;
|
|
427
|
+
scroll-margin-block: 20px;
|
|
428
|
+
animation: bxf-revealProvision .2s var(--ds-ease-out, ease-out) both;
|
|
429
|
+
}
|
|
430
|
+
.bxf-botProvision:focus { outline: none; }
|
|
431
|
+
.bxf-botProvision:focus-visible {
|
|
432
|
+
outline: 2px solid color-mix(in srgb, var(--bxf-accent) 75%, transparent);
|
|
433
|
+
outline-offset: 3px;
|
|
434
|
+
border-radius: 12px;
|
|
435
|
+
}
|
|
436
|
+
.bxf-botProvision > .bxf-provisionCard {
|
|
437
|
+
border-radius: 11px;
|
|
438
|
+
box-shadow: none;
|
|
439
|
+
background: color-mix(in srgb, var(--bxf-accent) 2.5%, var(--dsw-alias-bg-layer-3, #fff));
|
|
440
|
+
}
|
|
441
|
+
.bxf-botProvision .bxf-cardBody { padding: 18px; }
|
|
442
|
+
.bxf-botProvision .bxf-qrLayout {
|
|
443
|
+
grid-template-columns: 184px minmax(0, 1fr);
|
|
444
|
+
align-items: start;
|
|
445
|
+
gap: 24px;
|
|
446
|
+
}
|
|
447
|
+
.bxf-botProvision .bxf-qrFrame { width: 176px; height: 176px; padding: 10px; border-radius: 11px; }
|
|
448
|
+
.bxf-botProvision .bxf-countdown { width: 176px; }
|
|
449
|
+
.bxf-botProvision .bxf-qrCopy h3 { font-size: 18px; line-height: 26px; }
|
|
450
|
+
.bxf-botProvision .bxf-steps { gap: 8px; margin-top: 14px; }
|
|
451
|
+
.bxf-botProvision .bxf-actions { margin-top: 16px; }
|
|
452
|
+
.bxf-botProvision .bxf-inlineError { min-height: 160px; padding: 22px; }
|
|
453
|
+
|
|
410
454
|
.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); }
|
|
411
455
|
.bxf-healthSummary { min-width: 0; color: var(--dsw-alias-label-secondary, #646a73); font-size: 12px; line-height: 18px; }
|
|
412
456
|
.bxf-healthSummary[data-error="true"] { color: var(--bxf-error); }
|
|
@@ -459,6 +503,7 @@ const CSS = String.raw`
|
|
|
459
503
|
@keyframes bxf-rotate { to { transform: rotate(360deg); } }
|
|
460
504
|
@keyframes bxf-pulse { 0%, 100% { transform: scale(.9); opacity: .45; } 50% { transform: scale(1.08); opacity: 1; } }
|
|
461
505
|
@keyframes bxf-shimmer { to { background-position: -220% 0; } }
|
|
506
|
+
@keyframes bxf-revealProvision { from { opacity: 0; transform: translateY(-5px); } }
|
|
462
507
|
|
|
463
508
|
@container (max-width: 620px) {
|
|
464
509
|
.bxf-headingTools { gap: 6px; }
|
|
@@ -470,6 +515,7 @@ const CSS = String.raw`
|
|
|
470
515
|
.bxf-intro { grid-template-columns: minmax(0, 1fr); }
|
|
471
516
|
.bxf-markStage { display: none; }
|
|
472
517
|
.bxf-qrLayout { grid-template-columns: minmax(0, 1fr); justify-items: center; }
|
|
518
|
+
.bxf-botProvision .bxf-qrLayout { grid-template-columns: minmax(0, 1fr); }
|
|
473
519
|
.bxf-qrCopy { width: 100%; }
|
|
474
520
|
.bxf-statusGrid { grid-template-columns: minmax(0, 1fr); }
|
|
475
521
|
.bxf-connectedTop { align-items: flex-start; flex-direction: column; }
|
|
@@ -68,6 +68,24 @@ const EN = Object.freeze({
|
|
|
68
68
|
'WhatsApp账号': 'WhatsApp account',
|
|
69
69
|
'微信设置': 'WeChat settings',
|
|
70
70
|
'飞书机器人设置': 'Feishu bot settings',
|
|
71
|
+
'群聊响应方式': 'Group response mode',
|
|
72
|
+
'仅在 @机器人时响应(推荐)': 'Only respond when @mentioned (recommended)',
|
|
73
|
+
'响应所有群消息': 'Respond to all group messages',
|
|
74
|
+
'响应所有群消息(需飞书敏感权限)': 'Respond to all group messages (requires a sensitive Feishu scope)',
|
|
75
|
+
'重新授权': 'Reauthorize',
|
|
76
|
+
'去授权': 'Authorize',
|
|
77
|
+
'重新授权群消息权限': 'Reauthorize group-message permission',
|
|
78
|
+
'授权群消息权限': 'Authorize group-message permission',
|
|
79
|
+
'正在准备授权…': 'Preparing authorization…',
|
|
80
|
+
'正在准备…': 'Preparing…',
|
|
81
|
+
'私聊始终响应;群聊仅处理明确 @当前机器人的消息。群消息权限已开通,再次切换无需授权。': 'Direct messages always work; group chats require an explicit @mention of this bot. The group-message permission is already granted, so switching again needs no authorization.',
|
|
82
|
+
'私聊始终响应;群聊仅处理明确 @当前机器人的消息。选择全部消息后会打开飞书官方授权流程。': 'Direct messages always work; group chats require an explicit @mention of this bot. Selecting all messages opens the official Feishu authorization flow.',
|
|
83
|
+
'已开通“获取群组中所有消息”权限(im:message.group_msg);机器人会处理群聊中的所有可见消息。': 'The “Read all messages in associated group chat” scope (im:message.group_msg) is granted; the bot processes every visible group message.',
|
|
84
|
+
'尚未确认“获取群组中所有消息”权限,请完成飞书授权。': 'The “Read all messages in associated group chat” scope has not been confirmed. Complete Feishu authorization.',
|
|
85
|
+
'私聊始终响应;群聊仅处理明确 @当前机器人的消息。': 'Direct messages always work; group chats require an explicit @mention of this bot.',
|
|
86
|
+
'需在飞书为该机器人开通“获取群组中所有消息”权限(im:message.group_msg);开通后,机器人会处理群聊中的所有可见消息。': 'Grant this bot the “Read all messages in associated group chat” Feishu scope (im:message.group_msg); once granted, it will process every visible group message.',
|
|
87
|
+
'群聊响应方式修改失败,请重试。': 'Could not update the group response mode. Try again.',
|
|
88
|
+
'群消息权限授权失败,请重试。': 'Could not authorize group-message permission. Try again.',
|
|
71
89
|
'钉钉设置': 'DingTalk settings',
|
|
72
90
|
'企业微信设置': 'WeCom settings',
|
|
73
91
|
'扫码接入机器人': 'Scan QR code',
|
|
@@ -190,6 +208,7 @@ const EN = Object.freeze({
|
|
|
190
208
|
'生成 QQ 二维码': 'Generate QQ QR code',
|
|
191
209
|
'正在生成二维码…': 'Generating QR code…',
|
|
192
210
|
'正在准备授权二维码': 'Preparing authorization QR code',
|
|
211
|
+
'正在准备权限授权二维码': 'Preparing permission authorization QR code',
|
|
193
212
|
'正在准备微信二维码': 'Preparing WeChat QR code',
|
|
194
213
|
'正在添加新机器人': 'Adding a new bot',
|
|
195
214
|
'正在申请钉钉授权二维码…': 'Requesting DingTalk authorization QR code…',
|
|
@@ -207,6 +226,16 @@ const EN = Object.freeze({
|
|
|
207
226
|
'在飞书中打开': 'Open in Feishu',
|
|
208
227
|
'取消添加': 'Cancel',
|
|
209
228
|
'使用飞书扫码修复卡片按钮': 'Scan with Feishu to repair card buttons',
|
|
229
|
+
'使用飞书确认群消息权限': 'Confirm group-message permission with Feishu',
|
|
230
|
+
'扫码会更新现有飞书应用,只增量开通“获取群组中所有消息”权限;不会创建新应用。确认后会自动启用“响应所有群消息”,其他机器人不受影响。': 'Scanning updates the existing Feishu app with only the “Read all messages in associated group chat” scope. It does not create a new app. After confirmation, “Respond to all group messages” is enabled automatically; other bots are unaffected.',
|
|
231
|
+
'核对现有应用,并确认“获取群组中所有消息”权限': 'Review the existing app and confirm the “Read all messages in associated group chat” permission',
|
|
232
|
+
'保持本页打开,等待权限生效并自动切换响应方式': 'Keep this page open while the permission takes effect and the response mode switches automatically',
|
|
233
|
+
'取消授权': 'Cancel authorization',
|
|
234
|
+
'已确认,正在启用全部消息模式': 'Confirmed. Enabling all-message mode',
|
|
235
|
+
'权限配置已提交,正在保存设置并重连此机器人;此阶段无法取消,其他机器人不会中断。': 'The permission update was submitted. Saving the setting and reconnecting this bot. This stage cannot be cancelled; other bots will not be interrupted.',
|
|
236
|
+
'权限配置已提交,正在启用全部消息模式并重连此机器人;此阶段无法取消,其他机器人不会中断。': 'The permission update was submitted. Enabling all-message mode and reconnecting this bot. This stage cannot be cancelled; other bots will not be interrupted.',
|
|
237
|
+
'正在为现有飞书应用申请群消息权限二维码,请稍候。': 'Requesting a group-message permission QR code for the existing Feishu app…',
|
|
238
|
+
'群消息权限没有开通完成': 'Group-message permission was not granted',
|
|
210
239
|
'扫码会更新现有飞书应用,只增量补充卡片按钮回调;不会创建新应用。确认后此机器人会短暂重连,其他机器人不受影响。': 'Scanning updates the existing Feishu app with only the card-button callback. It does not create a new app. This bot reconnects briefly after confirmation; other bots are not affected.',
|
|
211
240
|
'核对现有应用名称,并确认只新增卡片回调': 'Review the existing app name and confirm that only the card callback is added',
|
|
212
241
|
'保持本页打开,等待卡片按钮修复完成': 'Keep this page open until card-button repair finishes',
|
|
@@ -219,14 +248,25 @@ const EN = Object.freeze({
|
|
|
219
248
|
'修复卡片按钮': 'Repair card buttons',
|
|
220
249
|
'等待扫码…': 'Waiting for scan…',
|
|
221
250
|
'飞书服务返回了不匹配的卡片修复二维码': 'Feishu returned a repair QR code for a different bot',
|
|
251
|
+
'飞书服务返回了不匹配的群消息权限二维码': 'Feishu returned a group-message permission QR code for a different bot',
|
|
222
252
|
'飞书服务返回的修复信息缺少 botId': 'Feishu repair status is missing the bot ID',
|
|
253
|
+
'飞书服务返回的应用更新信息缺少 botId': 'Feishu app-update status is missing the bot ID',
|
|
223
254
|
'飞书服务返回了不匹配的注册进度': 'Feishu returned registration progress for a different operation',
|
|
224
255
|
'此机器人': 'this bot',
|
|
256
|
+
'用于为${botName}开通群消息权限的一次性授权二维码': 'One-time QR code for granting group-message permission to ${botName}',
|
|
257
|
+
'正在为「${botName}」开通群消息权限': 'Granting group-message permission to “${botName}”',
|
|
225
258
|
'${botName ?? "机器人"}的修复二维码已生成,请使用飞书扫码。': 'Repair QR code generated for ${botName ?? "bot"}. Scan it with Feishu.',
|
|
259
|
+
'${botName ?? "机器人"}的群消息权限二维码已生成,请使用飞书确认。': 'Group-message permission QR code generated for ${botName ?? "bot"}. Confirm it with Feishu.',
|
|
260
|
+
'${targetBotName}已开通群消息权限,并启用“响应所有群消息”。': '${targetBotName} now has group-message permission and “Respond to all group messages” is enabled.',
|
|
261
|
+
'${targetBot.bot.name}已开通群消息权限,并启用“响应所有群消息”。': '${targetBot.bot.name} now has group-message permission and “Respond to all group messages” is enabled.',
|
|
226
262
|
'${targetBot.bot.name}已连接,可以在飞书中开始聊天。': '${targetBot.bot.name} is connected and ready to chat in Feishu.',
|
|
227
263
|
'已取消卡片按钮修复。': 'Card-button repair was cancelled.',
|
|
264
|
+
'已取消群消息权限授权。': 'Group-message permission authorization was cancelled.',
|
|
228
265
|
'卡片按钮已更新,但暂时无法确认机器人连接状态': 'The card callback was updated, but the bot connection could not be confirmed yet',
|
|
266
|
+
'群消息权限已更新,但暂时无法确认机器人连接状态': 'The group-message permission was updated, but the bot connection could not be confirmed yet',
|
|
229
267
|
'飞书卡片按钮修复失败': 'Could not repair the Feishu card buttons',
|
|
268
|
+
'飞书群消息权限开通失败': 'Could not grant the Feishu group-message permission',
|
|
269
|
+
'请先完成当前飞书授权操作,再开通群消息权限。': 'Finish the current Feishu authorization before granting group-message permission.',
|
|
230
270
|
'已确认,正在连接新机器人': 'Confirmed. Connecting the new bot',
|
|
231
271
|
'正在安全保存凭据并检查新机器人的消息通道,其他机器人不会中断。': 'Saving credentials and checking the new bot connection. Existing bots will not be interrupted.',
|
|
232
272
|
'正在向飞书申请一次性授权二维码,请稍候。': 'Requesting a one-time authorization QR code from Feishu…',
|
|
@@ -525,16 +565,26 @@ function translateDynamic(text) {
|
|
|
525
565
|
if (match) return `Remove “${match[1]}” from DeepSeek Harness?`;
|
|
526
566
|
match = /^从 DeepSeek Harness 移除(.+)$/.exec(text);
|
|
527
567
|
if (match) return `Remove ${match[1]} from DeepSeek Harness`;
|
|
568
|
+
match = /^(.+)的飞书授权流程$/.exec(text);
|
|
569
|
+
if (match) return `Feishu authorization flow for ${match[1]}`;
|
|
528
570
|
match = /^用于修复(.+)卡片按钮的一次性授权二维码$/.exec(text);
|
|
529
571
|
if (match) return `One-time QR code for repairing card buttons for ${match[1]}`;
|
|
572
|
+
match = /^用于为(.+)开通群消息权限的一次性授权二维码$/.exec(text);
|
|
573
|
+
if (match) return `One-time QR code for granting group-message permission to ${match[1]}`;
|
|
530
574
|
match = /^正在修复「(.+)」$/.exec(text);
|
|
531
575
|
if (match) return `Repairing “${match[1]}”`;
|
|
576
|
+
match = /^正在为「(.+)」开通群消息权限$/.exec(text);
|
|
577
|
+
if (match) return `Granting group-message permission to “${match[1]}”`;
|
|
532
578
|
match = /^修复(.+)的卡片按钮$/.exec(text);
|
|
533
579
|
if (match) return `Repair card buttons for ${match[1]}`;
|
|
534
580
|
match = /^(.+)的修复二维码已生成,请使用飞书扫码。$/.exec(text);
|
|
535
581
|
if (match) return `Repair QR code generated for ${match[1]}. Scan it with Feishu.`;
|
|
582
|
+
match = /^(.+)的群消息权限二维码已生成,请使用飞书确认。$/.exec(text);
|
|
583
|
+
if (match) return `Group-message permission QR code generated for ${match[1]}. Confirm it with Feishu.`;
|
|
536
584
|
match = /^(.+)的卡片按钮已修复。$/.exec(text);
|
|
537
585
|
if (match) return `Card buttons repaired for ${match[1]}.`;
|
|
586
|
+
match = /^(.+)已开通群消息权限,并启用“响应所有群消息”。$/.exec(text);
|
|
587
|
+
if (match) return `${match[1]} now has group-message permission and “Respond to all group messages” is enabled.`;
|
|
538
588
|
match = /^(检查连接|重试连接)(.+)$/.exec(text);
|
|
539
589
|
if (match) return `${localizeText(match[1])} ${match[2]}`;
|
|
540
590
|
match = /^移除(.+)$/.exec(text);
|
|
@@ -53,6 +53,7 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|
|
53
53
|
const logger = typeof ctx.logger === 'function'
|
|
54
54
|
? ctx.logger('dsh-dingtalk')
|
|
55
55
|
: (ctx.logger ?? console);
|
|
56
|
+
const agentPresetCatalog = () => listAgentPresetCatalog(ctx);
|
|
56
57
|
const paths = pluginPaths(config);
|
|
57
58
|
const configStore = await new ConfigStore(paths.config).load();
|
|
58
59
|
const defaultWorkspace = resolve(config.workspace ?? process.cwd());
|
|
@@ -106,7 +107,9 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|
|
106
107
|
createRuntime: async ({ botId, config: botConfig, clientSecret }) => {
|
|
107
108
|
const state = await stateFor(botId);
|
|
108
109
|
await workspaces.ensure(botId, { defaultAgentPreset: config.agentPreset });
|
|
109
|
-
const workspaceScope = createBotWorkspaceScope(harness, {
|
|
110
|
+
const workspaceScope = createBotWorkspaceScope(harness, {
|
|
111
|
+
botId, workspaces, state, agentPresetCatalog,
|
|
112
|
+
});
|
|
110
113
|
return new Runtime({
|
|
111
114
|
config: botConfig,
|
|
112
115
|
clientSecret,
|
|
@@ -140,7 +143,7 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|
|
140
143
|
const controller = createWorkspaceAwareController(coreController, {
|
|
141
144
|
workspaces,
|
|
142
145
|
stateFor,
|
|
143
|
-
agentPresetCatalog
|
|
146
|
+
agentPresetCatalog,
|
|
144
147
|
});
|
|
145
148
|
const supervisor = createSupervisor({
|
|
146
149
|
controller,
|
|
@@ -66,6 +66,7 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|
|
66
66
|
const logger = typeof ctx.logger === 'function'
|
|
67
67
|
? ctx.logger('dsh-feishu')
|
|
68
68
|
: (ctx.logger ?? console);
|
|
69
|
+
const agentPresetCatalog = () => listAgentPresetCatalog(ctx);
|
|
69
70
|
const paths = pluginPaths(config);
|
|
70
71
|
const configStore = await new ConfigStore(paths.config).load();
|
|
71
72
|
const defaultWorkspace = resolve(config.workspace ?? process.cwd());
|
|
@@ -136,7 +137,9 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|
|
136
137
|
const state = await stateFor(botConfig);
|
|
137
138
|
const id = botId ?? botConfig.id ?? botConfig.appId;
|
|
138
139
|
await workspaces.ensure(id, { defaultAgentPreset: config.agentPreset });
|
|
139
|
-
const workspaceScope = createBotWorkspaceScope(harness, {
|
|
140
|
+
const workspaceScope = createBotWorkspaceScope(harness, {
|
|
141
|
+
botId: id, workspaces, state, agentPresetCatalog,
|
|
142
|
+
});
|
|
140
143
|
return new Runtime({
|
|
141
144
|
lark,
|
|
142
145
|
botId: id,
|
|
@@ -144,6 +147,8 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|
|
144
147
|
appId: botConfig.appId,
|
|
145
148
|
appSecret,
|
|
146
149
|
domain: botConfig.domain,
|
|
150
|
+
botOpenId: botConfig.botOpenId,
|
|
151
|
+
groupResponseMode: botConfig.groupResponseMode,
|
|
147
152
|
ownerOpenIds: botConfig.ownerOpenIds ?? [botConfig.ownerOpenId],
|
|
148
153
|
harness: workspaceScope.harness,
|
|
149
154
|
state: workspaceScope.state,
|
|
@@ -168,7 +173,7 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|
|
168
173
|
const controller = createWorkspaceAwareController(coreController, {
|
|
169
174
|
workspaces,
|
|
170
175
|
stateFor: stateForBotId,
|
|
171
|
-
agentPresetCatalog
|
|
176
|
+
agentPresetCatalog,
|
|
172
177
|
});
|
|
173
178
|
|
|
174
179
|
const supervisor = createSupervisor({
|
|
@@ -7,6 +7,10 @@ import { publicConnectionTestResult } from '../../../../src/channels/shared/conn
|
|
|
7
7
|
import { resolveRpcAuthority } from '../../rpc-authority.mjs';
|
|
8
8
|
import { publicWorkspaceError, validWorkspacePayload } from '../shared/workspace-rpc.mjs';
|
|
9
9
|
import { validAgentPresetPayload } from '../shared/agent-preset-rpc.mjs';
|
|
10
|
+
import {
|
|
11
|
+
isFeishuGroupResponseMode,
|
|
12
|
+
normalizeFeishuGroupResponseMode,
|
|
13
|
+
} from '../../../../src/channels/feishu/group-response-mode.mjs';
|
|
10
14
|
import {
|
|
11
15
|
FEISHU_ENDPOINTS,
|
|
12
16
|
FEISHU_RPC_CHANNEL,
|
|
@@ -26,8 +30,17 @@ const REGISTRATION_STATES = new Set([
|
|
|
26
30
|
'idle', 'starting', 'qr_ready', 'polling', 'slow_down',
|
|
27
31
|
'domain_switched', 'saving', 'succeeded', 'expired', 'cancelled', 'error',
|
|
28
32
|
]);
|
|
29
|
-
const REGISTRATION_OPERATIONS = new Set([
|
|
33
|
+
const REGISTRATION_OPERATIONS = new Set([
|
|
34
|
+
'provision',
|
|
35
|
+
'callback_repair',
|
|
36
|
+
'group_message_permission',
|
|
37
|
+
]);
|
|
30
38
|
const CALLBACK_REPAIR_OPERATION = 'callback_repair';
|
|
39
|
+
const GROUP_MESSAGE_PERMISSION_OPERATION = 'group_message_permission';
|
|
40
|
+
const TARGETED_APP_UPDATE_OPERATIONS = new Set([
|
|
41
|
+
CALLBACK_REPAIR_OPERATION,
|
|
42
|
+
GROUP_MESSAGE_PERMISSION_OPERATION,
|
|
43
|
+
]);
|
|
31
44
|
const OFFICIAL_REGISTRATION_HOSTS = new Set([
|
|
32
45
|
'accounts.feishu.cn',
|
|
33
46
|
'accounts.larksuite.com',
|
|
@@ -59,6 +72,9 @@ const PUBLIC_ERROR_MESSAGES = Object.freeze({
|
|
|
59
72
|
credential_update_failed: 'Unable to store the repaired Feishu credentials.',
|
|
60
73
|
credential_state_unknown: 'The repaired Feishu credentials could not be confirmed after saving.',
|
|
61
74
|
repair_connection_failed: 'The callback update was accepted, but the selected bot could not reconnect.',
|
|
75
|
+
group_message_permission_required: 'Authorize access to all group messages before enabling this mode.',
|
|
76
|
+
group_message_permission_save_failed: 'Feishu granted the permission, but all-message mode could not be saved.',
|
|
77
|
+
group_message_permission_connection_failed: 'Feishu granted the permission, but the selected bot could not reconnect.',
|
|
62
78
|
card_action_probe_unavailable: 'The selected bot is not connected, so its card button cannot be verified.',
|
|
63
79
|
card_action_probe_send_failed: 'The callback update was accepted, but the verification card could not be sent.',
|
|
64
80
|
card_action_probe_timeout: 'Feishu accepted the update, but the card button was not verified in time. Start the repair again and click the test button within two minutes.',
|
|
@@ -138,7 +154,7 @@ function safeRegistrationUrl(value, operation, expectedHost) {
|
|
|
138
154
|
|| url.port
|
|
139
155
|
|| url.username
|
|
140
156
|
|| url.password) return undefined;
|
|
141
|
-
if (operation
|
|
157
|
+
if (TARGETED_APP_UPDATE_OPERATIONS.has(operation)) {
|
|
142
158
|
const clientIds = url.searchParams.getAll('clientID');
|
|
143
159
|
const transportKinds = url.searchParams.getAll('tp');
|
|
144
160
|
const addons = url.searchParams.getAll('addons');
|
|
@@ -240,7 +256,7 @@ async function publicProvisioning(registration, encodeQr, expectedHost) {
|
|
|
240
256
|
// remote update is committed. Keep only the opaque attempt identity so a
|
|
241
257
|
// browser reload can resume polling the non-cancellable verification phase.
|
|
242
258
|
if (registration.state === 'saving'
|
|
243
|
-
&& registration.operation
|
|
259
|
+
&& TARGETED_APP_UPDATE_OPERATIONS.has(registration.operation)
|
|
244
260
|
&& registration.botId) {
|
|
245
261
|
return { ...projection, submitted: true };
|
|
246
262
|
}
|
|
@@ -259,6 +275,8 @@ function publicBotEntry(entry) {
|
|
|
259
275
|
connected,
|
|
260
276
|
configured: source.configured === true,
|
|
261
277
|
agentPreset: normalizeAgentPresetId(source.agentPreset),
|
|
278
|
+
groupResponseMode: normalizeFeishuGroupResponseMode(source.groupResponseMode),
|
|
279
|
+
groupMessagePermissionGranted: source.groupMessagePermissionGranted === true,
|
|
262
280
|
bot: publicBot(source.bot),
|
|
263
281
|
health: publicHealth(source, connected),
|
|
264
282
|
};
|
|
@@ -274,12 +292,12 @@ export async function toPublicFeishuStatus(status, { encodeQr = qrCodeDataUrl }
|
|
|
274
292
|
const registration = publicRegistration(source.registration);
|
|
275
293
|
const facts = connectionFacts(source.connection);
|
|
276
294
|
const connected = source.connected === true || facts.connected;
|
|
277
|
-
const
|
|
295
|
+
const appUpdateTarget = TARGETED_APP_UPDATE_OPERATIONS.has(registration.operation)
|
|
278
296
|
&& registration.botId
|
|
279
297
|
&& Array.isArray(source.bots)
|
|
280
298
|
? source.bots.find((entry) => entry?.botId === registration.botId)
|
|
281
299
|
: undefined;
|
|
282
|
-
const expectedRegistrationHost =
|
|
300
|
+
const expectedRegistrationHost = appUpdateTarget?.bot?.domain === 'lark'
|
|
283
301
|
? 'open.larksuite.com'
|
|
284
302
|
: 'open.feishu.cn';
|
|
285
303
|
const provisioning = await publicProvisioning(
|
|
@@ -345,6 +363,11 @@ function validPayload(endpoint, payload) {
|
|
|
345
363
|
? null
|
|
346
364
|
: 'Callback repair requires a single valid botId.';
|
|
347
365
|
}
|
|
366
|
+
if (endpoint === FEISHU_ENDPOINTS.beginGroupMessagePermission) {
|
|
367
|
+
return hasOnlyKeys(payload, new Set(['botId'])) && safeOpaqueId(payload.botId)
|
|
368
|
+
? null
|
|
369
|
+
: 'Group message permission update requires a single valid botId.';
|
|
370
|
+
}
|
|
348
371
|
if (endpoint === FEISHU_ENDPOINTS.bindCredentials) {
|
|
349
372
|
return hasOnlyKeys(payload, new Set(['appId', 'appSecret']))
|
|
350
373
|
&& validCredential(payload.appId, 256)
|
|
@@ -389,6 +412,13 @@ function validPayload(endpoint, payload) {
|
|
|
389
412
|
return validAgentPresetPayload(payload)
|
|
390
413
|
? null : '请选择 Agent Preset。';
|
|
391
414
|
}
|
|
415
|
+
if (endpoint === FEISHU_ENDPOINTS.setGroupResponseMode) {
|
|
416
|
+
return hasOnlyKeys(payload, new Set(['botId', 'groupResponseMode']))
|
|
417
|
+
&& safeOpaqueId(payload.botId)
|
|
418
|
+
&& isFeishuGroupResponseMode(payload.groupResponseMode)
|
|
419
|
+
? null
|
|
420
|
+
: '请选择群聊响应方式。';
|
|
421
|
+
}
|
|
392
422
|
return 'Unknown Feishu endpoint.';
|
|
393
423
|
}
|
|
394
424
|
|
|
@@ -508,6 +538,20 @@ export function createFeishuRpcHandler(controller, { encodeQr = qrCodeDataUrl }
|
|
|
508
538
|
throw new Error('Callback repair did not produce a safe QR code.');
|
|
509
539
|
}
|
|
510
540
|
attemptQr.set(attemptId, value.verificationUrl);
|
|
541
|
+
} else if (endpoint === FEISHU_ENDPOINTS.beginGroupMessagePermission) {
|
|
542
|
+
if (typeof controller.startGroupMessagePermission !== 'function') {
|
|
543
|
+
throw new Error('Feishu group message permission update is unavailable.');
|
|
544
|
+
}
|
|
545
|
+
const started = await controller.startGroupMessagePermission(payload.botId);
|
|
546
|
+
const attemptId = String(publicRegistration(started?.registration).attempt);
|
|
547
|
+
const ready = await waitForQr(controller, started, attemptId, signal);
|
|
548
|
+
value = (await toPublicFeishuStatus(ready, { encodeQr: cachedEncodeQr })).provisioning;
|
|
549
|
+
if (!value
|
|
550
|
+
|| value.operation !== GROUP_MESSAGE_PERMISSION_OPERATION
|
|
551
|
+
|| value.botId !== payload.botId) {
|
|
552
|
+
throw new Error('Group message permission update did not produce a safe QR code.');
|
|
553
|
+
}
|
|
554
|
+
attemptQr.set(attemptId, value.verificationUrl);
|
|
511
555
|
} else if (endpoint === FEISHU_ENDPOINTS.pollProvisioning) {
|
|
512
556
|
const current = await statusForRegistration(controller, payload.attemptId);
|
|
513
557
|
if (!current || !sameAttempt(current, payload.attemptId)) {
|
|
@@ -542,14 +586,16 @@ export function createFeishuRpcHandler(controller, { encodeQr = qrCodeDataUrl }
|
|
|
542
586
|
after = await controller.cancelRegistration(payload.attemptId);
|
|
543
587
|
}
|
|
544
588
|
const afterRegistration = publicRegistration(after?.registration);
|
|
545
|
-
if (registration.operation
|
|
589
|
+
if (TARGETED_APP_UPDATE_OPERATIONS.has(registration.operation)
|
|
546
590
|
&& registration.state === 'saving'
|
|
547
591
|
&& ['saving', 'succeeded'].includes(afterRegistration.state)) {
|
|
548
592
|
value = {
|
|
549
593
|
status: pollStatus(after),
|
|
550
594
|
operation: afterRegistration.operation,
|
|
551
595
|
...(afterRegistration.botId ? { botId: afterRegistration.botId } : {}),
|
|
552
|
-
message:
|
|
596
|
+
message: registration.operation === GROUP_MESSAGE_PERMISSION_OPERATION
|
|
597
|
+
? 'The permission update was already submitted and is still being applied.'
|
|
598
|
+
: 'Callback repair was already submitted and is still being verified.',
|
|
553
599
|
};
|
|
554
600
|
}
|
|
555
601
|
if (value?.status !== 'connecting') {
|
|
@@ -623,6 +669,14 @@ export function createFeishuRpcHandler(controller, { encodeQr = qrCodeDataUrl }
|
|
|
623
669
|
await controller.updateAgentPreset(payload.botId, payload.agentPreset),
|
|
624
670
|
{ encodeQr: cachedEncodeQr },
|
|
625
671
|
);
|
|
672
|
+
} else if (endpoint === FEISHU_ENDPOINTS.setGroupResponseMode) {
|
|
673
|
+
if (typeof controller.updateGroupResponseMode !== 'function') {
|
|
674
|
+
throw new Error('Group response mode update is unavailable');
|
|
675
|
+
}
|
|
676
|
+
value = await toPublicFeishuStatus(
|
|
677
|
+
await controller.updateGroupResponseMode(payload.botId, payload.groupResponseMode),
|
|
678
|
+
{ encodeQr: cachedEncodeQr },
|
|
679
|
+
);
|
|
626
680
|
} else {
|
|
627
681
|
if (typeof controller.deleteBot !== 'function') throw new Error('Multi-bot delete is unavailable');
|
|
628
682
|
value = await toPublicFeishuStatus(await controller.deleteBot(payload.botId), { encodeQr: cachedEncodeQr });
|
|
@@ -50,6 +50,7 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|
|
50
50
|
const QrAuth = internals.QrAuth ?? QqQrAuth;
|
|
51
51
|
const createSupervisor = internals.createConnectionSupervisor ?? createConnectionSupervisor;
|
|
52
52
|
const logger = typeof ctx.logger === 'function' ? ctx.logger('dsh-im:qq') : (ctx.logger ?? console);
|
|
53
|
+
const agentPresetCatalog = () => listAgentPresetCatalog(ctx);
|
|
53
54
|
const paths = pluginPaths(config);
|
|
54
55
|
const configStore = await new ConfigStore(paths.config).load();
|
|
55
56
|
const defaultWorkspace = resolve(config.workspace ?? process.cwd());
|
|
@@ -97,7 +98,9 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|
|
97
98
|
createRuntime: async ({ botId, config: botConfig, appSecret }) => {
|
|
98
99
|
const state = await stateFor(botId);
|
|
99
100
|
await workspaces.ensure(botId, { defaultAgentPreset: config.agentPreset });
|
|
100
|
-
const workspaceScope = createBotWorkspaceScope(harness, {
|
|
101
|
+
const workspaceScope = createBotWorkspaceScope(harness, {
|
|
102
|
+
botId, workspaces, state, agentPresetCatalog,
|
|
103
|
+
});
|
|
101
104
|
return new Runtime({
|
|
102
105
|
config: botConfig,
|
|
103
106
|
appSecret,
|
|
@@ -130,7 +133,7 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|
|
130
133
|
const controller = createWorkspaceAwareController(coreController, {
|
|
131
134
|
workspaces,
|
|
132
135
|
stateFor,
|
|
133
|
-
agentPresetCatalog
|
|
136
|
+
agentPresetCatalog,
|
|
134
137
|
});
|
|
135
138
|
const supervisor = createSupervisor({
|
|
136
139
|
controller,
|
|
@@ -52,6 +52,7 @@ export async function createTokenProductionController(ctx, config, internals, de
|
|
|
52
52
|
const createSupervisor = internals.createConnectionSupervisor ?? createTokenConnectionSupervisor;
|
|
53
53
|
const logger = typeof ctx.logger === 'function'
|
|
54
54
|
? ctx.logger(`dsh-im:${channel}`) : (ctx.logger ?? console);
|
|
55
|
+
const agentPresetCatalog = () => listAgentPresetCatalog(ctx);
|
|
55
56
|
const paths = pluginPaths(config, channel);
|
|
56
57
|
const configStore = await new ResolvedConfigStore(paths.config).load();
|
|
57
58
|
const defaultWorkspace = resolve(config.workspace ?? process.cwd());
|
|
@@ -98,7 +99,9 @@ export async function createTokenProductionController(ctx, config, internals, de
|
|
|
98
99
|
createRuntime: async ({ botId, config: botConfig, token }) => {
|
|
99
100
|
const state = await stateFor(botId);
|
|
100
101
|
await workspaces.ensure(botId, { defaultAgentPreset: config.agentPreset });
|
|
101
|
-
const workspaceScope = createBotWorkspaceScope(harness, {
|
|
102
|
+
const workspaceScope = createBotWorkspaceScope(harness, {
|
|
103
|
+
botId, workspaces, state, agentPresetCatalog,
|
|
104
|
+
});
|
|
102
105
|
return new ResolvedRuntime({
|
|
103
106
|
...channelRuntimeOptions,
|
|
104
107
|
config: botConfig,
|
|
@@ -132,7 +135,7 @@ export async function createTokenProductionController(ctx, config, internals, de
|
|
|
132
135
|
const controller = createWorkspaceAwareController(coreController, {
|
|
133
136
|
workspaces,
|
|
134
137
|
stateFor,
|
|
135
|
-
agentPresetCatalog
|
|
138
|
+
agentPresetCatalog,
|
|
136
139
|
});
|
|
137
140
|
const supervisor = createSupervisor({
|
|
138
141
|
channel,
|
|
@@ -30,6 +30,7 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|
|
30
30
|
const createSupervisor = internals.createConnectionSupervisor ?? createTokenConnectionSupervisor;
|
|
31
31
|
const logger = typeof ctx.logger === 'function'
|
|
32
32
|
? ctx.logger('dsh-im:slack') : (ctx.logger ?? console);
|
|
33
|
+
const agentPresetCatalog = () => listAgentPresetCatalog(ctx);
|
|
33
34
|
const paths = pluginPaths(config, 'slack');
|
|
34
35
|
const configStore = await new ResolvedConfigStore(paths.config).load();
|
|
35
36
|
const defaultWorkspace = resolve(config.workspace ?? process.cwd());
|
|
@@ -76,7 +77,9 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|
|
76
77
|
createRuntime: async ({ botId, config: botConfig, botToken, appToken }) => {
|
|
77
78
|
const state = await stateFor(botId);
|
|
78
79
|
await workspaces.ensure(botId, { defaultAgentPreset: config.agentPreset });
|
|
79
|
-
const workspaceScope = createBotWorkspaceScope(harness, {
|
|
80
|
+
const workspaceScope = createBotWorkspaceScope(harness, {
|
|
81
|
+
botId, workspaces, state, agentPresetCatalog,
|
|
82
|
+
});
|
|
80
83
|
return new ResolvedRuntime({
|
|
81
84
|
config: botConfig,
|
|
82
85
|
botToken,
|
|
@@ -110,7 +113,7 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|
|
110
113
|
const controller = createWorkspaceAwareController(coreController, {
|
|
111
114
|
workspaces,
|
|
112
115
|
stateFor,
|
|
113
|
-
agentPresetCatalog
|
|
116
|
+
agentPresetCatalog,
|
|
114
117
|
});
|
|
115
118
|
const supervisor = createSupervisor({
|
|
116
119
|
channel: 'slack',
|
|
@@ -50,6 +50,7 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|
|
50
50
|
const QrAuth = internals.QrAuth ?? WecomQrAuth;
|
|
51
51
|
const createSupervisor = internals.createConnectionSupervisor ?? createConnectionSupervisor;
|
|
52
52
|
const logger = typeof ctx.logger === 'function' ? ctx.logger('dsh-im:wecom') : (ctx.logger ?? console);
|
|
53
|
+
const agentPresetCatalog = () => listAgentPresetCatalog(ctx);
|
|
53
54
|
const paths = pluginPaths(config);
|
|
54
55
|
const configStore = await new ConfigStore(paths.config).load();
|
|
55
56
|
const defaultWorkspace = resolve(config.workspace ?? process.cwd());
|
|
@@ -100,7 +101,9 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|
|
100
101
|
createRuntime: async ({ botId, config: botConfig, secret }) => {
|
|
101
102
|
const state = await stateFor(botId);
|
|
102
103
|
await workspaces.ensure(botId, { defaultAgentPreset: config.agentPreset });
|
|
103
|
-
const workspaceScope = createBotWorkspaceScope(harness, {
|
|
104
|
+
const workspaceScope = createBotWorkspaceScope(harness, {
|
|
105
|
+
botId, workspaces, state, agentPresetCatalog,
|
|
106
|
+
});
|
|
104
107
|
return new Runtime({
|
|
105
108
|
config: botConfig,
|
|
106
109
|
secret,
|
|
@@ -134,7 +137,7 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|
|
134
137
|
const controller = createWorkspaceAwareController(coreController, {
|
|
135
138
|
workspaces,
|
|
136
139
|
stateFor,
|
|
137
|
-
agentPresetCatalog
|
|
140
|
+
agentPresetCatalog,
|
|
138
141
|
});
|
|
139
142
|
const supervisor = createSupervisor({
|
|
140
143
|
controller,
|
|
@@ -53,6 +53,7 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|
|
53
53
|
const logger = typeof ctx.logger === 'function'
|
|
54
54
|
? ctx.logger('dsh-weixin')
|
|
55
55
|
: (ctx.logger ?? console);
|
|
56
|
+
const agentPresetCatalog = () => listAgentPresetCatalog(ctx);
|
|
56
57
|
const paths = pluginPaths(config);
|
|
57
58
|
const configStore = await new ConfigStore(paths.config).load();
|
|
58
59
|
const defaultWorkspace = resolve(config.workspace ?? process.cwd());
|
|
@@ -100,7 +101,9 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|
|
100
101
|
createRuntime: async ({ botId, config: accountConfig, token }) => {
|
|
101
102
|
const state = await stateFor(botId);
|
|
102
103
|
await workspaces.ensure(botId, { defaultAgentPreset: config.agentPreset });
|
|
103
|
-
const workspaceScope = createBotWorkspaceScope(harness, {
|
|
104
|
+
const workspaceScope = createBotWorkspaceScope(harness, {
|
|
105
|
+
botId, workspaces, state, agentPresetCatalog,
|
|
106
|
+
});
|
|
104
107
|
return new Runtime({
|
|
105
108
|
api,
|
|
106
109
|
config: accountConfig,
|
|
@@ -134,7 +137,7 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|
|
134
137
|
const controller = createWorkspaceAwareController(coreController, {
|
|
135
138
|
workspaces,
|
|
136
139
|
stateFor,
|
|
137
|
-
agentPresetCatalog
|
|
140
|
+
agentPresetCatalog,
|
|
138
141
|
});
|
|
139
142
|
const supervisor = createSupervisor({
|
|
140
143
|
controller,
|
|
@@ -50,6 +50,7 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|
|
50
50
|
if (!ctx?.webServer) throw new TypeError('dsh-im WhatsApp requires ctx.webServer');
|
|
51
51
|
const logger = typeof ctx.logger === 'function'
|
|
52
52
|
? ctx.logger('dsh-im:whatsapp') : (ctx.logger ?? console);
|
|
53
|
+
const agentPresetCatalog = () => listAgentPresetCatalog(ctx);
|
|
53
54
|
const ConfigStore = internals.ConfigStore ?? WhatsappConfigStore;
|
|
54
55
|
const StateStore = internals.StateStore ?? WhatsappStateStore;
|
|
55
56
|
const Harness = internals.HarnessClient ?? WhatsappHarnessClient;
|
|
@@ -103,7 +104,9 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|
|
103
104
|
createRuntime: async ({ botId, config: botConfig, authDir }) => {
|
|
104
105
|
const state = await stateFor(botId);
|
|
105
106
|
await workspaces.ensure(botId, { defaultAgentPreset: config.agentPreset });
|
|
106
|
-
const workspaceScope = createBotWorkspaceScope(harness, {
|
|
107
|
+
const workspaceScope = createBotWorkspaceScope(harness, {
|
|
108
|
+
botId, workspaces, state, agentPresetCatalog,
|
|
109
|
+
});
|
|
107
110
|
return new Runtime({
|
|
108
111
|
config: botConfig,
|
|
109
112
|
authDir,
|
|
@@ -141,7 +144,7 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|
|
141
144
|
const controller = createWorkspaceAwareController(coreController, {
|
|
142
145
|
workspaces,
|
|
143
146
|
stateFor,
|
|
144
|
-
agentPresetCatalog
|
|
147
|
+
agentPresetCatalog,
|
|
145
148
|
});
|
|
146
149
|
const supervisor = createSupervisor({
|
|
147
150
|
channel: 'whatsapp',
|
|
@@ -18,6 +18,10 @@ import {
|
|
|
18
18
|
isModelCommand,
|
|
19
19
|
runModelCommand,
|
|
20
20
|
} from '../shared/model-command.mjs';
|
|
21
|
+
import {
|
|
22
|
+
isPresetCommand,
|
|
23
|
+
runPresetCommand,
|
|
24
|
+
} from '../shared/preset-command.mjs';
|
|
21
25
|
import { runWorkspaceCommand } from '../shared/workspace-command.mjs';
|
|
22
26
|
import { askInWorkspaceSession } from '../shared/workspace-session.mjs';
|
|
23
27
|
import {
|
|
@@ -44,6 +48,10 @@ const HELP_TEXT = [
|
|
|
44
48
|
'/models 按序号列出所有可用模型',
|
|
45
49
|
'/model [序号或完整模型ID] 查看或切换当前会话模型',
|
|
46
50
|
'示例:先发 /models,再发 /model 2',
|
|
51
|
+
'/presetlist 按序号列出可用 Agent Preset',
|
|
52
|
+
'/preset [序号或完整ID] 查看或设置当前机器人 Agent Preset',
|
|
53
|
+
'纯数字 ID:/preset id:<ID>',
|
|
54
|
+
'/preset --default 跟随 Host 默认',
|
|
47
55
|
'/stop 停止当前任务',
|
|
48
56
|
'/steer 补充指令 纠偏当前任务',
|
|
49
57
|
'/status 检查连接状态',
|
|
@@ -336,7 +344,9 @@ export class DingtalkHarnessBridge {
|
|
|
336
344
|
const commandText = nonEmptyString(promptMessage.content) ?? '';
|
|
337
345
|
const commandRunner = isControlCommand(commandText)
|
|
338
346
|
? runControlCommand
|
|
339
|
-
: (isModelCommand(commandText)
|
|
347
|
+
: (isModelCommand(commandText)
|
|
348
|
+
? runModelCommand
|
|
349
|
+
: (isPresetCommand(commandText) ? runPresetCommand : null));
|
|
340
350
|
const addressed = String(message.conversationType) !== '2' || message?.isInAtList === true;
|
|
341
351
|
if (commandRunner && sessionWebhook && addressed) {
|
|
342
352
|
let task;
|
|
@@ -108,7 +108,7 @@ export class DiscordApi {
|
|
|
108
108
|
headers: {
|
|
109
109
|
authorization: `Bot ${this.#token}`,
|
|
110
110
|
'content-type': 'application/json',
|
|
111
|
-
'user-agent': 'DeepSeek-Harness-dsh-im (https://github.com/xmanrui/dsh-im, 0.
|
|
111
|
+
'user-agent': 'DeepSeek-Harness-dsh-im (https://github.com/xmanrui/dsh-im, 1.0.0)',
|
|
112
112
|
},
|
|
113
113
|
...(body === undefined ? {} : { body: JSON.stringify(body) }),
|
|
114
114
|
signal: requestSignal(signal, timeoutMs),
|