{"id":49574788,"url":"https://github.com/andyleimc-source/wx-cc-bridge","last_synced_at":"2026-05-03T16:08:00.052Z","repository":{"id":353710646,"uuid":"1220167350","full_name":"andyleimc-source/wx-cc-bridge","owner":"andyleimc-source","description":"WeChat ↔ Claude Code CLI bridge — talk to Claude from WeChat via the ClawBot (iLink) protocol, using your Max subscription.","archived":false,"fork":false,"pushed_at":"2026-04-25T05:15:42.000Z","size":15,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-25T07:26:22.205Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/andyleimc-source.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-24T16:04:28.000Z","updated_at":"2026-04-25T05:15:47.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/andyleimc-source/wx-cc-bridge","commit_stats":null,"previous_names":["andyleimc-source/wx-cc-bridge"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/andyleimc-source/wx-cc-bridge","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andyleimc-source%2Fwx-cc-bridge","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andyleimc-source%2Fwx-cc-bridge/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andyleimc-source%2Fwx-cc-bridge/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andyleimc-source%2Fwx-cc-bridge/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/andyleimc-source","download_url":"https://codeload.github.com/andyleimc-source/wx-cc-bridge/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andyleimc-source%2Fwx-cc-bridge/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32575239,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-03T06:36:36.687Z","status":"ssl_error","status_checked_at":"2026-05-03T06:36:09.306Z","response_time":103,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-05-03T16:07:59.422Z","updated_at":"2026-05-03T16:08:00.044Z","avatar_url":"https://github.com/andyleimc-source.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# wx-cc-bridge\n\nWeChat ↔ Claude Code CLI bridge. Talk to Claude Code from your personal WeChat account through the official ClawBot / iLink protocol, using your Claude Max subscription (no API billing).\n\n```\nWeChat user → iLink long-poll (ilinkai.weixin.qq.com)\n           → this bridge (Python / FastAPI-free, asyncio)\n           → subprocess: claude -p \"…\" --resume \u003csession_id\u003e\n           → reply back via sendmessage\n```\n\n## Why\n\n- **No API key required** — runs the `claude` CLI against your Max subscription.\n- **Per-chat isolation** — each WeChat contact gets its own `cwd` and Claude session.\n- **Slash commands** — `/new`, `/history`, `/resume`, `/cd`, `/pwd`, `/help`.\n\n## Status\n\n- **M1**: iLink integration (login, long-poll, send) — working\n- **M2**: Claude CLI integration + slash commands — working\n- **M3**: Hardening (token auth, bash-tool denylist, reply chunking) — not yet\n\n## Requirements\n\n- macOS (launchd deployment uses Apple-specific bits; core Python works anywhere)\n- Python 3.11+\n- [`uv`](https://github.com/astral-sh/uv)\n- `claude` CLI installed and logged in with a Max subscription — **must not** be configured to hit the Anthropic API\n- A WeChat account that can register as a ClawBot (this is Tencent-side; not every account can)\n\n## Install\n\n```bash\ngit clone https://github.com/\u003cyou\u003e/wx-cc-bridge.git\ncd wx-cc-bridge\nmake sync                    # uv-based deps + editable install\n```\n\n## Run (foreground, for first-time login)\n\n```bash\nmake run\n```\n\nA QR code prints in the terminal. Scan it with the WeChat account you want to use as the bot (that account will act as the bridge endpoint). Login persists in `~/.wx-cc-bridge/token.json`.\n\nSend any message from a second WeChat account to the bot account and you should see `[msg] …` in the log and a Claude reply back in WeChat.\n\n## Run as a background service (macOS launchd)\n\n```bash\nmake install-service         # one-time\nmake status                  # PID / exit code\nmake logs                    # tail stdout+stderr\nmake restart-service\nmake uninstall-service\n```\n\nLogs: `~/Library/Logs/wx-cc-bridge/`. Auto-restarts on crash; survives logout/login; **does not** survive Mac sleep/shutdown.\n\n## Commands (in WeChat)\n\n| Command        | Effect                                      |\n| -------------- | ------------------------------------------- |\n| `/help`        | List commands                               |\n| `/pwd`         | Show current workspace for this chat        |\n| `/cd \u003cpath\u003e`   | Switch workspace (persists, opens new session) |\n| `/new`, `/clear` | Start a fresh Claude session              |\n| `/history`     | List recent Claude sessions in this workspace |\n| `/resume \u003cn\u003e`  | Resume session by number from `/history`    |\n\nAnything that doesn't start with `/` is forwarded to `claude -p` in the chat's current workspace, resuming its session.\n\n## Layout\n\n```\nsrc/wx_cc_bridge/\n  ilink/client.py     # QR login, long-poll, send — iLink protocol\n  claude_runner.py    # async subprocess wrapper around `claude -p`\n  session_store.py    # per-chat state (session_id, cwd) in JSON\n  commands.py         # slash-command dispatcher\n  bridge.py           # main loop: poll → dispatch → reply\n  echo.py             # M1-era echo bot, handy for iLink protocol debugging\nscripts/\n  wx-cc-bridge.plist.template  # launchd service template\n```\n\nRuntime state:\n\n```\n~/.wx-cc-bridge/\n  token.json       # iLink bot_token + baseurl (sensitive — keep off GitHub)\n  cursor.txt       # long-poll cursor\n  sessions.json    # chat_id → {session_id, cwd}\n~/cc-wx-sessions/{safe_chat_id}/  # per-chat workspace (Claude's cwd)\n```\n\n## Protocol gotchas (learned the hard way)\n\nNone of the following are in the public `weixin-bot-api.md` — reverse-engineered from [x1ah/wechat-ilink-demo](https://github.com/x1ah/wechat-ilink-demo)'s `bot.mjs`:\n\n1. **QR code encoding** — the `qrcode` field in `get_bot_qrcode` response is an opaque id (scanning it opens a landing page). The actual login URL is in `qrcode_img_content` (despite its name, it's a URL string, not image bytes). Render *that* as a QR.\n2. **`AuthorizationType: ilink_bot_token` header** — required on every request after login. Without it the server returns `{errcode: -14, errmsg: \"session timeout\"}` on every `getupdates` call.\n3. **`X-WECHAT-UIN` encoding** — `base64(str(random_uint32).encode())`, i.e. the uint32 rendered as a decimal string, then base64. *Not* `base64(4_random_bytes)`.\n4. **`sendmessage` needs `from_user_id: \"\"` and `client_id: \u003cuuid\u003e`** — the server silently accepts messages missing these and returns `{}`, but never delivers them if the reply takes more than ~1s. Quick echoes work, Claude replies don't.\n5. **Claude Code's project dir encoding** at `~/.claude/projects/` replaces all non-`[A-Za-z0-9-]` characters with `-`, not just `/`. So `/Users/foo/_bar` becomes `-Users-foo--bar`.\n\n## Security\n\nThe default workspace for each chat is `~/cc-wx-sessions/{safe_chat_id}/`, *not* `$HOME`, to avoid exposing dotfiles (`.ssh`, `.aws`, etc.) to `/cd` accidents or a compromised WeChat account.\n\nClaude runs with `--permission-mode bypassPermissions`. Once you `/cd` out of the sandbox, Claude has read/write/delete on whatever you point it at. Don't point it at `~` unless you understand that implication. A future milestone adds a `--disallowedTools` baseline (no `Bash(rm*)` etc.).\n\nNothing here exposes a public port — this bridge only *makes* outbound long-poll requests.\n\n## License\n\nMIT — see `LICENSE`.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fandyleimc-source%2Fwx-cc-bridge","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fandyleimc-source%2Fwx-cc-bridge","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fandyleimc-source%2Fwx-cc-bridge/lists"}