miaw-core 1.9.0 → 1.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/.env.example +59 -0
  2. package/CHANGELOG.md +150 -0
  3. package/README.md +13 -1
  4. package/bin/miaw-cli.ts +50 -4
  5. package/dist/cli/commands/commands-index.d.ts +1 -0
  6. package/dist/cli/commands/commands-index.d.ts.map +1 -1
  7. package/dist/cli/commands/commands-index.js +1 -0
  8. package/dist/cli/commands/commands-index.js.map +1 -1
  9. package/dist/cli/commands/index.d.ts +6 -0
  10. package/dist/cli/commands/index.d.ts.map +1 -1
  11. package/dist/cli/commands/index.js +45 -2
  12. package/dist/cli/commands/index.js.map +1 -1
  13. package/dist/cli/commands/proxy.d.ts +59 -0
  14. package/dist/cli/commands/proxy.d.ts.map +1 -0
  15. package/dist/cli/commands/proxy.js +418 -0
  16. package/dist/cli/commands/proxy.js.map +1 -0
  17. package/dist/cli/repl.d.ts +4 -0
  18. package/dist/cli/repl.d.ts.map +1 -1
  19. package/dist/cli/repl.js +43 -3
  20. package/dist/cli/repl.js.map +1 -1
  21. package/dist/cli/utils/proxy-config.d.ts +46 -0
  22. package/dist/cli/utils/proxy-config.d.ts.map +1 -0
  23. package/dist/cli/utils/proxy-config.js +93 -0
  24. package/dist/cli/utils/proxy-config.js.map +1 -0
  25. package/dist/client/MiawClient.d.ts +8 -0
  26. package/dist/client/MiawClient.d.ts.map +1 -1
  27. package/dist/client/MiawClient.js +71 -10
  28. package/dist/client/MiawClient.js.map +1 -1
  29. package/dist/index.d.ts +6 -2
  30. package/dist/index.d.ts.map +1 -1
  31. package/dist/index.js +5 -1
  32. package/dist/index.js.map +1 -1
  33. package/dist/types/index.d.ts +22 -0
  34. package/dist/types/index.d.ts.map +1 -1
  35. package/dist/types/index.js.map +1 -1
  36. package/dist/utils/proxy-agent.d.ts +47 -11
  37. package/dist/utils/proxy-agent.d.ts.map +1 -1
  38. package/dist/utils/proxy-agent.js +65 -12
  39. package/dist/utils/proxy-agent.js.map +1 -1
  40. package/dist/utils/proxy-loader.d.ts +114 -0
  41. package/dist/utils/proxy-loader.d.ts.map +1 -0
  42. package/dist/utils/proxy-loader.js +379 -0
  43. package/dist/utils/proxy-loader.js.map +1 -0
  44. package/dist/utils/proxy-rotator.d.ts +126 -0
  45. package/dist/utils/proxy-rotator.d.ts.map +1 -0
  46. package/dist/utils/proxy-rotator.js +0 -0
  47. package/dist/utils/proxy-rotator.js.map +1 -0
  48. package/docs/BAILEYS_VS_MIAW_COMPARISON.md +8 -6
  49. package/docs/CLI.md +158 -1
  50. package/docs/DEFERRED_FEATURES.md +67 -0
  51. package/docs/DEPLOYMENT_PROXY.md +145 -0
  52. package/docs/PROXY.md +617 -0
  53. package/docs/USAGE.md +34 -63
  54. package/package.json +3 -2
  55. package/docs/PROXY_SUPPORT_PLAN.md +0 -1442
package/.env.example ADDED
@@ -0,0 +1,59 @@
1
+ # miaw-core / miaw-cli runtime configuration
2
+ #
3
+ # Copy to .env and edit. Every value here is optional — each has a default and
4
+ # each can also be supplied as a CLI flag. Flags win over environment variables.
5
+ #
6
+ # cp .env.example .env
7
+ #
8
+ # .env is gitignored. Prefer this file over CLI flags for anything containing a
9
+ # password: flags land in your shell history and in the process list, where any
10
+ # other user on the machine can read them.
11
+
12
+ # ─── Instance ────────────────────────────────────────────────────────────────
13
+
14
+ # Instance ID. Sessions are isolated per instance. (flag: --instance-id)
15
+ MIAW_INSTANCE_ID=default
16
+
17
+ # Where session credentials are stored, as {MIAW_SESSION_PATH}/{MIAW_INSTANCE_ID}/
18
+ # (flag: --session-path)
19
+ MIAW_SESSION_PATH=./sessions-cli
20
+
21
+ # ─── Proxy (all optional; added in v1.10.0) ──────────────────────────────────
22
+
23
+ # A single proxy for this instance. (flag: --proxy)
24
+ # Schemes: http, https, socks4, socks4a, socks5, socks5h
25
+ #
26
+ # Prefer the h/a variants (socks5h, socks4a): plain socks5/socks4 resolve DNS
27
+ # locally, so the destination hostname leaks to your resolver even though the
28
+ # connection itself is tunnelled.
29
+ #
30
+ # Passwords containing @ : / ? # or % must be percent-encoded here
31
+ # ("p@ss#1" becomes "p%40ss%231"), or use the ProxyConfig object form in code.
32
+ #
33
+ # MIAW_PROXY=socks5h://user:password@proxy.example.com:1080
34
+
35
+ # 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)
38
+ #
39
+ # See examples/proxies.example.txt for the format.
40
+ #
41
+ # MIAW_PROXY_FILE=./proxies.txt
42
+
43
+ # How a proxy is picked from MIAW_PROXY_FILE. (flag: --proxy-strategy)
44
+ # deterministic same instanceId always maps to the same proxy [default]
45
+ # round-robin next in the list, wrapping
46
+ # random uniform random
47
+ # weighted random, biased by each entry's weight=
48
+ #
49
+ # Leave this as deterministic for anything with a persisted WhatsApp session.
50
+ # WhatsApp treats a session's source IP as a trust signal, so a session whose
51
+ # egress IP jumps between runs looks like an account takeover. Rotation is for
52
+ # spreading instances across proxies, never for rotating a live session's IP.
53
+ #
54
+ # MIAW_PROXY_STRATEGY=deterministic
55
+
56
+ # ─── Logging ─────────────────────────────────────────────────────────────────
57
+
58
+ # Verbose Baileys logging. (flag: --debug)
59
+ # DEBUG=false
package/CHANGELOG.md CHANGED
@@ -5,6 +5,113 @@ 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.10.0] - 2026-07-29
9
+
10
+ **Proxy files, rotation, and CLI diagnostics** - builds on the v1.3.0 proxy core.
11
+ All additive; no breaking changes.
12
+
13
+ ### Added
14
+
15
+ - **`socks5h://` and `socks4a://` support.** These resolve DNS *at the proxy*;
16
+ plain `socks5://` / `socks4://` resolve locally, so the destination hostname still
17
+ leaks to your DNS resolver even though the connection is tunnelled. Both variants
18
+ were previously rejected as unsupported protocols despite the underlying agent
19
+ supporting them.
20
+ - **Proxy list files** - `loadProxyList()` / `loadProxyListSync()` / `parseProxyList()`
21
+ read TXT (one per line, `#`/`;` comments, optional `weight=` and `label=` tokens,
22
+ scheme-less `host:port[:user:pass]` vendor forms) or JSON (array of strings and/or
23
+ objects, or a `{ "proxies": [...] }` wrapper). Format is auto-detected by extension
24
+ then by content. `validateProxyList()` partitions a list; `watchProxyList()` hot-reloads
25
+ a file.
26
+ - **`ProxyRotator`** - `round-robin`, `random`, `weighted`, and `deterministic`
27
+ strategies, plus `ProxyRotator.fromFile()`, `setProxies()`, `getStats()`, and `close()`.
28
+ It returns a `ProxyPoolEntry` you pass to `new MiawClient({ proxy })`, so `getProxyInfo()`
29
+ and proxy validation keep working for rotated instances.
30
+ - **`maskProxyUrl()`** - exported credential masking; `getProxyInfo()` now uses it.
31
+ - **CLI**: `proxy list` (alias `ls`), `proxy test <url>`, `proxy test-all`. These need
32
+ **no WhatsApp connection** - they probe the proxy through the same agent Baileys uses,
33
+ so you can validate a proxy before spending a pairing attempt on it. `proxy test-all`
34
+ exits non-zero if any proxy fails, so it can gate a deploy.
35
+ - **CLI global flags**: `--proxy-file <path>`, `--proxy-strategy <strategy>`
36
+ (default `deterministic`; `instance` accepted as an alias). Env fallbacks:
37
+ `MIAW_PROXY`, `MIAW_PROXY_FILE`, `MIAW_PROXY_STRATEGY`.
38
+ - **[.env.example](./.env.example)** - documents every `MIAW_*` runtime variable,
39
+ including `MIAW_INSTANCE_ID` and `MIAW_SESSION_PATH`, which the CLI has always
40
+ read but which were never written down anywhere.
41
+ - **[docs/DEPLOYMENT_PROXY.md](./docs/DEPLOYMENT_PROXY.md)** - operational notes for
42
+ deploying with proxies: why `instanceId` must be stable across rollouts, treating
43
+ proxy files as secrets, ConfigMap-safe hot reload, egress firewall rules, and the
44
+ sticky-session requirement.
45
+ - **[docs/PROXY.md](./docs/PROXY.md)** - dedicated proxy guide covering configuration,
46
+ proxy files, rotation, CLI diagnostics, troubleshooting, security, and provider
47
+ selection. Replaces the internal `docs/PROXY_SUPPORT_PLAN.md`, which is removed.
48
+ - 97 new tests (42 loader, 33 rotator, 22 CLI). The CLI proxy tests are the first in
49
+ `tests/integration/cli/` that run without a WhatsApp session.
50
+
51
+ ### Fixed
52
+
53
+ - **Media uploads through a proxy were broken and always had been** (since v1.3.0).
54
+ `fetchAgent` was set to an undici `ProxyAgent`, but Baileys declares it as an
55
+ `https.Agent` and its Node upload path hands it to `https.request({ agent })`, which
56
+ cannot use a Dispatcher. Every `sendImage`/`sendVideo`/`sendDocument` through a proxy
57
+ failed with "Media upload failed on all hosts". `fetchAgent` is now the same
58
+ `http.Agent` as `wsAgent`. **Uploads are consequently proxied on SOCKS too**, which
59
+ the previous documentation said was impossible.
60
+ - **Media downloads were never proxied.** Baileys downloads with
61
+ `fetch(url, { dispatcher })` and never wires a proxy into that path, and
62
+ `downloadMedia()` passed no options — so every download revealed the real IP
63
+ regardless of proxy settings. It now passes an undici Dispatcher explicitly.
64
+ This works for HTTP/HTTPS proxies; SOCKS downloads remain direct because undici
65
+ has no SOCKS transport.
66
+ - `proxy test` reported a **407 Proxy Authentication Required as success**. A 407 comes
67
+ from the proxy itself and means the tunnel was refused, so nothing ever reached
68
+ WhatsApp - but the probe counted any HTTP status as reachable. `proxy test-all` would
69
+ therefore green-light a proxy list with wrong credentials, defeating the pre-flight
70
+ check it exists to provide. It now fails with `EPROXYAUTH` and a non-zero exit code.
71
+ - Proxy credentials leaked in two places: `MiawClient` interpolated the raw proxy URL,
72
+ password included, into its "Invalid proxy configuration" error (which lands in logs
73
+ and stack traces), and the CLI printed the raw `--proxy` URL to the REPL startup
74
+ banner. Both are masked now.
75
+
76
+ ### Notes
77
+
78
+ - `--proxy-strategy` defaults to `deterministic` so a given `instanceId` keeps a stable
79
+ egress IP across runs. **Rotation is for distributing instances across proxies, never
80
+ for rotating a live session's IP** - WhatsApp treats a session's source IP as a trust
81
+ signal, and churning it looks like account takeover.
82
+ - Deterministic selection uses rendezvous hashing rather than `hash % length`, so adding
83
+ or removing a proxy remaps only the instances that were on it. Under modulo, one edit
84
+ to your proxy file would change the egress IP of every live session at once.
85
+ - SOCKS proxies still route media over a direct connection: Node's `fetch()` requires an
86
+ undici dispatcher and undici has no SOCKS transport. `proxy list` and `proxy test`
87
+ report this per proxy. See [docs/PROXY.md](./docs/PROXY.md).
88
+
89
+ ## [1.9.2] - 2026-07-09
90
+
91
+ **CommonJS-consumer fix** - Let `require()` resolve the package.
92
+
93
+ ### Fixed
94
+
95
+ - The `exports` map only declared the `import` condition, so CommonJS /
96
+ `require()` resolution (e.g. `ts-node` compiling to CJS) failed with
97
+ `ERR_PACKAGE_PATH_NOT_EXPORTED`. Added a `default` condition pointing at the
98
+ same ESM build; on Node >=22.12 `require(esm)` now resolves it. The package
99
+ stays ESM-only — no separate CJS build was added.
100
+
101
+ ## [1.9.1] - 2026-07-07
102
+
103
+ **Message receipts** - Observe delivery/read/played status of messages you send.
104
+
105
+ ### Added
106
+
107
+ - New `message_receipt` event (`MessageReceiptUpdate`) surfaced from Baileys'
108
+ `message-receipt.update`: `{ messageId, chatId, recipientId, type, timestamp, fromMe }`
109
+ where `type` is `'delivery' | 'read' | 'played'` (derived from the receipt
110
+ timestamps). This is the observe half of read-receipts — `markAsRead` sends
111
+ them, `message_receipt` now reports incoming ones.
112
+ - Added `docs/DEFERRED_FEATURES.md` tracking the remaining deferred backlog
113
+ (group/community admin, privacy & blocklist, calls, leftover message types).
114
+
8
115
  ## [1.9.0] - 2026-07-07
9
116
 
10
117
  **Communities** - Wrap Baileys' `community*` API (communities are hubs that link
@@ -314,6 +421,49 @@ unchanged.
314
421
 
315
422
  ---
316
423
 
424
+ ## [1.3.0] - 2026-03-13
425
+
426
+ **Proxy support** - route a WhatsApp connection through a proxy, per instance.
427
+
428
+ _Backfilled: this release shipped in commit `8def495` but was never recorded here._
429
+
430
+ ### Added
431
+
432
+ - `proxy` option on `MiawClientOptions`, accepting a URL string or a `ProxyConfig`
433
+ object (`{ url, username?, password? }`). Supports HTTP, HTTPS, SOCKS4, and SOCKS5.
434
+ - `createProxyAgents()` and `validateProxyConfig()` in `src/utils/proxy-agent.ts`.
435
+ Dual-agent approach: `https-proxy-agent` / `socks-proxy-agent` for the WebSocket
436
+ transport, `undici`'s `ProxyAgent` for media upload/download.
437
+ - `agent` and `fetchAgent` escape-hatch options for supplying custom transports.
438
+ - `MiawClient.getProxyInfo()` for inspecting the active proxy.
439
+ - `--proxy <url>` CLI flag.
440
+
441
+ ### Notes
442
+
443
+ - SOCKS proxies tunnel the WebSocket but not media transfers: Node's `fetch()` needs an
444
+ undici dispatcher and undici has no SOCKS transport, so media falls back to a direct
445
+ connection.
446
+
447
+ ## [1.2.0] - 2026-01-20
448
+
449
+ **Code quality release** - configurable timeouts, validation utilities, and type safety.
450
+
451
+ _Backfilled: this release shipped in commit `2e922d2` but was never recorded here._
452
+
453
+ ### Added
454
+
455
+ - Configurable timeouts and exported `TIMEOUTS` / `THRESHOLDS` constants.
456
+ - Validation utilities and type-guard helpers (`isError`, `getErrorMessage`,
457
+ `isBaileysMessage`, `isBaileysMessageUpsert`).
458
+ - Custom logger support.
459
+ - CLI: `send video`, `send audio`, `media download`; topic-specific help sub-commands
460
+ and expanded autocomplete.
461
+
462
+ ### Changed
463
+
464
+ - Type-safety improvements throughout; 14 of 15 issues from the code review report
465
+ resolved. No breaking changes.
466
+
317
467
  ## [1.1.0] - 2026-01-02
318
468
 
319
469
  **Baileys v7 Migration** - ESM-only release with improved session stability
package/README.md CHANGED
@@ -28,6 +28,9 @@ For a detailed comparison, see [Baileys vs Miaw Core Comparison](./docs/BAILEYS_
28
28
 
29
29
  - **[CLI Guide](./docs/CLI.md)** - Command-line interface usage
30
30
  - **[Usage Guide](./docs/USAGE.md)** - Complete guide for all current features
31
+ - **[Proxy Guide](./docs/PROXY.md)** - Proxies, proxy list files, and rotation strategies
32
+ - **[Proxy Deployment Notes](./docs/DEPLOYMENT_PROXY.md)** - Operational guidance for deploying with proxies
33
+ - **[LID Resolution](./docs/LID_RESOLUTION.md)** - Working with privacy-masked (`@lid`) JIDs
31
34
  - **[Baileys Comparison](./docs/BAILEYS_VS_MIAW_COMPARISON.md)** - Feature comparison with raw Baileys
32
35
  - **[Migration Guide](./docs/MIGRATION.md)** - Upgrading between versions
33
36
  - **[Test Coverage](./docs/TEST_COVERAGE_ANALYSIS.md)** - API coverage analysis
@@ -41,6 +44,7 @@ For a detailed comparison, see [Baileys vs Miaw Core Comparison](./docs/BAILEYS_
41
44
  - **Auto-Reconnection** - Handles connection drops and reconnects automatically
42
45
  - **Session Management** - File-based session storage with automatic persistence
43
46
  - **Multiple Instances** - Run multiple WhatsApp connections in a single process
47
+ - **Proxy Support** - HTTP/HTTPS/SOCKS proxies, proxy list files, and per-instance rotation
44
48
  - **TypeScript Support** - Full type definitions for excellent IDE experience
45
49
  - **Event-Driven** - Easy-to-use event system for messages and connection states
46
50
  - **Normalized Messages** - Simplified message format, no more complex Baileys structures
@@ -177,7 +181,7 @@ npm run test:watch
177
181
  npm run test:coverage
178
182
  ```
179
183
 
180
- ## Current Capabilities (v1.4.1)
184
+ ## Current Capabilities (v1.10.0)
181
185
 
182
186
  Built on **Baileys v7.0.0-rc13** - the latest WhatsApp Web protocol implementation.
183
187
 
@@ -193,6 +197,14 @@ Built on **Baileys v7.0.0-rc13** - the latest WhatsApp Web protocol implementati
193
197
  - ✅ TypeScript support
194
198
  - ✅ Error handling
195
199
 
200
+ ### Networking
201
+
202
+ - ✅ HTTP / HTTPS / SOCKS4 / SOCKS5 proxies, per instance
203
+ - ✅ Proxy list files (TXT / JSON) with validation and hot reload
204
+ - ✅ Rotation strategies (round-robin, random, weighted, deterministic-per-instance)
205
+ - ✅ `miaw-cli proxy test` connectivity diagnostics — no WhatsApp connection needed
206
+ - ⚠️ SOCKS proxies: media *downloads* use a direct connection ([details](./docs/PROXY.md#️-socks-media-downloads-use-a-direct-connection))
207
+
196
208
  ### Advanced Messaging
197
209
 
198
210
  - ✅ Reply/quote messages
package/bin/miaw-cli.ts CHANGED
@@ -17,6 +17,11 @@ import { runRepl } from "../src/cli/repl.js";
17
17
  import { runCommand } from "../src/cli/commands/index.js";
18
18
  import { initializeCLICleanup } from "../src/cli/utils/cleanup.js";
19
19
  import { getErrorMessage } from "../src/utils/type-guards.js";
20
+ import { maskProxyUrl } from "../src/utils/proxy-agent.js";
21
+ import {
22
+ parseProxyStrategy,
23
+ selectProxyForInstance,
24
+ } from "../src/cli/utils/proxy-config.js";
20
25
 
21
26
  // Initialize CLI cleanup handlers for graceful shutdown
22
27
  initializeCLICleanup();
@@ -28,6 +33,10 @@ dotenv.config({ path: ".env.test" });
28
33
  // Default configuration
29
34
  const DEFAULT_INSTANCE_ID = process.env.MIAW_INSTANCE_ID || "default";
30
35
  const DEFAULT_SESSION_PATH = process.env.MIAW_SESSION_PATH || "./sessions-cli";
36
+ // Proxy credentials belong in .env, not in shell history.
37
+ const DEFAULT_PROXY = process.env.MIAW_PROXY || undefined;
38
+ const DEFAULT_PROXY_FILE = process.env.MIAW_PROXY_FILE || undefined;
39
+ const DEFAULT_PROXY_STRATEGY_ENV = process.env.MIAW_PROXY_STRATEGY || undefined;
31
40
 
32
41
  /**
33
42
  * Parse CLI arguments
@@ -78,7 +87,10 @@ USAGE:
78
87
  GLOBAL FLAGS:
79
88
  --instance-id <id> Instance ID (default: "default")
80
89
  --session-path <path> Session directory
81
- --proxy <url> Proxy URL (http, https, socks4, socks5)
90
+ --proxy <url> Proxy URL (http/https/socks4/socks4a/socks5/socks5h)
91
+ --proxy-file <path> Proxy list file (.txt one-per-line, or .json array)
92
+ --proxy-strategy <strategy> Pick from --proxy-file: round-robin | random |
93
+ weighted | deterministic (default: deterministic)
82
94
  --json Output as JSON
83
95
  --debug Enable verbose logging
84
96
 
@@ -93,6 +105,7 @@ COMMANDS:
93
105
  profile Profile management (picture, name, status)
94
106
  label Label management - WhatsApp Business (list, chats, add, chat)
95
107
  catalog Catalog management - WhatsApp Business (list, collections, product)
108
+ proxy Inspect and test proxies (list, test, test-all) - no connection needed
96
109
 
97
110
  COMMON OPTIONS:
98
111
  --limit N Limit number of results
@@ -103,6 +116,8 @@ EXAMPLES:
103
116
  miaw-cli send text 6281234567890 "Hello"
104
117
  miaw-cli group participants add 120363xxx@g.us 628xxx
105
118
  miaw-cli contact add 6281234567890 "John Doe"
119
+ miaw-cli proxy test socks5://proxy.example.com:1080
120
+ miaw-cli --instance-id bot-3 --proxy-file ./proxies.txt get groups
106
121
 
107
122
  REPL MODE:
108
123
  Run 'miaw-cli' without arguments to start interactive mode.
@@ -132,8 +147,35 @@ async function main() {
132
147
  const jsonOutput = flags.json === true;
133
148
  const debugMode = flags.debug === true;
134
149
 
135
- // Extract proxy flag
136
- const proxyUrl = flags.proxy as string | undefined;
150
+ // Extract proxy flags (CLI flag wins over the MIAW_PROXY* env fallbacks)
151
+ const explicitProxy = (flags.proxy as string | undefined) || DEFAULT_PROXY;
152
+ const proxyFile = (flags["proxy-file"] as string | undefined) || DEFAULT_PROXY_FILE;
153
+
154
+ let proxyStrategy;
155
+ try {
156
+ proxyStrategy = parseProxyStrategy(
157
+ (flags["proxy-strategy"] as string | undefined) || DEFAULT_PROXY_STRATEGY_ENV
158
+ );
159
+ } catch (error: unknown) {
160
+ console.error(`❌ ${getErrorMessage(error)}`);
161
+ process.exit(1);
162
+ }
163
+
164
+ // Precedence: an explicit --proxy always wins; otherwise a --proxy-file
165
+ // selects one proxy for this process. The default strategy is
166
+ // deterministic so a given instanceId keeps a stable egress IP across
167
+ // invocations - round-robin here would silently rotate a live session's IP.
168
+ let proxyUrl = explicitProxy;
169
+ if (explicitProxy && proxyFile) {
170
+ console.log("⚠️ --proxy overrides --proxy-file");
171
+ } else if (!explicitProxy && proxyFile) {
172
+ try {
173
+ proxyUrl = await selectProxyForInstance(proxyFile, proxyStrategy, instanceId);
174
+ } catch (error: unknown) {
175
+ console.error(`❌ Failed to select a proxy from ${proxyFile}: ${getErrorMessage(error)}`);
176
+ process.exit(1);
177
+ }
178
+ }
137
179
 
138
180
  // Create client configuration
139
181
  const clientConfig = {
@@ -141,6 +183,8 @@ async function main() {
141
183
  sessionPath,
142
184
  debug: debugMode,
143
185
  ...(proxyUrl && { proxy: proxyUrl }),
186
+ ...(proxyFile && { proxyFile }),
187
+ proxyStrategy,
144
188
  };
145
189
 
146
190
  // No command provided - start REPL
@@ -148,7 +192,7 @@ async function main() {
148
192
  console.log(`\n🚀 Starting miaw-cli REPL...`);
149
193
  console.log(`📂 Instance: ${instanceId}`);
150
194
  console.log(`📂 Session: ${sessionPath}`);
151
- if (proxyUrl) console.log(`🌐 Proxy: ${proxyUrl}`);
195
+ if (proxyUrl) console.log(`🌐 Proxy: ${maskProxyUrl(proxyUrl)}`);
152
196
  console.log(`🔧 Debug: ${debugMode ? "ON" : "OFF"}\n`);
153
197
 
154
198
  try {
@@ -166,6 +210,8 @@ async function main() {
166
210
  clientConfig,
167
211
  jsonOutput,
168
212
  flags,
213
+ ...(proxyFile && { proxyFile }),
214
+ proxyStrategy,
169
215
  });
170
216
 
171
217
  if (!success) {
@@ -13,6 +13,7 @@ export { cmdCommunityList, cmdCommunityInfo, cmdCommunityCreate, cmdCommunityLea
13
13
  export { cmdMediaDownload } from "./media.js";
14
14
  export { cmdGroupList, cmdGroupInfo, cmdGroupParticipants, cmdGroupInviteLink, cmdGroupCreate, cmdGroupLeave, cmdGroupInviteAccept, cmdGroupInviteRevoke, cmdGroupInviteInfo, cmdGroupParticipantsAdd, cmdGroupParticipantsRemove, cmdGroupParticipantsPromote, cmdGroupParticipantsDemote, cmdGroupNameSet, cmdGroupDescriptionSet, cmdGroupPictureSet, } from "./group.js";
15
15
  export { cmdCheck, } from "./misc.js";
16
+ export { cmdProxyList, cmdProxyTest, cmdProxyTestAll, } from "./proxy.js";
16
17
  export { cmdContactList, cmdContactInfo, cmdContactBusiness, cmdContactPicture, cmdContactAdd, cmdContactRemove, } from "./contact.js";
17
18
  export { cmdProfilePictureSet, cmdProfilePictureRemove, cmdProfileNameSet, cmdProfileStatusSet, } from "./profile.js";
18
19
  export { cmdLabelAdd, cmdLabelChats, cmdLabelChatAdd, cmdLabelChatRemove, } from "./label.js";
@@ -1 +1 @@
1
- {"version":3,"file":"commands-index.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/commands-index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EACL,eAAe,EACf,iBAAiB,EACjB,iBAAiB,EACjB,iBAAiB,EACjB,kBAAkB,EAClB,qBAAqB,EACrB,iBAAiB,GAClB,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,aAAa,EACb,cAAc,EACd,YAAY,EACZ,WAAW,EACX,cAAc,EACd,mBAAmB,EACnB,YAAY,GACb,MAAM,UAAU,CAAC;AAElB,OAAO,EACL,WAAW,EACX,YAAY,EACZ,eAAe,EACf,YAAY,EACZ,YAAY,EACZ,eAAe,EACf,cAAc,EACd,WAAW,EACX,cAAc,GACf,MAAM,WAAW,CAAC;AAEnB,OAAO,EACL,cAAc,EACd,gBAAgB,EAChB,UAAU,EACV,YAAY,EACZ,WAAW,EACX,aAAa,EACb,WAAW,EACX,aAAa,EACb,YAAY,EACZ,aAAa,GACd,MAAM,WAAW,CAAC;AAEnB,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE5E,OAAO,EACL,kBAAkB,EAClB,mBAAmB,EACnB,sBAAsB,GACvB,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,gBAAgB,EAChB,gBAAgB,EAChB,kBAAkB,EAClB,iBAAiB,EACjB,mBAAmB,EACnB,0BAA0B,EAC1B,kBAAkB,EAClB,gBAAgB,EAChB,kBAAkB,EAClB,uBAAuB,EACvB,mBAAmB,EACnB,sBAAsB,EACtB,yBAAyB,EACzB,0BAA0B,EAC1B,yBAAyB,EACzB,sBAAsB,EACtB,wBAAwB,EACxB,wBAAwB,EACxB,sBAAsB,GACvB,MAAM,gBAAgB,CAAC;AAExB,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAE9C,OAAO,EACL,YAAY,EACZ,YAAY,EACZ,oBAAoB,EACpB,kBAAkB,EAClB,cAAc,EACd,aAAa,EACb,oBAAoB,EACpB,oBAAoB,EACpB,kBAAkB,EAClB,uBAAuB,EACvB,0BAA0B,EAC1B,2BAA2B,EAC3B,0BAA0B,EAC1B,eAAe,EACf,sBAAsB,EACtB,kBAAkB,GACnB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,QAAQ,GACT,MAAM,WAAW,CAAC;AAEnB,OAAO,EACL,cAAc,EACd,cAAc,EACd,kBAAkB,EAClB,iBAAiB,EACjB,aAAa,EACb,gBAAgB,GACjB,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,oBAAoB,EACpB,uBAAuB,EACvB,iBAAiB,EACjB,mBAAmB,GACpB,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,WAAW,EACX,aAAa,EACb,eAAe,EACf,kBAAkB,GACnB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,cAAc,EACd,qBAAqB,EACrB,uBAAuB,EACvB,uBAAuB,EACvB,uBAAuB,GACxB,MAAM,cAAc,CAAC"}
1
+ {"version":3,"file":"commands-index.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/commands-index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EACL,eAAe,EACf,iBAAiB,EACjB,iBAAiB,EACjB,iBAAiB,EACjB,kBAAkB,EAClB,qBAAqB,EACrB,iBAAiB,GAClB,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,aAAa,EACb,cAAc,EACd,YAAY,EACZ,WAAW,EACX,cAAc,EACd,mBAAmB,EACnB,YAAY,GACb,MAAM,UAAU,CAAC;AAElB,OAAO,EACL,WAAW,EACX,YAAY,EACZ,eAAe,EACf,YAAY,EACZ,YAAY,EACZ,eAAe,EACf,cAAc,EACd,WAAW,EACX,cAAc,GACf,MAAM,WAAW,CAAC;AAEnB,OAAO,EACL,cAAc,EACd,gBAAgB,EAChB,UAAU,EACV,YAAY,EACZ,WAAW,EACX,aAAa,EACb,WAAW,EACX,aAAa,EACb,YAAY,EACZ,aAAa,GACd,MAAM,WAAW,CAAC;AAEnB,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE5E,OAAO,EACL,kBAAkB,EAClB,mBAAmB,EACnB,sBAAsB,GACvB,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,gBAAgB,EAChB,gBAAgB,EAChB,kBAAkB,EAClB,iBAAiB,EACjB,mBAAmB,EACnB,0BAA0B,EAC1B,kBAAkB,EAClB,gBAAgB,EAChB,kBAAkB,EAClB,uBAAuB,EACvB,mBAAmB,EACnB,sBAAsB,EACtB,yBAAyB,EACzB,0BAA0B,EAC1B,yBAAyB,EACzB,sBAAsB,EACtB,wBAAwB,EACxB,wBAAwB,EACxB,sBAAsB,GACvB,MAAM,gBAAgB,CAAC;AAExB,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAE9C,OAAO,EACL,YAAY,EACZ,YAAY,EACZ,oBAAoB,EACpB,kBAAkB,EAClB,cAAc,EACd,aAAa,EACb,oBAAoB,EACpB,oBAAoB,EACpB,kBAAkB,EAClB,uBAAuB,EACvB,0BAA0B,EAC1B,2BAA2B,EAC3B,0BAA0B,EAC1B,eAAe,EACf,sBAAsB,EACtB,kBAAkB,GACnB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,QAAQ,GACT,MAAM,WAAW,CAAC;AAEnB,OAAO,EACL,YAAY,EACZ,YAAY,EACZ,eAAe,GAChB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,cAAc,EACd,cAAc,EACd,kBAAkB,EAClB,iBAAiB,EACjB,aAAa,EACb,gBAAgB,GACjB,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,oBAAoB,EACpB,uBAAuB,EACvB,iBAAiB,EACjB,mBAAmB,GACpB,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,WAAW,EACX,aAAa,EACb,eAAe,EACf,kBAAkB,GACnB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,cAAc,EACd,qBAAqB,EACrB,uBAAuB,EACvB,uBAAuB,EACvB,uBAAuB,GACxB,MAAM,cAAc,CAAC"}
@@ -13,6 +13,7 @@ export { cmdCommunityList, cmdCommunityInfo, cmdCommunityCreate, cmdCommunityLea
13
13
  export { cmdMediaDownload } from "./media.js";
14
14
  export { cmdGroupList, cmdGroupInfo, cmdGroupParticipants, cmdGroupInviteLink, cmdGroupCreate, cmdGroupLeave, cmdGroupInviteAccept, cmdGroupInviteRevoke, cmdGroupInviteInfo, cmdGroupParticipantsAdd, cmdGroupParticipantsRemove, cmdGroupParticipantsPromote, cmdGroupParticipantsDemote, cmdGroupNameSet, cmdGroupDescriptionSet, cmdGroupPictureSet, } from "./group.js";
15
15
  export { cmdCheck, } from "./misc.js";
16
+ export { cmdProxyList, cmdProxyTest, cmdProxyTestAll, } from "./proxy.js";
16
17
  export { cmdContactList, cmdContactInfo, cmdContactBusiness, cmdContactPicture, cmdContactAdd, cmdContactRemove, } from "./contact.js";
17
18
  export { cmdProfilePictureSet, cmdProfilePictureRemove, cmdProfileNameSet, cmdProfileStatusSet, } from "./profile.js";
18
19
  export { cmdLabelAdd, cmdLabelChats, cmdLabelChatAdd, cmdLabelChatRemove, } from "./label.js";
@@ -1 +1 @@
1
- {"version":3,"file":"commands-index.js","sourceRoot":"","sources":["../../../src/cli/commands/commands-index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EACL,eAAe,EACf,iBAAiB,EACjB,iBAAiB,EACjB,iBAAiB,EACjB,kBAAkB,EAClB,qBAAqB,EACrB,iBAAiB,GAClB,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,aAAa,EACb,cAAc,EACd,YAAY,EACZ,WAAW,EACX,cAAc,EACd,mBAAmB,EACnB,YAAY,GACb,MAAM,UAAU,CAAC;AAElB,OAAO,EACL,WAAW,EACX,YAAY,EACZ,eAAe,EACf,YAAY,EACZ,YAAY,EACZ,eAAe,EACf,cAAc,EACd,WAAW,EACX,cAAc,GACf,MAAM,WAAW,CAAC;AAEnB,OAAO,EACL,cAAc,EACd,gBAAgB,EAChB,UAAU,EACV,YAAY,EACZ,WAAW,EACX,aAAa,EACb,WAAW,EACX,aAAa,EACb,YAAY,EACZ,aAAa,GACd,MAAM,WAAW,CAAC;AAEnB,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE5E,OAAO,EACL,kBAAkB,EAClB,mBAAmB,EACnB,sBAAsB,GACvB,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,gBAAgB,EAChB,gBAAgB,EAChB,kBAAkB,EAClB,iBAAiB,EACjB,mBAAmB,EACnB,0BAA0B,EAC1B,kBAAkB,EAClB,gBAAgB,EAChB,kBAAkB,EAClB,uBAAuB,EACvB,mBAAmB,EACnB,sBAAsB,EACtB,yBAAyB,EACzB,0BAA0B,EAC1B,yBAAyB,EACzB,sBAAsB,EACtB,wBAAwB,EACxB,wBAAwB,EACxB,sBAAsB,GACvB,MAAM,gBAAgB,CAAC;AAExB,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAE9C,OAAO,EACL,YAAY,EACZ,YAAY,EACZ,oBAAoB,EACpB,kBAAkB,EAClB,cAAc,EACd,aAAa,EACb,oBAAoB,EACpB,oBAAoB,EACpB,kBAAkB,EAClB,uBAAuB,EACvB,0BAA0B,EAC1B,2BAA2B,EAC3B,0BAA0B,EAC1B,eAAe,EACf,sBAAsB,EACtB,kBAAkB,GACnB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,QAAQ,GACT,MAAM,WAAW,CAAC;AAEnB,OAAO,EACL,cAAc,EACd,cAAc,EACd,kBAAkB,EAClB,iBAAiB,EACjB,aAAa,EACb,gBAAgB,GACjB,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,oBAAoB,EACpB,uBAAuB,EACvB,iBAAiB,EACjB,mBAAmB,GACpB,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,WAAW,EACX,aAAa,EACb,eAAe,EACf,kBAAkB,GACnB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,cAAc,EACd,qBAAqB,EACrB,uBAAuB,EACvB,uBAAuB,EACvB,uBAAuB,GACxB,MAAM,cAAc,CAAC"}
1
+ {"version":3,"file":"commands-index.js","sourceRoot":"","sources":["../../../src/cli/commands/commands-index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EACL,eAAe,EACf,iBAAiB,EACjB,iBAAiB,EACjB,iBAAiB,EACjB,kBAAkB,EAClB,qBAAqB,EACrB,iBAAiB,GAClB,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,aAAa,EACb,cAAc,EACd,YAAY,EACZ,WAAW,EACX,cAAc,EACd,mBAAmB,EACnB,YAAY,GACb,MAAM,UAAU,CAAC;AAElB,OAAO,EACL,WAAW,EACX,YAAY,EACZ,eAAe,EACf,YAAY,EACZ,YAAY,EACZ,eAAe,EACf,cAAc,EACd,WAAW,EACX,cAAc,GACf,MAAM,WAAW,CAAC;AAEnB,OAAO,EACL,cAAc,EACd,gBAAgB,EAChB,UAAU,EACV,YAAY,EACZ,WAAW,EACX,aAAa,EACb,WAAW,EACX,aAAa,EACb,YAAY,EACZ,aAAa,GACd,MAAM,WAAW,CAAC;AAEnB,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE5E,OAAO,EACL,kBAAkB,EAClB,mBAAmB,EACnB,sBAAsB,GACvB,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,gBAAgB,EAChB,gBAAgB,EAChB,kBAAkB,EAClB,iBAAiB,EACjB,mBAAmB,EACnB,0BAA0B,EAC1B,kBAAkB,EAClB,gBAAgB,EAChB,kBAAkB,EAClB,uBAAuB,EACvB,mBAAmB,EACnB,sBAAsB,EACtB,yBAAyB,EACzB,0BAA0B,EAC1B,yBAAyB,EACzB,sBAAsB,EACtB,wBAAwB,EACxB,wBAAwB,EACxB,sBAAsB,GACvB,MAAM,gBAAgB,CAAC;AAExB,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAE9C,OAAO,EACL,YAAY,EACZ,YAAY,EACZ,oBAAoB,EACpB,kBAAkB,EAClB,cAAc,EACd,aAAa,EACb,oBAAoB,EACpB,oBAAoB,EACpB,kBAAkB,EAClB,uBAAuB,EACvB,0BAA0B,EAC1B,2BAA2B,EAC3B,0BAA0B,EAC1B,eAAe,EACf,sBAAsB,EACtB,kBAAkB,GACnB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,QAAQ,GACT,MAAM,WAAW,CAAC;AAEnB,OAAO,EACL,YAAY,EACZ,YAAY,EACZ,eAAe,GAChB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,cAAc,EACd,cAAc,EACd,kBAAkB,EAClB,iBAAiB,EACjB,aAAa,EACb,gBAAgB,GACjB,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,oBAAoB,EACpB,uBAAuB,EACvB,iBAAiB,EACjB,mBAAmB,GACpB,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,WAAW,EACX,aAAa,EACb,eAAe,EACf,kBAAkB,GACnB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,cAAc,EACd,qBAAqB,EACrB,uBAAuB,EACvB,uBAAuB,EACvB,uBAAuB,GACxB,MAAM,cAAc,CAAC"}
@@ -8,11 +8,17 @@ export interface CommandContext {
8
8
  instanceId: string;
9
9
  sessionPath: string;
10
10
  debug?: boolean;
11
+ /** Set by bin/miaw-cli.ts from --proxy or a --proxy-file selection. */
12
+ proxy?: string;
11
13
  };
12
14
  jsonOutput?: boolean;
13
15
  flags?: {
14
16
  [key: string]: string | boolean;
15
17
  };
18
+ /** Proxy list file from --proxy-file, for the `proxy` commands. */
19
+ proxyFile?: string;
20
+ /** Selection strategy from --proxy-strategy. */
21
+ proxyStrategy?: string;
16
22
  }
17
23
  /**
18
24
  * Run a command
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAoHH,MAAM,WAAW,cAAc;IAC7B,YAAY,EAAE;QACZ,UAAU,EAAE,MAAM,CAAC;QACnB,WAAW,EAAE,MAAM,CAAC;QACpB,KAAK,CAAC,EAAE,OAAO,CAAC;KACjB,CAAC;IACF,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,KAAK,CAAC,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAA;KAAE,CAAC;CAC7C;AAED;;GAEG;AACH,wBAAsB,UAAU,CAC9B,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EAAE,EACd,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,OAAO,GAAG;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CA2+BpE"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAyHH,MAAM,WAAW,cAAc;IAC7B,YAAY,EAAE;QACZ,UAAU,EAAE,MAAM,CAAC;QACnB,WAAW,EAAE,MAAM,CAAC;QACpB,KAAK,CAAC,EAAE,OAAO,CAAC;QAChB,uEAAuE;QACvE,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC;IACF,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,KAAK,CAAC,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAA;KAAE,CAAC;IAC5C,mEAAmE;IACnE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gDAAgD;IAChD,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;GAEG;AACH,wBAAsB,UAAU,CAC9B,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EAAE,EACd,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,OAAO,GAAG;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CA2hCpE"}
@@ -33,7 +33,10 @@ cmdProfilePictureSet, cmdProfilePictureRemove, cmdProfileNameSet, cmdProfileStat
33
33
  // Label commands (Business)
34
34
  cmdLabelAdd, cmdLabelChats, cmdLabelChatAdd, cmdLabelChatRemove,
35
35
  // Catalog commands (Business)
36
- cmdCatalogList, cmdCatalogCollections, cmdCatalogProductCreate, cmdCatalogProductUpdate, cmdCatalogProductDelete, } from "./commands-index.js";
36
+ cmdCatalogList, cmdCatalogCollections, cmdCatalogProductCreate, cmdCatalogProductUpdate, cmdCatalogProductDelete,
37
+ // Proxy commands (no connection required)
38
+ cmdProxyList, cmdProxyTest, cmdProxyTestAll, } from "./commands-index.js";
39
+ import { resolveProxyFile } from "../utils/proxy-config.js";
37
40
  /**
38
41
  * Run a command
39
42
  */
@@ -93,6 +96,46 @@ export async function runCommand(command, args, context) {
93
96
  return false;
94
97
  }
95
98
  }
99
+ // Proxy commands (don't require connection - they probe the proxy directly).
100
+ // Must stay ABOVE getOrCreateClient() so no client is ever constructed.
101
+ if (command === "proxy") {
102
+ const subCommand = parsedArgs._[0] || "";
103
+ const subArgs = parsedArgs._.slice(1);
104
+ const proxyFile = resolveProxyFile(parsedArgs, context.flags, context.proxyFile);
105
+ // Flags arrive from two different places: one-shot mode strips every
106
+ // --flag in bin/miaw-cli.ts before calling runCommand (so they land in
107
+ // context.flags), while the REPL passes the raw tokenized line (so they
108
+ // land in parsedArgs). Both must be read or the flag silently no-ops.
109
+ const showIp = parsedArgs.ip === true || context.flags?.ip === true;
110
+ const rawTimeout = parsedArgs.timeout ?? context.flags?.timeout;
111
+ const timeoutMs = typeof rawTimeout === "number"
112
+ ? rawTimeout
113
+ : typeof rawTimeout === "string" && /^\d+$/.test(rawTimeout)
114
+ ? Number(rawTimeout)
115
+ : undefined;
116
+ const probeOptions = {
117
+ showIp,
118
+ ...(timeoutMs !== undefined && { timeoutMs }),
119
+ };
120
+ switch (subCommand) {
121
+ case "ls":
122
+ case "list":
123
+ return await cmdProxyList(proxyFile, jsonOutput);
124
+ case "test":
125
+ return await cmdProxyTest(subArgs[0] || context.flags?.proxy || clientConfig.proxy, probeOptions, jsonOutput);
126
+ case "test-all":
127
+ return await cmdProxyTestAll(proxyFile, probeOptions, jsonOutput);
128
+ default:
129
+ if (!subCommand) {
130
+ console.log("Usage: proxy <command>");
131
+ }
132
+ else {
133
+ console.log(`❌ Unknown proxy command: ${subCommand}`);
134
+ }
135
+ console.log("Commands: list, test, test-all");
136
+ return false;
137
+ }
138
+ }
96
139
  // Create or get cached client for commands that need connection
97
140
  const client = getOrCreateClient(clientConfig);
98
141
  // Get commands
@@ -940,7 +983,7 @@ export async function runCommand(command, args, context) {
940
983
  * Parse command arguments.
941
984
  * Only auto-converts known numeric flags to numbers; all others stay as strings.
942
985
  */
943
- const NUMERIC_FLAGS = new Set(["limit", "count", "cursor"]);
986
+ const NUMERIC_FLAGS = new Set(["limit", "count", "cursor", "timeout"]);
944
987
  function parseCommandArgs(args) {
945
988
  const parsed = { _: [] };
946
989
  for (let i = 0; i < args.length; i++) {