@xmanrui/dsh-im 4.22.0 → 4.23.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 +3 -3
- package/README.md +3 -3
- package/lib/client.js +2505 -1152
- package/lib/index.js +290 -299
- package/package.json +8 -1
- package/plugin-src/client/channel-card-meta.js +2 -0
- package/plugin-src/client/channel-logos.js +11 -0
- package/plugin-src/client/channels/dingtalk/api.js +5 -1
- package/plugin-src/client/channels/dingtalk/index.js +15 -13
- package/plugin-src/client/channels/email/api.js +52 -0
- package/plugin-src/client/channels/email/index.js +728 -0
- package/plugin-src/client/channels/email/styles.js +51 -0
- package/plugin-src/client/channels/feishu/api.js +4 -2
- package/plugin-src/client/channels/feishu/index.js +16 -3
- package/plugin-src/client/channels/imessage/index.js +2 -1
- package/plugin-src/client/channels/office/api.js +2 -0
- package/plugin-src/client/channels/office/index.js +6 -5
- package/plugin-src/client/channels/qq/api.js +7 -0
- package/plugin-src/client/channels/qq/index.js +16 -3
- package/plugin-src/client/channels/shared/token-api.js +5 -1
- package/plugin-src/client/channels/shared/token-channel.js +28 -3
- package/plugin-src/client/channels/slack/index.js +2 -1
- package/plugin-src/client/channels/wecom/api.js +7 -1
- package/plugin-src/client/channels/wecom/index.js +16 -3
- package/plugin-src/client/channels/wecom-app/api.js +6 -1
- package/plugin-src/client/channels/wecom-app/index.js +16 -3
- package/plugin-src/client/channels/weixin/api.js +2 -1
- package/plugin-src/client/channels/weixin/connection-error.js +1 -71
- package/plugin-src/client/channels/whatsapp/api.js +5 -0
- package/plugin-src/client/channels/whatsapp/index.js +16 -3
- package/plugin-src/client/connection-error.js +87 -0
- package/plugin-src/client/global-settings.js +81 -1
- package/plugin-src/client/i18n.js +61 -0
- package/plugin-src/client/index.js +30 -2
- package/plugin-src/client/last-message-error.js +2 -1
- package/plugin-src/client/styles.js +6 -0
- package/plugin-src/host/build.mjs +4 -0
- package/plugin-src/host/channels/dingtalk/production.mjs +2 -0
- package/plugin-src/host/channels/dingtalk/rpc.mjs +7 -4
- package/plugin-src/host/channels/email/availability.mjs +60 -0
- package/plugin-src/host/channels/email/index.mjs +36 -0
- package/plugin-src/host/channels/email/production.mjs +58 -0
- package/plugin-src/host/channels/email/rpc.mjs +163 -0
- package/plugin-src/host/channels/feishu/production.mjs +3 -0
- package/plugin-src/host/channels/feishu/rpc.mjs +7 -4
- package/plugin-src/host/channels/imessage/rpc.mjs +13 -6
- package/plugin-src/host/channels/office/rpc.mjs +3 -1
- package/plugin-src/host/channels/qq/production.mjs +2 -0
- package/plugin-src/host/channels/qq/rpc.mjs +5 -3
- package/plugin-src/host/channels/shared/production.mjs +52 -5
- package/plugin-src/host/channels/shared/rpc.mjs +5 -3
- package/plugin-src/host/channels/shared/startup-error.mjs +4 -3
- package/plugin-src/host/channels/shared/startup.mjs +11 -6
- package/plugin-src/host/channels/slack/production.mjs +2 -0
- package/plugin-src/host/channels/slack/rpc.mjs +5 -3
- package/plugin-src/host/channels/wecom/production.mjs +2 -0
- package/plugin-src/host/channels/wecom/rpc.mjs +5 -3
- package/plugin-src/host/channels/wecom-app/production.mjs +2 -0
- package/plugin-src/host/channels/wecom-app/rpc.mjs +5 -3
- package/plugin-src/host/channels/weixin/production.mjs +2 -0
- package/plugin-src/host/channels/whatsapp/production.mjs +2 -0
- package/plugin-src/host/channels/whatsapp/rpc.mjs +5 -3
- package/plugin-src/host/delivery-adapter.mjs +11 -0
- package/plugin-src/host/image-input-rpc.mjs +24 -0
- package/plugin-src/host/inbound-ttl-rpc.mjs +6 -1
- package/plugin-src/host/index.mjs +3 -0
- package/plugin-src/management-rpc.mjs +12 -2
- package/scripts/verify-package.mjs +3 -1
- package/src/channels/dingtalk/connection-error.mjs +5 -12
- package/src/channels/dingtalk/device-auth.mjs +4 -1
- package/src/channels/dingtalk/dingtalk-bridge.mjs +6 -4
- package/src/channels/dingtalk/dingtalk-controller.mjs +60 -45
- package/src/channels/dingtalk/dingtalk-runtime.mjs +20 -4
- package/src/channels/discord/discord-api.mjs +3 -3
- package/src/channels/discord/discord-runtime.mjs +18 -12
- package/src/channels/email/config-store.mjs +194 -0
- package/src/channels/email/email-api.mjs +20 -0
- package/src/channels/email/email-bridge.mjs +17 -0
- package/src/channels/email/email-controller.mjs +824 -0
- package/src/channels/email/email-runtime.mjs +692 -0
- package/src/channels/email/harness-client.mjs +7 -0
- package/src/channels/email/mail-format.mjs +91 -0
- package/src/channels/email/state-store.mjs +176 -0
- package/src/channels/email/transport.mjs +64 -0
- package/src/channels/email/transports/agent-mail.mjs +593 -0
- package/src/channels/email/transports/agently-cli.mjs +288 -0
- package/src/channels/email/transports/imap-smtp.mjs +165 -0
- package/src/channels/feishu/bridge.mjs +8 -7
- package/src/channels/feishu/feishu-runtime.mjs +15 -8
- package/src/channels/feishu/group-message-permission-manager.mjs +2 -1
- package/src/channels/feishu/message-utils.mjs +3 -2
- package/src/channels/feishu/multi-bot-controller.mjs +56 -47
- package/src/channels/feishu/registration-manager.mjs +8 -4
- package/src/channels/feishu/repair-manager.mjs +2 -1
- package/src/channels/imessage/runtime.mjs +9 -5
- package/src/channels/office/office-controller.mjs +23 -12
- package/src/channels/office/office-runtime.mjs +6 -3
- package/src/channels/office/office-transport.mjs +1 -0
- package/src/channels/qq/qq-bridge.mjs +7 -5
- package/src/channels/qq/qq-controller.mjs +56 -42
- package/src/channels/qq/qq-runtime.mjs +16 -11
- package/src/channels/shared/bot-workspace-store.mjs +29 -10
- package/src/channels/shared/connection-error.mjs +191 -0
- package/src/channels/shared/connection-test.mjs +3 -1
- package/src/channels/shared/conversation-state-store.mjs +46 -1
- package/src/channels/shared/diagnostic-details.mjs +95 -0
- package/src/channels/shared/harness-client.mjs +63 -18
- package/src/channels/shared/i18n-en/diagnostics.mjs +55 -0
- package/src/channels/shared/i18n-en/email.mjs +25 -0
- package/src/channels/shared/i18n-en/image-input.mjs +17 -0
- package/src/channels/shared/i18n-en/shared-a.mjs +1 -0
- package/src/channels/shared/i18n-en/shared-c.mjs +1 -1
- package/src/channels/shared/i18n-en.mjs +6 -0
- package/src/channels/shared/image-input-policy.mjs +35 -0
- package/src/channels/shared/image-input-settings-store.mjs +60 -0
- package/src/channels/shared/image-input.mjs +124 -0
- package/src/channels/shared/image-prompt.mjs +15 -7
- package/src/channels/shared/message-failure.mjs +12 -0
- package/src/channels/shared/semantic/reply-reference.mjs +3 -3
- package/src/channels/shared/text-harness-bridge.mjs +90 -11
- package/src/channels/shared/token-bot-controller.mjs +52 -47
- package/src/channels/slack/slack-api.mjs +3 -3
- package/src/channels/slack/slack-controller.mjs +59 -51
- package/src/channels/slack/slack-runtime.mjs +18 -12
- package/src/channels/telegram/telegram-api.mjs +2 -2
- package/src/channels/telegram/telegram-runtime.mjs +17 -13
- package/src/channels/wecom/wecom-bridge.mjs +15 -6
- package/src/channels/wecom/wecom-controller.mjs +51 -37
- package/src/channels/wecom/wecom-runtime.mjs +10 -6
- package/src/channels/wecom-app/wecom-app-bridge.mjs +7 -5
- package/src/channels/wecom-app/wecom-app-controller.mjs +50 -37
- package/src/channels/wecom-app/wecom-app-runtime.mjs +2 -1
- package/src/channels/weixin/connection-error.mjs +26 -92
- package/src/channels/weixin/diagnostic-details.mjs +1 -63
- package/src/channels/weixin/weixin-bridge.mjs +7 -5
- package/src/channels/whatsapp/whatsapp-controller.mjs +34 -17
- package/src/channels/whatsapp/whatsapp-runtime.mjs +35 -8
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import packageInfo from '../../../package.json' with { type: 'json' };
|
|
1
|
+
import { createConnectionDiagnostics, connectionHint } from '../shared/connection-error.mjs';
|
|
3
2
|
import { t } from '../shared/i18n.mjs';
|
|
4
3
|
import { CONFIG_ISSUE_LABELS, normalizeWeixinDiagnosticDetails } from './diagnostic-details.mjs';
|
|
5
4
|
import { configReadErrorDetails } from '../shared/config-read-error.mjs';
|
|
@@ -73,7 +72,7 @@ const MESSAGES = Object.freeze({
|
|
|
73
72
|
});
|
|
74
73
|
|
|
75
74
|
const ownedStages = new WeakMap();
|
|
76
|
-
|
|
75
|
+
|
|
77
76
|
const CODE_STAGES = {
|
|
78
77
|
'credential-read-failed': ['credential.read', 'credential-store'],
|
|
79
78
|
'credential-save-failed': ['credential.save', 'credential-store'],
|
|
@@ -97,7 +96,7 @@ export function knownWeixinErrorCode(code) { return Object.hasOwn(MESSAGES, code
|
|
|
97
96
|
|
|
98
97
|
/** Annotate at the operation that knows the failing resource; do not inspect exception prose. */
|
|
99
98
|
export function weixinStageError(code, cause, stage) {
|
|
100
|
-
if (
|
|
99
|
+
if (cause?.publicError) return cause;
|
|
101
100
|
const error = new Error('Weixin operation failed', { cause });
|
|
102
101
|
error.code = knownWeixinErrorCode(code) ? code : 'weixin-operation-failed';
|
|
103
102
|
const defaults = CODE_STAGES[error.code] ?? [];
|
|
@@ -105,17 +104,6 @@ export function weixinStageError(code, cause, stage) {
|
|
|
105
104
|
return error;
|
|
106
105
|
}
|
|
107
106
|
|
|
108
|
-
function chainOf(error) {
|
|
109
|
-
const chain = [];
|
|
110
|
-
const seen = new Set();
|
|
111
|
-
while (error && typeof error === 'object' && !seen.has(error) && chain.length < 4) {
|
|
112
|
-
seen.add(error);
|
|
113
|
-
chain.push(error);
|
|
114
|
-
error = error.cause;
|
|
115
|
-
}
|
|
116
|
-
return chain;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
107
|
function hintFor(code, details) {
|
|
120
108
|
if (details.rollback === 'failed' || details.rollback === 'unknown') return t('原账号状态尚未确认,请重新读取状态,并反馈诊断信息。');
|
|
121
109
|
if (details.rollback === 'succeeded') return t('操作未完成,已恢复之前的本机状态。请处理上述问题后重试。');
|
|
@@ -136,84 +124,30 @@ function hintFor(code, details) {
|
|
|
136
124
|
if (code === 'stale-token' || code === 'missing-token') return t('请移除失效接入并重新扫码绑定。');
|
|
137
125
|
if (details.httpStatus === 429) return t('微信服务限流,请稍后重试。');
|
|
138
126
|
if (details.httpStatus === 401 || details.httpStatus === 403) return t('访问被拒绝,请检查服务访问限制;HTTP 状态本身不能确认登录凭据已失效。');
|
|
139
|
-
if (code === 'network-error' || code === 'timeout'
|
|
127
|
+
if (code === 'network-error' || code === 'timeout' || details.reason === 'multiple-causes') return connectionHint(details);
|
|
140
128
|
return t('请按参考号查看 DSH 日志,并复制诊断信息反馈。');
|
|
141
129
|
}
|
|
142
130
|
|
|
143
|
-
export function createWeixinDiagnostics(
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
});
|
|
166
|
-
const botId = /^wx_[a-f0-9]{24}$/.test(context.botId ?? '') ? context.botId : undefined;
|
|
167
|
-
const key = JSON.stringify([botId, details.operation, details.stage, code, details.reason, details.httpStatus, details.providerCode,
|
|
168
|
-
details.resource, details.file, details.field, details.issue]);
|
|
169
|
-
const previous = recent.get(key);
|
|
170
|
-
const time = now();
|
|
171
|
-
if (context.automatic && previous && time - previous.time < 60_000) {
|
|
172
|
-
previous.retries += 1;
|
|
173
|
-
const wrapped = new Error(previous.publicError.message, { cause });
|
|
174
|
-
wrapped.code = code;
|
|
175
|
-
wrapped.publicError = structuredClone(previous.publicError);
|
|
176
|
-
reportedErrors.set(wrapped, true);
|
|
177
|
-
return wrapped;
|
|
178
|
-
}
|
|
179
|
-
if (previous?.retries) write('warn', { event: 'connection-retries', referenceId: previous.publicError.details.referenceId, retries: previous.retries });
|
|
180
|
-
details.referenceId = `WX-CONN-${randomUUID().replaceAll('-', '').slice(0, 8).toUpperCase()}`;
|
|
181
|
-
details.occurredAt = new Date(time).toISOString();
|
|
182
|
-
details.hint = hintFor(code, details);
|
|
183
|
-
const message = code === 'weixin-startup-config-invalid' && details.file
|
|
184
|
-
? t('微信配置格式错误:{file}。请查看诊断详情,修复后重启 DSH。', { file: details.file })
|
|
185
|
-
: t(MESSAGES[code], { status: details.httpStatus ?? '?' });
|
|
186
|
-
const publicError = { code, message, details };
|
|
187
|
-
const wrapped = new Error(publicError.message, { cause });
|
|
188
|
-
wrapped.code = code;
|
|
189
|
-
wrapped.publicError = publicError;
|
|
190
|
-
reportedErrors.set(wrapped, true);
|
|
191
|
-
if (context.automatic) {
|
|
192
|
-
if (recent.size >= 128) recent.delete(recent.keys().next().value);
|
|
193
|
-
recent.set(key, { time, publicError: structuredClone(publicError), retries: 0, botId });
|
|
194
|
-
}
|
|
195
|
-
write(context.warning ? 'warn' : 'error', {
|
|
196
|
-
event: context.warning ? 'connection-warning' : 'connection-failure', code, ...details,
|
|
197
|
-
...(botId ? { botId } : {}), pluginVersion: packageInfo.version, nodeVersion: process.versions.node, platform: process.platform,
|
|
198
|
-
...(/^WX-CONN-[A-F0-9]{8}$/.test(context.parentReferenceId ?? '') ? { parentReferenceId: context.parentReferenceId } : {}),
|
|
199
|
-
...Object.fromEntries(['durationMs', 'timeoutMs'].filter(field => Number.isFinite(cause?.[field]) && cause[field] >= 0).map(field => [field, cause[field]])),
|
|
200
|
-
});
|
|
201
|
-
return wrapped;
|
|
202
|
-
}
|
|
203
|
-
function clear(botId) {
|
|
204
|
-
for (const [key, entry] of recent) {
|
|
205
|
-
if (botId !== undefined && entry.botId !== botId) continue;
|
|
206
|
-
if (entry.retries) write('info', { event: 'connection-retries', referenceId: entry.publicError.details.referenceId, retries: entry.retries });
|
|
207
|
-
recent.delete(key);
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
function outcome(error, rollback) {
|
|
211
|
-
if (!reportedErrors.has(error)) return;
|
|
212
|
-
const details = normalizeWeixinDiagnosticDetails({ rollback });
|
|
213
|
-
if (!details.rollback) return;
|
|
214
|
-
error.publicError.details.rollback = details.rollback;
|
|
215
|
-
error.publicError.details.hint = hintFor(error.code, error.publicError.details);
|
|
216
|
-
write('info', { event: 'connection-outcome', referenceId: error.publicError.details.referenceId, ...details });
|
|
217
|
-
}
|
|
218
|
-
return { report, clear, outcome };
|
|
131
|
+
export function createWeixinDiagnostics(options = {}) {
|
|
132
|
+
return createConnectionDiagnostics({ ...options, channel: 'weixin', prefix: 'WX-CONN',
|
|
133
|
+
describe(cause, context, evidence) {
|
|
134
|
+
const chain = evidence.chain;
|
|
135
|
+
const selected = chain.find(error => knownWeixinErrorCode(error.code));
|
|
136
|
+
const code = selected?.code ?? (knownWeixinErrorCode(context.code) ? context.code : 'weixin-operation-failed');
|
|
137
|
+
const staged = chain.map(error => ownedStages.get(error)).find(Boolean) ?? {};
|
|
138
|
+
const configDetails = chain.map(configReadErrorDetails).find(Boolean) ?? {};
|
|
139
|
+
const defaults = CODE_STAGES[code] ?? [];
|
|
140
|
+
const details = normalizeWeixinDiagnosticDetails({
|
|
141
|
+
...context, ...evidence.details, ...configDetails,
|
|
142
|
+
file: { 'account-config': 'config.json', 'workspace-config': 'workspaces.json' }[configDetails.resource],
|
|
143
|
+
stage: staged.stage ?? defaults[0] ?? (code.startsWith('harness-') ? 'harness.check' : context.stage),
|
|
144
|
+
resource: staged.resource ?? defaults[1] ?? configDetails.resource ?? context.resource,
|
|
145
|
+
reason: evidence.details.reason === 'unknown' ? configDetails.reason ?? context.reason ?? 'unknown' : evidence.details.reason,
|
|
146
|
+
});
|
|
147
|
+
const message = code === 'weixin-startup-config-invalid' && details.file
|
|
148
|
+
? t('微信配置格式错误:{file}。请查看诊断详情,修复后重启 DSH。', { file: details.file })
|
|
149
|
+
: t(MESSAGES[code], { status: details.httpStatus ?? '?' });
|
|
150
|
+
return { publicError: { code, message }, details, hint: hintFor(code, details) };
|
|
151
|
+
},
|
|
152
|
+
});
|
|
219
153
|
}
|
|
@@ -1,63 +1 @@
|
|
|
1
|
-
|
|
2
|
-
const OPERATIONS = new Set([
|
|
3
|
-
'startup', 'connection.restore', 'connection.monitor', 'connection.close', 'connection.status',
|
|
4
|
-
'provision.begin', 'provision.poll', 'provision.verify', 'provision.cancel', 'bot.reconnect', 'bot.delete',
|
|
5
|
-
'bot.workspace.set', 'bot.model.set', 'bot.agent-preset.set', 'bot.context-enhancement.set', 'bot.access-policy.set', 'bot.alias.set',
|
|
6
|
-
]);
|
|
7
|
-
const STAGES = new Set([
|
|
8
|
-
'startup.load', 'qr.begin', 'qr.encode', 'qr.poll', 'qr.verify', 'qr.cancel', 'credential.read', 'credential.save', 'credential.remove',
|
|
9
|
-
'account.save', 'account.remove', 'state.load', 'state.write', 'state.cleanup', 'workspace.write', 'workspace.cleanup',
|
|
10
|
-
'runtime.prepare', 'activation', 'harness.check', 'connection.start', 'connection.poll', 'connection.stop', 'status.read', 'rollback', 'management.request',
|
|
11
|
-
]);
|
|
12
|
-
const REASONS = new Set([
|
|
13
|
-
'ENOTFOUND', 'EAI_AGAIN', 'ECONNREFUSED', 'ECONNRESET', 'EHOSTUNREACH', 'ENETUNREACH', 'ETIMEDOUT', 'EPIPE',
|
|
14
|
-
'UND_ERR_CONNECT_TIMEOUT', 'UND_ERR_HEADERS_TIMEOUT', 'UND_ERR_BODY_TIMEOUT', 'UND_ERR_SOCKET',
|
|
15
|
-
'CERT_HAS_EXPIRED', 'CERT_NOT_YET_VALID', 'DEPTH_ZERO_SELF_SIGNED_CERT', 'SELF_SIGNED_CERT_IN_CHAIN',
|
|
16
|
-
'UNABLE_TO_VERIFY_LEAF_SIGNATURE', 'UNABLE_TO_GET_ISSUER_CERT_LOCALLY', 'ERR_TLS_CERT_ALTNAME_INVALID', 'ERR_SSL_WRONG_VERSION_NUMBER',
|
|
17
|
-
'ENOENT', 'EACCES', 'EPERM', 'ENOSPC', 'EROFS', 'ENOTDIR', 'EISDIR', 'EBUSY', 'EIO', 'EXDEV', 'EMFILE', 'ENFILE', 'EEXIST',
|
|
18
|
-
'invalid-json', 'invalid-config', 'read-only',
|
|
19
|
-
]);
|
|
20
|
-
const RESOURCES = new Set(['credential-store', 'account-config', 'account-state', 'workspace-config', 'workspace-directory']);
|
|
21
|
-
const CONFIG_FILES = new Set(['config.json', 'workspaces.json']);
|
|
22
|
-
export const CONFIG_ISSUE_LABELS = Object.freeze({
|
|
23
|
-
'expected-object': '应为 JSON 对象。',
|
|
24
|
-
'expected-array': '应为 JSON 数组。',
|
|
25
|
-
'unsupported-version': '配置版本缺失或不受当前插件支持。',
|
|
26
|
-
'invalid-string': '字段缺失或不是非空字符串。',
|
|
27
|
-
'invalid-identifier': '标识符格式不符合要求。',
|
|
28
|
-
'identity-mismatch': '标识符与 accountId 派生结果不一致。',
|
|
29
|
-
'duplicate-identity': '账号标识重复。',
|
|
30
|
-
'invalid-api-url': '微信服务地址不是有效 URL。',
|
|
31
|
-
'untrusted-api-url': '微信服务地址必须使用受信任的 HTTPS 域名和端口。',
|
|
32
|
-
'invalid-workspace-path': '工作区路径必须是当前操作系统的绝对路径。',
|
|
33
|
-
'invalid-agent-preset': 'Agent Preset 标识无效。',
|
|
34
|
-
'invalid-model-selection': '模型设置须包含有效的 provider、model 和可选 reasoningEffort。',
|
|
35
|
-
'invalid-delivery-target': '投递目标的标识、结构或路由不符合当前配置版本要求。',
|
|
36
|
-
'unexpected-field': '当前配置版本不允许此字段。',
|
|
37
|
-
});
|
|
38
|
-
// Only schema-owned field names and numeric entry positions may leave the Host.
|
|
39
|
-
// Map keys are replaced with zero-based positions to avoid exposing identities.
|
|
40
|
-
const CONFIG_FIELD = /^(?:\$|version|accounts(?:\[\d{1,10}\](?:\.(?:accountId|ownerUserId|botId|tokenRef|baseUrl))?)?|(?:workspaces|agentPresets|models)(?:\[\d{1,10}\]\.(?:key|value))?|deliveryTargets(?:\[\d{1,10}\]\.(?:key|targets(?:\[\d{1,10}\])?))?)$/;
|
|
41
|
-
|
|
42
|
-
export function normalizeWeixinDiagnosticDetails(value) {
|
|
43
|
-
if (!value || typeof value !== 'object' || Array.isArray(value)) return {};
|
|
44
|
-
const result = {};
|
|
45
|
-
for (const [field, allowed] of [['operation', OPERATIONS], ['stage', STAGES], ['reason', REASONS], ['resource', RESOURCES]]) {
|
|
46
|
-
if (allowed.has(value[field])) result[field] = value[field];
|
|
47
|
-
}
|
|
48
|
-
if (CONFIG_FILES.has(value.file)) result.file = value.file;
|
|
49
|
-
if (typeof value.field === 'string' && CONFIG_FIELD.test(value.field)) result.field = value.field;
|
|
50
|
-
if (typeof value.issue === 'string' && Object.hasOwn(CONFIG_ISSUE_LABELS, value.issue)) result.issue = value.issue;
|
|
51
|
-
if (/^WX-CONN-[A-F0-9]{8}$/.test(value.referenceId ?? '')) result.referenceId = value.referenceId;
|
|
52
|
-
if (typeof value.occurredAt === 'string' && /^\d{4}-\d\d-\d\dT\d\d:\d\d:\d\d\.\d{3}Z$/.test(value.occurredAt)
|
|
53
|
-
&& Number.isFinite(Date.parse(value.occurredAt))) result.occurredAt = value.occurredAt;
|
|
54
|
-
if (Number.isInteger(value.httpStatus) && value.httpStatus >= 100 && value.httpStatus <= 599) result.httpStatus = value.httpStatus;
|
|
55
|
-
const provider = typeof value.providerCode === 'number' && Number.isSafeInteger(value.providerCode)
|
|
56
|
-
? String(value.providerCode) : value.providerCode;
|
|
57
|
-
if (typeof provider === 'string' && /^-?\d{1,12}$/.test(provider)) result.providerCode = provider;
|
|
58
|
-
if (typeof value.pluginVersion === 'string' && /^\d+\.\d+\.\d+(?:-[A-Za-z0-9.-]+)?$/.test(value.pluginVersion)
|
|
59
|
-
&& value.pluginVersion.length <= 64) result.pluginVersion = value.pluginVersion;
|
|
60
|
-
if (['succeeded', 'failed', 'not-attempted', 'unknown'].includes(value.rollback)) result.rollback = value.rollback;
|
|
61
|
-
if (typeof value.hint === 'string' && value.hint.trim()) result.hint = value.hint.trim().slice(0, 500);
|
|
62
|
-
return result;
|
|
63
|
-
}
|
|
1
|
+
export { CONFIG_ISSUE_LABELS, normalizeDiagnosticDetails as normalizeWeixinDiagnosticDetails } from '../shared/diagnostic-details.mjs';
|
|
@@ -63,6 +63,7 @@ import {
|
|
|
63
63
|
} from '../shared/semantic/delivery.mjs';
|
|
64
64
|
import { recoverAssistantTextByTimestamp } from '../shared/session-reply-recovery.mjs';
|
|
65
65
|
import {
|
|
66
|
+
messageFailureDiagnostic,
|
|
66
67
|
channelDeliveryFailure,
|
|
67
68
|
clearLastMessageFailure,
|
|
68
69
|
messageFailureText,
|
|
@@ -467,7 +468,7 @@ export class WeixinHarnessBridge {
|
|
|
467
468
|
const failure = setLastMessageFailure(this.#status, error);
|
|
468
469
|
this.#logger.error?.(
|
|
469
470
|
`[dsh-weixin] failed to process a command [${failure.referenceId}]:`,
|
|
470
|
-
error,
|
|
471
|
+
messageFailureDiagnostic(error, failure),
|
|
471
472
|
);
|
|
472
473
|
return this.#sendOutOfBand(key, sender, messageFailureText(failure), contextToken, runId)
|
|
473
474
|
.catch(() => undefined);
|
|
@@ -623,7 +624,7 @@ export class WeixinHarnessBridge {
|
|
|
623
624
|
const failure = setLastMessageFailure(this.#status, error);
|
|
624
625
|
this.#logger.error?.(
|
|
625
626
|
`[dsh-weixin] failed to process a batch input message [${failure.referenceId}]:`,
|
|
626
|
-
error,
|
|
627
|
+
messageFailureDiagnostic(error, failure),
|
|
627
628
|
);
|
|
628
629
|
await this.#sendOutOfBand(key, sender, messageFailureText(failure), contextToken, runId)
|
|
629
630
|
.catch(() => undefined);
|
|
@@ -814,7 +815,7 @@ export class WeixinHarnessBridge {
|
|
|
814
815
|
await this.#startTyping(sender, contextToken);
|
|
815
816
|
try {
|
|
816
817
|
let content = hasImages || hasReply
|
|
817
|
-
? await promptContentForInboundMessage(promptMessage, { signal: this.#signal })
|
|
818
|
+
? await promptContentForInboundMessage(promptMessage, { signal: this.#signal, deferImages: true })
|
|
818
819
|
: undefined;
|
|
819
820
|
const snapshot = this.#acceptedMessageIds.get(messageId);
|
|
820
821
|
let contextEnhanced = false;
|
|
@@ -857,6 +858,7 @@ export class WeixinHarnessBridge {
|
|
|
857
858
|
await this.#resumeTyping(key, sender, contextToken);
|
|
858
859
|
},
|
|
859
860
|
files: promptMessage.files,
|
|
861
|
+
images: promptMessage.images,
|
|
860
862
|
},
|
|
861
863
|
}));
|
|
862
864
|
if (batchSubmission) {
|
|
@@ -934,7 +936,7 @@ export class WeixinHarnessBridge {
|
|
|
934
936
|
});
|
|
935
937
|
this.#logger.error?.(
|
|
936
938
|
`[dsh-weixin] failed to process an inbound message [${failure.referenceId}]:`,
|
|
937
|
-
error,
|
|
939
|
+
messageFailureDiagnostic(error, failure),
|
|
938
940
|
);
|
|
939
941
|
try {
|
|
940
942
|
await this.#send(
|
|
@@ -1251,7 +1253,7 @@ export class WeixinHarnessBridge {
|
|
|
1251
1253
|
const failure = setLastMessageFailure(this.#status, error);
|
|
1252
1254
|
this.#logger.error?.(
|
|
1253
1255
|
`[dsh-weixin] failed to process an interaction reply [${failure.referenceId}]:`,
|
|
1254
|
-
error,
|
|
1256
|
+
messageFailureDiagnostic(error, failure),
|
|
1255
1257
|
);
|
|
1256
1258
|
if (!this.#state.hasSeen(messageId)) {
|
|
1257
1259
|
await this.#state.markSeen(messageId).catch(() => undefined);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { extractConnectionEvidence, atConnectionStage, createConnectionDiagnostics } from '../shared/connection-error.mjs';
|
|
1
2
|
import { randomUUID } from 'node:crypto';
|
|
2
3
|
|
|
3
4
|
import { connectionTestMessage } from '../shared/connection-test.mjs';
|
|
@@ -39,6 +40,7 @@ export class WhatsappController {
|
|
|
39
40
|
#deleteAuth;
|
|
40
41
|
#deleteState;
|
|
41
42
|
#logger;
|
|
43
|
+
#diagnostics;
|
|
42
44
|
#runtimes = new Map();
|
|
43
45
|
#errors = new Map();
|
|
44
46
|
#attempts = new Map();
|
|
@@ -71,6 +73,14 @@ export class WhatsappController {
|
|
|
71
73
|
this.#deleteAuth = deleteAuth;
|
|
72
74
|
this.#deleteState = deleteState;
|
|
73
75
|
this.#logger = logger;
|
|
76
|
+
this.#diagnostics = createConnectionDiagnostics({ channel: 'whatsapp', logger });
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
get diagnostics() { return this.#diagnostics; }
|
|
80
|
+
|
|
81
|
+
#failure(error, code, message) {
|
|
82
|
+
const stage = code.startsWith('qr-') ? 'qr.begin' : code === 'activation-failed' ? 'activation' : 'connection.start';
|
|
83
|
+
return this.#diagnostics.report(error, { reuse: true, stage, publicError: { code, message } }).publicError;
|
|
74
84
|
}
|
|
75
85
|
|
|
76
86
|
async initialize() {
|
|
@@ -82,7 +92,7 @@ export class WhatsappController {
|
|
|
82
92
|
await this.#startRuntime(config);
|
|
83
93
|
this.#errors.delete(config.botId);
|
|
84
94
|
} catch (error) {
|
|
85
|
-
this.#errors.set(config.botId,
|
|
95
|
+
this.#errors.set(config.botId, this.#failure(error,
|
|
86
96
|
error?.code === 'relink-required' ? 'relink-required' : 'connection-failed',
|
|
87
97
|
error?.code === 'relink-required'
|
|
88
98
|
? t('WhatsApp 关联设备已失效,请移除后重新扫码。')
|
|
@@ -191,7 +201,7 @@ export class WhatsappController {
|
|
|
191
201
|
await this.#startRuntime(config);
|
|
192
202
|
this.#errors.delete(botId);
|
|
193
203
|
} catch (error) {
|
|
194
|
-
this.#errors.set(botId,
|
|
204
|
+
this.#errors.set(botId, this.#failure(error,
|
|
195
205
|
error?.code === 'relink-required' ? 'relink-required' : 'connection-failed',
|
|
196
206
|
error?.code === 'relink-required'
|
|
197
207
|
? t('WhatsApp 关联设备已失效,请移除后重新扫码。')
|
|
@@ -245,7 +255,7 @@ export class WhatsappController {
|
|
|
245
255
|
if (this.#closed) throw new Error('WhatsApp controller is closed');
|
|
246
256
|
const config = this.#configStore.get(botId);
|
|
247
257
|
if (!config) throw new Error('Unknown WhatsApp bot');
|
|
248
|
-
const saved = await this.#configStore.save({ ...config, ...accessPolicy });
|
|
258
|
+
const saved = await atConnectionStage('account.save', () => this.#configStore.save({ ...config, ...accessPolicy }), 'account-config');
|
|
249
259
|
this.#runtimes.get(botId)?.setAccessPolicy?.(saved);
|
|
250
260
|
this.#touch();
|
|
251
261
|
});
|
|
@@ -253,24 +263,31 @@ export class WhatsappController {
|
|
|
253
263
|
}
|
|
254
264
|
|
|
255
265
|
async deleteBot(botId) {
|
|
266
|
+
const warnings = [];
|
|
256
267
|
const config = this.#configStore.get(botId);
|
|
257
268
|
if (!config) throw new Error('Unknown WhatsApp bot');
|
|
258
269
|
await this.#withBotTransition(botId, async () => {
|
|
259
270
|
await this.#stopRuntime(botId);
|
|
260
271
|
try {
|
|
261
|
-
await this.#configStore.remove(botId);
|
|
272
|
+
await atConnectionStage('account.remove', () => this.#configStore.remove(botId), 'account-config');
|
|
262
273
|
} catch (error) {
|
|
263
|
-
|
|
264
|
-
|
|
274
|
+
if (!this.#configStore.get(botId)) {
|
|
275
|
+
warnings.push(this.#diagnostics.report(error, { operation: 'bot.delete', stage: 'workspace.cleanup', warning: true,
|
|
276
|
+
publicError: { code: 'workspace-cleanup-failed', message: '账号已移除,但本地状态清理失败。' } }).publicError);
|
|
277
|
+
} else {
|
|
278
|
+
await this.#startRuntime(config).catch(() => undefined);
|
|
279
|
+
throw error;
|
|
280
|
+
}
|
|
265
281
|
}
|
|
266
|
-
await Promise.allSettled([
|
|
282
|
+
const cleanup = await Promise.allSettled([
|
|
267
283
|
this.#deleteAuth(config.authDirectory),
|
|
268
284
|
this.#deleteState({ botId, config }),
|
|
269
285
|
]);
|
|
286
|
+
for (const result of cleanup) if (result.status === 'rejected') warnings.push(this.#diagnostics.report(result.reason, { operation: 'bot.delete', stage: 'state.cleanup', warning: true, publicError: { code: 'cleanup-failed', message: '账号已移除,但本地状态清理失败。' } }).publicError);
|
|
270
287
|
this.#errors.delete(botId);
|
|
271
288
|
this.#touch();
|
|
272
289
|
});
|
|
273
|
-
return this.status();
|
|
290
|
+
return { ...this.status(), ...(warnings.length ? { warnings } : {}) };
|
|
274
291
|
}
|
|
275
292
|
|
|
276
293
|
status() {
|
|
@@ -302,7 +319,7 @@ export class WhatsappController {
|
|
|
302
319
|
},
|
|
303
320
|
lastMessageError: publicMessageFailure(runtimeStatus?.lastMessageError),
|
|
304
321
|
accessPolicy: normalizeWhatsappAccessPolicy(config),
|
|
305
|
-
error: structuredClone(this.#errors.get(config.botId) ?? null),
|
|
322
|
+
error: structuredClone(runtimeStatus?.error ?? this.#errors.get(config.botId) ?? null),
|
|
306
323
|
};
|
|
307
324
|
});
|
|
308
325
|
const connectedCount = bots.filter((bot) => bot.connected).length;
|
|
@@ -351,13 +368,13 @@ export class WhatsappController {
|
|
|
351
368
|
};
|
|
352
369
|
try {
|
|
353
370
|
if (record.controller.signal.aborted || this.#closed) throw Object.assign(new Error(), { name: 'AbortError' });
|
|
354
|
-
await this.#configStore.save(config);
|
|
371
|
+
await atConnectionStage('account.save', () => this.#configStore.save(config), 'account-config');
|
|
355
372
|
if (record.controller.signal.aborted || this.#closed) throw Object.assign(new Error(), { name: 'AbortError' });
|
|
356
373
|
try {
|
|
357
374
|
await this.#startRuntime(config);
|
|
358
375
|
this.#errors.delete(botId);
|
|
359
376
|
} catch (error) {
|
|
360
|
-
this.#errors.set(botId,
|
|
377
|
+
this.#errors.set(botId, this.#failure(error, 'connection-failed', t('WhatsApp 已绑定,消息连接暂未就绪。')));
|
|
361
378
|
this.#logger.warn?.(`[dsh-im:whatsapp] bot ${botId} did not reconnect after QR binding`);
|
|
362
379
|
}
|
|
363
380
|
if (previous?.authDirectory && previous.authDirectory !== config.authDirectory) {
|
|
@@ -368,12 +385,12 @@ export class WhatsappController {
|
|
|
368
385
|
} catch (error) {
|
|
369
386
|
if (record.controller.signal.aborted || this.#closed || error?.name === 'AbortError') {
|
|
370
387
|
await this.#stopRuntime(botId);
|
|
371
|
-
if (previous) await this.#configStore.save(previous).catch(() => undefined);
|
|
388
|
+
if (previous) await atConnectionStage('account.save', () => this.#configStore.save(previous), 'account-config').catch(() => undefined);
|
|
372
389
|
else {
|
|
373
|
-
const removed = await this.#configStore.remove(botId).catch(() => null);
|
|
390
|
+
const removed = await atConnectionStage('account.remove', () => this.#configStore.remove(botId), 'account-config').catch(() => null);
|
|
374
391
|
if (removed) {
|
|
375
392
|
await this.#deleteState({ botId, config }).catch((cleanupError) => {
|
|
376
|
-
this.#logger.warn?.('[dsh-im:whatsapp] cancelled bot state cleanup failed:', cleanupError);
|
|
393
|
+
this.#logger.warn?.('[dsh-im:whatsapp] cancelled bot state cleanup failed:', extractConnectionEvidence(cleanupError).details);
|
|
377
394
|
});
|
|
378
395
|
}
|
|
379
396
|
}
|
|
@@ -384,7 +401,7 @@ export class WhatsappController {
|
|
|
384
401
|
} else {
|
|
385
402
|
await this.#deleteAuth(record.authDirectory).catch(() => undefined);
|
|
386
403
|
record.state = 'failed';
|
|
387
|
-
record.error =
|
|
404
|
+
record.error = this.#failure(error, 'activation-failed', t('WhatsApp 已扫码,但无法保存关联设备。'));
|
|
388
405
|
this.#logger.error?.('[dsh-im:whatsapp] unable to persist linked-device session');
|
|
389
406
|
}
|
|
390
407
|
} finally {
|
|
@@ -413,11 +430,11 @@ export class WhatsappController {
|
|
|
413
430
|
if (this.#closed) throw new Error('WhatsApp controller is closed');
|
|
414
431
|
await this.#stopRuntime(config.botId);
|
|
415
432
|
if (this.#closed) throw new Error('WhatsApp controller is closed');
|
|
416
|
-
const runtime = await this.#createRuntime({
|
|
433
|
+
const runtime = await atConnectionStage('runtime.prepare', () => this.#createRuntime({
|
|
417
434
|
botId: config.botId,
|
|
418
435
|
config,
|
|
419
436
|
authDir: this.#authPath(config.authDirectory),
|
|
420
|
-
});
|
|
437
|
+
}));
|
|
421
438
|
if (!runtime || typeof runtime.start !== 'function' || typeof runtime.stop !== 'function') {
|
|
422
439
|
throw new TypeError('createRuntime returned an invalid WhatsApp runtime');
|
|
423
440
|
}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
import { createConnectionDiagnostics, atConnectionStage } from '../shared/connection-error.mjs';
|
|
1
2
|
import { createHash, randomBytes } from 'node:crypto';
|
|
2
3
|
|
|
3
4
|
import {
|
|
4
5
|
areJidsSameUser,
|
|
5
6
|
downloadMediaMessage,
|
|
6
7
|
jidDecode,
|
|
8
|
+
jidNormalizedUser,
|
|
7
9
|
normalizeMessageContent,
|
|
8
10
|
} from '@whiskeysockets/baileys';
|
|
9
11
|
|
|
@@ -14,6 +16,7 @@ import { trackOutboundArtifactProviderPromise } from '../shared/semantic/artifac
|
|
|
14
16
|
import { createWhatsappBridgeStatus, WhatsappHarnessBridge } from './whatsapp-bridge.mjs';
|
|
15
17
|
import {
|
|
16
18
|
WHATSAPP_ACCESS_MODES,
|
|
19
|
+
normalizeWhatsappAccountJid,
|
|
17
20
|
} from './config-store.mjs';
|
|
18
21
|
import { createWhatsappWebSession } from './whatsapp-web-session.mjs';
|
|
19
22
|
|
|
@@ -289,8 +292,22 @@ export function createWhatsappMediaDownloader({
|
|
|
289
292
|
});
|
|
290
293
|
}
|
|
291
294
|
|
|
295
|
+
function whatsappAccountMatcher(accountJid, aliases) {
|
|
296
|
+
// PN and LID are separate identities unless the linked account supplies both.
|
|
297
|
+
const accountJids = new Set(
|
|
298
|
+
[accountJid, ...(Array.isArray(aliases) ? aliases : [])]
|
|
299
|
+
.map((jid) => normalizeWhatsappAccountJid(jidNormalizedUser(jid)))
|
|
300
|
+
.filter(Boolean),
|
|
301
|
+
);
|
|
302
|
+
return (jid) => {
|
|
303
|
+
const normalized = normalizeWhatsappAccountJid(jidNormalizedUser(jid));
|
|
304
|
+
return normalized !== null && accountJids.has(normalized);
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
|
|
292
308
|
export function normalizeWhatsappMessage(message, accountJid, {
|
|
293
309
|
download = downloadMediaMessage,
|
|
310
|
+
accountAliases = [],
|
|
294
311
|
} = {}) {
|
|
295
312
|
const remoteJid = typeof message?.key?.remoteJid === 'string' ? message.key.remoteJid : '';
|
|
296
313
|
const alternateRemoteJid = typeof message?.key?.remoteJidAlt === 'string'
|
|
@@ -300,8 +317,9 @@ export function normalizeWhatsappMessage(message, accountJid, {
|
|
|
300
317
|
|| remoteJid.endsWith('@newsletter')) return null;
|
|
301
318
|
const group = remoteJid.endsWith('@g.us');
|
|
302
319
|
const fromMe = message.key.fromMe === true;
|
|
320
|
+
const matchesAccount = whatsappAccountMatcher(accountJid, accountAliases);
|
|
303
321
|
const selfChat = fromMe && !group
|
|
304
|
-
&& [remoteJid, alternateRemoteJid].some(
|
|
322
|
+
&& [remoteJid, alternateRemoteJid].some(matchesAccount);
|
|
305
323
|
if (fromMe && !selfChat && !group) return null;
|
|
306
324
|
const senderJid = fromMe ? accountJid : group ? message.key.participant : remoteJid;
|
|
307
325
|
const senderAlternateJid = group && !fromMe ? message.key.participantAlt : alternateRemoteJid;
|
|
@@ -310,9 +328,9 @@ export function normalizeWhatsappMessage(message, accountJid, {
|
|
|
310
328
|
const content = normalizeMessageContent(message.message);
|
|
311
329
|
const context = messageContext(content);
|
|
312
330
|
const mentioned = Array.isArray(context?.mentionedJid)
|
|
313
|
-
&& context.mentionedJid.some(
|
|
331
|
+
&& context.mentionedJid.some(matchesAccount);
|
|
314
332
|
const replyToSelf = typeof context?.participant === 'string'
|
|
315
|
-
&&
|
|
333
|
+
&& matchesAccount(context.participant);
|
|
316
334
|
const image = whatsappImageSource(message, content, download, { viewOnce });
|
|
317
335
|
const file = whatsappFileSource(message, content, download);
|
|
318
336
|
const replyTo = whatsappReplyReference(context);
|
|
@@ -677,6 +695,7 @@ export class WhatsappRuntime {
|
|
|
677
695
|
#contextEnhancement;
|
|
678
696
|
#accessPolicy;
|
|
679
697
|
#logger;
|
|
698
|
+
#diagnostics;
|
|
680
699
|
#replyTimeoutMs;
|
|
681
700
|
#connectTimeoutMs;
|
|
682
701
|
#mediaUploadTimeoutMs;
|
|
@@ -710,7 +729,7 @@ export class WhatsappRuntime {
|
|
|
710
729
|
this.#state = state;
|
|
711
730
|
this.#contextEnhancement = contextEnhancement;
|
|
712
731
|
this.#accessPolicy = accessPolicy;
|
|
713
|
-
this.#logger = logger;
|
|
732
|
+
this.#logger = logger; this.#diagnostics = createConnectionDiagnostics({ channel: 'whatsapp', logger });
|
|
714
733
|
this.#replyTimeoutMs = replyTimeoutMs;
|
|
715
734
|
this.#connectTimeoutMs = connectTimeoutMs;
|
|
716
735
|
if (!Number.isSafeInteger(mediaUploadTimeoutMs) || mediaUploadTimeoutMs <= 0) {
|
|
@@ -738,8 +757,8 @@ export class WhatsappRuntime {
|
|
|
738
757
|
await this.stop();
|
|
739
758
|
this.#status.startedAt = new Date().toISOString();
|
|
740
759
|
this.#status.connectionState = 'connecting';
|
|
741
|
-
this.#status.lastError = null;
|
|
742
|
-
await this.#harness.ensureRunning();
|
|
760
|
+
this.#status.lastError = null; this.#status.error = null; this.#diagnostics.clear();
|
|
761
|
+
await atConnectionStage('harness.check', () => this.#harness.ensureRunning());
|
|
743
762
|
this.#status.harnessReachable = true;
|
|
744
763
|
const controller = new AbortController();
|
|
745
764
|
this.#abortController = controller;
|
|
@@ -757,7 +776,13 @@ export class WhatsappRuntime {
|
|
|
757
776
|
{ code: 'relink-required' },
|
|
758
777
|
)),
|
|
759
778
|
onMessage: async (raw, context) => {
|
|
779
|
+
const linkedAccount = context?.socket?.user;
|
|
760
780
|
const message = normalizeWhatsappMessage(raw, this.#config.accountJid, {
|
|
781
|
+
accountAliases: [
|
|
782
|
+
linkedAccount?.id,
|
|
783
|
+
linkedAccount?.lid,
|
|
784
|
+
linkedAccount?.phoneNumber,
|
|
785
|
+
],
|
|
761
786
|
download: createWhatsappMediaDownloader({
|
|
762
787
|
socket: context?.socket,
|
|
763
788
|
logger: this.#logger,
|
|
@@ -771,7 +796,8 @@ export class WhatsappRuntime {
|
|
|
771
796
|
if (controller.signal.aborted) return;
|
|
772
797
|
this.#status.ready = false;
|
|
773
798
|
this.#status.connectionState = 'failed';
|
|
774
|
-
this.#status.
|
|
799
|
+
this.#status.error = this.#diagnostics.report(error, { operation: 'connection.monitor', botId: this.#config?.botId, automatic: true }).publicError;
|
|
800
|
+
this.#status.lastError = this.#status.error.message;
|
|
775
801
|
},
|
|
776
802
|
});
|
|
777
803
|
this.#session = session;
|
|
@@ -822,7 +848,8 @@ export class WhatsappRuntime {
|
|
|
822
848
|
} catch (error) {
|
|
823
849
|
this.#status.ready = false;
|
|
824
850
|
this.#status.connectionState = 'failed';
|
|
825
|
-
this.#status.
|
|
851
|
+
this.#status.error = this.#diagnostics.report(error, { operation: 'connection.monitor', botId: this.#config?.botId, automatic: true }).publicError;
|
|
852
|
+
this.#status.lastError = this.#status.error.message;
|
|
826
853
|
await this.stop();
|
|
827
854
|
throw error;
|
|
828
855
|
}
|