codex-to-im 0.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/LICENSE +21 -0
- package/README.md +163 -0
- package/README_CN.md +161 -0
- package/SECURITY.md +38 -0
- package/SKILL.md +79 -0
- package/config.env.example +106 -0
- package/dist/cli.mjs +182 -0
- package/dist/daemon.mjs +206122 -0
- package/dist/ui-server.mjs +7725 -0
- package/docs/codex-to-im-prd.md +424 -0
- package/docs/codex-to-im-shared-thread-design.md +572 -0
- package/docs/install-windows.md +287 -0
- package/package.json +55 -0
- package/references/setup-guides.md +329 -0
- package/references/token-validation.md +44 -0
- package/references/troubleshooting.md +88 -0
- package/references/usage.md +46 -0
- package/scripts/build.js +36 -0
- package/scripts/daemon.ps1 +16 -0
- package/scripts/daemon.sh +225 -0
- package/scripts/doctor.ps1 +27 -0
- package/scripts/doctor.sh +450 -0
- package/scripts/install-codex.sh +65 -0
- package/scripts/run-tests.js +44 -0
- package/scripts/supervisor-linux.sh +49 -0
- package/scripts/supervisor-macos.sh +154 -0
- package/scripts/supervisor-windows.ps1 +481 -0
|
@@ -0,0 +1,572 @@
|
|
|
1
|
+
# Codex-to-IM 共享 Thread 技术设计
|
|
2
|
+
|
|
3
|
+
## 1. 文档目标
|
|
4
|
+
|
|
5
|
+
本文档在 PRD 的基础上,进一步定义 `codex-to-im` 的共享 thread 架构。
|
|
6
|
+
|
|
7
|
+
关注点不是 IM 渠道接入本身,而是:
|
|
8
|
+
|
|
9
|
+
- 如何发现 `Codex Windows App` 的真实会话
|
|
10
|
+
- 如何把飞书 chat 绑定到同一条底层 `thread_id`
|
|
11
|
+
- 如何让桌面端和飞书端围绕同一条 thread 无缝切换
|
|
12
|
+
- 如何在多会话、多入口条件下保持上下文、状态和并发行为可控
|
|
13
|
+
|
|
14
|
+
## 2. 设计结论
|
|
15
|
+
|
|
16
|
+
### 2.1 可行性结论
|
|
17
|
+
|
|
18
|
+
共享 thread 方案可行。
|
|
19
|
+
|
|
20
|
+
当前本地环境已验证:
|
|
21
|
+
|
|
22
|
+
- `@openai/codex-sdk` 支持 `startThread()` 和 `resumeThread(threadId)`
|
|
23
|
+
- SDK README 明确说明 thread 持久化在 `~/.codex/sessions`
|
|
24
|
+
- 本机 `~/.codex/sessions/**/*.jsonl` 中可以稳定读取到:
|
|
25
|
+
- `session_meta.payload.id`
|
|
26
|
+
- `session_meta.payload.cwd`
|
|
27
|
+
- `session_meta.payload.originator`
|
|
28
|
+
- `session_meta.payload.source`
|
|
29
|
+
|
|
30
|
+
这意味着系统可以:
|
|
31
|
+
|
|
32
|
+
- 发现桌面端最近的真实会话
|
|
33
|
+
- 以 `thread_id` 为唯一真相源
|
|
34
|
+
- 通过 `resumeThread(threadId)` 从飞书继续向同一条会话写入
|
|
35
|
+
|
|
36
|
+
### 2.2 不确定点
|
|
37
|
+
|
|
38
|
+
当前没有公开证据表明外部程序可以可靠读取“Codex Windows App 当前聚焦 tab”。
|
|
39
|
+
|
|
40
|
+
因此第一阶段不应依赖“自动接管当前 tab”,而应采用:
|
|
41
|
+
|
|
42
|
+
- 显式共享当前会话
|
|
43
|
+
- 或显式选择最近桌面会话
|
|
44
|
+
|
|
45
|
+
## 3. 设计原则
|
|
46
|
+
|
|
47
|
+
### 3.1 单一真实会话
|
|
48
|
+
|
|
49
|
+
桌面端和飞书端必须共享同一条底层 `thread_id`。
|
|
50
|
+
|
|
51
|
+
不采用以下模型:
|
|
52
|
+
|
|
53
|
+
- 桌面端一条 thread
|
|
54
|
+
- 飞书端另一条 thread
|
|
55
|
+
- 再做文本搬运或伪同步
|
|
56
|
+
|
|
57
|
+
### 3.2 桌面优先
|
|
58
|
+
|
|
59
|
+
`Codex Windows App` 是主工作台。
|
|
60
|
+
|
|
61
|
+
飞书是远程控制和远程观察端,不是独立主入口。
|
|
62
|
+
|
|
63
|
+
### 3.3 显式绑定优先于猜测
|
|
64
|
+
|
|
65
|
+
系统可以帮助用户发现候选桌面会话,但不能假设“最近会话”必然就是用户当前想共享的会话。
|
|
66
|
+
|
|
67
|
+
因此所有共享动作都应是显式确认。
|
|
68
|
+
|
|
69
|
+
### 3.4 文本命令优先于卡片能力
|
|
70
|
+
|
|
71
|
+
飞书卡片、按钮、富交互可以增强体验,但会话切换和 thread 绑定必须能通过固定文本命令完成。
|
|
72
|
+
|
|
73
|
+
## 4. 术语定义
|
|
74
|
+
|
|
75
|
+
### 4.1 Desktop Session
|
|
76
|
+
|
|
77
|
+
由 `Codex Windows App` 或本地 Codex SDK 产生的真实会话,底层由 `thread_id` 标识,并持久化在 `~/.codex/sessions`。
|
|
78
|
+
|
|
79
|
+
### 4.2 Shared Session
|
|
80
|
+
|
|
81
|
+
`codex-to-im` 产品层暴露给用户的“共享会话”。
|
|
82
|
+
|
|
83
|
+
一个 `Shared Session` 对应一个底层 `thread_id`,并附加便于用户理解和管理的元信息。
|
|
84
|
+
|
|
85
|
+
### 4.3 Channel Control Binding
|
|
86
|
+
|
|
87
|
+
某个 IM chat 当前控制哪个 `Shared Session` 的绑定关系。
|
|
88
|
+
|
|
89
|
+
第一阶段默认规则:
|
|
90
|
+
|
|
91
|
+
- 一个飞书 chat 同时只控制一个活动共享会话
|
|
92
|
+
|
|
93
|
+
### 4.4 Mirror
|
|
94
|
+
|
|
95
|
+
将 thread 中产生的增量事件同步到飞书,包括:
|
|
96
|
+
|
|
97
|
+
- 模型输出
|
|
98
|
+
- 工具调用
|
|
99
|
+
- 状态变化
|
|
100
|
+
- 权限请求
|
|
101
|
+
|
|
102
|
+
## 5. 总体架构
|
|
103
|
+
|
|
104
|
+
```mermaid
|
|
105
|
+
flowchart LR
|
|
106
|
+
A["Codex Windows App"] --> B["~/.codex/sessions JSONL"]
|
|
107
|
+
C["Feishu Adapter"] --> D["Channel Command Router"]
|
|
108
|
+
D --> E["Shared Session Registry"]
|
|
109
|
+
F["Desktop Session Indexer"] --> E
|
|
110
|
+
G["Session Tailer / Mirror Engine"] --> E
|
|
111
|
+
E --> H["Codex Provider"]
|
|
112
|
+
H --> I["@openai/codex-sdk resumeThread(threadId)"]
|
|
113
|
+
I --> B
|
|
114
|
+
G --> C
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### 5.1 组件分层
|
|
118
|
+
|
|
119
|
+
- `Desktop Session Indexer`
|
|
120
|
+
- 扫描 `~/.codex/sessions`
|
|
121
|
+
- 发现最近桌面会话
|
|
122
|
+
- 维护 thread 元信息索引
|
|
123
|
+
- `Shared Session Registry`
|
|
124
|
+
- 把产品层共享会话映射到底层 `thread_id`
|
|
125
|
+
- 保存展示名称、工作目录、最后活跃时间、共享状态
|
|
126
|
+
- `Channel Command Router`
|
|
127
|
+
- 解析飞书固定命令
|
|
128
|
+
- 负责会话选择、切换、绑定、停止
|
|
129
|
+
- `Session Tailer / Mirror Engine`
|
|
130
|
+
- tail `~/.codex/sessions` 对应 JSONL
|
|
131
|
+
- 把桌面端新增事件推送到飞书
|
|
132
|
+
- `Codex Provider`
|
|
133
|
+
- 用 `resumeThread(threadId)` 继续同一条 thread
|
|
134
|
+
- 把飞书消息送入共享会话
|
|
135
|
+
|
|
136
|
+
## 6. 与现有实现的衔接
|
|
137
|
+
|
|
138
|
+
当前代码中已经存在可复用的基础能力:
|
|
139
|
+
|
|
140
|
+
- [D:/codex/Claude-to-IM-skill/src/codex-provider.ts](D:/codex/Claude-to-IM-skill/src/codex-provider.ts)
|
|
141
|
+
- 已具备 `sdkSessionId` 复用逻辑
|
|
142
|
+
- 已使用 `startThread()` / `resumeThread()`
|
|
143
|
+
- [D:/codex/Claude-to-IM-skill/src/store.ts](D:/codex/Claude-to-IM-skill/src/store.ts)
|
|
144
|
+
- 已能持久化 session、binding、message、lock
|
|
145
|
+
- binding 已保存 `sdkSessionId`
|
|
146
|
+
- [D:/codex/Claude-to-IM-skill/src/lib/bridge/channel-router.ts](D:/codex/Claude-to-IM-skill/src/lib/bridge/channel-router.ts)
|
|
147
|
+
- 已支持 chat 到 session 的自动解析与绑定
|
|
148
|
+
- [D:/codex/Claude-to-IM-skill/src/lib/bridge/bridge-manager.ts](D:/codex/Claude-to-IM-skill/src/lib/bridge/bridge-manager.ts)
|
|
149
|
+
- 已支持 `/new`、`/bind`、`/status`、`/sessions`、`/stop`
|
|
150
|
+
|
|
151
|
+
当前缺失的不是桥接基础设施,而是“桌面真实会话接管”的这一层:
|
|
152
|
+
|
|
153
|
+
- 现有 `/sessions` 列出的主要是 bridge 自己管理的 session
|
|
154
|
+
- 还没有桌面 session 发现器
|
|
155
|
+
- 还没有针对 `~/.codex/sessions` 的 tail mirror
|
|
156
|
+
- 还没有“飞书显式切换到某条桌面 thread”的正式模型
|
|
157
|
+
|
|
158
|
+
## 7. 数据模型
|
|
159
|
+
|
|
160
|
+
### 7.1 DiscoveredDesktopSession
|
|
161
|
+
|
|
162
|
+
表示从 `~/.codex/sessions` 扫描得到的候选桌面会话。
|
|
163
|
+
|
|
164
|
+
```ts
|
|
165
|
+
interface DiscoveredDesktopSession {
|
|
166
|
+
threadId: string;
|
|
167
|
+
filePath: string;
|
|
168
|
+
cwd: string;
|
|
169
|
+
originator: string;
|
|
170
|
+
source?: string;
|
|
171
|
+
cliVersion?: string;
|
|
172
|
+
firstSeenAt: string;
|
|
173
|
+
lastEventAt: string;
|
|
174
|
+
title?: string;
|
|
175
|
+
activeEstimate: boolean;
|
|
176
|
+
}
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
说明:
|
|
180
|
+
|
|
181
|
+
- `activeEstimate` 只是启发式字段,用于 UI 排序,不能等同于“当前 tab”
|
|
182
|
+
|
|
183
|
+
### 7.2 SharedSession
|
|
184
|
+
|
|
185
|
+
表示产品层共享会话。
|
|
186
|
+
|
|
187
|
+
```ts
|
|
188
|
+
interface SharedSession {
|
|
189
|
+
id: string;
|
|
190
|
+
threadId: string;
|
|
191
|
+
displayName: string;
|
|
192
|
+
cwd: string;
|
|
193
|
+
source: "desktop" | "bridge";
|
|
194
|
+
createdAt: string;
|
|
195
|
+
updatedAt: string;
|
|
196
|
+
syncToFeishu: boolean;
|
|
197
|
+
mirrorMode: "off" | "read_only" | "interactive";
|
|
198
|
+
lastEventAt: string;
|
|
199
|
+
lastOutputPreview?: string;
|
|
200
|
+
}
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### 7.3 ChannelControlBinding
|
|
204
|
+
|
|
205
|
+
表示飞书 chat 当前控制哪个共享会话。
|
|
206
|
+
|
|
207
|
+
```ts
|
|
208
|
+
interface ChannelControlBinding {
|
|
209
|
+
id: string;
|
|
210
|
+
channelType: string;
|
|
211
|
+
chatId: string;
|
|
212
|
+
sharedSessionId: string;
|
|
213
|
+
threadId: string;
|
|
214
|
+
mode: "interactive" | "read_only";
|
|
215
|
+
createdAt: string;
|
|
216
|
+
updatedAt: string;
|
|
217
|
+
}
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
### 7.4 MirrorCursor
|
|
221
|
+
|
|
222
|
+
表示某个 thread 同步到飞书时读到了 JSONL 的哪个位置。
|
|
223
|
+
|
|
224
|
+
```ts
|
|
225
|
+
interface MirrorCursor {
|
|
226
|
+
threadId: string;
|
|
227
|
+
filePath: string;
|
|
228
|
+
offsetBytes: number;
|
|
229
|
+
lastEventHash?: string;
|
|
230
|
+
lastMirroredAt?: string;
|
|
231
|
+
}
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
## 8. 本地存储建议
|
|
235
|
+
|
|
236
|
+
建议在现有 `~/.codex-to-im/data` 下新增以下文件:
|
|
237
|
+
|
|
238
|
+
- `desktop-sessions.json`
|
|
239
|
+
- 缓存已发现的桌面会话索引
|
|
240
|
+
- `shared-sessions.json`
|
|
241
|
+
- 共享会话元数据
|
|
242
|
+
- `channel-control-bindings.json`
|
|
243
|
+
- 飞书 chat 当前控制目标
|
|
244
|
+
- `mirror-cursors.json`
|
|
245
|
+
- tail 进度
|
|
246
|
+
|
|
247
|
+
现有文件继续保留:
|
|
248
|
+
|
|
249
|
+
- `sessions.json`
|
|
250
|
+
- `bindings.json`
|
|
251
|
+
- `messages/*.json`
|
|
252
|
+
|
|
253
|
+
原则上:
|
|
254
|
+
|
|
255
|
+
- `sessions.json` 继续服务 bridge 自己创建的逻辑会话
|
|
256
|
+
- `shared-sessions.json` 负责“桌面真实会话接管”场景
|
|
257
|
+
|
|
258
|
+
## 9. 核心流程
|
|
259
|
+
|
|
260
|
+
### 9.1 流程 A:发现最近桌面会话
|
|
261
|
+
|
|
262
|
+
1. `Desktop Session Indexer` 扫描 `~/.codex/sessions/**/*.jsonl`
|
|
263
|
+
2. 读取每个文件首条 `session_meta`
|
|
264
|
+
3. 抽取 `threadId`、`cwd`、`originator`、`source`
|
|
265
|
+
4. 以文件最后修改时间或最后事件时间排序
|
|
266
|
+
5. 输出“最近桌面会话”列表给本地 UI
|
|
267
|
+
|
|
268
|
+
结果:
|
|
269
|
+
|
|
270
|
+
- 用户可以在 UI 中选择“要共享到飞书的会话”
|
|
271
|
+
|
|
272
|
+
### 9.2 流程 B:桌面会话共享到飞书
|
|
273
|
+
|
|
274
|
+
首选交互:
|
|
275
|
+
|
|
276
|
+
- 在桌面端提供一个按钮:`同步当前会话到飞书`
|
|
277
|
+
|
|
278
|
+
第一阶段可落地的替代方案:
|
|
279
|
+
|
|
280
|
+
- 在本地 UI 中列出“最近桌面会话”
|
|
281
|
+
- 用户选择其中一条
|
|
282
|
+
- 绑定到某个飞书 chat
|
|
283
|
+
|
|
284
|
+
绑定成功后:
|
|
285
|
+
|
|
286
|
+
- 创建或更新 `SharedSession`
|
|
287
|
+
- 创建或更新 `ChannelControlBinding`
|
|
288
|
+
- 启动该 thread 的 mirror
|
|
289
|
+
|
|
290
|
+
### 9.3 流程 C:飞书继续向共享 thread 发消息
|
|
291
|
+
|
|
292
|
+
```mermaid
|
|
293
|
+
sequenceDiagram
|
|
294
|
+
participant F as Feishu User
|
|
295
|
+
participant A as Feishu Adapter
|
|
296
|
+
participant R as Command Router
|
|
297
|
+
participant S as Shared Session Registry
|
|
298
|
+
participant P as Codex Provider
|
|
299
|
+
participant C as Codex SDK
|
|
300
|
+
|
|
301
|
+
F->>A: 普通消息
|
|
302
|
+
A->>R: 解析 chat 当前 binding
|
|
303
|
+
R->>S: 取出 sharedSession.threadId
|
|
304
|
+
S-->>R: threadId
|
|
305
|
+
R->>P: streamChat(threadId, prompt)
|
|
306
|
+
P->>C: resumeThread(threadId)
|
|
307
|
+
C-->>P: thread events
|
|
308
|
+
P-->>A: SSE events
|
|
309
|
+
A-->>F: 输出 / 状态 / 工具信息
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
关键点:
|
|
313
|
+
|
|
314
|
+
- 飞书消息不能再走“新建私有 bridge thread”
|
|
315
|
+
- 必须命中当前 chat 绑定的 `threadId`
|
|
316
|
+
|
|
317
|
+
### 9.4 流程 D:桌面端输出镜像到飞书
|
|
318
|
+
|
|
319
|
+
1. `Session Tailer` 持续读取目标 JSONL 的新增行
|
|
320
|
+
2. 识别出模型输出、工具调用、权限请求、状态事件
|
|
321
|
+
3. 生成结构化镜像事件
|
|
322
|
+
4. 推送到飞书
|
|
323
|
+
|
|
324
|
+
镜像内容建议分级:
|
|
325
|
+
|
|
326
|
+
- 必须同步:
|
|
327
|
+
- 文本输出
|
|
328
|
+
- 工具开始/完成
|
|
329
|
+
- 权限请求
|
|
330
|
+
- 运行中 / 空闲 / 失败
|
|
331
|
+
- 可选同步:
|
|
332
|
+
- token 统计
|
|
333
|
+
- 全量原始事件
|
|
334
|
+
|
|
335
|
+
## 10. 飞书命令模型
|
|
336
|
+
|
|
337
|
+
### 10.1 第一版命令集
|
|
338
|
+
|
|
339
|
+
建议保留并扩展以下命令:
|
|
340
|
+
|
|
341
|
+
- `/status`
|
|
342
|
+
- 查看当前 chat 正在控制的共享会话、thread、工作目录、运行状态
|
|
343
|
+
- `/sessions`
|
|
344
|
+
- 列出当前用户可切换的共享会话
|
|
345
|
+
- `/use <shared-session-id>`
|
|
346
|
+
- 切换当前 chat 控制的共享会话
|
|
347
|
+
- `/threads`
|
|
348
|
+
- 列出最近发现的桌面 thread
|
|
349
|
+
- `/thread <thread-id>`
|
|
350
|
+
- 直接把当前 chat 切换到指定 thread 对应的共享会话
|
|
351
|
+
- `/bind <thread-id>`
|
|
352
|
+
- 若该 thread 还没有共享会话,则创建并绑定
|
|
353
|
+
- `/new`
|
|
354
|
+
- 新建一条 bridge 自己管理的新共享会话
|
|
355
|
+
- `/stop`
|
|
356
|
+
- 停止当前共享会话正在执行的任务
|
|
357
|
+
|
|
358
|
+
### 10.2 语义划分
|
|
359
|
+
|
|
360
|
+
- `session`
|
|
361
|
+
- 面向用户的产品层对象
|
|
362
|
+
- `thread`
|
|
363
|
+
- 面向 Codex SDK 的底层对象
|
|
364
|
+
|
|
365
|
+
飞书端必须始终能回答两个问题:
|
|
366
|
+
|
|
367
|
+
- 我当前控制的是哪个 `Shared Session`
|
|
368
|
+
- 它底下对应的是哪个 `thread_id`
|
|
369
|
+
|
|
370
|
+
## 11. 并发与切换规则
|
|
371
|
+
|
|
372
|
+
### 11.1 单 thread 串行写入
|
|
373
|
+
|
|
374
|
+
同一条 `thread_id` 在任意时刻只允许一个写入任务处于运行中。
|
|
375
|
+
|
|
376
|
+
原因:
|
|
377
|
+
|
|
378
|
+
- 防止桌面端和飞书端同时向同一 thread 提交输入
|
|
379
|
+
- 防止工具链并发执行导致上下文错乱
|
|
380
|
+
|
|
381
|
+
### 11.2 输入竞争策略
|
|
382
|
+
|
|
383
|
+
第一阶段建议规则:
|
|
384
|
+
|
|
385
|
+
- 如果当前 thread 正在运行,飞书新消息进入等待队列
|
|
386
|
+
- `/stop` 可以取消当前运行
|
|
387
|
+
- 队列中的下一条输入在 thread 空闲后执行
|
|
388
|
+
|
|
389
|
+
替代策略不建议第一阶段就做:
|
|
390
|
+
|
|
391
|
+
- 双端抢占式切换
|
|
392
|
+
- 自动撤销桌面输入
|
|
393
|
+
|
|
394
|
+
### 11.3 控制权提示
|
|
395
|
+
|
|
396
|
+
飞书和本地 UI 都应显示:
|
|
397
|
+
|
|
398
|
+
- 当前 thread 是否运行中
|
|
399
|
+
- 当前消息是否排队
|
|
400
|
+
- 最近一次输入来自哪里:
|
|
401
|
+
- `desktop`
|
|
402
|
+
- `feishu`
|
|
403
|
+
- `bridge`
|
|
404
|
+
|
|
405
|
+
## 12. 去重与镜像规则
|
|
406
|
+
|
|
407
|
+
### 12.1 风险来源
|
|
408
|
+
|
|
409
|
+
如果飞书消息通过 `resumeThread(threadId)` 写入,随后 `Session Tailer` 再去读同一 JSONL,就可能把“bridge 自己触发的输出”再次作为桌面更新回推到飞书。
|
|
410
|
+
|
|
411
|
+
### 12.2 第一版去重方案
|
|
412
|
+
|
|
413
|
+
为每次 bridge 发起的运行生成 `runId`,并维护:
|
|
414
|
+
|
|
415
|
+
- `threadId`
|
|
416
|
+
- `runId`
|
|
417
|
+
- `startedAt`
|
|
418
|
+
- `origin = feishu | desktop | bridge-ui`
|
|
419
|
+
|
|
420
|
+
镜像层在回推飞书时做两层判断:
|
|
421
|
+
|
|
422
|
+
- 已经通过实时流推送给飞书的 event,不再重复镜像
|
|
423
|
+
- 只镜像“来自桌面端新增而非当前 bridge 运行”的事件
|
|
424
|
+
|
|
425
|
+
### 12.3 保守策略
|
|
426
|
+
|
|
427
|
+
第一阶段优先保证“不重复刷屏”,即使因此少同步少量边缘事件,也比双发更稳。
|
|
428
|
+
|
|
429
|
+
## 13. UI 设计要求
|
|
430
|
+
|
|
431
|
+
### 13.1 本地 UI
|
|
432
|
+
|
|
433
|
+
本地 UI 需要新增这些视图:
|
|
434
|
+
|
|
435
|
+
- 最近桌面会话
|
|
436
|
+
- 已共享会话
|
|
437
|
+
- 每条会话的:
|
|
438
|
+
- thread id
|
|
439
|
+
- cwd
|
|
440
|
+
- 最近活跃时间
|
|
441
|
+
- 是否已同步到飞书
|
|
442
|
+
- 当前绑定的飞书 chat
|
|
443
|
+
|
|
444
|
+
操作按钮建议包括:
|
|
445
|
+
|
|
446
|
+
- `同步到飞书`
|
|
447
|
+
- `停止同步`
|
|
448
|
+
- `复制 thread id`
|
|
449
|
+
- `在飞书中接管`
|
|
450
|
+
|
|
451
|
+
### 13.2 飞书侧反馈
|
|
452
|
+
|
|
453
|
+
飞书需要能看到:
|
|
454
|
+
|
|
455
|
+
- 当前会话名称
|
|
456
|
+
- 当前 thread 短 id
|
|
457
|
+
- 当前 cwd
|
|
458
|
+
- 当前状态
|
|
459
|
+
- 最近输出预览
|
|
460
|
+
|
|
461
|
+
## 14. 权限与安全边界
|
|
462
|
+
|
|
463
|
+
### 14.1 可见性
|
|
464
|
+
|
|
465
|
+
默认不应把所有本地桌面会话暴露给任意飞书用户。
|
|
466
|
+
|
|
467
|
+
至少需要:
|
|
468
|
+
|
|
469
|
+
- 飞书用户白名单
|
|
470
|
+
- 或飞书 chat 白名单
|
|
471
|
+
|
|
472
|
+
### 14.2 接管边界
|
|
473
|
+
|
|
474
|
+
只有明确授权的飞书聊天,才能:
|
|
475
|
+
|
|
476
|
+
- 绑定桌面 thread
|
|
477
|
+
- 切换共享会话
|
|
478
|
+
- 向共享会话发送写入指令
|
|
479
|
+
|
|
480
|
+
### 14.3 信息披露控制
|
|
481
|
+
|
|
482
|
+
如果共享会话 cwd 或输出涉及敏感目录,本地 UI 需要允许:
|
|
483
|
+
|
|
484
|
+
- 关闭飞书同步
|
|
485
|
+
- 降级为只读同步
|
|
486
|
+
|
|
487
|
+
## 15. 分阶段落地方案
|
|
488
|
+
|
|
489
|
+
### Phase 2A:桌面会话发现
|
|
490
|
+
|
|
491
|
+
交付目标:
|
|
492
|
+
|
|
493
|
+
- 扫描 `~/.codex/sessions`
|
|
494
|
+
- 列出最近桌面会话
|
|
495
|
+
- 在本地 UI 中可选中某条 thread
|
|
496
|
+
|
|
497
|
+
### Phase 2B:共享 thread 绑定
|
|
498
|
+
|
|
499
|
+
交付目标:
|
|
500
|
+
|
|
501
|
+
- 建立 `SharedSession`
|
|
502
|
+
- 飞书 chat 可绑定指定 `threadId`
|
|
503
|
+
- 飞书普通消息走 `resumeThread(threadId)`
|
|
504
|
+
|
|
505
|
+
### Phase 2C:镜像输出
|
|
506
|
+
|
|
507
|
+
交付目标:
|
|
508
|
+
|
|
509
|
+
- tail JSONL
|
|
510
|
+
- 把桌面端新增输出同步到飞书
|
|
511
|
+
- 对 bridge 自己已推送事件做去重
|
|
512
|
+
|
|
513
|
+
### Phase 2D:飞书会话切换
|
|
514
|
+
|
|
515
|
+
交付目标:
|
|
516
|
+
|
|
517
|
+
- `/sessions`
|
|
518
|
+
- `/use`
|
|
519
|
+
- `/threads`
|
|
520
|
+
- `/thread`
|
|
521
|
+
- `/bind`
|
|
522
|
+
|
|
523
|
+
### Phase 2E:桌面一键共享
|
|
524
|
+
|
|
525
|
+
交付目标:
|
|
526
|
+
|
|
527
|
+
- 在 Codex 侧提供显式按钮或可选集成入口
|
|
528
|
+
- 允许“把当前会话共享到飞书”
|
|
529
|
+
|
|
530
|
+
这是最接近“无缝切换”的一步,但仍然应基于显式动作,而非猜测当前焦点 tab。
|
|
531
|
+
|
|
532
|
+
## 16. 待验证清单
|
|
533
|
+
|
|
534
|
+
进入编码前,建议先验证以下事实:
|
|
535
|
+
|
|
536
|
+
1. 当 bridge 使用 `resumeThread(threadId)` 写入后,Codex Windows App 是否会在切回后显示完整新历史
|
|
537
|
+
2. Codex Windows App 是否会对该 thread 的外部写入做实时刷新
|
|
538
|
+
3. JSONL 中哪些事件最适合用作“镜像到飞书”的来源
|
|
539
|
+
4. 同一 thread 在桌面端和 bridge 端交替写入时,是否会出现 SDK 层冲突或异常
|
|
540
|
+
5. 是否存在稳定标记来区分“桌面发起的 run”和“bridge 发起的 run”
|
|
541
|
+
|
|
542
|
+
## 17. 正式建议
|
|
543
|
+
|
|
544
|
+
共享 thread 架构应成为 `codex-to-im` 的主架构,而不是附加高级功能。
|
|
545
|
+
|
|
546
|
+
原因很直接:
|
|
547
|
+
|
|
548
|
+
- 你的主工作流在桌面
|
|
549
|
+
- 飞书是补充端,但必须能继续下指令
|
|
550
|
+
- 如果不共享真实 thread,就做不到真正的无缝切换
|
|
551
|
+
|
|
552
|
+
因此后续实现优先级应是:
|
|
553
|
+
|
|
554
|
+
1. 发现桌面真实会话
|
|
555
|
+
2. 绑定共享 thread
|
|
556
|
+
3. 飞书远程继续写入同一 thread
|
|
557
|
+
4. 桌面输出镜像回飞书
|
|
558
|
+
5. 飞书命令切换会话 / thread
|
|
559
|
+
|
|
560
|
+
## 18. 参考依据
|
|
561
|
+
|
|
562
|
+
### 本地依据
|
|
563
|
+
|
|
564
|
+
- [D:/codex/Claude-to-IM-skill/node_modules/@openai/codex-sdk/README.md](D:/codex/Claude-to-IM-skill/node_modules/@openai/codex-sdk/README.md)
|
|
565
|
+
- [D:/codex/Claude-to-IM-skill/src/codex-provider.ts](D:/codex/Claude-to-IM-skill/src/codex-provider.ts)
|
|
566
|
+
- [D:/codex/Claude-to-IM-skill/src/store.ts](D:/codex/Claude-to-IM-skill/src/store.ts)
|
|
567
|
+
- [D:/codex/Claude-to-IM-skill/src/lib/bridge/channel-router.ts](D:/codex/Claude-to-IM-skill/src/lib/bridge/channel-router.ts)
|
|
568
|
+
- `%USERPROFILE%/.codex/sessions/**/*.jsonl` 的本地实测样本
|
|
569
|
+
|
|
570
|
+
### 官方资料
|
|
571
|
+
|
|
572
|
+
- [Introducing the Codex app](https://openai.com/index/introducing-the-codex-app/)
|