@xmanrui/dsh-im 4.9.0 → 4.10.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 +8 -0
- package/PROACTIVE_DELIVERY.md +8 -0
- package/lib/client.js +679 -73
- package/lib/index.js +276 -275
- package/package.json +21 -6
- package/plugin-src/client/channels/dingtalk/api.js +23 -6
- package/plugin-src/client/channels/dingtalk/index.js +33 -6
- package/plugin-src/client/channels/dingtalk/styles.js +2 -0
- package/plugin-src/client/channels/feishu/api.js +2 -0
- package/plugin-src/client/channels/feishu/index.js +54 -0
- package/plugin-src/client/global-settings.js +341 -0
- package/plugin-src/client/i18n.js +33 -0
- package/plugin-src/client/index.js +47 -6
- package/plugin-src/client/styles.js +49 -0
- package/plugin-src/host/channels/dingtalk/production.mjs +12 -0
- package/plugin-src/host/channels/dingtalk/rpc.mjs +24 -0
- package/plugin-src/host/channels/feishu/production.mjs +14 -0
- package/plugin-src/host/channels/feishu/rpc.mjs +16 -0
- package/plugin-src/host/channels/qq/production.mjs +12 -0
- package/plugin-src/host/channels/shared/production.mjs +12 -0
- package/plugin-src/host/channels/slack/production.mjs +12 -0
- package/plugin-src/host/channels/wecom/production.mjs +12 -0
- package/plugin-src/host/channels/weixin/production.mjs +12 -0
- package/plugin-src/host/channels/whatsapp/production.mjs +12 -0
- package/plugin-src/host/delivery-adapter.mjs +1 -0
- package/plugin-src/host/delivery-service.mjs +15 -0
- package/plugin-src/host/harness-session-coordinator.mjs +26 -5
- package/plugin-src/host/inbound-ttl-rpc.mjs +83 -0
- package/plugin-src/host/inbound-ttl-runtime.mjs +85 -0
- package/plugin-src/host/inbound-ttl-service.mjs +179 -0
- package/plugin-src/host/index.mjs +8 -0
- package/plugin-src/host/modern-harness-api.mjs +75 -10
- package/src/channels/dingtalk/connection-error.mjs +299 -0
- package/src/channels/dingtalk/dingtalk-controller.mjs +79 -34
- package/src/channels/dingtalk/dingtalk-runtime.mjs +9 -2
- package/src/channels/feishu/bridge.mjs +164 -4
- package/src/channels/feishu/feishu-channel.mjs +45 -10
- package/src/channels/feishu/feishu-runtime.mjs +9 -0
- package/src/channels/feishu/message-utils.mjs +13 -0
- package/src/channels/feishu/multi-bot-controller.mjs +16 -0
- package/src/channels/feishu/plugin-config-store.mjs +1 -0
- package/src/channels/feishu/state-store.mjs +25 -0
- package/src/channels/shared/bot-workspace-store.mjs +6 -0
- package/src/channels/shared/i18n-en/dingtalk.mjs +23 -0
- package/src/channels/shared/inbound-file.mjs +117 -2
- package/src/channels/shared/inbound-ttl-store.mjs +110 -0
- package/src/channels/shared/inbound-ttl.mjs +22 -0
|
@@ -215,6 +215,11 @@ const EN = Object.freeze({
|
|
|
215
215
|
'需在飞书为该机器人开通“获取群组中所有消息”权限(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.',
|
|
216
216
|
'群聊响应方式修改失败,请重试。': 'Could not update the group response mode. Try again.',
|
|
217
217
|
'群消息权限授权失败,请重试。': 'Could not authorize group-message permission. Try again.',
|
|
218
|
+
'群聊以话题方式回复': 'Reply to group chats as Feishu topics',
|
|
219
|
+
'群聊以话题方式回复设置保存失败,请重试。': 'Could not save the topic reply setting. Try again.',
|
|
220
|
+
'关闭(群内直接回复)': 'Off (reply in the group feed)',
|
|
221
|
+
'开启(自动开启独立飞书话题)': 'On (auto-open a dedicated Feishu topic)',
|
|
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.',
|
|
218
223
|
'钉钉设置': 'DingTalk settings',
|
|
219
224
|
'企业微信设置': 'WeCom settings',
|
|
220
225
|
'扫码接入机器人': 'Scan QR code',
|
|
@@ -351,6 +356,8 @@ const EN = Object.freeze({
|
|
|
351
356
|
'检查连接': 'Check connection',
|
|
352
357
|
'检查中…': 'Checking…',
|
|
353
358
|
'连接检查失败,请稍后重试。': 'Connection check failed. Try again later.',
|
|
359
|
+
'机器人已保存,但连接未就绪': 'The bot was saved, but the connection is not ready',
|
|
360
|
+
'查看已保存的机器人': 'View saved bot',
|
|
354
361
|
'测试消息已发送,请到对应机器人会话中确认。': 'Test message sent. Check the matching bot conversation.',
|
|
355
362
|
'连接检查完成。机器人尚未收到可用于测试的私聊消息。': 'Connection check completed. The bot has not received a direct message it can use for testing.',
|
|
356
363
|
'连接检查完成,但测试消息发送失败。': 'Connection check completed, but the test message could not be sent.',
|
|
@@ -835,6 +842,28 @@ const EN = Object.freeze({
|
|
|
835
842
|
'正在读取投递目标…': 'Loading delivery targets…',
|
|
836
843
|
'尚未配置投递目标': 'No delivery targets yet',
|
|
837
844
|
'点击“新建目标”可从已聊过的会话选择,也可手动填写。': 'Select New target to choose a conversation or enter the details manually.',
|
|
845
|
+
'全局设置': 'Global settings',
|
|
846
|
+
'通用设置': 'General settings',
|
|
847
|
+
'通用设置分类': 'General settings categories',
|
|
848
|
+
'附件': 'Attachments',
|
|
849
|
+
'IM 设置导航': 'IM settings navigation',
|
|
850
|
+
'附件保留时长 (小时)': 'Attachment retention (hours)',
|
|
851
|
+
'查看附件保留时长说明': 'View attachment retention details',
|
|
852
|
+
'永久保留,不会自动清理': 'Keep forever; nothing is removed automatically',
|
|
853
|
+
'每 Turn 结束后立即清理': 'Cleaned up as soon as each turn ends',
|
|
854
|
+
'小时后自动清理': 'hours until automatic cleanup',
|
|
855
|
+
'请输入 -1、0 或 1~${INBOUND_TTL_MAX_HOURS} 之间的整数。': 'Enter -1, 0, or an integer from 1 to ${INBOUND_TTL_MAX_HOURS}.',
|
|
856
|
+
'设置保存失败,请稍后重试。': 'Could not save the setting. Try again later.',
|
|
857
|
+
'已保存': 'Saved',
|
|
858
|
+
'通用设置暂不可用。': 'General settings are unavailable.',
|
|
859
|
+
'正在读取通用设置…': 'Loading general settings…',
|
|
860
|
+
'无法读取通用设置,请稍后重试。': 'Could not load general settings. Try again later.',
|
|
861
|
+
'通用设置返回了无法识别的响应。': 'General settings returned an unrecognized response.',
|
|
862
|
+
'清理过期附件': 'Clean up expired attachments',
|
|
863
|
+
'确认清理过期附件': 'Confirm expired attachment cleanup',
|
|
864
|
+
'确认清理当前已过期的附件?': 'Clean up the attachments that are currently expired?',
|
|
865
|
+
'确认清理': 'Confirm cleanup',
|
|
866
|
+
'正在清理…': 'Cleaning up…',
|
|
838
867
|
});
|
|
839
868
|
|
|
840
869
|
export const en = EN;
|
|
@@ -863,6 +892,8 @@ function translateDynamic(text) {
|
|
|
863
892
|
if (match) return `${match[1]}/${match[2]} online`;
|
|
864
893
|
match = /^已接入 (\d+) 个机器人,其中 (\d+) 个在线$/.exec(text);
|
|
865
894
|
if (match) return `${match[2]} of ${match[1]} bots online`;
|
|
895
|
+
match = /^请输入 -1、0 或 1~(\d+) 之间的整数。$/.exec(text);
|
|
896
|
+
if (match) return `Enter -1, 0, or an integer from 1 to ${match[1]}.`;
|
|
866
897
|
match = /^正在读取\s*(.+?)\s*机器人状态…$/.exec(text);
|
|
867
898
|
if (match) return `Loading ${channelName(match[1])} bot status…`;
|
|
868
899
|
match = /^无法读取\s*(.+?)\s*机器人状态$/.exec(text);
|
|
@@ -921,6 +952,8 @@ function translateDynamic(text) {
|
|
|
921
952
|
if (match) return `Automatic status refresh failed: ${match[1]}`;
|
|
922
953
|
match = /^操作失败:(.+)$/.exec(text);
|
|
923
954
|
if (match) return `Operation failed: ${match[1]}`;
|
|
955
|
+
match = /^连接检查失败:(.+)(参考号:(.+))$/.exec(text);
|
|
956
|
+
if (match) return `Connection check failed: ${localizeText(match[1])} (reference: ${match[2]})`;
|
|
924
957
|
match = /^连接检查失败:(.+)$/.exec(text);
|
|
925
958
|
if (match) return `Connection check failed: ${match[1]}`;
|
|
926
959
|
match = /^移除失败:(.+)$/.exec(text);
|
|
@@ -48,6 +48,12 @@ import {
|
|
|
48
48
|
DELIVERY_RPC_CHANNEL,
|
|
49
49
|
DeliveryTargetSettingsPage,
|
|
50
50
|
} from './delivery-settings.js';
|
|
51
|
+
import {
|
|
52
|
+
GLOBAL_SETTINGS_RPC_CHANNEL,
|
|
53
|
+
GLOBAL_SETTINGS_TAB_ID,
|
|
54
|
+
GlobalSettingsLogoGlyph,
|
|
55
|
+
GlobalSettingsPanel,
|
|
56
|
+
} from './global-settings.js';
|
|
51
57
|
import {
|
|
52
58
|
createLoopbackAwareRpcCalls,
|
|
53
59
|
replacePageLocation,
|
|
@@ -173,6 +179,7 @@ export function IMSettingsTab({
|
|
|
173
179
|
officeRpcCall,
|
|
174
180
|
updateRpcCall,
|
|
175
181
|
deliveryRpcCall,
|
|
182
|
+
globalSettingsRpcCall,
|
|
176
183
|
workspaceDirectoryPicker,
|
|
177
184
|
browserLocation = globalThis.location,
|
|
178
185
|
navigateToRecoveryUrl = replacePageLocation,
|
|
@@ -182,7 +189,15 @@ export function IMSettingsTab({
|
|
|
182
189
|
const [runningVersion, setRunningVersion] = React.useState(IM_PLUGIN_VERSION);
|
|
183
190
|
const [deliverySettings, setDeliverySettings] = React.useState(null);
|
|
184
191
|
const githubTooltipId = React.useId();
|
|
192
|
+
const generalSettingsTooltipId = React.useId();
|
|
193
|
+
const globalSettingsSelected = selected === GLOBAL_SETTINGS_TAB_ID;
|
|
185
194
|
const active = CHANNELS.find((channel) => channel.id === selected) ?? CHANNELS[0];
|
|
195
|
+
const activeTabId = globalSettingsSelected
|
|
196
|
+
? 'dim-general-settings-trigger'
|
|
197
|
+
: `dim-tab-${active.id}`;
|
|
198
|
+
const activePanelId = globalSettingsSelected
|
|
199
|
+
? `dim-panel-${GLOBAL_SETTINGS_TAB_ID}`
|
|
200
|
+
: `dim-panel-${active.id}`;
|
|
186
201
|
const reportLoopbackRecovery = React.useCallback((recovery) => {
|
|
187
202
|
setLoopbackRecovery((current) => current?.url === recovery.url ? current : recovery);
|
|
188
203
|
}, []);
|
|
@@ -202,6 +217,7 @@ export function IMSettingsTab({
|
|
|
202
217
|
officeRpcCall,
|
|
203
218
|
updateRpcCall,
|
|
204
219
|
deliveryRpcCall,
|
|
220
|
+
globalSettingsRpcCall,
|
|
205
221
|
}, {
|
|
206
222
|
location: browserLocation,
|
|
207
223
|
onRecovery: reportLoopbackRecovery,
|
|
@@ -211,6 +227,7 @@ export function IMSettingsTab({
|
|
|
211
227
|
discordRpcCall,
|
|
212
228
|
deliveryRpcCall,
|
|
213
229
|
feishuRpcCall,
|
|
230
|
+
globalSettingsRpcCall,
|
|
214
231
|
officeRpcCall,
|
|
215
232
|
qqRpcCall,
|
|
216
233
|
reportLoopbackRecovery,
|
|
@@ -253,17 +270,36 @@ export function IMSettingsTab({
|
|
|
253
270
|
id: githubTooltipId,
|
|
254
271
|
className: 'dim-githubTooltip',
|
|
255
272
|
role: 'tooltip',
|
|
256
|
-
}, '帮助与反馈 · 前往 GitHub'))
|
|
273
|
+
}, '帮助与反馈 · 前往 GitHub')),
|
|
274
|
+
h('span', { className: 'dim-generalSettingsAction' },
|
|
275
|
+
h('button', {
|
|
276
|
+
type: 'button',
|
|
277
|
+
id: 'dim-general-settings-trigger',
|
|
278
|
+
className: 'dim-generalSettingsButton',
|
|
279
|
+
'aria-label': '通用设置',
|
|
280
|
+
'aria-describedby': generalSettingsTooltipId,
|
|
281
|
+
'aria-controls': `dim-panel-${GLOBAL_SETTINGS_TAB_ID}`,
|
|
282
|
+
'aria-current': globalSettingsSelected ? 'page' : undefined,
|
|
283
|
+
onClick: () => {
|
|
284
|
+
setSelected(GLOBAL_SETTINGS_TAB_ID);
|
|
285
|
+
setDeliverySettings(null);
|
|
286
|
+
},
|
|
287
|
+
}, h(GlobalSettingsLogoGlyph, { size: 17 })),
|
|
288
|
+
h('span', {
|
|
289
|
+
id: generalSettingsTooltipId,
|
|
290
|
+
className: 'dim-generalSettingsTooltip',
|
|
291
|
+
role: 'tooltip',
|
|
292
|
+
}, '通用设置'))),
|
|
257
293
|
),
|
|
258
294
|
h('div', { className: 'dim-layout' },
|
|
259
|
-
h('nav', { className: 'dim-rail', role: 'tablist', 'aria-label': 'IM
|
|
295
|
+
h('nav', { className: 'dim-rail', role: 'tablist', 'aria-label': 'IM 设置导航' },
|
|
260
296
|
CHANNELS.map((channel) => h('button', {
|
|
261
297
|
key: channel.id,
|
|
262
298
|
type: 'button',
|
|
263
299
|
role: 'tab',
|
|
264
300
|
id: `dim-tab-${channel.id}`,
|
|
265
301
|
className: 'dim-channel',
|
|
266
|
-
'aria-selected': channel.id === active.id,
|
|
302
|
+
'aria-selected': !globalSettingsSelected && channel.id === active.id,
|
|
267
303
|
'aria-controls': `dim-panel-${channel.id}`,
|
|
268
304
|
onClick: () => {
|
|
269
305
|
setSelected(channel.id);
|
|
@@ -279,8 +315,8 @@ export function IMSettingsTab({
|
|
|
279
315
|
h('main', {
|
|
280
316
|
className: 'dim-panel',
|
|
281
317
|
role: 'tabpanel',
|
|
282
|
-
id:
|
|
283
|
-
'aria-labelledby':
|
|
318
|
+
id: activePanelId,
|
|
319
|
+
'aria-labelledby': activeTabId,
|
|
284
320
|
},
|
|
285
321
|
loopbackRecovery
|
|
286
322
|
? h(LoopbackRecoveryNotice, {
|
|
@@ -289,7 +325,9 @@ export function IMSettingsTab({
|
|
|
289
325
|
})
|
|
290
326
|
: null,
|
|
291
327
|
h(BotSettingsContext.Provider, { value: botSettingsContext },
|
|
292
|
-
|
|
328
|
+
globalSettingsSelected
|
|
329
|
+
? h(GlobalSettingsPanel, { rpcCall: rpcCalls.globalSettingsRpcCall })
|
|
330
|
+
: deliverySettings?.channel === active.id
|
|
293
331
|
? h(DeliveryTargetSettingsPage, {
|
|
294
332
|
channel: active.id,
|
|
295
333
|
account: deliverySettings,
|
|
@@ -370,6 +408,8 @@ export function apply(ctx) {
|
|
|
370
408
|
ctx.connection.rpc.call(UPDATE_RPC_CHANNEL, endpoint, payload, signal);
|
|
371
409
|
const deliveryRpcCall = (endpoint, payload, signal) =>
|
|
372
410
|
ctx.connection.rpc.call(DELIVERY_RPC_CHANNEL, endpoint, payload, signal);
|
|
411
|
+
const globalSettingsRpcCall = (endpoint, payload, signal) =>
|
|
412
|
+
ctx.connection.rpc.call(GLOBAL_SETTINGS_RPC_CHANNEL, endpoint, payload, signal);
|
|
373
413
|
const workspaceDirectoryPicker = Object.freeze({
|
|
374
414
|
listDirectory: (path, signal) =>
|
|
375
415
|
callWorkspaceDirectoryApi(ctx, 'listDirectory', path, signal),
|
|
@@ -395,6 +435,7 @@ export function apply(ctx) {
|
|
|
395
435
|
officeRpcCall,
|
|
396
436
|
updateRpcCall,
|
|
397
437
|
deliveryRpcCall,
|
|
438
|
+
globalSettingsRpcCall,
|
|
398
439
|
workspaceDirectoryPicker,
|
|
399
440
|
}),
|
|
400
441
|
}, IMSettingsTab));
|
|
@@ -61,6 +61,15 @@ const CSS = String.raw`
|
|
|
61
61
|
.dim-githubArrow { font-size: 13px; line-height: 1; }
|
|
62
62
|
.dim-githubTooltip { position: absolute; top: calc(100% + 8px); right: 0; z-index: 20; width: max-content; max-width: min(220px, 80vw); padding: 6px 9px; 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-3, #fff); box-shadow: 0 8px 24px rgb(31 35 41 / 14%); font-size: 11px; line-height: 16px; font-weight: 500; white-space: nowrap; opacity: 0; visibility: hidden; transform: translateY(-3px); pointer-events: none; transition: opacity .15s ease, transform .15s ease, visibility .15s ease; }
|
|
63
63
|
.dim-githubAction:hover .dim-githubTooltip, .dim-githubAction:focus-within .dim-githubTooltip { opacity: 1; visibility: visible; transform: translateY(0); }
|
|
64
|
+
.dim-generalSettingsAction { position: relative; display: inline-flex; flex: none; }
|
|
65
|
+
.dim-generalSettingsButton { width: 30px; height: 30px; display: grid; place-items: center; flex: none; padding: 0; border: 1px solid var(--dsw-alias-border-l2, #dfe1e5); border-radius: 8px; color: var(--dsw-alias-label-secondary, #646a73); background: var(--dsw-alias-bg-layer-1, #fff); cursor: pointer; transition: border-color .15s ease, color .15s ease, background .15s ease, box-shadow .15s ease; }
|
|
66
|
+
.dim-generalSettingsButton svg { display: block; }
|
|
67
|
+
.dim-generalSettingsButton:hover { border-color: #aeb3bb; color: var(--dsw-alias-label-primary, #1f2329); background: var(--dsw-alias-interactive-bg-hover, #f7f8fa); }
|
|
68
|
+
.dim-generalSettingsButton[aria-current="page"] { border-color: color-mix(in srgb, var(--dim-blue) 42%, var(--dsw-alias-border-l2, #dfe1e5)); color: var(--dim-blue); background: var(--dim-blue-soft); }
|
|
69
|
+
.dim-generalSettingsButton:focus-visible { outline: 2px solid color-mix(in srgb, var(--dim-blue) 70%, white); outline-offset: 2px; }
|
|
70
|
+
.dim-generalSettingsTooltip { position: absolute; top: calc(100% + 8px); right: 0; z-index: 20; width: max-content; padding: 6px 9px; 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-3, #fff); box-shadow: 0 8px 24px rgb(31 35 41 / 14%); font-size: 11px; line-height: 16px; font-weight: 500; white-space: nowrap; opacity: 0; visibility: hidden; transform: translateY(-3px); pointer-events: none; transition: opacity .15s ease, transform .15s ease, visibility .15s ease; }
|
|
71
|
+
.dim-generalSettingsAction:hover .dim-generalSettingsTooltip, .dim-generalSettingsButton:focus-visible + .dim-generalSettingsTooltip { opacity: 1; visibility: visible; transform: translateY(0); }
|
|
72
|
+
.dim-generalSettingsButton[aria-current="page"] + .dim-generalSettingsTooltip { opacity: 0; visibility: hidden; transform: translateY(-3px); }
|
|
64
73
|
.dim-layout { display: grid; grid-template-columns: 174px 1px minmax(0, 1fr); gap: 24px; align-items: start; }
|
|
65
74
|
.dim-rail { max-height: 520px; display: grid; align-content: start; gap: 8px; overflow-y: auto; padding: 0 4px 1px 1px; scrollbar-width: thin; scrollbar-color: var(--dsw-alias-border-l2, #dfe1e5) transparent; }
|
|
66
75
|
.dim-rail::-webkit-scrollbar { width: 4px; }
|
|
@@ -468,6 +477,46 @@ const CSS = String.raw`
|
|
|
468
477
|
.dim-accessFeedback[data-tone="success"] { color: var(--dsw-alias-state-success-primary, #20a162); }
|
|
469
478
|
.dim-accessFeedback[data-tone="error"] { color: var(--dsw-alias-state-error-primary, #d54941); }
|
|
470
479
|
.dim-accessActions { display: flex; justify-content: flex-end; }
|
|
480
|
+
.dim-generalSettingsPage { min-width: 0; display: grid; }
|
|
481
|
+
.dim-generalSettingsHeader { min-width: 0; margin: 0 0 8px; }
|
|
482
|
+
.dim-generalSettingsHeader h2 { margin: 0; color: var(--dsw-alias-label-primary, #1f2329); font-size: 18px; line-height: 25px; font-weight: 680; }
|
|
483
|
+
.dim-generalSettingsTabsBar { min-width: 0; border-bottom: 1px solid var(--dsw-alias-border-l2, #dfe1e5); }
|
|
484
|
+
.dim-generalSettingsTabs { min-width: 0; display: flex; align-items: flex-end; gap: 24px; overflow-x: auto; scrollbar-width: none; }
|
|
485
|
+
.dim-generalSettingsTabs::-webkit-scrollbar { display: none; }
|
|
486
|
+
.dim-generalSettingsTab { position: relative; min-height: 38px; flex: none; display: inline-flex; align-items: center; justify-content: center; padding: 6px 2px 9px; border: 0; color: var(--dsw-alias-label-secondary, #646a73); background: transparent; font: inherit; font-size: 13px; line-height: 20px; font-weight: 560; white-space: nowrap; cursor: pointer; transition: color .15s ease; }
|
|
487
|
+
.dim-generalSettingsTab::after { content: ''; position: absolute; right: 0; bottom: -1px; left: 0; height: 2px; border-radius: 2px 2px 0 0; background: transparent; transform: scaleX(.45); transition: background .15s ease, transform .15s ease; }
|
|
488
|
+
.dim-generalSettingsTab:hover:not([aria-selected="true"]) { color: var(--dsw-alias-label-primary, #1f2329); }
|
|
489
|
+
.dim-generalSettingsTab[aria-selected="true"] { color: var(--dim-blue); font-weight: 650; }
|
|
490
|
+
.dim-generalSettingsTab[aria-selected="true"]::after { background: var(--dim-blue); transform: scaleX(1); }
|
|
491
|
+
.dim-generalSettingsTab:focus-visible { outline: 2px solid var(--dim-blue); outline-offset: -2px; border-radius: 5px; }
|
|
492
|
+
.dim-generalSettingsTabPanel { min-width: 0; padding-top: 14px; }
|
|
493
|
+
.dim-globalSection { min-width: 0; padding: 14px 16px; border: 1px solid var(--dsw-alias-border-l2, #dfe1e5); border-radius: 12px; background: var(--dsw-alias-bg-layer-3, #fff); }
|
|
494
|
+
.dim-globalHead { min-width: 0; display: flex; align-items: center; }
|
|
495
|
+
.dim-globalHeadTitle { min-width: 0; display: inline-flex; align-items: center; gap: 6px; }
|
|
496
|
+
.dim-globalHead h3 { min-width: 0; overflow: hidden; margin: 0; color: var(--dsw-alias-label-primary, #1f2329); font-size: 15px; line-height: 22px; font-weight: 650; text-overflow: ellipsis; white-space: nowrap; }
|
|
497
|
+
.dim-globalTtlHelp { position: relative; display: inline-flex; flex: none; }
|
|
498
|
+
.dim-globalTtlTooltip { position: absolute; top: calc(100% + 7px); left: 0; z-index: 30; width: max-content; max-width: min(280px, calc(100vw - 48px)); padding: 9px 10px; border: 1px solid var(--dsw-alias-border-l2, #dfe1e5); border-radius: 8px; background: var(--dsw-alias-bg-layer-3, #fff); box-shadow: 0 10px 28px rgb(31 35 41 / 16%); opacity: 0; visibility: hidden; transform: translateY(-3px); pointer-events: none; transition: opacity .15s ease, transform .15s ease, visibility .15s ease; }
|
|
499
|
+
.dim-globalTtlHelp:hover .dim-globalTtlTooltip, .dim-globalTtlHelpButton:focus-visible + .dim-globalTtlTooltip { opacity: 1; visibility: visible; transform: translateY(0); }
|
|
500
|
+
.dim-globalTtlHints { display: grid; gap: 4px; margin: 0; padding: 0; list-style: none; }
|
|
501
|
+
.dim-globalTtlHints li { min-width: 0; display: flex; align-items: baseline; gap: 8px; }
|
|
502
|
+
.dim-globalTtlHints code { flex: none; min-width: 44px; padding: 0 6px; border-radius: 5px; color: var(--dsw-alias-label-primary, #1f2329); background: var(--dsw-alias-bg-module-platform, #f7f8fa); font: 10px/16px ui-monospace, SFMono-Regular, Menlo, monospace; text-align: center; }
|
|
503
|
+
.dim-globalTtlHints span { min-width: 0; color: var(--dsw-alias-label-secondary, #646a73); font-size: 11px; line-height: 16px; }
|
|
504
|
+
.dim-globalTtlRow { display: flex; align-items: center; flex-wrap: wrap; gap: 4px 10px; margin-top: 12px; }
|
|
505
|
+
.dim-globalTtlInput { width: min(160px, 100%); min-width: 110px; max-width: 160px; flex: 1 1 130px; height: 34px; padding: 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: 13px ui-monospace, SFMono-Regular, Menlo, monospace; transition: border-color .16s ease, box-shadow .16s ease; }
|
|
506
|
+
.dim-globalTtlInput:hover:not(:disabled) { border-color: var(--dsw-alias-border-l3, #bbbfc4); }
|
|
507
|
+
.dim-globalTtlInput:focus { outline: none; border-color: var(--dim-blue); box-shadow: 0 0 0 3px color-mix(in srgb, var(--dim-blue) 14%, transparent); }
|
|
508
|
+
.dim-globalTtlInput[aria-invalid="true"] { border-color: color-mix(in srgb, var(--dsw-alias-state-error-primary, #d54941) 62%, var(--dsw-alias-border-l2, #dfe1e5)); }
|
|
509
|
+
.dim-globalTtlInput:disabled { cursor: not-allowed; opacity: .55; }
|
|
510
|
+
.dim-globalSaveButton { min-width: 58px; min-height: 34px; }
|
|
511
|
+
.dim-globalSweepAction { position: relative; display: inline-flex; flex: none; margin-left: auto; }
|
|
512
|
+
.dim-globalSweepButton { flex: none; }
|
|
513
|
+
.dim-globalSweepConfirm { position: absolute; top: calc(100% + 8px); right: 0; z-index: 35; width: 218px; padding: 11px 12px; border: 1px solid var(--dsw-alias-border-l2, #dfe1e5); border-radius: 9px; background: var(--dsw-alias-bg-layer-3, #fff); box-shadow: 0 12px 30px rgb(31 35 41 / 18%); }
|
|
514
|
+
.dim-globalSweepConfirm p { margin: 0; color: var(--dsw-alias-label-primary, #1f2329); font-size: 12px; line-height: 18px; }
|
|
515
|
+
.dim-globalSweepConfirmActions { display: flex; justify-content: flex-end; gap: 7px; margin-top: 10px; }
|
|
516
|
+
.dim-deliveryButton.dim-globalSweepConfirmButton { border-color: var(--dsw-alias-state-error-primary, #d54941); color: #fff; background: var(--dsw-alias-state-error-primary, #d54941); }
|
|
517
|
+
.dim-deliveryButton.dim-globalSweepConfirmButton:hover:not(:disabled) { border-color: color-mix(in srgb, var(--dsw-alias-state-error-primary, #d54941) 82%, #000); background: color-mix(in srgb, var(--dsw-alias-state-error-primary, #d54941) 88%, #000); }
|
|
518
|
+
.dim-globalInline { flex: 1 0 100%; min-width: 0; margin: 2px 0 0; color: var(--dsw-alias-label-secondary, #646a73); font-size: 11px; line-height: 16px; overflow-wrap: anywhere; }
|
|
519
|
+
.dim-globalInline[data-tone="error"] { color: var(--dsw-alias-state-error-primary, #d54941); }
|
|
471
520
|
.dim-panel .dim-botCard .dim-cardFooter { margin-top: 0; }
|
|
472
521
|
.dim-panel .ddt-headingCopy { display: none; }
|
|
473
522
|
.dim-panel .ddt-qrFrame, .dim-panel .ddt-countdown { width: min(270px, 100%); }
|
|
@@ -20,6 +20,10 @@ import { createConnectionSupervisor } from './connection-supervisor.mjs';
|
|
|
20
20
|
import { createHarnessCommandExecutor } from '../../harness-command-executor.mjs';
|
|
21
21
|
import { harnessConnection } from '../../harness-connection.mjs';
|
|
22
22
|
import { createHarnessSessionExecutors } from '../../harness-session-coordinator.mjs';
|
|
23
|
+
import {
|
|
24
|
+
getInboundTtlRuntime,
|
|
25
|
+
registerInboundTtlWorkspaces,
|
|
26
|
+
} from '../../inbound-ttl-runtime.mjs';
|
|
23
27
|
import {
|
|
24
28
|
accessPolicyProvider,
|
|
25
29
|
initialAccessPolicyFor,
|
|
@@ -84,10 +88,18 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|
|
84
88
|
return state;
|
|
85
89
|
};
|
|
86
90
|
const commandExecutor = createHarnessCommandExecutor(ctx, internals.commandExecutor);
|
|
91
|
+
const inboundTtl = internals.inboundTtl ?? getInboundTtlRuntime(ctx, config);
|
|
92
|
+
const inboundTtlService = inboundTtl?.service ?? inboundTtl;
|
|
93
|
+
registerInboundTtlWorkspaces(ctx, inboundTtlService, {
|
|
94
|
+
workspaces,
|
|
95
|
+
configStore: observedConfigStore,
|
|
96
|
+
defaultWorkspace,
|
|
97
|
+
});
|
|
87
98
|
const { controlExecutor, sessionMaintenanceExecutor, fileIngressExecutor } = createHarnessSessionExecutors(ctx, {
|
|
88
99
|
controlExecutor: internals.controlExecutor,
|
|
89
100
|
sessionMaintenanceExecutor: internals.sessionMaintenanceExecutor,
|
|
90
101
|
fileIngressExecutor: internals.fileIngressExecutor,
|
|
102
|
+
inboundTtlService,
|
|
91
103
|
});
|
|
92
104
|
const harness = new Harness({
|
|
93
105
|
...connection,
|
|
@@ -140,6 +140,27 @@ function internalFailure() {
|
|
|
140
140
|
};
|
|
141
141
|
}
|
|
142
142
|
|
|
143
|
+
function publicConnectionFailure(error) {
|
|
144
|
+
if (error?.name !== 'DingtalkPublicConnectionError' || !isRecord(error.publicError)) return null;
|
|
145
|
+
const source = error.publicError;
|
|
146
|
+
const code = typeof source.code === 'string' && /^[a-z][a-z\d-]{1,79}$/.test(source.code)
|
|
147
|
+
? source.code
|
|
148
|
+
: null;
|
|
149
|
+
const message = typeof source.message === 'string' && source.message.trim()
|
|
150
|
+
? source.message.trim().slice(0, 240)
|
|
151
|
+
: null;
|
|
152
|
+
const hint = typeof source.hint === 'string' && source.hint.trim()
|
|
153
|
+
? source.hint.trim().slice(0, 480)
|
|
154
|
+
: null;
|
|
155
|
+
const referenceId = typeof source.referenceId === 'string'
|
|
156
|
+
&& /^DT-CONN-[A-F0-9]{8}$/.test(source.referenceId)
|
|
157
|
+
? source.referenceId
|
|
158
|
+
: null;
|
|
159
|
+
return code && message && hint && referenceId
|
|
160
|
+
? { code, message, hint, referenceId }
|
|
161
|
+
: null;
|
|
162
|
+
}
|
|
163
|
+
|
|
143
164
|
function sanitizePublic(value) {
|
|
144
165
|
if (Array.isArray(value)) return value.map(sanitizePublic);
|
|
145
166
|
if (!isRecord(value)) return value;
|
|
@@ -293,8 +314,11 @@ export function createDingtalkRpcHandler(controller, { encodeQr = qrDataUrl } =
|
|
|
293
314
|
return signal?.aborted ? cancelled() : { ok: true, value };
|
|
294
315
|
} catch (error) {
|
|
295
316
|
const workspaceError = publicWorkspaceError(error);
|
|
317
|
+
const connectionError = publicConnectionFailure(error);
|
|
296
318
|
return signal?.aborted ? cancelled() : workspaceError
|
|
297
319
|
? { ok: false, error: workspaceError }
|
|
320
|
+
: connectionError
|
|
321
|
+
? { ok: false, error: connectionError }
|
|
298
322
|
: internalFailure();
|
|
299
323
|
}
|
|
300
324
|
};
|
|
@@ -7,6 +7,10 @@ import { createConnectionSupervisor } from './connection-supervisor.mjs';
|
|
|
7
7
|
import { createHarnessCommandExecutor } from '../../harness-command-executor.mjs';
|
|
8
8
|
import { harnessConnection } from '../../harness-connection.mjs';
|
|
9
9
|
import { createHarnessSessionExecutors } from '../../harness-session-coordinator.mjs';
|
|
10
|
+
import {
|
|
11
|
+
getInboundTtlRuntime,
|
|
12
|
+
registerInboundTtlWorkspaces,
|
|
13
|
+
} from '../../inbound-ttl-runtime.mjs';
|
|
10
14
|
import { verifyFeishuApp } from '../../../../src/channels/feishu/feishu-app.mjs';
|
|
11
15
|
import { FeishuRuntime } from '../../../../src/channels/feishu/feishu-runtime.mjs';
|
|
12
16
|
import { HarnessClient } from '../../../../src/channels/feishu/harness-client.mjs';
|
|
@@ -158,10 +162,19 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|
|
158
162
|
return stateFor(botConfig);
|
|
159
163
|
};
|
|
160
164
|
const commandExecutor = createHarnessCommandExecutor(ctx, internals.commandExecutor);
|
|
165
|
+
const inboundTtl = internals.inboundTtl ?? getInboundTtlRuntime(ctx, config);
|
|
166
|
+
const inboundTtlService = inboundTtl?.service ?? inboundTtl;
|
|
167
|
+
registerInboundTtlWorkspaces(ctx, inboundTtlService, {
|
|
168
|
+
workspaces,
|
|
169
|
+
configStore: observedConfigStore,
|
|
170
|
+
defaultWorkspace,
|
|
171
|
+
botIdFrom: (bot) => bot?.id,
|
|
172
|
+
});
|
|
161
173
|
const { controlExecutor, sessionMaintenanceExecutor, fileIngressExecutor } = createHarnessSessionExecutors(ctx, {
|
|
162
174
|
controlExecutor: internals.controlExecutor,
|
|
163
175
|
sessionMaintenanceExecutor: internals.sessionMaintenanceExecutor,
|
|
164
176
|
fileIngressExecutor: internals.fileIngressExecutor,
|
|
177
|
+
inboundTtlService,
|
|
165
178
|
});
|
|
166
179
|
const harness = new Harness({
|
|
167
180
|
...connection,
|
|
@@ -202,6 +215,7 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|
|
202
215
|
domain: botConfig.domain,
|
|
203
216
|
botOpenId: botConfig.botOpenId,
|
|
204
217
|
groupResponseMode: botConfig.groupResponseMode,
|
|
218
|
+
groupTopicReply: botConfig.groupTopicReply,
|
|
205
219
|
ownerOpenIds: botConfig.ownerOpenIds ?? [botConfig.ownerOpenId],
|
|
206
220
|
harness: workspaceScope.harness,
|
|
207
221
|
state: workspaceScope.state,
|
|
@@ -287,6 +287,7 @@ function publicBotEntry(entry) {
|
|
|
287
287
|
contextEnhancement: normalizeContextEnhancementConfig(source.contextEnhancement),
|
|
288
288
|
accessPolicy: normalizeAccessPolicy(source.accessPolicy),
|
|
289
289
|
groupResponseMode: normalizeFeishuGroupResponseMode(source.groupResponseMode),
|
|
290
|
+
groupTopicReply: source.groupTopicReply === true,
|
|
290
291
|
groupMessagePermissionGranted: source.groupMessagePermissionGranted === true,
|
|
291
292
|
bot: publicBot(source.bot),
|
|
292
293
|
health: publicHealth(source, connected),
|
|
@@ -440,6 +441,13 @@ function validPayload(endpoint, payload) {
|
|
|
440
441
|
? null
|
|
441
442
|
: '请选择群聊响应方式。';
|
|
442
443
|
}
|
|
444
|
+
if (endpoint === FEISHU_ENDPOINTS.setGroupTopicReply) {
|
|
445
|
+
return hasOnlyKeys(payload, new Set(['botId', 'groupTopicReply']))
|
|
446
|
+
&& safeOpaqueId(payload.botId)
|
|
447
|
+
&& typeof payload.groupTopicReply === 'boolean'
|
|
448
|
+
? null
|
|
449
|
+
: '请选择是否以话题方式回复。';
|
|
450
|
+
}
|
|
443
451
|
return 'Unknown Feishu endpoint.';
|
|
444
452
|
}
|
|
445
453
|
|
|
@@ -710,6 +718,14 @@ export function createFeishuRpcHandler(controller, { encodeQr = qrCodeDataUrl }
|
|
|
710
718
|
await controller.updateGroupResponseMode(payload.botId, payload.groupResponseMode),
|
|
711
719
|
{ encodeQr: cachedEncodeQr },
|
|
712
720
|
);
|
|
721
|
+
} else if (endpoint === FEISHU_ENDPOINTS.setGroupTopicReply) {
|
|
722
|
+
if (typeof controller.updateGroupTopicReply !== 'function') {
|
|
723
|
+
throw new Error('Group topic reply update is unavailable');
|
|
724
|
+
}
|
|
725
|
+
value = await toPublicFeishuStatus(
|
|
726
|
+
await controller.updateGroupTopicReply(payload.botId, payload.groupTopicReply),
|
|
727
|
+
{ encodeQr: cachedEncodeQr },
|
|
728
|
+
);
|
|
713
729
|
} else {
|
|
714
730
|
if (typeof controller.deleteBot !== 'function') throw new Error('Multi-bot delete is unavailable');
|
|
715
731
|
value = await toPublicFeishuStatus(await controller.deleteBot(payload.botId), { encodeQr: cachedEncodeQr });
|
|
@@ -20,6 +20,10 @@ import { createConnectionSupervisor } from './connection-supervisor.mjs';
|
|
|
20
20
|
import { createHarnessCommandExecutor } from '../../harness-command-executor.mjs';
|
|
21
21
|
import { harnessConnection } from '../../harness-connection.mjs';
|
|
22
22
|
import { createHarnessSessionExecutors } from '../../harness-session-coordinator.mjs';
|
|
23
|
+
import {
|
|
24
|
+
getInboundTtlRuntime,
|
|
25
|
+
registerInboundTtlWorkspaces,
|
|
26
|
+
} from '../../inbound-ttl-runtime.mjs';
|
|
23
27
|
import {
|
|
24
28
|
accessPolicyProvider,
|
|
25
29
|
initialAccessPolicyFor,
|
|
@@ -75,10 +79,18 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|
|
75
79
|
return state;
|
|
76
80
|
};
|
|
77
81
|
const commandExecutor = createHarnessCommandExecutor(ctx, internals.commandExecutor);
|
|
82
|
+
const inboundTtl = internals.inboundTtl ?? getInboundTtlRuntime(ctx, config);
|
|
83
|
+
const inboundTtlService = inboundTtl?.service ?? inboundTtl;
|
|
84
|
+
registerInboundTtlWorkspaces(ctx, inboundTtlService, {
|
|
85
|
+
workspaces,
|
|
86
|
+
configStore: observedConfigStore,
|
|
87
|
+
defaultWorkspace,
|
|
88
|
+
});
|
|
78
89
|
const { controlExecutor, sessionMaintenanceExecutor, fileIngressExecutor } = createHarnessSessionExecutors(ctx, {
|
|
79
90
|
controlExecutor: internals.controlExecutor,
|
|
80
91
|
sessionMaintenanceExecutor: internals.sessionMaintenanceExecutor,
|
|
81
92
|
fileIngressExecutor: internals.fileIngressExecutor,
|
|
93
|
+
inboundTtlService,
|
|
82
94
|
});
|
|
83
95
|
const harness = new Harness({
|
|
84
96
|
...connection,
|
|
@@ -6,6 +6,10 @@ import { createTokenConnectionSupervisor } from './connection-supervisor.mjs';
|
|
|
6
6
|
import { createHarnessCommandExecutor } from '../../harness-command-executor.mjs';
|
|
7
7
|
import { harnessConnection } from '../../harness-connection.mjs';
|
|
8
8
|
import { createHarnessSessionExecutors } from '../../harness-session-coordinator.mjs';
|
|
9
|
+
import {
|
|
10
|
+
getInboundTtlRuntime,
|
|
11
|
+
registerInboundTtlWorkspaces,
|
|
12
|
+
} from '../../inbound-ttl-runtime.mjs';
|
|
9
13
|
import {
|
|
10
14
|
BotWorkspaceStore,
|
|
11
15
|
createBotWorkspaceScope,
|
|
@@ -84,10 +88,18 @@ export async function createTokenProductionController(ctx, config, internals, de
|
|
|
84
88
|
return state;
|
|
85
89
|
};
|
|
86
90
|
const commandExecutor = createHarnessCommandExecutor(ctx, internals.commandExecutor);
|
|
91
|
+
const inboundTtl = internals.inboundTtl ?? getInboundTtlRuntime(ctx, config);
|
|
92
|
+
const inboundTtlService = inboundTtl?.service ?? inboundTtl;
|
|
93
|
+
registerInboundTtlWorkspaces(ctx, inboundTtlService, {
|
|
94
|
+
workspaces,
|
|
95
|
+
configStore: observedConfigStore,
|
|
96
|
+
defaultWorkspace,
|
|
97
|
+
});
|
|
87
98
|
const { controlExecutor, sessionMaintenanceExecutor, fileIngressExecutor } = createHarnessSessionExecutors(ctx, {
|
|
88
99
|
controlExecutor: internals.controlExecutor,
|
|
89
100
|
sessionMaintenanceExecutor: internals.sessionMaintenanceExecutor,
|
|
90
101
|
fileIngressExecutor: internals.fileIngressExecutor,
|
|
102
|
+
inboundTtlService,
|
|
91
103
|
});
|
|
92
104
|
const harness = new ResolvedHarness({
|
|
93
105
|
...connection,
|
|
@@ -19,6 +19,10 @@ import { pluginPaths } from '../shared/production.mjs';
|
|
|
19
19
|
import { createHarnessCommandExecutor } from '../../harness-command-executor.mjs';
|
|
20
20
|
import { harnessConnection } from '../../harness-connection.mjs';
|
|
21
21
|
import { createHarnessSessionExecutors } from '../../harness-session-coordinator.mjs';
|
|
22
|
+
import {
|
|
23
|
+
getInboundTtlRuntime,
|
|
24
|
+
registerInboundTtlWorkspaces,
|
|
25
|
+
} from '../../inbound-ttl-runtime.mjs';
|
|
22
26
|
import {
|
|
23
27
|
accessPolicyProvider,
|
|
24
28
|
initialAccessPolicyFor,
|
|
@@ -63,10 +67,18 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|
|
63
67
|
return state;
|
|
64
68
|
};
|
|
65
69
|
const commandExecutor = createHarnessCommandExecutor(ctx, internals.commandExecutor);
|
|
70
|
+
const inboundTtl = internals.inboundTtl ?? getInboundTtlRuntime(ctx, config);
|
|
71
|
+
const inboundTtlService = inboundTtl?.service ?? inboundTtl;
|
|
72
|
+
registerInboundTtlWorkspaces(ctx, inboundTtlService, {
|
|
73
|
+
workspaces,
|
|
74
|
+
configStore: observedConfigStore,
|
|
75
|
+
defaultWorkspace,
|
|
76
|
+
});
|
|
66
77
|
const { controlExecutor, sessionMaintenanceExecutor, fileIngressExecutor } = createHarnessSessionExecutors(ctx, {
|
|
67
78
|
controlExecutor: internals.controlExecutor,
|
|
68
79
|
sessionMaintenanceExecutor: internals.sessionMaintenanceExecutor,
|
|
69
80
|
fileIngressExecutor: internals.fileIngressExecutor,
|
|
81
|
+
inboundTtlService,
|
|
70
82
|
});
|
|
71
83
|
const harness = new ResolvedHarness({
|
|
72
84
|
...connection,
|
|
@@ -20,6 +20,10 @@ import { createConnectionSupervisor } from './connection-supervisor.mjs';
|
|
|
20
20
|
import { createHarnessCommandExecutor } from '../../harness-command-executor.mjs';
|
|
21
21
|
import { harnessConnection } from '../../harness-connection.mjs';
|
|
22
22
|
import { createHarnessSessionExecutors } from '../../harness-session-coordinator.mjs';
|
|
23
|
+
import {
|
|
24
|
+
getInboundTtlRuntime,
|
|
25
|
+
registerInboundTtlWorkspaces,
|
|
26
|
+
} from '../../inbound-ttl-runtime.mjs';
|
|
23
27
|
import {
|
|
24
28
|
accessPolicyProvider,
|
|
25
29
|
initialAccessPolicyFor,
|
|
@@ -78,10 +82,18 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|
|
78
82
|
return state;
|
|
79
83
|
};
|
|
80
84
|
const commandExecutor = createHarnessCommandExecutor(ctx, internals.commandExecutor);
|
|
85
|
+
const inboundTtl = internals.inboundTtl ?? getInboundTtlRuntime(ctx, config);
|
|
86
|
+
const inboundTtlService = inboundTtl?.service ?? inboundTtl;
|
|
87
|
+
registerInboundTtlWorkspaces(ctx, inboundTtlService, {
|
|
88
|
+
workspaces,
|
|
89
|
+
configStore: observedConfigStore,
|
|
90
|
+
defaultWorkspace,
|
|
91
|
+
});
|
|
81
92
|
const { controlExecutor, sessionMaintenanceExecutor, fileIngressExecutor } = createHarnessSessionExecutors(ctx, {
|
|
82
93
|
controlExecutor: internals.controlExecutor,
|
|
83
94
|
sessionMaintenanceExecutor: internals.sessionMaintenanceExecutor,
|
|
84
95
|
fileIngressExecutor: internals.fileIngressExecutor,
|
|
96
|
+
inboundTtlService,
|
|
85
97
|
});
|
|
86
98
|
const harness = new Harness({
|
|
87
99
|
...connection,
|
|
@@ -23,6 +23,10 @@ import { createConnectionSupervisor } from './connection-supervisor.mjs';
|
|
|
23
23
|
import { createHarnessCommandExecutor } from '../../harness-command-executor.mjs';
|
|
24
24
|
import { harnessConnection } from '../../harness-connection.mjs';
|
|
25
25
|
import { createHarnessSessionExecutors } from '../../harness-session-coordinator.mjs';
|
|
26
|
+
import {
|
|
27
|
+
getInboundTtlRuntime,
|
|
28
|
+
registerInboundTtlWorkspaces,
|
|
29
|
+
} from '../../inbound-ttl-runtime.mjs';
|
|
26
30
|
import {
|
|
27
31
|
accessPolicyProvider,
|
|
28
32
|
initialAccessPolicyFor,
|
|
@@ -81,10 +85,18 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|
|
81
85
|
return state;
|
|
82
86
|
};
|
|
83
87
|
const commandExecutor = createHarnessCommandExecutor(ctx, internals.commandExecutor);
|
|
88
|
+
const inboundTtl = internals.inboundTtl ?? getInboundTtlRuntime(ctx, config);
|
|
89
|
+
const inboundTtlService = inboundTtl?.service ?? inboundTtl;
|
|
90
|
+
registerInboundTtlWorkspaces(ctx, inboundTtlService, {
|
|
91
|
+
workspaces,
|
|
92
|
+
configStore: observedConfigStore,
|
|
93
|
+
defaultWorkspace,
|
|
94
|
+
});
|
|
84
95
|
const { controlExecutor, sessionMaintenanceExecutor, fileIngressExecutor } = createHarnessSessionExecutors(ctx, {
|
|
85
96
|
controlExecutor: internals.controlExecutor,
|
|
86
97
|
sessionMaintenanceExecutor: internals.sessionMaintenanceExecutor,
|
|
87
98
|
fileIngressExecutor: internals.fileIngressExecutor,
|
|
99
|
+
inboundTtlService,
|
|
88
100
|
});
|
|
89
101
|
const harness = new Harness({
|
|
90
102
|
...connection,
|
|
@@ -23,6 +23,10 @@ import { createTokenConnectionSupervisor } from '../shared/connection-supervisor
|
|
|
23
23
|
import { createHarnessCommandExecutor } from '../../harness-command-executor.mjs';
|
|
24
24
|
import { harnessConnection } from '../../harness-connection.mjs';
|
|
25
25
|
import { createHarnessSessionExecutors } from '../../harness-session-coordinator.mjs';
|
|
26
|
+
import {
|
|
27
|
+
getInboundTtlRuntime,
|
|
28
|
+
registerInboundTtlWorkspaces,
|
|
29
|
+
} from '../../inbound-ttl-runtime.mjs';
|
|
26
30
|
import {
|
|
27
31
|
accessPolicyProvider,
|
|
28
32
|
initialAccessPolicyFor,
|
|
@@ -84,10 +88,18 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|
|
84
88
|
return state;
|
|
85
89
|
};
|
|
86
90
|
const commandExecutor = createHarnessCommandExecutor(ctx, internals.commandExecutor);
|
|
91
|
+
const inboundTtl = internals.inboundTtl ?? getInboundTtlRuntime(ctx, config);
|
|
92
|
+
const inboundTtlService = inboundTtl?.service ?? inboundTtl;
|
|
93
|
+
registerInboundTtlWorkspaces(ctx, inboundTtlService, {
|
|
94
|
+
workspaces,
|
|
95
|
+
configStore: observedConfigStore,
|
|
96
|
+
defaultWorkspace,
|
|
97
|
+
});
|
|
87
98
|
const { controlExecutor, sessionMaintenanceExecutor, fileIngressExecutor } = createHarnessSessionExecutors(ctx, {
|
|
88
99
|
controlExecutor: internals.controlExecutor,
|
|
89
100
|
sessionMaintenanceExecutor: internals.sessionMaintenanceExecutor,
|
|
90
101
|
fileIngressExecutor: internals.fileIngressExecutor,
|
|
102
|
+
inboundTtlService,
|
|
91
103
|
});
|
|
92
104
|
const harness = new Harness({
|
|
93
105
|
...connection,
|
|
@@ -144,6 +144,7 @@ export function createDeliveryAdapter({ channel, workspaces, coreController, sta
|
|
|
144
144
|
return Object.freeze({
|
|
145
145
|
channel,
|
|
146
146
|
ownsBot: (botId) => workspaces.has(botId),
|
|
147
|
+
listBots: () => workspaces.listBotIds(),
|
|
147
148
|
listTargets: (botId) => workspaces.listDeliveryTargets(botId),
|
|
148
149
|
async listSuggestions(botId) {
|
|
149
150
|
const state = await stateFor(botId);
|
|
@@ -7,6 +7,7 @@ const DRAFT_TARGET_ID = '__test__';
|
|
|
7
7
|
|
|
8
8
|
const ADAPTER_METHODS = Object.freeze([
|
|
9
9
|
'ownsBot',
|
|
10
|
+
'listBots',
|
|
10
11
|
'listTargets',
|
|
11
12
|
'listSuggestions',
|
|
12
13
|
'createTarget',
|
|
@@ -118,6 +119,20 @@ export class DeliveryService {
|
|
|
118
119
|
}
|
|
119
120
|
}
|
|
120
121
|
|
|
122
|
+
async listBots() {
|
|
123
|
+
const bots = [];
|
|
124
|
+
for (const { adapter } of this.#adapters.values()) {
|
|
125
|
+
try {
|
|
126
|
+
const ids = await adapter.listBots();
|
|
127
|
+
if (!Array.isArray(ids)) throw new TypeError('Adapter returned invalid bots');
|
|
128
|
+
for (const botId of ids) bots.push({ botId: botIdOf(botId), channel: adapter.channel });
|
|
129
|
+
} catch (error) {
|
|
130
|
+
throw publicOperationError(error);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return bots;
|
|
134
|
+
}
|
|
135
|
+
|
|
121
136
|
async listSuggestions(botId) {
|
|
122
137
|
const id = botIdOf(botId);
|
|
123
138
|
const adapter = await this.#adapterFor(id);
|