mvibe 0.1.0__tar.gz

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.
mvibe-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Yong Wang
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
mvibe-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,226 @@
1
+ Metadata-Version: 2.4
2
+ Name: mvibe
3
+ Version: 0.1.0
4
+ Summary: Mirror a local Claude Code TUI to a remote chat (WeChat). Local stays identical; remote takes over I/O via a flag file.
5
+ Project-URL: Homepage, https://github.com/wvalianty/mvibe
6
+ Project-URL: Repository, https://github.com/wvalianty/mvibe
7
+ Project-URL: Issues, https://github.com/wvalianty/mvibe/issues
8
+ Author-email: Yong Wang <yong.wang@cobo.com>
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Keywords: claude,claude-code,mirror,pty,remote,tui,wechat
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Environment :: Console
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Operating System :: MacOS
16
+ Classifier: Operating System :: POSIX :: Linux
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Topic :: Communications :: Chat
22
+ Classifier: Topic :: Terminals
23
+ Requires-Python: >=3.11
24
+ Requires-Dist: aiohttp>=3.9
25
+ Requires-Dist: pyte>=0.8.2
26
+ Requires-Dist: segno>=1.6
27
+ Description-Content-Type: text/markdown
28
+
29
+ # mvibe
30
+
31
+ 把本地 **Claude Code** TUI 镜像到远程聊天(微信)。本地终端与直接运行 `claude`
32
+ 逐字节一致;两个开关(输入路由 `flag` + 输出/入站总闸 `gate`)决定远程接管程度。
33
+ **全程只有一个 `claude` 进程**——切换只是 I/O 路由,不重启、不交接会话。
34
+
35
+ ## 工作原理
36
+
37
+ ```
38
+ ┌──────────── mvibe run(持 PTY master)────────────┐
39
+ 终端 ────┤ stdin → PTY (local 路由;一敲键即夺回) │
40
+ ←───┤ PTY → stdout (始终;远程驱动时也能围观) │──► claude TUI
41
+ │ inject FIFO → PTY(remote 路由时) │ (单进程)
42
+ └───────────────────────────────────────────────────┘ │ 写入
43
+ .jsonl ◄─────────┘
44
+ 微信 ──long-poll 入站──► inject FIFO + flag=remote │
45
+ ◄── 推送(iLink) ──── tail transcript .jsonl(assistant text)◄┘
46
+ [mvibe bridge](出站受 remote gate 控制,无网络监听)
47
+ ```
48
+
49
+ - **本地保持真 TUI**(富渲染、slash 命令、plan mode):PTY 透明透传,即
50
+ `script(1)` / `asciinema` 那套。
51
+ - **远程输出 ≠ 裸 ANSI**:从会话 transcript(`~/.claude/projects/<cwd>/<id>.jsonl`)
52
+ 读,而非镜像 PTY 字节——TUI 的 ANSI 流在聊天框里是乱码。
53
+ - **远程输入**:当作键盘注入 PTY,驱动真 TUI。
54
+
55
+ ### 两个开关,别搞混
56
+
57
+ - **`flag`(`~/.mvibe/active`,local|remote)= 输入路由**:谁的键盘进 claude。
58
+ 入站消息置 `remote`;**本地敲任意键立即翻回 `local` 并夺回控制**(human 优先,
59
+ 永不锁死)。这是输入端互斥,杜绝两人同敲。
60
+ - **`gate`(remote on/off)= 输出镜像 + 入站总闸**:决定是否把 claude 回复推手机、
61
+ 是否接受手机消息。`/mvibe-on` 开、`/mvibe-off` 关。
62
+ - 二者解耦:所以你本地夺回打字(flag=local)时,回复仍会推手机(gate 仍 on);
63
+ 要彻底断开手机,`/mvibe-off`。
64
+
65
+ ## 安装
66
+
67
+ ```bash
68
+ uv tool install --editable /path/to/mvibe # 把 mvibe 装上全局 PATH
69
+ cp /path/to/mvibe/commands/mvibe-*.md ~/.claude/commands/ # 装会话内 slash 开关
70
+ mvibe login # 一次性:扫码绑定微信 bot
71
+ ```
72
+
73
+ 依赖仅 `aiohttp` + `segno`(PyPI),**零 avibe 依赖**。
74
+
75
+ ## 用法(单终端,推荐)
76
+
77
+ 进到想用 claude 的目录,直接 `mvibe`——**裸 `mvibe` 等价于在当前目录 `mvibe up`**:
78
+ 同一终端前台跑 claude TUI,后台跑 bridge(镜像输出 + 微信入站轮询)。桥日志写
79
+ `~/.mvibe/bridge.log`,不污染界面。
80
+
81
+ ```bash
82
+ cd /path/to/project
83
+ mvibe # = mvibe up,cwd 即当前目录
84
+ ```
85
+
86
+ 等价写法:`mvibe up`(同上)、`mvibe up --cwd /other/dir`(指定目录)。
87
+
88
+ **传 claude 参数**:`--` 前是 mvibe 参数,后是 claude 参数(整条当命令运行):
89
+
90
+ ```bash
91
+ mvibe -- claude --resume 873fd8af-... # 续接指定会话
92
+ mvibe -- claude -c # 续接最近会话
93
+ mvibe up --cwd /proj -- claude --model opus
94
+ ```
95
+
96
+ `--` 后写程序名即可换后端(如 `mvibe -- codex ...`)。注意:显式 `-- <cmd>` 时
97
+ mvibe 不再自动加 claude 默认参数(`--yolo` 等被忽略,全由你控制)。
98
+
99
+ 手机端:先给 bot 发一条消息建立会话,之后你发的内容驱动这个 Claude TUI,
100
+ Claude 的回复以聊天消息返回。退出 claude(`/exit`)即整体结束。
101
+
102
+ ### 会话内开关(Claude slash 命令)
103
+
104
+ 把 `commands/mvibe-*.md` 拷到 `~/.claude/commands/`,即可在任意会话里:
105
+
106
+ | 命令 | 作用 |
107
+ |------|------|
108
+ | `/mvibe-on` | 开启远程接管(微信驱动会话) |
109
+ | `/mvibe-off` | 关闭远程接管(忽略微信,保持本地) |
110
+ | `/mvibe-status` | 查看 remote 开关 / 路由 flag / transcript |
111
+
112
+ `mvibe up` 默认 remote=on(`--no-remote` 可改)。
113
+
114
+ **夺回控制**:远程接管时本地键盘被让出,但**只要你在本地敲任意键,立即夺回
115
+ 本地控制**(human 在场优先)——所以你永远不会被锁死,可随时打字。想彻底不让
116
+ 手机再抢,敲 `/mvibe-off`(关闭 gate)。下次手机消息会再次接管,除非 gate 关着。
117
+
118
+ ### 两终端模式(可选)
119
+
120
+ ```bash
121
+ mvibe run --cwd . # 终端1:claude TUI(必须先起)
122
+ mvibe bridge --cwd . # 终端2:镜像 + 入站
123
+ ```
124
+
125
+ ### 直接命令
126
+
127
+ ```bash
128
+ mvibe remote on|off|status # 远程接管开关
129
+ mvibe flag remote|local # 仅切路由
130
+ mvibe send "继续" # 注入一行到当前会话
131
+ ```
132
+
133
+ 无网络监听:微信入站是 long-poll(出站连接),本地控制全走 CLI + 文件。
134
+
135
+ ## 微信:独立 bot
136
+
137
+ mvibe 端到端自持微信 bot——**零 avibe 依赖**、无需 service、无需公网 webhook:
138
+
139
+ - **绑定**(`mvibe login`,`wechat_login.py`):跑官方 iLink 扫码流程,把
140
+ `bot_token` / `base_url` / `user_id` 写入 `~/.mvibe/config.json`。
141
+ - **入站**(`wechat_in.py`):iLink `getUpdates` 长轮询,无需 webhook/公网。
142
+ 每条消息产出 `(text, user_id)`,置 flag 为 `remote` 并把文本注入 PTY。同步游标
143
+ 与每用户 `context_token` 持久化在 `~/.mvibe/state/`。
144
+ - **出站**(`wechat_out.py`):tailer 读到的 assistant 文本经 `send_message`
145
+ 推手机,用记住的 `context_token` 寻址。**只在 remote gate 开启时推送**(跟 gate
146
+ 走,不跟 flag),所以本地按键夺回输入不会吞掉回复。失败写 `~/.mvibe/bridge.log`
147
+ (如 `errcode -14` 会话过期,需手机再发条消息刷新)。
148
+
149
+ iLink 协议代码 vendored 在 `mvibe/ilink/`(`wechat_api.py` / `wechat_auth.py`,
150
+ 源自 avibe,仅依赖 `aiohttp` + stdlib)。mvibe 不在运行时引用任何 avibe 安装。
151
+
152
+ > 一个 bot_token 上不要同时跑两个 `getUpdates` 轮询(会争同步游标)。mvibe 有自己
153
+ > 的登录/bot,与任何 avibe 实例互不干扰。
154
+
155
+ ## 安全
156
+
157
+ 远程聊天面拥有**完整 agent 权限**(Claude 可跑 bash/工具)。务必收紧入口:
158
+
159
+ - **入站鉴权(默认安全)**:`mvibe login` 会写入 `wechat.user_id`,此后只有该
160
+ 用户能驱动会话;其余被丢弃。多人放行用 `wechat.allowed_users: [...]`。若两者
161
+ 皆空,则放行所有人并打印告警——别在这种状态下长期运行。
162
+ - **无网络监听**:不开任何端口,没有可被本机其他进程访问的注入入口;入站只来自
163
+ 已鉴权的微信 bot。
164
+ - **凭证文件权限**:`~/.mvibe` 为 `0700`,`config.json` / `context_tokens` /
165
+ `sync_buf` 为 `0600`(仅本人可读)。
166
+ - **注入消毒**:远程文本注入 PTY 前剥除 C0 控制符(含 ESC),防终端转义/控制键
167
+ 注入;制表与换行保留。
168
+ - **TLS**:若存在 `~/tmp/cacert.pem`,会经 `SSL_CERT_FILE` 启用该 CA bundle
169
+ (Cloudflare WARP 拦截环境所需)——这会扩大信任根,按需保留。
170
+
171
+ ## 交互确认转发(抓屏)
172
+
173
+ claude 的确认框(「是否允许 Web Search?1/2/3」「(y/n)」)是 **TUI 界面元素**,不进
174
+ transcript。mvibe 用**抓屏**把它转发到手机:
175
+
176
+ - `mvibe up` 用 pyte 终端模拟器维护「渲染后的屏幕」,喂入 PTY 输出(`prompt_detect.py`)。
177
+ - 屏幕稳定(停 ~300ms)且匹配「确认框形态」(编号选项 + 光标 ❯,或 `(y/n)`)时,
178
+ 把框文本发手机:`⚠️ 需要确认:… 回复数字 或 yes/no`。
179
+ - 手机回 `1`/`2`/`yes`/`no` → 映射成按键注入 TUI(`yes`→含 "Yes" 那项,`no`→"No"/取消)。
180
+ - **remote gate 开启时就转发**(与出站镜像同规则,不看 flag);手机或本地终端都能答。
181
+ 久不回则框留着。不想被转发就 `/mvibe-off`。
182
+
183
+ **后端无关**:机制(pyte 渲染 + 按键注入)跟谁是后端无关;只有「确认框形态」识别规则
184
+ 按后端配(现内置 claude 形态)。换 codex 等只需在 `prompt_detect.py` 加一条规则。
185
+
186
+ 限制 & 选项:
187
+
188
+ - 仅 `mvibe up`(单进程)生效——PTY 字节只有 wrapper 进程有;两终端 `bridge` 看不到。
189
+ - 完全不想要确认(自动跑):`mvibe up --yolo`(给 claude 加 `--dangerously-skip-permissions`)。
190
+ - 检测靠 UI 形态匹配,claude 改版可能要调 `prompt_detect.py` 的规则。
191
+ - 远程能让 claude 跑任何工具——**靠入站 allowlist 限制是谁**(见安全)。
192
+
193
+ ## 注意事项
194
+
195
+ - iLink 仅允许在手机最近一次给 bot 发消息后的窗口内推送(`context_token`);
196
+ 长时间空闲后推送可能报 errcode -14。
197
+ - 键盘注入以 CR 提交;多行文本可能提前断行提交。
198
+ - 两端必须用**相同 cwd**(transcript 目录按 cwd 编码)。
199
+
200
+ ## 项目结构
201
+
202
+ ```
203
+ mvibe/
204
+ cli.py 命令行(argparse):默认/up/run/bridge/login/remote/send/flag/status
205
+ wrapper.py PTY mux 核心:本地透传 + flag 路由 + 本地按键夺回 + 注入消毒
206
+ bridge.py 后台:transcript→微信镜像 + 微信入站 poll(无网络监听)
207
+ tailer.py follow transcript .jsonl,抽 assistant text
208
+ prompt_detect.py pyte 抓屏:检测确认框 → 回调(确认转发用)
209
+ wechat_in.py iLink getUpdates 长轮询入站
210
+ wechat_out.py iLink send_message 出站
211
+ wechat_login.py QR 扫码绑定
212
+ config.py ~/.mvibe 凭证/tokens/sync_buf(0600)
213
+ paths.py 路径布局 + flag/gate + cwd→transcript 编码
214
+ _tls.py 可选 CA bundle
215
+ ilink/ vendored iLink 协议(wechat_api.py / wechat_auth.py,仅 aiohttp)
216
+ commands/ Claude slash 命令模板(/mvibe-on /off /status)
217
+ scripts/smoke.sh 本地自测(无微信、隔离 HOME)
218
+ ```
219
+
220
+ ## 自测
221
+
222
+ ```bash
223
+ bash scripts/smoke.sh # 9 项:注入/flag 互斥/消毒/HTTP/token/权限/tailer
224
+ ```
225
+
226
+ 不碰真 `~/.mvibe`(用隔离 `MVIBE_HOME`),无网络无微信,安全可随时跑。
mvibe-0.1.0/README.md ADDED
@@ -0,0 +1,198 @@
1
+ # mvibe
2
+
3
+ 把本地 **Claude Code** TUI 镜像到远程聊天(微信)。本地终端与直接运行 `claude`
4
+ 逐字节一致;两个开关(输入路由 `flag` + 输出/入站总闸 `gate`)决定远程接管程度。
5
+ **全程只有一个 `claude` 进程**——切换只是 I/O 路由,不重启、不交接会话。
6
+
7
+ ## 工作原理
8
+
9
+ ```
10
+ ┌──────────── mvibe run(持 PTY master)────────────┐
11
+ 终端 ────┤ stdin → PTY (local 路由;一敲键即夺回) │
12
+ ←───┤ PTY → stdout (始终;远程驱动时也能围观) │──► claude TUI
13
+ │ inject FIFO → PTY(remote 路由时) │ (单进程)
14
+ └───────────────────────────────────────────────────┘ │ 写入
15
+ .jsonl ◄─────────┘
16
+ 微信 ──long-poll 入站──► inject FIFO + flag=remote │
17
+ ◄── 推送(iLink) ──── tail transcript .jsonl(assistant text)◄┘
18
+ [mvibe bridge](出站受 remote gate 控制,无网络监听)
19
+ ```
20
+
21
+ - **本地保持真 TUI**(富渲染、slash 命令、plan mode):PTY 透明透传,即
22
+ `script(1)` / `asciinema` 那套。
23
+ - **远程输出 ≠ 裸 ANSI**:从会话 transcript(`~/.claude/projects/<cwd>/<id>.jsonl`)
24
+ 读,而非镜像 PTY 字节——TUI 的 ANSI 流在聊天框里是乱码。
25
+ - **远程输入**:当作键盘注入 PTY,驱动真 TUI。
26
+
27
+ ### 两个开关,别搞混
28
+
29
+ - **`flag`(`~/.mvibe/active`,local|remote)= 输入路由**:谁的键盘进 claude。
30
+ 入站消息置 `remote`;**本地敲任意键立即翻回 `local` 并夺回控制**(human 优先,
31
+ 永不锁死)。这是输入端互斥,杜绝两人同敲。
32
+ - **`gate`(remote on/off)= 输出镜像 + 入站总闸**:决定是否把 claude 回复推手机、
33
+ 是否接受手机消息。`/mvibe-on` 开、`/mvibe-off` 关。
34
+ - 二者解耦:所以你本地夺回打字(flag=local)时,回复仍会推手机(gate 仍 on);
35
+ 要彻底断开手机,`/mvibe-off`。
36
+
37
+ ## 安装
38
+
39
+ ```bash
40
+ uv tool install --editable /path/to/mvibe # 把 mvibe 装上全局 PATH
41
+ cp /path/to/mvibe/commands/mvibe-*.md ~/.claude/commands/ # 装会话内 slash 开关
42
+ mvibe login # 一次性:扫码绑定微信 bot
43
+ ```
44
+
45
+ 依赖仅 `aiohttp` + `segno`(PyPI),**零 avibe 依赖**。
46
+
47
+ ## 用法(单终端,推荐)
48
+
49
+ 进到想用 claude 的目录,直接 `mvibe`——**裸 `mvibe` 等价于在当前目录 `mvibe up`**:
50
+ 同一终端前台跑 claude TUI,后台跑 bridge(镜像输出 + 微信入站轮询)。桥日志写
51
+ `~/.mvibe/bridge.log`,不污染界面。
52
+
53
+ ```bash
54
+ cd /path/to/project
55
+ mvibe # = mvibe up,cwd 即当前目录
56
+ ```
57
+
58
+ 等价写法:`mvibe up`(同上)、`mvibe up --cwd /other/dir`(指定目录)。
59
+
60
+ **传 claude 参数**:`--` 前是 mvibe 参数,后是 claude 参数(整条当命令运行):
61
+
62
+ ```bash
63
+ mvibe -- claude --resume 873fd8af-... # 续接指定会话
64
+ mvibe -- claude -c # 续接最近会话
65
+ mvibe up --cwd /proj -- claude --model opus
66
+ ```
67
+
68
+ `--` 后写程序名即可换后端(如 `mvibe -- codex ...`)。注意:显式 `-- <cmd>` 时
69
+ mvibe 不再自动加 claude 默认参数(`--yolo` 等被忽略,全由你控制)。
70
+
71
+ 手机端:先给 bot 发一条消息建立会话,之后你发的内容驱动这个 Claude TUI,
72
+ Claude 的回复以聊天消息返回。退出 claude(`/exit`)即整体结束。
73
+
74
+ ### 会话内开关(Claude slash 命令)
75
+
76
+ 把 `commands/mvibe-*.md` 拷到 `~/.claude/commands/`,即可在任意会话里:
77
+
78
+ | 命令 | 作用 |
79
+ |------|------|
80
+ | `/mvibe-on` | 开启远程接管(微信驱动会话) |
81
+ | `/mvibe-off` | 关闭远程接管(忽略微信,保持本地) |
82
+ | `/mvibe-status` | 查看 remote 开关 / 路由 flag / transcript |
83
+
84
+ `mvibe up` 默认 remote=on(`--no-remote` 可改)。
85
+
86
+ **夺回控制**:远程接管时本地键盘被让出,但**只要你在本地敲任意键,立即夺回
87
+ 本地控制**(human 在场优先)——所以你永远不会被锁死,可随时打字。想彻底不让
88
+ 手机再抢,敲 `/mvibe-off`(关闭 gate)。下次手机消息会再次接管,除非 gate 关着。
89
+
90
+ ### 两终端模式(可选)
91
+
92
+ ```bash
93
+ mvibe run --cwd . # 终端1:claude TUI(必须先起)
94
+ mvibe bridge --cwd . # 终端2:镜像 + 入站
95
+ ```
96
+
97
+ ### 直接命令
98
+
99
+ ```bash
100
+ mvibe remote on|off|status # 远程接管开关
101
+ mvibe flag remote|local # 仅切路由
102
+ mvibe send "继续" # 注入一行到当前会话
103
+ ```
104
+
105
+ 无网络监听:微信入站是 long-poll(出站连接),本地控制全走 CLI + 文件。
106
+
107
+ ## 微信:独立 bot
108
+
109
+ mvibe 端到端自持微信 bot——**零 avibe 依赖**、无需 service、无需公网 webhook:
110
+
111
+ - **绑定**(`mvibe login`,`wechat_login.py`):跑官方 iLink 扫码流程,把
112
+ `bot_token` / `base_url` / `user_id` 写入 `~/.mvibe/config.json`。
113
+ - **入站**(`wechat_in.py`):iLink `getUpdates` 长轮询,无需 webhook/公网。
114
+ 每条消息产出 `(text, user_id)`,置 flag 为 `remote` 并把文本注入 PTY。同步游标
115
+ 与每用户 `context_token` 持久化在 `~/.mvibe/state/`。
116
+ - **出站**(`wechat_out.py`):tailer 读到的 assistant 文本经 `send_message`
117
+ 推手机,用记住的 `context_token` 寻址。**只在 remote gate 开启时推送**(跟 gate
118
+ 走,不跟 flag),所以本地按键夺回输入不会吞掉回复。失败写 `~/.mvibe/bridge.log`
119
+ (如 `errcode -14` 会话过期,需手机再发条消息刷新)。
120
+
121
+ iLink 协议代码 vendored 在 `mvibe/ilink/`(`wechat_api.py` / `wechat_auth.py`,
122
+ 源自 avibe,仅依赖 `aiohttp` + stdlib)。mvibe 不在运行时引用任何 avibe 安装。
123
+
124
+ > 一个 bot_token 上不要同时跑两个 `getUpdates` 轮询(会争同步游标)。mvibe 有自己
125
+ > 的登录/bot,与任何 avibe 实例互不干扰。
126
+
127
+ ## 安全
128
+
129
+ 远程聊天面拥有**完整 agent 权限**(Claude 可跑 bash/工具)。务必收紧入口:
130
+
131
+ - **入站鉴权(默认安全)**:`mvibe login` 会写入 `wechat.user_id`,此后只有该
132
+ 用户能驱动会话;其余被丢弃。多人放行用 `wechat.allowed_users: [...]`。若两者
133
+ 皆空,则放行所有人并打印告警——别在这种状态下长期运行。
134
+ - **无网络监听**:不开任何端口,没有可被本机其他进程访问的注入入口;入站只来自
135
+ 已鉴权的微信 bot。
136
+ - **凭证文件权限**:`~/.mvibe` 为 `0700`,`config.json` / `context_tokens` /
137
+ `sync_buf` 为 `0600`(仅本人可读)。
138
+ - **注入消毒**:远程文本注入 PTY 前剥除 C0 控制符(含 ESC),防终端转义/控制键
139
+ 注入;制表与换行保留。
140
+ - **TLS**:若存在 `~/tmp/cacert.pem`,会经 `SSL_CERT_FILE` 启用该 CA bundle
141
+ (Cloudflare WARP 拦截环境所需)——这会扩大信任根,按需保留。
142
+
143
+ ## 交互确认转发(抓屏)
144
+
145
+ claude 的确认框(「是否允许 Web Search?1/2/3」「(y/n)」)是 **TUI 界面元素**,不进
146
+ transcript。mvibe 用**抓屏**把它转发到手机:
147
+
148
+ - `mvibe up` 用 pyte 终端模拟器维护「渲染后的屏幕」,喂入 PTY 输出(`prompt_detect.py`)。
149
+ - 屏幕稳定(停 ~300ms)且匹配「确认框形态」(编号选项 + 光标 ❯,或 `(y/n)`)时,
150
+ 把框文本发手机:`⚠️ 需要确认:… 回复数字 或 yes/no`。
151
+ - 手机回 `1`/`2`/`yes`/`no` → 映射成按键注入 TUI(`yes`→含 "Yes" 那项,`no`→"No"/取消)。
152
+ - **remote gate 开启时就转发**(与出站镜像同规则,不看 flag);手机或本地终端都能答。
153
+ 久不回则框留着。不想被转发就 `/mvibe-off`。
154
+
155
+ **后端无关**:机制(pyte 渲染 + 按键注入)跟谁是后端无关;只有「确认框形态」识别规则
156
+ 按后端配(现内置 claude 形态)。换 codex 等只需在 `prompt_detect.py` 加一条规则。
157
+
158
+ 限制 & 选项:
159
+
160
+ - 仅 `mvibe up`(单进程)生效——PTY 字节只有 wrapper 进程有;两终端 `bridge` 看不到。
161
+ - 完全不想要确认(自动跑):`mvibe up --yolo`(给 claude 加 `--dangerously-skip-permissions`)。
162
+ - 检测靠 UI 形态匹配,claude 改版可能要调 `prompt_detect.py` 的规则。
163
+ - 远程能让 claude 跑任何工具——**靠入站 allowlist 限制是谁**(见安全)。
164
+
165
+ ## 注意事项
166
+
167
+ - iLink 仅允许在手机最近一次给 bot 发消息后的窗口内推送(`context_token`);
168
+ 长时间空闲后推送可能报 errcode -14。
169
+ - 键盘注入以 CR 提交;多行文本可能提前断行提交。
170
+ - 两端必须用**相同 cwd**(transcript 目录按 cwd 编码)。
171
+
172
+ ## 项目结构
173
+
174
+ ```
175
+ mvibe/
176
+ cli.py 命令行(argparse):默认/up/run/bridge/login/remote/send/flag/status
177
+ wrapper.py PTY mux 核心:本地透传 + flag 路由 + 本地按键夺回 + 注入消毒
178
+ bridge.py 后台:transcript→微信镜像 + 微信入站 poll(无网络监听)
179
+ tailer.py follow transcript .jsonl,抽 assistant text
180
+ prompt_detect.py pyte 抓屏:检测确认框 → 回调(确认转发用)
181
+ wechat_in.py iLink getUpdates 长轮询入站
182
+ wechat_out.py iLink send_message 出站
183
+ wechat_login.py QR 扫码绑定
184
+ config.py ~/.mvibe 凭证/tokens/sync_buf(0600)
185
+ paths.py 路径布局 + flag/gate + cwd→transcript 编码
186
+ _tls.py 可选 CA bundle
187
+ ilink/ vendored iLink 协议(wechat_api.py / wechat_auth.py,仅 aiohttp)
188
+ commands/ Claude slash 命令模板(/mvibe-on /off /status)
189
+ scripts/smoke.sh 本地自测(无微信、隔离 HOME)
190
+ ```
191
+
192
+ ## 自测
193
+
194
+ ```bash
195
+ bash scripts/smoke.sh # 9 项:注入/flag 互斥/消毒/HTTP/token/权限/tailer
196
+ ```
197
+
198
+ 不碰真 `~/.mvibe`(用隔离 `MVIBE_HOME`),无网络无微信,安全可随时跑。
@@ -0,0 +1,10 @@
1
+ ---
2
+ description: Turn OFF mvibe remote takeover; keep control local
3
+ disable-model-invocation: true
4
+ allowed-tools: Bash(mvibe remote off)
5
+ ---
6
+
7
+ !`mvibe remote off`
8
+
9
+ mvibe remote takeover is now **OFF** — incoming WeChat messages are ignored; the
10
+ terminal keeps control.
@@ -0,0 +1,9 @@
1
+ ---
2
+ description: Turn ON mvibe remote (WeChat) takeover for this session
3
+ disable-model-invocation: true
4
+ allowed-tools: Bash(mvibe remote on)
5
+ ---
6
+
7
+ !`mvibe remote on`
8
+
9
+ mvibe remote takeover is now **ON** — WeChat messages will drive this session.
@@ -0,0 +1,7 @@
1
+ ---
2
+ description: Show mvibe remote on/off + routing flag + active transcript
3
+ disable-model-invocation: true
4
+ allowed-tools: Bash(mvibe remote status), Bash(mvibe status)
5
+ ---
6
+
7
+ !`mvibe remote status && mvibe status`
@@ -0,0 +1,8 @@
1
+ """mvibe — mirror a local Claude Code TUI to a remote chat surface.
2
+
3
+ Single live `claude` process. A PTY wrapper keeps the local terminal identical
4
+ to running `claude` directly, while a flag file toggles whether remote (WeChat)
5
+ input is injected and remote output is mirrored. No process restart on switch.
6
+ """
7
+
8
+ __version__ = "0.1.0"
@@ -0,0 +1,17 @@
1
+ """TLS CA setup for environments that intercept HTTPS (e.g. Cloudflare WARP).
2
+
3
+ If ~/tmp/cacert.pem exists, point SSL at it. Optional; no-op otherwise.
4
+ """
5
+
6
+ from __future__ import annotations
7
+
8
+ import os
9
+ from pathlib import Path
10
+
11
+ _CA_BUNDLE = Path.home() / "tmp/cacert.pem"
12
+
13
+
14
+ def setup_tls_ca() -> None:
15
+ if _CA_BUNDLE.is_file():
16
+ os.environ.setdefault("SSL_CERT_FILE", str(_CA_BUNDLE))
17
+ os.environ.setdefault("REQUESTS_CA_BUNDLE", str(_CA_BUNDLE))