Skip to content

feat(wecom): add CLI integration - #300

Merged
alwaysmavs merged 3 commits into
mainfrom
codex/add-wecom-cli
Aug 3, 2026
Merged

feat(wecom): add CLI integration#300
alwaysmavs merged 3 commits into
mainfrom
codex/add-wecom-cli

Conversation

@alwaysmavs

Copy link
Copy Markdown
Contributor

Summary

Wanta already supported a local direct connection through the Lark CLI, but users who work in WeCom had no equivalent way to authorize the official WeCom CLI and make its tools available to the local agent. This change adds a dedicated WeCom CLI connection while keeping its QR-code workflow and bot identity model separate from Lark's authorization experience.

User impact

Users can now open Connections, select WeCom CLI, scan the enterprise WeChat QR code, and use the capabilities granted to the resulting WeCom bot. The connection page exposes progress, cancellation, QR-code reopening, runtime version, and disconnect controls. It uses the official WeCom brand icon rather than the consumer WeChat icon.

Root cause and implementation

The application previously had no bundled WeCom runtime, isolated credential directory, IPC lifecycle, renderer provider model, or agent-side Skill installation. The new WecomCliManager runs the pinned official @wecom/cli binary with a Wanta-owned config and temporary-data root, allowlists the official QR-code URL, redacts credential-bearing output, and exposes only safe connection metadata to the renderer.

The build now downloads and verifies the platform-specific CLI package, exports the matching official Skills from the pinned source commit, and packages both resources. The agent workspace installs Lark and WeCom direct-mode Skills independently, injects the managed binary and isolated paths, and removes inherited WeCom logging overrides that could redirect credential-adjacent diagnostics.

The Connections UI routes each direct provider through its own hook and behavior. Shared presentation components gained only the small extension points required for provider-specific labels, reconnect behavior, QR reopening, and direct-provider actions.

Validation

  • pnpm lint
  • pnpm build:app
  • pnpm test — 289 test files and 2,164 tests passed during implementation
  • pnpm prepare:binaries
  • Verified the bundled binary reports wecom-cli 0.1.9
  • Verified all seven pinned WeCom Skills are exported and installed into the private agent workspace

The unrelated untracked docs/research/ directory is intentionally excluded from this PR.

@alwaysmavs
alwaysmavs marked this pull request as ready for review August 3, 2026 08:39
@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d0bab2cd-6996-46b7-815b-6c4c41c5c2c8

📥 Commits

Reviewing files that changed from the base of the PR and between ab37596 and 6a0fb6b.

📒 Files selected for processing (2)
  • scripts/wecom-cli.test.ts
  • scripts/wecom-cli.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • scripts/wecom-cli.test.ts

Summary by CodeRabbit

  • New Features
    • Added WeCom CLI integration with secure distribution and bundled skills.
    • Added QR-code authorization, connection status, reconnect, disconnect, cancellation, and authorization reopening.
    • Added WeCom connection management to desktop and mobile connection views.
    • Added English and Simplified Chinese translations for the WeCom connection flow.
  • Bug Fixes
    • Improved connection labels and reconnect availability across providers.
    • Added safer handling and redaction of CLI output and authorization URLs.
  • Documentation
    • Documented WeCom CLI architecture, security, configuration, and runtime behavior.

Walkthrough

This change adds WeCom CLI support across distribution, runtime, Agent integration, and Connections UI. The build downloads pinned platform binaries and Skills with validation and integrity checks. The Electron runtime manages QR authorization, isolated credentials, connection state, cancellation, and redacted output. The Agent receives WeCom paths and direct Skills. The UI exposes WeCom connection actions, status phases, polling reopening, localized labels, and provider details.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant ConnectionsUI
  participant LinkRuntimeService
  participant WecomCliManager
  participant WeComCLI
  User->>ConnectionsUI: Start WeCom connection
  ConnectionsUI->>LinkRuntimeService: connectWecomCli()
  LinkRuntimeService->>WecomCliManager: connect()
  WecomCliManager->>WeComCLI: Run QR authorization
  WeComCLI-->>WecomCliManager: Authorization URL and status
  WecomCliManager-->>LinkRuntimeService: Updated WecomCliState
  LinkRuntimeService-->>ConnectionsUI: wecomCliChanged
  ConnectionsUI-->>User: Display connection status
Loading

Possibly related PRs

  • oomol-lab/wanta#299: Provides the Lark CLI direct-provider architecture extended here for WeCom CLI support.
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title follows the required <type>(<scope>): <subject> format, uses English, and accurately describes the WeCom CLI integration.
Description check ✅ Passed The description provides a detailed summary, user impact, implementation details, validation results, and relevant safety and compatibility considerations.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch codex/add-wecom-cli

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (3)
scripts/wecom-cli.ts (1)

15-16: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Document or enforce the version/commit pairing.

WECOM_CLI_VERSION and WECOM_CLI_GIT_HEAD are two independent pins. Nothing ties them together. If a future update bumps one without the other, the packaged CLI binary and the exported Skills can silently diverge (for example, a Skill that references a CLI subcommand the pinned binary does not yet support). Add a comment stating that both constants must be updated together, or add a lightweight consistency check (for example, verify the binary's reported capabilities against the exported Skills at build time).

🤖 Prompt for 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.

In `@scripts/wecom-cli.ts` around lines 15 - 16, Document or enforce the pairing
between WECOM_CLI_VERSION and WECOM_CLI_GIT_HEAD so they cannot be updated
independently. Prefer a clear comment requiring both constants to change
together, or add a lightweight build-time consistency check validating the
pinned binary against the exported Skills.
scripts/wecom-cli.test.ts (1)

1-29: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add coverage for the path-traversal guard and tar parsing.

The tests cover resolveWecomCliTarget and wecomCliBinaryName, both pure and low-risk. safeSkillPath and tarEntries in scripts/wecom-cli.ts are not covered. These functions protect against path traversal and parse a custom tar format used to select what gets written into the app's Skills resources. Export these functions and add unit tests for at least: a skill path containing ../, an absolute path, and a truncated/oversized tar entry.

🤖 Prompt for 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.

In `@scripts/wecom-cli.test.ts` around lines 1 - 29, Export the safeSkillPath and
tarEntries functions from scripts/wecom-cli.ts, then extend
scripts/wecom-cli.test.ts with unit coverage for rejecting skill paths
containing ../ and absolute paths, plus rejecting truncated or oversized entries
in the custom tar parser. Keep the existing resolveWecomCliTarget and
wecomCliBinaryName tests unchanged.
src/routes/Connections/index.tsx (1)

184-263: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider a single direct-provider lookup instead of repeated service ternaries.

Direct-provider dispatch by service is repeated at Lines 184-189, 198-205, 233-243, 251-260, 372-373, 391-394, and 541-545. Lark is matched by the literal "lark-cli" while WeCom uses the exported wecomCliService. A third direct provider requires an edit in every one of those places.

Extract one map from service to { cli, busy, phaseKey }, and export a larkCliService constant for symmetry. Behavior stays the same.

🤖 Prompt for 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.

In `@src/routes/Connections/index.tsx` around lines 184 - 263, Consolidate
direct-provider dispatch in the Connections component by exporting a
larkCliService constant and defining one service-keyed map containing each
provider’s cli, busy state, and phase key. Update the repeated ternaries around
selectedProviderDetail, busy/polling/progress handling, and direct-provider
actions to reuse this lookup, preserving current Lark and WeCom behavior while
making future providers require only one map entry.
🤖 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 `@electron/link-runtime/wecom-cli.ts`:
- Around line 170-175: Check for cancellation in connectNow after readAuthState
determines the account is not connected and before setting phase to
waiting_for_scan or invoking runAuthorizationCommand. Reuse the existing
cancellation assertion mechanism so a cancellation recorded during preparation
prevents the QR authorization process and external page from starting.
- Around line 277-290: Update the consume callback to call
findOfficialWecomAuthorizationUrl only with the portion of output ending at the
last newline, so URLs are matched only after complete lines are received.
Preserve the existing openedUrl latch and authorization-opening behavior once a
complete-line match is found.

In `@src/i18n/app-messages.zh.ts`:
- Around line 1100-1101: Update the localized value for
connections.wecomCli.description to distinguish capabilities by enterprise size:
enterprises over 10 people support documents and smart sheets, while enterprises
with 10 or fewer people support messaging, documents, schedules, meetings, and
to-dos. Remove the wording that states the official CLI is restricted to
enterprises with no more than 10 people.

---

Nitpick comments:
In `@scripts/wecom-cli.test.ts`:
- Around line 1-29: Export the safeSkillPath and tarEntries functions from
scripts/wecom-cli.ts, then extend scripts/wecom-cli.test.ts with unit coverage
for rejecting skill paths containing ../ and absolute paths, plus rejecting
truncated or oversized entries in the custom tar parser. Keep the existing
resolveWecomCliTarget and wecomCliBinaryName tests unchanged.

In `@scripts/wecom-cli.ts`:
- Around line 15-16: Document or enforce the pairing between WECOM_CLI_VERSION
and WECOM_CLI_GIT_HEAD so they cannot be updated independently. Prefer a clear
comment requiring both constants to change together, or add a lightweight
build-time consistency check validating the pinned binary against the exported
Skills.

In `@src/routes/Connections/index.tsx`:
- Around line 184-263: Consolidate direct-provider dispatch in the Connections
component by exporting a larkCliService constant and defining one service-keyed
map containing each provider’s cli, busy state, and phase key. Update the
repeated ternaries around selectedProviderDetail, busy/polling/progress
handling, and direct-provider actions to reuse this lookup, preserving current
Lark and WeCom behavior while making future providers require only one map
entry.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f8f93272-5ec7-4268-ac5e-c1006ededd55

📥 Commits

Reviewing files that changed from the base of the PR and between 62b53a6 and d42cb1f.

⛔ Files ignored due to path filters (1)
  • src/assets/apps/wecom.svg is excluded by !**/*.svg
📒 Files selected for processing (30)
  • .gitignore
  • NOTICE
  • THIRD_PARTY_NOTICES.md
  • docs/architecture.md
  • electron-builder.ts
  • electron/agent/binaries.ts
  • electron/agent/manager.test.ts
  • electron/agent/manager.ts
  • electron/agent/sidecar.ts
  • electron/agent/workspace.test.ts
  • electron/agent/workspace.ts
  • electron/connections/common.ts
  • electron/link-runtime/common.ts
  • electron/link-runtime/node.ts
  • electron/link-runtime/wecom-cli.test.ts
  • electron/link-runtime/wecom-cli.ts
  • electron/main.ts
  • package.json
  • scripts/download-wecom-cli.ts
  • scripts/prepare-binaries.ts
  • scripts/wecom-cli.test.ts
  • scripts/wecom-cli.ts
  • src/hooks/useLarkCliConnection.ts
  • src/hooks/useWecomCliConnection.test.ts
  • src/hooks/useWecomCliConnection.ts
  • src/i18n/app-messages.en.ts
  • src/i18n/app-messages.zh.ts
  • src/routes/Connections/ConnectionAccountsList.tsx
  • src/routes/Connections/ConnectionProviderDetailPane.tsx
  • src/routes/Connections/index.tsx

Comment thread electron/link-runtime/wecom-cli.ts
Comment thread electron/link-runtime/wecom-cli.ts
Comment thread src/i18n/app-messages.zh.ts Outdated
@alwaysmavs alwaysmavs changed the title Add WeCom CLI integration feat(wecom): add CLI integration Aug 3, 2026
@alwaysmavs

Copy link
Copy Markdown
Contributor Author

Addressed all actionable review feedback in ab375967:

  • honor cancellation before starting QR authorization;
  • parse authorization URLs only from complete output lines;
  • correct the enterprise-size capability descriptions in English and Chinese;
  • enforce the npm version/source-commit pairing through registry gitHead metadata;
  • harden and test Skill path validation and tar parsing;
  • consolidate direct-provider routing behind a single service-keyed binding map and export larkCliService;
  • update the PR title to the repository's conventional format.

Validation completed:

  • pnpm lint
  • pnpm ts-check
  • pnpm test — 289 files, 2,168 tests passed
  • pnpm build:app

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
electron/link-runtime/wecom-cli.test.ts (1)

98-132: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Wait for the authorization-status process before cancelling.

The fixed 25 ms delay does not prove that the mock reached the auth --auth-status branch. If setup takes longer, cancelConnection() can run before the status child starts. The test can then pass without covering cancellation of the in-flight status check. Have the mock create a readiness marker before sleep 0.1, and wait for that marker with a bounded timeout before calling cancelConnection().

🤖 Prompt for 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.

In `@electron/link-runtime/wecom-cli.test.ts` around lines 98 - 132, Update the
“honors cancellation while checking existing authorization” test so its mock
auth-status branch creates a readiness marker before sleeping, then wait for
that marker with a bounded timeout before invoking manager.cancelConnection().
Preserve the cancellation assertions and cleanup while ensuring the test cancels
only after the authorization-status process is in flight.
🤖 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/wecom-cli.ts`:
- Around line 146-150: Update the TAR size parsing used by the WeCom CLI archive
reader to validate the entire size field before converting it, rejecting
non-octal trailing characters instead of allowing Number.parseInt to partially
parse them. Ensure malformed fields fail through the existing invalid or
truncated archive error path, and add a test covering a size value with an
invalid trailing character such as “00000000001x”.

---

Nitpick comments:
In `@electron/link-runtime/wecom-cli.test.ts`:
- Around line 98-132: Update the “honors cancellation while checking existing
authorization” test so its mock auth-status branch creates a readiness marker
before sleeping, then wait for that marker with a bounded timeout before
invoking manager.cancelConnection(). Preserve the cancellation assertions and
cleanup while ensuring the test cancels only after the authorization-status
process is in flight.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d896eccf-bf29-43a2-a6e8-0f067de925e3

📥 Commits

Reviewing files that changed from the base of the PR and between d42cb1f and ab37596.

📒 Files selected for processing (8)
  • electron/link-runtime/wecom-cli.test.ts
  • electron/link-runtime/wecom-cli.ts
  • scripts/wecom-cli.test.ts
  • scripts/wecom-cli.ts
  • src/hooks/useLarkCliConnection.ts
  • src/i18n/app-messages.en.ts
  • src/i18n/app-messages.zh.ts
  • src/routes/Connections/index.tsx
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/i18n/app-messages.zh.ts
  • src/i18n/app-messages.en.ts
  • src/routes/Connections/index.tsx
  • electron/link-runtime/wecom-cli.ts

Comment thread scripts/wecom-cli.ts
Comment on lines +146 to +150
const dataEnd = dataStart + size
const nextOffset = dataStart + Math.ceil(size / 512) * 512
if (!Number.isSafeInteger(size) || size < 0 || dataEnd > tar.length || nextOffset > tar.length) {
throw new Error(`Invalid or truncated WeCom CLI source archive entry: ${entryPath}`)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the TAR string normalization and numeric parsing path.
rg -n -C 4 'function tarString|const size = Number\.parseInt|Invalid or truncated' scripts/wecom-cli.ts

# Confirm that the test suite covers malformed numeric fields, not only truncation.
rg -n -C 3 'truncated|oversized|parseInt|invalid.*size|size.*invalid' scripts/wecom-cli.test.ts

Repository: oomol-lab/wanta

Length of output: 1702


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '120,175p' scripts/wecom-cli.ts
sed -n '1,80p' scripts/wecom-cli.test.ts

python3 - <<'PY'
import math

fields = ["00000000001x", "00000000001", "0000000000x1", "", "bad"]
for field in fields:
    text = field.strip() or "0"
    digits = []
    for ch in text:
        if ch in "01234567":
            digits.append(ch)
        else:
            break
    value = int("".join(digits), 8) if digits else None
    print(repr(field), "parseInt-like:", value, "full-octal:", bool(text) and all(c in "01234567" for c in text))
PY

Repository: oomol-lab/wanta

Length of output: 5022


Reject malformed TAR size fields.

Number.parseInt accepts an octal prefix and ignores invalid trailing bytes. A field such as 00000000001x parses as 1. Validate the complete size field and add a test for an invalid trailing character.

🧰 Tools
🪛 ast-grep (0.45.0)

[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { execFile } from "node:child_process"
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)

🤖 Prompt for 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.

In `@scripts/wecom-cli.ts` around lines 146 - 150, Update the TAR size parsing
used by the WeCom CLI archive reader to validate the entire size field before
converting it, rejecting non-octal trailing characters instead of allowing
Number.parseInt to partially parse them. Ensure malformed fields fail through
the existing invalid or truncated archive error path, and add a test covering a
size value with an invalid trailing character such as “00000000001x”.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant