@wednesdayai/whatsapp 0.4.10

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.
@@ -0,0 +1,1064 @@
1
+ import { DEFAULT_ACCOUNT_ID, VERSION, acquireFileLock, createAccountListHelpers, danger, defaultRuntime, encodePngRgba, ensureDir, fillPixel, formatCliCommand, getChildLogger, info, jidToE164, logInfo, normalizeAccountId, resolveAccountEntry, resolveOAuthDir, resolveUserPath, success, toPinoLikeLogger } from "openclaw/plugin-sdk";
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import { randomUUID } from "node:crypto";
5
+ import fs$1 from "node:fs/promises";
6
+ import { resolveProcessScopedMap } from "openclaw/plugin-sdk/process-scoped-map";
7
+ import QRCodeModule from "qrcode-terminal/vendor/QRCode/index.js";
8
+ import QRErrorCorrectLevelModule from "qrcode-terminal/vendor/QRCode/QRErrorCorrectLevel.js";
9
+ import qrcode from "qrcode-terminal";
10
+
11
+ //#region \0rolldown/runtime.js
12
+ var __defProp = Object.defineProperty;
13
+ var __exportAll = (all, no_symbols) => {
14
+ let target = {};
15
+ for (var name in all) {
16
+ __defProp(target, name, {
17
+ get: all[name],
18
+ enumerable: true
19
+ });
20
+ }
21
+ if (!no_symbols) {
22
+ __defProp(target, Symbol.toStringTag, { value: "Module" });
23
+ }
24
+ return target;
25
+ };
26
+
27
+ //#endregion
28
+ //#region src/accounts.ts
29
+ const { listConfiguredAccountIds, listAccountIds, resolveDefaultAccountId } = createAccountListHelpers("whatsapp");
30
+ const listWhatsAppAccountIds = listAccountIds;
31
+ const resolveDefaultWhatsAppAccountId = resolveDefaultAccountId;
32
+ function resolveAccountConfig(cfg, accountId) {
33
+ return resolveAccountEntry(cfg.channels?.whatsapp?.accounts, accountId);
34
+ }
35
+ function resolveDefaultAuthDir(accountId) {
36
+ return path.join(resolveOAuthDir(), "whatsapp", normalizeAccountId(accountId));
37
+ }
38
+ function resolveLegacyAuthDir() {
39
+ return resolveOAuthDir();
40
+ }
41
+ function legacyAuthExists(authDir) {
42
+ try {
43
+ return fs.existsSync(path.join(authDir, "creds.json"));
44
+ } catch {
45
+ return false;
46
+ }
47
+ }
48
+ function resolveWhatsAppAuthDir(params) {
49
+ const accountId = params.accountId.trim() || DEFAULT_ACCOUNT_ID;
50
+ const configured = resolveAccountConfig(params.cfg, accountId)?.authDir?.trim();
51
+ if (configured) return {
52
+ authDir: resolveUserPath(configured),
53
+ isLegacy: false
54
+ };
55
+ const defaultDir = resolveDefaultAuthDir(accountId);
56
+ if (accountId === DEFAULT_ACCOUNT_ID) {
57
+ const legacyDir = resolveLegacyAuthDir();
58
+ if (legacyAuthExists(legacyDir) && !legacyAuthExists(defaultDir)) return {
59
+ authDir: legacyDir,
60
+ isLegacy: true
61
+ };
62
+ }
63
+ return {
64
+ authDir: defaultDir,
65
+ isLegacy: false
66
+ };
67
+ }
68
+ function resolveWhatsAppAccount$1(params) {
69
+ const rootCfg = params.cfg.channels?.whatsapp;
70
+ const accountId = params.accountId?.trim() || resolveDefaultWhatsAppAccountId(params.cfg);
71
+ const accountCfg = resolveAccountConfig(params.cfg, accountId);
72
+ const enabled = rootCfg?.enabled !== false && accountCfg?.enabled !== false;
73
+ const { authDir, isLegacy } = resolveWhatsAppAuthDir({
74
+ cfg: params.cfg,
75
+ accountId
76
+ });
77
+ return {
78
+ accountId,
79
+ name: accountCfg?.name?.trim() || void 0,
80
+ enabled,
81
+ sendReadReceipts: accountCfg?.sendReadReceipts ?? rootCfg?.sendReadReceipts ?? true,
82
+ messagePrefix: accountCfg?.messagePrefix ?? rootCfg?.messagePrefix ?? params.cfg.messages?.messagePrefix,
83
+ authDir,
84
+ isLegacyAuthDir: isLegacy,
85
+ selfChatMode: accountCfg?.selfChatMode ?? rootCfg?.selfChatMode,
86
+ dmPolicy: accountCfg?.dmPolicy ?? rootCfg?.dmPolicy,
87
+ allowFrom: accountCfg?.allowFrom ?? rootCfg?.allowFrom,
88
+ groupAllowFrom: accountCfg?.groupAllowFrom ?? rootCfg?.groupAllowFrom,
89
+ groupPolicy: accountCfg?.groupPolicy ?? rootCfg?.groupPolicy,
90
+ textChunkLimit: accountCfg?.textChunkLimit ?? rootCfg?.textChunkLimit,
91
+ chunkMode: accountCfg?.chunkMode ?? rootCfg?.chunkMode,
92
+ chunkNewlinePacking: accountCfg?.chunkNewlinePacking ?? rootCfg?.chunkNewlinePacking,
93
+ mediaMaxMb: accountCfg?.mediaMaxMb ?? rootCfg?.mediaMaxMb,
94
+ blockStreaming: accountCfg?.blockStreaming ?? rootCfg?.blockStreaming,
95
+ ackReaction: accountCfg?.ackReaction ?? rootCfg?.ackReaction,
96
+ groups: accountCfg?.groups ?? rootCfg?.groups,
97
+ debounceMs: accountCfg?.debounceMs ?? rootCfg?.debounceMs
98
+ };
99
+ }
100
+
101
+ //#endregion
102
+ //#region src/active-listener.ts
103
+ const listeners = /* @__PURE__ */ new Map();
104
+ const connectionLocks = /* @__PURE__ */ new Map();
105
+ function withAccountLock(accountId, fn) {
106
+ const next = (connectionLocks.get(accountId) ?? Promise.resolve()).then(fn);
107
+ const settled = next.catch(() => void 0);
108
+ connectionLocks.set(accountId, settled);
109
+ settled.finally(() => {
110
+ if (connectionLocks.get(accountId) === settled) connectionLocks.delete(accountId);
111
+ });
112
+ return next;
113
+ }
114
+ function resolveWebAccountId(accountId) {
115
+ return (accountId ?? "").trim() || DEFAULT_ACCOUNT_ID;
116
+ }
117
+ function setActiveWebListener(accountIdOrListener, maybeListener) {
118
+ const { accountId, listener } = typeof accountIdOrListener === "string" ? {
119
+ accountId: accountIdOrListener,
120
+ listener: maybeListener ?? null
121
+ } : {
122
+ accountId: DEFAULT_ACCOUNT_ID,
123
+ listener: accountIdOrListener ?? null
124
+ };
125
+ const id = resolveWebAccountId(accountId);
126
+ if (!listener) listeners.delete(id);
127
+ else listeners.set(id, listener);
128
+ }
129
+ function getActiveWebListener(accountId) {
130
+ const id = resolveWebAccountId(accountId);
131
+ return listeners.get(id) ?? null;
132
+ }
133
+ async function stopActiveWebListener(accountId, reason = {
134
+ status: void 0,
135
+ isLoggedOut: false,
136
+ error: "closed"
137
+ }) {
138
+ const id = resolveWebAccountId(accountId);
139
+ if (!listeners.has(id)) return false;
140
+ let stopped = false;
141
+ await withAccountLock(id, async () => {
142
+ const current = listeners.get(id);
143
+ if (!current) return;
144
+ listeners.delete(id);
145
+ try {
146
+ current.signalClose?.(reason);
147
+ } catch {}
148
+ try {
149
+ await current.close?.();
150
+ stopped = true;
151
+ } catch (err) {
152
+ if (!listeners.has(id)) listeners.set(id, current);
153
+ throw err;
154
+ }
155
+ });
156
+ return stopped;
157
+ }
158
+
159
+ //#endregion
160
+ //#region src/runtime.ts
161
+ let runtime = null;
162
+ function setWhatsAppRuntime(next) {
163
+ runtime = next;
164
+ }
165
+ function getWhatsAppRuntime() {
166
+ if (!runtime) throw new Error("WhatsApp runtime not initialized");
167
+ return runtime;
168
+ }
169
+
170
+ //#endregion
171
+ //#region src/auth-store.ts
172
+ function resolveDefaultWebAuthDir() {
173
+ return path.join(resolveOAuthDir(), "whatsapp", DEFAULT_ACCOUNT_ID);
174
+ }
175
+ const WA_WEB_AUTH_DIR = resolveDefaultWebAuthDir();
176
+ function resolveWebCredsPath(authDir) {
177
+ return path.join(authDir, "creds.json");
178
+ }
179
+ function resolveWebCredsBackupPath(authDir) {
180
+ return path.join(authDir, "creds.json.bak");
181
+ }
182
+ const RECENT_CREDS_WRITE_GRACE_MS = 5e3;
183
+ const AUTH_CLEAR_SAVE_SUPPRESSION_MS = 5e3;
184
+ const WA_SOCKET_LOCK_OPTIONS$1 = {
185
+ retries: {
186
+ retries: 0,
187
+ factor: 1,
188
+ minTimeout: 100,
189
+ maxTimeout: 100
190
+ },
191
+ stale: 10080 * 60 * 1e3,
192
+ reentrant: false
193
+ };
194
+ const authDirsBeingCleared = resolveProcessScopedMap(Symbol.for("openclaw.webAuthDirsBeingCleared"));
195
+ const authDirClearSuppressedUntil = resolveProcessScopedMap(Symbol.for("openclaw.webAuthDirClearSuppressedUntil"));
196
+ const credsSaveQueues = resolveProcessScopedMap(Symbol.for("openclaw.webCredsSaveQueues"));
197
+ function canonicalWebAuthDir(authDir) {
198
+ const resolvedAuthDir = resolveUserPath(authDir);
199
+ try {
200
+ return fs.realpathSync.native(resolvedAuthDir);
201
+ } catch (err) {
202
+ if (err.code === "ENOENT") return resolvedAuthDir;
203
+ throw err;
204
+ }
205
+ }
206
+ function isWebAuthBeingCleared(authDir) {
207
+ const authDirKey = canonicalWebAuthDir(authDir);
208
+ const suppressedUntil = authDirClearSuppressedUntil.get(authDirKey) ?? 0;
209
+ if (suppressedUntil > Date.now()) return true;
210
+ if (suppressedUntil) authDirClearSuppressedUntil.delete(authDirKey);
211
+ return authDirsBeingCleared.has(authDirKey);
212
+ }
213
+ function statFileSync(filePath) {
214
+ try {
215
+ return fs.statSync(filePath);
216
+ } catch {
217
+ return null;
218
+ }
219
+ }
220
+ function isRecentFileWrite(stats) {
221
+ return Boolean(stats?.isFile() && Date.now() - stats.mtimeMs < RECENT_CREDS_WRITE_GRACE_MS);
222
+ }
223
+ function readCredsJsonRaw(filePath) {
224
+ try {
225
+ if (!fs.existsSync(filePath)) return null;
226
+ const stats = fs.statSync(filePath);
227
+ if (!stats.isFile() || stats.size <= 1) return null;
228
+ return fs.readFileSync(filePath, "utf-8");
229
+ } catch {
230
+ return null;
231
+ }
232
+ }
233
+ function enqueueWebCredsSave(authDir, saveCreds, logger) {
234
+ const resolvedAuthDir = resolveUserPath(authDir);
235
+ const authDirKey = canonicalWebAuthDir(resolvedAuthDir);
236
+ if (isWebAuthBeingCleared(authDirKey)) return;
237
+ const previous = credsSaveQueues.get(authDirKey) ?? Promise.resolve();
238
+ let next;
239
+ next = previous.then(() => safeSaveCreds(resolvedAuthDir, saveCreds, logger)).catch((err) => {
240
+ logger.warn({ error: String(err) }, "WhatsApp creds save queue error");
241
+ }).finally(() => {
242
+ if (credsSaveQueues.get(authDirKey) === next) credsSaveQueues.delete(authDirKey);
243
+ });
244
+ credsSaveQueues.set(authDirKey, next);
245
+ }
246
+ function suppressPostClearCredsSaves(authDir) {
247
+ const authDirKey = canonicalWebAuthDir(authDir);
248
+ const suppressedUntil = Date.now() + AUTH_CLEAR_SAVE_SUPPRESSION_MS;
249
+ authDirClearSuppressedUntil.set(authDirKey, suppressedUntil);
250
+ setTimeout(() => {
251
+ if (authDirClearSuppressedUntil.get(authDirKey) === suppressedUntil) authDirClearSuppressedUntil.delete(authDirKey);
252
+ }, AUTH_CLEAR_SAVE_SUPPRESSION_MS).unref?.();
253
+ }
254
+ function clearWebAuthClearSuppression(authDir) {
255
+ authDirClearSuppressedUntil.delete(canonicalWebAuthDir(authDir));
256
+ }
257
+ async function waitForWebCredsSaveQueue(authDir) {
258
+ const authDirKey = canonicalWebAuthDir(authDir);
259
+ while (true) {
260
+ const queue = credsSaveQueues.get(authDirKey);
261
+ if (!queue) return;
262
+ await queue;
263
+ }
264
+ }
265
+ async function safeSaveCreds(authDir, saveCreds, logger) {
266
+ if (isWebAuthBeingCleared(authDir)) return;
267
+ try {
268
+ const credsPath = resolveWebCredsPath(authDir);
269
+ const backupPath = resolveWebCredsBackupPath(authDir);
270
+ const raw = readCredsJsonRaw(credsPath);
271
+ if (raw) try {
272
+ JSON.parse(raw);
273
+ fs.copyFileSync(credsPath, backupPath);
274
+ try {
275
+ fs.chmodSync(backupPath, 384);
276
+ } catch {}
277
+ } catch {}
278
+ } catch {}
279
+ if (isWebAuthBeingCleared(authDir)) return;
280
+ try {
281
+ await Promise.resolve(saveCreds());
282
+ try {
283
+ fs.chmodSync(resolveWebCredsPath(authDir), 384);
284
+ } catch {}
285
+ } catch (err) {
286
+ logger.warn({ error: String(err) }, "failed saving WhatsApp creds");
287
+ }
288
+ }
289
+ function isParseableJson(raw) {
290
+ try {
291
+ JSON.parse(raw);
292
+ return true;
293
+ } catch {
294
+ return false;
295
+ }
296
+ }
297
+ function maybeRestoreCredsFromBackup(authDir) {
298
+ const logger = getChildLogger({ module: "web-session" });
299
+ try {
300
+ if (isWebAuthBeingCleared(authDir)) return;
301
+ const credsPath = resolveWebCredsPath(authDir);
302
+ const backupPath = resolveWebCredsBackupPath(authDir);
303
+ const raw = readCredsJsonRaw(credsPath);
304
+ if (raw && isParseableJson(raw)) return;
305
+ if (isRecentFileWrite(statFileSync(credsPath))) return;
306
+ const backupRaw = readCredsJsonRaw(backupPath);
307
+ const backupStats = statFileSync(backupPath);
308
+ if (!backupStats?.isFile() || !backupRaw || !isParseableJson(backupRaw)) return;
309
+ fs.copyFileSync(backupPath, credsPath);
310
+ try {
311
+ fs.chmodSync(credsPath, 384);
312
+ } catch {}
313
+ try {
314
+ fs.utimesSync(credsPath, backupStats.atime, backupStats.mtime);
315
+ } catch {}
316
+ logger.warn({ credsPath }, "restored corrupted WhatsApp creds.json from backup");
317
+ } catch {}
318
+ }
319
+ async function webAuthExists(authDir = resolveDefaultWebAuthDir()) {
320
+ const resolvedAuthDir = resolveUserPath(authDir);
321
+ maybeRestoreCredsFromBackup(resolvedAuthDir);
322
+ const credsPath = resolveWebCredsPath(resolvedAuthDir);
323
+ try {
324
+ await fs$1.access(resolvedAuthDir);
325
+ } catch {
326
+ return false;
327
+ }
328
+ try {
329
+ const stats = await fs$1.stat(credsPath);
330
+ if (!stats.isFile() || stats.size <= 1) return false;
331
+ const raw = await fs$1.readFile(credsPath, "utf-8");
332
+ JSON.parse(raw);
333
+ return true;
334
+ } catch {
335
+ return false;
336
+ }
337
+ }
338
+ function isLegacyBaileysAuthFile(name) {
339
+ if (name === "creds.json" || name === "creds.json.bak") return true;
340
+ if (!name.endsWith(".json")) return false;
341
+ return /^(app-state-sync|session|sender-key|pre-key)-/.test(name);
342
+ }
343
+ async function clearBaileysAuthState(authDir, isLegacyAuthDir) {
344
+ const entries = await fs$1.readdir(authDir, { withFileTypes: true });
345
+ const shouldDelete = (name) => {
346
+ if (name === "oauth.json") return false;
347
+ if (isLegacyAuthDir) return isLegacyBaileysAuthFile(name);
348
+ return name === "creds.json.bak" || name.endsWith(".json");
349
+ };
350
+ const deleteNames = entries.filter((entry) => (entry.isFile() || entry.isSymbolicLink()) && shouldDelete(entry.name)).map((entry) => entry.name).toSorted((a, b) => {
351
+ if (a === "creds.json.bak") return -1;
352
+ if (b === "creds.json.bak") return 1;
353
+ if (a === "creds.json") return -1;
354
+ if (b === "creds.json") return 1;
355
+ return a.localeCompare(b);
356
+ });
357
+ for (const name of deleteNames) await fs$1.rm(path.join(authDir, name), { force: true });
358
+ return deleteNames.length;
359
+ }
360
+ function resolveWaSocketLockPath$1(authDir) {
361
+ return path.join(authDir, ".socket-owner");
362
+ }
363
+ async function acquireWaLogoutLock(authDir) {
364
+ try {
365
+ return await acquireFileLock(resolveWaSocketLockPath$1(authDir), WA_SOCKET_LOCK_OPTIONS$1);
366
+ } catch (err) {
367
+ throw new Error(`WhatsApp auth directory is already in use by another openclaw process or socket in this process (${authDir}). Stop the gateway before logging out.`, { cause: err });
368
+ }
369
+ }
370
+ async function releaseWaLogoutLock(lock) {
371
+ try {
372
+ await lock.release();
373
+ } catch {
374
+ await lock.release();
375
+ }
376
+ }
377
+ async function logoutWeb(params) {
378
+ const runtime = params.runtime ?? defaultRuntime;
379
+ const resolvedAuthDir = resolveUserPath(params.authDir ?? resolveDefaultWebAuthDir());
380
+ let stats;
381
+ try {
382
+ stats = await fs$1.lstat(resolvedAuthDir);
383
+ } catch {
384
+ runtime.log(info("No WhatsApp Web session found; nothing to delete."));
385
+ return false;
386
+ }
387
+ if (stats.isSymbolicLink()) throw new Error(`Refusing to clear WhatsApp auth directory because it is a symlink: ${resolvedAuthDir}`);
388
+ if (!stats.isDirectory()) {
389
+ runtime.log(info("No WhatsApp Web session found; nothing to delete."));
390
+ return false;
391
+ }
392
+ const socketLock = await acquireWaLogoutLock(resolvedAuthDir);
393
+ let authDirKey = null;
394
+ try {
395
+ authDirKey = canonicalWebAuthDir(resolvedAuthDir);
396
+ authDirsBeingCleared.set(authDirKey, true);
397
+ await waitForWebCredsSaveQueue(authDirKey);
398
+ const exists = await webAuthExists(resolvedAuthDir);
399
+ const deletedCount = await clearBaileysAuthState(resolvedAuthDir, Boolean(params.isLegacyAuthDir));
400
+ if (!exists && deletedCount === 0) {
401
+ runtime.log(info("No WhatsApp Web session found; nothing to delete."));
402
+ return false;
403
+ }
404
+ runtime.log(success("Cleared WhatsApp Web credentials."));
405
+ return true;
406
+ } finally {
407
+ if (authDirKey) {
408
+ authDirsBeingCleared.delete(authDirKey);
409
+ suppressPostClearCredsSaves(authDirKey);
410
+ }
411
+ await releaseWaLogoutLock(socketLock);
412
+ }
413
+ }
414
+ function readWebSelfId(authDir = resolveDefaultWebAuthDir()) {
415
+ try {
416
+ const credsPath = resolveWebCredsPath(resolveUserPath(authDir));
417
+ if (!fs.existsSync(credsPath)) return {
418
+ e164: null,
419
+ jid: null
420
+ };
421
+ const raw = fs.readFileSync(credsPath, "utf-8");
422
+ const jid = JSON.parse(raw)?.me?.id ?? null;
423
+ return {
424
+ e164: jid ? jidToE164(jid, { authDir }) : null,
425
+ jid
426
+ };
427
+ } catch {
428
+ return {
429
+ e164: null,
430
+ jid: null
431
+ };
432
+ }
433
+ }
434
+ /**
435
+ * Return the age (in milliseconds) of the cached WhatsApp web auth state, or null when missing.
436
+ * Helpful for heartbeats/observability to spot stale credentials.
437
+ */
438
+ function getWebAuthAgeMs(authDir = resolveDefaultWebAuthDir()) {
439
+ try {
440
+ const stats = fs.statSync(resolveWebCredsPath(resolveUserPath(authDir)));
441
+ return Date.now() - stats.mtimeMs;
442
+ } catch {
443
+ return null;
444
+ }
445
+ }
446
+ function logWebSelfId(authDir = resolveDefaultWebAuthDir(), runtime = defaultRuntime, includeChannelPrefix = false) {
447
+ const { e164, jid } = readWebSelfId(authDir);
448
+ const details = e164 || jid ? `${e164 ?? "unknown"}${jid ? ` (jid ${jid})` : ""}` : "unknown";
449
+ const prefix = includeChannelPrefix ? "Web Channel: " : "";
450
+ runtime.log(info(`${prefix}${details}`));
451
+ }
452
+
453
+ //#endregion
454
+ //#region src/baileys.ts
455
+ var WhatsAppDependencyError = class extends Error {
456
+ constructor(cause) {
457
+ super("WhatsApp dependency '@whiskeysockets/baileys' is not installed. Install the WhatsApp extension's dependencies (run `npm install --omit=dev` in the extension directory, or install the channel via `openclaw plugins install`).", { cause });
458
+ this.name = "WhatsAppDependencyError";
459
+ }
460
+ };
461
+ function isModuleNotFound(err) {
462
+ if (typeof err !== "object" || err === null) return false;
463
+ const code = err.code;
464
+ if (code === "ERR_MODULE_NOT_FOUND" || code === "MODULE_NOT_FOUND") return true;
465
+ const message = err.message;
466
+ return typeof message === "string" && message.includes("Cannot find module");
467
+ }
468
+ let cached = null;
469
+ /**
470
+ * Resolve baileys via an injectable importer. `getBaileys` calls this with the
471
+ * real dynamic import; tests inject a controlled importer to exercise the
472
+ * dependency-missing path without uninstalling the package. Throws
473
+ * WhatsAppDependencyError when the package is absent; re-throws any other
474
+ * import failure unchanged.
475
+ */
476
+ async function resolveBaileys(importer) {
477
+ try {
478
+ return await importer();
479
+ } catch (err) {
480
+ if (isModuleNotFound(err)) throw new WhatsAppDependencyError(err);
481
+ throw err;
482
+ }
483
+ }
484
+ /**
485
+ * Lazily resolve @whiskeysockets/baileys. Caches the in-flight promise so
486
+ * concurrent first-callers share one import. On failure the cache is cleared so
487
+ * a later call can retry.
488
+ */
489
+ async function getBaileys() {
490
+ if (cached) return cached;
491
+ cached = resolveBaileys(() => import("@whiskeysockets/baileys")).catch((err) => {
492
+ cached = null;
493
+ throw err;
494
+ });
495
+ return cached;
496
+ }
497
+
498
+ //#endregion
499
+ //#region src/qr-image.ts
500
+ const QRCode = QRCodeModule;
501
+ const QRErrorCorrectLevel = QRErrorCorrectLevelModule;
502
+ function createQrMatrix(input) {
503
+ const qr = new QRCode(-1, QRErrorCorrectLevel.L);
504
+ qr.addData(input);
505
+ qr.make();
506
+ return qr;
507
+ }
508
+ async function renderQrPngBase64(input, opts = {}) {
509
+ const { scale = 6, marginModules = 4 } = opts;
510
+ const qr = createQrMatrix(input);
511
+ const modules = qr.getModuleCount();
512
+ const size = (modules + marginModules * 2) * scale;
513
+ const buf = Buffer.alloc(size * size * 4, 255);
514
+ for (let row = 0; row < modules; row += 1) for (let col = 0; col < modules; col += 1) {
515
+ if (!qr.isDark(row, col)) continue;
516
+ const startX = (col + marginModules) * scale;
517
+ const startY = (row + marginModules) * scale;
518
+ for (let y = 0; y < scale; y += 1) {
519
+ const pixelY = startY + y;
520
+ for (let x = 0; x < scale; x += 1) fillPixel(buf, startX + x, pixelY, size, 0, 0, 0, 255);
521
+ }
522
+ }
523
+ return encodePngRgba(buf, size, size).toString("base64");
524
+ }
525
+
526
+ //#endregion
527
+ //#region src/session.ts
528
+ const BAILEYS_VERSION_CACHE_TTL_MS = 1440 * 60 * 1e3;
529
+ const BAILEYS_VERSION_FETCH_TIMEOUT_MS = 3e3;
530
+ const BAILEYS_VERSION_FETCH_BACKOFF_MS = 300 * 1e3;
531
+ const WA_SOCKET_LOCK_OPTIONS = {
532
+ retries: {
533
+ retries: 0,
534
+ factor: 1,
535
+ minTimeout: 100,
536
+ maxTimeout: 100
537
+ },
538
+ stale: 10080 * 60 * 1e3,
539
+ reentrant: false
540
+ };
541
+ const WA_SOCKET_LOCK_RELEASE_KEY = "__openclawWaSocketLockRelease";
542
+ let cachedBaileysVersion = null;
543
+ let inflightBaileysVersion = null;
544
+ let baileysVersionFetchBackoffUntil = 0;
545
+ function fetchBaileysVersionOnce() {
546
+ if (inflightBaileysVersion) return inflightBaileysVersion;
547
+ const controller = new AbortController();
548
+ const timeout = setTimeout(() => controller.abort(), BAILEYS_VERSION_FETCH_TIMEOUT_MS);
549
+ const next = getBaileys().then(({ fetchLatestBaileysVersion }) => fetchLatestBaileysVersion({ signal: controller.signal })).finally(() => {
550
+ clearTimeout(timeout);
551
+ if (inflightBaileysVersion === next) inflightBaileysVersion = null;
552
+ });
553
+ inflightBaileysVersion = next;
554
+ return next;
555
+ }
556
+ function backOffBaileysVersionFetch() {
557
+ baileysVersionFetchBackoffUntil = Date.now() + BAILEYS_VERSION_FETCH_BACKOFF_MS;
558
+ inflightBaileysVersion = null;
559
+ }
560
+ function resolveWaSocketLockPath(authDir) {
561
+ return path.join(authDir, ".socket-owner");
562
+ }
563
+ function shouldAcquireWaSocketLock() {
564
+ return process.env.VITEST !== "true" || process.env.OPENCLAW_TEST_WA_SOCKET_LOCK === "1";
565
+ }
566
+ async function acquireWaSocketLock(authDir) {
567
+ try {
568
+ return await acquireFileLock(resolveWaSocketLockPath(authDir), WA_SOCKET_LOCK_OPTIONS);
569
+ } catch (err) {
570
+ throw new Error(`WhatsApp auth directory is already in use by another openclaw process or socket in this process (${authDir}). Stop the gateway or use the gateway login flow before relinking.`, { cause: err });
571
+ }
572
+ }
573
+ function attachWaSocketLockRelease(sock, lock) {
574
+ let releasePromise = null;
575
+ const release = () => {
576
+ if (!releasePromise) releasePromise = lock.release().catch((err) => {
577
+ releasePromise = null;
578
+ throw err;
579
+ });
580
+ return releasePromise;
581
+ };
582
+ sock[WA_SOCKET_LOCK_RELEASE_KEY] = release;
583
+ }
584
+ async function closeWaSocket(sock) {
585
+ const release = sock[WA_SOCKET_LOCK_RELEASE_KEY];
586
+ try {
587
+ sock.ws?.close();
588
+ } catch {}
589
+ await release?.();
590
+ if (release) delete sock[WA_SOCKET_LOCK_RELEASE_KEY];
591
+ }
592
+ function withBaileysVersionTimeout(promise, timeoutMs) {
593
+ return new Promise((resolve, reject) => {
594
+ const timeout = setTimeout(() => resolve(null), timeoutMs);
595
+ promise.then((result) => {
596
+ clearTimeout(timeout);
597
+ resolve(result);
598
+ }, (err) => {
599
+ clearTimeout(timeout);
600
+ reject(err);
601
+ });
602
+ });
603
+ }
604
+ async function resolveBaileysVersion(logger) {
605
+ const { DEFAULT_CONNECTION_CONFIG } = await getBaileys();
606
+ const now = Date.now();
607
+ if (cachedBaileysVersion && now - cachedBaileysVersion.fetchedAt < BAILEYS_VERSION_CACHE_TTL_MS) return cachedBaileysVersion.version;
608
+ if (now < baileysVersionFetchBackoffUntil) return cachedBaileysVersion?.version ?? DEFAULT_CONNECTION_CONFIG.version;
609
+ try {
610
+ const result = await withBaileysVersionTimeout(fetchBaileysVersionOnce(), BAILEYS_VERSION_FETCH_TIMEOUT_MS);
611
+ if (!result) {
612
+ logger.warn("timed out fetching latest WhatsApp Web version");
613
+ backOffBaileysVersionFetch();
614
+ return cachedBaileysVersion?.version ?? DEFAULT_CONNECTION_CONFIG.version;
615
+ }
616
+ if (result.error) {
617
+ logger.warn({ error: safeStringify(result.error) }, "failed fetching latest WhatsApp Web version");
618
+ backOffBaileysVersionFetch();
619
+ } else {
620
+ cachedBaileysVersion = {
621
+ version: result.version,
622
+ fetchedAt: now
623
+ };
624
+ baileysVersionFetchBackoffUntil = 0;
625
+ return result.version;
626
+ }
627
+ } catch (err) {
628
+ logger.warn({ error: String(err) }, "failed fetching latest WhatsApp Web version");
629
+ backOffBaileysVersionFetch();
630
+ }
631
+ return cachedBaileysVersion?.version ?? DEFAULT_CONNECTION_CONFIG.version;
632
+ }
633
+ async function createWaSocket(printQr, verbose, opts = {}) {
634
+ const noop = (..._args) => {};
635
+ const noopLogger = {
636
+ level: "silent",
637
+ child: () => noopLogger,
638
+ trace: noop,
639
+ debug: noop,
640
+ info: noop,
641
+ warn: noop,
642
+ error: noop,
643
+ fatal: noop
644
+ };
645
+ const logger = verbose ? toPinoLikeLogger(getChildLogger({ module: "baileys" }, { level: "info" }), "info") : noopLogger;
646
+ const authDir = resolveUserPath(opts.authDir ?? resolveDefaultWebAuthDir());
647
+ await ensureDir(authDir);
648
+ const sessionLogger = getChildLogger({ module: "web-session" });
649
+ const socketLock = shouldAcquireWaSocketLock() ? await acquireWaSocketLock(authDir) : null;
650
+ let sock;
651
+ try {
652
+ const { useMultiFileAuthState, makeWASocket, makeCacheableSignalKeyStore } = await getBaileys();
653
+ clearWebAuthClearSuppression(authDir);
654
+ maybeRestoreCredsFromBackup(authDir);
655
+ const { state, saveCreds } = await useMultiFileAuthState(authDir);
656
+ const version = await resolveBaileysVersion(sessionLogger);
657
+ sock = makeWASocket({
658
+ auth: {
659
+ creds: state.creds,
660
+ keys: makeCacheableSignalKeyStore(state.keys, logger)
661
+ },
662
+ version,
663
+ logger,
664
+ printQRInTerminal: false,
665
+ browser: [
666
+ "openclaw",
667
+ "cli",
668
+ VERSION
669
+ ],
670
+ syncFullHistory: false,
671
+ markOnlineOnConnect: false
672
+ });
673
+ sock.ev.on("creds.update", () => enqueueWebCredsSave(authDir, saveCreds, sessionLogger));
674
+ } catch (err) {
675
+ await socketLock?.release();
676
+ throw err;
677
+ }
678
+ if (socketLock) attachWaSocketLockRelease(sock, socketLock);
679
+ const attachedWebSockets = /* @__PURE__ */ new WeakSet();
680
+ const attachWebSocketErrorHandler = () => {
681
+ const ws = sock.ws;
682
+ if (!ws || typeof ws !== "object" || attachedWebSockets.has(ws)) return;
683
+ const on = ws.on;
684
+ if (typeof on !== "function") return;
685
+ on.call(ws, "error", (err) => {
686
+ sessionLogger.error({ error: String(err) }, "WebSocket error");
687
+ });
688
+ attachedWebSockets.add(ws);
689
+ };
690
+ const { DisconnectReason } = await getBaileys();
691
+ sock.ev.on("connection.update", (update) => {
692
+ try {
693
+ const { connection, lastDisconnect, qr } = update;
694
+ attachWebSocketErrorHandler();
695
+ if (qr) {
696
+ opts.onQr?.(qr);
697
+ if (printQr) {
698
+ sessionLogger.info("Scan this QR in WhatsApp (Linked Devices):");
699
+ qrcode.generate(qr, { small: true });
700
+ }
701
+ }
702
+ if (connection === "close") {
703
+ if (getStatusCode(lastDisconnect?.error) === DisconnectReason.loggedOut) sessionLogger.error(danger(`WhatsApp session logged out. Run: ${formatCliCommand("openclaw channels login")}`));
704
+ }
705
+ if (connection === "open" && verbose) sessionLogger.info(success("WhatsApp Web connected."));
706
+ } catch (err) {
707
+ sessionLogger.error({ error: String(err) }, "connection.update handler error");
708
+ }
709
+ });
710
+ attachWebSocketErrorHandler();
711
+ return sock;
712
+ }
713
+ async function waitForWaConnection(sock) {
714
+ return new Promise((resolve, reject) => {
715
+ const evWithOff = sock.ev;
716
+ const handler = (...args) => {
717
+ const update = args[0] ?? {};
718
+ if (update.connection === "open") {
719
+ removeHandler();
720
+ resolve();
721
+ }
722
+ if (update.connection === "close") {
723
+ removeHandler();
724
+ reject(update.lastDisconnect ?? /* @__PURE__ */ new Error("Connection closed"));
725
+ }
726
+ };
727
+ function removeHandler() {
728
+ if (typeof evWithOff.off === "function") {
729
+ evWithOff.off("connection.update", handler);
730
+ return;
731
+ }
732
+ evWithOff.removeListener?.("connection.update", handler);
733
+ }
734
+ sock.ev.on("connection.update", handler);
735
+ });
736
+ }
737
+ function getStatusCode(err) {
738
+ const nested = err;
739
+ return nested.output?.statusCode ?? nested.status ?? nested.error?.output?.statusCode ?? nested.error?.status ?? nested.lastDisconnect?.error?.output?.statusCode ?? nested.lastDisconnect?.error?.status;
740
+ }
741
+ function safeStringify(value, limit = 800) {
742
+ try {
743
+ const seen = /* @__PURE__ */ new WeakSet();
744
+ const raw = JSON.stringify(value, (_key, v) => {
745
+ if (typeof v === "bigint") return v.toString();
746
+ if (typeof v === "function") {
747
+ const maybeName = v.name;
748
+ return `[Function ${typeof maybeName === "string" && maybeName.length > 0 ? maybeName : "anonymous"}]`;
749
+ }
750
+ if (typeof v === "object" && v) {
751
+ if (seen.has(v)) return "[Circular]";
752
+ seen.add(v);
753
+ }
754
+ return v;
755
+ }, 2);
756
+ if (!raw) return String(value);
757
+ return raw.length > limit ? `${raw.slice(0, limit)}...` : raw;
758
+ } catch {
759
+ return String(value);
760
+ }
761
+ }
762
+ function extractBoomDetails(err) {
763
+ if (!err || typeof err !== "object") return null;
764
+ const output = err?.output;
765
+ if (!output || typeof output !== "object") return null;
766
+ const payload = output.payload;
767
+ const statusCode = typeof output.statusCode === "number" ? output.statusCode : typeof payload?.statusCode === "number" ? payload.statusCode : void 0;
768
+ const error = typeof payload?.error === "string" ? payload.error : void 0;
769
+ const message = typeof payload?.message === "string" ? payload.message : void 0;
770
+ if (!statusCode && !error && !message) return null;
771
+ return {
772
+ statusCode,
773
+ error,
774
+ message
775
+ };
776
+ }
777
+ function formatError(err) {
778
+ if (err instanceof Error) return err.message;
779
+ if (typeof err === "string") return err;
780
+ if (!err || typeof err !== "object") return String(err);
781
+ const boom = extractBoomDetails(err) ?? extractBoomDetails(err?.error) ?? extractBoomDetails(err?.lastDisconnect?.error);
782
+ const status = boom?.statusCode ?? getStatusCode(err);
783
+ const code = err?.code;
784
+ const codeText = typeof code === "string" || typeof code === "number" ? String(code) : void 0;
785
+ const message = [
786
+ boom?.message,
787
+ typeof err?.message === "string" ? err.message : void 0,
788
+ typeof err?.error?.message === "string" ? err.error?.message : void 0
789
+ ].filter((v) => Boolean(v && v.trim().length > 0))[0];
790
+ const pieces = [];
791
+ if (typeof status === "number") pieces.push(`status=${status}`);
792
+ if (boom?.error) pieces.push(boom.error);
793
+ if (message) pieces.push(message);
794
+ if (codeText) pieces.push(`code=${codeText}`);
795
+ if (pieces.length > 0) return pieces.join(" ");
796
+ return safeStringify(err);
797
+ }
798
+
799
+ //#endregion
800
+ //#region src/login-qr.ts
801
+ var login_qr_exports = /* @__PURE__ */ __exportAll({
802
+ canonicalWhatsAppAuthDir: () => canonicalWhatsAppAuthDir,
803
+ closeActiveWebLoginsForAuthDir: () => closeActiveWebLoginsForAuthDir,
804
+ startWebLoginWithQr: () => startWebLoginWithQr,
805
+ tryCanonicalWhatsAppAuthDir: () => tryCanonicalWhatsAppAuthDir,
806
+ waitForWebLogin: () => waitForWebLogin
807
+ });
808
+ const ACTIVE_LOGIN_TTL_MS = 3 * 6e4;
809
+ const activeLogins = /* @__PURE__ */ new Map();
810
+ function loadRuntimeConfig(cfg) {
811
+ return cfg ?? getWhatsAppRuntime().config.loadConfig();
812
+ }
813
+ async function closeSocket(sock) {
814
+ try {
815
+ await closeWaSocket(sock);
816
+ } catch {
817
+ await closeWaSocket(sock);
818
+ }
819
+ }
820
+ async function resetActiveLogin(accountId, reason) {
821
+ const login = activeLogins.get(accountId);
822
+ if (login) try {
823
+ await closeSocket(login.sock);
824
+ } finally {
825
+ activeLogins.delete(accountId);
826
+ }
827
+ if (reason) logInfo(reason);
828
+ }
829
+ function canonicalWhatsAppAuthDir(authDir) {
830
+ const resolvedAuthDir = resolveUserPath(authDir);
831
+ try {
832
+ return fs.realpathSync.native(resolvedAuthDir);
833
+ } catch (err) {
834
+ if (err.code === "ENOENT") return resolvedAuthDir;
835
+ throw err;
836
+ }
837
+ }
838
+ function tryCanonicalWhatsAppAuthDir(authDir) {
839
+ try {
840
+ return canonicalWhatsAppAuthDir(authDir);
841
+ } catch {
842
+ return null;
843
+ }
844
+ }
845
+ async function closeActiveWebLoginsForAuthDir(authDir) {
846
+ const resolvedAuthDir = tryCanonicalWhatsAppAuthDir(authDir);
847
+ if (!resolvedAuthDir) return 0;
848
+ const accountIds = [...activeLogins.values()].filter((login) => tryCanonicalWhatsAppAuthDir(login.authDir) === resolvedAuthDir).map((login) => login.accountId);
849
+ await Promise.all(accountIds.map((accountId) => resetActiveLogin(accountId)));
850
+ return accountIds.length;
851
+ }
852
+ function isLoginFresh(login) {
853
+ return Date.now() - login.startedAt < ACTIVE_LOGIN_TTL_MS;
854
+ }
855
+ function findActiveGatewayAccountForAuthDir(params) {
856
+ const resolvedAuthDir = tryCanonicalWhatsAppAuthDir(params.authDir);
857
+ if (resolvedAuthDir === null) return null;
858
+ const candidateAccountIds = new Set([params.accountId, ...listWhatsAppAccountIds(params.cfg)]);
859
+ for (const accountId of candidateAccountIds) {
860
+ if (tryCanonicalWhatsAppAuthDir(resolveWhatsAppAccount$1({
861
+ cfg: params.cfg,
862
+ accountId
863
+ }).authDir) !== resolvedAuthDir) continue;
864
+ if (getActiveWebListener(accountId)) return accountId;
865
+ }
866
+ return null;
867
+ }
868
+ function attachLoginWaiter(accountId, login) {
869
+ login.waitPromise = waitForWaConnection(login.sock).then(() => {
870
+ const current = activeLogins.get(accountId);
871
+ if (current?.id === login.id) current.connected = true;
872
+ }).catch((err) => {
873
+ const current = activeLogins.get(accountId);
874
+ if (current?.id !== login.id) return;
875
+ current.error = formatError(err);
876
+ current.errorStatus = getStatusCode(err);
877
+ });
878
+ }
879
+ async function restartLoginSocket(login, runtime) {
880
+ if (login.restartAttempted) return false;
881
+ login.restartAttempted = true;
882
+ runtime.log(info("WhatsApp asked for a restart after pairing (code 515); retrying connection once..."));
883
+ await closeSocket(login.sock);
884
+ try {
885
+ login.sock = await createWaSocket(false, login.verbose, { authDir: login.authDir });
886
+ login.connected = false;
887
+ login.error = void 0;
888
+ login.errorStatus = void 0;
889
+ attachLoginWaiter(login.accountId, login);
890
+ return true;
891
+ } catch (err) {
892
+ login.error = formatError(err);
893
+ login.errorStatus = getStatusCode(err);
894
+ return false;
895
+ }
896
+ }
897
+ async function startWebLoginWithQr(opts = {}) {
898
+ const runtime = opts.runtime ?? defaultRuntime;
899
+ const cfg = loadRuntimeConfig(opts.cfg);
900
+ const account = resolveWhatsAppAccount$1({
901
+ cfg,
902
+ accountId: opts.accountId
903
+ });
904
+ const hasWeb = await webAuthExists(account.authDir);
905
+ const selfId = readWebSelfId(account.authDir);
906
+ if (hasWeb && !opts.force) return { message: `WhatsApp is already linked (${selfId.e164 ?? selfId.jid ?? "unknown"}). Say "relink" if you want a fresh QR.` };
907
+ if (tryCanonicalWhatsAppAuthDir(account.authDir) === null) return { message: `Cannot start QR login: WhatsApp auth directory ${account.authDir} is not accessible (broken symlink or permission error). Fix the directory before relinking.` };
908
+ const activeGatewayAccount = findActiveGatewayAccountForAuthDir({
909
+ cfg,
910
+ accountId: account.accountId,
911
+ authDir: account.authDir
912
+ });
913
+ if (activeGatewayAccount) return { message: activeGatewayAccount === account.accountId ? "WhatsApp gateway is running for this account. Stop the channel or use the gateway login flow before relinking." : `WhatsApp gateway is running for account ${activeGatewayAccount} using this auth directory. Stop that channel or use the gateway login flow before relinking.` };
914
+ const existing = activeLogins.get(account.accountId);
915
+ if (existing && isLoginFresh(existing) && existing.qrDataUrl) return {
916
+ qrDataUrl: existing.qrDataUrl,
917
+ message: "QR already active. Scan it in WhatsApp -> Linked Devices."
918
+ };
919
+ await resetActiveLogin(account.accountId);
920
+ let resolveQr = null;
921
+ let rejectQr = null;
922
+ const qrPromise = new Promise((resolve, reject) => {
923
+ resolveQr = resolve;
924
+ rejectQr = reject;
925
+ });
926
+ const qrTimer = setTimeout(() => {
927
+ rejectQr?.(/* @__PURE__ */ new Error("Timed out waiting for WhatsApp QR"));
928
+ }, Math.max(opts.timeoutMs ?? 3e4, 5e3));
929
+ let sock;
930
+ let pendingQr = null;
931
+ try {
932
+ sock = await createWaSocket(false, Boolean(opts.verbose), {
933
+ authDir: account.authDir,
934
+ onQr: (qr) => {
935
+ if (pendingQr) return;
936
+ pendingQr = qr;
937
+ const current = activeLogins.get(account.accountId);
938
+ if (current && !current.qr) current.qr = qr;
939
+ clearTimeout(qrTimer);
940
+ runtime.log(info("WhatsApp QR received."));
941
+ resolveQr?.(qr);
942
+ }
943
+ });
944
+ } catch (err) {
945
+ clearTimeout(qrTimer);
946
+ await resetActiveLogin(account.accountId);
947
+ return { message: `Failed to start WhatsApp login: ${String(err)}` };
948
+ }
949
+ const login = {
950
+ accountId: account.accountId,
951
+ authDir: account.authDir,
952
+ isLegacyAuthDir: account.isLegacyAuthDir,
953
+ id: randomUUID(),
954
+ sock,
955
+ startedAt: Date.now(),
956
+ connected: false,
957
+ waitPromise: Promise.resolve(),
958
+ restartAttempted: false,
959
+ verbose: Boolean(opts.verbose)
960
+ };
961
+ activeLogins.set(account.accountId, login);
962
+ if (pendingQr && !login.qr) login.qr = pendingQr;
963
+ attachLoginWaiter(account.accountId, login);
964
+ let qr;
965
+ try {
966
+ qr = await qrPromise;
967
+ } catch (err) {
968
+ clearTimeout(qrTimer);
969
+ await resetActiveLogin(account.accountId);
970
+ return { message: `Failed to get QR: ${String(err)}` };
971
+ }
972
+ login.qrDataUrl = `data:image/png;base64,${await renderQrPngBase64(qr)}`;
973
+ return {
974
+ qrDataUrl: login.qrDataUrl,
975
+ message: "Scan this QR in WhatsApp -> Linked Devices."
976
+ };
977
+ }
978
+ async function waitForWebLogin(opts = {}) {
979
+ const { DisconnectReason } = await getBaileys();
980
+ const runtime = opts.runtime ?? defaultRuntime;
981
+ const account = resolveWhatsAppAccount$1({
982
+ cfg: loadRuntimeConfig(opts.cfg),
983
+ accountId: opts.accountId
984
+ });
985
+ const activeLogin = activeLogins.get(account.accountId);
986
+ if (!activeLogin) return {
987
+ connected: false,
988
+ message: "No active WhatsApp login in progress."
989
+ };
990
+ const login = activeLogin;
991
+ if (!isLoginFresh(login)) {
992
+ await resetActiveLogin(account.accountId);
993
+ return {
994
+ connected: false,
995
+ message: "The login QR expired. Ask me to generate a new one."
996
+ };
997
+ }
998
+ const timeoutMs = Math.max(opts.timeoutMs ?? 12e4, 1e3);
999
+ const deadline = Date.now() + timeoutMs;
1000
+ while (true) {
1001
+ const remaining = deadline - Date.now();
1002
+ if (remaining <= 0) return {
1003
+ connected: false,
1004
+ message: "Still waiting for the QR scan. Let me know when you've scanned it."
1005
+ };
1006
+ let timeoutHandle;
1007
+ const timeout = new Promise((resolve) => {
1008
+ timeoutHandle = setTimeout(() => resolve("timeout"), remaining);
1009
+ });
1010
+ let result;
1011
+ try {
1012
+ result = await Promise.race([login.waitPromise.then(() => "done"), timeout]);
1013
+ } finally {
1014
+ if (timeoutHandle) clearTimeout(timeoutHandle);
1015
+ }
1016
+ if (result === "timeout") return {
1017
+ connected: false,
1018
+ message: "Still waiting for the QR scan. Let me know when you've scanned it."
1019
+ };
1020
+ if (login.error) {
1021
+ if (login.errorStatus === DisconnectReason.loggedOut) {
1022
+ await closeActiveWebLoginsForAuthDir(login.authDir);
1023
+ await logoutWeb({
1024
+ authDir: login.authDir,
1025
+ isLegacyAuthDir: login.isLegacyAuthDir,
1026
+ runtime
1027
+ });
1028
+ const message = "WhatsApp reported the session is logged out. Cleared cached web session; please scan a new QR.";
1029
+ logInfo(message);
1030
+ runtime.log(danger(message));
1031
+ return {
1032
+ connected: false,
1033
+ message
1034
+ };
1035
+ }
1036
+ if (login.errorStatus === 515) {
1037
+ if (await restartLoginSocket(login, runtime) && isLoginFresh(login)) continue;
1038
+ }
1039
+ const message = `WhatsApp login failed: ${login.error}`;
1040
+ await resetActiveLogin(account.accountId, message);
1041
+ runtime.log(danger(message));
1042
+ return {
1043
+ connected: false,
1044
+ message
1045
+ };
1046
+ }
1047
+ if (login.connected) {
1048
+ const message = "Linked! WhatsApp is ready.";
1049
+ runtime.log(success(message));
1050
+ await resetActiveLogin(account.accountId);
1051
+ return {
1052
+ connected: true,
1053
+ message
1054
+ };
1055
+ }
1056
+ return {
1057
+ connected: false,
1058
+ message: "Login ended without a connection."
1059
+ };
1060
+ }
1061
+ }
1062
+
1063
+ //#endregion
1064
+ export { listWhatsAppAccountIds as C, resolveWhatsAppAuthDir as E, stopActiveWebListener as S, resolveWhatsAppAccount$1 as T, webAuthExists as _, waitForWebLogin as a, getActiveWebListener as b, formatError as c, getBaileys as d, getWebAuthAgeMs as f, waitForWebCredsSaveQueue as g, readWebSelfId as h, tryCanonicalWhatsAppAuthDir as i, getStatusCode as l, logoutWeb as m, login_qr_exports as n, closeWaSocket as o, logWebSelfId as p, startWebLoginWithQr as r, createWaSocket as s, closeActiveWebLoginsForAuthDir as t, waitForWaConnection as u, getWhatsAppRuntime as v, resolveDefaultWhatsAppAccountId as w, setActiveWebListener as x, setWhatsAppRuntime as y };