feat(step2): 打通 neocode://run 唤醒-会话创建-终端接管闭环并补齐系统级 URL 注册#496
feat(step2): 打通 neocode://run 唤醒-会话创建-终端接管闭环并补齐系统级 URL 注册#496phantom5099 merged 20 commits into1024XEngineer:mainfrom
Conversation
|
Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits. |
| if d.launchTerminalFn == nil { | ||
| return DispatchResult{}, newDispatchError(ErrorCodeInternal, "terminal launcher is unavailable") | ||
| } | ||
| launchCommand := fmt.Sprintf("neocode --session %s", sessionID) |
There was a problem hiding this comment.
Medium – brittle launch command: this hard-codes neocode from PATH (neocode --session ...). In the URL-scheme flow, dispatch may be invoked via an absolute executable path that is not on PATH, so the new terminal can open but fail with command not found. Consider launching with the resolved current executable path (or carrying forward the registered absolute path) instead of a bare command name.
|
|
||
| // escapeDoubleQuotedShellLiteral 转义双引号 shell 字符串中的反斜杠与引号,避免路径截断。 | ||
| func escapeDoubleQuotedShellLiteral(value string) string { | ||
| escaped := strings.ReplaceAll(value, `\`, `\\`) |
There was a problem hiding this comment.
High – shell injection/expansion risk in generated launcher script: escapeDoubleQuotedShellLiteral escapes only \\ and ", but values embedded in double-quoted shell strings still expand $... and backticks. A crafted executable path can change command behavior when the script runs. Please also escape $ and ` (or avoid shell interpolation by writing a script that does not embed untrusted text directly).
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
可以想象一块干瘪的海绵,当它吸满水后,才具有了擦拭桌子的真实弹性和功能,这过程就是水化。 |
| return nil, nil, err | ||
| } | ||
| if sessionID := strings.TrimSpace(opts.SessionID); sessionID != "" { | ||
| if err := tuiApp.HydrateSession(ctx, sessionID); err != nil { |
There was a problem hiding this comment.
这里有点迷惑,为什么app层会主动调用tui的业务逻辑
There was a problem hiding this comment.
@xgopilot 请你解析app层、cli层、gateway层以及tui层的职责是什么,他们之间的依赖关系是怎么样的,这里bootstrap.go调用update.go的逻辑是否合理?
There was a problem hiding this comment.
结论
这个调用整体是合理的,但有一个可优化点:app 层现在直接调用了 tui.App 的具体方法(HydrateSession),耦合略高;从职责上看不越层,但从依赖形态上看可以进一步收敛成启动参数/Init 命令。
分层职责(按当前代码实现)
app层(internal/app)
- 职责:启动装配与依赖注入。
- 证据:
NewProgram负责组装配置、远程 runtime 适配器、TUI 实例与资源清理(internal/app/bootstrap.go:243-279)。 - 不承载协议路由,不执行工具业务。
cli层(internal/cli)
- 职责:命令入口、参数解析、进程生命周期、把命令请求路由到 app/gateway。
- 证据:根命令解析
--workdir/--session,调用app.NewProgram(internal/cli/root.go:68-84);gateway 子命令负责启动网关与 URL register/dispatch(internal/cli/gateway_commands.go)。
gateway层(internal/gateway)
- 职责:协议边界与转发编排,向下通过
RuntimePort调 runtime。 - 证据:
handleWakeOpenURLFrame中先CreateSession再Run,并做鉴权与 ACK 编排(internal/gateway/bootstrap.go:111-166)。
tui层(internal/tui)
- 职责:事件消费 + UI 状态渲染与交互。
- 证据:
HydrateSession通过runtime.LoadSession拉会话快照并同步前端状态(internal/tui/core/app/update.go:1450-1477);TUI 只依赖tui/services.Runtime契约(internal/tui/services/runtime_contract.go:14-27)。
依赖关系(当前)
cli -> app -> tui/services.Runtime(适配器) -> gateway -> runtime
补充:gateway 侧 runtime 接入通过 bridge 实现(internal/cli/gateway_runtime_bridge.go),符合“通过契约接线”的方向。
关于“bootstrap.go 调用 update.go 逻辑是否合理”
合理,原因如下:
update.go只是文件名,HydrateSession本质是tui.App的启动期状态装载 API,不是“运行期输入事件处理”专属逻辑。- 调用时机在
program.Run()前,属于启动装配流程的一部分(internal/app/bootstrap.go:266-273)。 - 该调用没有绕开 Runtime/Gateway,仍通过
a.runtime.LoadSession取数据(internal/tui/core/app/update.go:1460),没有跨层直连 provider/tool。
需要注意的点(非 blocker):
app层当前依赖tui.App具体方法,而非纯启动契约;若后续希望进一步解耦,可把 hydration 变成NewWith...的 options 或Initcmd,由 TUI 内部在启动时自行执行。
| // LoadSession 加载指定会话详情。 | ||
| LoadSession(ctx context.Context, input LoadSessionInput) (Session, error) | ||
| // CreateSession 创建并返回可用会话标识。 | ||
| CreateSession(ctx context.Context, input CreateSessionInput) (string, error) |
There was a problem hiding this comment.
结论
在这个 PR 的目标下,RuntimePort.CreateSession(...) 是有必要的(不是绝对唯一方案,但在现有契约下是最小且清晰的方案)。
原因:
wake.openUrl(action=run)需要在 ACK 里回传最终session_id,用于后续neocode --session <id>接管。- 现有
RuntimePort.Run(...)是“受理即返回”的异步接口(仅返回error),不会同步返回session_id。 - 会话 ID 生成职责已经下放到 Runtime(而不是 Gateway),因此 Gateway 不能自己生成 ID,只能通过 Runtime 明确创建/获取。
对应代码链路:
internal/gateway/bootstrap.go:142先调用runtimePort.CreateSession(...)internal/gateway/bootstrap.go:150再发起handleRunFrame(...)internal/gateway/contracts.go:309定义RuntimePort.CreateSession(...)
可选替代(如果后续要简化接口)
可以把 Run 改为同步返回 acceptedSessionID(例如 Run(...) (RunAccepted, error)),这样可删除独立 CreateSession。但这会影响现有 gateway/runtime 运行契约和调用方,改动面更大。
本次我只做了分析答复,没有改代码。
- root 命令树移除 url-register/url-dispatch,并清理对应 preload/update 特判 - dispatcher 删除原始 URL 解析入口,仅保留 DispatchWakeIntent - 删除跨平台 RegisterURLScheme 实现与相关测试 - protocol 删除 ParseNeoCodeURL/ParseError,保留 WakeIntent 与 WakeAction 支持集 - 重写/调整 urlscheme 与 cli 相关测试以覆盖新链路
- 安装脚本移除 url-register 自动调用,仅保留 daemon install - 网关 RPC 示例生成器将 wake.raw_url 样例改为 http://neocode:18921 - README 与网关相关文档移除 neocode:// 与 url-* 命令说明 - 更新升级指引中的静默更新跳过策略描述
a45853e to
de34f90
Compare
|
@xgopilot 给我详细的补充下这个PR实际上都做了什么,具体修改了什么内容 |
这个 PR 实际做了什么(按链路拆解)你这条 PR 现在的实际内容,已经不只是
核心闭环已经落到代码里:外部唤醒 -> Gateway 关键改动(模块维度)
一句话总结“实际落地结果”这版 PR 已把“外部点击 -> 会话生成/接管 -> 终端启动 -> TUI 自动恢复并可直接执行”的链路跑通;并且在实现上从早期“单纯 URL dispatch”演进成了“wake IPC + HTTP daemon 双入口 + 平台自启动/注册”的完整可用方案。 |
背景
Step 2 目标是打通完整闭环:
neocode://run外部触发-> Gateway 解析与放行
-> Runtime 创建/接管会话并执行 run
-> Dispatcher 拉起终端
->
neocode --session <id>启动后自动水化会话状态同时补齐系统级 URL Scheme 注册能力,实现“文档/网页/聊天窗口点击即用”。
变更摘要
本 PR 在不改动核心分层边界的前提下,完成了 Step 2 的关键落地:
CreateSession支持空session_id自动生成(session_*)。RuntimePort.CreateSession(...)契约并在 bridge 层接入鉴权。wake.openUrl(action=run)路径改为:先CreateSession,后Run。wake.openUrlACK 回传最终session_id(用于后续接管)。wake.run启动改为使用脱离连接取消的上下文(context.WithoutCancel语义封装)。session_id后执行终端拉起命令:neocode --session <id>。not_supported提示(现阶段不做弹窗终端适配)。--session并贯通到启动装配链路。session_id水化历史消息/标题/Todo/活动会话。workdir接管;路径失效时保留当前工作区并给出告警。Waiting for agent...视图改动。neocode url-register [--executable <abs-path>]。HKCU\Software\Classes\neocode。.appbundle(含CFBundleURLTypes、可执行脚本、lsregister刷新)。.desktop+xdg-mime,Exec ... "%u"。not_supported。url-register(失败仅 warning,不阻断安装)。url-register用法、验证方式、排障建议与手动清理指引。run唤醒与session_id回传行为。兼容性与边界说明
TUI -> Gateway -> Runtime,未引入跨层直连。bridge.LoadSession的“not found -> upsert”仍保留(已加 TODO,后续在 TUI Submit 显式创建会话后再移除)。测试与验证
自动化测试
url-register参数解析、默认路径、错误透传、跳过 preload/silent checksession_id回传、上下文脱离取消回归CreateSession空 ID 自生成行为HydrateSession与工作区接管相关测试go test ./...✅手工验收建议
neocode url-registerneocode://run?prompt=写一个简单的HTTP服务器neocode --session <id>接管 -> run 正常开始风险与回滚
xdg-mime、lsregister)。