@xmanrui/dsh-im 4.15.0 → 4.16.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 +2 -1
- package/README.md +9 -5
- package/lib/client.js +1729 -614
- package/lib/index.js +303 -293
- package/package.json +6 -2
- package/plugin-src/client/access-policy-settings.js +6 -0
- package/plugin-src/client/channels/feishu/index.js +15 -3
- package/plugin-src/client/channels/wecom-app/api.js +124 -0
- package/plugin-src/client/channels/wecom-app/index.js +612 -0
- package/plugin-src/client/channels/wecom-app/styles.js +25 -0
- package/plugin-src/client/delivery-settings.js +7 -0
- package/plugin-src/client/i18n.js +63 -0
- package/plugin-src/client/index.js +18 -0
- package/plugin-src/client/model-setting.js +135 -69
- package/plugin-src/client/styles.js +22 -7
- package/plugin-src/host/channels/dingtalk/index.mjs +8 -15
- package/plugin-src/host/channels/discord/index.mjs +8 -10
- package/plugin-src/host/channels/feishu/index.mjs +8 -15
- package/plugin-src/host/channels/office/index.mjs +9 -5
- package/plugin-src/host/channels/qq/index.mjs +8 -15
- package/plugin-src/host/channels/shared/access-policy-production.mjs +1 -1
- package/plugin-src/host/channels/shared/model-setting-rpc.mjs +1 -2
- package/plugin-src/host/channels/shared/startup-error.mjs +53 -0
- package/plugin-src/host/channels/shared/startup.mjs +47 -0
- package/plugin-src/host/channels/shared/workspace-rpc.mjs +1 -0
- package/plugin-src/host/channels/slack/index.mjs +8 -10
- package/plugin-src/host/channels/telegram/index.mjs +8 -10
- package/plugin-src/host/channels/wecom/index.mjs +8 -15
- package/plugin-src/host/channels/wecom/rpc.mjs +6 -1
- package/plugin-src/host/channels/wecom-app/index.mjs +33 -0
- package/plugin-src/host/channels/wecom-app/production.mjs +217 -0
- package/plugin-src/host/channels/wecom-app/rpc.mjs +225 -0
- package/plugin-src/host/channels/weixin/index.mjs +8 -15
- package/plugin-src/host/channels/whatsapp/index.mjs +8 -15
- package/plugin-src/host/delivery-adapter.mjs +4 -0
- package/plugin-src/host/delivery-suggestions.mjs +4 -0
- package/plugin-src/host/index.mjs +7 -2
- package/scripts/verify-model-setting.mjs +54 -0
- package/src/channels/feishu/bridge.mjs +295 -9
- package/src/channels/feishu/feishu-cards.mjs +38 -0
- package/src/channels/feishu/feishu-channel.mjs +88 -20
- package/src/channels/shared/bot-workspace-store.mjs +11 -3
- package/src/channels/shared/command-catalog.mjs +102 -0
- package/src/channels/shared/i18n-en/feishu.mjs +12 -0
- package/src/channels/shared/i18n-en/shared-a.mjs +3 -0
- package/src/channels/shared/i18n-en/shared-c.mjs +9 -0
- package/src/channels/shared/i18n-en/wecom-app.mjs +33 -0
- package/src/channels/shared/i18n-en.mjs +2 -0
- package/src/channels/shared/model-setting.mjs +43 -8
- package/src/channels/shared/text-harness-bridge.mjs +2 -26
- package/src/channels/telegram/telegram-api.mjs +18 -6
- package/src/channels/telegram/telegram-runtime.mjs +34 -32
- package/src/channels/wecom/send-error.mjs +31 -0
- package/src/channels/wecom/wecom-bridge.mjs +59 -17
- package/src/channels/wecom-app/callback-server.mjs +471 -0
- package/src/channels/wecom-app/config-store.mjs +240 -0
- package/src/channels/wecom-app/harness-client.mjs +11 -0
- package/src/channels/wecom-app/state-store.mjs +107 -0
- package/src/channels/wecom-app/wecom-app-api.mjs +432 -0
- package/src/channels/wecom-app/wecom-app-bridge.mjs +1059 -0
- package/src/channels/wecom-app/wecom-app-controller.mjs +440 -0
- package/src/channels/wecom-app/wecom-app-runtime.mjs +221 -0
|
@@ -221,6 +221,7 @@ const EN = Object.freeze({
|
|
|
221
221
|
'开启(自动开启独立飞书话题)': 'On (auto-open a dedicated Feishu topic)',
|
|
222
222
|
'开启后,群聊中向机器人提问会自动开启独立飞书话题,回复落在话题内;每个话题是 dsh 会话列表里一条独立会话,上下文互不串。私聊不受影响。': 'When enabled, questions to the bot in a group auto-open a dedicated Feishu topic and replies stay inside it. Each topic is an independent conversation in dsh with its own context. Private chats are unaffected.',
|
|
223
223
|
'分步直推': 'Step push',
|
|
224
|
+
'查看分步直推说明': 'View step push help',
|
|
224
225
|
'开启后逐步推送工具调用与过程说明': 'Push tool calls and process notes step by step',
|
|
225
226
|
'关闭(保持流式卡模式)': 'Off (keep the streaming card)',
|
|
226
227
|
'开启(逐步推送工具调用与过程说明)': 'On (push tool calls and process notes step by step)',
|
|
@@ -359,6 +360,14 @@ const EN = Object.freeze({
|
|
|
359
360
|
'Agent Preset 无效。': 'The Agent Preset is invalid.',
|
|
360
361
|
'Agent Preset 不存在或不可用。': 'The Agent Preset does not exist or is unavailable.',
|
|
361
362
|
'模型': 'Model',
|
|
363
|
+
'模型与思考强度': 'Model and reasoning effort',
|
|
364
|
+
'思考强度': 'Reasoning effort',
|
|
365
|
+
'跟随模型默认': 'Model default',
|
|
366
|
+
'先选择模型,再设置思考强度。': 'Select a model to configure its reasoning effort.',
|
|
367
|
+
'该模型未提供可调节的思考强度。': 'This model does not provide adjustable reasoning effort.',
|
|
368
|
+
'已保存的思考强度已不可用,请选择其他强度或恢复默认。': 'The saved reasoning effort is unavailable. Choose another effort or restore the default.',
|
|
369
|
+
'使用模型或服务提供方的默认思考强度。': 'Use the default reasoning effort from the model or provider.',
|
|
370
|
+
'当前模型不支持所选思考强度,请重新选择。': 'This model does not support the selected reasoning effort. Choose another effort.',
|
|
362
371
|
'查看模型设置说明': 'View model setting help',
|
|
363
372
|
'跟随默认模型': 'Follow the default model',
|
|
364
373
|
'模型修改失败,请重试。': 'Could not update the model. Try again.',
|
|
@@ -891,6 +900,60 @@ const EN = Object.freeze({
|
|
|
891
900
|
'确认清理当前已过期的附件?': 'Clean up the attachments that are currently expired?',
|
|
892
901
|
'确认清理': 'Confirm cleanup',
|
|
893
902
|
'正在清理…': 'Cleaning up…',
|
|
903
|
+
'正在读取企业微信应用状态…': 'Loading WeCom app status…',
|
|
904
|
+
'添加企业微信应用': 'Add WeCom app',
|
|
905
|
+
'收起表单': 'Collapse form',
|
|
906
|
+
'添加企业微信自建应用': 'Add a WeCom self-built app',
|
|
907
|
+
'正在填写': 'Filling in details',
|
|
908
|
+
'尚未绑定企业微信应用': 'No WeCom app connected yet',
|
|
909
|
+
'在企业微信中创建自建应用,即可在微信里使用': 'Create a self-built app in WeCom to use it inside WeChat',
|
|
910
|
+
'在应用管理后台创建自建应用,开启「接收消息」并填入下面的参数。成员的微信关注该企业的微信插件后,就能在微信中与本应用双向对话。': 'Create the self-built app in the WeCom admin console, enable receive-messages, and fill in the parameters below. Once members follow the enterprise WeChat plugin, they can chat with this app directly inside WeChat.',
|
|
911
|
+
'填写应用参数': 'Fill in app parameters',
|
|
912
|
+
'企业 ID(CorpID)': 'Corp ID',
|
|
913
|
+
'例如 ww1234567890abcdef': 'for example ww1234567890abcdef',
|
|
914
|
+
'应用 AgentId': 'AgentId',
|
|
915
|
+
'例如 1000002': 'for example 1000002',
|
|
916
|
+
'应用 Secret': 'App Secret',
|
|
917
|
+
'应用详情页的 Secret': 'The Secret from the app details page',
|
|
918
|
+
'回调 Token': 'Callback Token',
|
|
919
|
+
'接收消息 → API 接收中的 Token': 'The Token from Receive messages → API receive',
|
|
920
|
+
'43 位字母或数字': '43 alphanumeric characters',
|
|
921
|
+
'代理地址(可选)': 'API proxy (optional)',
|
|
922
|
+
'留空直连 qyapi.weixin.qq.com': 'leave empty to reach qyapi.weixin.qq.com directly',
|
|
923
|
+
'公网回调基址(可选)': 'Public callback base URL (optional)',
|
|
924
|
+
'例如 https://im.example.com': 'for example https://im.example.com',
|
|
925
|
+
'流式回复:开': 'Streaming replies: on',
|
|
926
|
+
'流式回复:关': 'Streaming replies: off',
|
|
927
|
+
'企业微信客户端实时出字;微信端不支持时自动改为整段发送': 'Streams replies in the WeCom client; falls back to segmented text messages when the WeChat side does not support it',
|
|
928
|
+
'绑定企业微信自建应用': 'Bind a WeCom self-built app',
|
|
929
|
+
'回调 URL(填入企业微信后台「接收消息 → 设置API接收」)': 'Callback URL (paste into the WeCom console under Receive messages → API receive)',
|
|
930
|
+
'未配置公网回调基址:请在本页「设置」中填写回调基址,或手动拼接 回调基址 + 路径': 'No public callback base URL configured: fill it in under Settings on this page, or manually combine the base URL and the path',
|
|
931
|
+
'重置密钥': 'Reset secret',
|
|
932
|
+
'重置中…': 'Resetting…',
|
|
933
|
+
'重置密钥后回调 URL 会变化,需要同步更新企业微信后台。若公网反代未就绪,可临时使用 http://服务器IP:端口 形式的回调地址。': 'Resetting the secret changes the callback URL; update the WeCom console accordingly. If the public reverse proxy is not ready yet, a http://server-ip:port callback address works temporarily.',
|
|
934
|
+
'保存设置': 'Save settings',
|
|
935
|
+
'这会删除本机保存的应用凭据、回调配置及会话映射。企业微信后台的应用不会被删除。': 'This removes the locally stored app credentials, callback configuration, and session mappings. The app in the WeCom console is not deleted.',
|
|
936
|
+
'保留应用': 'Keep app',
|
|
937
|
+
'已绑定的企业微信应用': 'Connected WeCom apps',
|
|
938
|
+
'HTTP 回调通道': 'HTTP callback channel',
|
|
939
|
+
'企业微信应用': 'WeCom app',
|
|
940
|
+
'企业 ID 已保存': 'Corp ID saved securely',
|
|
941
|
+
'企业微信应用回调通道就绪': 'WeCom app callback channel is ready',
|
|
942
|
+
'企业微信应用连接尚未就绪': 'WeCom app connection is not ready',
|
|
943
|
+
'企业微信应用仍未就绪,插件会继续自动重试。': 'The WeCom app is still not ready; the plugin keeps retrying automatically.',
|
|
944
|
+
'连接检查完成。': 'Connection check finished.',
|
|
945
|
+
'连接检查完成,测试消息已发送。': 'Connection check finished; the test message was sent.',
|
|
946
|
+
'连接检查完成。应用尚未收到可用于测试的私聊消息。': 'Connection check finished. The app has not received a private chat usable for testing yet.',
|
|
947
|
+
'企业微信应用已绑定。请把回调 URL 填入企业微信后台并保存。': 'The WeCom app is bound. Paste the callback URL into the WeCom console and save.',
|
|
948
|
+
'回调密钥已重置,请把新的回调 URL 更新到企业微信后台。': 'The callback secret was reset; update the new callback URL in the WeCom console.',
|
|
949
|
+
'回调密钥重置失败,请稍后重试。': 'The callback secret reset failed; try again later.',
|
|
950
|
+
'无法读取企业微信应用状态': 'Could not load WeCom app status',
|
|
951
|
+
'企业微信应用设置': 'WeCom app settings',
|
|
952
|
+
'企业微信应用设置页缺少 RPC 连接': 'The WeCom app settings page is missing the RPC connection',
|
|
953
|
+
'企业微信应用操作失败': 'The WeCom app operation failed',
|
|
954
|
+
'企业微信应用操作失败,请稍后重试': 'The WeCom app operation failed; try again later',
|
|
955
|
+
'企业微信应用服务返回了无法识别的响应': 'The WeCom app service returned an unrecognized response',
|
|
956
|
+
'企业微信应用服务没有返回有效的机器人列表': 'The WeCom app service did not return a valid bot list',
|
|
894
957
|
});
|
|
895
958
|
|
|
896
959
|
export const en = EN;
|
|
@@ -36,6 +36,9 @@ import { installTelegramStyles } from './channels/telegram/styles.js';
|
|
|
36
36
|
import { WECOM_RPC_CHANNEL } from './channels/wecom/api.js';
|
|
37
37
|
import { WecomSettingsTab } from './channels/wecom/index.js';
|
|
38
38
|
import { installWecomStyles } from './channels/wecom/styles.js';
|
|
39
|
+
import { WECOM_APP_RPC_CHANNEL } from './channels/wecom-app/api.js';
|
|
40
|
+
import { WecomAppSettingsTab } from './channels/wecom-app/index.js';
|
|
41
|
+
import { installWecomAppStyles } from './channels/wecom-app/styles.js';
|
|
39
42
|
import { WeixinSettingsTab } from './channels/weixin/index.js';
|
|
40
43
|
import { WEIXIN_RPC_CHANNEL } from './channels/weixin/api.js';
|
|
41
44
|
import { installWeixinStyles } from './channels/weixin/styles.js';
|
|
@@ -87,6 +90,7 @@ const CHANNELS = Object.freeze([
|
|
|
87
90
|
{ id: 'telegram', label: 'Telegram' },
|
|
88
91
|
{ id: 'discord', label: 'Discord' },
|
|
89
92
|
{ id: 'whatsapp', label: 'WhatsApp' },
|
|
93
|
+
{ id: 'wecomApp', label: '企业微信应用', note: '(实验功能)' },
|
|
90
94
|
{ id: 'office', label: 'AI Office', note: '(实验功能)' },
|
|
91
95
|
]);
|
|
92
96
|
|
|
@@ -113,6 +117,10 @@ function WecomLogo() {
|
|
|
113
117
|
return h('span', { className: 'dim-logo dim-logoWecom', 'aria-hidden': 'true' }, h(WecomLogoGlyph));
|
|
114
118
|
}
|
|
115
119
|
|
|
120
|
+
function WecomAppLogo() {
|
|
121
|
+
return h('span', { className: 'dim-logo dim-logoWecomApp', 'aria-hidden': 'true' }, h(WecomLogoGlyph));
|
|
122
|
+
}
|
|
123
|
+
|
|
116
124
|
function TelegramLogo() {
|
|
117
125
|
return h('span', { className: 'dim-logo dim-logoTelegram', 'aria-hidden': 'true' },
|
|
118
126
|
h(TelegramLogoGlyph));
|
|
@@ -143,6 +151,7 @@ function ChannelLogo({ channel }) {
|
|
|
143
151
|
if (channel === 'feishu') return h(FeishuLogo);
|
|
144
152
|
if (channel === 'dingtalk') return h(DingtalkLogo);
|
|
145
153
|
if (channel === 'wecom') return h(WecomLogo);
|
|
154
|
+
if (channel === 'wecomApp') return h(WecomAppLogo);
|
|
146
155
|
if (channel === 'qq') return h(QqLogo);
|
|
147
156
|
if (channel === 'slack') return h(SlackLogo);
|
|
148
157
|
if (channel === 'telegram') return h(TelegramLogo);
|
|
@@ -175,6 +184,7 @@ export function IMSettingsTab({
|
|
|
175
184
|
slackRpcCall,
|
|
176
185
|
telegramRpcCall,
|
|
177
186
|
wecomRpcCall,
|
|
187
|
+
wecomAppRpcCall,
|
|
178
188
|
weixinRpcCall,
|
|
179
189
|
whatsappRpcCall,
|
|
180
190
|
officeRpcCall,
|
|
@@ -213,6 +223,7 @@ export function IMSettingsTab({
|
|
|
213
223
|
slackRpcCall,
|
|
214
224
|
telegramRpcCall,
|
|
215
225
|
wecomRpcCall,
|
|
226
|
+
wecomAppRpcCall,
|
|
216
227
|
weixinRpcCall,
|
|
217
228
|
whatsappRpcCall,
|
|
218
229
|
officeRpcCall,
|
|
@@ -236,6 +247,7 @@ export function IMSettingsTab({
|
|
|
236
247
|
telegramRpcCall,
|
|
237
248
|
updateRpcCall,
|
|
238
249
|
wecomRpcCall,
|
|
250
|
+
wecomAppRpcCall,
|
|
239
251
|
weixinRpcCall,
|
|
240
252
|
whatsappRpcCall,
|
|
241
253
|
]);
|
|
@@ -344,6 +356,8 @@ export function IMSettingsTab({
|
|
|
344
356
|
? h(DingtalkSettingsTab, { rpcCall: rpcCalls.dingtalkRpcCall })
|
|
345
357
|
: active.id === 'wecom'
|
|
346
358
|
? h(WecomSettingsTab, { rpcCall: rpcCalls.wecomRpcCall })
|
|
359
|
+
: active.id === 'wecomApp'
|
|
360
|
+
? h(WecomAppSettingsTab, { rpcCall: rpcCalls.wecomAppRpcCall })
|
|
347
361
|
: active.id === 'qq'
|
|
348
362
|
? h(QqSettingsTab, { rpcCall: rpcCalls.qqRpcCall })
|
|
349
363
|
: active.id === 'slack'
|
|
@@ -374,6 +388,7 @@ export function apply(ctx) {
|
|
|
374
388
|
installFeishuStyles(),
|
|
375
389
|
installWeixinStyles(),
|
|
376
390
|
installWecomStyles(),
|
|
391
|
+
installWecomAppStyles(),
|
|
377
392
|
installQqStyles(),
|
|
378
393
|
installSlackStyles(),
|
|
379
394
|
installTelegramStyles(),
|
|
@@ -397,6 +412,8 @@ export function apply(ctx) {
|
|
|
397
412
|
ctx.connection.rpc.call(QQ_RPC_CHANNEL, endpoint, payload, signal);
|
|
398
413
|
const wecomRpcCall = (endpoint, payload, signal) =>
|
|
399
414
|
ctx.connection.rpc.call(WECOM_RPC_CHANNEL, endpoint, payload, signal);
|
|
415
|
+
const wecomAppRpcCall = (endpoint, payload, signal) =>
|
|
416
|
+
ctx.connection.rpc.call(WECOM_APP_RPC_CHANNEL, endpoint, payload, signal);
|
|
400
417
|
const telegramRpcCall = (endpoint, payload, signal) =>
|
|
401
418
|
ctx.connection.rpc.call(TELEGRAM_RPC_CHANNEL, endpoint, payload, signal);
|
|
402
419
|
const discordRpcCall = (endpoint, payload, signal) =>
|
|
@@ -433,6 +450,7 @@ export function apply(ctx) {
|
|
|
433
450
|
slackRpcCall,
|
|
434
451
|
telegramRpcCall,
|
|
435
452
|
wecomRpcCall,
|
|
453
|
+
wecomAppRpcCall,
|
|
436
454
|
weixinRpcCall,
|
|
437
455
|
whatsappRpcCall,
|
|
438
456
|
officeRpcCall,
|
|
@@ -2,103 +2,169 @@ import * as React from 'react';
|
|
|
2
2
|
|
|
3
3
|
import {
|
|
4
4
|
EMPTY_MODEL_CATALOG,
|
|
5
|
+
modelCatalogEntry,
|
|
5
6
|
modelSelectionId,
|
|
6
7
|
normalizeModelCatalog,
|
|
7
8
|
normalizeModelSelection,
|
|
9
|
+
sameModelSelection,
|
|
8
10
|
} from '../../src/channels/shared/model-setting.mjs';
|
|
9
|
-
import { h } from './i18n.js';
|
|
11
|
+
import { h, localizeText } from './i18n.js';
|
|
10
12
|
|
|
11
13
|
export const SET_MODEL_ENDPOINT = 'bot.model.set';
|
|
12
14
|
export { EMPTY_MODEL_CATALOG, normalizeModelCatalog, normalizeModelSelection };
|
|
13
15
|
|
|
14
16
|
export const ModelCatalogContext = React.createContext(EMPTY_MODEL_CATALOG);
|
|
15
17
|
|
|
16
|
-
function
|
|
17
|
-
return
|
|
18
|
+
function chevron(open = false) {
|
|
19
|
+
return h('svg', { width: 14, height: 14, viewBox: '0 0 16 16', 'aria-hidden': true,
|
|
20
|
+
className: 'dim-modelChevron', style: open ? { transform: 'rotate(90deg)' } : undefined },
|
|
21
|
+
h('path', { d: 'm6 4 4 4-4 4', fill: 'none', stroke: 'currentColor', strokeWidth: 1.5 }));
|
|
18
22
|
}
|
|
19
23
|
|
|
20
24
|
export function ModelEditor({ model = null, disabled = false, onSave }) {
|
|
21
|
-
const catalog = normalizeModelCatalog(
|
|
22
|
-
|
|
23
|
-
);
|
|
24
|
-
const
|
|
25
|
-
const
|
|
25
|
+
const catalog = normalizeModelCatalog(React.useContext(ModelCatalogContext));
|
|
26
|
+
const id = React.useId();
|
|
27
|
+
const rootRef = React.useRef(null);
|
|
28
|
+
const menuRef = React.useRef(null);
|
|
29
|
+
const triggerRef = React.useRef(null);
|
|
30
|
+
const restoreFocusRef = React.useRef(false);
|
|
31
|
+
const savingRef = React.useRef(false);
|
|
32
|
+
const [pane, setPane] = React.useState(null);
|
|
26
33
|
const [saving, setSaving] = React.useState(false);
|
|
27
34
|
const [error, setError] = React.useState(null);
|
|
28
|
-
const
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
35
|
+
const current = normalizeModelSelection(model);
|
|
36
|
+
const entry = modelCatalogEntry(catalog, current);
|
|
37
|
+
const currentAvailable = !current || Boolean(entry);
|
|
38
|
+
const reasoning = entry?.reasoning;
|
|
39
|
+
const effort = current?.reasoningEffort;
|
|
40
|
+
const effectiveEffort = effort ?? reasoning?.defaultEffort;
|
|
41
|
+
const effortEntry = reasoning?.efforts.find((level) => level.id === effectiveEffort);
|
|
42
|
+
const effortUnavailable = effort !== undefined
|
|
43
|
+
&& !reasoning?.efforts.some((level) => level.id === effort);
|
|
44
|
+
const defaultName = reasoning?.efforts.find((level) => level.id === reasoning.defaultEffort)?.name;
|
|
45
|
+
const defaultLabel = localizeText('跟随模型默认') + (defaultName ? ` · ${defaultName}` : '');
|
|
46
|
+
const effortLabel = !current ? localizeText('跟随默认模型')
|
|
47
|
+
: effort === undefined ? defaultLabel : effortEntry?.name ?? effort;
|
|
48
|
+
const effortHint = !current ? '先选择模型,再设置思考强度。'
|
|
49
|
+
: !currentAvailable ? null
|
|
50
|
+
: effortUnavailable ? '已保存的思考强度已不可用,请选择其他强度或恢复默认。'
|
|
51
|
+
: !reasoning ? '该模型未提供可调节的思考强度。' : null;
|
|
52
|
+
const effortDisabled = !current || !currentAvailable || (!reasoning && effort === undefined);
|
|
33
53
|
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
54
|
+
const close = (restoreFocus = false) => {
|
|
55
|
+
restoreFocusRef.current = restoreFocus;
|
|
56
|
+
setPane(null);
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
React.useEffect(() => {
|
|
60
|
+
// A successful RPC also unlocks the card in its parent. Wait for that
|
|
61
|
+
// commit before focusing the trigger, which cannot be focused disabled.
|
|
62
|
+
if (!pane && !saving && !disabled && restoreFocusRef.current) {
|
|
63
|
+
restoreFocusRef.current = false;
|
|
64
|
+
triggerRef.current?.focus();
|
|
39
65
|
}
|
|
40
|
-
}
|
|
41
|
-
|
|
66
|
+
}, [pane, saving, disabled]);
|
|
67
|
+
|
|
68
|
+
React.useEffect(() => {
|
|
69
|
+
if (!pane) return undefined;
|
|
70
|
+
const options = menuRef.current?.querySelectorAll('[role="menuitemradio"]');
|
|
71
|
+
const selected = menuRef.current?.querySelector('[aria-checked="true"]');
|
|
72
|
+
(selected ?? options?.[0])?.focus();
|
|
73
|
+
const outside = (event) => {
|
|
74
|
+
if (!rootRef.current?.contains(event.target)) setPane(null);
|
|
75
|
+
};
|
|
76
|
+
globalThis.document?.addEventListener('mousedown', outside);
|
|
77
|
+
return () => globalThis.document?.removeEventListener('mousedown', outside);
|
|
78
|
+
}, [pane]);
|
|
42
79
|
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
if (
|
|
46
|
-
|
|
47
|
-
if (nextValue && !next) return;
|
|
80
|
+
const save = async (next) => {
|
|
81
|
+
if (savingRef.current || disabled) return;
|
|
82
|
+
if (sameModelSelection(current, next)) { close(true); return; }
|
|
83
|
+
savingRef.current = true;
|
|
48
84
|
setSaving(true);
|
|
49
85
|
setError(null);
|
|
50
86
|
try {
|
|
51
87
|
await onSave?.(next);
|
|
88
|
+
close(true);
|
|
52
89
|
} catch (cause) {
|
|
53
90
|
setError(cause?.message ?? '模型修改失败,请重试。');
|
|
54
91
|
} finally {
|
|
92
|
+
savingRef.current = false;
|
|
55
93
|
setSaving(false);
|
|
56
94
|
}
|
|
57
95
|
};
|
|
58
96
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
97
|
+
const option = (key, label, description, selected, next) => h('button', {
|
|
98
|
+
key, type: 'button', role: 'menuitemradio', 'aria-checked': selected,
|
|
99
|
+
className: 'dim-modelOption', disabled: disabled || saving,
|
|
100
|
+
onClick: () => { void save(next); },
|
|
101
|
+
},
|
|
102
|
+
h('span', { className: 'dim-modelOptionCopy' },
|
|
103
|
+
h('span', { className: 'dim-modelOptionName' }, label),
|
|
104
|
+
description ? h('span', { className: 'dim-modelDescription' }, description) : null),
|
|
105
|
+
h('span', { className: 'dim-modelCheck', 'aria-hidden': true }, selected ? '✓' : ''));
|
|
106
|
+
|
|
107
|
+
const row = (key, label, value, blocked = false) => h('button', {
|
|
108
|
+
type: 'button', className: 'dim-modelRow', disabled: disabled || saving || blocked,
|
|
109
|
+
'aria-label': label, 'aria-haspopup': 'menu', 'aria-expanded': pane === key,
|
|
110
|
+
'aria-controls': pane === key ? `${id}-menu` : undefined,
|
|
111
|
+
'aria-describedby': key === 'effort' && effortHint ? `${id}-hint` : undefined,
|
|
112
|
+
onClick: (event) => {
|
|
113
|
+
triggerRef.current = event.currentTarget;
|
|
114
|
+
setPane(pane === key ? null : key);
|
|
115
|
+
},
|
|
116
|
+
}, h('span', { className: 'dim-modelRowLabel' }, label),
|
|
117
|
+
h('span', { className: 'dim-modelValue', title: value }, value), chevron(pane === key));
|
|
118
|
+
|
|
119
|
+
return h('div', {
|
|
120
|
+
ref: rootRef, className: 'dim-preset dim-modelSetting',
|
|
121
|
+
onBlur: (event) => {
|
|
122
|
+
if (event.relatedTarget && !event.currentTarget.contains(event.relatedTarget)) close();
|
|
123
|
+
},
|
|
124
|
+
onKeyDown: (event) => {
|
|
125
|
+
if (!pane) return;
|
|
126
|
+
if (event.key === 'Escape') { event.preventDefault(); event.stopPropagation(); close(true); }
|
|
127
|
+
if (!['ArrowDown', 'ArrowUp', 'Home', 'End'].includes(event.key)) return;
|
|
128
|
+
const items = [...(menuRef.current?.querySelectorAll('[role="menuitemradio"]:not(:disabled)') ?? [])];
|
|
129
|
+
if (!items.length) return;
|
|
130
|
+
event.preventDefault();
|
|
131
|
+
const at = items.indexOf(globalThis.document?.activeElement);
|
|
132
|
+
const next = event.key === 'Home' ? 0 : event.key === 'End' ? items.length - 1
|
|
133
|
+
: (at + (event.key === 'ArrowDown' ? 1 : -1) + items.length) % items.length;
|
|
134
|
+
items[next]?.focus();
|
|
82
135
|
},
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
136
|
+
},
|
|
137
|
+
h('div', { className: 'dim-presetHeader' },
|
|
138
|
+
h('span', { className: 'dim-presetTitle' }, '模型与思考强度',
|
|
139
|
+
h('span', { className: 'dim-presetHelp' },
|
|
140
|
+
h('button', { type: 'button', className: 'dim-presetHelpButton',
|
|
141
|
+
'aria-label': '查看模型设置说明', 'aria-describedby': `${id}-help` },
|
|
142
|
+
h('span', { 'aria-hidden': true }, '?')),
|
|
143
|
+
h('span', { id: `${id}-help`, className: 'dim-presetTooltip', role: 'tooltip' },
|
|
144
|
+
'只影响新建会话;若当前聊天已有会话,先发送 /new,再发送普通消息生效。'))),
|
|
145
|
+
saving ? h('span', { className: 'dim-presetStatus', role: 'status' }, '保存中…') : null),
|
|
146
|
+
row('model', '模型', entry?.name ?? (current ? modelSelectionId(current) : localizeText('跟随默认模型'))),
|
|
147
|
+
row('effort', '思考强度', effortLabel, effortDisabled),
|
|
148
|
+
pane ? h('div', { ref: menuRef, id: `${id}-menu`, role: 'menu',
|
|
149
|
+
'aria-label': pane === 'model' ? '模型' : '思考强度', 'aria-busy': saving,
|
|
150
|
+
className: 'dim-modelMenu' },
|
|
151
|
+
pane === 'model' ? [
|
|
152
|
+
option('default', '跟随默认模型', null, !current, null),
|
|
153
|
+
...catalog.groups.map((group) => h('section', { key: group.id, role: 'group', 'aria-label': group.name },
|
|
154
|
+
h('div', { className: 'dim-modelGroupTitle' }, group.name),
|
|
155
|
+
...group.models.map((choice) => {
|
|
156
|
+
const selected = current?.provider === group.id && current?.model === choice.id;
|
|
157
|
+
return option(choice.id, choice.name, choice.description ?? `${group.id}/${choice.id}`,
|
|
158
|
+
selected, selected ? current : { provider: group.id, model: choice.id });
|
|
159
|
+
}))),
|
|
160
|
+
] : [
|
|
161
|
+
option('provider-default', defaultLabel, '使用模型或服务提供方的默认思考强度。', effort === undefined,
|
|
162
|
+
current ? { provider: current.provider, model: current.model } : null),
|
|
163
|
+
...(reasoning?.efforts ?? []).map((level) => option(`effort:${level.id}`, level.name, level.description,
|
|
164
|
+
effort === level.id, { ...current, reasoningEffort: level.id })),
|
|
165
|
+
]) : null,
|
|
166
|
+
effortHint ? h('p', { id: `${id}-hint`, className: 'dim-modelHint',
|
|
167
|
+
role: effortUnavailable ? 'status' : undefined }, effortHint) : null,
|
|
168
|
+
error || !currentAvailable ? h('p', { className: 'dim-presetError', role: error ? 'alert' : 'status' },
|
|
169
|
+
error ?? '当前模型已不可用,请选择其他模型或跟随默认模型。') : null);
|
|
104
170
|
}
|
|
@@ -71,9 +71,7 @@ const CSS = String.raw`
|
|
|
71
71
|
.dim-generalSettingsAction:hover .dim-generalSettingsTooltip, .dim-generalSettingsButton:focus-visible + .dim-generalSettingsTooltip { opacity: 1; visibility: visible; transform: translateY(0); }
|
|
72
72
|
.dim-generalSettingsButton[aria-current="page"] + .dim-generalSettingsTooltip { opacity: 0; visibility: hidden; transform: translateY(-3px); }
|
|
73
73
|
.dim-layout { display: grid; grid-template-columns: 174px 1px minmax(0, 1fr); gap: 24px; align-items: start; }
|
|
74
|
-
.dim-rail {
|
|
75
|
-
.dim-rail::-webkit-scrollbar { width: 4px; }
|
|
76
|
-
.dim-rail::-webkit-scrollbar-thumb { border-radius: 99px; background: var(--dsw-alias-border-l2, #dfe1e5); }
|
|
74
|
+
.dim-rail { display: grid; align-content: start; gap: 8px; padding: 0 4px 1px 1px; }
|
|
77
75
|
.dim-channel { width: 100%; min-height: 48px; display: grid; grid-template-columns: 30px minmax(0, 1fr); align-items: center; gap: 10px; padding: 8px 12px; border: 1px solid var(--dsw-alias-border-l2, #eef0f3); border-radius: 14px; color: inherit; background: var(--dsw-alias-bg-layer-3, #fff); box-shadow: 0 2px 8px rgb(31 35 41 / 3%); font: inherit; text-align: left; cursor: pointer; transition: border-color .16s ease, background .16s ease, box-shadow .16s ease; }
|
|
78
76
|
.dim-channel:hover { border-color: color-mix(in srgb, var(--dim-blue) 25%, var(--dsw-alias-border-l2, #eef0f3)); background: color-mix(in srgb, var(--dim-blue) 2%, var(--dsw-alias-bg-layer-3, #fff)); box-shadow: 0 5px 16px rgb(31 35 41 / 5%); }
|
|
79
77
|
.dim-channel[aria-selected="true"] { border-color: color-mix(in srgb, var(--dim-blue) 43%, var(--dsw-alias-border-l2, #dfe1e5)); color: var(--dim-blue); background: color-mix(in srgb, var(--dim-blue) 12%, var(--dsw-alias-bg-layer-3, #fff)); box-shadow: 0 3px 12px rgb(51 112 255 / 7%); }
|
|
@@ -103,7 +101,7 @@ const CSS = String.raw`
|
|
|
103
101
|
.dim-channelCopy { min-width: 0; display: grid; }
|
|
104
102
|
.dim-channelCopy strong { overflow: hidden; color: inherit; font-size: 14px; line-height: 20px; font-weight: 680; text-overflow: ellipsis; white-space: nowrap; }
|
|
105
103
|
.dim-channelNote { overflow: hidden; color: var(--dsw-alias-label-tertiary, #8f959e); font-size: 10px; line-height: 13px; font-weight: 500; text-overflow: ellipsis; white-space: nowrap; }
|
|
106
|
-
.dim-divider { width: 1px;
|
|
104
|
+
.dim-divider { width: 1px; align-self: stretch; background: var(--dsw-alias-border-l1, #eef0f3); }
|
|
107
105
|
.dim-panel { min-width: 0; container-type: inline-size; }
|
|
108
106
|
.dim-loopbackRecovery { display: flex; align-items: center; justify-content: space-between; gap: 16px; margin: 0 0 14px; padding: 14px 16px; border: 1px solid color-mix(in srgb, var(--dsw-alias-state-warn-primary, #d97706) 30%, var(--dsw-alias-border-l2, #dfe1e5)); border-radius: 12px; color: var(--dsw-alias-label-primary, #1f2329); background: color-mix(in srgb, var(--dsw-alias-state-warn-primary, #d97706) 8%, var(--dsw-alias-bg-layer-1, #fff)); }
|
|
109
107
|
.dim-loopbackRecoveryCopy { min-width: 0; }
|
|
@@ -219,6 +217,24 @@ const CSS = String.raw`
|
|
|
219
217
|
.dim-panel .dim-presetStatus { grid-column: 2; grid-row: 1; color: var(--dsw-alias-label-tertiary, #8f959e); font-size: 12px; white-space: nowrap; }
|
|
220
218
|
.dim-panel .dim-presetSelect { min-width: 0; max-width: 100%; grid-column: 1 / -1; grid-row: 2; height: 30px; padding: 0 8px; border: 1px solid var(--dsw-alias-border-l2, #dfe1e5); border-radius: 7px; color: var(--dsw-alias-label-primary, #1f2329); background: var(--dsw-alias-bg-layer-1, #fff); font: inherit; font-size: 12px; }
|
|
221
219
|
.dim-panel .dim-presetSelect:disabled { cursor: not-allowed; opacity: .55; }
|
|
220
|
+
.dim-panel .dim-modelSetting { display: block; padding: 8px 4px 4px; }
|
|
221
|
+
.dim-modelSetting > .dim-presetHeader { padding: 0 6px 6px; }
|
|
222
|
+
.dim-modelRow { display: flex; align-items: center; gap: 8px; width: 100%; min-height: 40px; padding: 6px 10px; border: 0; border-radius: 10px; background: transparent; color: var(--dsw-alias-label-primary, #1f2329); font: inherit; font-size: 14px; line-height: 22px; text-align: left; cursor: pointer; }
|
|
223
|
+
.dim-modelRowLabel { flex: none; }
|
|
224
|
+
.dim-modelValue { flex: 1; min-width: 0; overflow: hidden; color: var(--dsw-alias-label-tertiary, #81858c); text-overflow: ellipsis; white-space: nowrap; text-align: right; }
|
|
225
|
+
.dim-modelChevron { flex: none; color: var(--dsw-alias-label-tertiary, #81858c); }
|
|
226
|
+
.dim-modelRow:hover:not(:disabled), .dim-modelOption:hover:not(:disabled) { background: var(--dsw-alias-interactive-bg-hover, #eceef1); }
|
|
227
|
+
.dim-modelRow:focus-visible, .dim-modelOption:focus-visible { outline: 2px solid var(--dsw-alias-border-l3, #1677ff); outline-offset: -2px; }
|
|
228
|
+
.dim-modelRow:disabled, .dim-modelOption:disabled { opacity: .55; cursor: default; }
|
|
229
|
+
/* In-flow lists avoid clipping by collapsed-card and settings scroll containers. */
|
|
230
|
+
.dim-modelMenu { max-height: 280px; overflow-y: auto; margin: 4px; padding: 4px; border: 1px solid var(--dsw-alias-border-l2, #e5e6eb); border-radius: 12px; background: var(--dsw-specific-menu, var(--dsw-alias-bg-layer-3, #fff)); color: var(--dsw-alias-label-primary, #1f2329); scrollbar-width: thin; }
|
|
231
|
+
.dim-modelGroupTitle { padding: 7px 8px 3px; color: var(--dsw-alias-label-tertiary, #81858c); font-size: 12px; line-height: 18px; font-weight: 500; }
|
|
232
|
+
.dim-modelOption { display: flex; align-items: center; gap: 8px; width: 100%; min-height: 38px; padding: 6px 8px; border: 0; border-radius: 10px; background: transparent; color: inherit; text-align: left; font: inherit; cursor: pointer; }
|
|
233
|
+
.dim-modelOptionCopy { display: flex; flex: 1; min-width: 0; flex-direction: column; gap: 2px; }
|
|
234
|
+
.dim-modelOptionName { font-size: 14px; line-height: 20px; font-weight: 500; overflow-wrap: anywhere; }
|
|
235
|
+
.dim-modelDescription { color: var(--dsw-alias-label-tertiary, #81858c); font-size: 12px; line-height: 18px; overflow-wrap: anywhere; }
|
|
236
|
+
.dim-modelCheck { flex: 0 0 18px; text-align: center; }
|
|
237
|
+
.dim-modelHint { margin: 4px 10px 6px; color: var(--dsw-alias-label-tertiary, #81858c); font-size: 11px; line-height: 17px; }
|
|
222
238
|
.dim-panel .dim-presetError { grid-column: 1 / -1; grid-row: 3; margin: 0; color: var(--dsw-alias-state-error-primary, #d54941); font-size: 12px; line-height: 1.4; }
|
|
223
239
|
.dim-contextEntry { width: 100%; min-height: 40px; display: grid; grid-template-columns: 16px minmax(0, 1fr) max-content 16px; align-items: center; gap: 9px; margin: 10px 0; padding: 8px 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-module-platform, #f7f8fa); font: inherit; font-size: 13px; line-height: 20px; text-align: left; cursor: pointer; }
|
|
224
240
|
.dim-contextEntry:hover:not(:disabled) { background: var(--dsw-alias-interactive-bg-hover, #eef0f3); }
|
|
@@ -406,7 +422,7 @@ const CSS = String.raw`
|
|
|
406
422
|
.dim-feishuGroupSettings { min-width: 0; display: grid; gap: 14px; }
|
|
407
423
|
.dim-feishuGroupControls { min-width: 0; display: grid; gap: 12px; }
|
|
408
424
|
.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); }
|
|
409
|
-
.dim-feishuGroupControlHeader { min-width: 0; display: flex; align-items: center; justify-content: space-between; gap: 12px; }
|
|
425
|
+
.dim-feishuGroupControlHeader { position: relative; min-width: 0; display: flex; align-items: center; justify-content: space-between; gap: 12px; }
|
|
410
426
|
.dim-feishuGroupControlHeader h3 { min-width: 0; margin: 0; color: var(--dsw-alias-label-primary, #1f2329); font-size: 14px; line-height: 21px; font-weight: 650; }
|
|
411
427
|
.dim-feishuGroupControlStatus { flex: none; color: var(--dsw-alias-label-tertiary, #8f959e); font-size: 11px; line-height: 17px; }
|
|
412
428
|
.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; }
|
|
@@ -608,9 +624,8 @@ const CSS = String.raw`
|
|
|
608
624
|
@media (max-width: 840px) {
|
|
609
625
|
.dim-title { align-items: flex-start; }
|
|
610
626
|
.dim-layout { grid-template-columns: minmax(0, 1fr); gap: 18px; }
|
|
611
|
-
.dim-rail { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
|
627
|
+
.dim-rail { grid-template-columns: repeat(2, minmax(0, 1fr)); padding-right: 1px; }
|
|
612
628
|
.dim-divider { display: none; }
|
|
613
|
-
.dim-rail { max-height: none; overflow: visible; padding-right: 1px; }
|
|
614
629
|
.dim-channel { min-height: 48px; }
|
|
615
630
|
}
|
|
616
631
|
@media (max-width: 560px) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createProductionController } from './production.mjs';
|
|
2
|
-
import { installDingtalkRpc } from './rpc.mjs';
|
|
2
|
+
import { createDingtalkRpcHandler, installDingtalkRpc, DINGTALK_RPC_CHANNEL } from './rpc.mjs';
|
|
3
|
+
import { installProductionChannel } from '../shared/startup.mjs';
|
|
3
4
|
|
|
4
5
|
export const name = 'dsh-dingtalk-host';
|
|
5
6
|
export const inject = ['connection', 'credentials', 'typertGateway'];
|
|
@@ -9,20 +10,12 @@ export async function apply(ctx, config = {}) {
|
|
|
9
10
|
return installDingtalkRpc(ctx, config.controller, config.rpcOptions, config.rpcAuthority);
|
|
10
11
|
}
|
|
11
12
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
config.rpcOptions,
|
|
19
|
-
config.rpcAuthority,
|
|
20
|
-
);
|
|
21
|
-
ctx.effect(() => async () => {
|
|
22
|
-
await unregisterDelivery?.();
|
|
23
|
-
await production.close();
|
|
24
|
-
}, 'dsh-dingtalk: close bot connections');
|
|
25
|
-
return disposeRpc;
|
|
13
|
+
return installProductionChannel(ctx, config, {
|
|
14
|
+
channel: 'dingtalk',
|
|
15
|
+
rpcChannel: DINGTALK_RPC_CHANNEL,
|
|
16
|
+
createProduction: () => createProductionController(ctx, config, config.internals),
|
|
17
|
+
createHandler: controller => createDingtalkRpcHandler(controller, config.rpcOptions),
|
|
18
|
+
});
|
|
26
19
|
}
|
|
27
20
|
|
|
28
21
|
export function createDingtalkHostPlugin(config) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createProductionController } from './production.mjs';
|
|
2
|
-
import { installDiscordRpc } from './rpc.mjs';
|
|
2
|
+
import { createDiscordRpcHandler, installDiscordRpc, DISCORD_RPC_CHANNEL } from './rpc.mjs';
|
|
3
|
+
import { installProductionChannel } from '../shared/startup.mjs';
|
|
3
4
|
|
|
4
5
|
export const name = 'dsh-im-discord-host';
|
|
5
6
|
export const inject = ['connection', 'credentials', 'typertGateway'];
|
|
@@ -8,15 +9,12 @@ export async function apply(ctx, config = {}) {
|
|
|
8
9
|
if (config?.controller) {
|
|
9
10
|
return installDiscordRpc(ctx, config.controller, config.rpcAuthority);
|
|
10
11
|
}
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
await production.close();
|
|
18
|
-
}, 'dsh-im: close Discord bot connections');
|
|
19
|
-
return disposeRpc;
|
|
12
|
+
return installProductionChannel(ctx, config, {
|
|
13
|
+
channel: 'discord',
|
|
14
|
+
rpcChannel: DISCORD_RPC_CHANNEL,
|
|
15
|
+
createProduction: () => createProductionController(ctx, config, config.internals ?? {}),
|
|
16
|
+
createHandler: controller => createDiscordRpcHandler(controller),
|
|
17
|
+
});
|
|
20
18
|
}
|
|
21
19
|
|
|
22
20
|
export function createDiscordHostPlugin(config) {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createProvisioningBackedController } from './controller.mjs';
|
|
2
2
|
import { createProductionController } from './production.mjs';
|
|
3
|
-
import { installFeishuRpc } from './rpc.mjs';
|
|
3
|
+
import { createFeishuRpcHandler, installFeishuRpc, FEISHU_RPC_CHANNEL } from './rpc.mjs';
|
|
4
|
+
import { installProductionChannel } from '../shared/startup.mjs';
|
|
4
5
|
|
|
5
6
|
export const name = 'dsh-feishu-host';
|
|
6
7
|
export const inject = ['connection', 'credentials', 'typertGateway'];
|
|
@@ -28,20 +29,12 @@ export async function apply(ctx, config = {}) {
|
|
|
28
29
|
return installFeishuRpc(ctx, controller, config.rpcOptions, config.rpcAuthority);
|
|
29
30
|
}
|
|
30
31
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
config.rpcOptions,
|
|
38
|
-
config.rpcAuthority,
|
|
39
|
-
);
|
|
40
|
-
ctx.effect(() => async () => {
|
|
41
|
-
await unregisterDelivery?.();
|
|
42
|
-
await production.close();
|
|
43
|
-
}, 'dsh-feishu: close controller and live connection');
|
|
44
|
-
return disposeRpc;
|
|
32
|
+
return installProductionChannel(ctx, config, {
|
|
33
|
+
channel: 'feishu',
|
|
34
|
+
rpcChannel: FEISHU_RPC_CHANNEL,
|
|
35
|
+
createProduction: () => createProductionController(ctx, config, config.internals),
|
|
36
|
+
createHandler: controller => createFeishuRpcHandler(controller, config.rpcOptions),
|
|
37
|
+
});
|
|
45
38
|
}
|
|
46
39
|
|
|
47
40
|
/** Create a programmatic plugin module with dependencies closed over. */
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import { createProductionController } from './production.mjs';
|
|
2
|
-
import { installOfficeRpc } from './rpc.mjs';
|
|
2
|
+
import { createOfficeRpcHandler, installOfficeRpc } from './rpc.mjs';
|
|
3
|
+
import { installProductionChannel } from '../shared/startup.mjs';
|
|
4
|
+
import { OFFICE_RPC_CHANNEL } from '../../../../src/channels/office/protocol.mjs';
|
|
3
5
|
|
|
4
6
|
export async function apply(ctx, config = {}) {
|
|
5
7
|
if (config.controller) return installOfficeRpc(ctx, config.controller, config.rpcAuthority);
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
return installProductionChannel(ctx, config, {
|
|
9
|
+
channel: 'office',
|
|
10
|
+
rpcChannel: OFFICE_RPC_CHANNEL,
|
|
11
|
+
createProduction: () => createProductionController(ctx, config, config.internals ?? {}),
|
|
12
|
+
createHandler: controller => createOfficeRpcHandler(controller),
|
|
13
|
+
});
|
|
10
14
|
}
|