Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
# Agent 笔记:调试网络通路与普通 TCP 生命周期的边界

状态:提议中

## 先说结论

当前调试传输这轮只做普通 TCP(可靠的双向字节流)连接和生命周期,复用 [#1520](https://github.com/1lck/Lithe-IDEA/pull/1520) 的 socket 基础,不另写一套 TCP 实现,也不取代标准输入输出(stdio)传输。代理、隧道、端口转发和远端执行是独立的网络通路方案,本轮不实现,不作为普通 TCP 的交付前置。

远程调试不等于必须使用 SSH:端点可以直接可达,也可以经用户自己配置的 VPN 或隧道可达;Lithe 本轮只连接最终可达的端点,不创建或管理这些网络设施。[#1525](https://github.com/1lck/Lithe-IDEA/pull/1525) 当前只实现远程 attach 的契约与 Rust 校验;本 Note 记录后续连接实现的边界和验收要求,不表示宿主已经支持全部场景。

## 问题

一次远程调试可能有两条不同的连接,不能把它们的主机、端口和权限混成一个目标:

```text
IDE -- DAP(调试适配器协议,stdio 或 TCP)--> 调试适配器
调试适配器 -- 调试器自己的协议 --> 被调试程序
```

- Java:IDE 可以通过回环 TCP 连接 JDT LS 内的 Java Debug Server;适配器再通过 Java 调试线协议(JDWP)连接远端 JVM。`targetBindings.connection` 绑定的是后一条连接的参数。
- GDB:IDE 可以通过 stdio 连接本机 `gdb -i=dap`,GDB 再通过 GDB Remote 协议连接远端 `gdbserver`,并非把 DAP 发给 gdbserver。
- Python:默认 `python -m debugpy --listen host:port` 会启动 DAP 适配器,IDE 需要直接连接该 DAP 端点。只在本机另起适配器并填入 `connect.host/port`,不能据此宣称这种模式可用。

连接方式与 DAP 的 `launch` / `attach` 请求也是两个维度:连接一个已有 DAP 服务,不等于发送 `attach`,更不等于已经完成 SSH 或代理配置。

## 提案

### 本轮只交付普通 TCP 与生命周期

| 边界 | 开发动作 |
| --- | --- |
| 复用已有传输 | 复用 #1520 的 socket 接口、异步端点解析和会话清理思路;`tcpLanguageServer` 从同插件 LSP 命令获取回环端口。现有实现不是任意远端 DAP 的直连入口。 |
| 不混用端点 | 自动取得的语言服务器端点继续限制在回环。未来用户提供的外部 DAP 端点要有独立来源、确认和校验,不能直接把 attach 目标的 `host/port` 当作 IDE 的 socket 地址。 |
| 正常连接与收发 | 明确连接准备、连接中、可收发、停止和失败;有界等待端点解析、建连及停止,连接成功前的写入有顺序和资源上限,读写失败不静默丢弃。 |
| 取消与过期结果 | 解析端点、建连、等待响应时都能取消。回调绑定会话和连接代次,旧连接晚到的成功、数据或失败不能改变新会话,也不能重新打开已停止的连接。 |
| 断开与清理 | EOF、连接失败、读写失败、超时、用户停止都进入同一条幂等清理链;关闭 socket,撤销回调和计时器,清空待发数据,终止通知只发一次。失联不承诺瞬时可知,也不自动重放请求或重连会话。 |
| 按所有权停止 | 仅清理本会话创建的 socket、适配器进程和其他资源。共享语言服务器、用户启动的外部 DAP 服务、attach 目标和用户网络设施不归 socket 所有;attach 默认只断开,不结束被调试程序。 |

会话可以持续任意长时间,不设置调试会话总时长;有界的是准备、建连、请求和停止等操作。哪些错误属于传输失败,哪些属于 DAP 请求失败,要保留各自原因,不能把连接失败误报为适配器参数无效。

### 独立网络方案:仅记录后续问题

网络通路指“让某个端点可达的办法”,不负责 DAP 帧、语言语义或调试器参数。本轮不新增代理、路由或隧道的声明字段,也不把所有远程调试统一成 SSH。

后续独立方案再决定:

- 直连、SOCKS / HTTP CONNECT 代理、SSH 隧道 / 跳板和平台转发如何选择和组合;先核对可复用的系统工具、远程工作区能力及各平台限制,不自研代理协议栈。
- 访问的是 IDE → 适配器,还是适配器 → 被调试程序。后一条由适配器所在机器发起,本机代理设置不会自动让它走同一条通路。
- 目的地、认证、主机身份、权限、用户确认和诊断怎样表达;敏感信息不得进入普通日志,不能承诺会话重连或网络自动恢复。
- 谁创建通路、谁持有它、是否共享,以及取消或失败后如何回滚已创建的监听端口、转发连接与辅助进程。
- 远端执行与路径映射如何协作。优先使用适配器已有的映射能力;同一对路径空间只转换一次,不因为适配器在远端就强制 Core 再翻译一遍。

已有[远程开发的传输链、隧道与端口转发](2026-10-24-remote-development-workspace-host.md)是后续复用候选,不代表调试产品已经接入或具备完整的托管生命周期。[SSH + stdio 草案](2026-10-27-remote-debug-adapter-over-ssh.md)只是其中一个暂缓的子方案,不再是本轮的下一项交付。

### 正确做法与反例

- 正确:用户自己建立隧道,提供最终端点;在对应端点入口已实现后,Lithe 按普通 TCP 连接,结束时只关闭自己的 socket。
- 正确:Java 结束 attach 时断开 DAP 会话并释放 socket,但不停止共享 JDT LS 或远端 JVM。
- 不要:把 `tcpLanguageServer` 的回环检查放开,就宣称支持任意远程 DAP 服务。
- 不要:在 TCP 的建连函数里临时拉起 `ssh -L`、解析代理密码,或在停止时杀死用户的隧道。

## 考虑过的备选方案

- **把代理、SSH 和转发一起做进本轮 TCP。** 看起来一次覆盖远程调试,但认证、监听暴露和共享资源的清理远超 socket 生命周期,还会重复已有远程工作区能力。改为独立方案。
- **所有调试都改成 TCP 或经 SSH 的 stdio。** 接口看似单一,但 GDB 的 stdio、Java 的回环 DAP 与 debugpy 的远端 DAP 都有合理用法;保留传输选择,不强迫适配器换协议。
- **为远端直连另写一套 TCP 传输。** 容易重复建连、取消和过期回调处理。端点来源可以扩展,底层 socket 和生命周期应复用;扩展入口未实现时仍明确报告不支持。

## 验收标准

- 正常连接可有序双向收发;端点解析失败、拒绝连接、建连超时、读写失败、EOF 都有明确结束结果。
- 取消覆盖端点解析中、建连中和连接成功后;停止后到达的回调全部失效,重复停止不重复通知、释放或污染下一次连接。
- 待发数据、操作等待和清理都有界;在对端不读、静默失联或停止期间不会无限积压,也不依赖自动重连掩盖失败。
- 结束后 socket、回调、任务及本轮创建的进程被释放;外部 DAP 服务、共享语言服务器、attach 目标与用户的 VPN / 隧道保持不变。
- #1520 的回环端点限制不放宽,stdio 行为不退化;外部 DAP 直连只有对应入口实现并验收后才能标为支持。
- 不引入代理认证、托管隧道、端口转发或 SSH 远端启动代码;这些能力另行评审,不阻塞本轮 TCP。

## 风险

- 普通 TCP 不提供认证和加密。最终端点必须位于用户认可的受保护网络,不能把裸调试端口推荐为公网服务。
- 网络可达不等于协议匹配或源码路径可用。真实适配器验收必须核对端点协议、DAP 请求和上游路径映射。
- 复用现有 socket 不代表生命周期要求已全部满足;有界写入、失联策略和清理必须通过实际实现及测试证明,不能靠设计文档标为已完成。

## 验证

本轮仅补充设计:运行 `./scripts/verify-agent-notes.sh`、`./scripts/verify-shared-contracts.sh`、`./scripts/verify-platform-feature-matrix.sh` 和 `git diff --check`,不据此声称有运行时连接证据。

后续实现先加载 `write-stable-tests`,用可控 socket 和有界等待覆盖验收标准,再执行对应平台测试与真实适配器流程;同步平台功能矩阵,验证发行包只读,清理本次启动的所有进程。

## 适用范围

- `rust/lithe-core/src/debug/`
- `macos/Sources/Lithe/Platform/MacOS/Debug/`
- `windows/tauri/src-tauri/src/debug.rs`
- `shared/contracts/plugins/v1/`
- `rust/lithe-remote-client/src/transport.rs`
- `rust/lithe-remote-client/src/forward.rs`

## 依据

- [debugpy 1.8.22 的 listen 会启动适配器](https://github.com/microsoft/debugpy/blob/v1.8.22/src/debugpy/server/api.py#L189-L225)。
- [统一调试内核与远程 attach 兼容性审计](2026-10-25-unified-debug-engine-and-adapter-registry.md)。
Loading
Loading