@openclaw/whatsapp 2026.5.2 → 2026.5.3-beta.1

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.
Files changed (302) hide show
  1. package/dist/access-control-BQ3tCkH8.js +288 -0
  2. package/dist/account-config-CNQ33oav.js +47 -0
  3. package/dist/account-ids-DG18xddX.js +5 -0
  4. package/dist/accounts-BDqgpFEB.js +116 -0
  5. package/dist/action-runtime-DTGNgG-i.js +74 -0
  6. package/dist/action-runtime-api.js +2 -0
  7. package/dist/action-runtime.runtime.js +2 -0
  8. package/dist/active-listener-N7GFKsuN.js +11 -0
  9. package/dist/agent-tools-login-DpRxLNRB.js +89 -0
  10. package/dist/api.js +148 -0
  11. package/dist/audio-preflight.runtime-Bcdsl_ri.js +7 -0
  12. package/dist/auth-presence.js +32 -0
  13. package/dist/auth-store-BjCb50Ry.js +381 -0
  14. package/dist/channel-DQ-5Xoj9.js +542 -0
  15. package/dist/channel-config-api.js +2 -0
  16. package/dist/channel-plugin-api.js +2 -0
  17. package/dist/channel-react-action-Df20qAy9.js +42 -0
  18. package/dist/channel.runtime-DKXRnA8x.js +55 -0
  19. package/dist/channel.setup-DOHR2BGp.js +20 -0
  20. package/dist/command-policy-O8sRnqUV.js +8 -0
  21. package/dist/config-schema-Dmo66Ubb.js +27 -0
  22. package/dist/connection-controller-BiHAsTM8.js +596 -0
  23. package/dist/connection-controller-registry-D_iIGz5m.js +22 -0
  24. package/dist/constants-DLGCIbfl.js +4 -0
  25. package/dist/constants.js +2 -0
  26. package/dist/contract-api.js +19 -0
  27. package/dist/creds-files-_2V8jzQa.js +19 -0
  28. package/dist/directory-config-DVs4ffXu.js +37 -0
  29. package/dist/directory-contract-api.js +2 -0
  30. package/dist/doctor-A9tXm9Om.js +48 -0
  31. package/dist/doctor-contract-api.js +10 -0
  32. package/dist/group-session-contract-B8QBn0I2.js +15 -0
  33. package/dist/identity-QHAlV7Fu.js +75 -0
  34. package/dist/index.js +18 -0
  35. package/dist/legacy-session-surface-api.js +8 -0
  36. package/dist/legacy-state-migrations-api.js +2 -0
  37. package/dist/light-runtime-api.js +5 -0
  38. package/dist/login-DymdrKca.js +52 -0
  39. package/dist/login-qr-DIS0Iv-7.js +378 -0
  40. package/dist/login-qr-api.js +2 -0
  41. package/dist/login-qr-runtime.js +16 -0
  42. package/dist/monitor-C4Fhg_lD.js +3349 -0
  43. package/dist/normalize-CWYiGkXw.js +2 -0
  44. package/dist/normalize-target-BPVHNsM5.js +74 -0
  45. package/dist/outbound-adapter-BssVnZdz.js +29 -0
  46. package/dist/outbound-base-Bq1UYEK5.js +119 -0
  47. package/dist/outbound-media-contract-CGss_QHy.js +163 -0
  48. package/dist/outbound-payload-test-api.js +2 -0
  49. package/dist/quoted-message-TENQzyxV.js +108 -0
  50. package/dist/reaction-level-CRVEVAfi.js +16 -0
  51. package/dist/reply-resolver.runtime-BRCgoO4C.js +2 -0
  52. package/dist/resolve-outbound-target-CN-Wo6IY.js +39 -0
  53. package/dist/rolldown-runtime-RkAeH_Qm.js +27 -0
  54. package/dist/runtime-6fZ67abR.js +8 -0
  55. package/dist/runtime-api.js +15 -0
  56. package/dist/runtime-group-policy-Gp1lQ0at.js +11 -0
  57. package/dist/secret-contract-api.js +5 -0
  58. package/dist/security-contract-DqH8JDX6.js +26 -0
  59. package/dist/security-contract-api.js +2 -0
  60. package/dist/send-CIyLFxdZ.js +234 -0
  61. package/dist/session-contract-B0efQf9P.js +27 -0
  62. package/dist/session-errors-CSuKZPub.js +63 -0
  63. package/dist/session.runtime-C1eSE_KK.js +13 -0
  64. package/dist/setup-core-DZKvpzbH.js +46 -0
  65. package/dist/setup-entry.js +23 -0
  66. package/dist/setup-plugin-api.js +2 -0
  67. package/dist/setup-surface-ieFnIW4g.js +344 -0
  68. package/dist/shared-D_wzSVar.js +307 -0
  69. package/dist/state-migrations-DnCdvoYD.js +39 -0
  70. package/dist/targets.js +2 -0
  71. package/dist/test-api.js +3 -0
  72. package/dist/text-runtime-C1zNiZeO.js +116 -0
  73. package/package.json +14 -6
  74. package/action-runtime-api.ts +0 -1
  75. package/action-runtime.runtime.ts +0 -1
  76. package/api.ts +0 -213
  77. package/auth-presence.ts +0 -80
  78. package/channel-config-api.ts +0 -1
  79. package/channel-plugin-api.ts +0 -3
  80. package/config-api.ts +0 -4
  81. package/constants.ts +0 -1
  82. package/contract-api.ts +0 -29
  83. package/directory-contract-api.ts +0 -4
  84. package/doctor-contract-api.ts +0 -8
  85. package/index.test.ts +0 -13
  86. package/index.ts +0 -16
  87. package/legacy-session-surface-api.ts +0 -6
  88. package/legacy-state-migrations-api.ts +0 -1
  89. package/light-runtime-api.ts +0 -12
  90. package/login-qr-api.ts +0 -1
  91. package/login-qr-runtime.ts +0 -23
  92. package/outbound-payload-test-api.ts +0 -1
  93. package/runtime-api.ts +0 -84
  94. package/secret-contract-api.ts +0 -4
  95. package/security-contract-api.ts +0 -4
  96. package/setup-entry.test.ts +0 -21
  97. package/setup-entry.ts +0 -21
  98. package/setup-plugin-api.ts +0 -3
  99. package/src/account-config.ts +0 -77
  100. package/src/account-ids.ts +0 -13
  101. package/src/account-types.ts +0 -5
  102. package/src/accounts.test.ts +0 -182
  103. package/src/accounts.ts +0 -176
  104. package/src/accounts.whatsapp-auth.test.ts +0 -59
  105. package/src/action-runtime-target-auth.ts +0 -27
  106. package/src/action-runtime.test.ts +0 -330
  107. package/src/action-runtime.ts +0 -76
  108. package/src/active-listener.test.ts +0 -65
  109. package/src/active-listener.ts +0 -17
  110. package/src/agent-tools-login.test.ts +0 -81
  111. package/src/agent-tools-login.ts +0 -113
  112. package/src/approval-auth.test.ts +0 -24
  113. package/src/approval-auth.ts +0 -27
  114. package/src/auth-store.runtime.ts +0 -1
  115. package/src/auth-store.test.ts +0 -311
  116. package/src/auth-store.ts +0 -502
  117. package/src/auto-reply/config.runtime.ts +0 -16
  118. package/src/auto-reply/constants.ts +0 -1
  119. package/src/auto-reply/deliver-reply.test.ts +0 -843
  120. package/src/auto-reply/deliver-reply.ts +0 -279
  121. package/src/auto-reply/loggers.ts +0 -6
  122. package/src/auto-reply/mentions.ts +0 -131
  123. package/src/auto-reply/monitor/ack-reaction.test.ts +0 -170
  124. package/src/auto-reply/monitor/ack-reaction.ts +0 -99
  125. package/src/auto-reply/monitor/audio-preflight.runtime.ts +0 -9
  126. package/src/auto-reply/monitor/broadcast.ts +0 -153
  127. package/src/auto-reply/monitor/commands.ts +0 -19
  128. package/src/auto-reply/monitor/echo.ts +0 -64
  129. package/src/auto-reply/monitor/group-activation.runtime.ts +0 -1
  130. package/src/auto-reply/monitor/group-activation.test.ts +0 -189
  131. package/src/auto-reply/monitor/group-activation.ts +0 -73
  132. package/src/auto-reply/monitor/group-gating.audio-preflight.test.ts +0 -103
  133. package/src/auto-reply/monitor/group-gating.runtime.ts +0 -8
  134. package/src/auto-reply/monitor/group-gating.ts +0 -217
  135. package/src/auto-reply/monitor/group-members.test.ts +0 -56
  136. package/src/auto-reply/monitor/group-members.ts +0 -65
  137. package/src/auto-reply/monitor/inbound-context.test.ts +0 -97
  138. package/src/auto-reply/monitor/inbound-context.ts +0 -92
  139. package/src/auto-reply/monitor/inbound-dispatch.runtime.ts +0 -21
  140. package/src/auto-reply/monitor/inbound-dispatch.test.ts +0 -895
  141. package/src/auto-reply/monitor/inbound-dispatch.ts +0 -422
  142. package/src/auto-reply/monitor/last-route.test.ts +0 -37
  143. package/src/auto-reply/monitor/last-route.ts +0 -61
  144. package/src/auto-reply/monitor/message-line.runtime.ts +0 -38
  145. package/src/auto-reply/monitor/message-line.ts +0 -54
  146. package/src/auto-reply/monitor/on-message.audio-preflight.test.ts +0 -354
  147. package/src/auto-reply/monitor/on-message.ts +0 -291
  148. package/src/auto-reply/monitor/peer.ts +0 -17
  149. package/src/auto-reply/monitor/process-message.audio-preflight.test.ts +0 -420
  150. package/src/auto-reply/monitor/process-message.test.ts +0 -355
  151. package/src/auto-reply/monitor/process-message.ts +0 -531
  152. package/src/auto-reply/monitor/runtime-api.ts +0 -30
  153. package/src/auto-reply/monitor-state.test.ts +0 -64
  154. package/src/auto-reply/monitor-state.ts +0 -106
  155. package/src/auto-reply/monitor.ts +0 -600
  156. package/src/auto-reply/reply-resolver.runtime.ts +0 -1
  157. package/src/auto-reply/types.ts +0 -48
  158. package/src/auto-reply/util.ts +0 -63
  159. package/src/auto-reply/web-auto-reply-monitor.test.ts +0 -687
  160. package/src/auto-reply/web-auto-reply-utils.test.ts +0 -392
  161. package/src/auto-reply.broadcast-groups.combined.test.ts +0 -246
  162. package/src/auto-reply.broadcast-groups.test-harness.ts +0 -47
  163. package/src/auto-reply.impl.ts +0 -6
  164. package/src/auto-reply.test-harness.ts +0 -421
  165. package/src/auto-reply.ts +0 -1
  166. package/src/auto-reply.web-auto-reply.compresses-common-formats-jpeg-cap.test.ts +0 -427
  167. package/src/auto-reply.web-auto-reply.connection-and-logging.e2e.test.ts +0 -908
  168. package/src/auto-reply.web-auto-reply.last-route.test.ts +0 -184
  169. package/src/channel-actions.runtime.ts +0 -7
  170. package/src/channel-actions.test.ts +0 -216
  171. package/src/channel-actions.ts +0 -84
  172. package/src/channel-outbound.test.ts +0 -131
  173. package/src/channel-outbound.ts +0 -36
  174. package/src/channel-react-action.runtime.ts +0 -7
  175. package/src/channel-react-action.test.ts +0 -294
  176. package/src/channel-react-action.ts +0 -84
  177. package/src/channel.runtime.ts +0 -117
  178. package/src/channel.setup.test.ts +0 -461
  179. package/src/channel.setup.ts +0 -28
  180. package/src/channel.ts +0 -339
  181. package/src/command-policy.ts +0 -7
  182. package/src/config-accessors.test.ts +0 -34
  183. package/src/config-accessors.ts +0 -22
  184. package/src/config-schema.test.ts +0 -121
  185. package/src/config-schema.ts +0 -6
  186. package/src/config-ui-hints.ts +0 -24
  187. package/src/connection-controller-registry.test.ts +0 -27
  188. package/src/connection-controller-registry.ts +0 -49
  189. package/src/connection-controller.test.ts +0 -282
  190. package/src/connection-controller.ts +0 -679
  191. package/src/creds-files.ts +0 -19
  192. package/src/creds-persistence.ts +0 -102
  193. package/src/directory-config.test.ts +0 -60
  194. package/src/directory-config.ts +0 -40
  195. package/src/directory-contract.test.ts +0 -56
  196. package/src/doctor-contract.ts +0 -11
  197. package/src/doctor.test.ts +0 -74
  198. package/src/doctor.ts +0 -56
  199. package/src/group-intro.ts +0 -15
  200. package/src/group-policy.test.ts +0 -36
  201. package/src/group-policy.ts +0 -40
  202. package/src/group-session-contract.ts +0 -20
  203. package/src/group-session-key.test.ts +0 -53
  204. package/src/group-session-key.ts +0 -41
  205. package/src/heartbeat.ts +0 -34
  206. package/src/identity.ts +0 -164
  207. package/src/inbound/access-control.test-harness.ts +0 -37
  208. package/src/inbound/access-control.test.ts +0 -375
  209. package/src/inbound/access-control.ts +0 -237
  210. package/src/inbound/dedupe.ts +0 -132
  211. package/src/inbound/extract.test.ts +0 -282
  212. package/src/inbound/extract.ts +0 -488
  213. package/src/inbound/lifecycle.ts +0 -39
  214. package/src/inbound/media.node.test.ts +0 -83
  215. package/src/inbound/media.ts +0 -102
  216. package/src/inbound/monitor.ts +0 -890
  217. package/src/inbound/runtime-api.ts +0 -7
  218. package/src/inbound/save-media.runtime.ts +0 -1
  219. package/src/inbound/send-api.test.ts +0 -319
  220. package/src/inbound/send-api.ts +0 -145
  221. package/src/inbound/send-result.ts +0 -61
  222. package/src/inbound/types.ts +0 -106
  223. package/src/inbound-context.contract.test.ts +0 -32
  224. package/src/inbound-policy.ts +0 -231
  225. package/src/inbound.media.test.ts +0 -320
  226. package/src/inbound.test.ts +0 -290
  227. package/src/inbound.ts +0 -9
  228. package/src/login-qr.test.ts +0 -483
  229. package/src/login-qr.ts +0 -542
  230. package/src/login.coverage.test.ts +0 -133
  231. package/src/login.test.ts +0 -89
  232. package/src/login.ts +0 -70
  233. package/src/logout.test.ts +0 -162
  234. package/src/media.test.ts +0 -480
  235. package/src/media.ts +0 -10
  236. package/src/monitor-inbox.allows-messages-from-senders-allowfrom-list.test-support.ts +0 -400
  237. package/src/monitor-inbox.append-upsert.test-support.ts +0 -133
  238. package/src/monitor-inbox.behavior.test.ts +0 -5
  239. package/src/monitor-inbox.blocks-messages-from-unauthorized-senders-not-allowfrom.test-support.ts +0 -362
  240. package/src/monitor-inbox.captures-media-path-image-messages.test-support.ts +0 -306
  241. package/src/monitor-inbox.streams-inbound-messages.test-support.ts +0 -729
  242. package/src/monitor-inbox.test-harness.ts +0 -304
  243. package/src/normalize-target.ts +0 -123
  244. package/src/normalize.ts +0 -7
  245. package/src/outbound-adapter.poll.test.ts +0 -62
  246. package/src/outbound-adapter.sendpayload.test.ts +0 -195
  247. package/src/outbound-adapter.ts +0 -36
  248. package/src/outbound-base.test.ts +0 -560
  249. package/src/outbound-base.ts +0 -244
  250. package/src/outbound-media-contract.ts +0 -278
  251. package/src/outbound-media.runtime.ts +0 -36
  252. package/src/outbound-payload.contract.test.ts +0 -61
  253. package/src/outbound-send-deps.ts +0 -1
  254. package/src/outbound-test-support.ts +0 -16
  255. package/src/pairing-security.test-harness.ts +0 -59
  256. package/src/qr-image.ts +0 -1
  257. package/src/qr-terminal.ts +0 -1
  258. package/src/quoted-message.test.ts +0 -83
  259. package/src/quoted-message.ts +0 -184
  260. package/src/reaction-level.test.ts +0 -94
  261. package/src/reaction-level.ts +0 -21
  262. package/src/reconnect.test.ts +0 -51
  263. package/src/reconnect.ts +0 -55
  264. package/src/resolve-outbound-target.test.ts +0 -288
  265. package/src/resolve-outbound-target.ts +0 -58
  266. package/src/resolve-target.test.ts +0 -120
  267. package/src/runtime-api.ts +0 -59
  268. package/src/runtime-group-policy.ts +0 -16
  269. package/src/runtime.ts +0 -9
  270. package/src/security-contract.ts +0 -47
  271. package/src/security-fix.ts +0 -71
  272. package/src/send.test.ts +0 -534
  273. package/src/send.ts +0 -292
  274. package/src/session-contract.test.ts +0 -31
  275. package/src/session-contract.ts +0 -43
  276. package/src/session-errors.ts +0 -125
  277. package/src/session-route.test.ts +0 -41
  278. package/src/session-route.ts +0 -32
  279. package/src/session.runtime.ts +0 -8
  280. package/src/session.test.ts +0 -559
  281. package/src/session.ts +0 -330
  282. package/src/setup-core.ts +0 -52
  283. package/src/setup-finalize.ts +0 -446
  284. package/src/setup-surface.test.ts +0 -367
  285. package/src/setup-surface.ts +0 -69
  286. package/src/setup-test-helpers.ts +0 -216
  287. package/src/shared.ts +0 -291
  288. package/src/socket-timing.test.ts +0 -49
  289. package/src/socket-timing.ts +0 -38
  290. package/src/state-migrations.ts +0 -54
  291. package/src/status-issues.test.ts +0 -137
  292. package/src/status-issues.ts +0 -185
  293. package/src/system-prompt.test.ts +0 -199
  294. package/src/system-prompt.ts +0 -31
  295. package/src/targets-runtime.ts +0 -180
  296. package/src/test-helpers.ts +0 -690
  297. package/src/text-runtime.test.ts +0 -162
  298. package/src/text-runtime.ts +0 -11
  299. package/src/vcard.ts +0 -84
  300. package/targets.ts +0 -5
  301. package/test-api.ts +0 -2
  302. package/tsconfig.json +0 -16
@@ -1,446 +0,0 @@
1
- import path from "node:path";
2
- import {
3
- DEFAULT_ACCOUNT_ID,
4
- normalizeAllowFromEntries,
5
- normalizeE164,
6
- pathExists,
7
- splitSetupEntries,
8
- type DmPolicy,
9
- type OpenClawConfig,
10
- } from "openclaw/plugin-sdk/setup";
11
- import type { ChannelSetupWizard } from "openclaw/plugin-sdk/setup";
12
- import { formatCliCommand, formatDocsLink } from "openclaw/plugin-sdk/setup-tools";
13
- import {
14
- resolveDefaultWhatsAppAccountId,
15
- resolveWhatsAppAccount,
16
- resolveWhatsAppAuthDir,
17
- } from "./accounts.js";
18
- import { whatsappSetupAdapter } from "./setup-core.js";
19
-
20
- type SetupPrompter = Parameters<NonNullable<ChannelSetupWizard["finalize"]>>[0]["prompter"];
21
- type SetupRuntime = Parameters<NonNullable<ChannelSetupWizard["finalize"]>>[0]["runtime"];
22
- type WhatsAppConfig = NonNullable<NonNullable<OpenClawConfig["channels"]>["whatsapp"]>;
23
- type WhatsAppAccountConfig = NonNullable<NonNullable<WhatsAppConfig["accounts"]>[string]>;
24
-
25
- function trimPromptText(value: string | null | undefined): string {
26
- return value?.trim() ?? "";
27
- }
28
-
29
- function isDefaultWhatsAppAccountKey(accountId: string): boolean {
30
- return accountId.trim().toLowerCase() === DEFAULT_ACCOUNT_ID;
31
- }
32
-
33
- function shouldWriteDefaultWhatsAppAccountConfigAtAccountScope(cfg: OpenClawConfig): boolean {
34
- const accounts = cfg.channels?.whatsapp?.accounts;
35
- if (!accounts) {
36
- return false;
37
- }
38
- if (accounts.default) {
39
- return true;
40
- }
41
- return Object.keys(accounts).some((accountId) => !isDefaultWhatsAppAccountKey(accountId));
42
- }
43
-
44
- function resolveDefaultWhatsAppAccountWriteKey(cfg: OpenClawConfig): string {
45
- const accounts = cfg.channels?.whatsapp?.accounts;
46
- if (!accounts) {
47
- return DEFAULT_ACCOUNT_ID;
48
- }
49
- const match = Object.keys(accounts).find((accountId) => isDefaultWhatsAppAccountKey(accountId));
50
- return match ?? DEFAULT_ACCOUNT_ID;
51
- }
52
-
53
- function resolveWhatsAppConfigPathPrefix(cfg: OpenClawConfig, accountId: string): string {
54
- if (
55
- accountId === DEFAULT_ACCOUNT_ID &&
56
- shouldWriteDefaultWhatsAppAccountConfigAtAccountScope(cfg)
57
- ) {
58
- return `channels.whatsapp.accounts.${resolveDefaultWhatsAppAccountWriteKey(cfg)}`;
59
- }
60
- return accountId === DEFAULT_ACCOUNT_ID
61
- ? "channels.whatsapp"
62
- : `channels.whatsapp.accounts.${accountId}`;
63
- }
64
-
65
- function mergeWhatsAppConfig(
66
- cfg: OpenClawConfig,
67
- accountId: string,
68
- patch: Partial<WhatsAppAccountConfig>,
69
- options?: { unsetOnUndefined?: string[] },
70
- ): OpenClawConfig {
71
- const channelConfig: WhatsAppConfig = { ...cfg.channels?.whatsapp };
72
- const mutableChannelConfig = channelConfig as Record<string, unknown>;
73
- const targetPathPrefix = resolveWhatsAppConfigPathPrefix(cfg, accountId);
74
- if (targetPathPrefix === "channels.whatsapp") {
75
- for (const [key, value] of Object.entries(patch)) {
76
- if (value === undefined) {
77
- if (options?.unsetOnUndefined?.includes(key)) {
78
- delete mutableChannelConfig[key];
79
- }
80
- continue;
81
- }
82
- mutableChannelConfig[key] = value;
83
- }
84
- return {
85
- ...cfg,
86
- channels: {
87
- ...cfg.channels,
88
- whatsapp: channelConfig,
89
- },
90
- };
91
- }
92
- const accounts = {
93
- ...(channelConfig.accounts as Record<string, WhatsAppAccountConfig> | undefined),
94
- };
95
- const targetAccountId =
96
- accountId === DEFAULT_ACCOUNT_ID ? resolveDefaultWhatsAppAccountWriteKey(cfg) : accountId;
97
- const lowerDefaultAccount =
98
- accountId === DEFAULT_ACCOUNT_ID && targetAccountId !== DEFAULT_ACCOUNT_ID
99
- ? accounts[DEFAULT_ACCOUNT_ID]
100
- : undefined;
101
- const nextAccount: WhatsAppAccountConfig = {
102
- ...accounts[targetAccountId],
103
- ...lowerDefaultAccount,
104
- };
105
- const mutableNextAccount = nextAccount as Record<string, unknown>;
106
- for (const [key, value] of Object.entries(patch)) {
107
- if (value === undefined) {
108
- if (options?.unsetOnUndefined?.includes(key)) {
109
- delete mutableNextAccount[key];
110
- }
111
- continue;
112
- }
113
- mutableNextAccount[key] = value;
114
- }
115
- accounts[targetAccountId] = nextAccount;
116
- if (lowerDefaultAccount) {
117
- delete accounts[DEFAULT_ACCOUNT_ID];
118
- }
119
- return {
120
- ...cfg,
121
- channels: {
122
- ...cfg.channels,
123
- whatsapp: {
124
- ...channelConfig,
125
- accounts,
126
- },
127
- },
128
- };
129
- }
130
-
131
- function setWhatsAppDmPolicy(
132
- cfg: OpenClawConfig,
133
- accountId: string,
134
- dmPolicy: DmPolicy,
135
- ): OpenClawConfig {
136
- return mergeWhatsAppConfig(cfg, accountId, { dmPolicy });
137
- }
138
-
139
- function setWhatsAppAllowFrom(
140
- cfg: OpenClawConfig,
141
- accountId: string,
142
- allowFrom?: string[],
143
- ): OpenClawConfig {
144
- return mergeWhatsAppConfig(cfg, accountId, { allowFrom }, { unsetOnUndefined: ["allowFrom"] });
145
- }
146
-
147
- function setWhatsAppSelfChatMode(
148
- cfg: OpenClawConfig,
149
- accountId: string,
150
- selfChatMode: boolean,
151
- ): OpenClawConfig {
152
- return mergeWhatsAppConfig(cfg, accountId, { selfChatMode });
153
- }
154
-
155
- async function detectWhatsAppLinked(cfg: OpenClawConfig, accountId: string): Promise<boolean> {
156
- const { authDir } = resolveWhatsAppAuthDir({ cfg, accountId });
157
- const credsPath = path.join(authDir, "creds.json");
158
- return await pathExists(credsPath);
159
- }
160
-
161
- async function promptWhatsAppOwnerAllowFrom(params: {
162
- existingAllowFrom: string[];
163
- prompter: SetupPrompter;
164
- }): Promise<{ normalized: string; allowFrom: string[] }> {
165
- const { prompter, existingAllowFrom } = params;
166
-
167
- await prompter.note(
168
- "We need the sender/owner number so OpenClaw can allowlist you.",
169
- "WhatsApp number",
170
- );
171
- const entry = await prompter.text({
172
- message: "Your personal WhatsApp number (the phone you will message from)",
173
- placeholder: "+15555550123",
174
- initialValue: existingAllowFrom[0],
175
- validate: (value) => {
176
- const raw = trimPromptText(value);
177
- if (!raw) {
178
- return "Required";
179
- }
180
- const normalized = normalizeE164(raw);
181
- if (!normalized) {
182
- return `Invalid number: ${raw}`;
183
- }
184
- return undefined;
185
- },
186
- });
187
-
188
- const normalized = normalizeE164(trimPromptText(entry));
189
- if (!normalized) {
190
- throw new Error("Invalid WhatsApp owner number (expected E.164 after validation).");
191
- }
192
- const allowFrom = normalizeAllowFromEntries(
193
- [...existingAllowFrom.filter((item) => item !== "*"), normalized],
194
- normalizeE164,
195
- );
196
- return { normalized, allowFrom };
197
- }
198
-
199
- async function applyWhatsAppOwnerAllowlist(params: {
200
- cfg: OpenClawConfig;
201
- accountId: string;
202
- existingAllowFrom: string[];
203
- messageLines: string[];
204
- prompter: SetupPrompter;
205
- title: string;
206
- }): Promise<OpenClawConfig> {
207
- const { normalized, allowFrom } = await promptWhatsAppOwnerAllowFrom({
208
- prompter: params.prompter,
209
- existingAllowFrom: params.existingAllowFrom,
210
- });
211
- let next = setWhatsAppSelfChatMode(params.cfg, params.accountId, true);
212
- next = setWhatsAppDmPolicy(next, params.accountId, "allowlist");
213
- next = setWhatsAppAllowFrom(next, params.accountId, allowFrom);
214
- await params.prompter.note(
215
- [...params.messageLines, `- allowFrom includes ${normalized}`].join("\n"),
216
- params.title,
217
- );
218
- return next;
219
- }
220
-
221
- function parseWhatsAppAllowFromEntries(raw: string): { entries: string[]; invalidEntry?: string } {
222
- const parts = splitSetupEntries(raw);
223
- if (parts.length === 0) {
224
- return { entries: [] };
225
- }
226
- const entries: string[] = [];
227
- for (const part of parts) {
228
- if (part === "*") {
229
- entries.push("*");
230
- continue;
231
- }
232
- const normalized = normalizeE164(part);
233
- if (!normalized) {
234
- return { entries: [], invalidEntry: part };
235
- }
236
- entries.push(normalized);
237
- }
238
- return { entries: normalizeAllowFromEntries(entries, normalizeE164) };
239
- }
240
-
241
- async function promptWhatsAppDmAccess(params: {
242
- cfg: OpenClawConfig;
243
- accountId: string;
244
- forceAllowFrom: boolean;
245
- prompter: SetupPrompter;
246
- }): Promise<OpenClawConfig> {
247
- const accountId = params.accountId.trim() || DEFAULT_ACCOUNT_ID;
248
- const account = resolveWhatsAppAccount({ cfg: params.cfg, accountId });
249
- const existingPolicy = account.dmPolicy ?? "pairing";
250
- const existingAllowFrom = account.allowFrom ?? [];
251
- const existingLabel = existingAllowFrom.length > 0 ? existingAllowFrom.join(", ") : "unset";
252
- const configPathPrefix = resolveWhatsAppConfigPathPrefix(params.cfg, accountId);
253
- const policyKey = `${configPathPrefix}.dmPolicy`;
254
- const allowFromKey = `${configPathPrefix}.allowFrom`;
255
-
256
- if (params.forceAllowFrom) {
257
- return await applyWhatsAppOwnerAllowlist({
258
- cfg: params.cfg,
259
- accountId,
260
- prompter: params.prompter,
261
- existingAllowFrom,
262
- title: "WhatsApp allowlist",
263
- messageLines: ["Allowlist mode enabled."],
264
- });
265
- }
266
-
267
- await params.prompter.note(
268
- [
269
- `WhatsApp direct chats are gated by \`${policyKey}\` + \`${allowFromKey}\`.`,
270
- "- pairing (default): unknown senders get a pairing code; owner approves",
271
- "- allowlist: unknown senders are blocked",
272
- '- open: public inbound DMs (requires allowFrom to include "*")',
273
- "- disabled: ignore WhatsApp DMs",
274
- "",
275
- `Current: dmPolicy=${existingPolicy}, allowFrom=${existingLabel}`,
276
- `Docs: ${formatDocsLink("/whatsapp", "whatsapp")}`,
277
- ].join("\n"),
278
- "WhatsApp DM access",
279
- );
280
-
281
- const phoneMode = await params.prompter.select({
282
- message: "WhatsApp phone setup",
283
- options: [
284
- { value: "personal", label: "This is my personal phone number" },
285
- { value: "separate", label: "Separate phone just for OpenClaw" },
286
- ],
287
- });
288
-
289
- if (phoneMode === "personal") {
290
- return await applyWhatsAppOwnerAllowlist({
291
- cfg: params.cfg,
292
- accountId,
293
- prompter: params.prompter,
294
- existingAllowFrom,
295
- title: "WhatsApp personal phone",
296
- messageLines: [
297
- "Personal phone mode enabled.",
298
- "- dmPolicy set to allowlist (pairing skipped)",
299
- ],
300
- });
301
- }
302
-
303
- const policy = (await params.prompter.select({
304
- message: "WhatsApp DM policy",
305
- options: [
306
- { value: "pairing", label: "Pairing (recommended)" },
307
- { value: "allowlist", label: "Allowlist only (block unknown senders)" },
308
- { value: "open", label: "Open (public inbound DMs)" },
309
- { value: "disabled", label: "Disabled (ignore WhatsApp DMs)" },
310
- ],
311
- })) as DmPolicy;
312
-
313
- let next = setWhatsAppSelfChatMode(params.cfg, accountId, false);
314
- next = setWhatsAppDmPolicy(next, accountId, policy);
315
- if (policy === "open") {
316
- const allowFrom = normalizeAllowFromEntries(["*", ...existingAllowFrom], normalizeE164);
317
- next = setWhatsAppAllowFrom(next, accountId, allowFrom.length > 0 ? allowFrom : ["*"]);
318
- return next;
319
- }
320
- if (policy === "disabled") {
321
- return next;
322
- }
323
-
324
- const allowOptions =
325
- existingAllowFrom.length > 0
326
- ? ([
327
- { value: "keep", label: "Keep current allowFrom" },
328
- {
329
- value: "unset",
330
- label: "Unset allowFrom (use pairing approvals only)",
331
- },
332
- { value: "list", label: "Set allowFrom to specific numbers" },
333
- ] as const)
334
- : ([
335
- { value: "unset", label: "Unset allowFrom (default)" },
336
- { value: "list", label: "Set allowFrom to specific numbers" },
337
- ] as const);
338
-
339
- const mode = await params.prompter.select({
340
- message: "WhatsApp allowFrom (optional pre-allowlist)",
341
- options: allowOptions.map((opt) => ({
342
- value: opt.value,
343
- label: opt.label,
344
- })),
345
- });
346
-
347
- if (mode === "keep") {
348
- return next;
349
- }
350
- if (mode === "unset") {
351
- return setWhatsAppAllowFrom(next, accountId, undefined);
352
- }
353
-
354
- const allowRaw = await params.prompter.text({
355
- message: "Allowed sender numbers (comma-separated, E.164)",
356
- placeholder: "+15555550123, +447700900123",
357
- validate: (value) => {
358
- const raw = trimPromptText(value);
359
- if (!raw) {
360
- return "Required";
361
- }
362
- const parsed = parseWhatsAppAllowFromEntries(raw);
363
- if (parsed.entries.length === 0 && !parsed.invalidEntry) {
364
- return "Required";
365
- }
366
- if (parsed.invalidEntry) {
367
- return `Invalid number: ${parsed.invalidEntry}`;
368
- }
369
- return undefined;
370
- },
371
- });
372
-
373
- const parsed = parseWhatsAppAllowFromEntries(trimPromptText(allowRaw));
374
- if (parsed.invalidEntry) {
375
- throw new Error(`Invalid number: ${parsed.invalidEntry}`);
376
- }
377
- if (parsed.entries.length === 0) {
378
- throw new Error("Invalid WhatsApp allowFrom list (expected at least one E.164 number).");
379
- }
380
- return setWhatsAppAllowFrom(next, accountId, parsed.entries);
381
- }
382
-
383
- export async function finalizeWhatsAppSetup(params: {
384
- cfg: OpenClawConfig;
385
- accountId: string;
386
- forceAllowFrom: boolean;
387
- prompter: SetupPrompter;
388
- runtime: SetupRuntime;
389
- }) {
390
- const accountId = params.accountId.trim() || resolveDefaultWhatsAppAccountId(params.cfg);
391
- let next =
392
- accountId === DEFAULT_ACCOUNT_ID
393
- ? params.cfg
394
- : whatsappSetupAdapter.applyAccountConfig({
395
- cfg: params.cfg,
396
- accountId,
397
- input: {},
398
- });
399
-
400
- const linked = await detectWhatsAppLinked(next, accountId);
401
- const { authDir } = resolveWhatsAppAuthDir({
402
- cfg: next,
403
- accountId,
404
- });
405
-
406
- if (!linked) {
407
- await params.prompter.note(
408
- [
409
- "Scan the QR with WhatsApp on your phone.",
410
- `Credentials are stored under ${authDir}/ for future runs.`,
411
- `Docs: ${formatDocsLink("/whatsapp", "whatsapp")}`,
412
- ].join("\n"),
413
- "WhatsApp linking",
414
- );
415
- }
416
-
417
- const wantsLink = await params.prompter.confirm({
418
- message: linked ? "WhatsApp already linked. Re-link now?" : "Link WhatsApp now (QR)?",
419
- initialValue: !linked,
420
- });
421
- if (wantsLink) {
422
- try {
423
- const { loginWeb } = await import("./login.js");
424
- await loginWeb(false, undefined, params.runtime, accountId);
425
- } catch (error) {
426
- params.runtime.error(`WhatsApp login failed: ${String(error)}`);
427
- await params.prompter.note(
428
- `Docs: ${formatDocsLink("/whatsapp", "whatsapp")}`,
429
- "WhatsApp help",
430
- );
431
- }
432
- } else if (!linked) {
433
- await params.prompter.note(
434
- `Run \`${formatCliCommand("openclaw channels login")}\` later to link WhatsApp.`,
435
- "WhatsApp",
436
- );
437
- }
438
-
439
- next = await promptWhatsAppDmAccess({
440
- cfg: next,
441
- accountId,
442
- forceAllowFrom: params.forceAllowFrom,
443
- prompter: params.prompter,
444
- });
445
- return { cfg: next };
446
- }