@xmanrui/dsh-im 4.17.0 → 4.18.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 +20 -14
- package/README.md +19 -16
- package/lib/client.js +447 -257
- package/lib/index.js +285 -263
- package/package.json +10 -4
- package/plugin-src/client/access-policy-settings.js +5 -0
- package/plugin-src/client/channel-logos.js +10 -0
- package/plugin-src/client/channels/imessage/api.js +12 -0
- package/plugin-src/client/channels/imessage/index.js +45 -0
- package/plugin-src/client/channels/imessage/styles.js +19 -0
- package/plugin-src/client/channels/shared/token-api.js +1 -0
- package/plugin-src/client/channels/shared/token-channel.js +5 -1
- package/plugin-src/client/delivery-settings.js +5 -0
- package/plugin-src/client/i18n.js +23 -1
- package/plugin-src/client/index.js +35 -14
- package/plugin-src/client/session-channel-logos.js +2 -0
- package/plugin-src/client/styles.js +1 -0
- package/plugin-src/host/channels/dingtalk/rpc.mjs +2 -4
- package/plugin-src/host/channels/feishu/rpc.mjs +2 -4
- package/plugin-src/host/channels/imessage/index.mjs +21 -0
- package/plugin-src/host/channels/imessage/production.mjs +13 -0
- package/plugin-src/host/channels/imessage/rpc.mjs +70 -0
- package/plugin-src/host/channels/office/rpc.mjs +2 -1
- package/plugin-src/host/channels/qq/rpc.mjs +2 -4
- package/plugin-src/host/channels/shared/access-policy-production.mjs +1 -1
- package/plugin-src/host/channels/shared/rpc.mjs +2 -4
- package/plugin-src/host/channels/shared/startup.mjs +2 -4
- package/plugin-src/host/channels/slack/rpc.mjs +2 -4
- package/plugin-src/host/channels/telegram/rpc.mjs +2 -4
- package/plugin-src/host/channels/wecom/rpc.mjs +2 -4
- package/plugin-src/host/channels/wecom-app/rpc.mjs +2 -4
- package/plugin-src/host/channels/weixin/rpc.mjs +2 -4
- package/plugin-src/host/channels/whatsapp/rpc.mjs +2 -4
- package/plugin-src/host/delivery-adapter.mjs +4 -0
- package/plugin-src/host/delivery-rpc.mjs +2 -4
- package/plugin-src/host/inbound-ttl-rpc.mjs +2 -4
- package/plugin-src/host/index.mjs +4 -1
- package/plugin-src/host/modern-harness-api.mjs +1 -1
- package/plugin-src/host/update-rpc.mjs +2 -1
- package/plugin-src/management-rpc.mjs +77 -0
- package/src/channels/imessage/config-store.mjs +24 -0
- package/src/channels/imessage/controller.mjs +37 -0
- package/src/channels/imessage/harness-client.mjs +7 -0
- package/src/channels/imessage/imessage-api.mjs +194 -0
- package/src/channels/imessage/imessage-bridge.mjs +16 -0
- package/src/channels/imessage/runtime.mjs +98 -0
- package/src/channels/imessage/state-store.mjs +3 -0
- package/src/channels/shared/i18n-en/shared-a.mjs +2 -2
- package/src/channels/shared/message-failure.mjs +5 -5
- package/src/channels/shared/session-channel-labels.mjs +1 -0
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xmanrui/dsh-im",
|
|
3
|
-
"version": "4.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "4.18.0",
|
|
4
|
+
"description": "把十一种 IM 渠道和公网 AI Office 接入本机 DeepSeek Harness。 Connect eleven IM channels and a public AI Office to a local DeepSeek Harness.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"deepseek-harness",
|
|
7
7
|
"dsh",
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
"telegram",
|
|
21
21
|
"discord",
|
|
22
22
|
"whatsapp",
|
|
23
|
+
"imessage",
|
|
23
24
|
"ai-office"
|
|
24
25
|
],
|
|
25
26
|
"author": "xmanrui (https://github.com/xmanrui)",
|
|
@@ -43,6 +44,10 @@
|
|
|
43
44
|
{
|
|
44
45
|
"name": "masucc",
|
|
45
46
|
"url": "https://github.com/masucc"
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"name": "cherryFloris",
|
|
50
|
+
"url": "https://github.com/cherryFloris"
|
|
46
51
|
}
|
|
47
52
|
],
|
|
48
53
|
"license": "MIT",
|
|
@@ -97,12 +102,13 @@
|
|
|
97
102
|
"platform": "web"
|
|
98
103
|
},
|
|
99
104
|
"compatibility": {
|
|
100
|
-
"dsh": "0.1.2-alpha.4 || 0.1.2-alpha.5 || 0.1.2-rc.1 || 0.1.3-alpha.1",
|
|
105
|
+
"dsh": "0.1.2-alpha.4 || 0.1.2-alpha.5 || 0.1.2-rc.1 || 0.1.3-alpha.1 || 0.1.5-alpha.1",
|
|
101
106
|
"dshReleases": {
|
|
102
107
|
"0.1.2-alpha.4": "compatible",
|
|
103
108
|
"0.1.2-alpha.5": "compatible",
|
|
104
109
|
"0.1.2-rc.1": "compatible",
|
|
105
|
-
"0.1.3-alpha.1": "compatible"
|
|
110
|
+
"0.1.3-alpha.1": "compatible",
|
|
111
|
+
"0.1.5-alpha.1": "compatible"
|
|
106
112
|
},
|
|
107
113
|
"profiles": [
|
|
108
114
|
"web"
|
|
@@ -69,6 +69,11 @@ export const ACCESS_CHANNEL_DEFINITIONS = Object.freeze({
|
|
|
69
69
|
groupUserLabel: '群成员电话号码或 JID',
|
|
70
70
|
groupPlaceholder: '8613800000000 或完整 JID',
|
|
71
71
|
}),
|
|
72
|
+
imessage: Object.freeze({
|
|
73
|
+
directUserLabel: 'iMessage Chat GUID',
|
|
74
|
+
directPlaceholder: 'iMessage;+;chat123',
|
|
75
|
+
groupSupported: false,
|
|
76
|
+
}),
|
|
72
77
|
});
|
|
73
78
|
|
|
74
79
|
function clonePolicy(policy) {
|
|
@@ -132,6 +132,16 @@ export function WhatsappLogoGlyph({ size } = {}) {
|
|
|
132
132
|
}));
|
|
133
133
|
}
|
|
134
134
|
|
|
135
|
+
export function IMessageLogoGlyph({ size } = {}) {
|
|
136
|
+
return h('svg', {
|
|
137
|
+
...dimensions(size), viewBox: '0 0 24 24', focusable: 'false',
|
|
138
|
+
'aria-hidden': 'true', 'data-im-channel-logo': 'imessage',
|
|
139
|
+
}, h('path', {
|
|
140
|
+
fill: 'currentColor',
|
|
141
|
+
d: 'M12 2.2c-5.52 0-10 3.75-10 8.38 0 2.63 1.52 4.98 3.9 6.52l-.92 3.43a.5.5 0 0 0 .72.56l3.54-1.96c.87.23 1.8.35 2.76.35 5.52 0 10-3.75 10-8.9S17.52 2.2 12 2.2Zm-3.4 8.2a1.2 1.2 0 1 1 0 2.4 1.2 1.2 0 0 1 0-2.4Zm3.4 0a1.2 1.2 0 1 1 0 2.4 1.2 1.2 0 0 1 0-2.4Zm3.4 0a1.2 1.2 0 1 1 0 2.4 1.2 1.2 0 0 1 0-2.4Z',
|
|
142
|
+
}));
|
|
143
|
+
}
|
|
144
|
+
|
|
135
145
|
export function OfficeLogoGlyph({ size } = {}) {
|
|
136
146
|
return h('svg', {
|
|
137
147
|
...dimensions(size),
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { TOKEN_BOT_ENDPOINTS, createTokenChannelApi } from '../shared/token-api.js';
|
|
2
|
+
|
|
3
|
+
export const IMESSAGE_RPC_CHANNEL = '/imessage';
|
|
4
|
+
export const IMESSAGE_ENDPOINTS = Object.freeze({
|
|
5
|
+
...TOKEN_BOT_ENDPOINTS,
|
|
6
|
+
bindCredentials: 'bot.bind-native',
|
|
7
|
+
});
|
|
8
|
+
const api = createTokenChannelApi('iMessage', ' macOS Messages 连接');
|
|
9
|
+
export { api as imessageClientApi };
|
|
10
|
+
export const unwrapRpcResult = api.unwrapRpcResult;
|
|
11
|
+
export const normalizeSnapshot = api.normalizeSnapshot;
|
|
12
|
+
export const presentError = api.presentError;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { IMessageLogoGlyph } from '../../channel-logos.js';
|
|
3
|
+
import { createTokenChannelSettings } from '../shared/token-channel.js';
|
|
4
|
+
import { IMESSAGE_ENDPOINTS, imessageClientApi } from './api.js';
|
|
5
|
+
import { installIMessageStyles } from './styles.js';
|
|
6
|
+
import { h } from '../../i18n.js';
|
|
7
|
+
|
|
8
|
+
function PermissionPanel({ permissions, busy, error, onSubmit, onCancel }) {
|
|
9
|
+
const databaseReady = permissions?.database === 'granted';
|
|
10
|
+
const automationReady = permissions?.automation === 'granted';
|
|
11
|
+
return h('section', { className: 'ddt-card dim-surfaceCard dim-imessagePermissionPanel' },
|
|
12
|
+
h('h3', null, '启用 macOS 原生 iMessage'),
|
|
13
|
+
h('p', null, '支持同账号自聊:在 iPhone 或 Mac 上给自己已登录的 iMessage 邮箱或号码发指令。AI 回复以 🤖 DSH 开头,不会再次触发机器人。'),
|
|
14
|
+
h('p', null, 'DeepSeek Harness 通过 macOS Messages.app 收发文本消息。首次使用需要授予以下权限:'),
|
|
15
|
+
h('ol', { className: 'dim-imessagePermissionSteps' },
|
|
16
|
+
h('li', null,
|
|
17
|
+
h('strong', null, databaseReady ? '✓ 完全磁盘访问权限已授予' : '1. 授予完全磁盘访问权限'),
|
|
18
|
+
databaseReady ? null : h('p', null, '在“系统设置 → 隐私与安全性 → 完全磁盘访问权限”中,打开运行 DeepSeek Harness 的终端或应用。'),
|
|
19
|
+
databaseReady ? null : h('a', { href: 'x-apple.systempreferences:com.apple.preference.security?Privacy_AllFiles' }, '打开完全磁盘访问权限设置')),
|
|
20
|
+
h('li', null,
|
|
21
|
+
h('strong', null, automationReady ? '✓ Messages 自动化权限已授予' : '2. 允许自动化控制 Messages'),
|
|
22
|
+
automationReady ? null : h('p', null, '在“系统设置 → 隐私与安全性 → 自动化”中,允许运行 DeepSeek Harness 的应用控制 Messages。'),
|
|
23
|
+
automationReady ? null : h('a', { href: 'x-apple.systempreferences:com.apple.preference.security?Privacy_Automation' }, '打开自动化设置'))),
|
|
24
|
+
error ? h('p', { className: 'dim-inlineError', role: 'alert' }, error.message ?? String(error)) : null,
|
|
25
|
+
h('div', { className: 'ddt-actions dim-viewActions' },
|
|
26
|
+
h('button', { type: 'button', className: 'ddt-button', onClick: onCancel, disabled: busy }, '取消'),
|
|
27
|
+
h('button', { type: 'button', className: 'ddt-button', 'data-kind': 'primary', onClick: () => onSubmit({}), disabled: busy },
|
|
28
|
+
busy ? '正在检查…' : '检查权限并启用')));
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const channel = createTokenChannelSettings({
|
|
32
|
+
channel: 'iMessage', endpoints: IMESSAGE_ENDPOINTS, api: imessageClientApi,
|
|
33
|
+
LogoGlyph: IMessageLogoGlyph, installStyles: installIMessageStyles,
|
|
34
|
+
pageClass: 'dim-pageIMessage', avatarClass: 'dim-avatarIMessage',
|
|
35
|
+
connectionLabel: ' macOS Messages 连接',
|
|
36
|
+
tokenPlaceholder: '', emptyTitle: '接入 macOS 原生 iMessage',
|
|
37
|
+
emptyDescription: '无需安装第三方服务;请按指引授予 macOS Messages 访问权限。',
|
|
38
|
+
platformLabel: 'macOS Messages', credentialPayload: () => ({}),
|
|
39
|
+
CredentialPanel: PermissionPanel,
|
|
40
|
+
credentialAriaLabel: '配置 macOS Messages 权限', credentialOpenLabel: '配置本机权限',
|
|
41
|
+
credentialNoun: 'macOS Messages 权限', emptyActionLabel: '配置本机权限',
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
export const IMessageSettingsTab = channel.SettingsTab;
|
|
45
|
+
export const IMessageAccountCard = channel.AccountCard;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export const IMESSAGE_STYLE_ID = 'xmanrui-dsh-im-imessage-settings';
|
|
2
|
+
const CSS = String.raw`
|
|
3
|
+
.dim-pageIMessage { --ddt-accent: #32a852; --ddt-accent-deep: #248a40; --ddt-accent-wash: #edf9f0; }
|
|
4
|
+
.dim-avatarIMessage { color: #fff; background: #32a852; }
|
|
5
|
+
.dim-avatarIMessage svg { display: block; }
|
|
6
|
+
.dim-imessagePermissionPanel { padding: 20px; }
|
|
7
|
+
.dim-imessagePermissionPanel p { color: var(--dsw-alias-label-secondary, #646a73); }
|
|
8
|
+
.dim-imessagePermissionSteps { margin: 16px 0; padding-left: 22px; }
|
|
9
|
+
.dim-imessagePermissionSteps li { margin: 12px 0; }
|
|
10
|
+
.dim-imessagePermissionSteps a { color: var(--ddt-accent-deep); }
|
|
11
|
+
`;
|
|
12
|
+
export function installIMessageStyles() {
|
|
13
|
+
if (typeof document === 'undefined') return () => {};
|
|
14
|
+
const existing = document.querySelector(`style[data-plugin-css="${IMESSAGE_STYLE_ID}"]`);
|
|
15
|
+
if (existing) return () => {};
|
|
16
|
+
const style = document.createElement('style'); style.dataset.plugin = '@xmanrui/dsh-im';
|
|
17
|
+
style.dataset.pluginCss = IMESSAGE_STYLE_ID; style.textContent = CSS; document.head.appendChild(style);
|
|
18
|
+
return () => style.remove();
|
|
19
|
+
}
|
|
@@ -101,6 +101,7 @@ export function createTokenChannelApi(channel, connectionSummary, {
|
|
|
101
101
|
totals: { configured: bots.length, connected: bots.filter((bot) => bot.connected).length },
|
|
102
102
|
agentPresetCatalog: normalizeAgentPresetCatalog(source.agentPresetCatalog),
|
|
103
103
|
modelCatalog: normalizeModelCatalog(source.modelCatalog),
|
|
104
|
+
...(isRecord(source.permissions) ? { permissions: source.permissions } : {}),
|
|
104
105
|
};
|
|
105
106
|
};
|
|
106
107
|
|
|
@@ -182,7 +182,7 @@ export function createTokenChannelSettings(definition) {
|
|
|
182
182
|
const [model, setModel] = React.useState({
|
|
183
183
|
phase: 'loading', bots: [], totals: { configured: 0, connected: 0 }, error: null,
|
|
184
184
|
agentPresetCatalog: EMPTY_AGENT_PRESET_CATALOG,
|
|
185
|
-
modelCatalog: EMPTY_MODEL_CATALOG,
|
|
185
|
+
modelCatalog: EMPTY_MODEL_CATALOG, permissions: null,
|
|
186
186
|
});
|
|
187
187
|
const [credentialOpen, setCredentialOpen] = React.useState(false);
|
|
188
188
|
const [credentialError, setCredentialError] = React.useState(null);
|
|
@@ -221,6 +221,7 @@ export function createTokenChannelSettings(definition) {
|
|
|
221
221
|
phase: 'ready', bots: snapshot.bots, totals: snapshot.totals, error: null,
|
|
222
222
|
agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG,
|
|
223
223
|
modelCatalog: snapshot.modelCatalog ?? EMPTY_MODEL_CATALOG,
|
|
224
|
+
permissions: snapshot.permissions ?? null,
|
|
224
225
|
});
|
|
225
226
|
} catch (error) {
|
|
226
227
|
if (error?.name !== 'AbortError' && mounted.current && !signal?.aborted
|
|
@@ -268,6 +269,7 @@ export function createTokenChannelSettings(definition) {
|
|
|
268
269
|
phase: 'ready', bots: snapshot.bots, totals: snapshot.totals, error: null,
|
|
269
270
|
agentPresetCatalog: snapshot.agentPresetCatalog ?? EMPTY_AGENT_PRESET_CATALOG,
|
|
270
271
|
modelCatalog: snapshot.modelCatalog ?? EMPTY_MODEL_CATALOG,
|
|
272
|
+
permissions: snapshot.permissions ?? null,
|
|
271
273
|
});
|
|
272
274
|
}
|
|
273
275
|
setCredentialOpen(false);
|
|
@@ -418,6 +420,8 @@ export function createTokenChannelSettings(definition) {
|
|
|
418
420
|
: h(React.Fragment, null,
|
|
419
421
|
credentialOpen ? (CredentialPanel
|
|
420
422
|
? h(CredentialPanel, {
|
|
423
|
+
channel,
|
|
424
|
+
permissions: model.permissions,
|
|
421
425
|
busy,
|
|
422
426
|
error: credentialError,
|
|
423
427
|
onSubmit: bindCredentials,
|
|
@@ -115,6 +115,11 @@ const CHANNEL_DEFINITIONS = Object.freeze({
|
|
|
115
115
|
group: [{ key: 'jid', label: '群 JID', placeholder: '1234567890@g.us' }],
|
|
116
116
|
},
|
|
117
117
|
},
|
|
118
|
+
imessage: {
|
|
119
|
+
label: 'iMessage',
|
|
120
|
+
kinds: [{ value: 'user', label: '私聊' }],
|
|
121
|
+
fields: { user: [{ key: 'chatGuid', label: 'Chat GUID', placeholder: 'iMessage;+;chat123' }] },
|
|
122
|
+
},
|
|
118
123
|
});
|
|
119
124
|
|
|
120
125
|
export const DELIVERY_CHANNEL_DEFINITIONS = CHANNEL_DEFINITIONS;
|
|
@@ -4,6 +4,27 @@ export const IM_LOCALE_NAMESPACE = 'dsh-im';
|
|
|
4
4
|
|
|
5
5
|
const EN = Object.freeze({
|
|
6
6
|
'$locale': 'en',
|
|
7
|
+
' macOS Messages 连接': ' macOS Messages connection',
|
|
8
|
+
'接入 iMessage': 'Connect iMessage',
|
|
9
|
+
'先在 macOS 系统设置中授予 Messages 权限。': 'Grant Messages permissions in macOS System Settings first.',
|
|
10
|
+
'配置 macOS Messages 权限': 'Configure macOS Messages permissions',
|
|
11
|
+
'配置本机权限': 'Configure local permissions',
|
|
12
|
+
'启用 macOS 原生 iMessage': 'Enable native macOS iMessage',
|
|
13
|
+
'支持同账号自聊:在 iPhone 或 Mac 上给自己已登录的 iMessage 邮箱或号码发指令。AI 回复以 🤖 DSH 开头,不会再次触发机器人。': 'Self-chat is supported: send instructions from your iPhone or Mac to your signed-in iMessage email or phone number. AI replies start with 🤖 DSH and do not trigger the bot again.',
|
|
14
|
+
'检查权限并启用': 'Check permissions and enable',
|
|
15
|
+
'DeepSeek Harness 通过 macOS Messages.app 收发文本消息。首次使用需要授予以下权限:': 'DeepSeek Harness sends and receives messages through macOS Messages.app. The following permissions are required the first time:',
|
|
16
|
+
'✓ 完全磁盘访问权限已授予': '✓ Full Disk Access granted',
|
|
17
|
+
'1. 授予完全磁盘访问权限': '1. Grant Full Disk Access',
|
|
18
|
+
'在“系统设置 → 隐私与安全性 → 完全磁盘访问权限”中,打开运行 DeepSeek Harness 的终端或应用。': 'In System Settings → Privacy & Security → Full Disk Access, enable the terminal or app running DeepSeek Harness.',
|
|
19
|
+
'打开完全磁盘访问权限设置': 'Open Full Disk Access settings',
|
|
20
|
+
'✓ Messages 自动化权限已授予': '✓ Messages automation granted',
|
|
21
|
+
'2. 允许自动化控制 Messages': '2. Allow automation control of Messages',
|
|
22
|
+
'在“系统设置 → 隐私与安全性 → 自动化”中,允许运行 DeepSeek Harness 的应用控制 Messages。': 'In System Settings → Privacy & Security → Automation, allow the app running DeepSeek Harness to control Messages.',
|
|
23
|
+
'打开自动化设置': 'Open Automation settings',
|
|
24
|
+
'正在检查…': 'Checking…',
|
|
25
|
+
'接入 macOS 原生 iMessage': 'Connect native macOS iMessage',
|
|
26
|
+
'无需安装第三方服务;请按指引授予 macOS Messages 访问权限。': 'No third-party service is required; follow the guide to grant macOS Messages access.',
|
|
27
|
+
'macOS Messages 权限': 'macOS Messages permissions',
|
|
7
28
|
'IM机器人': 'IM bots',
|
|
8
29
|
'IM机器人设置': 'IM bot settings',
|
|
9
30
|
'更多机器人设置': 'More bot settings',
|
|
@@ -1062,7 +1083,8 @@ function translateDynamic(text) {
|
|
|
1062
1083
|
|
|
1063
1084
|
const phrases = [
|
|
1064
1085
|
['企业微信', 'WeCom'], ['DeepSeek Harness', 'DeepSeek Harness'],
|
|
1065
|
-
['WhatsApp', 'WhatsApp'], ['
|
|
1086
|
+
['WhatsApp', 'WhatsApp'], ['iMessage', 'iMessage'], ['macOS Messages', 'macOS Messages'],
|
|
1087
|
+
['Telegram', 'Telegram'], ['Discord', 'Discord'],
|
|
1066
1088
|
['Slack', 'Slack'], ['飞书', 'Feishu'], ['钉钉', 'DingTalk'], ['微信', 'WeChat'],
|
|
1067
1089
|
['机器人', 'bot'], ['账号', 'account'], ['应用', 'app'], ['凭据', 'credentials'],
|
|
1068
1090
|
['服务返回了无法识别的响应', 'service returned an unrecognized response'],
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { callManagementRpc } from '../management-rpc.mjs';
|
|
1
2
|
import * as React from 'react';
|
|
2
3
|
import manifest from '../../package.json' with { type: 'json' };
|
|
3
4
|
|
|
@@ -12,6 +13,7 @@ import {
|
|
|
12
13
|
WecomLogoGlyph,
|
|
13
14
|
WeixinLogoGlyph,
|
|
14
15
|
WhatsappLogoGlyph,
|
|
16
|
+
IMessageLogoGlyph,
|
|
15
17
|
} from './channel-logos.js';
|
|
16
18
|
import { DINGTALK_RPC_CHANNEL } from './channels/dingtalk/api.js';
|
|
17
19
|
import { DingtalkSettingsTab } from './channels/dingtalk/index.js';
|
|
@@ -45,6 +47,9 @@ import { installWeixinStyles } from './channels/weixin/styles.js';
|
|
|
45
47
|
import { WHATSAPP_RPC_CHANNEL } from './channels/whatsapp/api.js';
|
|
46
48
|
import { WhatsappSettingsTab } from './channels/whatsapp/index.js';
|
|
47
49
|
import { installWhatsappStyles } from './channels/whatsapp/styles.js';
|
|
50
|
+
import { IMESSAGE_RPC_CHANNEL } from './channels/imessage/api.js';
|
|
51
|
+
import { IMessageSettingsTab } from './channels/imessage/index.js';
|
|
52
|
+
import { installIMessageStyles } from './channels/imessage/styles.js';
|
|
48
53
|
import { en, h, IM_LOCALE_NAMESPACE, setImTranslator, zh } from './i18n.js';
|
|
49
54
|
import { BotSettingsContext } from './channel-card-meta.js';
|
|
50
55
|
import {
|
|
@@ -91,6 +96,7 @@ const CHANNELS = Object.freeze([
|
|
|
91
96
|
{ id: 'discord', label: 'Discord' },
|
|
92
97
|
{ id: 'whatsapp', label: 'WhatsApp' },
|
|
93
98
|
{ id: 'wecomApp', label: '企业微信应用', note: '(实验功能)' },
|
|
99
|
+
{ id: 'imessage', label: 'iMessage', note: '(实验功能)' },
|
|
94
100
|
{ id: 'office', label: 'AI Office', note: '(实验功能)' },
|
|
95
101
|
]);
|
|
96
102
|
|
|
@@ -141,6 +147,11 @@ function WhatsappLogo() {
|
|
|
141
147
|
h(WhatsappLogoGlyph));
|
|
142
148
|
}
|
|
143
149
|
|
|
150
|
+
function IMessageLogo() {
|
|
151
|
+
return h('span', { className: 'dim-logo dim-logoIMessage', 'aria-hidden': 'true' },
|
|
152
|
+
h(IMessageLogoGlyph));
|
|
153
|
+
}
|
|
154
|
+
|
|
144
155
|
function OfficeLogo() {
|
|
145
156
|
return h('span', { className: 'dim-logo dim-logoOffice', 'aria-hidden': 'true' },
|
|
146
157
|
h(OfficeLogoGlyph));
|
|
@@ -157,6 +168,7 @@ function ChannelLogo({ channel }) {
|
|
|
157
168
|
if (channel === 'telegram') return h(TelegramLogo);
|
|
158
169
|
if (channel === 'discord') return h(DiscordLogo);
|
|
159
170
|
if (channel === 'whatsapp') return h(WhatsappLogo);
|
|
171
|
+
if (channel === 'imessage') return h(IMessageLogo);
|
|
160
172
|
return h(OfficeLogo);
|
|
161
173
|
}
|
|
162
174
|
|
|
@@ -180,6 +192,7 @@ export function IMSettingsTab({
|
|
|
180
192
|
dingtalkRpcCall,
|
|
181
193
|
discordRpcCall,
|
|
182
194
|
feishuRpcCall,
|
|
195
|
+
imessageRpcCall,
|
|
183
196
|
qqRpcCall,
|
|
184
197
|
slackRpcCall,
|
|
185
198
|
telegramRpcCall,
|
|
@@ -230,6 +243,7 @@ export function IMSettingsTab({
|
|
|
230
243
|
updateRpcCall,
|
|
231
244
|
deliveryRpcCall,
|
|
232
245
|
globalSettingsRpcCall,
|
|
246
|
+
imessageRpcCall,
|
|
233
247
|
}, {
|
|
234
248
|
location: browserLocation,
|
|
235
249
|
onRecovery: reportLoopbackRecovery,
|
|
@@ -240,6 +254,7 @@ export function IMSettingsTab({
|
|
|
240
254
|
deliveryRpcCall,
|
|
241
255
|
feishuRpcCall,
|
|
242
256
|
globalSettingsRpcCall,
|
|
257
|
+
imessageRpcCall,
|
|
243
258
|
officeRpcCall,
|
|
244
259
|
qqRpcCall,
|
|
245
260
|
reportLoopbackRecovery,
|
|
@@ -368,6 +383,8 @@ export function IMSettingsTab({
|
|
|
368
383
|
? h(DiscordSettingsTab, { rpcCall: rpcCalls.discordRpcCall })
|
|
369
384
|
: active.id === 'whatsapp'
|
|
370
385
|
? h(WhatsappSettingsTab, { rpcCall: rpcCalls.whatsappRpcCall })
|
|
386
|
+
: active.id === 'imessage'
|
|
387
|
+
? h(IMessageSettingsTab, { rpcCall: rpcCalls.imessageRpcCall })
|
|
371
388
|
: h(OfficeSettingsTab, { rpcCall: rpcCalls.officeRpcCall }))),
|
|
372
389
|
),
|
|
373
390
|
));
|
|
@@ -394,6 +411,7 @@ export function apply(ctx) {
|
|
|
394
411
|
installTelegramStyles(),
|
|
395
412
|
installDiscordStyles(),
|
|
396
413
|
installWhatsappStyles(),
|
|
414
|
+
installIMessageStyles(),
|
|
397
415
|
installOfficeStyles(),
|
|
398
416
|
installImStyles(),
|
|
399
417
|
];
|
|
@@ -403,33 +421,35 @@ export function apply(ctx) {
|
|
|
403
421
|
}, 'im-settings: install combined channel styles');
|
|
404
422
|
|
|
405
423
|
const feishuRpcCall = (endpoint, payload, signal) =>
|
|
406
|
-
ctx.connection
|
|
424
|
+
callManagementRpc(ctx.connection, FEISHU_RPC_CHANNEL, endpoint, payload, signal);
|
|
407
425
|
const weixinRpcCall = (endpoint, payload, signal) =>
|
|
408
|
-
ctx.connection
|
|
426
|
+
callManagementRpc(ctx.connection, WEIXIN_RPC_CHANNEL, endpoint, payload, signal);
|
|
409
427
|
const dingtalkRpcCall = (endpoint, payload, signal) =>
|
|
410
|
-
ctx.connection
|
|
428
|
+
callManagementRpc(ctx.connection, DINGTALK_RPC_CHANNEL, endpoint, payload, signal);
|
|
411
429
|
const qqRpcCall = (endpoint, payload, signal) =>
|
|
412
|
-
ctx.connection
|
|
430
|
+
callManagementRpc(ctx.connection, QQ_RPC_CHANNEL, endpoint, payload, signal);
|
|
413
431
|
const wecomRpcCall = (endpoint, payload, signal) =>
|
|
414
|
-
ctx.connection
|
|
432
|
+
callManagementRpc(ctx.connection, WECOM_RPC_CHANNEL, endpoint, payload, signal);
|
|
415
433
|
const wecomAppRpcCall = (endpoint, payload, signal) =>
|
|
416
|
-
ctx.connection
|
|
434
|
+
callManagementRpc(ctx.connection, WECOM_APP_RPC_CHANNEL, endpoint, payload, signal);
|
|
417
435
|
const telegramRpcCall = (endpoint, payload, signal) =>
|
|
418
|
-
ctx.connection
|
|
436
|
+
callManagementRpc(ctx.connection, TELEGRAM_RPC_CHANNEL, endpoint, payload, signal);
|
|
419
437
|
const discordRpcCall = (endpoint, payload, signal) =>
|
|
420
|
-
ctx.connection
|
|
438
|
+
callManagementRpc(ctx.connection, DISCORD_RPC_CHANNEL, endpoint, payload, signal);
|
|
421
439
|
const whatsappRpcCall = (endpoint, payload, signal) =>
|
|
422
|
-
ctx.connection
|
|
440
|
+
callManagementRpc(ctx.connection, WHATSAPP_RPC_CHANNEL, endpoint, payload, signal);
|
|
441
|
+
const imessageRpcCall = (endpoint, payload, signal) =>
|
|
442
|
+
callManagementRpc(ctx.connection, IMESSAGE_RPC_CHANNEL, endpoint, payload, signal);
|
|
423
443
|
const slackRpcCall = (endpoint, payload, signal) =>
|
|
424
|
-
ctx.connection
|
|
444
|
+
callManagementRpc(ctx.connection, SLACK_RPC_CHANNEL, endpoint, payload, signal);
|
|
425
445
|
const officeRpcCall = (endpoint, payload, signal) =>
|
|
426
|
-
ctx.connection
|
|
446
|
+
callManagementRpc(ctx.connection, OFFICE_RPC_CHANNEL, endpoint, payload, signal);
|
|
427
447
|
const updateRpcCall = (endpoint, payload, signal) =>
|
|
428
|
-
ctx.connection
|
|
448
|
+
callManagementRpc(ctx.connection, UPDATE_RPC_CHANNEL, endpoint, payload, signal);
|
|
429
449
|
const deliveryRpcCall = (endpoint, payload, signal) =>
|
|
430
|
-
ctx.connection
|
|
450
|
+
callManagementRpc(ctx.connection, DELIVERY_RPC_CHANNEL, endpoint, payload, signal);
|
|
431
451
|
const globalSettingsRpcCall = (endpoint, payload, signal) =>
|
|
432
|
-
ctx.connection
|
|
452
|
+
callManagementRpc(ctx.connection, GLOBAL_SETTINGS_RPC_CHANNEL, endpoint, payload, signal);
|
|
433
453
|
const workspaceDirectoryPicker = Object.freeze({
|
|
434
454
|
listDirectory: (path, signal) =>
|
|
435
455
|
callWorkspaceDirectoryApi(ctx, 'listDirectory', path, signal),
|
|
@@ -453,6 +473,7 @@ export function apply(ctx) {
|
|
|
453
473
|
wecomAppRpcCall,
|
|
454
474
|
weixinRpcCall,
|
|
455
475
|
whatsappRpcCall,
|
|
476
|
+
imessageRpcCall,
|
|
456
477
|
officeRpcCall,
|
|
457
478
|
updateRpcCall,
|
|
458
479
|
deliveryRpcCall,
|
|
@@ -2,6 +2,7 @@ import { parseSessionChannelTitle } from '../../src/channels/shared/session-chan
|
|
|
2
2
|
import {
|
|
3
3
|
WeixinLogoGlyph, FeishuLogoGlyph, DingtalkLogoGlyph, WecomLogoGlyph, QqLogoGlyph,
|
|
4
4
|
SlackLogoGlyph, TelegramLogoGlyph, DiscordLogoGlyph, WhatsappLogoGlyph, OfficeLogoGlyph,
|
|
5
|
+
IMessageLogoGlyph,
|
|
5
6
|
} from './channel-logos.js';
|
|
6
7
|
|
|
7
8
|
const GLYPHS = Object.freeze({
|
|
@@ -14,6 +15,7 @@ const GLYPHS = Object.freeze({
|
|
|
14
15
|
telegram: [TelegramLogoGlyph, '#229ed9'],
|
|
15
16
|
discord: [DiscordLogoGlyph, '#5865f2'],
|
|
16
17
|
whatsapp: [WhatsappLogoGlyph, '#25d366'],
|
|
18
|
+
imessage: [IMessageLogoGlyph, '#3478f6'],
|
|
17
19
|
office: [OfficeLogoGlyph, '#f97316'],
|
|
18
20
|
});
|
|
19
21
|
const CHANNEL_ATTR = 'data-dsh-im-session-channel';
|
|
@@ -97,6 +97,7 @@ const CSS = String.raw`
|
|
|
97
97
|
.dim-logoSlack { color: white; background: #4a154b; }
|
|
98
98
|
.dim-logoSlack svg { width: 21px; height: 21px; }
|
|
99
99
|
.dim-logoWhatsapp { color: white; background: #25d366; }
|
|
100
|
+
.dim-logoIMessage { color: white; background: #32a852; }
|
|
100
101
|
.dim-logoWhatsapp svg { width: 21px; height: 21px; }
|
|
101
102
|
.dim-channelCopy { min-width: 0; display: grid; }
|
|
102
103
|
.dim-channelCopy strong { overflow: hidden; color: inherit; font-size: 14px; line-height: 20px; font-weight: 680; text-overflow: ellipsis; white-space: nowrap; }
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { registerManagementRpc } from '../../../management-rpc.mjs';
|
|
1
2
|
import QRCode from 'qrcode';
|
|
2
3
|
import { SET_CONTEXT_ENHANCEMENT_ENDPOINT, validContextEnhancementPayload } from '../shared/context-enhancement-rpc.mjs';
|
|
3
4
|
import { SET_ACCESS_POLICY_ENDPOINT, validAccessPolicyPayload } from '../shared/access-policy-rpc.mjs';
|
|
@@ -336,10 +337,7 @@ export function createDingtalkRpcHandler(controller, { encodeQr = qrDataUrl } =
|
|
|
336
337
|
}
|
|
337
338
|
|
|
338
339
|
export function installDingtalkRpc(ctx, controller, options, authority) {
|
|
339
|
-
|
|
340
|
-
throw new TypeError('DSH Host Connection RPC is required');
|
|
341
|
-
}
|
|
342
|
-
return ctx.connection.rpc.handle(
|
|
340
|
+
return registerManagementRpc(ctx,
|
|
343
341
|
DINGTALK_RPC_CHANNEL,
|
|
344
342
|
createDingtalkRpcHandler(controller, options),
|
|
345
343
|
{ authority: resolveRpcAuthority(authority) },
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { registerManagementRpc } from '../../../management-rpc.mjs';
|
|
1
2
|
import QRCode from 'qrcode';
|
|
2
3
|
import {
|
|
3
4
|
normalizeAgentPresetCatalog,
|
|
@@ -795,10 +796,7 @@ export function createFeishuRpcHandler(controller, { encodeQr = qrCodeDataUrl }
|
|
|
795
796
|
|
|
796
797
|
/** Register the `/feishu` logical channel with its configured browser authority. */
|
|
797
798
|
export function installFeishuRpc(ctx, controller, options, authority) {
|
|
798
|
-
|
|
799
|
-
throw new TypeError('DSH Host Connection RPC is required');
|
|
800
|
-
}
|
|
801
|
-
return ctx.connection.rpc.handle(
|
|
799
|
+
return registerManagementRpc(ctx,
|
|
802
800
|
FEISHU_RPC_CHANNEL,
|
|
803
801
|
createFeishuRpcHandler(controller, options),
|
|
804
802
|
{ authority: resolveRpcAuthority(authority) },
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { createProductionController } from './production.mjs';
|
|
2
|
+
import { installIMessageRpc } from './rpc.mjs';
|
|
3
|
+
|
|
4
|
+
export const name = 'dsh-im-imessage-host';
|
|
5
|
+
export const inject = ['connection', 'credentials', 'typertGateway'];
|
|
6
|
+
|
|
7
|
+
export async function apply(ctx, config = {}) {
|
|
8
|
+
if (config?.controller) return installIMessageRpc(ctx, config.controller, config.rpcAuthority);
|
|
9
|
+
const production = await createProductionController(ctx, config, config.internals ?? {});
|
|
10
|
+
const unregisterDelivery = config.deliveryService && production.deliveryAdapter
|
|
11
|
+
? config.deliveryService.registerAdapter(production.deliveryAdapter) : undefined;
|
|
12
|
+
const disposeRpc = installIMessageRpc(ctx, production.controller, config.rpcAuthority);
|
|
13
|
+
ctx.effect(() => async () => {
|
|
14
|
+
await unregisterDelivery?.(); await production.close();
|
|
15
|
+
}, 'dsh-im: close iMessage connections');
|
|
16
|
+
return disposeRpc;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export { createProductionController } from './production.mjs';
|
|
20
|
+
export { IMESSAGE_ENDPOINTS, IMESSAGE_RPC_CHANNEL, IMESSAGE_RPC_ENDPOINTS,
|
|
21
|
+
createIMessageRpcHandler, installIMessageRpc } from './rpc.mjs';
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { IMessageConfigStore } from '../../../../src/channels/imessage/config-store.mjs';
|
|
2
|
+
import { IMessageController } from '../../../../src/channels/imessage/controller.mjs';
|
|
3
|
+
import { IMessageHarnessClient } from '../../../../src/channels/imessage/harness-client.mjs';
|
|
4
|
+
import { IMessageRuntime } from '../../../../src/channels/imessage/runtime.mjs';
|
|
5
|
+
import { IMessageStateStore } from '../../../../src/channels/imessage/state-store.mjs';
|
|
6
|
+
import { createTokenProductionController } from '../shared/production.mjs';
|
|
7
|
+
|
|
8
|
+
export function createProductionController(ctx, config = {}, internals = {}) {
|
|
9
|
+
return createTokenProductionController(ctx, config, internals, {
|
|
10
|
+
channel: 'imessage', ConfigStore: IMessageConfigStore, StateStore: IMessageStateStore,
|
|
11
|
+
HarnessClient: IMessageHarnessClient, Controller: IMessageController, Runtime: IMessageRuntime,
|
|
12
|
+
});
|
|
13
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { createTokenBotRpcHandler } from '../shared/rpc.mjs';
|
|
2
|
+
import { registerManagementRpc } from '../../../management-rpc.mjs';
|
|
3
|
+
import { resolveRpcAuthority } from '../../rpc-authority.mjs';
|
|
4
|
+
|
|
5
|
+
export const IMESSAGE_RPC_CHANNEL = '/imessage';
|
|
6
|
+
export const IMESSAGE_ENDPOINTS = Object.freeze({
|
|
7
|
+
status: 'connection.status',
|
|
8
|
+
bindNative: 'bot.bind-native',
|
|
9
|
+
bindCredentials: 'bot.bind-native',
|
|
10
|
+
permissions: 'permissions.status',
|
|
11
|
+
reconnectBot: 'bot.reconnect',
|
|
12
|
+
deleteBot: 'bot.delete',
|
|
13
|
+
setWorkspace: 'bot.workspace.set',
|
|
14
|
+
setModel: 'bot.model.set',
|
|
15
|
+
setAgentPreset: 'bot.agent-preset.set',
|
|
16
|
+
setContextEnhancement: 'bot.context-enhancement.set',
|
|
17
|
+
setAccessPolicy: 'bot.access-policy.set',
|
|
18
|
+
});
|
|
19
|
+
export const IMESSAGE_RPC_ENDPOINTS = Object.freeze(Object.values(IMESSAGE_ENDPOINTS));
|
|
20
|
+
|
|
21
|
+
function withRpcDetails(result) {
|
|
22
|
+
if (result?.ok !== false) return result;
|
|
23
|
+
return {
|
|
24
|
+
...result,
|
|
25
|
+
error: { ...result.error, details: result.error?.details ?? {} },
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function createIMessageRpcHandler(controller) {
|
|
30
|
+
const tokenHandler = createTokenBotRpcHandler(controller, {
|
|
31
|
+
channel: 'iMessage',
|
|
32
|
+
});
|
|
33
|
+
return async (endpoint, payload, signal) => {
|
|
34
|
+
if (endpoint === IMESSAGE_ENDPOINTS.status) {
|
|
35
|
+
const value = await controller.status();
|
|
36
|
+
return { ok: true, value: { ...value, permissions: await controller.permissions() } };
|
|
37
|
+
}
|
|
38
|
+
if (endpoint === IMESSAGE_ENDPOINTS.permissions) {
|
|
39
|
+
try {
|
|
40
|
+
return { ok: true, value: await controller.permissions() };
|
|
41
|
+
} catch (error) {
|
|
42
|
+
return withRpcDetails({
|
|
43
|
+
ok: false,
|
|
44
|
+
error: { code: 'permissions-check-failed', message: error.message },
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
if (endpoint === IMESSAGE_ENDPOINTS.bindNative) {
|
|
49
|
+
try {
|
|
50
|
+
return { ok: true, value: await controller.bindNative() };
|
|
51
|
+
} catch (error) {
|
|
52
|
+
return withRpcDetails({
|
|
53
|
+
ok: false,
|
|
54
|
+
error: { code: error.code ?? 'imessage-bind-failed', message: error.message },
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
const translated = endpoint === IMESSAGE_ENDPOINTS.setModel ? 'bot.model.set' : endpoint;
|
|
59
|
+
return withRpcDetails(await tokenHandler(translated, payload, signal));
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function installIMessageRpc(ctx, controller, authority) {
|
|
64
|
+
return registerManagementRpc(
|
|
65
|
+
ctx,
|
|
66
|
+
IMESSAGE_RPC_CHANNEL,
|
|
67
|
+
createIMessageRpcHandler(controller),
|
|
68
|
+
{ authority: resolveRpcAuthority(authority) },
|
|
69
|
+
);
|
|
70
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { registerManagementRpc } from '../../../management-rpc.mjs';
|
|
1
2
|
import { resolveRpcAuthority } from '../../rpc-authority.mjs';
|
|
2
3
|
import { OFFICE_RPC_CHANNEL, OFFICE_RPC_ENDPOINTS } from '../../../../src/channels/office/protocol.mjs';
|
|
3
4
|
|
|
@@ -40,7 +41,7 @@ export function createOfficeRpcHandler(controller) {
|
|
|
40
41
|
}
|
|
41
42
|
|
|
42
43
|
export function installOfficeRpc(ctx, controller, authority) {
|
|
43
|
-
return ctx
|
|
44
|
+
return registerManagementRpc(ctx,
|
|
44
45
|
OFFICE_RPC_CHANNEL,
|
|
45
46
|
createOfficeRpcHandler(controller),
|
|
46
47
|
{ authority: resolveRpcAuthority(authority) },
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { registerManagementRpc } from '../../../management-rpc.mjs';
|
|
1
2
|
import QRCode from 'qrcode';
|
|
2
3
|
import {
|
|
3
4
|
connectionTestTargetUnavailable,
|
|
@@ -227,10 +228,7 @@ export function createQqRpcHandler(controller, { encodeQr = qrDataUrl } = {}) {
|
|
|
227
228
|
}
|
|
228
229
|
|
|
229
230
|
export function installQqRpc(ctx, controller, options, authority) {
|
|
230
|
-
|
|
231
|
-
throw new TypeError('DSH Host Connection RPC is required');
|
|
232
|
-
}
|
|
233
|
-
return ctx.connection.rpc.handle(
|
|
231
|
+
return registerManagementRpc(ctx,
|
|
234
232
|
QQ_RPC_CHANNEL,
|
|
235
233
|
createQqRpcHandler(controller, options),
|
|
236
234
|
{ authority: resolveRpcAuthority(authority) },
|
|
@@ -91,7 +91,7 @@ export function initialAccessPolicyFor(channel, config = {}) {
|
|
|
91
91
|
group: allowlistScope(),
|
|
92
92
|
});
|
|
93
93
|
}
|
|
94
|
-
if (['dingtalk', 'wecom', 'wecom-app', 'slack', 'discord'].includes(key)) {
|
|
94
|
+
if (['dingtalk', 'wecom', 'wecom-app', 'slack', 'discord', 'imessage'].includes(key)) {
|
|
95
95
|
return createAccessPolicy({ direct: openScope(), group: openScope() });
|
|
96
96
|
}
|
|
97
97
|
throw new TypeError(`Unsupported access-policy channel: ${channel}`);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { registerManagementRpc } from '../../../management-rpc.mjs';
|
|
1
2
|
import { SET_CONTEXT_ENHANCEMENT_ENDPOINT, validContextEnhancementPayload } from './context-enhancement-rpc.mjs';
|
|
2
3
|
import { SET_ACCESS_POLICY_ENDPOINT, validAccessPolicyPayload } from './access-policy-rpc.mjs';
|
|
3
4
|
import { resolveRpcAuthority } from '../../rpc-authority.mjs';
|
|
@@ -197,10 +198,7 @@ export function createTokenBotRpcHandler(controller, { channel }) {
|
|
|
197
198
|
}
|
|
198
199
|
|
|
199
200
|
export function installTokenBotRpc(ctx, controller, { channel, rpcChannel, authority }) {
|
|
200
|
-
|
|
201
|
-
throw new TypeError('DSH Host Connection RPC is required');
|
|
202
|
-
}
|
|
203
|
-
return ctx.connection.rpc.handle(
|
|
201
|
+
return registerManagementRpc(ctx,
|
|
204
202
|
rpcChannel,
|
|
205
203
|
createTokenBotRpcHandler(controller, { channel }),
|
|
206
204
|
{ authority: resolveRpcAuthority(authority) },
|