@xmanrui/dsh-im 4.10.0 → 4.12.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/PROACTIVE_DELIVERY.en.md +15 -1
- package/PROACTIVE_DELIVERY.md +15 -1
- package/README.en.md +7 -2
- package/README.md +8 -3
- package/lib/client.js +2341 -1315
- package/lib/index.js +250 -245
- package/package.json +4 -3
- package/plugin-src/client/channel-card-meta.js +9 -1
- package/plugin-src/client/channels/dingtalk/api.js +4 -0
- package/plugin-src/client/channels/dingtalk/index.js +41 -7
- package/plugin-src/client/channels/feishu/api.js +4 -0
- package/plugin-src/client/channels/feishu/group-settings.js +581 -0
- package/plugin-src/client/channels/feishu/index.js +64 -233
- package/plugin-src/client/channels/qq/api.js +4 -0
- package/plugin-src/client/channels/qq/index.js +41 -7
- package/plugin-src/client/channels/shared/collapsible-account.js +80 -0
- package/plugin-src/client/channels/shared/token-api.js +4 -0
- package/plugin-src/client/channels/shared/token-channel.js +62 -29
- package/plugin-src/client/channels/wecom/api.js +4 -0
- package/plugin-src/client/channels/wecom/index.js +41 -7
- package/plugin-src/client/channels/weixin/api.js +4 -0
- package/plugin-src/client/channels/weixin/index.js +41 -7
- package/plugin-src/client/channels/whatsapp/api.js +4 -0
- package/plugin-src/client/channels/whatsapp/index.js +40 -7
- package/plugin-src/client/delivery-settings.js +73 -5
- package/plugin-src/client/i18n.js +22 -0
- package/plugin-src/client/model-setting.js +104 -0
- package/plugin-src/client/styles.js +62 -0
- package/plugin-src/host/channels/dingtalk/production.mjs +3 -0
- package/plugin-src/host/channels/dingtalk/rpc.mjs +11 -0
- package/plugin-src/host/channels/feishu/production.mjs +3 -0
- package/plugin-src/host/channels/feishu/rpc.mjs +16 -0
- package/plugin-src/host/channels/qq/production.mjs +3 -0
- package/plugin-src/host/channels/qq/rpc.mjs +11 -0
- package/plugin-src/host/channels/shared/model-setting-rpc.mjs +19 -0
- package/plugin-src/host/channels/shared/production.mjs +3 -0
- package/plugin-src/host/channels/shared/rpc.mjs +9 -0
- package/plugin-src/host/channels/shared/workspace-rpc.mjs +2 -0
- package/plugin-src/host/channels/slack/production.mjs +3 -0
- package/plugin-src/host/channels/slack/rpc.mjs +9 -0
- package/plugin-src/host/channels/wecom/production.mjs +3 -0
- package/plugin-src/host/channels/wecom/rpc.mjs +11 -0
- package/plugin-src/host/channels/weixin/production.mjs +3 -0
- package/plugin-src/host/channels/weixin/rpc.mjs +11 -0
- package/plugin-src/host/channels/whatsapp/production.mjs +3 -0
- package/plugin-src/host/channels/whatsapp/rpc.mjs +11 -0
- package/plugin-src/host/delivery-adapter.mjs +141 -3
- package/plugin-src/host/delivery-rpc.mjs +12 -0
- package/plugin-src/host/delivery-service.mjs +110 -3
- package/plugin-src/host/delivery-suggestions.mjs +46 -0
- package/plugin-src/host/harness-session-coordinator.mjs +6 -5
- package/plugin-src/host/index.mjs +18 -1
- package/plugin-src/host/session-sync-coordinator.mjs +217 -0
- package/scripts/verify-package.mjs +12 -6
- package/src/channels/shared/bot-workspace-store.mjs +285 -38
- package/src/channels/shared/harness-client.mjs +124 -43
- package/src/channels/shared/i18n-en/wecom.mjs +24 -0
- package/src/channels/shared/model-setting.mjs +110 -0
- package/src/channels/wecom/wecom-bridge.mjs +275 -2
- package/src/channels/wecom/wecom-cards.mjs +101 -0
- package/src/channels/wecom/wecom-runtime.mjs +2 -0
- package/src/channels/whatsapp/whatsapp-runtime.mjs +78 -26
|
@@ -2,6 +2,7 @@ import * as React from 'react';
|
|
|
2
2
|
|
|
3
3
|
import { WhatsappLogoGlyph } from '../../channel-logos.js';
|
|
4
4
|
import { QrActionIcon } from '../../credential-binding.js';
|
|
5
|
+
import { CollapsibleAccountSection } from '../shared/collapsible-account.js';
|
|
5
6
|
import { h } from '../../i18n.js';
|
|
6
7
|
import { WorkspaceEditor } from '../../workspace-editor.js';
|
|
7
8
|
import { ContextEnhancementEditor } from '../../context-enhancement.js';
|
|
@@ -10,6 +11,11 @@ import {
|
|
|
10
11
|
AgentPresetEditor,
|
|
11
12
|
EMPTY_AGENT_PRESET_CATALOG,
|
|
12
13
|
} from '../../agent-preset.js';
|
|
14
|
+
import {
|
|
15
|
+
EMPTY_MODEL_CATALOG,
|
|
16
|
+
ModelCatalogContext,
|
|
17
|
+
ModelEditor,
|
|
18
|
+
} from '../../model-setting.js';
|
|
13
19
|
import { useWorkspaceSnapshotFence } from '../../workspace-snapshot-fence.js';
|
|
14
20
|
import {
|
|
15
21
|
BotSettingsButton,
|
|
@@ -185,6 +191,7 @@ export function WhatsappAccountCard({
|
|
|
185
191
|
removing,
|
|
186
192
|
onReconnect,
|
|
187
193
|
onWorkspaceSave,
|
|
194
|
+
onModelSave,
|
|
188
195
|
onAgentPresetSave,
|
|
189
196
|
onContextEnhancementSave,
|
|
190
197
|
onRequestRemove,
|
|
@@ -197,7 +204,9 @@ export function WhatsappAccountCard({
|
|
|
197
204
|
const summary = account.error?.message ?? (account.connected ? null : account.health.summary);
|
|
198
205
|
return h('article', { className: 'ddt-card dim-botCard', 'data-bot-id': account.botId },
|
|
199
206
|
h('div', { className: 'ddt-cardBody dim-botCardBody' },
|
|
200
|
-
h(
|
|
207
|
+
h(CollapsibleAccountSection, {
|
|
208
|
+
id: `wsp-settings-${account.botId.replace(/[^a-zA-Z0-9_-]/g, '-')}`,
|
|
209
|
+
header: h('div', { className: 'ddt-accountTop dim-botCardTop' },
|
|
201
210
|
h('div', { className: 'ddt-accountIdentity dim-botIdentity' },
|
|
202
211
|
h('div', {
|
|
203
212
|
className: 'ddt-avatar dim-botAvatar dwa-avatar',
|
|
@@ -205,7 +214,12 @@ export function WhatsappAccountCard({
|
|
|
205
214
|
}, h(WhatsappLogoGlyph, { size: 29 })),
|
|
206
215
|
h('div', { className: 'dim-botName' },
|
|
207
216
|
h('h3', null, account.bot.name), h('p', null, account.bot.idMasked))),
|
|
208
|
-
h('div', {
|
|
217
|
+
h('div', {
|
|
218
|
+
className: 'dim-botCardTools',
|
|
219
|
+
// The header is the collapse toggle; keep inner controls clickable.
|
|
220
|
+
onClick: (event) => { event.stopPropagation(); },
|
|
221
|
+
onKeyDown: (event) => { if (event.key === 'Enter' || event.key === ' ') event.stopPropagation(); },
|
|
222
|
+
},
|
|
209
223
|
h(BotStatusMeta, {
|
|
210
224
|
className: 'ddt-health',
|
|
211
225
|
dotClassName: 'ddt-dot',
|
|
@@ -220,12 +234,18 @@ export function WhatsappAccountCard({
|
|
|
220
234
|
botName: account.bot.name,
|
|
221
235
|
connected: account.connected,
|
|
222
236
|
accessPolicy: account.accessPolicy,
|
|
223
|
-
})))
|
|
224
|
-
|
|
237
|
+
})))
|
|
238
|
+
},
|
|
239
|
+
h(WorkspaceEditor, {
|
|
225
240
|
workspace: account.workspace,
|
|
226
241
|
disabled: Boolean(busy),
|
|
227
242
|
onSave: onWorkspaceSave,
|
|
228
243
|
}),
|
|
244
|
+
h(ModelEditor, {
|
|
245
|
+
model: account.model,
|
|
246
|
+
disabled: Boolean(busy),
|
|
247
|
+
onSave: onModelSave,
|
|
248
|
+
}),
|
|
229
249
|
h(AgentPresetEditor, {
|
|
230
250
|
agentPreset: account.agentPreset,
|
|
231
251
|
disabled: Boolean(busy),
|
|
@@ -253,7 +273,9 @@ export function WhatsappAccountCard({
|
|
|
253
273
|
testNotice ? h('div', {
|
|
254
274
|
className: 'ddt-summary dim-cardFeedback',
|
|
255
275
|
role: 'status',
|
|
256
|
-
}, testNotice) : null))
|
|
276
|
+
}, testNotice) : null)),
|
|
277
|
+
),
|
|
278
|
+
),
|
|
257
279
|
removing ? h(RemoveConfirmation, {
|
|
258
280
|
account,
|
|
259
281
|
busy: busy === 'delete',
|
|
@@ -266,6 +288,7 @@ export function WhatsappSettingsTab({ rpcCall }) {
|
|
|
266
288
|
const [model, setModel] = React.useState({
|
|
267
289
|
phase: 'loading', bots: [], totals: { configured: 0, connected: 0 }, error: null,
|
|
268
290
|
agentPresetCatalog: EMPTY_AGENT_PRESET_CATALOG,
|
|
291
|
+
modelCatalog: EMPTY_MODEL_CATALOG,
|
|
269
292
|
});
|
|
270
293
|
const [provision, setProvision] = React.useState(null);
|
|
271
294
|
const [busy, setBusy] = React.useState(false);
|
|
@@ -304,6 +327,7 @@ export function WhatsappSettingsTab({ rpcCall }) {
|
|
|
304
327
|
setModel({
|
|
305
328
|
phase: 'ready', bots: snapshot.bots, totals: snapshot.totals, error: null,
|
|
306
329
|
agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG,
|
|
330
|
+
modelCatalog: snapshot.modelCatalog ?? EMPTY_MODEL_CATALOG,
|
|
307
331
|
});
|
|
308
332
|
if (restore && snapshot.provisioning) setProvision({
|
|
309
333
|
...snapshot.provisioning,
|
|
@@ -435,6 +459,7 @@ export function WhatsappSettingsTab({ rpcCall }) {
|
|
|
435
459
|
setModel({
|
|
436
460
|
phase: 'ready', bots: snapshot.bots, totals: snapshot.totals, error: null,
|
|
437
461
|
agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG,
|
|
462
|
+
modelCatalog: snapshot.modelCatalog ?? EMPTY_MODEL_CATALOG,
|
|
438
463
|
});
|
|
439
464
|
if (operation === 'reconnect') {
|
|
440
465
|
setTestNoticeByBot((current) => ({
|
|
@@ -487,6 +512,12 @@ export function WhatsappSettingsTab({ rpcCall }) {
|
|
|
487
512
|
WHATSAPP_ENDPOINTS.setWorkspace,
|
|
488
513
|
{ botId: account.botId, workspace },
|
|
489
514
|
),
|
|
515
|
+
onModelSave: (selectedModel) => botAction(
|
|
516
|
+
account,
|
|
517
|
+
'model',
|
|
518
|
+
WHATSAPP_ENDPOINTS.setModel,
|
|
519
|
+
{ botId: account.botId, model: selectedModel },
|
|
520
|
+
),
|
|
490
521
|
onAgentPresetSave: (agentPreset) => botAction(
|
|
491
522
|
account,
|
|
492
523
|
'preset',
|
|
@@ -511,7 +542,9 @@ export function WhatsappSettingsTab({ rpcCall }) {
|
|
|
511
542
|
})))))
|
|
512
543
|
: null;
|
|
513
544
|
|
|
514
|
-
return h(
|
|
545
|
+
return h(ModelCatalogContext.Provider, {
|
|
546
|
+
value: model.modelCatalog ?? EMPTY_MODEL_CATALOG,
|
|
547
|
+
}, h(AgentPresetCatalogContext.Provider, {
|
|
515
548
|
value: model.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG,
|
|
516
549
|
}, h('section', {
|
|
517
550
|
className: 'ddt-page dwa-page dim-channelPage',
|
|
@@ -550,5 +583,5 @@ export function WhatsappSettingsTab({ rpcCall }) {
|
|
|
550
583
|
: model.bots.length === 0
|
|
551
584
|
? h(EmptyView, { busy, onStart: () => void startProvisioning(false) })
|
|
552
585
|
: null,
|
|
553
|
-
botList)));
|
|
586
|
+
botList))));
|
|
554
587
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
|
|
3
3
|
import { AccessPolicySettingsPage } from './access-policy-settings.js';
|
|
4
|
+
import { FeishuGroupSettingsPage } from './channels/feishu/group-settings.js';
|
|
4
5
|
import { h, isEnglish, localizeText } from './i18n.js';
|
|
5
6
|
|
|
6
7
|
export const DELIVERY_RPC_CHANNEL = '/dsh-im-delivery';
|
|
@@ -16,6 +17,7 @@ export const DELIVERY_ENDPOINTS = Object.freeze({
|
|
|
16
17
|
create: 'target.create',
|
|
17
18
|
update: 'target.update',
|
|
18
19
|
delete: 'target.delete',
|
|
20
|
+
sessionSync: 'target.session-sync.set',
|
|
19
21
|
test: 'target.test',
|
|
20
22
|
});
|
|
21
23
|
|
|
@@ -24,6 +26,15 @@ export const BOT_SETTINGS_TABS = Object.freeze([
|
|
|
24
26
|
Object.freeze({ id: 'access', label: '访问设置' }),
|
|
25
27
|
]);
|
|
26
28
|
|
|
29
|
+
export const FEISHU_BOT_SETTINGS_TABS = Object.freeze([
|
|
30
|
+
...BOT_SETTINGS_TABS,
|
|
31
|
+
Object.freeze({ id: 'group', label: '群聊' }),
|
|
32
|
+
]);
|
|
33
|
+
|
|
34
|
+
export function botSettingsTabsForChannel(channel) {
|
|
35
|
+
return channel === 'feishu' ? FEISHU_BOT_SETTINGS_TABS : BOT_SETTINGS_TABS;
|
|
36
|
+
}
|
|
37
|
+
|
|
27
38
|
const CHANNEL_DEFINITIONS = Object.freeze({
|
|
28
39
|
weixin: {
|
|
29
40
|
label: '微信',
|
|
@@ -296,7 +307,9 @@ function TargetForm({
|
|
|
296
307
|
h('div', { className: 'dim-targetFormHeading' },
|
|
297
308
|
h('h3', null, editing ? '编辑投递目标' : '新建投递目标'),
|
|
298
309
|
editing
|
|
299
|
-
?
|
|
310
|
+
? initialValue?.sessionSync?.enabled
|
|
311
|
+
? h('p', null, '修改接收位置会关闭会话双向同步。')
|
|
312
|
+
: null
|
|
300
313
|
: h('p', null, source === 'suggestion'
|
|
301
314
|
? '已从会话自动填入目标信息;确认后再保存。'
|
|
302
315
|
: '请手动填写从对应平台取得的原生标识。')),
|
|
@@ -457,8 +470,17 @@ function TargetSuggestionPicker({
|
|
|
457
470
|
function TargetRow({ definition, target, botId, connected, rpcCall, onChanged, onEdit }) {
|
|
458
471
|
const [action, setAction] = React.useState(null);
|
|
459
472
|
const [testState, setTestState] = React.useState(null);
|
|
473
|
+
const [syncFeedback, setSyncFeedback] = React.useState(null);
|
|
460
474
|
const [copyState, setCopyState] = React.useState(null);
|
|
461
475
|
const [confirmDelete, setConfirmDelete] = React.useState(false);
|
|
476
|
+
const sessionSync = target.sessionSync ?? { enabled: false, state: 'unavailable' };
|
|
477
|
+
const syncDescription = sessionSync.state === 'active'
|
|
478
|
+
? '自动跟随该私聊的当前会话'
|
|
479
|
+
: sessionSync.state === 'waiting'
|
|
480
|
+
? '等待该私聊建立新会话'
|
|
481
|
+
: sessionSync.state === 'unavailable'
|
|
482
|
+
? '仅支持已经聊过、已建立当前会话且使用当前 Host Harness 的私聊目标'
|
|
483
|
+
: '关闭时不发送 DSH 会话消息';
|
|
462
484
|
const testTarget = async () => {
|
|
463
485
|
setAction('test');
|
|
464
486
|
setTestState(null);
|
|
@@ -494,6 +516,28 @@ function TargetRow({ definition, target, botId, connected, rpcCall, onChanged, o
|
|
|
494
516
|
}
|
|
495
517
|
};
|
|
496
518
|
|
|
519
|
+
const setSessionSync = async (enabled) => {
|
|
520
|
+
setAction('sync');
|
|
521
|
+
setSyncFeedback(null);
|
|
522
|
+
try {
|
|
523
|
+
await rpcCall(DELIVERY_ENDPOINTS.sessionSync, {
|
|
524
|
+
botId,
|
|
525
|
+
targetId: target.targetId,
|
|
526
|
+
enabled,
|
|
527
|
+
});
|
|
528
|
+
await onChanged();
|
|
529
|
+
} catch (error) {
|
|
530
|
+
setSyncFeedback({
|
|
531
|
+
tone: 'error',
|
|
532
|
+
message: error?.code === 'session-sync-unavailable'
|
|
533
|
+
? '会话同步仅支持已经聊过、已建立当前会话且使用当前 Host Harness 的私聊目标。'
|
|
534
|
+
: presentError(error, '会话同步设置失败,请稍后重试。'),
|
|
535
|
+
});
|
|
536
|
+
} finally {
|
|
537
|
+
setAction(null);
|
|
538
|
+
}
|
|
539
|
+
};
|
|
540
|
+
|
|
497
541
|
return h('li', { className: 'dim-targetRow', 'data-target-id': target.targetId },
|
|
498
542
|
h('div', { className: 'dim-targetSummary' },
|
|
499
543
|
h('div', { className: 'dim-targetTitle' },
|
|
@@ -514,7 +558,24 @@ function TargetRow({ definition, target, botId, connected, rpcCall, onChanged, o
|
|
|
514
558
|
onClick: () => setConfirmDelete(true),
|
|
515
559
|
disabled: Boolean(action),
|
|
516
560
|
}, '删除')),
|
|
561
|
+
h('label', { className: 'dim-targetSessionSync' },
|
|
562
|
+
h('span', { className: 'dim-targetSessionSyncCopy' },
|
|
563
|
+
h('strong', null, '会话双向同步'),
|
|
564
|
+
h('small', null, syncDescription)),
|
|
565
|
+
h('input', {
|
|
566
|
+
type: 'checkbox',
|
|
567
|
+
checked: sessionSync.enabled === true,
|
|
568
|
+
disabled: Boolean(action)
|
|
569
|
+
|| (sessionSync.enabled !== true && sessionSync.state === 'unavailable'),
|
|
570
|
+
onChange: (event) => void setSessionSync(event.target.checked),
|
|
571
|
+
'aria-label': '会话双向同步',
|
|
572
|
+
})),
|
|
517
573
|
copyState ? h('p', { className: 'dim-targetFeedback', role: 'status' }, copyState) : null,
|
|
574
|
+
syncFeedback ? h('p', {
|
|
575
|
+
className: 'dim-targetFeedback',
|
|
576
|
+
'data-tone': syncFeedback.tone,
|
|
577
|
+
role: 'alert',
|
|
578
|
+
}, syncFeedback.message) : null,
|
|
518
579
|
testState ? h('p', {
|
|
519
580
|
className: 'dim-targetFeedback',
|
|
520
581
|
'data-tone': testState.tone,
|
|
@@ -524,7 +585,8 @@ function TargetRow({ definition, target, botId, connected, rpcCall, onChanged, o
|
|
|
524
585
|
confirmDelete ? h('div', { className: 'dim-targetDeleteConfirm', role: 'alertdialog' },
|
|
525
586
|
h('p', null,
|
|
526
587
|
'删除 ', h('code', null, target.targetId),
|
|
527
|
-
'?使用这个 targetId 的外部调用将返回 unknown-target。'
|
|
588
|
+
'?使用这个 targetId 的外部调用将返回 unknown-target。',
|
|
589
|
+
sessionSync.enabled ? ' 会话同步设置也会一并删除。' : null),
|
|
528
590
|
h('div', { className: 'dim-targetFormActions' },
|
|
529
591
|
h(DeliveryButton, {
|
|
530
592
|
onClick: () => setConfirmDelete(false),
|
|
@@ -672,8 +734,9 @@ export function DeliveryTargetSettingsPage({
|
|
|
672
734
|
}
|
|
673
735
|
};
|
|
674
736
|
|
|
675
|
-
const
|
|
676
|
-
|
|
737
|
+
const settingsTabs = botSettingsTabsForChannel(channel);
|
|
738
|
+
const activeTab = settingsTabs.find((tab) => tab.id === activeTabId)
|
|
739
|
+
?? settingsTabs[0];
|
|
677
740
|
const activeTabDomId = `dim-bot-settings-${activeTab.id}-tab`;
|
|
678
741
|
const activePanelId = `dim-bot-settings-${activeTab.id}-panel`;
|
|
679
742
|
|
|
@@ -688,7 +751,7 @@ export function DeliveryTargetSettingsPage({
|
|
|
688
751
|
className: 'dim-botSettingsTabs',
|
|
689
752
|
role: 'tablist',
|
|
690
753
|
'aria-label': '机器人设置页签',
|
|
691
|
-
},
|
|
754
|
+
}, settingsTabs.map((tab) => h('button', {
|
|
692
755
|
key: tab.id,
|
|
693
756
|
id: `dim-bot-settings-${tab.id}-tab`,
|
|
694
757
|
type: 'button',
|
|
@@ -712,6 +775,11 @@ export function DeliveryTargetSettingsPage({
|
|
|
712
775
|
rpcCall: accessRpcCall,
|
|
713
776
|
onSaved: setAccessPolicy,
|
|
714
777
|
})
|
|
778
|
+
: activeTab.id === 'group' && channel === 'feishu'
|
|
779
|
+
? h(FeishuGroupSettingsPage, {
|
|
780
|
+
account,
|
|
781
|
+
rpcCall: accessRpcCall,
|
|
782
|
+
})
|
|
715
783
|
: h(React.Fragment, null,
|
|
716
784
|
h('section', { className: 'dim-deliveryIdentity', 'aria-labelledby': 'dim-delivery-bot-title' },
|
|
717
785
|
h('div', { className: 'dim-deliveryIdentityHeading' },
|
|
@@ -227,6 +227,8 @@ const EN = Object.freeze({
|
|
|
227
227
|
'手动接入': 'Manual setup',
|
|
228
228
|
'收起凭据': 'Hide credentials',
|
|
229
229
|
'收起接入': 'Hide setup',
|
|
230
|
+
'展开该账号的设置': "Expand this account's settings",
|
|
231
|
+
'收起该账号的设置': "Collapse this account's settings",
|
|
230
232
|
'接入机器人': 'Connect bot',
|
|
231
233
|
'开始接入': 'Start setup',
|
|
232
234
|
'在线': 'online',
|
|
@@ -351,6 +353,13 @@ const EN = Object.freeze({
|
|
|
351
353
|
'请选择 Agent Preset。': 'Choose an Agent Preset.',
|
|
352
354
|
'Agent Preset 无效。': 'The Agent Preset is invalid.',
|
|
353
355
|
'Agent Preset 不存在或不可用。': 'The Agent Preset does not exist or is unavailable.',
|
|
356
|
+
'模型': 'Model',
|
|
357
|
+
'查看模型设置说明': 'View model setting help',
|
|
358
|
+
'跟随默认模型': 'Follow the default model',
|
|
359
|
+
'模型修改失败,请重试。': 'Could not update the model. Try again.',
|
|
360
|
+
'模型无效。': 'The model is invalid.',
|
|
361
|
+
'模型不存在或不可用。': 'The model does not exist or is unavailable.',
|
|
362
|
+
'当前模型已不可用,请选择其他模型或跟随默认模型。': 'The current model is unavailable. Choose another model or follow the default.',
|
|
354
363
|
'尚未检查': 'Not checked yet',
|
|
355
364
|
'刚刚': 'Just now',
|
|
356
365
|
'检查连接': 'Check connection',
|
|
@@ -766,6 +775,10 @@ const EN = Object.freeze({
|
|
|
766
775
|
'填写接收消息的用户 ID': 'Enter the recipient user ID',
|
|
767
776
|
'私聊': 'Direct message',
|
|
768
777
|
'群聊': 'Group',
|
|
778
|
+
'群聊设置': 'Group settings',
|
|
779
|
+
'当前机器人': 'Current bot',
|
|
780
|
+
'飞书群聊设置暂不可用。': 'Feishu group settings are currently unavailable.',
|
|
781
|
+
'飞书服务没有返回当前机器人的设置': 'Feishu did not return settings for this bot',
|
|
769
782
|
'群 Chat ID': 'Group Chat ID',
|
|
770
783
|
'用户 ID': 'User ID',
|
|
771
784
|
'填写钉钉用户 ID': 'Enter the DingTalk user ID',
|
|
@@ -828,6 +841,15 @@ const EN = Object.freeze({
|
|
|
828
841
|
'?使用这个 targetId 的外部调用将返回 unknown-target。': '? External calls using this targetId will return unknown-target.',
|
|
829
842
|
'正在删除…': 'Deleting…',
|
|
830
843
|
'确认删除': 'Delete target',
|
|
844
|
+
'会话双向同步': 'Two-way Session sync',
|
|
845
|
+
'自动跟随该私聊的当前会话': 'Automatically follow this direct message’s current Session',
|
|
846
|
+
'等待该私聊建立新会话': 'Waiting for this direct message to establish a new Session',
|
|
847
|
+
'关闭时不发送 DSH 会话消息': 'DSH Session messages are not sent while disabled',
|
|
848
|
+
'仅支持已经聊过、已建立当前会话且使用当前 Host Harness 的私聊目标': 'Only a known direct message with a current Session on this Host Harness is supported',
|
|
849
|
+
'会话同步仅支持已经聊过、已建立当前会话且使用当前 Host Harness 的私聊目标。': 'Session sync requires a known direct message with a current Session on this Host Harness.',
|
|
850
|
+
'会话同步设置失败,请稍后重试。': 'Could not update Session sync. Try again later.',
|
|
851
|
+
'修改接收位置会关闭会话双向同步。': 'Changing the destination disables two-way Session sync.',
|
|
852
|
+
' 会话同步设置也会一并删除。': ' Its Session sync setting will also be deleted.',
|
|
831
853
|
'投递目标设置暂不可用。': 'Delivery target settings are unavailable.',
|
|
832
854
|
'无法读取投递目标,请稍后重试。': 'Could not load delivery targets. Try again later.',
|
|
833
855
|
'← 返回机器人列表': '← Back to bots',
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
EMPTY_MODEL_CATALOG,
|
|
5
|
+
modelSelectionId,
|
|
6
|
+
normalizeModelCatalog,
|
|
7
|
+
normalizeModelSelection,
|
|
8
|
+
} from '../../src/channels/shared/model-setting.mjs';
|
|
9
|
+
import { h } from './i18n.js';
|
|
10
|
+
|
|
11
|
+
export const SET_MODEL_ENDPOINT = 'bot.model.set';
|
|
12
|
+
export { EMPTY_MODEL_CATALOG, normalizeModelCatalog, normalizeModelSelection };
|
|
13
|
+
|
|
14
|
+
export const ModelCatalogContext = React.createContext(EMPTY_MODEL_CATALOG);
|
|
15
|
+
|
|
16
|
+
function optionValue(selection) {
|
|
17
|
+
return JSON.stringify([selection.provider, selection.model]);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function ModelEditor({ model = null, disabled = false, onSave }) {
|
|
21
|
+
const catalog = normalizeModelCatalog(
|
|
22
|
+
React.useContext(ModelCatalogContext) ?? EMPTY_MODEL_CATALOG,
|
|
23
|
+
);
|
|
24
|
+
const helpId = React.useId();
|
|
25
|
+
const current = normalizeModelSelection(model);
|
|
26
|
+
const [saving, setSaving] = React.useState(false);
|
|
27
|
+
const [error, setError] = React.useState(null);
|
|
28
|
+
const currentValue = current ? optionValue(current) : '';
|
|
29
|
+
const currentAvailable = !current || catalog.groups.some(
|
|
30
|
+
(group) => group.id === current.provider
|
|
31
|
+
&& group.models.some((entry) => entry.id === current.model),
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
const selections = new Map();
|
|
35
|
+
for (const group of catalog.groups) {
|
|
36
|
+
for (const entry of group.models) {
|
|
37
|
+
const selection = { provider: group.id, model: entry.id };
|
|
38
|
+
selections.set(optionValue(selection), selection);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
if (current && !currentAvailable) selections.set(currentValue, current);
|
|
42
|
+
|
|
43
|
+
const change = async (event) => {
|
|
44
|
+
const nextValue = event.target.value;
|
|
45
|
+
if (nextValue === currentValue || saving || disabled) return;
|
|
46
|
+
const next = nextValue ? selections.get(nextValue) : null;
|
|
47
|
+
if (nextValue && !next) return;
|
|
48
|
+
setSaving(true);
|
|
49
|
+
setError(null);
|
|
50
|
+
try {
|
|
51
|
+
await onSave?.(next);
|
|
52
|
+
} catch (cause) {
|
|
53
|
+
setError(cause?.message ?? '模型修改失败,请重试。');
|
|
54
|
+
} finally {
|
|
55
|
+
setSaving(false);
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
return h('div', { className: 'dim-preset dim-modelSetting' },
|
|
60
|
+
h('div', { className: 'dim-presetHeader' },
|
|
61
|
+
h('span', { className: 'dim-presetTitle' },
|
|
62
|
+
h('span', null, '模型'),
|
|
63
|
+
h('span', { className: 'dim-presetHelp' },
|
|
64
|
+
h('button', {
|
|
65
|
+
type: 'button',
|
|
66
|
+
className: 'dim-presetHelpButton',
|
|
67
|
+
'aria-label': '查看模型设置说明',
|
|
68
|
+
'aria-describedby': helpId,
|
|
69
|
+
}, h('span', { 'aria-hidden': 'true' }, '?')),
|
|
70
|
+
h('span', {
|
|
71
|
+
id: helpId,
|
|
72
|
+
className: 'dim-presetTooltip',
|
|
73
|
+
role: 'tooltip',
|
|
74
|
+
}, '只影响新建会话;若当前聊天已有会话,先发送 /new,再发送普通消息生效。'))),
|
|
75
|
+
saving ? h('span', { className: 'dim-presetStatus' }, '保存中…') : null),
|
|
76
|
+
h('select', {
|
|
77
|
+
className: 'dim-presetSelect dim-modelSelect',
|
|
78
|
+
value: currentValue,
|
|
79
|
+
disabled: disabled || saving,
|
|
80
|
+
'aria-label': '模型',
|
|
81
|
+
onChange: (event) => { void change(event); },
|
|
82
|
+
},
|
|
83
|
+
h('option', { value: '' }, '跟随默认模型'),
|
|
84
|
+
...catalog.groups.map((group) => h(
|
|
85
|
+
'optgroup',
|
|
86
|
+
{ key: group.id, label: group.name },
|
|
87
|
+
...group.models.map((entry) => {
|
|
88
|
+
const selection = { provider: group.id, model: entry.id };
|
|
89
|
+
const id = modelSelectionId(selection);
|
|
90
|
+
return h('option', { key: id, value: optionValue(selection) },
|
|
91
|
+
entry.name && entry.name !== entry.id ? `${entry.name}(${id})` : id);
|
|
92
|
+
}),
|
|
93
|
+
)),
|
|
94
|
+
current && !currentAvailable
|
|
95
|
+
? h('option', { value: currentValue }, [modelSelectionId(current), '(已不可用)'])
|
|
96
|
+
: null,
|
|
97
|
+
),
|
|
98
|
+
error || !currentAvailable ? h(
|
|
99
|
+
'p',
|
|
100
|
+
{ className: 'dim-presetError', role: error ? 'alert' : 'status' },
|
|
101
|
+
error ?? '当前模型已不可用,请选择其他模型或跟随默认模型。',
|
|
102
|
+
) : null,
|
|
103
|
+
);
|
|
104
|
+
}
|
|
@@ -358,6 +358,17 @@ const CSS = String.raw`
|
|
|
358
358
|
.dim-panel .dim-botCard { position: relative; min-width: 0; width: 100%; max-width: 100%; overflow: hidden; border: 1px solid var(--dsw-alias-border-l2, #e5e6eb); border-radius: 14px; background: var(--dsw-alias-bg-layer-1, #fff); box-shadow: 0 1px 2px rgb(31 35 41 / 3%); }
|
|
359
359
|
.dim-panel .dim-botCard::before { display: none; }
|
|
360
360
|
.dim-panel .dim-botCardBody { position: relative; min-width: 0; width: 100%; max-width: 100%; padding: 12px; }
|
|
361
|
+
.dim-collapsibleAccount { min-width: 0; display: flex; flex-direction: column; }
|
|
362
|
+
.dim-collapsibleHead { min-width: 0; display: flex; align-items: center; gap: 10px; cursor: pointer; user-select: none; -webkit-user-select: none; }
|
|
363
|
+
.dim-collapsibleHead:focus-visible { outline: 2px solid var(--dsw-alias-state-business-primary, #3370ff); outline-offset: 2px; border-radius: 8px; }
|
|
364
|
+
.dim-collapsibleHeaderContent { min-width: 0; flex: 1 1 auto; display: flex; align-items: center; }
|
|
365
|
+
.dim-collapsibleChevron { flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center; width: 9px; height: 9px; border-right: 1.6px solid var(--dsw-alias-label-tertiary, #8f959e); border-bottom: 1.6px solid var(--dsw-alias-label-tertiary, #8f959e); transform: rotate(-45deg); transition: transform .22s cubic-bezier(.4, 0, .2, 1); transform-origin: 50% 50%; }
|
|
366
|
+
.dim-collapsibleAccount.is-open .dim-collapsibleChevron { transform: rotate(45deg); }
|
|
367
|
+
/* Animate height without measuring content; hide collapsed controls from focus and accessibility. */
|
|
368
|
+
.dim-collapsibleBody { display: grid; grid-template-rows: 0fr; transition: grid-template-rows .22s cubic-bezier(.4, 0, .2, 1); }
|
|
369
|
+
.dim-collapsibleAccount.is-open > .dim-collapsibleBody { grid-template-rows: 1fr; }
|
|
370
|
+
.dim-collapsibleBodyInner { min-height: 0; overflow: hidden; }
|
|
371
|
+
.dim-collapsibleAccount:not(.is-open) .dim-collapsibleBodyInner { visibility: hidden; }
|
|
361
372
|
.dim-panel .dim-botCardTop { min-width: 0; max-width: 100%; display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; }
|
|
362
373
|
.dim-panel .dim-botIdentity { min-width: 0; flex: 1 1 0; display: flex; align-items: center; gap: 10px; }
|
|
363
374
|
.dim-panel .dim-botAvatar { flex: none; width: 38px; height: 38px; display: grid; place-items: center; overflow: hidden; border-radius: 11px; box-shadow: none; }
|
|
@@ -391,6 +402,45 @@ const CSS = String.raw`
|
|
|
391
402
|
.dim-botSettingsTab[aria-selected="true"]::after { background: var(--dsw-alias-state-business-primary, #3370ff); transform: scaleX(1); }
|
|
392
403
|
.dim-botSettingsTab:focus-visible { outline: 2px solid var(--dsw-alias-state-business-primary, #3370ff); outline-offset: -2px; border-radius: 5px; }
|
|
393
404
|
.dim-botSettingsTabPanel { min-width: 0; display: grid; gap: 14px; padding-top: 14px; }
|
|
405
|
+
.dim-feishuGroupSettings { min-width: 0; display: grid; gap: 14px; }
|
|
406
|
+
.dim-feishuGroupControls { min-width: 0; display: grid; gap: 12px; }
|
|
407
|
+
.dim-feishuGroupControl { min-width: 0; display: grid; grid-template-columns: minmax(0, 1fr); gap: 10px; padding: 16px; border: 1px solid var(--dsw-alias-border-l2, #dfe1e5); border-radius: 12px; background: var(--dsw-alias-bg-layer-3, #fff); }
|
|
408
|
+
.dim-feishuGroupControlHeader { min-width: 0; display: flex; align-items: center; justify-content: space-between; gap: 12px; }
|
|
409
|
+
.dim-feishuGroupControlHeader h3 { min-width: 0; margin: 0; color: var(--dsw-alias-label-primary, #1f2329); font-size: 14px; line-height: 21px; font-weight: 650; }
|
|
410
|
+
.dim-feishuGroupControlStatus { flex: none; color: var(--dsw-alias-label-tertiary, #8f959e); font-size: 11px; line-height: 17px; }
|
|
411
|
+
.dim-feishuGroupSelect { min-width: 0; width: 100%; height: 38px; padding: 0 34px 0 11px; border: 1px solid var(--dsw-alias-border-l2, #dfe1e5); border-radius: 8px; color: var(--dsw-alias-label-primary, #1f2329); background: var(--dsw-alias-bg-layer-1, #fff); font: inherit; font-size: 13px; line-height: 38px; cursor: pointer; transition: border-color .15s ease, box-shadow .15s ease; }
|
|
412
|
+
.dim-feishuGroupSelect:hover:not(:disabled) { border-color: color-mix(in srgb, var(--dsw-alias-state-business-primary, #3370ff) 42%, var(--dsw-alias-border-l2, #dfe1e5)); }
|
|
413
|
+
.dim-feishuGroupSelect:focus-visible { outline: none; border-color: var(--dsw-alias-state-business-primary, #3370ff); box-shadow: 0 0 0 2px color-mix(in srgb, var(--dsw-alias-state-business-primary, #3370ff) 15%, transparent); }
|
|
414
|
+
.dim-feishuGroupSelect:disabled { cursor: not-allowed; opacity: .55; }
|
|
415
|
+
.dim-feishuGroupHelp { margin: -2px 0 0; color: var(--dsw-alias-label-tertiary, #8f959e); font-size: 12px; line-height: 19px; }
|
|
416
|
+
.dim-feishuGroupPermissionAction { display: flex; justify-content: flex-start; }
|
|
417
|
+
.dim-feishuGroupPermissionAction .dim-deliveryButton { color: var(--dsw-alias-state-business-primary, #3370ff); border-color: color-mix(in srgb, var(--dsw-alias-state-business-primary, #3370ff) 30%, var(--dsw-alias-border-l2, #dfe1e5)); }
|
|
418
|
+
.dim-feishuGroupError, .dim-feishuGroupRefreshError { margin: 0; padding: 9px 11px; border-radius: 8px; font-size: 12px; line-height: 18px; }
|
|
419
|
+
.dim-feishuGroupError, .dim-feishuGroupRefreshError { color: var(--dsw-alias-state-error-primary, #d54941); background: color-mix(in srgb, var(--dsw-alias-state-error-primary, #d54941) 7%, var(--dsw-alias-bg-layer-1, #fff)); }
|
|
420
|
+
.dim-feishuGroupAuthorization { min-width: 0; display: grid; grid-template-columns: 184px minmax(0, 1fr); align-items: start; gap: 24px; padding: 18px; border: 1px solid color-mix(in srgb, var(--dsw-alias-state-business-primary, #3370ff) 30%, var(--dsw-alias-border-l2, #dfe1e5)); border-radius: 12px; background: color-mix(in srgb, var(--dsw-alias-state-business-primary, #3370ff) 2.5%, var(--dsw-alias-bg-layer-3, #fff)); }
|
|
421
|
+
.dim-feishuGroupAuthorizationState { min-height: 126px; grid-template-columns: 32px minmax(0, 1fr); align-items: center; }
|
|
422
|
+
.dim-feishuGroupAuthorizationState h3, .dim-feishuGroupAuthorizationError h3, .dim-feishuGroupAuthorizationCopy h3 { margin: 0; color: var(--dsw-alias-label-primary, #1f2329); font-size: 16px; line-height: 24px; font-weight: 650; }
|
|
423
|
+
.dim-feishuGroupAuthorizationState p, .dim-feishuGroupAuthorizationError p, .dim-feishuGroupAuthorizationCopy > p { margin: 5px 0 0; color: var(--dsw-alias-label-secondary, #646a73); font-size: 12px; line-height: 19px; }
|
|
424
|
+
.dim-feishuGroupSpinner { width: 24px; height: 24px; border: 3px solid var(--dsw-alias-border-l2, #dfe1e5); border-top-color: var(--dsw-alias-state-business-primary, #3370ff); border-radius: 50%; animation: dim-spin .8s linear infinite; }
|
|
425
|
+
.dim-feishuGroupAuthorizationError { grid-template-columns: minmax(0, 1fr) max-content; align-items: center; border-color: color-mix(in srgb, var(--dsw-alias-state-error-primary, #d54941) 28%, var(--dsw-alias-border-l2, #dfe1e5)); background: color-mix(in srgb, var(--dsw-alias-state-error-primary, #d54941) 4%, var(--dsw-alias-bg-layer-3, #fff)); }
|
|
426
|
+
.dim-feishuGroupAuthorizationError code { display: inline-block; margin-top: 6px; color: var(--dsw-alias-label-tertiary, #8f959e); font: 11px/17px ui-monospace, SFMono-Regular, Menlo, monospace; }
|
|
427
|
+
.dim-feishuGroupQrColumn { min-width: 0; }
|
|
428
|
+
.dim-feishuGroupQrFrame { position: relative; width: 176px; height: 176px; display: grid; place-items: center; padding: 10px; border: 1px solid var(--dsw-alias-border-l2, #dfe1e5); border-radius: 11px; background: #fff; box-shadow: 0 6px 18px rgb(31 35 41 / 7%); }
|
|
429
|
+
.dim-feishuGroupQrFrame img { width: 100%; height: 100%; display: block; object-fit: contain; }
|
|
430
|
+
.dim-feishuGroupQrFallback { width: 100%; height: 100%; display: grid; place-content: center; justify-items: center; gap: 7px; border-radius: 7px; color: var(--dsw-alias-state-business-primary, #3370ff); background: #f7f9ff; text-align: center; }
|
|
431
|
+
.dim-feishuGroupQrFallback span { color: var(--dsw-alias-label-secondary, #646a73); font-size: 11px; line-height: 17px; }
|
|
432
|
+
.dim-feishuGroupQrExpired { position: absolute; inset: 10px; display: grid; place-content: center; gap: 2px; border-radius: 8px; color: var(--dsw-alias-label-primary, #1f2329); background: rgb(255 255 255 / 94%); backdrop-filter: blur(3px); text-align: center; }
|
|
433
|
+
.dim-feishuGroupQrExpired span { font-size: 13px; line-height: 20px; font-weight: 650; }
|
|
434
|
+
.dim-feishuGroupQrExpired small { color: var(--dsw-alias-label-secondary, #646a73); font-size: 11px; line-height: 17px; }
|
|
435
|
+
.dim-feishuGroupCountdown { width: 176px; display: grid; grid-template-columns: minmax(0, 1fr) max-content; gap: 5px 10px; margin-top: 9px; color: var(--dsw-alias-label-tertiary, #8f959e); font-size: 11px; line-height: 17px; font-variant-numeric: tabular-nums; }
|
|
436
|
+
.dim-feishuGroupCountdown strong { color: var(--dsw-alias-label-secondary, #646a73); font-weight: 600; }
|
|
437
|
+
.dim-feishuGroupProgress { grid-column: 1 / -1; height: 3px; overflow: hidden; border-radius: 99px; background: var(--dsw-alias-bg-module-platform, #f2f3f5); }
|
|
438
|
+
.dim-feishuGroupProgress > span { display: block; height: 100%; border-radius: inherit; background: var(--dsw-alias-state-business-primary, #3370ff); transition: width 1s linear; }
|
|
439
|
+
.dim-feishuGroupAuthorizationCopy { min-width: 0; }
|
|
440
|
+
.dim-feishuGroupAuthorizationEyebrow { display: block; margin-bottom: 7px; color: var(--dsw-alias-label-secondary, #646a73); font-size: 11px; line-height: 17px; font-weight: 600; }
|
|
441
|
+
.dim-feishuGroupAuthorizationCopy ol { display: grid; gap: 6px; margin: 13px 0 0; padding-left: 18px; color: var(--dsw-alias-label-secondary, #646a73); font-size: 11px; line-height: 18px; }
|
|
442
|
+
.dim-feishuGroupAuthorizationActions { display: flex; align-items: center; justify-content: flex-start; flex-wrap: wrap; gap: 8px; margin-top: 14px; }
|
|
443
|
+
.dim-feishuGroupAuthorizationLink { text-decoration: none; }
|
|
394
444
|
.dim-deliveryDocsLink { min-height: 30px; flex: none; display: inline-flex; align-items: center; gap: 4px; padding: 5px 8px; border-radius: 7px; color: var(--dsw-alias-state-business-primary, #3370ff); font-size: 12px; line-height: 18px; text-decoration: none; white-space: nowrap; }
|
|
395
445
|
.dim-deliveryDocsLink:hover { background: color-mix(in srgb, var(--dsw-alias-state-business-primary, #3370ff) 8%, transparent); }
|
|
396
446
|
.dim-deliveryDocsLink:focus-visible { outline: 2px solid var(--dsw-alias-state-business-primary, #3370ff); outline-offset: 2px; }
|
|
@@ -420,6 +470,12 @@ const CSS = String.raw`
|
|
|
420
470
|
.dim-targetTitle span { flex: none; padding: 1px 6px; border-radius: 5px; color: var(--dsw-alias-state-business-primary, #3370ff); background: color-mix(in srgb, var(--dsw-alias-state-business-primary, #3370ff) 9%, transparent); font-size: 10px; line-height: 16px; }
|
|
421
471
|
.dim-targetSummary code { display: block; overflow: hidden; margin-top: 3px; color: var(--dsw-alias-label-secondary, #646a73); font: 11px/17px ui-monospace, SFMono-Regular, Menlo, monospace; text-overflow: ellipsis; white-space: nowrap; }
|
|
422
472
|
.dim-targetActions { display: flex; align-items: center; justify-content: flex-end; gap: 6px; flex-wrap: wrap; }
|
|
473
|
+
.dim-targetSessionSync { grid-column: 1 / -1; min-width: 0; display: flex; align-items: center; justify-content: space-between; gap: 12px; padding-top: 9px; border-top: 1px solid var(--dsw-alias-border-l1, #eef0f3); cursor: pointer; }
|
|
474
|
+
.dim-targetSessionSyncCopy { min-width: 0; display: grid; gap: 1px; }
|
|
475
|
+
.dim-targetSessionSyncCopy strong { color: var(--dsw-alias-label-primary, #1f2329); font-size: 12px; line-height: 18px; font-weight: 600; }
|
|
476
|
+
.dim-targetSessionSyncCopy small { color: var(--dsw-alias-label-secondary, #646a73); font-size: 11px; line-height: 17px; }
|
|
477
|
+
.dim-targetSessionSync input { width: 16px; height: 16px; flex: none; margin: 0; accent-color: var(--dsw-alias-state-business-primary, #3370ff); }
|
|
478
|
+
.dim-targetSessionSync:has(input:disabled) { cursor: default; }
|
|
423
479
|
.dim-targetFeedback { grid-column: 1 / -1; margin: 0; color: var(--dsw-alias-label-secondary, #646a73); font-size: 11px; line-height: 17px; }
|
|
424
480
|
.dim-targetFeedback[data-tone="success"] { color: var(--dsw-alias-state-success-primary, #20a162); }
|
|
425
481
|
.dim-targetFeedback[data-tone="error"], .dim-targetFormError { color: var(--dsw-alias-state-error-primary, #d54941); }
|
|
@@ -543,6 +599,10 @@ const CSS = String.raw`
|
|
|
543
599
|
.dim-accessControls { grid-template-columns: minmax(0, 1fr); }
|
|
544
600
|
.dim-accessUserRow { grid-template-columns: minmax(0, 1fr); }
|
|
545
601
|
.dim-accessDeleteUser { justify-self: start; }
|
|
602
|
+
.dim-feishuGroupAuthorization { grid-template-columns: minmax(0, 1fr); justify-items: center; }
|
|
603
|
+
.dim-feishuGroupAuthorizationCopy { width: 100%; }
|
|
604
|
+
.dim-feishuGroupAuthorizationState { grid-template-columns: 32px minmax(0, 1fr); justify-items: stretch; }
|
|
605
|
+
.dim-feishuGroupAuthorizationError { justify-items: stretch; }
|
|
546
606
|
}
|
|
547
607
|
@media (max-width: 840px) {
|
|
548
608
|
.dim-title { align-items: flex-start; }
|
|
@@ -572,6 +632,7 @@ const CSS = String.raw`
|
|
|
572
632
|
.dim-deliveryBotId > span { grid-column: 1 / -1; }
|
|
573
633
|
.dim-targetActions .dim-deliveryButton { flex: 1 1 auto; }
|
|
574
634
|
.dim-accessActions .dim-deliveryButton { width: 100%; }
|
|
635
|
+
.dim-feishuGroupAuthorizationActions .dim-deliveryButton { flex: 1 1 auto; }
|
|
575
636
|
.dim-directoryPickerBackdrop { padding: 10px; }
|
|
576
637
|
.dim-directoryPicker { height: calc(100vh - 20px); min-height: 0; border-radius: 14px; }
|
|
577
638
|
.dim-directoryPickerHeader { padding: 18px 17px 14px; }
|
|
@@ -584,6 +645,7 @@ const CSS = String.raw`
|
|
|
584
645
|
@media (prefers-reduced-motion: reduce) {
|
|
585
646
|
.dim-page * { transition-duration: .01ms !important; }
|
|
586
647
|
.dim-directoryPickerSpinner { animation-duration: 1.8s; }
|
|
648
|
+
.dim-collapsibleBody, .dim-collapsibleChevron { transition: none !important; }
|
|
587
649
|
}
|
|
588
650
|
`;
|
|
589
651
|
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
observeBotWorkspaceRemovals,
|
|
16
16
|
} from '../../../../src/channels/shared/bot-workspace-store.mjs';
|
|
17
17
|
import { listAgentPresetCatalog } from '../../../../src/channels/shared/agent-preset.mjs';
|
|
18
|
+
import { listModelCatalog } from '../../../../src/channels/shared/model-setting.mjs';
|
|
18
19
|
import { createDeliveryAdapter } from '../../delivery-adapter.mjs';
|
|
19
20
|
import { createConnectionSupervisor } from './connection-supervisor.mjs';
|
|
20
21
|
import { createHarnessCommandExecutor } from '../../harness-command-executor.mjs';
|
|
@@ -111,6 +112,7 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|
|
111
112
|
...(sessionMaintenanceExecutor ? { sessionMaintenanceExecutor } : {}),
|
|
112
113
|
...(fileIngressExecutor ? { fileIngressExecutor } : {}),
|
|
113
114
|
});
|
|
115
|
+
const modelCatalog = () => listModelCatalog(harness);
|
|
114
116
|
const coreController = new Controller({
|
|
115
117
|
deviceAuth,
|
|
116
118
|
credentials: ctx.credentials,
|
|
@@ -163,6 +165,7 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|
|
163
165
|
workspaces,
|
|
164
166
|
stateFor,
|
|
165
167
|
agentPresetCatalog,
|
|
168
|
+
modelCatalog,
|
|
166
169
|
});
|
|
167
170
|
const supervisor = createSupervisor({
|
|
168
171
|
controller,
|
|
@@ -4,6 +4,7 @@ import { SET_ACCESS_POLICY_ENDPOINT, validAccessPolicyPayload } from '../shared/
|
|
|
4
4
|
import { resolveRpcAuthority } from '../../rpc-authority.mjs';
|
|
5
5
|
import { publicWorkspaceError, SET_WORKSPACE_ENDPOINT, validWorkspacePayload } from '../shared/workspace-rpc.mjs';
|
|
6
6
|
import { SET_AGENT_PRESET_ENDPOINT, validAgentPresetPayload } from '../shared/agent-preset-rpc.mjs';
|
|
7
|
+
import { SET_MODEL_ENDPOINT, validModelPayload } from '../shared/model-setting-rpc.mjs';
|
|
7
8
|
import {
|
|
8
9
|
connectionTestTargetUnavailable,
|
|
9
10
|
publicConnectionTestResult,
|
|
@@ -19,6 +20,7 @@ export const DINGTALK_ENDPOINTS = Object.freeze({
|
|
|
19
20
|
reconnectBot: 'bot.reconnect',
|
|
20
21
|
deleteBot: 'bot.delete',
|
|
21
22
|
setWorkspace: SET_WORKSPACE_ENDPOINT,
|
|
23
|
+
setModel: SET_MODEL_ENDPOINT,
|
|
22
24
|
setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
|
|
23
25
|
setContextEnhancement: SET_CONTEXT_ENHANCEMENT_ENDPOINT,
|
|
24
26
|
setAccessPolicy: SET_ACCESS_POLICY_ENDPOINT,
|
|
@@ -94,6 +96,9 @@ function payloadFailure(endpoint, payload) {
|
|
|
94
96
|
return validWorkspacePayload(payload)
|
|
95
97
|
? null : '请输入工作区绝对路径。';
|
|
96
98
|
}
|
|
99
|
+
if (endpoint === DINGTALK_ENDPOINTS.setModel) {
|
|
100
|
+
return validModelPayload(payload) ? null : '请选择有效模型。';
|
|
101
|
+
}
|
|
97
102
|
if (endpoint === DINGTALK_ENDPOINTS.setAgentPreset) {
|
|
98
103
|
return validAgentPresetPayload(payload)
|
|
99
104
|
? null : '请选择 Agent Preset。';
|
|
@@ -284,6 +289,12 @@ export function createDingtalkRpcHandler(controller, { encodeQr = qrDataUrl } =
|
|
|
284
289
|
await controller.updateWorkspace(payload.botId, payload.workspace),
|
|
285
290
|
cachedEncode,
|
|
286
291
|
);
|
|
292
|
+
} else if (endpoint === DINGTALK_ENDPOINTS.setModel) {
|
|
293
|
+
if (typeof controller.updateModel !== 'function') throw new Error('Model update is unavailable');
|
|
294
|
+
value = await publicStatus(
|
|
295
|
+
await controller.updateModel(payload.botId, payload.model),
|
|
296
|
+
cachedEncode,
|
|
297
|
+
);
|
|
287
298
|
} else if (endpoint === DINGTALK_ENDPOINTS.setContextEnhancement) {
|
|
288
299
|
if (typeof controller.updateContextEnhancement !== 'function') throw new Error('Context enhancement update is unavailable');
|
|
289
300
|
value = await controller.updateContextEnhancement(
|
|
@@ -27,6 +27,7 @@ import {
|
|
|
27
27
|
observeBotWorkspaceRemovals,
|
|
28
28
|
} from '../../../../src/channels/shared/bot-workspace-store.mjs';
|
|
29
29
|
import { listAgentPresetCatalog } from '../../../../src/channels/shared/agent-preset.mjs';
|
|
30
|
+
import { listModelCatalog } from '../../../../src/channels/shared/model-setting.mjs';
|
|
30
31
|
import { createDeliveryAdapter } from '../../delivery-adapter.mjs';
|
|
31
32
|
import {
|
|
32
33
|
accessPolicyProvider,
|
|
@@ -191,6 +192,7 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|
|
191
192
|
const proxyEnv = internals.proxyEnv ?? process.env;
|
|
192
193
|
const wsAgent = createFeishuWebSocketAgent(proxyEnv, internals.createProxyAgent);
|
|
193
194
|
|
|
195
|
+
const modelCatalog = () => listModelCatalog(harness);
|
|
194
196
|
const coreController = new Controller({
|
|
195
197
|
registerApp: (options) => lark.registerApp(options),
|
|
196
198
|
verifyApp,
|
|
@@ -247,6 +249,7 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|
|
247
249
|
workspaces,
|
|
248
250
|
stateFor: stateForBotId,
|
|
249
251
|
agentPresetCatalog,
|
|
252
|
+
modelCatalog,
|
|
250
253
|
});
|
|
251
254
|
|
|
252
255
|
const supervisor = createSupervisor({
|