@oatelauser/crosschat 1.0.0 → 1.1.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.
- package/CHANGELOG.md +53 -22
- package/LICENSE +25 -25
- package/README.md +254 -244
- package/dist/cli.js +38 -18
- package/dist/cli.js.map +1 -1
- package/dist/codex/deliver.js +20 -9
- package/dist/codex/deliver.js.map +1 -1
- package/dist/codex/rollout-meta.js +6 -0
- package/dist/codex/rollout-meta.js.map +1 -1
- package/dist/codex/transport.js +19 -1
- package/dist/codex/transport.js.map +1 -1
- package/dist/commands/doctor.js +9 -4
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/send.js +56 -13
- package/dist/commands/send.js.map +1 -1
- package/dist/outbox.js +204 -52
- package/dist/outbox.js.map +1 -1
- package/dist/send-log.js +74 -0
- package/dist/send-log.js.map +1 -0
- package/dist/watchdog.js +104 -0
- package/dist/watchdog.js.map +1 -0
- package/package.json +46 -47
- package/skills/crosschat/SKILL.md +65 -65
- package/dist/codex/queue.js +0 -71
- package/dist/codex/queue.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,244 +1,254 @@
|
|
|
1
|
-
<div align="center">
|
|
2
|
-
|
|
3
|
-
<img src="docs/assets/logo.svg" width="128" alt="crosschat logo"/>
|
|
4
|
-
|
|
5
|
-
# crosschat
|
|
6
|
-
|
|
7
|
-
**让本机的 Claude Code 与 Codex CLI 互相对话** —— 无守护进程、原生投递、双向实测。
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
-
|
|
20
|
-
-
|
|
21
|
-
-
|
|
22
|
-
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
|
31
|
-
|
|
32
|
-
|
|
|
33
|
-
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
#
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
```
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
crosschat
|
|
120
|
-
crosschat
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
- [
|
|
180
|
-
- [
|
|
181
|
-
- [
|
|
182
|
-
- [
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
**
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
|
226
|
-
|
|
|
227
|
-
|
|
|
228
|
-
|
|
|
229
|
-
|
|
|
230
|
-
|
|
231
|
-
##
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
<img src="docs/assets/logo.svg" width="128" alt="crosschat logo"/>
|
|
4
|
+
|
|
5
|
+
# crosschat
|
|
6
|
+
|
|
7
|
+
**让本机的 Claude Code 与 Codex CLI 互相对话** —— 无守护进程、原生投递、双向实测。
|
|
8
|
+
|
|
9
|
+
     
|
|
10
|
+
|
|
11
|
+
</div>
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## ✨ 为什么用 crosschat
|
|
16
|
+
|
|
17
|
+
- **🔌 原生投递,零轮询**:消息经 Claude 的 named pipe / Codex 的 App Server daemon 直接注入运行中的会话——接收方像收到一条用户消息一样开始工作,不需要任何轮询或常驻服务
|
|
18
|
+
- **🪶 无守护进程**:整个工具就是一条无状态 CLI。没有后台进程要看护、没有崩溃丢状态、没有端口要占——`send` 就是发消息,`status` 就是看在线
|
|
19
|
+
- **🤖 教学内建,低入侵**:agent 侧装一次 skill,且**每条消息的信封自带回复命令**——照抄即可回话。题词只写角色,不写协议;长对话也不会忘
|
|
20
|
+
- **📮 忙时不丢**:对方正在跑长任务?消息自动进本地发件箱(outbox),并由看门狗进程每 0.5–5 分钟自动重投,对方一空闲就送达——不需要你手动重发;滞留内容随时可在 mailbox 镜像文件里读到
|
|
21
|
+
- **🔐 同用户信任边界**:全部通道按 Windows 用户隔离,接收许可只授予 `crosschat claude` 启动的会话——你手敲开的会话不会被外部投递
|
|
22
|
+
- **🧪 每个结论都有实证**:通道可行性、写者锁、daemon 版本行为,全部真机联调验证(见 [📚 更多文档](#-更多文档))
|
|
23
|
+
|
|
24
|
+
> 灵感与部分模块实现来自 [embassy](https://github.com/YuanpingSong/embassy)(macOS-only,MIT)——crosschat 是它的 Windows 原生、无守护进程重实现。
|
|
25
|
+
|
|
26
|
+
## 🧰 环境搭建
|
|
27
|
+
|
|
28
|
+
| 依赖 | 版本要求 | 说明 |
|
|
29
|
+
|---|---|---|
|
|
30
|
+
| Node.js | ≥ 22 | |
|
|
31
|
+
| Claude Code | 任意近期版 | 接收需 `crosschat claude` 启动(工具自动注入许可) |
|
|
32
|
+
| Codex CLI | ≥ 0.160 推荐 | 0.160 daemon 支持开窗投递;接收需 app-server daemon |
|
|
33
|
+
| OS | Windows(一期) | mac/linux 在路线图 |
|
|
34
|
+
|
|
35
|
+
```powershell
|
|
36
|
+
npm i -g @oatelauser/crosschat # 一行安装(提供 crosschat / multichat 双命令)
|
|
37
|
+
crosschat install-skills # 协议教学装到两侧 agent
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
<details><summary>从源码安装(开发者)</summary>
|
|
41
|
+
|
|
42
|
+
```powershell
|
|
43
|
+
git clone https://github.com/Oatelauser/crosschat.git
|
|
44
|
+
cd crosschat && npm ci && npm run build && npm link
|
|
45
|
+
crosschat install-skills
|
|
46
|
+
```
|
|
47
|
+
</details>
|
|
48
|
+
|
|
49
|
+
**Codex daemon**(接收方向必需;从**干净终端**启动——勿在 Claude 会话内启动,否则派生 shell 身份污染):
|
|
50
|
+
|
|
51
|
+
```powershell
|
|
52
|
+
codex app-server daemon start # 重启电脑后需重新执行
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## 🚀 快速入门
|
|
56
|
+
|
|
57
|
+
### 30 秒版:两终端互发一句问候
|
|
58
|
+
|
|
59
|
+
```powershell
|
|
60
|
+
# 终端 A(claude,能收)
|
|
61
|
+
crosschat claude # 进入后 /rename alice
|
|
62
|
+
|
|
63
|
+
# 终端 B(任意终端,人手发)
|
|
64
|
+
crosschat status # 看到 alice
|
|
65
|
+
crosschat send --to alice --body "你好 alice!"
|
|
66
|
+
# → 终端 A 里几秒后出现这条消息
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### 完整剧本:codex 当领导派活给 claude(多任务循环的经典形态)
|
|
70
|
+
|
|
71
|
+
**任务目标**:codex(领导)命令 claude(工人)在 `D:\workspace\demo` 创建 `notes.txt` 写三行待办,并验收。
|
|
72
|
+
|
|
73
|
+
**第 1 步 · 左窗启动 claude(接收方先开机;先启动 ≠ 先说话)**
|
|
74
|
+
```
|
|
75
|
+
D:\workspace\demo> crosschat claude
|
|
76
|
+
```
|
|
77
|
+
进入后输入 `/rename worker`,待命。
|
|
78
|
+
|
|
79
|
+
**第 2 步 · 右窗启动 codex(领导)并贴题词**
|
|
80
|
+
```
|
|
81
|
+
D:\workspace\demo> codex
|
|
82
|
+
```
|
|
83
|
+
```
|
|
84
|
+
你是领导。用 crosschat(先 status 确认名字)给 claude 会话「worker」下发任务:
|
|
85
|
+
在当前目录创建 notes.txt,内容三行:买牛奶、交电费、给妈妈打电话。
|
|
86
|
+
收到完成报告后,亲自打开文件验证内容;通过则回复"验收通过,任务结束"并停止;
|
|
87
|
+
不通过则下发返工任务。
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
**第 3 步 · 右窗屏幕——任务由 codex 发出**
|
|
91
|
+
```
|
|
92
|
+
● exec: crosschat status
|
|
93
|
+
● exec: crosschat send --to worker --body "任务1:在当前目录创建 notes.txt…"
|
|
94
|
+
● delivered to worker (turn 1) ← 任务飞进左窗,发起方是 codex
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
**第 4 步 ·(0.160+ 可跳过)关掉右窗**:旧版 daemon(≤0.157)下收报告需关窗;**0.160+ 开窗也能收**(headless 执行)。
|
|
98
|
+
|
|
99
|
+
**第 5 步 · 左窗屏幕——claude 收令干活**
|
|
100
|
+
```
|
|
101
|
+
📨 来自另一会话的消息:
|
|
102
|
+
<cross-session-message from-name="codex/01a…" turn="1">
|
|
103
|
+
任务1:在当前目录创建 notes.txt,内容三行…
|
|
104
|
+
回复请运行: crosschat send --conversation mc1_xxx --body "<你的回复>"
|
|
105
|
+
</cross-session-message>
|
|
106
|
+
|
|
107
|
+
⏺ Write: notes.txt(三行待办)
|
|
108
|
+
⏺ Bash: crosschat send --conversation mc1_xxx --body "已完成:notes.txt 已创建…"
|
|
109
|
+
⏺ delivered (turn 2) ← 报告发回给 codex
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
**第 6 步 · 自动发生**:报告落进 codex 线程 → codex 验证 → 下发下一个/验收结论 → 又出现在左窗。
|
|
113
|
+
|
|
114
|
+
**第 7 步 · 左窗收到"验收通过,任务结束"——收工**。全程在左窗直播;想看领导的验收细节:开右窗 `codex resume` 翻历史。
|
|
115
|
+
|
|
116
|
+
## 📋 命令列表
|
|
117
|
+
|
|
118
|
+
```
|
|
119
|
+
crosschat send --to <名字> --body "<正文>" # 新消息(名字含空格加引号)
|
|
120
|
+
crosschat send --conversation <ref> --body "<正文>" # 回复(ref 照抄收到的信封)
|
|
121
|
+
echo … | crosschat send --to <名字> # 正文走 stdin
|
|
122
|
+
crosschat status [--json] # 双侧总览(名字/目录/时间/状态)
|
|
123
|
+
crosschat doctor # 一键环境体检(有 ❌ 时退出码 1)
|
|
124
|
+
crosschat install-skills [--dir <根>] # 安装/更新 agent skill(幂等)
|
|
125
|
+
crosschat claude [任意 claude 参数…] # 带接收许可启动 claude(透传)
|
|
126
|
+
crosschat -v | --version | help # 版本 / 帮助
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
发送输出三种状态:`delivered`(已投递)/ `parked`(对方忙,已入发件箱,看门狗自动重投;输出含队列深度与 mailbox 镜像路径)/ 错误码(见排障)。
|
|
130
|
+
|
|
131
|
+
## 📖 对话生命周期(规则总纲)
|
|
132
|
+
|
|
133
|
+
### 三种发起方式
|
|
134
|
+
|
|
135
|
+
| 发起者 | 怎么发 | 特性 |
|
|
136
|
+
|---|---|---|
|
|
137
|
+
| **claude** | 会话内 agent 跑 `crosschat send --to <codex 线程名> --body "…"` | 最顺,推荐默认 |
|
|
138
|
+
| **codex** | 会话内 agent 跑 `crosschat send --to <claude 会话名> --body "…"` | 发送随意;回信开窗关窗都能收(daemon 0.160+) |
|
|
139
|
+
| **人** | 任意终端直接 `crosschat send --to <名字> --body "…"` | 身份是 human:**能发、不能被回复**(单向指令) |
|
|
140
|
+
|
|
141
|
+
第一条消息永远用 `--to`(此刻生成对话引用 reply-ref);对方名字用 `status` 查。
|
|
142
|
+
|
|
143
|
+
### 往复规则
|
|
144
|
+
|
|
145
|
+
1. 收到方看到**信封消息**(`from-name` + `turn="N"` + 回复提示)
|
|
146
|
+
2. 回复 = **逐字照抄回复提示里的命令**,换掉 `<你的回复>`——引用自动轮转,无需记任何历史
|
|
147
|
+
3. 防失控:每对端点 30 条/60 秒限流
|
|
148
|
+
|
|
149
|
+
### 轮次与终止
|
|
150
|
+
|
|
151
|
+
信封显示 `turn="N"`;题词给预算(如"10 轮内完成");接近预算时 agent 按 skill 准则总结收尾;协议层不强制终止。
|
|
152
|
+
|
|
153
|
+
## 📡 投递语义矩阵
|
|
154
|
+
|
|
155
|
+
| 接收方 | 状态 | 行为 |
|
|
156
|
+
|---|---|---|
|
|
157
|
+
| claude(`crosschat claude` 启动) | 窗口开 | ✅ 秒达,会话内出现信封消息 |
|
|
158
|
+
| claude(裸 `claude` 启动) | 任何 | ❌ 无接收许可(换 `crosschat claude` 重启) |
|
|
159
|
+
| codex(daemon ≥0.160) | 窗口开/关 | ✅ 送达(headless 执行;TUI 不实时刷新,翻历史可见) |
|
|
160
|
+
| codex(daemon ≥0.160) | turn 进行中(忙) | ✅ **入队即达**:`queued`(按线程串行,轮结束瞬间落历史并被处理)——与 Claude 收件箱同粒度 |
|
|
161
|
+
| codex(旧 daemon ≤0.157 / `--no-daemon`) | 窗口开 | ⏳ 等待 120s,关窗瞬间送达;超时 `parked` 入发件箱(看门狗自动重投) |
|
|
162
|
+
| codex | 排队被旧 daemon 拒绝等罕见态 | 📮 `parked` 入发件箱(新消息排队不插队),看门狗每 0.5–5 分钟自动重投 |
|
|
163
|
+
|
|
164
|
+
**不对称速记**:claude 收发都随意;codex 收信在 0.160+ 开窗关窗均可,关窗永远是最稳路径。
|
|
165
|
+
|
|
166
|
+
## 🤝 Agent 兼容性
|
|
167
|
+
|
|
168
|
+
| Agent | 发送 | 接收 | 实测版本 |
|
|
169
|
+
|---|---|---|---|
|
|
170
|
+
| Claude Code | ✅ | ✅(需 `crosschat claude` 启动) | 2.1.287 |
|
|
171
|
+
| Codex CLI | ✅ | ✅(需 daemon;0.160+ 开窗可收) | 0.159.3 / 0.160.0 |
|
|
172
|
+
| 任意有 shell 的 agent | ✅(装 skill 后) | 🔜 按适配器扩展(GLM 等) | 路线图 |
|
|
173
|
+
|
|
174
|
+
## 🗺️ 路线图
|
|
175
|
+
|
|
176
|
+
**已完成(v1.0.0)**
|
|
177
|
+
- [x] Claude ↔ Codex / Claude ↔ Claude / Codex ↔ Codex 双向消息(真机联调验证)
|
|
178
|
+
- [x] 无状态 CLI 四命令 + 自包含会话引用 + 轮次计数
|
|
179
|
+
- [x] 双侧自动发现(含目录/时间标注)、信封自带教学 skill
|
|
180
|
+
- [x] 16KiB 上限、30 条/60s 防乒乓限流、身份冲突自纠指引
|
|
181
|
+
- [x] 忙/锁超时 → 本地发件箱 + 看门狗自动重投(outbox,FIFO 不插队,mailbox 镜像可读)
|
|
182
|
+
- [x] daemon 0.160 开窗投递(实证:同 daemon 多连接绕过写者锁)
|
|
183
|
+
|
|
184
|
+
**计划中**
|
|
185
|
+
- [ ] 🌐 跨机联邦(SSH,异构 win ↔ linux 互聊)
|
|
186
|
+
- [ ] 🐧 mac / linux 平台适配(平台接缝已预留:PipeTransport / ProcessInspector / PathLayout)
|
|
187
|
+
- [ ] 📦 常驻 broker(忙时持久队列、异步回执、投递状态机——embassy 完整集对齐)
|
|
188
|
+
- [ ] 🖼️ TUI 看板与服务安装(开机自启)
|
|
189
|
+
- [ ] 🤖 新 agent 适配器(GLM 等;有原生唤醒通道则原生,否则论证降级)
|
|
190
|
+
- [ ] 👀 status 标注 TUI 占用线程;专用信箱线程机制化
|
|
191
|
+
|
|
192
|
+
## 🧯 错误码排障
|
|
193
|
+
|
|
194
|
+
**使用类**:`NAME_NOT_FOUND`(错误信息列出全部可用名)· `NAME_COLLISION`(重名,`status --json` 看 id)· `MESSAGE_TOO_LARGE`(>16KiB → 落盘发路径)· `RATE_LIMITED`(等待或收尾)· `TARGET_*`/`BODY_*`/`USAGE`(参数错误)
|
|
195
|
+
|
|
196
|
+
**身份类**:`CALLER_IDENTITY_CONFLICT`(环境双身份残留。临时:命令前缀 `env -u CLAUDE_CODE_MESSAGING_SOCKET -u CLAUDE_CODE_MESSAGING_TOKEN -u CLAUDE_CODE_SESSION_ID`;根治:干净终端重启 daemon)· `CALLER_NOT_IN_CONVERSATION` · `CANNOT_REPLY_TO_HUMAN`(对话由人发起)
|
|
197
|
+
|
|
198
|
+
**通道类**:`CODEX_PROXY_SPAWN_FAILED`(看 stderr 摘录;通常 daemon 未跑)· `CODEX_THREAD_LOCKED`/`CODEX_THREAD_BUSY_TIMEOUT`(已自动转 `parked` 入发件箱,无需重发)· `OUTBOX_FULL`(每线程 200 条积压上限,读 mailbox 镜像取回内容)· `CODEX_APPROVAL_REQUIRED`(**工具永不代答审批**)· `CLAUDE_PIPE_*`/`CODEX_*UNCERTAIN`(写入中途失败状态不明——**勿盲目重发**,先 `status` 核实)
|
|
199
|
+
|
|
200
|
+
**发送审计**:每次发送的最终结果(delivered/queued/parked/failed、时间、对端、回执)追加记录在 `%LOCALAPPDATA%\crosschat\send-log.jsonl`;codex 投递附 rollout 回执(消息已确认落入对方会话历史 = `receipt: confirmed`)。命令超时转后台后结果同样在案,事后可查。
|
|
201
|
+
|
|
202
|
+
## ⚠️ 边界与限制
|
|
203
|
+
|
|
204
|
+
单条 ≤16KiB;每对端点 30 条/60s;发件箱每线程 200 条(满时读 `%LOCALAPPDATA%\crosschat\mailbox\<线程ID>.md` 取回内容);信任边界=同一 Windows 用户;接收许可仅 `crosschat claude` 启动的会话。
|
|
205
|
+
|
|
206
|
+
**daemon 依赖(重要)**:向 codex 会话投递走 `codex app-server proxy`,它连接**运行中的 app-server daemon** control socket。要获得完整能力(TUI 开窗可投、忙时入队),需要 `codex app-server daemon start` 且 daemon ≥0.160,TUI 用同版本 CLI 打开(0.160 起 TUI 自动附着 daemon)。daemon 未运行时投递会报 `CODEX_PROXY_SPAWN_FAILED` 并提示启动命令;旧版本 daemon 下开窗投递与忙时入队退化为「关窗投递 + 发件箱」。重启电脑后需重新 `codex app-server daemon start`。
|
|
207
|
+
|
|
208
|
+
## ❓ FAQ
|
|
209
|
+
|
|
210
|
+
**Q:codex 窗口开着收不到?** daemon ≥0.160 开窗也能收(headless,界面不实时刷新);旧 daemon/`--no-daemon` 下等待或已入发件箱,关窗后自动送达。
|
|
211
|
+
|
|
212
|
+
**Q:为什么必须 `crosschat claude`?** 它注入跨会话接收许可;裸 `claude` 的会话收不到。
|
|
213
|
+
|
|
214
|
+
**Q:`codex queue` 能用吗?** 仅当 TUI 附着 daemon(0.160+)时会被消费;旧形态下是黑洞,故 crosschat 不依赖它。
|
|
215
|
+
|
|
216
|
+
**Q:人能发消息吗?** 能(`--to`),身份 human,单向不能被回复。
|
|
217
|
+
|
|
218
|
+
**Q:消息历史在哪看?** claude 侧=会话 transcript;codex 侧=开窗 resume 线程。
|
|
219
|
+
|
|
220
|
+
## 🔧 故障恢复
|
|
221
|
+
|
|
222
|
+
| 症状 | 动作 |
|
|
223
|
+
|---|---|
|
|
224
|
+
| 任何异常 | 先跑 `crosschat doctor` 一键体检(逐项 ✅/⚠️/❌ 定位) |
|
|
225
|
+
| status 的 codex 段 unavailable | 干净终端 `codex app-server daemon start` |
|
|
226
|
+
| codex 回信撞身份冲突 | 同上(重启 daemon 即根治) |
|
|
227
|
+
| skill 误删/过期 | `crosschat install-skills` |
|
|
228
|
+
| 升级 crosschat 代码后 | `npm run build`(skill 有变再 install-skills) |
|
|
229
|
+
| 消息发出对方没反应 | 先 `status` 确认在线;`parked` 的等对方空闲自动补投;UNCERTAIN 类勿重发先核实 |
|
|
230
|
+
|
|
231
|
+
## 📚 更多文档
|
|
232
|
+
|
|
233
|
+
| 文档 | 内容 |
|
|
234
|
+
|---|---|
|
|
235
|
+
| [docs/architecture.md](docs/architecture.md) | **底层原理**:注册表/命名管道/daemon 通道、写者锁本质、信封与自包含引用、完整投递流程图 |
|
|
236
|
+
| [CHANGELOG.md](CHANGELOG.md) | 版本变更记录 |
|
|
237
|
+
| [docs/wayfinder/map.md](docs/wayfinder/map.md) | 设计决策地图(全部拍板过程与依据) |
|
|
238
|
+
| [docs/research/](docs/research/) | 实测研究报告(embassy 源码分析、两侧通道验证、0.160 inject_items 实验) |
|
|
239
|
+
| [docs/drill-reports/](docs/drill-reports/) | 联调实证记录 |
|
|
240
|
+
|
|
241
|
+
## 🛠️ 开发说明
|
|
242
|
+
|
|
243
|
+
```powershell
|
|
244
|
+
npm run check # lint + build + test
|
|
245
|
+
$env:CROSSCHAT_LIVE='1'; npx vitest run --dir test # 真机 live 测试
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
- 目录:`src/claude`(注册表/管道/鉴权)· `src/codex`(proxy/RPC/投递)· `src/commands`(CLI)· `src/platform`(平台接缝)· `src/outbox.ts`(发件箱)· `skills/`(agent 教学)
|
|
249
|
+
- 铭谢:[embassy](https://github.com/YuanpingSong/embassy)(MIT)的信封格式与 codex 传输模式
|
|
250
|
+
- License:[MIT](LICENSE)
|
|
251
|
+
|
|
252
|
+
---
|
|
253
|
+
|
|
254
|
+
<div align="center">Made with 🤝 between agents — crosschat 让它们自己对话</div>
|
package/dist/cli.js
CHANGED
|
@@ -11,29 +11,32 @@ import { runDoctor } from './commands/doctor.js';
|
|
|
11
11
|
import { runClaudeWrapper } from './commands/claude-wrapper.js';
|
|
12
12
|
import { defaultRateDir } from './rate-limit.js';
|
|
13
13
|
import { defaultOutboxDir, drain } from './outbox.js';
|
|
14
|
+
import { runWatchdog, spawnWatchdog as ensureWatchdog } from './watchdog.js';
|
|
15
|
+
import { appendSendLog, confirmInRollout, defaultSendLogFile } from './send-log.js';
|
|
16
|
+
import { codexHomeDir } from './codex/rollout-meta.js';
|
|
14
17
|
import { listClaudeSessions } from './claude/registry.js';
|
|
15
18
|
import { deliverToClaudeSession } from './claude/deliver.js';
|
|
16
19
|
import { listCodexThreads } from './codex/discovery.js';
|
|
17
20
|
import { deliverToCodexThread } from './codex/deliver.js';
|
|
18
21
|
const version = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8')).version;
|
|
19
|
-
const usage = `usage: crosschat <command> [options]
|
|
20
|
-
|
|
21
|
-
commands:
|
|
22
|
-
send --to <name> | --conversation <ref>
|
|
23
|
-
send a message (--body <text> or stdin);
|
|
24
|
-
quote <name> if it contains spaces
|
|
25
|
-
status show claude/codex session status
|
|
26
|
-
doctor environment health check (exits 1 on any ❌)
|
|
27
|
-
install-skills [--dir <root>]
|
|
28
|
-
install the agent skill into <root>/.claude and
|
|
29
|
-
<root>/.codex (default root: home directory)
|
|
30
|
-
claude [args...] run the real claude CLI with inbound peer
|
|
31
|
-
messaging enabled; other args pass through
|
|
32
|
-
|
|
33
|
-
options:
|
|
34
|
-
--json single-line JSON output (send/status)
|
|
35
|
-
-v, --version print version and exit
|
|
36
|
-
--help | help show this help and exit
|
|
22
|
+
const usage = `usage: crosschat <command> [options]
|
|
23
|
+
|
|
24
|
+
commands:
|
|
25
|
+
send --to <name> | --conversation <ref>
|
|
26
|
+
send a message (--body <text> or stdin);
|
|
27
|
+
quote <name> if it contains spaces
|
|
28
|
+
status show claude/codex session status
|
|
29
|
+
doctor environment health check (exits 1 on any ❌)
|
|
30
|
+
install-skills [--dir <root>]
|
|
31
|
+
install the agent skill into <root>/.claude and
|
|
32
|
+
<root>/.codex (default root: home directory)
|
|
33
|
+
claude [args...] run the real claude CLI with inbound peer
|
|
34
|
+
messaging enabled; other args pass through
|
|
35
|
+
|
|
36
|
+
options:
|
|
37
|
+
--json single-line JSON output (send/status)
|
|
38
|
+
-v, --version print version and exit
|
|
39
|
+
--help | help show this help and exit
|
|
37
40
|
`;
|
|
38
41
|
/** Hand-rolled send arg parsing (no dependency): --to/--conversation/--body take values, --json is a flag. */
|
|
39
42
|
export function parseSendArgs(argv) {
|
|
@@ -117,6 +120,9 @@ function realSendDeps(stdinText) {
|
|
|
117
120
|
rateDir: defaultRateDir(),
|
|
118
121
|
outboxDir: defaultOutboxDir(),
|
|
119
122
|
now: () => Date.now(),
|
|
123
|
+
spawnWatchdog: () => ensureWatchdog(defaultOutboxDir()),
|
|
124
|
+
appendLog: (entry) => appendSendLog(defaultSendLogFile(), entry),
|
|
125
|
+
confirmReceipt: (threadId, marker) => confirmInRollout(codexHomeDir(), threadId, marker),
|
|
120
126
|
};
|
|
121
127
|
}
|
|
122
128
|
function realStatusDeps() {
|
|
@@ -177,6 +183,20 @@ async function main(argv) {
|
|
|
177
183
|
process.stdout.write(usage);
|
|
178
184
|
return 0;
|
|
179
185
|
}
|
|
186
|
+
// Hidden maintenance entry (B12): one drain round by hand, or the watchdog
|
|
187
|
+
// loop when spawned with --watch. Not in usage on purpose.
|
|
188
|
+
if (command === '__drain') {
|
|
189
|
+
if (rest.includes('--watch')) {
|
|
190
|
+
await runWatchdog(defaultOutboxDir(), defaultRateDir(), {
|
|
191
|
+
deliverCodex: (threadId, content, busyTimeoutMs) => deliverToCodexThread(threadId, content, { busyTimeoutMs }),
|
|
192
|
+
err: (line) => process.stderr.write(`${line}\n`),
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
else {
|
|
196
|
+
await drainAtEntry();
|
|
197
|
+
}
|
|
198
|
+
return 0;
|
|
199
|
+
}
|
|
180
200
|
if (command === 'send') {
|
|
181
201
|
if (rest.includes('--help')) {
|
|
182
202
|
process.stdout.write(usage);
|