@xmanrui/dsh-im 4.3.0 → 4.5.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 -2
- package/README.md +2 -2
- package/lib/client.js +976 -634
- package/lib/index.js +241 -238
- package/package.json +1 -1
- package/plugin-src/client/access-policy-settings.js +351 -0
- package/plugin-src/client/channel-card-meta.js +2 -1
- package/plugin-src/client/channels/dingtalk/api.js +5 -0
- package/plugin-src/client/channels/dingtalk/index.js +1 -0
- package/plugin-src/client/channels/feishu/api.js +5 -0
- package/plugin-src/client/channels/feishu/index.js +1 -0
- package/plugin-src/client/channels/qq/api.js +5 -0
- package/plugin-src/client/channels/qq/index.js +1 -0
- package/plugin-src/client/channels/shared/token-api.js +5 -0
- package/plugin-src/client/channels/shared/token-channel.js +1 -0
- package/plugin-src/client/channels/telegram/api.js +2 -17
- package/plugin-src/client/channels/telegram/index.js +0 -112
- package/plugin-src/client/channels/telegram/styles.js +0 -28
- package/plugin-src/client/channels/wecom/api.js +5 -0
- package/plugin-src/client/channels/wecom/index.js +1 -0
- package/plugin-src/client/channels/weixin/api.js +5 -0
- package/plugin-src/client/channels/weixin/index.js +1 -0
- package/plugin-src/client/channels/whatsapp/api.js +4 -10
- package/plugin-src/client/channels/whatsapp/index.js +1 -125
- package/plugin-src/client/channels/whatsapp/styles.js +0 -25
- package/plugin-src/client/context-enhancement.js +19 -5
- package/plugin-src/client/delivery-settings.js +61 -9
- package/plugin-src/client/i18n.js +61 -2
- package/plugin-src/client/index.js +1 -0
- package/plugin-src/client/styles.js +50 -4
- package/plugin-src/host/channels/dingtalk/production.mjs +12 -1
- package/plugin-src/host/channels/dingtalk/rpc.mjs +11 -0
- package/plugin-src/host/channels/feishu/production.mjs +39 -1
- package/plugin-src/host/channels/feishu/rpc.mjs +19 -2
- package/plugin-src/host/channels/qq/production.mjs +12 -1
- package/plugin-src/host/channels/qq/rpc.mjs +11 -0
- package/plugin-src/host/channels/shared/access-policy-production.mjs +137 -0
- package/plugin-src/host/channels/shared/access-policy-rpc.mjs +17 -0
- package/plugin-src/host/channels/shared/production.mjs +15 -1
- package/plugin-src/host/channels/shared/rpc.mjs +9 -0
- package/plugin-src/host/channels/slack/production.mjs +12 -1
- package/plugin-src/host/channels/slack/rpc.mjs +10 -0
- package/plugin-src/host/channels/telegram/rpc.mjs +2 -49
- package/plugin-src/host/channels/wecom/production.mjs +12 -1
- package/plugin-src/host/channels/wecom/rpc.mjs +11 -0
- package/plugin-src/host/channels/weixin/production.mjs +12 -1
- package/plugin-src/host/channels/weixin/rpc.mjs +11 -0
- package/plugin-src/host/channels/whatsapp/production.mjs +16 -2
- package/plugin-src/host/channels/whatsapp/rpc.mjs +10 -15
- package/plugin-src/host/modern-harness-api.mjs +3 -0
- package/src/channels/dingtalk/dingtalk-bridge.mjs +70 -12
- package/src/channels/dingtalk/dingtalk-runtime.mjs +4 -0
- package/src/channels/discord/discord-runtime.mjs +29 -1
- package/src/channels/feishu/bridge.mjs +207 -82
- package/src/channels/feishu/feishu-cards.mjs +2 -2
- package/src/channels/feishu/feishu-channel.mjs +36 -6
- package/src/channels/feishu/feishu-runtime.mjs +4 -0
- package/src/channels/qq/qq-bridge.mjs +67 -20
- package/src/channels/qq/qq-runtime.mjs +20 -2
- package/src/channels/shared/access-policy.mjs +210 -0
- package/src/channels/shared/bot-workspace-store.mjs +181 -33
- package/src/channels/shared/command-permission.mjs +32 -0
- package/src/channels/shared/compact-command.mjs +5 -2
- package/src/channels/shared/context-enhancement.mjs +9 -4
- package/src/channels/shared/harness-client.mjs +6 -0
- package/src/channels/shared/i18n-en/feishu.mjs +3 -3
- package/src/channels/shared/i18n-en/shared-a.mjs +2 -1
- package/src/channels/shared/i18n-en/shared-b.mjs +6 -3
- package/src/channels/shared/i18n-en/shared-c.mjs +2 -0
- package/src/channels/shared/inbound-access.mjs +43 -0
- package/src/channels/shared/session-title.mjs +74 -0
- package/src/channels/shared/text-harness-bridge.mjs +54 -6
- package/src/channels/shared/workspace-command.mjs +26 -5
- package/src/channels/shared/workspace-session.mjs +28 -2
- package/src/channels/slack/slack-runtime.mjs +4 -0
- package/src/channels/telegram/telegram-runtime.mjs +6 -13
- package/src/channels/wecom/wecom-bridge.mjs +57 -6
- package/src/channels/wecom/wecom-runtime.mjs +4 -0
- package/src/channels/weixin/weixin-bridge.mjs +67 -19
- package/src/channels/weixin/weixin-runtime.mjs +4 -0
- package/src/channels/whatsapp/whatsapp-runtime.mjs +42 -20
|
@@ -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
|
+
accessPolicyProvider,
|
|
24
|
+
initialAccessPolicyFor,
|
|
25
|
+
} from '../shared/access-policy-production.mjs';
|
|
22
26
|
|
|
23
27
|
export async function createProductionController(ctx, config = {}, internals = {}) {
|
|
24
28
|
if (!ctx?.credentials) throw new TypeError('dsh-im slack requires ctx.credentials');
|
|
@@ -43,6 +47,7 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|
|
43
47
|
await workspaces.reconcile(configuredBots.map((bot) => bot.botId));
|
|
44
48
|
await Promise.all(configuredBots.map((bot) => workspaces.ensure(bot.botId, {
|
|
45
49
|
defaultAgentPreset: config.agentPreset,
|
|
50
|
+
initialAccessPolicy: initialAccessPolicyFor('slack', bot),
|
|
46
51
|
})));
|
|
47
52
|
const observedConfigStore = typeof configStore.remove === 'function'
|
|
48
53
|
? observeBotWorkspaceRemovals(configStore, { workspaces })
|
|
@@ -80,7 +85,10 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|
|
80
85
|
...(internals.inspectCredentials ? { inspectCredentials: internals.inspectCredentials } : {}),
|
|
81
86
|
createRuntime: async ({ botId, config: botConfig, botToken, appToken }) => {
|
|
82
87
|
const state = await stateFor(botId);
|
|
83
|
-
await workspaces.ensure(botId, {
|
|
88
|
+
await workspaces.ensure(botId, {
|
|
89
|
+
defaultAgentPreset: config.agentPreset,
|
|
90
|
+
initialAccessPolicy: initialAccessPolicyFor('slack', botConfig),
|
|
91
|
+
});
|
|
84
92
|
const workspaceScope = createBotWorkspaceScope(harness, {
|
|
85
93
|
botId, workspaces, state, agentPresetCatalog,
|
|
86
94
|
});
|
|
@@ -91,6 +99,9 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|
|
91
99
|
harness: workspaceScope.harness,
|
|
92
100
|
state: workspaceScope.state,
|
|
93
101
|
contextEnhancement: { botId, getSettings: () => workspaces.contextEnhancementFor(botId) },
|
|
102
|
+
accessPolicy: accessPolicyProvider(workspaces, botId, {
|
|
103
|
+
channel: 'slack', config: botConfig,
|
|
104
|
+
}),
|
|
94
105
|
replyTimeoutMs: config.replyTimeoutMs ?? 600_000,
|
|
95
106
|
connectTimeoutMs: config.connectTimeoutMs ?? 20_000,
|
|
96
107
|
logger: {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { SET_CONTEXT_ENHANCEMENT_ENDPOINT, validContextEnhancementPayload } from '../shared/context-enhancement-rpc.mjs';
|
|
2
|
+
import { SET_ACCESS_POLICY_ENDPOINT, validAccessPolicyPayload } from '../shared/access-policy-rpc.mjs';
|
|
2
3
|
import { resolveRpcAuthority } from '../../rpc-authority.mjs';
|
|
3
4
|
import { publicConnectionTestResult } from '../../../../src/channels/shared/connection-test.mjs';
|
|
4
5
|
import {
|
|
@@ -20,6 +21,7 @@ export const SLACK_ENDPOINTS = Object.freeze({
|
|
|
20
21
|
setWorkspace: SET_WORKSPACE_ENDPOINT,
|
|
21
22
|
setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
|
|
22
23
|
setContextEnhancement: SET_CONTEXT_ENHANCEMENT_ENDPOINT,
|
|
24
|
+
setAccessPolicy: SET_ACCESS_POLICY_ENDPOINT,
|
|
23
25
|
});
|
|
24
26
|
export const SLACK_RPC_ENDPOINTS = Object.freeze(Object.values(SLACK_ENDPOINTS));
|
|
25
27
|
|
|
@@ -81,6 +83,10 @@ function payloadFailure(endpoint, payload) {
|
|
|
81
83
|
return validContextEnhancementPayload(payload)
|
|
82
84
|
? null : '请提交有效的上下文增强设置。';
|
|
83
85
|
}
|
|
86
|
+
if (endpoint === SLACK_ENDPOINTS.setAccessPolicy) {
|
|
87
|
+
return validAccessPolicyPayload(payload)
|
|
88
|
+
? null : '请提交有效的访问设置。';
|
|
89
|
+
}
|
|
84
90
|
return 'Unknown Slack endpoint.';
|
|
85
91
|
}
|
|
86
92
|
|
|
@@ -164,6 +170,10 @@ export function createSlackRpcHandler(controller) {
|
|
|
164
170
|
if (typeof controller.updateContextEnhancement !== 'function') throw new Error('Context enhancement update is unavailable');
|
|
165
171
|
value = await controller.updateContextEnhancement(payload.botId, payload.config);
|
|
166
172
|
}
|
|
173
|
+
else if (endpoint === SLACK_ENDPOINTS.setAccessPolicy) {
|
|
174
|
+
if (typeof controller.updateAccessPolicy !== 'function') throw new Error('Access policy update is unavailable');
|
|
175
|
+
value = await controller.updateAccessPolicy(payload.botId, payload.policy);
|
|
176
|
+
}
|
|
167
177
|
else if (endpoint === SLACK_ENDPOINTS.setAgentPreset) {
|
|
168
178
|
if (typeof controller.updateAgentPreset !== 'function') throw new Error('Agent preset update is unavailable');
|
|
169
179
|
value = await controller.updateAgentPreset(payload.botId, payload.agentPreset);
|
|
@@ -3,60 +3,13 @@ import {
|
|
|
3
3
|
createTokenBotRpcHandler,
|
|
4
4
|
} from '../shared/rpc.mjs';
|
|
5
5
|
import { resolveRpcAuthority } from '../../rpc-authority.mjs';
|
|
6
|
-
import { normalizeTelegramAccessPolicy } from '../../../../src/channels/telegram/config-store.mjs';
|
|
7
6
|
|
|
8
7
|
export const TELEGRAM_RPC_CHANNEL = '/telegram';
|
|
9
|
-
export const TELEGRAM_ENDPOINTS =
|
|
10
|
-
...TOKEN_BOT_ENDPOINTS,
|
|
11
|
-
setAccessPolicy: 'bot.access-policy.set',
|
|
12
|
-
});
|
|
8
|
+
export const TELEGRAM_ENDPOINTS = TOKEN_BOT_ENDPOINTS;
|
|
13
9
|
export const TELEGRAM_RPC_ENDPOINTS = Object.freeze(Object.values(TELEGRAM_ENDPOINTS));
|
|
14
10
|
|
|
15
11
|
export function createTelegramRpcHandler(controller) {
|
|
16
|
-
|
|
17
|
-
throw new TypeError('A complete Telegram controller is required (setAccessPolicy)');
|
|
18
|
-
}
|
|
19
|
-
const sharedHandler = createTokenBotRpcHandler(controller, { channel: 'Telegram' });
|
|
20
|
-
return async (endpoint, payload, signal) => {
|
|
21
|
-
if (endpoint !== TELEGRAM_ENDPOINTS.setAccessPolicy) {
|
|
22
|
-
return sharedHandler(endpoint, payload, signal);
|
|
23
|
-
}
|
|
24
|
-
if (signal?.aborted) {
|
|
25
|
-
return { ok: false, error: { code: 'cancelled', message: 'The request was cancelled.' } };
|
|
26
|
-
}
|
|
27
|
-
const keys = payload && typeof payload === 'object' && !Array.isArray(payload)
|
|
28
|
-
? Object.keys(payload) : [];
|
|
29
|
-
if (keys.length !== 3 || !keys.every((key) => (
|
|
30
|
-
['botId', 'accessMode', 'allowedUsers'].includes(key)
|
|
31
|
-
)) || typeof payload.botId !== 'string' || !/^[A-Za-z0-9_-]{1,128}$/.test(payload.botId)) {
|
|
32
|
-
return {
|
|
33
|
-
ok: false,
|
|
34
|
-
error: { code: 'bad-request', message: 'bot.access-policy.set requires a valid policy.' },
|
|
35
|
-
};
|
|
36
|
-
}
|
|
37
|
-
let accessPolicy;
|
|
38
|
-
try {
|
|
39
|
-
accessPolicy = normalizeTelegramAccessPolicy(payload);
|
|
40
|
-
} catch {
|
|
41
|
-
return {
|
|
42
|
-
ok: false,
|
|
43
|
-
error: { code: 'bad-request', message: '请输入有效的 Telegram 访问模式和数字 User ID。' },
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
try {
|
|
47
|
-
const value = await controller.setAccessPolicy(payload.botId, accessPolicy);
|
|
48
|
-
return signal?.aborted
|
|
49
|
-
? { ok: false, error: { code: 'cancelled', message: 'The request was cancelled.' } }
|
|
50
|
-
: { ok: true, value };
|
|
51
|
-
} catch {
|
|
52
|
-
return signal?.aborted
|
|
53
|
-
? { ok: false, error: { code: 'cancelled', message: 'The request was cancelled.' } }
|
|
54
|
-
: {
|
|
55
|
-
ok: false,
|
|
56
|
-
error: { code: 'telegram-operation-failed', message: 'Telegram 操作失败,请稍后重试。' },
|
|
57
|
-
};
|
|
58
|
-
}
|
|
59
|
-
};
|
|
12
|
+
return createTokenBotRpcHandler(controller, { channel: 'Telegram' });
|
|
60
13
|
}
|
|
61
14
|
|
|
62
15
|
export function installTelegramRpc(ctx, controller, authority) {
|
|
@@ -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
|
+
accessPolicyProvider,
|
|
25
|
+
initialAccessPolicyFor,
|
|
26
|
+
} from '../shared/access-policy-production.mjs';
|
|
23
27
|
|
|
24
28
|
function pluginPaths(config) {
|
|
25
29
|
const dshHome = resolve(config.dshHome ?? process.env.DSH_HOME ?? join(homedir(), '.dsh'));
|
|
@@ -54,6 +58,7 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|
|
54
58
|
await workspaces.reconcile(configuredBots.map((bot) => bot.botId));
|
|
55
59
|
await Promise.all(configuredBots.map((bot) => workspaces.ensure(bot.botId, {
|
|
56
60
|
defaultAgentPreset: config.agentPreset,
|
|
61
|
+
initialAccessPolicy: initialAccessPolicyFor('wecom', bot),
|
|
57
62
|
})));
|
|
58
63
|
const observedConfigStore = typeof configStore.remove === 'function'
|
|
59
64
|
? observeBotWorkspaceRemovals(configStore, { workspaces })
|
|
@@ -95,7 +100,10 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|
|
95
100
|
logger,
|
|
96
101
|
createRuntime: async ({ botId, config: botConfig, secret }) => {
|
|
97
102
|
const state = await stateFor(botId);
|
|
98
|
-
await workspaces.ensure(botId, {
|
|
103
|
+
await workspaces.ensure(botId, {
|
|
104
|
+
defaultAgentPreset: config.agentPreset,
|
|
105
|
+
initialAccessPolicy: initialAccessPolicyFor('wecom', botConfig),
|
|
106
|
+
});
|
|
99
107
|
const workspaceScope = createBotWorkspaceScope(harness, {
|
|
100
108
|
botId, workspaces, state, agentPresetCatalog,
|
|
101
109
|
});
|
|
@@ -105,6 +113,9 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|
|
105
113
|
harness: workspaceScope.harness,
|
|
106
114
|
state: workspaceScope.state,
|
|
107
115
|
contextEnhancement: { botId, getSettings: () => workspaces.contextEnhancementFor(botId) },
|
|
116
|
+
accessPolicy: accessPolicyProvider(workspaces, botId, {
|
|
117
|
+
channel: 'wecom', config: botConfig,
|
|
118
|
+
}),
|
|
108
119
|
replyTimeoutMs: config.replyTimeoutMs ?? 600_000,
|
|
109
120
|
connectTimeoutMs: config.connectTimeoutMs ?? 20_000,
|
|
110
121
|
maxReconnectAttempts: config.maxReconnectAttempts ?? 10,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import QRCode from 'qrcode';
|
|
2
2
|
import { SET_CONTEXT_ENHANCEMENT_ENDPOINT, validContextEnhancementPayload } from '../shared/context-enhancement-rpc.mjs';
|
|
3
|
+
import { SET_ACCESS_POLICY_ENDPOINT, validAccessPolicyPayload } from '../shared/access-policy-rpc.mjs';
|
|
3
4
|
import { resolveRpcAuthority } from '../../rpc-authority.mjs';
|
|
4
5
|
import { publicWorkspaceError, SET_WORKSPACE_ENDPOINT, validWorkspacePayload } from '../shared/workspace-rpc.mjs';
|
|
5
6
|
import { SET_AGENT_PRESET_ENDPOINT, validAgentPresetPayload } from '../shared/agent-preset-rpc.mjs';
|
|
@@ -20,6 +21,7 @@ export const WECOM_ENDPOINTS = Object.freeze({
|
|
|
20
21
|
setWorkspace: SET_WORKSPACE_ENDPOINT,
|
|
21
22
|
setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
|
|
22
23
|
setContextEnhancement: SET_CONTEXT_ENHANCEMENT_ENDPOINT,
|
|
24
|
+
setAccessPolicy: SET_ACCESS_POLICY_ENDPOINT,
|
|
23
25
|
});
|
|
24
26
|
export const WECOM_RPC_ENDPOINTS = Object.freeze(Object.values(WECOM_ENDPOINTS));
|
|
25
27
|
|
|
@@ -82,6 +84,10 @@ function payloadFailure(endpoint, payload) {
|
|
|
82
84
|
return validContextEnhancementPayload(payload)
|
|
83
85
|
? null : '请提交有效的上下文增强设置。';
|
|
84
86
|
}
|
|
87
|
+
if (endpoint === WECOM_ENDPOINTS.setAccessPolicy) {
|
|
88
|
+
return validAccessPolicyPayload(payload)
|
|
89
|
+
? null : '请提交有效的访问设置。';
|
|
90
|
+
}
|
|
85
91
|
return 'Unknown Enterprise WeChat endpoint.';
|
|
86
92
|
}
|
|
87
93
|
|
|
@@ -183,6 +189,11 @@ export function createWecomRpcHandler(controller, { encodeQr = qrDataUrl } = {})
|
|
|
183
189
|
value = await controller.updateContextEnhancement(
|
|
184
190
|
payload.botId, payload.config, (status) => publicStatus(status, cachedEncode),
|
|
185
191
|
);
|
|
192
|
+
} else if (endpoint === WECOM_ENDPOINTS.setAccessPolicy) {
|
|
193
|
+
if (typeof controller.updateAccessPolicy !== 'function') throw new Error('Access policy update is unavailable');
|
|
194
|
+
value = await controller.updateAccessPolicy(
|
|
195
|
+
payload.botId, payload.policy, (status) => publicStatus(status, cachedEncode),
|
|
196
|
+
);
|
|
186
197
|
} else if (endpoint === WECOM_ENDPOINTS.setAgentPreset) {
|
|
187
198
|
if (typeof controller.updateAgentPreset !== 'function') throw new Error('Agent preset update is unavailable');
|
|
188
199
|
value = await publicStatus(
|
|
@@ -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
|
+
accessPolicyProvider,
|
|
28
|
+
initialAccessPolicyFor,
|
|
29
|
+
} from '../shared/access-policy-production.mjs';
|
|
26
30
|
|
|
27
31
|
function pluginPaths(config) {
|
|
28
32
|
const dshHome = resolve(config.dshHome ?? process.env.DSH_HOME ?? join(homedir(), '.dsh'));
|
|
@@ -60,6 +64,7 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|
|
60
64
|
await workspaces.reconcile(configuredBots.map((bot) => bot.botId));
|
|
61
65
|
await Promise.all(configuredBots.map((bot) => workspaces.ensure(bot.botId, {
|
|
62
66
|
defaultAgentPreset: config.agentPreset,
|
|
67
|
+
initialAccessPolicy: initialAccessPolicyFor('weixin', bot),
|
|
63
68
|
})));
|
|
64
69
|
const observedConfigStore = typeof configStore.remove === 'function'
|
|
65
70
|
? observeBotWorkspaceRemovals(configStore, { workspaces })
|
|
@@ -98,7 +103,10 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|
|
98
103
|
logger,
|
|
99
104
|
createRuntime: async ({ botId, config: accountConfig, token }) => {
|
|
100
105
|
const state = await stateFor(botId);
|
|
101
|
-
await workspaces.ensure(botId, {
|
|
106
|
+
await workspaces.ensure(botId, {
|
|
107
|
+
defaultAgentPreset: config.agentPreset,
|
|
108
|
+
initialAccessPolicy: initialAccessPolicyFor('weixin', accountConfig),
|
|
109
|
+
});
|
|
102
110
|
const workspaceScope = createBotWorkspaceScope(harness, {
|
|
103
111
|
botId, workspaces, state, agentPresetCatalog,
|
|
104
112
|
});
|
|
@@ -109,6 +117,9 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|
|
109
117
|
harness: workspaceScope.harness,
|
|
110
118
|
state: workspaceScope.state,
|
|
111
119
|
contextEnhancement: { botId, getSettings: () => workspaces.contextEnhancementFor(botId) },
|
|
120
|
+
accessPolicy: accessPolicyProvider(workspaces, botId, {
|
|
121
|
+
channel: 'weixin', config: accountConfig,
|
|
122
|
+
}),
|
|
112
123
|
replyTimeoutMs: config.replyTimeoutMs ?? 600_000,
|
|
113
124
|
maxMessageChars: config.maxMessageChars ?? DEFAULT_WEIXIN_MAX_MESSAGE_CHARS,
|
|
114
125
|
logger: {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import QRCode from 'qrcode';
|
|
2
2
|
import { SET_CONTEXT_ENHANCEMENT_ENDPOINT, validContextEnhancementPayload } from '../shared/context-enhancement-rpc.mjs';
|
|
3
|
+
import { SET_ACCESS_POLICY_ENDPOINT, validAccessPolicyPayload } from '../shared/access-policy-rpc.mjs';
|
|
3
4
|
import { resolveRpcAuthority } from '../../rpc-authority.mjs';
|
|
4
5
|
import {
|
|
5
6
|
publicWorkspaceError,
|
|
@@ -27,6 +28,7 @@ export const WEIXIN_ENDPOINTS = Object.freeze({
|
|
|
27
28
|
setWorkspace: SET_WORKSPACE_ENDPOINT,
|
|
28
29
|
setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
|
|
29
30
|
setContextEnhancement: SET_CONTEXT_ENHANCEMENT_ENDPOINT,
|
|
31
|
+
setAccessPolicy: SET_ACCESS_POLICY_ENDPOINT,
|
|
30
32
|
});
|
|
31
33
|
export const WEIXIN_RPC_ENDPOINTS = Object.freeze(Object.values(WEIXIN_ENDPOINTS));
|
|
32
34
|
|
|
@@ -89,6 +91,10 @@ function payloadFailure(endpoint, payload) {
|
|
|
89
91
|
return validContextEnhancementPayload(payload)
|
|
90
92
|
? null : '请提交有效的上下文增强设置。';
|
|
91
93
|
}
|
|
94
|
+
if (endpoint === WEIXIN_ENDPOINTS.setAccessPolicy) {
|
|
95
|
+
return validAccessPolicyPayload(payload)
|
|
96
|
+
? null : '请提交有效的访问设置。';
|
|
97
|
+
}
|
|
92
98
|
return 'Unknown Weixin endpoint.';
|
|
93
99
|
}
|
|
94
100
|
|
|
@@ -218,6 +224,11 @@ export function createWeixinRpcHandler(controller, { encodeQr = qrDataUrl } = {}
|
|
|
218
224
|
value = await controller.updateContextEnhancement(
|
|
219
225
|
payload.botId, payload.config, (status) => publicStatus(status, cachedEncode),
|
|
220
226
|
);
|
|
227
|
+
} else if (endpoint === WEIXIN_ENDPOINTS.setAccessPolicy) {
|
|
228
|
+
if (typeof controller.updateAccessPolicy !== 'function') throw new Error('Access policy update is unavailable');
|
|
229
|
+
value = await controller.updateAccessPolicy(
|
|
230
|
+
payload.botId, payload.policy, (status) => publicStatus(status, cachedEncode),
|
|
231
|
+
);
|
|
221
232
|
} else if (endpoint === WEIXIN_ENDPOINTS.setAgentPreset) {
|
|
222
233
|
if (typeof controller.updateAgentPreset !== 'function') throw new Error('Agent preset update is unavailable');
|
|
223
234
|
value = await publicStatus(
|
|
@@ -6,7 +6,10 @@ import { WhatsappConfigStore } from '../../../../src/channels/whatsapp/config-st
|
|
|
6
6
|
import { WhatsappHarnessClient } from '../../../../src/channels/whatsapp/harness-client.mjs';
|
|
7
7
|
import { WhatsappStateStore } from '../../../../src/channels/whatsapp/state-store.mjs';
|
|
8
8
|
import { WhatsappController } from '../../../../src/channels/whatsapp/whatsapp-controller.mjs';
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
WhatsappRuntime,
|
|
11
|
+
whatsappAccessPolicyIdsEqual,
|
|
12
|
+
} from '../../../../src/channels/whatsapp/whatsapp-runtime.mjs';
|
|
10
13
|
import { createWhatsappWebSession } from '../../../../src/channels/whatsapp/whatsapp-web-session.mjs';
|
|
11
14
|
import {
|
|
12
15
|
BotWorkspaceStore,
|
|
@@ -20,6 +23,10 @@ import { createTokenConnectionSupervisor } from '../shared/connection-supervisor
|
|
|
20
23
|
import { createHarnessCommandExecutor } from '../../harness-command-executor.mjs';
|
|
21
24
|
import { harnessConnection } from '../../harness-connection.mjs';
|
|
22
25
|
import { createHarnessSessionExecutors } from '../../harness-session-coordinator.mjs';
|
|
26
|
+
import {
|
|
27
|
+
accessPolicyProvider,
|
|
28
|
+
initialAccessPolicyFor,
|
|
29
|
+
} from '../shared/access-policy-production.mjs';
|
|
23
30
|
|
|
24
31
|
const AUTH_DIRECTORY_PATTERN = /^[a-f0-9-]{36}$/;
|
|
25
32
|
|
|
@@ -61,6 +68,7 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|
|
61
68
|
await workspaces.reconcile(configuredBots.map((bot) => bot.botId));
|
|
62
69
|
await Promise.all(configuredBots.map((bot) => workspaces.ensure(bot.botId, {
|
|
63
70
|
defaultAgentPreset: config.agentPreset,
|
|
71
|
+
initialAccessPolicy: initialAccessPolicyFor('whatsapp', bot),
|
|
64
72
|
})));
|
|
65
73
|
const observedConfigStore = typeof configStore.remove === 'function'
|
|
66
74
|
? observeBotWorkspaceRemovals(configStore, { workspaces })
|
|
@@ -98,7 +106,10 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|
|
98
106
|
logger,
|
|
99
107
|
createRuntime: async ({ botId, config: botConfig, authDir }) => {
|
|
100
108
|
const state = await stateFor(botId);
|
|
101
|
-
await workspaces.ensure(botId, {
|
|
109
|
+
await workspaces.ensure(botId, {
|
|
110
|
+
defaultAgentPreset: config.agentPreset,
|
|
111
|
+
initialAccessPolicy: initialAccessPolicyFor('whatsapp', botConfig),
|
|
112
|
+
});
|
|
102
113
|
const workspaceScope = createBotWorkspaceScope(harness, {
|
|
103
114
|
botId, workspaces, state, agentPresetCatalog,
|
|
104
115
|
});
|
|
@@ -108,6 +119,9 @@ export async function createProductionController(ctx, config = {}, internals = {
|
|
|
108
119
|
harness: workspaceScope.harness,
|
|
109
120
|
state: workspaceScope.state,
|
|
110
121
|
contextEnhancement: { botId, getSettings: () => workspaces.contextEnhancementFor(botId) },
|
|
122
|
+
accessPolicy: accessPolicyProvider(workspaces, botId, {
|
|
123
|
+
channel: 'whatsapp', config: botConfig, equals: whatsappAccessPolicyIdsEqual,
|
|
124
|
+
}),
|
|
111
125
|
replyTimeoutMs: config.replyTimeoutMs ?? 600_000,
|
|
112
126
|
connectTimeoutMs: config.connectTimeoutMs ?? 30_000,
|
|
113
127
|
createSession,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import QRCode from 'qrcode';
|
|
2
2
|
|
|
3
3
|
import { publicConnectionTestResult } from '../../../../src/channels/shared/connection-test.mjs';
|
|
4
|
-
import {
|
|
4
|
+
import { SET_ACCESS_POLICY_ENDPOINT, validAccessPolicyPayload } from '../shared/access-policy-rpc.mjs';
|
|
5
5
|
import { SET_CONTEXT_ENHANCEMENT_ENDPOINT, validContextEnhancementPayload } from '../shared/context-enhancement-rpc.mjs';
|
|
6
6
|
import { resolveRpcAuthority } from '../../rpc-authority.mjs';
|
|
7
7
|
import { publicWorkspaceError, SET_WORKSPACE_ENDPOINT, validWorkspacePayload } from '../shared/workspace-rpc.mjs';
|
|
@@ -15,7 +15,7 @@ export const WHATSAPP_ENDPOINTS = Object.freeze({
|
|
|
15
15
|
cancelProvisioning: 'provision.cancel',
|
|
16
16
|
reconnectBot: 'bot.reconnect',
|
|
17
17
|
deleteBot: 'bot.delete',
|
|
18
|
-
setAccessPolicy:
|
|
18
|
+
setAccessPolicy: SET_ACCESS_POLICY_ENDPOINT,
|
|
19
19
|
setWorkspace: SET_WORKSPACE_ENDPOINT,
|
|
20
20
|
setAgentPreset: SET_AGENT_PRESET_ENDPOINT,
|
|
21
21
|
setContextEnhancement: SET_CONTEXT_ENHANCEMENT_ENDPOINT,
|
|
@@ -55,15 +55,8 @@ function payloadFailure(endpoint, payload) {
|
|
|
55
55
|
&& payload.confirm === true ? null : 'bot.delete requires a botId and confirm=true.';
|
|
56
56
|
}
|
|
57
57
|
if (endpoint === WHATSAPP_ENDPOINTS.setAccessPolicy) {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|| !validId(payload.botId)) return '请输入有效的 WhatsApp 访问模式和电话号码。';
|
|
61
|
-
try {
|
|
62
|
-
normalizeWhatsappAccessPolicy(payload);
|
|
63
|
-
return null;
|
|
64
|
-
} catch {
|
|
65
|
-
return '请输入有效的 WhatsApp 访问模式和电话号码。';
|
|
66
|
-
}
|
|
58
|
+
return validAccessPolicyPayload(payload)
|
|
59
|
+
? null : '请提交有效的访问设置。';
|
|
67
60
|
}
|
|
68
61
|
if (endpoint === WHATSAPP_ENDPOINTS.setWorkspace) {
|
|
69
62
|
return validWorkspacePayload(payload)
|
|
@@ -111,7 +104,7 @@ async function publicStatus(value, encodeQr) {
|
|
|
111
104
|
}
|
|
112
105
|
|
|
113
106
|
export function createWhatsappRpcHandler(controller, { encodeQr = qrDataUrl } = {}) {
|
|
114
|
-
for (const method of ['status', 'startProvisioning', 'registrationStatus', 'cancelProvisioning', 'reconnectBot', 'deleteBot'
|
|
107
|
+
for (const method of ['status', 'startProvisioning', 'registrationStatus', 'cancelProvisioning', 'reconnectBot', 'deleteBot']) {
|
|
115
108
|
if (typeof controller?.[method] !== 'function') {
|
|
116
109
|
throw new TypeError(`A complete WhatsApp controller is required (${method})`);
|
|
117
110
|
}
|
|
@@ -191,9 +184,11 @@ export function createWhatsappRpcHandler(controller, { encodeQr = qrDataUrl } =
|
|
|
191
184
|
cachedEncode,
|
|
192
185
|
);
|
|
193
186
|
} else if (endpoint === WHATSAPP_ENDPOINTS.setAccessPolicy) {
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
187
|
+
if (typeof controller.updateAccessPolicy !== 'function') throw new Error('Access policy update is unavailable');
|
|
188
|
+
value = await controller.updateAccessPolicy(
|
|
189
|
+
payload.botId,
|
|
190
|
+
payload.policy,
|
|
191
|
+
(status) => publicStatus(status, cachedEncode),
|
|
197
192
|
);
|
|
198
193
|
} else {
|
|
199
194
|
value = await publicStatus(await controller.deleteBot(payload.botId), cachedEncode);
|
|
@@ -221,6 +221,9 @@ class ModernHarnessApi {
|
|
|
221
221
|
{ request: { requestId: request.rpcId, ...request.payload } },
|
|
222
222
|
signal,
|
|
223
223
|
)),
|
|
224
|
+
rename: (request, signal) => rpcResult(request, () => this.#invoke(
|
|
225
|
+
'session', 'rename', { request: request.payload }, signal,
|
|
226
|
+
)),
|
|
224
227
|
cancel: (request, signal) => rpcResult(request, () => this.#invoke(
|
|
225
228
|
'session', 'cancel', { request: { sessionId: request.payload.sessionId } }, signal,
|
|
226
229
|
)),
|
|
@@ -58,6 +58,10 @@ import {
|
|
|
58
58
|
messageFailureText,
|
|
59
59
|
setLastMessageFailure,
|
|
60
60
|
} from '../shared/message-failure.mjs';
|
|
61
|
+
import {
|
|
62
|
+
COMMAND_PERMISSION_DENIED_MESSAGE,
|
|
63
|
+
evaluateInboundAccess,
|
|
64
|
+
} from '../shared/inbound-access.mjs';
|
|
61
65
|
import { t } from '../shared/i18n.mjs';
|
|
62
66
|
|
|
63
67
|
const CARD_INITIAL_TEXT = '已连接 DeepSeek Harness,正在思考…';
|
|
@@ -70,7 +74,7 @@ const HELP_TEXT_LINES = [
|
|
|
70
74
|
'/new 开启一个全新会话',
|
|
71
75
|
'/compact 压缩当前会话的较早上下文',
|
|
72
76
|
'/history [数量] 查看最近历史消息(默认 3 条,最多 5 条)',
|
|
73
|
-
'/workspace
|
|
77
|
+
'/workspace 工作区序号或绝对路径 切换工作区',
|
|
74
78
|
'/workspacelist 列出工作区绝对路径',
|
|
75
79
|
'/sessionlist 或 /sessions [工作区序号或绝对路径] 列出会话 ID 和标题',
|
|
76
80
|
'/session Session ID 或当前工作区序号 将当前聊天绑定到指定会话',
|
|
@@ -374,6 +378,7 @@ export class DingtalkHarnessBridge {
|
|
|
374
378
|
#harness;
|
|
375
379
|
#state;
|
|
376
380
|
#contextEnhancement;
|
|
381
|
+
#accessPolicy;
|
|
377
382
|
#status;
|
|
378
383
|
#logger;
|
|
379
384
|
#replyTimeoutMs;
|
|
@@ -397,6 +402,7 @@ export class DingtalkHarnessBridge {
|
|
|
397
402
|
harness,
|
|
398
403
|
state,
|
|
399
404
|
contextEnhancement,
|
|
405
|
+
accessPolicy,
|
|
400
406
|
status = createDingtalkBridgeStatus(),
|
|
401
407
|
logger = console,
|
|
402
408
|
replyTimeoutMs = 600_000,
|
|
@@ -415,6 +421,7 @@ export class DingtalkHarnessBridge {
|
|
|
415
421
|
this.#harness = harness;
|
|
416
422
|
this.#state = state;
|
|
417
423
|
this.#contextEnhancement = contextEnhancement;
|
|
424
|
+
this.#accessPolicy = accessPolicy;
|
|
418
425
|
this.#status = status;
|
|
419
426
|
this.#logger = logger;
|
|
420
427
|
this.#approvals = new HarnessApprovalQueue({ label: 'DingTalk', logger });
|
|
@@ -439,17 +446,13 @@ export class DingtalkHarnessBridge {
|
|
|
439
446
|
const sender = senderStaffId(message);
|
|
440
447
|
if (!messageId || !sender || this.#state.hasSeen(messageId)
|
|
441
448
|
|| this.#acceptedMessageIds.has(messageId)) return Promise.resolve();
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
message.conversationType === '1' || message.conversationType === 1 ? 'direct'
|
|
445
|
-
: message.conversationType === '2' || message.conversationType === 2 ? 'group' : null,
|
|
446
|
-
) : contextSnapshot);
|
|
449
|
+
const conversationType = String(message.conversationType) === '2' ? 'group'
|
|
450
|
+
: String(message.conversationType) === '1' ? 'direct' : null;
|
|
447
451
|
|
|
448
452
|
let key;
|
|
449
453
|
try {
|
|
450
454
|
key = conversationKey(message, sender);
|
|
451
455
|
} catch {
|
|
452
|
-
this.#acceptedMessageIds.delete(messageId);
|
|
453
456
|
increment(this.#status, 'messagesRejected');
|
|
454
457
|
this.#status.lastRejectedAt = new Date().toISOString();
|
|
455
458
|
return Promise.resolve();
|
|
@@ -461,9 +464,6 @@ export class DingtalkHarnessBridge {
|
|
|
461
464
|
} catch {
|
|
462
465
|
// An unsafe reply route must never be able to submit an approval.
|
|
463
466
|
}
|
|
464
|
-
if (sessionWebhook && String(message.conversationType) !== '2') {
|
|
465
|
-
rememberConnectionTestTarget(this.#state, { sessionWebhook });
|
|
466
|
-
}
|
|
467
467
|
const pending = this.#pendingInteractions.get(key);
|
|
468
468
|
const promptMessage = dingtalkInboundMessage(message, {
|
|
469
469
|
api: this.#api,
|
|
@@ -473,6 +473,26 @@ export class DingtalkHarnessBridge {
|
|
|
473
473
|
const commandText = nonEmptyString(promptMessage.content) ?? '';
|
|
474
474
|
const addressed = String(message.conversationType) !== '2' || message?.isInAtList === true;
|
|
475
475
|
const direct = String(message.conversationType) !== '2';
|
|
476
|
+
if (addressed) {
|
|
477
|
+
const access = evaluateInboundAccess(this.#accessPolicy, {
|
|
478
|
+
conversationType,
|
|
479
|
+
senderIds: sender,
|
|
480
|
+
text: commandText,
|
|
481
|
+
hasImages: hasInboundImages(promptMessage),
|
|
482
|
+
hasFiles: hasInboundFiles(promptMessage),
|
|
483
|
+
});
|
|
484
|
+
if (!access.allowed) {
|
|
485
|
+
this.#acceptedMessageIds.set(messageId, null);
|
|
486
|
+
return this.#finishAccessDecision(message, messageId, sessionWebhook, access);
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
this.#acceptedMessageIds.set(messageId, contextSnapshot === undefined ? captureContextEnhancement(
|
|
490
|
+
this.#contextEnhancement,
|
|
491
|
+
conversationType,
|
|
492
|
+
) : contextSnapshot);
|
|
493
|
+
if (sessionWebhook && direct) {
|
|
494
|
+
rememberConnectionTestTarget(this.#state, { sessionWebhook });
|
|
495
|
+
}
|
|
476
496
|
const statusReaction = sessionWebhook && addressed ? this.#startStatusReaction(message) : null;
|
|
477
497
|
const finish = (task) => Promise.resolve(task).then(
|
|
478
498
|
(value) => {
|
|
@@ -855,6 +875,38 @@ export class DingtalkHarnessBridge {
|
|
|
855
875
|
return task;
|
|
856
876
|
}
|
|
857
877
|
|
|
878
|
+
#finishAccessDecision(message, messageId, sessionWebhook, access) {
|
|
879
|
+
let task;
|
|
880
|
+
task = Promise.resolve().then(async () => {
|
|
881
|
+
if (this.#state.hasSeen(messageId)) return;
|
|
882
|
+
await this.#state.markSeen(messageId);
|
|
883
|
+
if (access.reason === 'command-not-allowed' && sessionWebhook) {
|
|
884
|
+
increment(this.#status, 'messagesReceived');
|
|
885
|
+
this.#status.lastMessageAt = new Date().toISOString();
|
|
886
|
+
await this.#send(
|
|
887
|
+
sessionWebhook,
|
|
888
|
+
t(COMMAND_PERMISSION_DENIED_MESSAGE),
|
|
889
|
+
this.#atUsersFor(message),
|
|
890
|
+
);
|
|
891
|
+
increment(this.#status, 'messagesReplied');
|
|
892
|
+
this.#status.lastReplyAt = new Date().toISOString();
|
|
893
|
+
} else {
|
|
894
|
+
increment(this.#status, 'messagesRejected');
|
|
895
|
+
this.#status.lastRejectedAt = new Date().toISOString();
|
|
896
|
+
}
|
|
897
|
+
this.#status.lastError = null;
|
|
898
|
+
}).catch((error) => {
|
|
899
|
+
if (this.#signal?.aborted) return;
|
|
900
|
+
this.#status.lastError = error?.message ?? String(error);
|
|
901
|
+
this.#logger.error?.('[dsh-dingtalk] failed to apply inbound access policy', error);
|
|
902
|
+
}).finally(() => {
|
|
903
|
+
this.#acceptedMessageIds.delete(messageId);
|
|
904
|
+
this.#commandTasks.delete(task);
|
|
905
|
+
});
|
|
906
|
+
this.#commandTasks.add(task);
|
|
907
|
+
return task;
|
|
908
|
+
}
|
|
909
|
+
|
|
858
910
|
async #process(message, messageId, sender, key, {
|
|
859
911
|
alreadyRecorded = false,
|
|
860
912
|
preparedMessage,
|
|
@@ -944,12 +996,16 @@ export class DingtalkHarnessBridge {
|
|
|
944
996
|
? await promptContentForMessage(promptMessage, { signal: this.#signal })
|
|
945
997
|
: undefined;
|
|
946
998
|
const snapshot = this.#acceptedMessageIds.get(messageId);
|
|
999
|
+
let contextEnhanced = false;
|
|
947
1000
|
if (snapshot) {
|
|
948
|
-
|
|
1001
|
+
const originalContent = content ?? text;
|
|
1002
|
+
content = enhanceContextContent(originalContent, snapshot, () => ({
|
|
949
1003
|
channel: 'dingtalk',
|
|
950
1004
|
senderId: sender,
|
|
951
1005
|
senderName: message.senderNick,
|
|
1006
|
+
conversationTitle: message.conversationTitle,
|
|
952
1007
|
}));
|
|
1008
|
+
contextEnhanced = content !== originalContent;
|
|
953
1009
|
}
|
|
954
1010
|
if (typeof this.#api.createAiCard === 'function'
|
|
955
1011
|
&& typeof this.#api.updateAiCard === 'function'
|
|
@@ -968,7 +1024,9 @@ export class DingtalkHarnessBridge {
|
|
|
968
1024
|
harness: this.#harness,
|
|
969
1025
|
state: this.#state,
|
|
970
1026
|
key,
|
|
971
|
-
|
|
1027
|
+
text,
|
|
1028
|
+
content,
|
|
1029
|
+
contextEnhanced,
|
|
972
1030
|
createOptions: { signal: this.#signal },
|
|
973
1031
|
existsOptions: { signal: this.#signal },
|
|
974
1032
|
askOptions: {
|
|
@@ -130,6 +130,7 @@ export class DingtalkRuntime {
|
|
|
130
130
|
#harness;
|
|
131
131
|
#state;
|
|
132
132
|
#contextEnhancement;
|
|
133
|
+
#accessPolicy;
|
|
133
134
|
#logger;
|
|
134
135
|
#replyTimeoutMs;
|
|
135
136
|
#maxMessageChars;
|
|
@@ -152,6 +153,7 @@ export class DingtalkRuntime {
|
|
|
152
153
|
harness,
|
|
153
154
|
state,
|
|
154
155
|
contextEnhancement,
|
|
156
|
+
accessPolicy,
|
|
155
157
|
logger = console,
|
|
156
158
|
replyTimeoutMs = 600_000,
|
|
157
159
|
maxMessageChars = 4_000,
|
|
@@ -170,6 +172,7 @@ export class DingtalkRuntime {
|
|
|
170
172
|
this.#harness = harness;
|
|
171
173
|
this.#state = state;
|
|
172
174
|
this.#contextEnhancement = contextEnhancement;
|
|
175
|
+
this.#accessPolicy = accessPolicy;
|
|
173
176
|
this.#logger = logger;
|
|
174
177
|
this.#replyTimeoutMs = replyTimeoutMs;
|
|
175
178
|
this.#maxMessageChars = maxMessageChars;
|
|
@@ -238,6 +241,7 @@ export class DingtalkRuntime {
|
|
|
238
241
|
harness: this.#harness,
|
|
239
242
|
state: this.#state,
|
|
240
243
|
contextEnhancement: this.#contextEnhancement,
|
|
244
|
+
accessPolicy: this.#accessPolicy,
|
|
241
245
|
status: this.#status,
|
|
242
246
|
logger: this.#logger,
|
|
243
247
|
replyTimeoutMs: this.#replyTimeoutMs,
|