Skip to content

fix(whatsapp): render compact pairing QR and split gateway diagnostics#4607

Merged
cv merged 3 commits into
NVIDIA:mainfrom
yimoj:fix/4522-compact-whatsapp-qr
Jun 1, 2026
Merged

fix(whatsapp): render compact pairing QR and split gateway diagnostics#4607
cv merged 3 commits into
NVIDIA:mainfrom
yimoj:fix/4522-compact-whatsapp-qr

Conversation

@yimoj
Copy link
Copy Markdown
Contributor

@yimoj yimoj commented Jun 1, 2026

Summary

NemoClaw delegated in-sandbox WhatsApp QR pairing to the upstream @openclaw/whatsapp plugin, which renders the Linked-Devices QR at full size — on DGX Spark terminals it filled 50–80+ rows and hundreds of columns and could not be scanned in one phone-camera frame, and the reported 1008 abnormal closure surfaced as raw gateway text with no separation from the QR problem. This PR makes the NemoClaw-supported pairing path render a compact QR and diagnose gateway close/error conditions separately from QR rendering.

Related Issue

Fixes #4522

Changes

  • Add a NemoClaw-owned Node preload (nemoclaw-blueprint/scripts/whatsapp-qr-compact.js) that forces qrcode-terminal into the same { small: true } half-block mode the host-side WeChat QR path already uses (src/ext/wechat/login.ts), roughly quartering the rendered area without changing the QR payload. It hooks Module._load so the plugin's nested copy is patched and is idempotent.
  • Install the preload only when WhatsApp is configured, and inject it for the single openclaw channels login --channel whatsapp invocation via the sandbox openclaw() guard — the gateway never renders the pairing QR, so it stays off global NODE_OPTIONS. The preload is validated under validate_tmp_permissions (root:444) so the sandbox user cannot tamper with a NODE_OPTIONS-injected file.
  • Validate OPENCLAW_GATEWAY_URL before pairing, and on a non-zero login exit print retry/diagnostic guidance that distinguishes a gateway close (e.g. 1008) from a QR-size issue.
  • Update the WhatsApp channel help text to describe the compact, gateway-validated pairing path.
  • Add unit tests for the renderer (forces small, bounds rendered lines, idempotent) and the guard branch (gateway preflight, preload injection, close diagnostics), plus a live e2e check (M-WA6b/M-WA6c) that the preload + guard wiring land in the sandbox.
  • The preload carries a documented removal criterion for when upstream renders a scan-friendly QR by default.

Type of Change

  • Code change (feature, bug fix, or refactor)
  • Code change with doc updates
  • Doc only (prose changes, no code sample modifications)
  • Doc only (includes code sample changes)

Verification

  • npm test passes (cli + plugin projects; the only failures were pre-existing 5000ms timeouts in test/e2e-scenario/framework-tests/* that spawn real bash and flake under parallel load — they pass in isolation and are unrelated to this change)
  • Tests added or updated for new or changed behavior
  • No secrets, API keys, or credentials committed
  • Targeted: test/whatsapp-qr-compact.test.ts, src/lib/sandbox/channels.test.ts, src/lib/sandbox/whatsapp-diagnostics.test.ts, test/nemoclaw-start.test.ts, npm run typecheck:cli — all green

Live note: no live WhatsApp sandbox/account was available, so the compact-QR rendering and gateway diagnostics are validated by hermetic tests around the renderer and the sandbox guard; the live channels login --channel whatsapp scan was not exercised end-to-end. The compact renderer assumes the bundled plugin uses qrcode-terminal (the dominant renderer, same lib as the WeChat path); the gateway validation/diagnostics apply regardless.


Signed-off-by: Yimo Jiang yimoj@nvidia.com

Summary by CodeRabbit

  • New Features

    • WhatsApp pairing now displays compact, scan‑friendly QR codes; startup tooling can install a sandbox preload to enforce compact rendering during login and validates the gateway before pairing.
  • Documentation

    • Updated WhatsApp channel help text with revised login instructions and pairing flow guidance.
  • Tests

    • Added e2e and unit tests covering compact‑QR rendering, preload installation/wiring, and the WhatsApp login guard behavior.

In-sandbox WhatsApp pairing delegated QR rendering to the upstream
@openclaw/whatsapp plugin, which draws the Linked-Devices QR at full size
via qrcode-terminal. On DGX Spark terminals the code filled 50-80+ rows and
hundreds of columns, so it could not be captured in one phone-camera frame.
The reported "1008 abnormal closure" then surfaced as raw gateway text with
no separation from the QR problem.

NemoClaw owns the user-facing pairing workflow, so wrap the supported path:

- Add a NemoClaw-owned preload (whatsapp-qr-compact.js) that forces
  qrcode-terminal into the same `{ small: true }` half-block mode the
  host-side WeChat QR path already uses, quartering the rendered area
  without changing the payload. It hooks Module._load so the plugin's
  nested copy is patched, and is idempotent.
- Install it only when WhatsApp is configured and inject it for the single
  `openclaw channels login --channel whatsapp` invocation via the sandbox
  guard — the gateway never renders the QR, so it stays off global
  NODE_OPTIONS. Validate it under validate_tmp_permissions (root:444).
- Validate OPENCLAW_GATEWAY_URL before pairing and, on a non-zero login
  exit, print retry/diagnostic guidance that distinguishes a gateway close
  (e.g. 1008) from a QR-size issue.

Add a unit test for the renderer (forces small, bounds lines, idempotent)
and the guard branch (gateway preflight, preload injection, close
diagnostics), plus a live e2e check that the preload + guard wiring land in
the sandbox. Carries a documented removal criterion for when upstream
renders a scan-friendly QR by default.

Fixes NVIDIA#4522

Signed-off-by: Yimo Jiang <yimoj@nvidia.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jun 1, 2026

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 0d5b9bb8-617b-4fda-beaf-32ecd45644ee

📥 Commits

Reviewing files that changed from the base of the PR and between e4f0bed and 477f790.

📒 Files selected for processing (2)
  • scripts/nemoclaw-start.sh
  • test/whatsapp-qr-compact.test.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • scripts/nemoclaw-start.sh
  • test/whatsapp-qr-compact.test.ts

📝 Walkthrough

Walkthrough

Adds a Node.js preload to force compact WhatsApp QR rendering, installs and optionally injects the preload into the openclaw login guard, updates sandbox help text, and adds unit/e2e tests validating rendering, idempotency, and guard wiring.

Changes

WhatsApp Compact QR Pairing

Layer / File(s) Summary
Compact QR Preload Implementation
nemoclaw-blueprint/scripts/whatsapp-qr-compact.js
Node preload intercepts qrcode-terminal via Module._load, wraps generate to force opts.small = true, preserves other options, supports both (text, cb) and (text, opts, cb) signatures, is idempotent, and fails softly by returning the original module on error.
Startup installer & openclaw login guard
scripts/nemoclaw-start.sh
Defines /tmp and packaged preload paths and adds install_whatsapp_qr_compact() to emit a /tmp preload when WhatsApp is configured; extends openclaw channels login guard to validate OPENCLAW_GATEWAY_URL, print pairing instructions, and conditionally inject NODE_OPTIONS --require /tmp/nemoclaw-whatsapp-qr-compact.js; registers installer and adds the /tmp path to the trusted temporary-file allowlist for root and non-root startup flows.
WhatsApp Help Text Update
src/lib/sandbox/channels.ts
Revises the whatsapp channel help to instruct connecting to the sandbox before running OpenClaw/Hermes login, and to note NemoClaw's compact QR rendering and gateway validation, and clarifies gateway-close error reporting.
Unit tests: Preload Behavior and Probe
test/whatsapp-qr-compact.test.ts
Adds deterministic WhatsApp payload, an inline Node probe rendering via qrcode-terminal, and Vitest assertions comparing baseline vs preloaded rendering (option shapes, enforced small:true, line-count ceiling, idempotency).
Guard extraction harness tests
test/whatsapp-qr-compact.test.ts
Extracts the openclaw login guard body and exercises it inside a generated sandbox wrapper that fakes openclaw, controls OPENCLAW_GATEWAY_URL, conditionally stubs the preload, and asserts channel/env/preload behaviors and diagnostic separation.
E2E checks: preload presence & proxy env wiring
test/e2e/test-messaging-providers.sh
Adds M-WA6b to assert /tmp/nemoclaw-whatsapp-qr-compact.js exists with owner/mode root:444, and M-WA6c to verify NODE_OPTIONS --require $_whatsapp_qr_compact is present in /tmp/nemoclaw-proxy-env.sh.

Sequence Diagram

sequenceDiagram
  participant Startup as NemoClaw Startup
  participant InstallFunc as install_whatsapp_qr_compact()
  participant TmpPreload as /tmp/nemoclaw-whatsapp-qr-compact.js
  participant OpenClawGuard as openclaw channels login guard
  participant NodeRuntime as spawned openclaw process (NODE_OPTIONS)
  participant QRLibrary as qrcode-terminal
  Startup->>InstallFunc: call during service init
  InstallFunc->>TmpPreload: create preload file in /tmp
  OpenClawGuard->>OpenClawGuard: validate OPENCLAW_GATEWAY_URL
  OpenClawGuard->>NodeRuntime: set NODE_OPTIONS --require /tmp/...
  NodeRuntime->>QRLibrary: require('qrcode-terminal') (preload intercepts)
  QRLibrary->>NodeRuntime: generate() enforced with small:true
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 A tiny preload hopped in place,
Making QR codes fit the space,
Small squares snug so phones can scan,
Pairing done with a happy fan! 📸✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 12.50% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main changes: rendering compact QR codes for WhatsApp pairing and separating gateway diagnostics from QR-related output.
Linked Issues check ✅ Passed Changes comprehensively address issue #4522: compact QR rendering via preload reduces terminal lines to ~30-40, and gateway diagnostics are separated and clearly presented with retry guidance.
Out of Scope Changes check ✅ Passed All changes directly support the linked objectives: preload script and injection logic for compact QR, gateway URL validation, help text updates, and comprehensive test coverage for both features.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@test/e2e/test-messaging-providers.sh`:
- Around line 919-924: The current check uses whatsapp_qr_guard_wiring and only
greps for 'nemoclaw-whatsapp-qr-compact.js' which can miss whether NODE_OPTIONS
actually injects it; update the assertion to grep for the exact require flag
pattern (e.g., "--require $_whatsapp_qr_compact" or the resolved value of
_whatsapp_qr_compact) via sandbox_exec so the test verifies the actual --require
injection string, adjust the variable used (whatsapp_qr_guard_wiring) and the
subsequent conditional message to reflect the check for the --require injection
rather than just the filename.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: c228f084-608a-4055-b508-f89bfb45772f

📥 Commits

Reviewing files that changed from the base of the PR and between df7d054 and fe47c3c.

📒 Files selected for processing (5)
  • nemoclaw-blueprint/scripts/whatsapp-qr-compact.js
  • scripts/nemoclaw-start.sh
  • src/lib/sandbox/channels.ts
  • test/e2e/test-messaging-providers.sh
  • test/whatsapp-qr-compact.test.ts

Comment thread test/e2e/test-messaging-providers.sh Outdated
@yimoj
Copy link
Copy Markdown
Contributor Author

yimoj commented Jun 1, 2026

@coderabbitai full review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jun 1, 2026

✅ Actions performed

Full review triggered.

CodeRabbit flagged that the M-WA6c guard-wiring check grepped only for the
preload filename, which also appears in the install banner and the literal
path assignment — so it would pass even if the NODE_OPTIONS `--require`
injection regressed. Grep the emitted proxy-env file for the literal
`--require $_whatsapp_qr_compact` token so the test pins the actual injection.

Signed-off-by: Yimo Jiang <yimoj@nvidia.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@scripts/nemoclaw-start.sh`:
- Around line 1991-1999: The current guard only checks OPENCLAW_GATEWAY_URL is
non-empty; add a validation step before calling openclaw (where openclaw
channels login is used) to reject malformed or local gateway URLs. After the
existing empty check, validate OPENCLAW_GATEWAY_URL with a shell-safe regex
(e.g. require a scheme like http:// or https:// and a non-local host), and also
reject localhost/127.0.0.1 variants; on failure print a clear error (same style
as existing messages) and return 1 so openclaw is never invoked with a bad URL.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: ef067405-7d5d-4b8e-bfbb-9a9ee7e3ad0e

📥 Commits

Reviewing files that changed from the base of the PR and between df7d054 and fe47c3c.

📒 Files selected for processing (5)
  • nemoclaw-blueprint/scripts/whatsapp-qr-compact.js
  • scripts/nemoclaw-start.sh
  • src/lib/sandbox/channels.ts
  • test/e2e/test-messaging-providers.sh
  • test/whatsapp-qr-compact.test.ts

Comment thread scripts/nemoclaw-start.sh
CodeRabbit noted the gateway preflight only checked OPENCLAW_GATEWAY_URL for
non-emptiness, so a clobbered value (e.g. `foo` or an `http://` typo) would
still reach `openclaw channels login` and fail inside the login as the same
ambiguous close this guard exists to separate from QR rendering.

Validate the scheme up front: the OpenClaw gateway is a WebSocket endpoint
(set to ws://127.0.0.1:<port> at boot), so accept only ws:// and wss:// and
reject anything else with a gateway/env diagnostic. (CodeRabbit's literal
suggestion to require http(s):// and reject 127.0.0.1 would have rejected the
real local ws:// gateway URL, so the scheme check is adapted to this stack.)
Add parametrized guard tests covering rejected and accepted schemes.

Signed-off-by: Yimo Jiang <yimoj@nvidia.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@yimoj yimoj added the v0.0.56 Release target label Jun 1, 2026
@sandl99 sandl99 added the enhancement: messaging Enhancements related to messing support including Slack, Telegram, Discord and WhatsApp. label Jun 1, 2026
@wscurran wscurran added fix Integration: WhatsApp Use this label to identify WhatsApp communication integration issues with NemoClaw. NemoClaw CLI Use this label to identify issues with the NemoClaw command-line interface (CLI). Platform: DGX Spark Support for DGX Spark priority: high Important issue that should be resolved in the next release labels Jun 1, 2026
@wscurran
Copy link
Copy Markdown
Contributor

wscurran commented Jun 1, 2026

@wscurran wscurran removed the priority: high Important issue that should be resolved in the next release label Jun 1, 2026
@cv cv added v0.0.57 Release target and removed v0.0.56 Release target labels Jun 1, 2026
@cv cv merged commit 6ad8553 into NVIDIA:main Jun 1, 2026
30 checks passed
miyoungc added a commit that referenced this pull request Jun 1, 2026
## Summary

- Adds the v0.0.56 release notes section with links to the deeper docs
pages for installer, status, inference, messaging, policy, and lifecycle
changes.
- Updates source docs for the remaining release-prep gaps around `uv` in
the PyPI preset, compact WhatsApp pairing guidance, and `nemoclaw
inference set` command boundaries.
- Refreshes generated `nemoclaw-user-*` skills and removes skipped
experimental command terms from generated skill surfaces.

## Source summary

- #4613 -> `docs/manage-sandboxes/lifecycle.mdx`,
`docs/reference/commands.mdx`, `docs/about/release-notes.mdx`: Documents
that public installs and `nemoclaw update` follow the maintained `lkg`
tag by default.
- #4419 -> `docs/about/release-notes.mdx`: Notes that non-interactive
Linux installs can reactivate Docker group membership and continue in
one installer run when `sg docker` is available.
- #4550 -> `docs/reference/commands.mdx`,
`docs/about/release-notes.mdx`: Captures live sandbox agent-version
probing for status, connect, and upgrade checks.
- #4609 -> `docs/inference/use-local-inference.mdx`,
`docs/about/release-notes.mdx`: Captures the GPU Docker-driver
host-network local-inference reachability gate.
- #4607 -> `docs/manage-sandboxes/messaging-channels.mdx`,
`docs/reference/commands.mdx`, `docs/about/release-notes.mdx`: Documents
compact WhatsApp QR pairing guidance and gateway/session diagnostics.
- #4582 -> `docs/manage-sandboxes/messaging-channels.mdx`,
`docs/reference/commands.mdx`, `docs/about/release-notes.mdx`: Reflects
Slack credential validation before enabling the channel.
- #4554 -> `docs/manage-sandboxes/messaging-channels.mdx`,
`docs/reference/troubleshooting.mdx`, `docs/about/release-notes.mdx`:
Keeps Telegram allowlist alias guidance in the generated user skills and
release notes.
- #4563 -> `docs/reference/commands.mdx`,
`docs/about/release-notes.mdx`: Includes the new `nemoclaw <name> skill
remove <skill>` command in command docs and release notes.
- #4566 -> `docs/reference/commands.mdx`,
`docs/about/release-notes.mdx`: Documents the `nemoclaw inference set`
redirect boundary when `--provider` or `--model` is missing.
- #4323 -> `docs/reference/commands.mdx`,
`docs/about/release-notes.mdx`: Captures per-sandbox status JSON
support.
- #4506 -> `docs/reference/commands.mdx`,
`docs/about/release-notes.mdx`: Captures debug command sandbox-name
validation and safer tarball writing.
- #4569 -> `docs/network-policy/integration-policy-examples.mdx`,
`docs/about/release-notes.mdx`: Documents that the `pypi` preset allows
`/usr/local/bin/uv`.
- #4579 -> `docs/network-policy/integration-policy-examples.mdx`,
`docs/about/release-notes.mdx`: Captures observable Jira preset
validation guidance.
- #4229 -> `docs/manage-sandboxes/lifecycle.mdx`,
`docs/reference/commands.mdx`, `docs/about/release-notes.mdx`: Documents
user-data preservation defaults for uninstall.
- #4399 -> `docs/reference/commands.mdx`,
`docs/about/release-notes.mdx`: Captures CPU-only sandbox intent
preservation across rebuilds.
- #4058 -> `docs/reference/commands.mdx`,
`docs/about/release-notes.mdx`: Captures safer snapshot restore behavior
around existing destinations.
- #4155 and #4460 -> skipped by `docs/.docs-skip`: Removed skipped
experimental command terms from source docs and generated skill evals
instead of documenting those features.

## Verification

- `python3 scripts/docs-to-skills.py docs/ .agents/skills/ --prefix
nemoclaw-user --doc-platform fern-mdx`
- `npm run docs` (passes; Fern reports the pre-existing light-mode
accent contrast warning)
- `rg "permissive mode|shields down|shields up|shields status|config
rotate-token|rotate-token" .agents/skills` (no matches)
- `npm run build:cli` (run to refresh local CLI artifacts for the
pre-push TypeScript hook)
- Commit hooks passed, including `NEMOCLAW_* env-var documentation
gate`, `Verify docs-to-skills output`, `markdownlint-cli2`, `gitleaks`,
and `Test (skills YAML)`.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Documentation**
* Expanded Model Router setup with YAML examples, flow diagrams, and
credential handling; strengthened agent-config immutability and
integrity guidance; messaging channels updated (Telegram aliases,
WhatsApp pairing/diagnostics); CLI docs revised (GPU detection,
inference set behavior, uninstall/rebuild preservation); overview
rebranded to NemoClaw and added v0.0.56 release notes.

* **New Features**
* Added `nemoclaw <name> channels status` (messaging diagnostics, JSON);
added `nemoclaw <name> skill remove`; Hermes no longer marked
experimental; DGX Spark quickstart sandbox-name note.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement: messaging Enhancements related to messing support including Slack, Telegram, Discord and WhatsApp. fix Integration: WhatsApp Use this label to identify WhatsApp communication integration issues with NemoClaw. NemoClaw CLI Use this label to identify issues with the NemoClaw command-line interface (CLI). Platform: DGX Spark Support for DGX Spark v0.0.57 Release target

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[DGX Spark][CLI&UX] WhatsApp QR code renders too large in terminal — fills entire screen, impossible to scan

4 participants