miaw-core 1.10.0 → 1.12.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 (125) hide show
  1. package/.env.example +21 -2
  2. package/CHANGELOG.md +293 -0
  3. package/README.md +105 -27
  4. package/bin/miaw-cli.mjs +31 -0
  5. package/dist/cli/commands/call.d.ts +16 -0
  6. package/dist/cli/commands/call.d.ts.map +1 -0
  7. package/dist/cli/commands/call.js +29 -0
  8. package/dist/cli/commands/call.js.map +1 -0
  9. package/dist/cli/commands/catalog.d.ts +0 -5
  10. package/dist/cli/commands/catalog.d.ts.map +1 -1
  11. package/dist/cli/commands/catalog.js +42 -0
  12. package/dist/cli/commands/catalog.js.map +1 -1
  13. package/dist/cli/commands/chat.d.ts +14 -0
  14. package/dist/cli/commands/chat.d.ts.map +1 -1
  15. package/dist/cli/commands/chat.js +48 -0
  16. package/dist/cli/commands/chat.js.map +1 -1
  17. package/dist/cli/commands/commands-index.d.ts +7 -5
  18. package/dist/cli/commands/commands-index.d.ts.map +1 -1
  19. package/dist/cli/commands/commands-index.js +11 -5
  20. package/dist/cli/commands/commands-index.js.map +1 -1
  21. package/dist/cli/commands/community.d.ts +28 -0
  22. package/dist/cli/commands/community.d.ts.map +1 -1
  23. package/dist/cli/commands/community.js +83 -0
  24. package/dist/cli/commands/community.js.map +1 -1
  25. package/dist/cli/commands/contact.d.ts.map +1 -1
  26. package/dist/cli/commands/contact.js +6 -2
  27. package/dist/cli/commands/contact.js.map +1 -1
  28. package/dist/cli/commands/get.d.ts.map +1 -1
  29. package/dist/cli/commands/get.js +12 -2
  30. package/dist/cli/commands/get.js.map +1 -1
  31. package/dist/cli/commands/group.d.ts +49 -0
  32. package/dist/cli/commands/group.d.ts.map +1 -1
  33. package/dist/cli/commands/group.js +160 -3
  34. package/dist/cli/commands/group.js.map +1 -1
  35. package/dist/cli/commands/index.d.ts +11 -7
  36. package/dist/cli/commands/index.d.ts.map +1 -1
  37. package/dist/cli/commands/index.js +465 -53
  38. package/dist/cli/commands/index.js.map +1 -1
  39. package/dist/cli/commands/instance.d.ts +46 -7
  40. package/dist/cli/commands/instance.d.ts.map +1 -1
  41. package/dist/cli/commands/instance.js +224 -34
  42. package/dist/cli/commands/instance.js.map +1 -1
  43. package/dist/cli/commands/misc.d.ts.map +1 -1
  44. package/dist/cli/commands/misc.js +5 -1
  45. package/dist/cli/commands/misc.js.map +1 -1
  46. package/dist/cli/commands/privacy.d.ts +83 -0
  47. package/dist/cli/commands/privacy.d.ts.map +1 -0
  48. package/dist/cli/commands/privacy.js +167 -0
  49. package/dist/cli/commands/privacy.js.map +1 -0
  50. package/dist/cli/commands/send.d.ts +12 -0
  51. package/dist/cli/commands/send.d.ts.map +1 -1
  52. package/dist/cli/commands/send.js +44 -0
  53. package/dist/cli/commands/send.js.map +1 -1
  54. package/dist/cli/main.d.ts +22 -0
  55. package/dist/cli/main.d.ts.map +1 -0
  56. package/dist/cli/main.js +243 -0
  57. package/dist/cli/main.js.map +1 -0
  58. package/dist/cli/repl.d.ts +8 -10
  59. package/dist/cli/repl.d.ts.map +1 -1
  60. package/dist/cli/repl.js +464 -84
  61. package/dist/cli/repl.js.map +1 -1
  62. package/dist/cli/utils/client-cache.d.ts +8 -0
  63. package/dist/cli/utils/client-cache.d.ts.map +1 -1
  64. package/dist/cli/utils/client-cache.js +66 -7
  65. package/dist/cli/utils/client-cache.js.map +1 -1
  66. package/dist/cli/utils/instance-config.d.ts +89 -0
  67. package/dist/cli/utils/instance-config.d.ts.map +1 -0
  68. package/dist/cli/utils/instance-config.js +224 -0
  69. package/dist/cli/utils/instance-config.js.map +1 -0
  70. package/dist/cli/utils/instance-registry.d.ts +7 -0
  71. package/dist/cli/utils/instance-registry.d.ts.map +1 -1
  72. package/dist/cli/utils/instance-registry.js +2 -0
  73. package/dist/cli/utils/instance-registry.js.map +1 -1
  74. package/dist/cli/utils/parse-args.d.ts +41 -0
  75. package/dist/cli/utils/parse-args.d.ts.map +1 -0
  76. package/dist/cli/utils/parse-args.js +84 -0
  77. package/dist/cli/utils/parse-args.js.map +1 -0
  78. package/dist/cli/utils/proxy-resolver.d.ts +91 -0
  79. package/dist/cli/utils/proxy-resolver.d.ts.map +1 -0
  80. package/dist/cli/utils/proxy-resolver.js +180 -0
  81. package/dist/cli/utils/proxy-resolver.js.map +1 -0
  82. package/dist/cli/utils/session.d.ts +24 -1
  83. package/dist/cli/utils/session.d.ts.map +1 -1
  84. package/dist/cli/utils/session.js +5 -0
  85. package/dist/cli/utils/session.js.map +1 -1
  86. package/dist/client/MiawClient.d.ts +336 -9
  87. package/dist/client/MiawClient.d.ts.map +1 -1
  88. package/dist/client/MiawClient.js +886 -23
  89. package/dist/client/MiawClient.js.map +1 -1
  90. package/dist/index.d.ts +5 -2
  91. package/dist/index.d.ts.map +1 -1
  92. package/dist/index.js +4 -0
  93. package/dist/index.js.map +1 -1
  94. package/dist/types/index.d.ts +234 -2
  95. package/dist/types/index.d.ts.map +1 -1
  96. package/dist/types/index.js +22 -0
  97. package/dist/types/index.js.map +1 -1
  98. package/dist/utils/browser-presets.d.ts +51 -0
  99. package/dist/utils/browser-presets.d.ts.map +1 -0
  100. package/dist/utils/browser-presets.js +53 -0
  101. package/dist/utils/browser-presets.js.map +1 -0
  102. package/dist/utils/proxy-rotator.d.ts +7 -1
  103. package/dist/utils/proxy-rotator.d.ts.map +1 -1
  104. package/dist/utils/proxy-rotator.js +0 -0
  105. package/dist/utils/proxy-rotator.js.map +1 -1
  106. package/docs/API_STABILITY_REVIEW.md +10 -0
  107. package/docs/BAILEYS_MIGRATION_v7.md +10 -1
  108. package/docs/BAILEYS_VS_MIAW_COMPARISON.md +66 -47
  109. package/docs/CLI-ANALYSIS.md +10 -0
  110. package/docs/CLI.md +223 -3
  111. package/docs/CLI_INTEGRATION_TEST_PLAN.md +58 -24
  112. package/docs/CODE_REVIEW_REPORT.md +10 -0
  113. package/docs/DEFERRED_FEATURES.md +29 -41
  114. package/docs/DEPLOYMENT_INSTANCE_PROXY.md +168 -0
  115. package/docs/DEPLOYMENT_PROXY.md +6 -0
  116. package/docs/DEPLOYMENT_V1.12.0.md +162 -0
  117. package/docs/FOLLOW_UPS.md +366 -0
  118. package/docs/MIGRATION.md +147 -2
  119. package/docs/PROXY.md +112 -23
  120. package/docs/TEST_COVERAGE_ANALYSIS.md +29 -5
  121. package/docs/USAGE.md +320 -3
  122. package/eslint.config.js +12 -0
  123. package/package.json +8 -5
  124. package/sessions-cli/.cli_history +2 -0
  125. package/bin/miaw-cli.ts +0 -233
package/.env.example CHANGED
@@ -19,6 +19,18 @@ MIAW_INSTANCE_ID=default
19
19
  MIAW_SESSION_PATH=./sessions-cli
20
20
 
21
21
  # ─── Proxy (all optional; added in v1.10.0) ──────────────────────────────────
22
+ #
23
+ # Precedence, highest first (v1.11.0):
24
+ # 1. MIAW_PROXY / --proxy always wins; warns when it shadows a pin
25
+ # 2. a pin in <MIAW_SESSION_PATH>/instances.json, per instance
26
+ # 3. MIAW_PROXY_FILE + strategy hashed on the TARGET instanceId
27
+ # 4. direct
28
+ #
29
+ # The pin store is written by `miaw-cli instance set-proxy <id> ...`, not by
30
+ # env vars - it is per-instance state, so it belongs with the sessions rather
31
+ # than with process config. It holds credentials when a URL is pinned, so it is
32
+ # written 0600 and must live on the same persistent, secret-grade volume as the
33
+ # session directories. See docs/DEPLOYMENT_INSTANCE_PROXY.md.
22
34
 
23
35
  # A single proxy for this instance. (flag: --proxy)
24
36
  # Schemes: http, https, socks4, socks4a, socks5, socks5h
@@ -33,8 +45,15 @@ MIAW_SESSION_PATH=./sessions-cli
33
45
  # MIAW_PROXY=socks5h://user:password@proxy.example.com:1080
34
46
 
35
47
  # A proxy list file instead of a single proxy — TXT (one per line) or JSON
36
- # array. One proxy is selected per process using MIAW_PROXY_STRATEGY.
37
- # Ignored when MIAW_PROXY is also set. (flag: --proxy-file)
48
+ # array. A proxy is selected per instance using MIAW_PROXY_STRATEGY, keyed on
49
+ # the instance the command targets (v1.11.0; before that, once per process
50
+ # against the startup instance).
51
+ # Ignored when MIAW_PROXY is also set, and outranked by a per-instance pin.
52
+ # (flag: --proxy-file)
53
+ #
54
+ # Also required when an instance is pinned by label (`instance set-proxy <id>
55
+ # --label eu`): the label names an entry in THIS file. A label pin that cannot
56
+ # be resolved refuses to connect rather than silently going direct.
38
57
  #
39
58
  # See examples/proxies.example.txt for the format.
40
59
  #
package/CHANGELOG.md CHANGED
@@ -5,6 +5,299 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [1.12.1] - 2026-09-02
9
+
10
+ ### Fixed
11
+
12
+ Everything here was found by running the CLI integration suite against a
13
+ **live** WhatsApp connection for the first time. All four predate v1.12.0;
14
+ none is an rc14 regression. The suite goes from 15 failures to 0.
15
+
16
+ - **`--json` emitted unparseable output on seven commands.** `check`,
17
+ `contact info`, `contact business`, `group info`, `group participants`,
18
+ `group invite info` and `load messages` printed a progress line to stdout
19
+ even under `--json`, so a consumer got
20
+ `SyntaxError: Unexpected token '🔍'`. `--json` exists only for machines, and
21
+ it was broken for every one of these — invisible interactively, which is why
22
+ it survived. `load messages` was worse: on failure it printed a human error
23
+ instead of JSON, so `--json` never produced parseable output there at all.
24
+ It now emits the result object on both paths.
25
+ - **Catalog commands took 60s to fail on a personal account.** `getCatalog()`
26
+ and `getCollections()` are Business APIs; asked about a personal account's
27
+ own catalog, WhatsApp never answers and the call sat until Baileys' 60s
28
+ default query timeout. They now check the account type first and fail
29
+ immediately with a message that names the fix (`--phone` to read another
30
+ business's catalog). Only the own-account case is guarded — `--phone` may
31
+ legitimately target a business from a personal account. **The CLI suite
32
+ dropped from 745s to 189s as a result.**
33
+ - **`profile status set` with no text always failed.** The router passed an
34
+ empty string through with the comment "status can be empty to clear it";
35
+ WhatsApp rejects an empty about-text, so the documented behaviour never
36
+ worked. It is now refused up front with an explanation.
37
+ - **`isBlocked()` reported a blocked contact as not blocked.** See below —
38
+ fixed in the same live session.
39
+
40
+ ### Changed
41
+
42
+ - `load messages` tests no longer assert success unconditionally. They could
43
+ not distinguish "this chat has no older history" (expected) from "history
44
+ loading is broken" (a defect), so they now accept either but require the
45
+ no-history case to say so.
46
+
47
+ ## [1.12.0] - 2026-09-02
48
+
49
+ **Baileys 7.0.0-rc14, and the deferred feature backlog cleared.** All additive;
50
+ no breaking changes.
51
+
52
+ ### Changed
53
+
54
+ - **Operator notes:** [docs/DEPLOYMENT_V1.12.0.md](docs/DEPLOYMENT_V1.12.0.md) —
55
+ no env, migration or state-format changes; rollback does not require re-pairing.
56
+ - **Upgraded `@whiskeysockets/baileys` from `7.0.0-rc13` to `7.0.0-rc14`** (pinned
57
+ exactly, as before). Four substantive upstream commits:
58
+ - WhatsApp Web version `2.3000.1035194821` → `2.3000.1043857760`. Low impact —
59
+ `resolveWAVersion()` already prefers the live version from `web.whatsapp.com`,
60
+ so the bundled constant is only the third-tier fallback — but a fresher
61
+ fallback is still worth having.
62
+ - **`profilePictureUrl` fix.** The privacy `tctoken` now nests under the
63
+ `picture` query node and carries a `t` attribute, matching WA Web. This
64
+ affects `getContactInfo()`, `getProfilePicture()` and `getChatInfo()` when
65
+ the target has profile-picture privacy enabled.
66
+ - New `Browsers.android()` identity (see below).
67
+ - An internal `Long` type import fix.
68
+
69
+ No public API changed; miaw-core's whole Baileys surface — including the
70
+ `updateBussinesProfile` typo, `signalRepository.lidMapping`, the hand-built
71
+ `remove-companion-device` node, `messaging-history.set` `lidPnMappings`, the
72
+ `resyncAppState` collection names, and the dispatcher passed into
73
+ `downloadMediaMessage` — is byte-identical between the two releases.
74
+
75
+ ### Added
76
+
77
+ - **`BrowserPresets`** — `macOS`, `windows`, `ubuntu` and `android` browser
78
+ identities for the `browser` option, which was previously undocumented outside
79
+ its JSDoc. The default is unchanged (`macOS("Chrome")`).
80
+ - **Android connection identity.** `BrowserPresets.android()` negotiates as an
81
+ Android client rather than a web one. Baileys reports this is required to
82
+ **receive view-once media**, which a web session is never sent
83
+ ([#2201](https://github.com/WhiskeySockets/Baileys/pull/2201)); miaw-core
84
+ already normalizes view-once messages, so if that holds,
85
+ `message.media.viewOnce` and `downloadMedia()` work with no further change.
86
+ The handshake is verified here; the view-once receipt itself is upstream's
87
+ claim and is **not** confirmed end-to-end — see `tests/verify-viewonce.ts` to
88
+ check it on your own account. Baileys marks the identity experimental; prefer
89
+ a dedicated `instanceId` over switching an established session.
90
+ - **Group & community administration.** `setGroupAnnounceOnly`,
91
+ `setGroupRestrictInfo`, `setGroupMemberAddMode`, `setGroupJoinApproval`,
92
+ `setGroupEphemeral`, `getGroupJoinRequests`, `approveGroupJoinRequests`,
93
+ `rejectGroupJoinRequests`, and the eight `setCommunity*` /
94
+ `*CommunityJoinRequests` equivalents.
95
+ - **Privacy settings.** `getPrivacySettings()` plus `setLastSeenPrivacy`,
96
+ `setOnlinePrivacy`, `setProfilePicturePrivacy`, `setStatusPrivacy`,
97
+ `setReadReceiptsPrivacy`, `setGroupAddPrivacy`, `setMessagesPrivacy`,
98
+ `setCallPrivacy`, `setDefaultDisappearingMode` and `setLinkPreviewsDisabled`.
99
+ - **Blocklist.** `blockContact()`, `unblockContact()`, `getBlocklist()`,
100
+ `isBlocked()`.
101
+ - **Calls.** A new `call` event carrying a normalized `MiawCall`, plus
102
+ `rejectCall()` and `createCallLink()`. A single call fires the event several
103
+ times as it progresses; `status === "offer"` is the only stage at which
104
+ `rejectCall()` still works.
105
+ - **Disappearing messages.** `setChatEphemeral()` for 1:1 chats alongside the
106
+ group and community variants, with an `EphemeralDuration` constant for the four
107
+ durations the WhatsApp UI offers.
108
+ - **Group invite cards.** `sendGroupInvite()` sends the rich "join my group" card
109
+ rather than a pasted link. Its payload type is `GroupInviteMessage` — distinct
110
+ from the existing `GroupInviteInfo`, which is the *preview* returned by
111
+ `getGroupInviteInfo()` before joining.
112
+ - **Pin-in-chat.** `pinMessage()` / `unpinMessage()`, with a `PinDuration`
113
+ constant. WhatsApp accepts only 24h, 7d and 30d here — a different set from the
114
+ disappearing-message durations.
115
+ - **CLI:** `group|community announce|restrict|add-mode|approval|ephemeral`,
116
+ `group|community requests list|approve|reject`, `privacy
117
+ show|set|disappearing|link-previews`, `block list|add|remove`, `call link`,
118
+ `chat ephemeral|pin-message|unpin-message`, and `send group-invite`. All with
119
+ tab completion and `help` topics.
120
+ - **`tests/unit/baileys-export-surface.test.ts`** — imports Baileys unmocked and
121
+ asserts every symbol the two import sites destructure still exists. Seventeen
122
+ unit suites replace the module wholesale with hand-written factories, so
123
+ without this a renamed or removed upstream export stays invisible until
124
+ runtime.
125
+
126
+ ### Fixed
127
+
128
+ - **`isBlocked()` reported a blocked contact as not blocked.** WhatsApp
129
+ returns the blocklist in whatever addressing mode it holds per contact, and
130
+ on a privacy-enabled account that is a `@lid` rather than a phone JID —
131
+ blocking `628…` produced a blocklist of `["270819297075205@lid"]`. The
132
+ original plain `includes(phoneJid)` therefore always missed. It now compares
133
+ both directions, using the LID this account knows for the number and, failing
134
+ that, resolving each blocked LID back to a phone JID. Found only by live
135
+ testing; every mock in the unit suite returned phone JIDs.
136
+ - **`npx miaw-cli` was broken for every consumer, published or local.**
137
+ `bin.miaw-cli` pointed at `bin/miaw-cli.ts` — a TypeScript file carrying a
138
+ `#!/usr/bin/env node` shebang and importing from `../src/**`. A bin runs
139
+ under the user's plain `node`, which cannot resolve TypeScript's
140
+ `.js`-for-`.ts` specifiers; and `.npmignore` excludes `src/` from the
141
+ tarball, so the published package shipped an entry point that could not
142
+ find its own imports. The entry logic moved to `src/cli/main.ts` (so `tsc`
143
+ compiles it into `dist/`), `bin/miaw-cli.mjs` is the published executable,
144
+ and `bin/miaw-cli.ts` remains the tsx development shim behind
145
+ `npm run cli`.
146
+ - **`dotenv` was a devDependency but is imported at runtime**, so an
147
+ installed package failed on startup with `ERR_MODULE_NOT_FOUND`. Moved to
148
+ `dependencies`. Found by `npm pack` + installing the tarball into a clean
149
+ directory — neither bug is visible from inside the repo, where `src/`
150
+ exists and devDependencies are installed.
151
+ - **`community invite-link` was advertised but never worked.** The subcommand
152
+ list printed by `community` has named it since v1.9.0, but the dispatch
153
+ switch had no `case` for it, so it fell through to the unknown-command
154
+ branch and always errored. `group invite-link` has existed since v1.4.
155
+ - **Five `help` topics tab-completed and then failed.** `help chat|story|
156
+ community|call|business` were offered by completion but had no case in
157
+ `showReplHelp()`, printing "Unknown help topic". The completion tree, the
158
+ dispatch and two "available topics" lines were four hand-maintained copies
159
+ of the same list; they are now all derived from one `HELP_TOPIC_HANDLERS`
160
+ map, and the five missing help screens were written.
161
+ - **`test:manual:build` never worked and has been removed.** It ran
162
+ `node dist/tests/interactive-test.js`, but `tsconfig.json` sets
163
+ `rootDir: ./src` and excludes `tests/`, so `dist/tests/` is never emitted.
164
+ - **`sendGroupInvite()` failed for any group without a profile picture.** Baileys
165
+ embeds a thumbnail on the invite card by calling `getProfilePicUrl(groupJid)`
166
+ and does not guard it; WhatsApp answers `item-not-found` for a pictureless
167
+ group, which throws and aborts the whole send. Since most groups have no
168
+ picture, the unguarded path failed more often than it worked. `sendGroupInvite`
169
+ now passes its own guarded hook — Baileys spreads caller options last, so it
170
+ wins — and sends the card without a thumbnail rather than not at all. Found by
171
+ live testing; it is invisible to unit tests because the mock never throws.
172
+ - **Omitting a disappearing-message duration silently disabled it.**
173
+ `miaw-cli group ephemeral <jid>` with no duration turned disappearing messages
174
+ **off** for the whole group instead of printing a usage line — `Number("")` is
175
+ `0`, and `0` is a valid duration meaning "off", so the missing argument parsed
176
+ as a successful request to disable. Same for `community ephemeral`,
177
+ `chat ephemeral`, and `privacy disappearing` (which changed an account-wide
178
+ default). Found in review of this release, before any of it shipped.
179
+ - **`--force` could swallow the following token.** It was missing from
180
+ `BOOLEAN_FLAGS` in `bin/miaw-cli.ts`, the same class of bug that `--json` hit
181
+ previously.
182
+ - **CLI docs gap.** `chat` commands were entirely undocumented in
183
+ [CLI.md](docs/CLI.md) despite shipping in v1.7.0; they now have a section.
184
+
185
+ ### Verified live
186
+
187
+ Against a real WhatsApp connection (Baileys rc14):
188
+
189
+ - **`profilePictureUrl`** — the upstream tctoken-nesting fix. 2 of 5 real
190
+ contacts resolved to live URLs; the rest correctly returned `null`.
191
+ - **`getPrivacySettings()`** — WhatsApp returned 16 categories; the 8 with
192
+ Baileys setters map onto typed fields and the other 8 survive under `raw`.
193
+ - **All 19 v1.12.0 write paths** — every group setting toggled and restored with
194
+ a read-back confirming the change landed, plus 1:1 ephemeral, privacy
195
+ round-trips, `sendGroupInvite`, pin/unpin, and `createCallLink`. 19/19.
196
+ - **`BrowserPresets.android()`** — WhatsApp issued a QR rather than a 428, so
197
+ the `Platform.ANDROID` handshake is accepted. End-to-end view-once *receipt*
198
+ is not yet confirmed; see [FOLLOW_UPS.md](docs/FOLLOW_UPS.md) section 7.
199
+ - **No regressions from rc14.** The CLI integration suite has 15 pre-existing
200
+ failures, unrelated to this release — running the same six suites against
201
+ rc13 produced a byte-identical failure set. They are catalogued in
202
+ [FOLLOW_UPS.md](docs/FOLLOW_UPS.md) section 9.
203
+
204
+ ### Internal
205
+
206
+ - `jest.config.js` sets `maxWorkers: 1`. The live-connection suites share a
207
+ single WhatsApp session, and forking one worker per core for a full run is
208
+ memory-hungry; `test:cli` already passed `--runInBand` for the first reason.
209
+ - CLI integration teardown moved from `10-business-commands.test.ts` to the new
210
+ `13-privacy-call-commands.test.ts`. Files run in name order and 13 is now the
211
+ last one needing a connection (11 and 12 are deliberately offline), so
212
+ disconnecting at 10 would pull the socket out from under it.
213
+ - 623 unit tests across 30 suites (up from 417), 105 CLI router tests (up from
214
+ 26), 278 CLI integration tests across 13 files (up from 238).
215
+ - New `tests/unit/cli-help-consistency.test.ts` and a dispatchability sweep in
216
+ `01-command-router.test.ts`, both verified to fail against the previous code.
217
+ - The manual runner grew from 82 to 96 entries, and gained a `community` group:
218
+ all 19 community methods, v1.9.0's included, had been unreachable from it.
219
+
220
+ ## [1.11.0] - 2026-09-02
221
+
222
+ ### Added
223
+
224
+ - **Per-instance proxy pins (CLI).** `miaw-cli instance set-proxy <id>` persists a
225
+ proxy assignment to `<session-path>/instances.json`, so
226
+ `miaw-cli --instance-id bot-3 <command>` uses bot-3's proxy with no flags.
227
+ Four sources: a positional URL, `--label <name>` (selects a `label=` entry from
228
+ the proxy file and stores **no credentials**), `--from-env <VAR>`, and
229
+ `--from-file` (materializes the current deterministic selection).
230
+ `instance unset-proxy <id>` removes it.
231
+ - `instance ls` gains a Proxy column (masked) and lists instances that are pinned
232
+ but not yet created as `[not created]` — pinning before `instance create` is
233
+ supported, so the pairing itself comes from the final egress IP.
234
+ - **`MiawClient.setProxy(proxy)`** — stages a proxy for the **next** `connect()`,
235
+ returning `{ success, proxy, reconnectRequired, error }`. It never touches a
236
+ live socket, validates eagerly, and is refused when the client was constructed
237
+ with a custom `agent`/`fetchAgent`.
238
+ - `getProxyInfo()` gains `active` (is the open socket using this config?) and
239
+ `pending` (what it is using instead). The `{ url, protocol }` shape and the
240
+ `null`-for-custom-agent contract are unchanged.
241
+ - New exported types `SetProxyResult` and `ProxyInfo`.
242
+ - A complete, runnable dead-proxy failover recipe in docs/PROXY.md and
243
+ examples/10-proxy-rotation.ts, reusing a single client.
244
+ - `docs/DEPLOYMENT_INSTANCE_PROXY.md` — operational notes for the new
245
+ persistent pin store, and `docs/FOLLOW_UPS.md` for deferred findings.
246
+
247
+ ### Behavior changes
248
+
249
+ - **`ProxyRotator` now defaults to `deterministic`, not `round-robin`.** The docs
250
+ and CLAUDE.md have always said deterministic is the default; only the CLI
251
+ actually applied it. The mismatch was silent and severe: following the docs
252
+ (`new ProxyRotator(urls)` then `next(id)`) gave round-robin, which hands a
253
+ long-lived session a different egress IP on each call — read by WhatsApp as
254
+ account takeover. Code changed rather than docs, so the default fails safe.
255
+ `next()` with no `instanceId` on a defaulted rotator now throws a message
256
+ naming the fix; pass `{ strategy: "round-robin" }` explicitly to restore the
257
+ old behavior. Checked against the sibling `miaw-api`, which constructs with an
258
+ explicit strategy and always calls `next(instanceId)`: unaffected.
259
+
260
+ ### Fixed
261
+
262
+ - **The CLI silently dropped the proxy.** `instance create`/`connect`/`disconnect`/
263
+ `logout` built clients from `{instanceId, sessionPath}` only, discarding both
264
+ `proxy` and `debug`. Since the client cache key ignored the proxy, REPL
265
+ `connect <id>` cached a proxy-less client that every later command reused —
266
+ traffic went direct with no indication.
267
+ - **REPL `use <id>` carried the previous instance's proxy** to the next one: it
268
+ mutated `config.instanceId` in place without re-resolving.
269
+ - **Proxy selection ignored the target instance.** `--proxy-file` was resolved once
270
+ per process against the *startup* instance id, so `instance connect bot-3` got
271
+ another instance's egress IP. Resolution is now per target.
272
+ - `instance create` called `createClient()` directly, bypassing the cache, so the
273
+ freshly-paired live client was never registered and the next command built a
274
+ second `MiawClient` for the same account.
275
+ - An explicit `disconnect()` emitted `disconnected` twice — once as `"unknown"`
276
+ from the `connection.update{close}` that Baileys' `end()` fires, then once as
277
+ `"intentional"`.
278
+ - Console-filter reference leak: the filter was acquired only in the constructor
279
+ but released on every `disconnect()`/`logout()`, so a reconnect cycle lost this
280
+ client's reference and repeated cycles un-filtered other live clients' output.
281
+ - Proxy transports leaked: `connect()` built fresh agents on every call, auto-
282
+ reconnects included, and never released the old ones.
283
+ - `.cli_history` was written 0644 with every line verbatim, so an
284
+ `instance set-proxy` command turned it into a plaintext credential store. Now
285
+ redacted at entry and written 0600.
286
+ - **Global boolean flags swallowed the command name.** The arg parser assumed
287
+ every `--flag` took a value, so `miaw-cli --json instance ls` parsed as
288
+ `json="instance"` and failed with "Unknown command: ls" — even though
289
+ `--json` is documented as a global option. Value-less flags (`--json`,
290
+ `--debug`, `--ip`, `--from-file`, `--help`, `--version`) are now recognised
291
+ as booleans in any position.
292
+ - **`src/utils/proxy-rotator.ts` contained a literal NUL byte**, which made
293
+ `file(1)` classify the source as binary and caused `grep` to skip it in any
294
+ repo-wide search. Replaced with the equivalent escape sequence; verified
295
+ runtime-identical, so no instance is remapped to a different proxy.
296
+ - **Docs: `fetchAgent` is an `http.Agent`, not an undici Dispatcher.** The JSDoc
297
+ and three doc sites claimed otherwise; the PROXY.md "Custom agents" snippet was
298
+ copy-pasteable and would silently break every media upload through the proxy.
299
+ Also corrects the `createProxyAgents` API-table row and two broken anchors.
300
+
8
301
  ## [1.10.0] - 2026-07-29
9
302
 
10
303
  **Proxy files, rotation, and CLI diagnostics** - builds on the v1.3.0 proxy core.
package/README.md CHANGED
@@ -10,7 +10,7 @@ Miaw Core abstracts away the complexity of Baileys, providing a clean, simple AP
10
10
 
11
11
  | Aspect | Baileys (Direct) | Miaw Core |
12
12
  | ---------------------- | ----------------------------- | ---------------------------------- |
13
- | **Learning Curve** | Steep - 150+ methods to learn | Gentle - 92 focused methods |
13
+ | **Learning Curve** | Steep - 150+ methods to learn | Gentle - ~200 focused methods |
14
14
  | **Setup Code** | 50-100 lines boilerplate | 10 lines to start |
15
15
  | **Session Management** | Manual auth state handling | Automatic file-based persistence |
16
16
  | **Reconnection** | DIY implementation | Built-in with configurable retries |
@@ -30,6 +30,7 @@ For a detailed comparison, see [Baileys vs Miaw Core Comparison](./docs/BAILEYS_
30
30
  - **[Usage Guide](./docs/USAGE.md)** - Complete guide for all current features
31
31
  - **[Proxy Guide](./docs/PROXY.md)** - Proxies, proxy list files, and rotation strategies
32
32
  - **[Proxy Deployment Notes](./docs/DEPLOYMENT_PROXY.md)** - Operational guidance for deploying with proxies
33
+ - **[Per-Instance Proxy Deployment](./docs/DEPLOYMENT_INSTANCE_PROXY.md)** - What v1.11.0 changes for operators
33
34
  - **[LID Resolution](./docs/LID_RESOLUTION.md)** - Working with privacy-masked (`@lid`) JIDs
34
35
  - **[Baileys Comparison](./docs/BAILEYS_VS_MIAW_COMPARISON.md)** - Feature comparison with raw Baileys
35
36
  - **[Migration Guide](./docs/MIGRATION.md)** - Upgrading between versions
@@ -44,7 +45,7 @@ For a detailed comparison, see [Baileys vs Miaw Core Comparison](./docs/BAILEYS_
44
45
  - **Auto-Reconnection** - Handles connection drops and reconnects automatically
45
46
  - **Session Management** - File-based session storage with automatic persistence
46
47
  - **Multiple Instances** - Run multiple WhatsApp connections in a single process
47
- - **Proxy Support** - HTTP/HTTPS/SOCKS proxies, proxy list files, and per-instance rotation
48
+ - **Proxy Support** - HTTP/HTTPS/SOCKS proxies, proxy list files, and per-instance proxies (assigned or pinned)
48
49
  - **TypeScript Support** - Full type definitions for excellent IDE experience
49
50
  - **Event-Driven** - Easy-to-use event system for messages and connection states
50
51
  - **Normalized Messages** - Simplified message format, no more complex Baileys structures
@@ -132,7 +133,7 @@ For more examples and detailed usage, see the [Usage Guide](./docs/USAGE.md).
132
133
 
133
134
  ### Manual Interactive Testing
134
135
 
135
- Test all 92 API methods interactively:
136
+ Drive the API interactively against a live connection:
136
137
 
137
138
  ```bash
138
139
  # Show available test groups
@@ -147,17 +148,34 @@ npm run test:manual business # Business features only
147
148
 
148
149
  **Available test groups:**
149
150
 
150
- | Group | Description | Methods |
151
- | ------------ | ------------------------------- | ------- |
152
- | `core` | Connection, lifecycle | 6 |
153
- | `get` | Fetch contacts, groups, chats | 6 |
154
- | `messaging` | Send/receive, reactions, edit | 12 |
155
- | `contacts` | Check numbers, contact info | 7 |
156
- | `group` | Create, manage participants | 13 |
157
- | `profile` | Update picture, name, status | 4 |
158
- | `business` | Labels, catalog (Business only) | 10 |
159
- | `newsletter` | Channels, subscriptions | 6 |
160
- | `ux` | Typing, presence, read receipts | 5 |
151
+ | Group | Description |
152
+ | ------------ | ----------------------------------------------- |
153
+ | `core` | Connection, lifecycle |
154
+ | `get` | Fetch contacts, groups, chats |
155
+ | `messaging` | Send/receive, reactions, edit, pin, invites |
156
+ | `contacts` | Check numbers, contact info |
157
+ | `group` | Create, manage participants, admin settings |
158
+ | `community` | Communities, linked groups, admin, join requests|
159
+ | `profile` | Update picture, name, status |
160
+ | `business` | Labels, catalog (Business only) |
161
+ | `newsletter` | Channels, subscriptions |
162
+ | `ux` | Typing, presence, read receipts |
163
+ | `privacy` | Privacy settings, block / unblock |
164
+ | `calls` | Call event, reject, call links |
165
+
166
+ Run `npm run test:manual` with no argument for this list with a live entry count
167
+ per group — it is generated from the runner's own category map, so it cannot go
168
+ stale.
169
+
170
+ **Unattended mode:**
171
+
172
+ ```bash
173
+ npm run test:manual:auto # every group, no prompts, non-zero exit on failure
174
+ npm run test:manual group -- --auto # one group
175
+ ```
176
+
177
+ Prompts resolve to their `.env.test` defaults. Entries needing a human are
178
+ skipped; add `--destructive` to include the ones that change real state.
161
179
 
162
180
  **Features:**
163
181
 
@@ -165,25 +183,36 @@ npm run test:manual business # Business features only
165
183
  - Tracks test results with pass/fail/skip status
166
184
  - Generates summary report with timestamps
167
185
  - Pre-loads test configuration from `.env.test`
168
-
169
- See [Test Coverage Analysis](./docs/TEST_COVERAGE_ANALYSIS.md) for detailed coverage report.
186
+ - Warns when `.env.test` is configured in a way that silently invalidates
187
+ results (e.g. both contact numbers set to the bot's own number)
170
188
 
171
189
  ### Unit & Integration Tests
172
190
 
173
191
  ```bash
174
- # Run all tests
192
+ # Unit suites only - the fast gate (615 tests, no connection needed)
193
+ npm run test:unit
194
+
195
+ # Everything, including the live-connection suites
175
196
  npm test
176
197
 
177
- # Run in watch mode
178
- npm run test:watch
198
+ # CLI command tests against a real connection (13 files)
199
+ npm run test:cli
200
+
201
+ # Type-check src/ and tests/ together
202
+ npm run typecheck
179
203
 
180
- # Generate coverage report
204
+ # Watch mode / coverage
205
+ npm run test:watch
181
206
  npm run test:coverage
182
207
  ```
183
208
 
184
- ## Current Capabilities (v1.10.0)
209
+ > Jest runs serially (`maxWorkers: 1`): the live suites share one WhatsApp
210
+ > session, so a parallel run would have them fighting over it. For the same
211
+ > reason, never run two test commands at once.
212
+
213
+ ## Current Capabilities (v1.12.1)
185
214
 
186
- Built on **Baileys v7.0.0-rc13** - the latest WhatsApp Web protocol implementation.
215
+ Built on **Baileys v7.0.0-rc14** - the latest WhatsApp Web protocol implementation.
187
216
 
188
217
  ### Core Features
189
218
 
@@ -200,11 +229,19 @@ Built on **Baileys v7.0.0-rc13** - the latest WhatsApp Web protocol implementati
200
229
  ### Networking
201
230
 
202
231
  - ✅ HTTP / HTTPS / SOCKS4 / SOCKS5 proxies, per instance
232
+ - ✅ Per-instance proxy pins that persist — `miaw-cli instance set-proxy <id> ...`, then no flags needed
233
+ - ✅ `client.setProxy()` — stage a proxy for the next connect (never rotates a live session's IP)
203
234
  - ✅ Proxy list files (TXT / JSON) with validation and hot reload
204
235
  - ✅ Rotation strategies (round-robin, random, weighted, deterministic-per-instance)
205
236
  - ✅ `miaw-cli proxy test` connectivity diagnostics — no WhatsApp connection needed
206
237
  - ⚠️ SOCKS proxies: media *downloads* use a direct connection ([details](./docs/PROXY.md#️-socks-media-downloads-use-a-direct-connection))
207
238
 
239
+ ### Connection Identity
240
+
241
+ - ✅ Configurable browser identity via `BrowserPresets` (macOS / Windows / Ubuntu / Android)
242
+ - ✅ Android identity — Baileys reports this is required to **receive view-once media** ([details](./docs/USAGE.md#receiving-view-once-messages-android-identity))
243
+ - ✅ Live WhatsApp Web version negotiation (avoids stale-version 428 rejections)
244
+
208
245
  ### Advanced Messaging
209
246
 
210
247
  - ✅ Reply/quote messages
@@ -212,6 +249,10 @@ Built on **Baileys v7.0.0-rc13** - the latest WhatsApp Web protocol implementati
212
249
  - ✅ Delete messages
213
250
  - ✅ Message reactions
214
251
  - ✅ Forward messages
252
+ - ✅ Location, contact cards, polls and stickers
253
+ - ✅ Group invite cards — `sendGroupInvite()`
254
+ - ✅ Pin / unpin a message in a chat — `pinMessage()` / `unpinMessage()`
255
+ - ✅ Disappearing messages per chat — `setChatEphemeral()`
215
256
 
216
257
  ### Group Management
217
258
 
@@ -220,6 +261,39 @@ Built on **Baileys v7.0.0-rc13** - the latest WhatsApp Web protocol implementati
220
261
  - ✅ Promote/demote admins
221
262
  - ✅ Group invite links
222
263
  - ✅ Update group settings
264
+ - ✅ Announce-only and info-edit locks
265
+ - ✅ Member-add mode (admins only / all members)
266
+ - ✅ Join approval, plus listing / approving / rejecting join requests
267
+ - ✅ Group-wide disappearing messages
268
+
269
+ ### Communities
270
+
271
+ - ✅ Create communities, link and unlink groups
272
+ - ✅ Community members: add, remove, promote, demote
273
+ - ✅ Community invite links
274
+ - ✅ The same admin surface as groups — announce, restrict, add-mode,
275
+ join approval, join requests, disappearing messages
276
+
277
+ ### Privacy & Blocklist
278
+
279
+ - ✅ Read all privacy settings, including categories Baileys has no setter for
280
+ (preserved under `raw`)
281
+ - ✅ Eight setters — last seen, online, profile picture, status, read receipts,
282
+ group add, messages, calls
283
+ - ✅ Default disappearing mode for new chats, and link-preview control
284
+ - ✅ Block / unblock, list the blocklist, check whether a contact is blocked
285
+
286
+ ### Calls
287
+
288
+ - ✅ `call` event for incoming calls (offer / accept / reject / timeout)
289
+ - ✅ Reject an incoming call — `rejectCall()`
290
+ - ✅ Create shareable call links — `createCallLink()`
291
+
292
+ ### Chat Management
293
+
294
+ - ✅ Archive, pin, mute, mark read/unread
295
+ - ✅ Clear or delete a chat
296
+ - ✅ Star / unstar messages
223
297
 
224
298
  ### Profile Management
225
299
 
@@ -256,16 +330,20 @@ Miaw Core handles these Baileys complexities for you:
256
330
 
257
331
  ```text
258
332
  miaw-core/
333
+ ├── bin/
334
+ │ └── miaw-cli.ts # CLI entry point (one-shot + REPL)
259
335
  ├── src/
260
336
  │ ├── client/ # Main MiawClient class
261
337
  │ ├── handlers/ # Auth and message handlers
262
338
  │ ├── types/ # TypeScript definitions
263
- │ ├── utils/ # Storage utilities
339
+ │ ├── constants/ # Shared constant tables
340
+ │ ├── utils/ # Proxy agents, rotation, browser presets
341
+ │ ├── cli/ # CLI subsystem (commands + utils)
264
342
  │ └── index.ts # Public API exports
265
343
  ├── docs/ # Documentation
266
344
  ├── examples/ # Usage examples
267
- ├── tests/ # Unit and integration tests
268
- └── README.md # This file
345
+ ├── tests/ # Unit, integration and CLI integration tests
346
+ └── README.md # This file
269
347
  ```
270
348
 
271
349
  ## Contributing
@@ -290,8 +368,8 @@ MIT
290
368
 
291
369
  ## Credits
292
370
 
293
- Built on top of [@whiskeysockets/baileys](https://github.com/WhiskeySockets/Baileys) v7.0.0-rc13
371
+ Built on top of [@whiskeysockets/baileys](https://github.com/WhiskeySockets/Baileys) v7.0.0-rc14
294
372
 
295
373
  ---
296
374
 
297
- **Version:** 1.4.1 | **Baileys:** 7.0.0-rc13 | **Status:** Stable | **Updated:** 2026-06-26
375
+ **Version:** 1.12.1 | **Baileys:** 7.0.0-rc14 | **Status:** Stable | **Updated:** 2026-09-02
@@ -0,0 +1,31 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * miaw-cli executable (published entry point).
4
+ *
5
+ * Plain JavaScript on purpose: `bin` entries are run by whatever `node` the
6
+ * user has, with no TypeScript loader. The previous entry was a .ts file with
7
+ * a `#!/usr/bin/env node` shebang that imported from ../src/**, so it failed
8
+ * twice over -- node cannot resolve TypeScript's `.js`-for-`.ts` specifiers,
9
+ * and .npmignore excludes src/ from the published package entirely.
10
+ *
11
+ * Requires a build: run `npm run build` first, or use `npm run cli` (tsx) for
12
+ * development against the sources.
13
+ */
14
+ import { existsSync } from "node:fs";
15
+ import { dirname, join } from "node:path";
16
+ import { fileURLToPath } from "node:url";
17
+
18
+ const here = dirname(fileURLToPath(import.meta.url));
19
+ const entry = join(here, "..", "dist", "cli", "main.js");
20
+
21
+ if (!existsSync(entry)) {
22
+ console.error("❌ miaw-cli: dist/ is missing — the package was not built.");
23
+ console.error(" Run `npm run build`, or use `npm run cli` to run from source.");
24
+ process.exit(1);
25
+ }
26
+
27
+ const { main } = await import(entry);
28
+ main().catch((error) => {
29
+ console.error("Fatal error:", error);
30
+ process.exit(1);
31
+ });
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Call Commands (v1.12.0)
3
+ *
4
+ * Create shareable call links. Rejecting a call is an event-driven operation —
5
+ * it only works while a call is ringing — so it belongs in a bot's `call`
6
+ * handler rather than on the CLI.
7
+ */
8
+ import { MiawClient } from "../../index.js";
9
+ /**
10
+ * Create a shareable call link
11
+ */
12
+ export declare function cmdCallLink(client: MiawClient, args: {
13
+ type: "audio" | "video";
14
+ startTime?: number;
15
+ }): Promise<boolean>;
16
+ //# sourceMappingURL=call.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"call.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/call.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAI5C;;GAEG;AACH,wBAAsB,WAAW,CAC/B,MAAM,EAAE,UAAU,EAClB,IAAI,EAAE;IAAE,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GACpD,OAAO,CAAC,OAAO,CAAC,CAmBlB"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Call Commands (v1.12.0)
3
+ *
4
+ * Create shareable call links. Rejecting a call is an event-driven operation —
5
+ * it only works while a call is ringing — so it belongs in a bot's `call`
6
+ * handler rather than on the CLI.
7
+ */
8
+ import { ensureConnected } from "../utils/session.js";
9
+ import { formatMessage } from "../utils/formatter.js";
10
+ /**
11
+ * Create a shareable call link
12
+ */
13
+ export async function cmdCallLink(client, args) {
14
+ const conn = await ensureConnected(client);
15
+ if (!conn.success) {
16
+ console.log(`❌ Not connected: ${conn.reason}`);
17
+ return false;
18
+ }
19
+ console.log(`🔗 Creating ${args.type} call link...`);
20
+ const link = await client.createCallLink(args.type, args.startTime);
21
+ if (!link) {
22
+ console.log(formatMessage(false, "Failed to create call link"));
23
+ return false;
24
+ }
25
+ console.log(formatMessage(true, "Call link created"));
26
+ console.log(`\n${link}\n`);
27
+ return true;
28
+ }
29
+ //# sourceMappingURL=call.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"call.js","sourceRoot":"","sources":["../../../src/cli/commands/call.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAEtD;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,MAAkB,EAClB,IAAqD;IAErD,MAAM,IAAI,GAAG,MAAM,eAAe,CAAC,MAAM,CAAC,CAAC;IAC3C,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;QAClB,OAAO,CAAC,GAAG,CAAC,oBAAoB,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;QAC/C,OAAO,KAAK,CAAC;IACf,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,CAAC,IAAI,eAAe,CAAC,CAAC;IAErD,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;IAEpE,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,KAAK,EAAE,4BAA4B,CAAC,CAAC,CAAC;QAChE,OAAO,KAAK,CAAC;IACf,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,IAAI,EAAE,mBAAmB,CAAC,CAAC,CAAC;IACtD,OAAO,CAAC,GAAG,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC;IAC3B,OAAO,IAAI,CAAC;AACd,CAAC"}