@openclaw/whatsapp 2026.9.8 → 2026.10.1-beta.2
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/dist/.setup/{accounts-CtRPAQCL.mjs → accounts-F0vHuze2.mjs} +3 -11
- package/dist/.setup/{action-runtime-DeVk5V7r.mjs → action-runtime-CE3zGstN.mjs} +4 -5
- package/dist/.setup/{agent-tools-login-CL5C1rA2.mjs → agent-tools-login-B7VEvJO7.mjs} +17 -36
- package/dist/.setup/{approval-handler.runtime-CjqMlPOX.mjs → approval-handler.runtime-CKposl6w.mjs} +3 -3
- package/dist/.setup/{approval-reactions-DiOFvb1s.mjs → approval-reactions-CD6CDF9k.mjs} +9 -40
- package/dist/.setup/{auth-store-BBonwKks.mjs → auth-store-D2rN-hP5.mjs} +12 -19
- package/dist/.setup/{channel-CJFE0kD_.mjs → channel-DNgKHMCz.mjs} +15 -24
- package/dist/.setup/{channel-react-action-2r_hjLZV.mjs → channel-react-action-BaBV9X6M.mjs} +3 -3
- package/dist/.setup/{channel.runtime-C94goLw9.mjs → channel.runtime-BniF8Swm.mjs} +4 -4
- package/dist/.setup/{channel.setup-CfN1UfdY.mjs → channel.setup-CsS_NAKQ.mjs} +1 -1
- package/dist/.setup/{connection-controller-CHo1oRJA.mjs → connection-controller-COkcq48K.mjs} +5 -14
- package/dist/.setup/{creds-files-CDATZWWM.mjs → creds-files-B8WcOAmE.mjs} +1 -10
- package/dist/.setup/{directory-config-DUiJ5d8I.mjs → directory-config-RYQomHyB.mjs} +6 -14
- package/dist/.setup/{doctor-iMwg0Vhr.mjs → doctor-DKzeBEmH.mjs} +7 -1
- package/dist/.setup/{group-config-path-DZr3ucUO.mjs → group-config-path-BPrak_bJ.mjs} +3 -9
- package/dist/.setup/{group-session-key-2-mja4OF.mjs → group-session-key-BV8Le9r8.mjs} +5 -14
- package/dist/.setup/{login-BFlvSh9h.mjs → login-CV_4V021.mjs} +4 -4
- package/dist/.setup/{login-qr-Ayb8i25J.mjs → login-qr-DFhGc5yH.mjs} +11 -27
- package/dist/.setup/{monitor-CGpV2VNN.mjs → monitor-B5DS4PDV.mjs} +65 -186
- package/dist/.setup/{question-reactions-CPyyi5AY.mjs → question-reactions-LMeijbaq.mjs} +7 -37
- package/dist/.setup/{resolve-outbound-target-C2eUvsTc.mjs → resolve-outbound-target-CzyNKmrl.mjs} +2 -13
- package/dist/.setup/{send-BGdvf5cC.mjs → send-CGUsUdoX.mjs} +31 -6
- package/dist/.setup/{send-api-H4AkQJjT.mjs → send-api-B03VDwZ1.mjs} +35 -53
- package/dist/.setup/{setup-finalize-DmPX4EaW.mjs → setup-finalize-GtBmrO8p.mjs} +8 -20
- package/dist/.setup/setup-plugin-api-Bsp7jI-r.mjs +3 -0
- package/dist/.setup/{setup-surface-43fj197P.mjs → setup-surface-BuA39k6K.mjs} +3 -3
- package/dist/.setup/{shared-DgOG5Iqr.mjs → shared-CP_zs2U7.mjs} +12 -13
- package/dist/.setup/{socket-close-CT__V2fz.mjs → socket-close-DU7eerAa.mjs} +26 -54
- package/dist/.setup/{targets-runtime-DmiqXIXO.mjs → targets-runtime-Dls-Xh4F.mjs} +10 -34
- package/dist/action-runtime-api.js +1 -1
- package/dist/action-runtime.runtime.js +1 -1
- package/dist/agent-tools-api.js +3 -3
- package/dist/api.js +10 -10
- package/dist/auth-presence.js +3 -4
- package/dist/channel-plugin-api.js +1 -1
- package/dist/directory-contract-api.js +1 -1
- package/dist/doctor-contract-api.js +234 -59
- package/dist/light-runtime-api.js +2 -2
- package/dist/login-qr-runtime.js +1 -1
- package/dist/runtime-api.js +10 -10
- package/dist/setup-entry.js +1 -1
- package/dist/setup-plugin-api.js +1 -1
- package/package.json +5 -5
- package/dist/.setup/setup-plugin-api-vJlxKzca.mjs +0 -3
|
@@ -1,14 +1,16 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { a as readWebCredsJsonRawSync, o as resolveWebCredsBackupPath, s as resolveWebCredsPath, t as assertWebCredsPathRegularFileOrMissing } from "./creds-files-B8WcOAmE.mjs";
|
|
2
|
+
import { M as writeCredsJsonAtomically, N as writeWebCredsRawAtomically, j as waitForCredsSaveQueueWithTimeout, k as enqueueCredsSave, v as resolveDefaultWebAuthDir, y as restoreCredsFromBackupIfNeeded } from "./auth-store-D2rN-hP5.mjs";
|
|
3
3
|
import { n as getStatusCode } from "./session-errors-B8UcAnZN.mjs";
|
|
4
4
|
import { a as makeWASocket, i as makeCacheableSignalKeyStore, n as createBaileysSignalRepository, r as fetchLatestBaileysVersion, s as useMultiFileAuthState } from "./session.runtime-st1konFI.mjs";
|
|
5
5
|
import { VERSION, formatCliCommand } from "openclaw/plugin-sdk/cli-runtime";
|
|
6
6
|
import { toErrorObject } from "openclaw/plugin-sdk/error-runtime";
|
|
7
|
-
import { computeBackoff, danger, getChildLogger, sleepWithAbort, success, toPinoLikeLogger } from "openclaw/plugin-sdk/runtime-env";
|
|
7
|
+
import { computeBackoff, danger, getChildLogger, sleepWithAbort, sleepWithAbort as sleepWithAbort$1, success, toPinoLikeLogger } from "openclaw/plugin-sdk/runtime-env";
|
|
8
8
|
import { renderQrTerminal } from "openclaw/plugin-sdk/media-runtime";
|
|
9
9
|
import { clamp, ensureDir, resolveUserPath } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
10
10
|
import fs from "node:fs/promises";
|
|
11
|
+
import { KeyedAsyncQueue } from "openclaw/plugin-sdk/keyed-async-queue";
|
|
11
12
|
import { parseStrictPositiveInteger, resolveTimerTimeoutMs } from "openclaw/plugin-sdk/number-runtime";
|
|
13
|
+
import { raceWithTimeout, withTimeout } from "openclaw/plugin-sdk/time-runtime";
|
|
12
14
|
import { createDeferred } from "openclaw/plugin-sdk/extension-shared";
|
|
13
15
|
import { FILE_LOCK_STALE_ERROR_CODE, FILE_LOCK_TIMEOUT_ERROR_CODE, acquireFileLock } from "openclaw/plugin-sdk/file-lock";
|
|
14
16
|
import { createNodeProxyAgent } from "openclaw/plugin-sdk/fetch-runtime";
|
|
@@ -30,20 +32,6 @@ function ownershipCancelledError(signal) {
|
|
|
30
32
|
const reason = signal?.reason;
|
|
31
33
|
return reason instanceof Error ? reason : new Error("WhatsApp connection ownership cancelled", reason === void 0 ? {} : { cause: reason });
|
|
32
34
|
}
|
|
33
|
-
async function waitForAbortableDelay(delayMs, signal) {
|
|
34
|
-
if (signal?.aborted) throw ownershipCancelledError(signal);
|
|
35
|
-
await new Promise((resolve, reject) => {
|
|
36
|
-
const onAbort = () => {
|
|
37
|
-
clearTimeout(timer);
|
|
38
|
-
reject(ownershipCancelledError(signal));
|
|
39
|
-
};
|
|
40
|
-
const timer = setTimeout(() => {
|
|
41
|
-
signal?.removeEventListener("abort", onAbort);
|
|
42
|
-
resolve();
|
|
43
|
-
}, delayMs);
|
|
44
|
-
signal?.addEventListener("abort", onAbort, { once: true });
|
|
45
|
-
});
|
|
46
|
-
}
|
|
47
35
|
async function reserveProcessOwner(params) {
|
|
48
36
|
while (true) {
|
|
49
37
|
if (params.signal?.aborted) throw ownershipCancelledError(params.signal);
|
|
@@ -126,9 +114,9 @@ async function acquireOwnerLease(params) {
|
|
|
126
114
|
}
|
|
127
115
|
const delayMs = Math.min(100 * 1.5 ** attempt, 1e3);
|
|
128
116
|
attempt += 1;
|
|
129
|
-
await
|
|
117
|
+
await sleepWithAbort(delayMs, params.signal).catch(() => {
|
|
130
118
|
abandonProcessOwner(ownerPath, processOwner);
|
|
131
|
-
throw
|
|
119
|
+
throw ownershipCancelledError(params.signal);
|
|
132
120
|
});
|
|
133
121
|
}
|
|
134
122
|
}
|
|
@@ -162,7 +150,7 @@ async function acquireWhatsAppStandaloneConnectionOwner(authDir) {
|
|
|
162
150
|
}
|
|
163
151
|
//#endregion
|
|
164
152
|
//#region extensions/whatsapp/src/socket-timing.ts
|
|
165
|
-
const
|
|
153
|
+
const socketSendMessageQueues = /* @__PURE__ */ new WeakMap();
|
|
166
154
|
const DEFAULT_WHATSAPP_SOCKET_TIMING = {
|
|
167
155
|
keepAliveIntervalMs: 25e3,
|
|
168
156
|
connectTimeoutMs: 6e4,
|
|
@@ -190,29 +178,20 @@ function isWhatsAppSocketOperationTimeoutError(error) {
|
|
|
190
178
|
function resolveWhatsAppSocketOperationTimeoutMs(timeoutMs) {
|
|
191
179
|
return resolveTimerTimeoutMs(timeoutMs, DEFAULT_WHATSAPP_SOCKET_TIMING.defaultQueryTimeoutMs);
|
|
192
180
|
}
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
return await result;
|
|
181
|
+
function runSerializedSocketSendMessage(sock, run) {
|
|
182
|
+
let queue = socketSendMessageQueues.get(sock);
|
|
183
|
+
if (!queue) {
|
|
184
|
+
queue = new KeyedAsyncQueue();
|
|
185
|
+
socketSendMessageQueues.set(sock, queue);
|
|
186
|
+
}
|
|
187
|
+
return queue.enqueue("sendMessage", run);
|
|
201
188
|
}
|
|
202
189
|
async function withWhatsAppSocketOperationTimeout(operation, promise, timeoutMs, onTimeout) {
|
|
203
190
|
const resolvedTimeoutMs = resolveWhatsAppSocketOperationTimeoutMs(timeoutMs);
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
onTimeout?.();
|
|
209
|
-
reject(new WhatsAppSocketOperationTimeoutError(operation, resolvedTimeoutMs));
|
|
210
|
-
}, resolvedTimeoutMs);
|
|
211
|
-
timeout.unref?.();
|
|
212
|
-
})]);
|
|
213
|
-
} finally {
|
|
214
|
-
if (timeout) clearTimeout(timeout);
|
|
215
|
-
}
|
|
191
|
+
return await raceWithTimeout(promise, resolvedTimeoutMs, () => {
|
|
192
|
+
onTimeout?.();
|
|
193
|
+
throw new WhatsAppSocketOperationTimeoutError(operation, resolvedTimeoutMs);
|
|
194
|
+
}, { ref: false });
|
|
216
195
|
}
|
|
217
196
|
function createWhatsAppSocketOperationTimeoutAdapter(sock, timeoutMs, hooks) {
|
|
218
197
|
const operationTimeoutMs = resolveWhatsAppSocketOperationTimeoutMs(timeoutMs);
|
|
@@ -241,16 +220,14 @@ const DEFAULT_RECONNECT_POLICY = {
|
|
|
241
220
|
jitter: .25,
|
|
242
221
|
maxAttempts: 12
|
|
243
222
|
};
|
|
244
|
-
function resolveHeartbeatSeconds(
|
|
245
|
-
|
|
246
|
-
if (typeof candidate === "number" && candidate > 0) return candidate;
|
|
223
|
+
function resolveHeartbeatSeconds(overrideSeconds) {
|
|
224
|
+
if (typeof overrideSeconds === "number" && overrideSeconds > 0) return overrideSeconds;
|
|
247
225
|
return DEFAULT_HEARTBEAT_SECONDS;
|
|
248
226
|
}
|
|
249
|
-
function resolveReconnectPolicy(
|
|
250
|
-
const overrideConfig = overrides ?? {};
|
|
227
|
+
function resolveReconnectPolicy(overrides) {
|
|
251
228
|
const merged = {
|
|
252
229
|
...DEFAULT_RECONNECT_POLICY,
|
|
253
|
-
...
|
|
230
|
+
...overrides
|
|
254
231
|
};
|
|
255
232
|
merged.initialMs = Math.max(250, merged.initialMs);
|
|
256
233
|
merged.maxMs = Math.max(merged.initialMs, merged.maxMs);
|
|
@@ -276,7 +253,7 @@ async function safeSaveCreds(params) {
|
|
|
276
253
|
try {
|
|
277
254
|
const credsPath = resolveWebCredsPath(params.authDir);
|
|
278
255
|
const backupPath = resolveWebCredsBackupPath(params.authDir);
|
|
279
|
-
const raw =
|
|
256
|
+
const raw = readWebCredsJsonRawSync(credsPath);
|
|
280
257
|
if (raw) try {
|
|
281
258
|
JSON.parse(raw);
|
|
282
259
|
backup = {
|
|
@@ -559,12 +536,7 @@ function createConnectionTimeoutError(timeoutMs) {
|
|
|
559
536
|
//#region extensions/whatsapp/src/socket-close.ts
|
|
560
537
|
const SOCKET_CLOSE_TIMEOUT_MS = 15e3;
|
|
561
538
|
async function withCloseTimeout(task, operationName) {
|
|
562
|
-
|
|
563
|
-
await Promise.race([task, new Promise((_resolve, reject) => {
|
|
564
|
-
timer = setTimeout(() => reject(/* @__PURE__ */ new Error(`WhatsApp ${operationName} timed out`)), SOCKET_CLOSE_TIMEOUT_MS);
|
|
565
|
-
})]).finally(() => {
|
|
566
|
-
if (timer) clearTimeout(timer);
|
|
567
|
-
});
|
|
539
|
+
await withTimeout(task, SOCKET_CLOSE_TIMEOUT_MS, { message: `WhatsApp ${operationName} timed out` });
|
|
568
540
|
}
|
|
569
541
|
async function waitForTransportClose(ws, operation, operationName) {
|
|
570
542
|
let onClose;
|
|
@@ -604,4 +576,4 @@ async function closeWhatsAppSocketAndWait(sock, reason) {
|
|
|
604
576
|
throw new AggregateError(errors, "WhatsApp socket close could not be confirmed");
|
|
605
577
|
}
|
|
606
578
|
//#endregion
|
|
607
|
-
export { renderQrTerminal as _, DEFAULT_RECONNECT_POLICY as a, acquireWhatsAppStandaloneConnectionOwner as b, resolveHeartbeatSeconds as c, DEFAULT_WHATSAPP_SOCKET_TIMING as d, createWhatsAppSocketOperationTimeoutAdapter as f, withWhatsAppSocketOperationTimeout as g, resolveWhatsAppSocketTiming as h, waitForWaConnection as i, resolveReconnectPolicy as l, resolveWhatsAppSocketOperationTimeoutMs as m, createWaDirectorySocket as n, computeBackoff as o, isWhatsAppSocketOperationTimeoutError as p, createWaSocket as r, newConnectionId as s, closeWhatsAppSocketAndWait as t, sleepWithAbort as u, WhatsAppConnectionOwnerBusyError as v, acquireWhatsAppGatewayConnectionOwner as y };
|
|
579
|
+
export { renderQrTerminal as _, DEFAULT_RECONNECT_POLICY as a, acquireWhatsAppStandaloneConnectionOwner as b, resolveHeartbeatSeconds as c, DEFAULT_WHATSAPP_SOCKET_TIMING as d, createWhatsAppSocketOperationTimeoutAdapter as f, withWhatsAppSocketOperationTimeout as g, resolveWhatsAppSocketTiming as h, waitForWaConnection as i, resolveReconnectPolicy as l, resolveWhatsAppSocketOperationTimeoutMs as m, createWaDirectorySocket as n, computeBackoff as o, isWhatsAppSocketOperationTimeoutError as p, createWaSocket as r, newConnectionId as s, closeWhatsAppSocketAndWait as t, sleepWithAbort$1 as u, WhatsAppConnectionOwnerBusyError as v, acquireWhatsAppGatewayConnectionOwner as y };
|
|
@@ -56,17 +56,9 @@ function assertWebChannel(input) {
|
|
|
56
56
|
if (input !== "web") throw new Error("Web channel must be 'web'");
|
|
57
57
|
}
|
|
58
58
|
function isSelfChatMode(selfE164, allowFrom) {
|
|
59
|
-
if (!selfE164) return false;
|
|
60
|
-
if (!Array.isArray(allowFrom) || allowFrom.length === 0) return false;
|
|
59
|
+
if (!selfE164 || !Array.isArray(allowFrom) || allowFrom.length === 0) return false;
|
|
61
60
|
const normalizedSelf = normalizeE164(selfE164);
|
|
62
|
-
return allowFrom.some((
|
|
63
|
-
if (n === "*") return false;
|
|
64
|
-
try {
|
|
65
|
-
return normalizeE164(String(n)) === normalizedSelf;
|
|
66
|
-
} catch {
|
|
67
|
-
return false;
|
|
68
|
-
}
|
|
69
|
-
});
|
|
61
|
+
return allowFrom.some((entry) => entry !== "*" && normalizeE164(String(entry)) === normalizedSelf);
|
|
70
62
|
}
|
|
71
63
|
function toWhatsappJid(number) {
|
|
72
64
|
const withoutPrefix = number.replace(/^whatsapp:/i, "").trim();
|
|
@@ -147,34 +139,21 @@ function resolveLidMappingDirs(params) {
|
|
|
147
139
|
addDir(path.join(CONFIG_DIR, "credentials"));
|
|
148
140
|
return [...dirs];
|
|
149
141
|
}
|
|
150
|
-
function
|
|
151
|
-
const
|
|
152
|
-
const mappingDirs = resolveLidMappingDirs({ opts: params.opts });
|
|
153
|
-
for (const dir of mappingDirs) {
|
|
142
|
+
function readLidMapping(mappingFilename, opts, normalize) {
|
|
143
|
+
for (const dir of resolveLidMappingDirs({ opts })) {
|
|
154
144
|
const mappingPath = path.join(dir, mappingFilename);
|
|
155
145
|
try {
|
|
156
146
|
const data = fs.readFileSync(mappingPath, "utf8");
|
|
157
|
-
const
|
|
158
|
-
if (
|
|
159
|
-
|
|
147
|
+
const value = JSON.parse(data);
|
|
148
|
+
if (value === null || value === void 0) continue;
|
|
149
|
+
const normalized = normalize(String(value));
|
|
150
|
+
if (normalized !== null) return normalized;
|
|
160
151
|
} catch {}
|
|
161
152
|
}
|
|
162
153
|
return null;
|
|
163
154
|
}
|
|
164
155
|
function readLidForwardMapping(params) {
|
|
165
|
-
|
|
166
|
-
const mappingDirs = resolveLidMappingDirs({ opts: params.opts });
|
|
167
|
-
for (const dir of mappingDirs) {
|
|
168
|
-
const mappingPath = path.join(dir, mappingFilename);
|
|
169
|
-
try {
|
|
170
|
-
const data = fs.readFileSync(mappingPath, "utf8");
|
|
171
|
-
const lid = JSON.parse(data);
|
|
172
|
-
if (lid === null || lid === void 0) continue;
|
|
173
|
-
const digits = String(lid).replace(/\D/g, "");
|
|
174
|
-
if (digits) return digits;
|
|
175
|
-
} catch {}
|
|
176
|
-
}
|
|
177
|
-
return null;
|
|
156
|
+
return readLidMapping(`lid-mapping-${params.phoneDigits}.json`, params.opts, (value) => value.replace(/\D/g, "") || null);
|
|
178
157
|
}
|
|
179
158
|
function jidToE164(jid, opts) {
|
|
180
159
|
const phoneDigits = jid.match(/^(\d+)(?::\d+)?@(s\.whatsapp\.net|hosted)$/)?.[1];
|
|
@@ -183,10 +162,7 @@ function jidToE164(jid, opts) {
|
|
|
183
162
|
if (!lidMatch) return null;
|
|
184
163
|
const lid = lidMatch[1];
|
|
185
164
|
if (!lid) return null;
|
|
186
|
-
const phone =
|
|
187
|
-
lid,
|
|
188
|
-
opts
|
|
189
|
-
});
|
|
165
|
+
const phone = readLidMapping(`lid-mapping-${lid}_reverse.json`, opts, normalizeE164);
|
|
190
166
|
if (phone) return phone;
|
|
191
167
|
if (opts?.logMissing ?? shouldLogVerbose()) logVerbose(`LID mapping not found for ${lidMatch[1]}; skipping inbound message`);
|
|
192
168
|
return null;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as handleWhatsAppAction } from "./.setup/action-runtime-
|
|
1
|
+
import { t as handleWhatsAppAction } from "./.setup/action-runtime-CE3zGstN.mjs";
|
|
2
2
|
export { handleWhatsAppAction };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as handleWhatsAppAction } from "./.setup/action-runtime-
|
|
1
|
+
import { t as handleWhatsAppAction } from "./.setup/action-runtime-CE3zGstN.mjs";
|
|
2
2
|
export { handleWhatsAppAction };
|
package/dist/agent-tools-api.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { a as resolveWhatsAppAccount } from "./.setup/accounts-
|
|
1
|
+
import { a as resolveWhatsAppAccount } from "./.setup/accounts-F0vHuze2.mjs";
|
|
2
2
|
import { r as getWhatsAppConnectionController } from "./.setup/connection-controller-runtime-context-hgLJoRc8.mjs";
|
|
3
|
-
import { s as resolveJidToE164 } from "./.setup/targets-runtime-
|
|
4
|
-
import { n as registerWhatsAppLoginTool } from "./.setup/agent-tools-login-
|
|
3
|
+
import { s as resolveJidToE164 } from "./.setup/targets-runtime-Dls-Xh4F.mjs";
|
|
4
|
+
import { n as registerWhatsAppLoginTool } from "./.setup/agent-tools-login-B7VEvJO7.mjs";
|
|
5
5
|
import { createActionGate, stringEnum } from "openclaw/plugin-sdk/channel-actions";
|
|
6
6
|
import path from "node:path";
|
|
7
7
|
import { resolveOAuthDir } from "openclaw/plugin-sdk/state-paths";
|
package/dist/api.js
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
import { a as resolveWhatsAppAccount, i as listWhatsAppAuthDirs, n as hasAnyWhatsAppAuth, o as resolveWhatsAppAuthDir, r as listEnabledWhatsAppAccounts, s as resolveWhatsAppMediaMaxBytes, t as DEFAULT_WHATSAPP_MEDIA_MAX_MB } from "./.setup/accounts-
|
|
1
|
+
import { a as resolveWhatsAppAccount, i as listWhatsAppAuthDirs, n as hasAnyWhatsAppAuth, o as resolveWhatsAppAuthDir, r as listEnabledWhatsAppAccounts, s as resolveWhatsAppMediaMaxBytes, t as DEFAULT_WHATSAPP_MEDIA_MAX_MB } from "./.setup/accounts-F0vHuze2.mjs";
|
|
2
2
|
import { r as resolveDefaultWhatsAppAccountId, t as listAccountIds } from "./.setup/account-ids-CB5SOWjc.mjs";
|
|
3
3
|
import { a as normalizeWhatsAppAllowFromEntries, c as normalizeWhatsAppTarget, i as looksLikeWhatsAppTargetId, r as isWhatsAppUserTarget, s as normalizeWhatsAppMessagingTarget, t as isWhatsAppGroupJid } from "./.setup/normalize-target-BGra1ZnM.mjs";
|
|
4
|
-
import { t as resolveWhatsAppOutboundTarget } from "./.setup/resolve-outbound-target-
|
|
5
|
-
import { c as toWhatsappJid, i as markdownToWhatsApp, l as toWhatsappJidWithLid, n as isSelfChatMode, r as jidToE164, s as resolveJidToE164, t as assertWebChannel } from "./.setup/targets-runtime-
|
|
6
|
-
import { n as WHATSAPP_LEGACY_OUTBOUND_SEND_DEP_KEYS, t as whatsappPlugin } from "./.setup/channel-
|
|
7
|
-
import { i as resolveWhatsAppGroupToolPolicy, r as resolveWhatsAppGroupRequireMention } from "./.setup/shared-
|
|
4
|
+
import { t as resolveWhatsAppOutboundTarget } from "./.setup/resolve-outbound-target-CzyNKmrl.mjs";
|
|
5
|
+
import { c as toWhatsappJid, i as markdownToWhatsApp, l as toWhatsappJidWithLid, n as isSelfChatMode, r as jidToE164, s as resolveJidToE164, t as assertWebChannel } from "./.setup/targets-runtime-Dls-Xh4F.mjs";
|
|
6
|
+
import { n as WHATSAPP_LEGACY_OUTBOUND_SEND_DEP_KEYS, t as whatsappPlugin } from "./.setup/channel-DNgKHMCz.mjs";
|
|
7
|
+
import { i as resolveWhatsAppGroupToolPolicy, r as resolveWhatsAppGroupRequireMention } from "./.setup/shared-CP_zs2U7.mjs";
|
|
8
8
|
import { t as whatsappCommandPolicy } from "./.setup/command-policy-BIOSHySD.mjs";
|
|
9
|
-
import { t as whatsappSetupPlugin } from "./.setup/channel.setup-
|
|
9
|
+
import { t as whatsappSetupPlugin } from "./.setup/channel.setup-CsS_NAKQ.mjs";
|
|
10
10
|
import { t as DEFAULT_WEB_MEDIA_BYTES } from "./.setup/constants-C7m81BNe.mjs";
|
|
11
|
-
import { d as DEFAULT_WHATSAPP_SOCKET_TIMING, f as createWhatsAppSocketOperationTimeoutAdapter, i as waitForWaConnection, r as createWaSocket } from "./.setup/socket-close-
|
|
11
|
+
import { d as DEFAULT_WHATSAPP_SOCKET_TIMING, f as createWhatsAppSocketOperationTimeoutAdapter, i as waitForWaConnection, r as createWaSocket } from "./.setup/socket-close-DU7eerAa.mjs";
|
|
12
12
|
import { n as getStatusCode, t as formatError } from "./.setup/session-errors-B8UcAnZN.mjs";
|
|
13
|
-
import { n as listWhatsAppDirectoryGroupsFromConfig, r as listWhatsAppDirectoryPeersFromConfig } from "./.setup/directory-config-
|
|
14
|
-
import { _ as resolveInboundMediaMimetype, c as extractContextInfo, m as findMessageSection, o as describeReplyContext, p as extractText, t as createWebSendApi, u as extractLocationData } from "./.setup/send-api-
|
|
13
|
+
import { n as listWhatsAppDirectoryGroupsFromConfig, r as listWhatsAppDirectoryPeersFromConfig } from "./.setup/directory-config-RYQomHyB.mjs";
|
|
14
|
+
import { _ as resolveInboundMediaMimetype, c as extractContextInfo, m as findMessageSection, o as describeReplyContext, p as extractText, t as createWebSendApi, u as extractLocationData } from "./.setup/send-api-B03VDwZ1.mjs";
|
|
15
15
|
import { asBoolean, isRecord, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
16
16
|
import { formatLocationText } from "openclaw/plugin-sdk/channel-inbound";
|
|
17
17
|
import { normalizeE164, resolveUserPath } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
@@ -155,7 +155,7 @@ async function startWhatsAppQaDriverSession(params) {
|
|
|
155
155
|
settlePendingNotifications(waiterError);
|
|
156
156
|
for (const waiter of waiters) {
|
|
157
157
|
removeWaiter(waiter);
|
|
158
|
-
|
|
158
|
+
waiter.reject(waiterError);
|
|
159
159
|
}
|
|
160
160
|
removeMessageListener();
|
|
161
161
|
sock.end(void 0);
|
package/dist/auth-presence.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { n as hasWebCredsSync } from "./.setup/creds-files-B8WcOAmE.mjs";
|
|
2
2
|
import fs from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { resolveOAuthDir } from "openclaw/plugin-sdk/state-paths";
|
|
5
5
|
import { resolveUserPath } from "openclaw/plugin-sdk/account-resolution";
|
|
6
|
-
import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "openclaw/plugin-sdk/account-id";
|
|
6
|
+
import { DEFAULT_ACCOUNT_ID as DEFAULT_ACCOUNT_ID$1, normalizeAccountId } from "openclaw/plugin-sdk/account-id";
|
|
7
7
|
//#region extensions/whatsapp/auth-presence.ts
|
|
8
8
|
function addAccountAuthDirs(authDirs, accountId, authDir, accountsRoot, env) {
|
|
9
9
|
authDirs.add(path.join(accountsRoot, normalizeAccountId(accountId)));
|
|
@@ -14,8 +14,7 @@ function listWhatsAppAuthDirs(cfg, env = process.env) {
|
|
|
14
14
|
const oauthDir = resolveOAuthDir(env);
|
|
15
15
|
const accountsRoot = path.join(oauthDir, "whatsapp");
|
|
16
16
|
const channel = cfg.channels?.whatsapp;
|
|
17
|
-
const authDirs = /* @__PURE__ */ new Set([
|
|
18
|
-
addAccountAuthDirs(authDirs, DEFAULT_ACCOUNT_ID, void 0, accountsRoot, env);
|
|
17
|
+
const authDirs = /* @__PURE__ */ new Set([path.join(accountsRoot, DEFAULT_ACCOUNT_ID$1)]);
|
|
19
18
|
if (channel?.defaultAccount?.trim()) addAccountAuthDirs(authDirs, channel.defaultAccount, channel.accounts?.[channel.defaultAccount]?.authDir, accountsRoot, env);
|
|
20
19
|
const accounts = channel?.accounts;
|
|
21
20
|
if (accounts) for (const [accountId, account] of Object.entries(accounts)) addAccountAuthDirs(authDirs, accountId, account?.authDir, accountsRoot, env);
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as whatsappPlugin } from "./.setup/channel-
|
|
1
|
+
import { t as whatsappPlugin } from "./.setup/channel-DNgKHMCz.mjs";
|
|
2
2
|
export { whatsappPlugin };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as listWhatsAppDirectoryGroupsFromConfig, r as listWhatsAppDirectoryPeersFromConfig } from "./.setup/directory-config-
|
|
1
|
+
import { n as listWhatsAppDirectoryGroupsFromConfig, r as listWhatsAppDirectoryPeersFromConfig } from "./.setup/directory-config-RYQomHyB.mjs";
|
|
2
2
|
//#region extensions/whatsapp/directory-contract-api.ts
|
|
3
3
|
const whatsappDirectoryContractPlugin = {
|
|
4
4
|
id: "whatsapp",
|
|
@@ -1,31 +1,205 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { r as
|
|
3
|
-
import
|
|
1
|
+
import { r as resolveDefaultWhatsAppAccountId, t as listAccountIds } from "./.setup/account-ids-CB5SOWjc.mjs";
|
|
2
|
+
import { r as isWhatsAppBaileysAuthFileName } from "./.setup/creds-files-B8WcOAmE.mjs";
|
|
3
|
+
import { r as runtime_doctor_migrations_exports, t as normalizeCompatibilityConfig$1 } from "./.setup/doctor-DKzeBEmH.mjs";
|
|
4
|
+
import fs, { realpathSync } from "node:fs";
|
|
4
5
|
import path from "node:path";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
6
|
+
import { normalizeOptionalAccountId } from "openclaw/plugin-sdk/account-core";
|
|
7
|
+
import { resolveOAuthDir } from "openclaw/plugin-sdk/state-paths";
|
|
8
|
+
import { assertNoSymlinkParentsSync, readRegularFileSync, root } from "openclaw/plugin-sdk/file-access-runtime";
|
|
9
|
+
import { resolveUserPath as resolveUserPath$1 } from "openclaw/plugin-sdk/account-resolution";
|
|
10
|
+
import { extractErrorCode } from "openclaw/plugin-sdk/error-runtime";
|
|
11
|
+
import fs$1 from "node:fs/promises";
|
|
12
|
+
import { DEFAULT_ACCOUNT_ID as DEFAULT_ACCOUNT_ID$2 } from "openclaw/plugin-sdk/account-id";
|
|
13
|
+
import { createHash } from "node:crypto";
|
|
7
14
|
//#region extensions/whatsapp/src/state-migrations.ts
|
|
8
|
-
function
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
sourcePath,
|
|
25
|
-
targetPath
|
|
26
|
-
}];
|
|
15
|
+
function credentialSetDigest(files) {
|
|
16
|
+
return createHash("sha256").update(JSON.stringify([...files].toSorted(([left], [right]) => left < right ? -1 : left > right ? 1 : 0))).digest("hex");
|
|
17
|
+
}
|
|
18
|
+
function comparablePath(value) {
|
|
19
|
+
try {
|
|
20
|
+
return realpathSync.native(value);
|
|
21
|
+
} catch {
|
|
22
|
+
return path.resolve(value);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function explicitlyOwnsRoot(params) {
|
|
26
|
+
const channel = params.config.channels?.whatsapp;
|
|
27
|
+
const oauthRoot = comparablePath(params.oauthDir);
|
|
28
|
+
return Object.values(channel?.accounts ?? {}).some((owner) => {
|
|
29
|
+
const configured = owner?.authDir?.trim();
|
|
30
|
+
return configured && comparablePath(resolveUserPath$1(configured, params.env)) === oauthRoot;
|
|
27
31
|
});
|
|
28
32
|
}
|
|
33
|
+
async function listSources(params) {
|
|
34
|
+
try {
|
|
35
|
+
if (!(await fs$1.lstat(params.oauthDir)).isDirectory()) throw new Error("WhatsApp legacy credential root must be a regular directory.");
|
|
36
|
+
} catch (error) {
|
|
37
|
+
if (extractErrorCode(error) === "ENOENT") return [];
|
|
38
|
+
throw error;
|
|
39
|
+
}
|
|
40
|
+
const sources = /* @__PURE__ */ new Map();
|
|
41
|
+
for (const entry of await fs$1.readdir(params.oauthDir, { withFileTypes: true })) {
|
|
42
|
+
const name = runtime_doctor_migrations_exports.resolveLegacyMigrationSourcePath?.(entry.name) ?? entry.name;
|
|
43
|
+
if (!entry.isFile() || !isWhatsAppBaileysAuthFileName(name)) continue;
|
|
44
|
+
const source = sources.get(name) ?? {
|
|
45
|
+
name,
|
|
46
|
+
filePath: path.join(params.oauthDir, name),
|
|
47
|
+
claimPaths: []
|
|
48
|
+
};
|
|
49
|
+
if (name !== entry.name) source.claimPaths.push(path.join(params.oauthDir, entry.name));
|
|
50
|
+
sources.set(name, source);
|
|
51
|
+
}
|
|
52
|
+
return [...sources.values()].filter((source) => !explicitlyOwnsRoot(params) || source.claimPaths.length > 0).toSorted((left, right) => Number(left.name === "creds.json") - Number(right.name === "creds.json") || left.name.localeCompare(right.name));
|
|
53
|
+
}
|
|
54
|
+
const whatsappLegacyStateMigration = {
|
|
55
|
+
id: "whatsapp-legacy-state",
|
|
56
|
+
label: "WhatsApp legacy state",
|
|
57
|
+
collectBackupResources: ({ env, stateDir }) => [{
|
|
58
|
+
path: resolveOAuthDir(env, stateDir),
|
|
59
|
+
kind: "directory"
|
|
60
|
+
}, {
|
|
61
|
+
path: path.join(stateDir, "state", "openclaw.sqlite"),
|
|
62
|
+
kind: "sqlite"
|
|
63
|
+
}],
|
|
64
|
+
async detectLegacyState(params) {
|
|
65
|
+
const sources = await listSources(params);
|
|
66
|
+
return sources.length > 0 ? { preview: [explicitlyOwnsRoot(params) ? `- Restore ${sources.length} interrupted WhatsApp credential claims in the configured root` : `- Move ${sources.length} WhatsApp credential files into the default account`] } : null;
|
|
67
|
+
},
|
|
68
|
+
async migrateLegacyState(params) {
|
|
69
|
+
const sources = await listSources(params);
|
|
70
|
+
const changes = [];
|
|
71
|
+
const warnings = [];
|
|
72
|
+
if (sources.length === 0) return {
|
|
73
|
+
changes,
|
|
74
|
+
warnings
|
|
75
|
+
};
|
|
76
|
+
const assertCurrent = params.context.channelIngressQueues?.find((entry) => entry.channelId === "whatsapp")?.assertCurrent;
|
|
77
|
+
const { backupLegacyStateSource } = runtime_doctor_migrations_exports;
|
|
78
|
+
if (!assertCurrent || !backupLegacyStateSource) return {
|
|
79
|
+
changes,
|
|
80
|
+
warnings: ["WhatsApp credential import requires offline Doctor repair. Update OpenClaw core and run openclaw doctor --fix; original files remain unchanged."]
|
|
81
|
+
};
|
|
82
|
+
const backups = [];
|
|
83
|
+
for (const source of sources) backups.push({
|
|
84
|
+
source,
|
|
85
|
+
backup: await backupLegacyStateSource({
|
|
86
|
+
filePath: source.filePath,
|
|
87
|
+
claimPaths: source.claimPaths,
|
|
88
|
+
assertCurrent
|
|
89
|
+
})
|
|
90
|
+
});
|
|
91
|
+
if (explicitlyOwnsRoot(params)) return {
|
|
92
|
+
changes: backups.map(({ source, backup }) => `Restored WhatsApp auth ${source.name} in the configured root; backup: ${backup.backupPath}`),
|
|
93
|
+
warnings
|
|
94
|
+
};
|
|
95
|
+
const credentials = await root(params.oauthDir, {
|
|
96
|
+
assertBeforeMutation: assertCurrent,
|
|
97
|
+
symlinks: "reject",
|
|
98
|
+
hardlinks: "reject",
|
|
99
|
+
maxBytes: Number.MAX_SAFE_INTEGER
|
|
100
|
+
});
|
|
101
|
+
const targetDir = path.join("whatsapp", DEFAULT_ACCOUNT_ID$2);
|
|
102
|
+
const targetCreds = path.join(targetDir, "creds.json");
|
|
103
|
+
const canonical = await credentials.exists(targetCreds) ? await credentials.read(targetCreds) : void 0;
|
|
104
|
+
const legacy = backups.find(({ source }) => source.name === "creds.json");
|
|
105
|
+
if (!legacy) return {
|
|
106
|
+
changes,
|
|
107
|
+
warnings: ["WhatsApp shared-root key files have no creds.json identity. Kept all original files and private .migrated backups; restore the original creds.json before retrying Doctor."],
|
|
108
|
+
...canonical ? { warningDisposition: "recoverable" } : {}
|
|
109
|
+
};
|
|
110
|
+
if (canonical && !canonical.buffer.equals(legacy.backup.bytes)) return {
|
|
111
|
+
changes,
|
|
112
|
+
warnings: ["WhatsApp canonical credentials differ from the legacy shared root. Kept both complete sets and private .migrated backups; choose the intended authDir before removing either set."],
|
|
113
|
+
warningDisposition: "recoverable"
|
|
114
|
+
};
|
|
115
|
+
const targetNames = await fs$1.readdir(path.join(params.oauthDir, targetDir)).catch((error) => {
|
|
116
|
+
if (extractErrorCode(error) === "ENOENT") return [];
|
|
117
|
+
throw error;
|
|
118
|
+
});
|
|
119
|
+
const targetFiles = /* @__PURE__ */ new Map();
|
|
120
|
+
for (const name of targetNames.filter(isWhatsAppBaileysAuthFileName)) {
|
|
121
|
+
const target = await credentials.read(path.join(targetDir, name));
|
|
122
|
+
targetFiles.set(name, createHash("sha256").update(target.buffer).digest("hex"));
|
|
123
|
+
}
|
|
124
|
+
const receiptStore = params.context.openPluginStateKeyedStore({
|
|
125
|
+
namespace: "auth-directory-migrations",
|
|
126
|
+
maxEntries: 4096,
|
|
127
|
+
overflowPolicy: "reject-new"
|
|
128
|
+
});
|
|
129
|
+
if (!receiptStore.withCurrent) return {
|
|
130
|
+
changes,
|
|
131
|
+
warnings: ["WhatsApp credential import requires current offline storage authority. Update OpenClaw core and rerun Doctor; original credentials remain unchanged."]
|
|
132
|
+
};
|
|
133
|
+
const receipts = receiptStore.withCurrent({ assertCurrent });
|
|
134
|
+
const sourceIdentity = legacy.backup.snapshot.sha256;
|
|
135
|
+
const previous = await receipts.lookup(sourceIdentity);
|
|
136
|
+
if (previous && (!canonical || previous.targetDigest !== credentialSetDigest(targetFiles))) return {
|
|
137
|
+
changes,
|
|
138
|
+
warnings: ["WhatsApp credential migration previously started, but its complete canonical credential set is absent or changed. Kept source files and backups; restore the complete intended account or select its authDir explicitly. Doctor will not recreate credentials after logout."],
|
|
139
|
+
...canonical ? { warningDisposition: "recoverable" } : {}
|
|
140
|
+
};
|
|
141
|
+
if (!canonical) {
|
|
142
|
+
const sourceNames = new Set(sources.map((source) => source.name));
|
|
143
|
+
if (targetNames.some((name) => isWhatsAppBaileysAuthFileName(name) && !sourceNames.has(name))) return {
|
|
144
|
+
changes,
|
|
145
|
+
warnings: ["WhatsApp default account contains unmatched auth files without creds.json. Kept the incomplete account and legacy credentials separate; choose the intended credential set before retrying Doctor."]
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
for (const { source, backup } of backups) {
|
|
149
|
+
const relativePath = path.join(targetDir, source.name);
|
|
150
|
+
if (await credentials.exists(relativePath) && !(await credentials.read(relativePath)).buffer.equals(backup.bytes)) return {
|
|
151
|
+
changes,
|
|
152
|
+
warnings: [`WhatsApp auth ${source.name} differs between the default account and legacy shared root. Kept both copies and a private .migrated backup; choose the intended credential set before retrying Doctor.`],
|
|
153
|
+
...canonical ? { warningDisposition: "recoverable" } : {}
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
if (!previous) {
|
|
157
|
+
const expected = new Map(targetFiles);
|
|
158
|
+
for (const { source, backup } of backups) {
|
|
159
|
+
backup.assertUnchanged();
|
|
160
|
+
expected.set(source.name, backup.snapshot.sha256);
|
|
161
|
+
}
|
|
162
|
+
if (!await receipts.registerIfAbsent(sourceIdentity, { targetDigest: credentialSetDigest(expected) })) throw new Error("WhatsApp credential migration receipt changed; rerun Doctor.");
|
|
163
|
+
}
|
|
164
|
+
await credentials.mkdir(targetDir, { private: true });
|
|
165
|
+
const targets = [];
|
|
166
|
+
for (const { source, backup } of backups) {
|
|
167
|
+
backup.assertUnchanged();
|
|
168
|
+
const relativePath = path.join(targetDir, source.name);
|
|
169
|
+
if (!await credentials.exists(relativePath)) await credentials.create(relativePath, backup.bytes, {
|
|
170
|
+
atomic: true,
|
|
171
|
+
durable: true,
|
|
172
|
+
mode: 384,
|
|
173
|
+
private: true
|
|
174
|
+
});
|
|
175
|
+
const target = await credentials.read(relativePath);
|
|
176
|
+
if (!target.buffer.equals(backup.bytes)) throw new Error("WhatsApp canonical credentials changed during import; original files retained.");
|
|
177
|
+
targets.push({
|
|
178
|
+
source,
|
|
179
|
+
backup,
|
|
180
|
+
target,
|
|
181
|
+
targetPath: path.join(params.oauthDir, relativePath)
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
for (const { source, backup, target, targetPath } of targets) {
|
|
185
|
+
backup.removeSource(() => {
|
|
186
|
+
assertCurrent();
|
|
187
|
+
assertNoSymlinkParentsSync({
|
|
188
|
+
rootDir: params.oauthDir,
|
|
189
|
+
targetPath: path.dirname(targetPath),
|
|
190
|
+
requireDirectories: true
|
|
191
|
+
});
|
|
192
|
+
const current = readRegularFileSync({ filePath: targetPath });
|
|
193
|
+
if (current.stat.dev !== target.stat.dev || current.stat.ino !== target.stat.ino || !current.buffer.equals(target.buffer)) throw new Error("WhatsApp canonical credentials changed during import; original backup retained.");
|
|
194
|
+
});
|
|
195
|
+
changes.push(`Moved WhatsApp auth ${source.name} into the default account; backup: ${backup.backupPath}`);
|
|
196
|
+
}
|
|
197
|
+
return {
|
|
198
|
+
changes,
|
|
199
|
+
warnings
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
};
|
|
29
203
|
//#endregion
|
|
30
204
|
//#region extensions/whatsapp/src/doctor-contract.ts
|
|
31
205
|
const streamingAliasMigration = (0, runtime_doctor_migrations_exports.defineChannelAliasMigration)({
|
|
@@ -36,8 +210,35 @@ const streamingAliasMigration = (0, runtime_doctor_migrations_exports.defineChan
|
|
|
36
210
|
},
|
|
37
211
|
accountStreamingInheritsDefaultAccount: true
|
|
38
212
|
});
|
|
39
|
-
const hasExposeErrorText = (value) => Object.hasOwn((0, runtime_doctor_migrations_exports.asObjectRecord)(value) ?? {}, "exposeErrorText");
|
|
40
213
|
const hasAckReaction = (value) => Boolean((0, runtime_doctor_migrations_exports.asObjectRecord)((0, runtime_doctor_migrations_exports.asObjectRecord)(value)?.ackReaction));
|
|
214
|
+
const legacyDefaultPolicyKeys = /* @__PURE__ */ new Set([
|
|
215
|
+
"dmPolicy",
|
|
216
|
+
"allowFrom",
|
|
217
|
+
"groupPolicy",
|
|
218
|
+
"groupAllowFrom"
|
|
219
|
+
]);
|
|
220
|
+
function hasPossibleLeftoverDefault(cfg) {
|
|
221
|
+
const channel = (0, runtime_doctor_migrations_exports.asObjectRecord)(cfg.channels?.whatsapp);
|
|
222
|
+
const accounts = (0, runtime_doctor_migrations_exports.asObjectRecord)(channel?.accounts);
|
|
223
|
+
const fallback = (0, runtime_doctor_migrations_exports.asObjectRecord)(accounts?.default);
|
|
224
|
+
if (!channel || !accounts || !fallback || channel.authDir !== void 0 || typeof channel.defaultAccount === "string" && channel.defaultAccount.trim().toLowerCase() === "default" || Object.keys(accounts).length < 2) return false;
|
|
225
|
+
const keys = Object.keys(fallback);
|
|
226
|
+
if (keys.length === 0 || keys.some((key) => !legacyDefaultPolicyKeys.has(key)) || Object.values(accounts).some((account) => !(0, runtime_doctor_migrations_exports.asObjectRecord)(account)) || cfg.bindings?.some((binding) => binding.match.channel.trim().toLowerCase() === "whatsapp" && binding.match.accountId?.trim().toLowerCase() === "default")) return false;
|
|
227
|
+
const oauthDir = resolveOAuthDir();
|
|
228
|
+
for (const dir of [oauthDir, path.join(oauthDir, "whatsapp", "default")]) try {
|
|
229
|
+
if (fs.readdirSync(dir).some(isWhatsAppBaileysAuthFileName)) return false;
|
|
230
|
+
} catch (error) {
|
|
231
|
+
if ((0, runtime_doctor_migrations_exports.asObjectRecord)(error)?.code !== "ENOENT") return false;
|
|
232
|
+
}
|
|
233
|
+
return true;
|
|
234
|
+
}
|
|
235
|
+
function collectDefaultAccountWarnings(cfg) {
|
|
236
|
+
if (!hasPossibleLeftoverDefault(cfg)) return [];
|
|
237
|
+
const namedAccountId = listAccountIds(cfg).find((id) => id !== "default" && normalizeOptionalAccountId(id) === id);
|
|
238
|
+
if (!namedAccountId) return [];
|
|
239
|
+
const currentAccountId = resolveDefaultWhatsAppAccountId(cfg);
|
|
240
|
+
return [`channels.whatsapp.accounts.default contains only shared policy and has no detected credentials. It may be a leftover of an earlier Doctor migration or an intentional account awaiting login. Unqualified WhatsApp operations currently select "${currentAccountId}". Doctor left all accounts and routing unchanged. To select a named account while keeping all accounts and shared policy, run \`openclaw config set channels.whatsapp.defaultAccount '${JSON.stringify(currentAccountId === "default" ? namedAccountId : currentAccountId)}' --strict-json\`. Only if "default" is unwanted, preserve any shared policy you still need, then run \`openclaw channels remove --channel whatsapp --account default --delete\`.`];
|
|
241
|
+
}
|
|
41
242
|
const legacyConfigRules = [
|
|
42
243
|
...streamingAliasMigration.legacyConfigRules,
|
|
43
244
|
{
|
|
@@ -56,48 +257,22 @@ const legacyConfigRules = [
|
|
|
56
257
|
],
|
|
57
258
|
message: "channels.whatsapp.accounts.<id>.ackReaction moved to global message acknowledgement settings. Run \"openclaw doctor --fix\".",
|
|
58
259
|
match: (value) => (0, runtime_doctor_migrations_exports.hasLegacyAccountStreamingAliases)(value, hasAckReaction)
|
|
59
|
-
},
|
|
60
|
-
{
|
|
61
|
-
path: [
|
|
62
|
-
"channels",
|
|
63
|
-
"whatsapp",
|
|
64
|
-
"exposeErrorText"
|
|
65
|
-
],
|
|
66
|
-
message: "channels.whatsapp.exposeErrorText is retired and ignored. Run \"openclaw doctor --fix\"."
|
|
67
|
-
},
|
|
68
|
-
{
|
|
69
|
-
path: [
|
|
70
|
-
"channels",
|
|
71
|
-
"whatsapp",
|
|
72
|
-
"accounts"
|
|
73
|
-
],
|
|
74
|
-
message: "channels.whatsapp.accounts.<id>.exposeErrorText is retired and ignored. Run \"openclaw doctor --fix\".",
|
|
75
|
-
match: (value) => (0, runtime_doctor_migrations_exports.hasLegacyAccountStreamingAliases)(value, hasExposeErrorText)
|
|
76
260
|
}
|
|
77
261
|
];
|
|
78
|
-
function removeExposeErrorText(cfg, changes) {
|
|
79
|
-
return (0, runtime_doctor_migrations_exports.stripRetiredChannelKeys)({
|
|
80
|
-
cfg,
|
|
81
|
-
channelId: "whatsapp",
|
|
82
|
-
keys: /* @__PURE__ */ new Set(["exposeErrorText"]),
|
|
83
|
-
scope: "root-and-accounts",
|
|
84
|
-
onRemove: ({ key, pathPrefix }) => changes.push(`Removed retired ${pathPrefix}.${key}.`)
|
|
85
|
-
}).config;
|
|
86
|
-
}
|
|
87
262
|
function normalizeCompatibilityConfig({ cfg }) {
|
|
88
263
|
const ackReaction = normalizeCompatibilityConfig$1({ cfg });
|
|
89
|
-
const
|
|
90
|
-
|
|
91
|
-
cfg: retiredConfig,
|
|
264
|
+
const normalized = streamingAliasMigration.normalizeChannelConfig({
|
|
265
|
+
cfg: ackReaction.config,
|
|
92
266
|
changes: ackReaction.changes
|
|
93
267
|
});
|
|
268
|
+
const warnings = collectDefaultAccountWarnings(normalized.config);
|
|
269
|
+
return {
|
|
270
|
+
...normalized,
|
|
271
|
+
...warnings.length ? { warnings } : {}
|
|
272
|
+
};
|
|
94
273
|
}
|
|
95
274
|
//#endregion
|
|
96
275
|
//#region extensions/whatsapp/doctor-contract-api.ts
|
|
97
|
-
const stateMigrations = [
|
|
98
|
-
id: "whatsapp-legacy-state",
|
|
99
|
-
label: "WhatsApp legacy state",
|
|
100
|
-
resolvePlans: detectWhatsAppLegacyStateMigrations
|
|
101
|
-
})];
|
|
276
|
+
const stateMigrations = [whatsappLegacyStateMigration];
|
|
102
277
|
//#endregion
|
|
103
278
|
export { legacyConfigRules, normalizeCompatibilityConfig, stateMigrations };
|