feat(gateway): [EPIC-GW-02] 引入 neocode:// URL 唤醒分发与网关拦截#288
Conversation
实现了从 OS 层 URL 唤醒到网关底层派发的全链路闭环,当前基于 MessageFrame + IPC 通信。 主要改动: 1. 协议层:新增 neocode_url 解析器,标准化输出 WakeIntent。 2. 调度层:实现 url-dispatch 适配器,作为极简代理通过 IPC (UDS/Pipe) 将 URL 意图透传给网关。 3. 网关层:新增 wake.go 处理器,实施基于白名单的 Action 校验(本期 MVP 仅放行 'review')。 4. 错误处理:Dispatcher 接收到网关业务错误(如 invalid_action)时,统一输出结构化 JSON 并以 Exit(1) 退出。 5. CLI 架构:在 rootCmd 增加判定钩子,豁免 url-dispatch 的全局业务配置预加载,确保其“轻量代理”属性,修复未启动网关时因加载 Provider 导致的报错拦截。
|
/review |
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
|
/review |
|
/review |
|
/review |
|
/review |
|
/review |
| return "", nil | ||
| } | ||
|
|
||
| if strings.Contains(workdir, "..") { |
There was a problem hiding this comment.
strings.Contains(workdir, "..") will reject legitimate absolute paths that merely contain .. in a segment name (for example /opt/app..prod/project). This turns valid requests into unsafe_path errors. Consider checking for traversal by inspecting path segments (reject segment exactly ..) instead of substring matching.
| return "", nil | ||
| } | ||
|
|
||
| if strings.Contains(workdir, "..") { |
There was a problem hiding this comment.
sanitizeWorkdir currently rejects any value containing ".." as a substring. This causes false positives for legitimate absolute paths like /opt/releases/v1..2 (not traversal). Consider validating traversal by path semantics (e.g., inspect cleaned segments / compare normalized form) instead of raw substring matching.
|
|
||
| switch action { | ||
| case protocol.WakeActionReview: | ||
| path := strings.TrimSpace(intent.Params["path"]) |
There was a problem hiding this comment.
For review, the path parameter is only checked for non-empty. Since this comes from external URL input, values like ../../etc/passwd or absolute paths are currently accepted and forwarded. Add path safety validation (reject traversal / absolute paths, or constrain to workdir) before returning ACK.
| return nil | ||
| } | ||
|
|
||
| if err := writeURLDispatchSuccessOutput(os.Stdout, result); err != nil { |
There was a problem hiding this comment.
当 writeURLDispatchSuccessOutput 失败时这里直接返回 error,随后上层只会 exit(1),不会再输出结构化 JSON 错误。这会让 url-dispatch 在 I/O 异常路径下打破当前“统一 JSON 输出”的契约(调用方只能拿到空输出或非结构化 stderr)。建议和上面的失败分支一致:将该错误映射为结构化 {"status":"error",...} 后再退出。
| return "", nil | ||
| } | ||
|
|
||
| if strings.Contains(workdir, "..") { |
There was a problem hiding this comment.
strings.Contains(workdir, "..") 会误伤合法绝对路径(例如 /tmp/a..b/project 或 C:\data\v1..2\repo),导致用户无法使用实际可用目录。更稳妥的做法是先 filepath.Clean,再按路径段判断是否存在 .. 跳转语义(而不是子串匹配),并结合绝对路径校验。
|
|
||
| // applyDispatchDeadline 为调度连接设置统一超时控制。 | ||
| func applyDispatchDeadline(conn net.Conn, ctx context.Context) error { | ||
| if ctx == nil { |
There was a problem hiding this comment.
applyDispatchDeadline returns immediately when ctx == nil, which disables the documented fallback I/O timeout (defaultDispatchIOTimeout). That means callers of exported Dispatch(nil, ...) can block indefinitely on IPC read/write if the peer stalls. Please still apply a default deadline when context is nil (or normalize nil to context.Background() before deadline logic).
| return "", nil | ||
| } | ||
|
|
||
| if strings.Contains(workdir, "..") { |
There was a problem hiding this comment.
strings.Contains(workdir, "..") 会误判合法路径(例如目录名包含 .. 的普通绝对路径),导致 unsafe_path 假阳性。建议改为按路径段语义判断是否存在真正的 .. 段,而不是子串匹配。
|
|
||
| // applyDispatchDeadline 为调度连接设置统一超时控制。 | ||
| func applyDispatchDeadline(conn net.Conn, ctx context.Context) error { | ||
| if ctx == nil { |
There was a problem hiding this comment.
applyDispatchDeadline 在 ctx == nil 时直接返回,导致不会应用 defaultDispatchIOTimeout。这会让对外暴露的 Dispatch(nil, ...) 在对端卡住时出现无界阻塞风险。建议在 nil context 下仍设置默认 deadline(或先归一化为 context.Background())。
…Context 时丢失默认超时的问题
|
/simplify |
ResultI checked the historical review log and applied the unresolved fix that was still worth addressing. Changes pushed to this PR branch:
Implemented:
Simplification pass:
Validation executed:
Notes on other logged comments:
|
Generated with [codeagent](https://github.com/qbox/codeagent) Co-authored-by: pionxe <148670367+pionxe@users.noreply.github.com>
目标 (Motivation & Context)
本 PR 完整实现了网关重构计划的第二步 [EPIC-GW-02],使得操作系统、浏览器或外部 IDE 可以通过
neocode://协议唤醒本地系统,并将任务精准派发给常驻后台的 Gateway 进程。💡 核心架构决议说明: 经过评估,本期遵循了“最小改动与职责单一”原则:
gateway.MessageFrame作为 IPC 传输载体(JSON-RPC 改造已延后至 EPIC-GW-03)。url-dispatch仅做 URL 解析与无脑透传;业务合法性校验统一收拢在 Gateway 侧的wake.go处理器中。reviewaction 通过,其余 action 一律快速失败(Fail Fast)。Closes #271
✨ 主要变更 (Key Changes)
1. URL 协议层 (
internal/gateway/protocol)neocode_url.go,实现从neocode://...到WakeIntent结构体的标准化解析。2. 调度器与客户端 (
internal/gateway/adapters/urlscheme)dispatcher.go,负责通过本地 IPC (UDS / Named Pipe) 连接网关并发送wake.openUrl帧。os.Exit(1)退出。3. 网关服务端 (
internal/gateway/handlers)wake.go处理器及FrameActionWakeOpenURL动作标识。review的请求,返回invalid_action;对review请求强制校验path参数。4. CLI 命令框架优化 (
internal/cli)root.go,引入shouldSkipGlobalPreload钩子。url-dispatch与全局重度业务配置(如模型 Provider 初始化),保证分发命令的极速启动和“纯粹代理”属性。✅ 验收标准 (Acceptance Criteria)
neocode://review?path=README.md。status=ok响应。neocode://open),网关正确拦截并返回invalid_action,终端 Exit 1。url-dispatch时,不再触发全局配置(如 Provider 未找到)的无意义报错。🧪 测试与验证结果 (Verification)
单元测试:相关新增模块覆盖率均达 95% 以上,协议层/调度层覆盖率 100%。全仓
go test ./... -count=1全绿。CLI 行为打点: