Skip to content

Commit 67b0108

Browse files
authored
Merge pull request #496 from pionxe/feat/url-wake-session-hydration-mvp
feat(step2): 打通 neocode://run 唤醒-会话创建-终端接管闭环并补齐系统级 URL 注册
2 parents 4b57b5e + 6857b31 commit 67b0108

60 files changed

Lines changed: 4586 additions & 2938 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

README.md

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,50 @@ neocode --workdir /path/to/your/project
124124
/skill off <id> 停用 skill
125125
```
126126

127+
### 5. url scheme使用
128+
详细指南链接: [HTTP URL 唤醒使用指南(用户故事版)](https://neocode-docs.pages.dev/guide/http-daemon-wake-user-guide)
129+
130+
```bash
131+
# 启动本地 HTTP daemon(默认 127.0.0.1:18921)
132+
go run ./cmd/neocode daemon serve
133+
134+
# 安装用户态自启动 + best-effort hosts 别名写入(127.0.0.1 neocode)
135+
go run ./cmd/neocode daemon install
136+
137+
# 查看运行与安装状态
138+
go run ./cmd/neocode daemon status
139+
140+
# 卸载自启动配置
141+
go run ./cmd/neocode daemon uninstall
142+
```
143+
144+
可点击链接示例:
145+
146+
```text
147+
http://neocode:18921/review?path=README.md
148+
http://neocode:18921/run?prompt=写一个简单的HTTP服务器
149+
```
150+
151+
> 当前支持动作:
152+
> - `review`:必须携带 `path` 参数。
153+
> - `run`:必须携带 `prompt` 参数,网关会返回 `session_id` 并触发终端接管链路。
154+
155+
会话接管启动方式:
156+
157+
```bash
158+
go run ./cmd/neocode --session <session_id>
159+
```
160+
161+
> 当传入 `--session` 时,TUI 会优先按会话历史中的 `workdir` 进行上下文接管;若该路径在本地失效,会保留当前工作区并显示告警。
162+
>
163+
> Linux(及其他非 Windows/macOS)当前尚未接入自动弹窗终端;`wake.run` 会返回 `not_supported`,可手动执行 `neocode --session <session_id>` 接管。
164+
>
165+
> `daemon serve` 不提供 `--token-file`,默认仅监听 `127.0.0.1`,并限制 Host 白名单为 `neocode` / `localhost` / `127.0.0.1`
166+
>
167+
> Linux 自启动策略:优先 `systemd --user`,若不可用则回落到 `~/.config/autostart/neocode-daemon.desktop`
168+
>
169+
> 若未通过安装脚本安装(例如 `go build` / 裸二进制),请手动执行一次 `neocode daemon install`
170+
127171
---
128172

129173
## Gateway / MCP / Skills

docs/gateway-detailed-design.md

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919

2020
### 2.2 并发与稳定性
2121

22-
URL 派发(`url-dispatch`)在网关未启动时需要确定性恢复策略。
22+
URL 派发(`daemon dispatcher`)在网关未启动时需要确定性恢复策略。
2323
本次引入统一 launcher,固定发现顺序并限制单次回退,避免无限重试。
2424

2525
### 2.3 资产与运维复用
@@ -50,7 +50,7 @@ flowchart LR
5050
3. 鉴权与 ACL 装配。
5151
4. IPC/HTTP server 启停流程。
5252

53-
### 3.3 自动拉起状态机(url-dispatch
53+
### 3.3 自动拉起状态机(daemon dispatcher
5454

5555
```mermaid
5656
stateDiagram-v2
@@ -79,8 +79,8 @@ stateDiagram-v2
7979

8080
### 3.4 子进程回收
8181

82-
launcher 启动网关后立即 `Release` 进程句柄,不阻塞 url-dispatch 主流程。
83-
网关生命周期由目标进程自身管理,url-dispatch 仅负责“拉起 + 等待可连通 + 单次重拨”。
82+
launcher 启动网关后立即 `Release` 进程句柄,不阻塞 daemon dispatcher 主流程。
83+
网关生命周期由目标进程自身管理,daemon dispatcher 仅负责“拉起 + 等待可连通 + 单次重拨”。
8484

8585
## 4. Design Trade-offs
8686

@@ -105,7 +105,7 @@ launcher 启动网关后立即 `Release` 进程句柄,不阻塞 url-dispatch
105105

106106
### 5.2 连接重置与重试
107107

108-
1. url-dispatch 仅在首拨失败时触发一次 launcher 回退。
108+
1. daemon dispatcher 仅在首拨失败时触发一次 launcher 回退。
109109
2. 回退后仅重拨一次,避免无界重试。
110110

111111
### 5.3 心跳与超时
@@ -137,3 +137,17 @@ launcher 启动网关后立即 `Release` 进程句柄,不阻塞 url-dispatch
137137
3. 日志白名单字段具备自动化断言:`listen_address``auth_mode``request_id``method``source``status``gateway_code`
138138
4. CI 包含 gateway-only 冒烟链路:启动 -> `/healthz` -> `/rpc` 未鉴权失败 -> 清理。
139139
5. 安装脚本支持 `full|gateway` flavor 与 dry-run,且 URL/资产/checksum 命名规则可回归验证。
140+
## 8. HTTP Daemon Wake (Step 2.5)
141+
142+
为解决多数文档平台无法直接点击 `http://neocode:18921/` 的限制,引入本机 HTTP daemon 入口并与 HTTP daemon 单入口:
143+
144+
- 入口:`http://neocode:18921/run?...``http://neocode:18921/review?...`
145+
- `daemon` 不再执行 `HTTP -> http://neocode:18921/ -> ParseNeoCodeURL` 双序列化;而是直接将 HTTP 参数映射为 `protocol.WakeIntent`,复用与 `daemon dispatcher` 相同的 IPC/RPC 派发核心。
146+
- Gateway 在 IPC 来源下对 `wake.openUrl` 提供最小豁免(仅此方法),其余方法仍遵循既有鉴权与 ACL 约束。
147+
- Linux 自启动采用双分支:优先 `systemd --user`,不可用时回落 `~/.config/autostart/neocode-daemon.desktop`
148+
149+
迁移节奏:
150+
151+
1. Step 2.5 先新增 daemon 方案并人工验收。
152+
2. 验收通过后,再在后续 PR 中移除 `http://neocode:18921/` 系统注册链路。
153+

docs/gateway-rpc-api.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -441,4 +441,4 @@ type ResolvePermissionParams struct {
441441
- Response Schema: `ack` 或标准 `error`
442442
- Observation:
443443
- 统计进入 `gateway_requests_total{method="wake.openUrl",...}`
444-
-url-dispatch 自动拉起链路联动
444+
-daemon dispatcher 自动拉起链路联动
Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
# HTTP URL 唤醒使用指南(用户故事版)
2+
3+
本指南面向最终用户,目标是让你在文档、网页、IM 聊天里点击一个链接,就能拉起 NeoCode 并接管会话。
4+
5+
---
6+
7+
## 你会得到什么
8+
9+
- 点击 `http://neocode:18921/run?...` 可直接发起一次新任务。
10+
- 点击 `http://neocode:18921/review?...` 可直接发起文件审查任务。
11+
- 首次点击会自动创建会话并执行。
12+
- 后续点击带 `session_id` 的链接会直接进入同一会话,不重复发送提示词。
13+
14+
---
15+
16+
## 一分钟理解工作流
17+
18+
1. 浏览器打开链接(`run``review`)。
19+
2. 本机 daemon 收到请求并转发到 Gateway。
20+
3. Gateway 返回 `session_id`
21+
4. daemon 拉起 `neocode --session <session_id>`
22+
5. 首次点击时,TUI 启动后自动走标准 Submit 链路执行任务。
23+
6. 复点(带 `session_id`)时,只接管会话,不重复执行。
24+
25+
---
26+
27+
## 0. 首次准备(只做一次)
28+
29+
### 0.1 安装并注册 daemon 自启动
30+
31+
```bash
32+
neocode daemon install
33+
```
34+
35+
说明:
36+
37+
- 会配置用户态自启动。
38+
- 会 best-effort 写入 hosts 别名(`127.0.0.1 neocode`)。
39+
40+
### 0.2 启动 daemon(开发期可手动)
41+
42+
```bash
43+
neocode daemon serve
44+
```
45+
46+
默认监听:`127.0.0.1:18921`
47+
48+
### 0.3 查看状态
49+
50+
```bash
51+
neocode daemon status
52+
```
53+
54+
---
55+
56+
## 1. 用户故事 A:首次点击 run 链接并开始执行
57+
58+
场景:你在需求文档里放一个“让 NeoCode 直接开始做事”的链接。
59+
60+
### 1.1 生成可点击链接(推荐)
61+
62+
```bash
63+
neocode daemon encode run --prompt "实现一个最小 HTTP 服务" --workdir "C:\project"
64+
```
65+
66+
示例输出:
67+
68+
```text
69+
http://neocode:18921/run?prompt=%E5%AE%9E%E7%8E%B0%E4%B8%80%E4%B8%AA%E6%9C%80%E5%B0%8F+HTTP+%E6%9C%8D%E5%8A%A1&workdir=C%3A%5Cproject
70+
```
71+
72+
### 1.2 点击链接后的预期
73+
74+
- 弹出一个成功页,页面会显示:
75+
- `session_id`
76+
- `reusable_url`(可复用链接)
77+
- 自动拉起 TUI。
78+
- TUI 会显示完整思考/流式过程(与手动输入一致)。
79+
80+
---
81+
82+
## 2. 用户故事 B:首次点击 review 链接发起文件审查
83+
84+
场景:你在代码评审文档里放一个“审查这个文件”的链接。
85+
86+
### 2.1 生成 review 链接
87+
88+
```bash
89+
neocode daemon encode review --path "internal/gateway/bootstrap.go" --workdir "C:\project"
90+
```
91+
92+
示例输出:
93+
94+
```text
95+
http://neocode:18921/review?path=internal%2Fgateway%2Fbootstrap.go&workdir=C%3A%5Cproject
96+
```
97+
98+
### 2.2 review 首次点击的执行语义
99+
100+
- 系统会自动组装输入:`请审查文件 internal/gateway/bootstrap.go`
101+
- 然后按标准 Submit 链路执行(不是旁路执行)。
102+
103+
### 2.3 review 参数规则(首次)
104+
105+
- `path` 必填。
106+
- `workdir` 必填(除非你传了 `session_id`)。
107+
- `path` 必须是安全相对路径(不能绝对路径、不能 `..` 越界)。
108+
109+
---
110+
111+
## 3. 用户故事 C:复用同一会话(不重复发送提示词)
112+
113+
场景:你第一次点击后结果不错,想让团队后续都续接同一个会话。
114+
115+
### 3.1 使用成功页里的 `reusable_url`
116+
117+
成功页会给出带 `session_id` 的链接,形如:
118+
119+
```text
120+
http://neocode:18921/run?prompt=...&workdir=...&session_id=session_xxx
121+
```
122+
123+
### 3.2 带 `session_id` 的行为
124+
125+
- 只接管会话(打开同一 session)。
126+
- 不会再次自动发送 `prompt/path`
127+
- 适合“继续对话”和“多人协同续接”。
128+
129+
---
130+
131+
## 4. 推荐实践(避免踩坑)
132+
133+
1. 所有要放进文档/IM 的链接都先用 `neocode daemon encode ...` 生成。
134+
2. `review` 首次点击一定显式带 `workdir`,避免审错仓库。
135+
3. 首次执行后,把成功页里的 `reusable_url` 保存到文档,后续统一点它。
136+
4. Windows 路径、中文、空格都不要手写拼接,交给 `encode` 命令处理。
137+
138+
---
139+
140+
## 5. 常见问题排查
141+
142+
### Q1:点击后提示 `forbidden host`
143+
144+
原因:Host 不在白名单。
145+
允许的 Host:`neocode``localhost``127.0.0.1`
146+
147+
### Q2:提示 `missing required query: prompt`
148+
149+
原因:`run` 首次点击没带 `prompt`
150+
处理:补上 `prompt`,或直接使用 `daemon encode run`
151+
152+
### Q3:提示 `missing required query: workdir or session_id`
153+
154+
原因:`review` 首次点击没带 `workdir` 且没带 `session_id`
155+
处理:补 `workdir`,或使用已有 `session_id` 的复用链接。
156+
157+
### Q4:提示 `wake session not found`
158+
159+
原因:链接里的 `session_id` 在本机不存在。
160+
处理:重新走一次首次点击,获取新的 `session_id`
161+
162+
### Q5:Linux 下没有自动弹终端
163+
164+
当前 Linux 终端自动拉起能力受限。
165+
可手动执行:
166+
167+
```bash
168+
neocode --session <session_id>
169+
```
170+
171+
---
172+
173+
## 6. 命令速查
174+
175+
```bash
176+
# 启动 daemon
177+
neocode daemon serve
178+
179+
# 安装自启动
180+
neocode daemon install
181+
182+
# 卸载自启动
183+
neocode daemon uninstall
184+
185+
# 查看状态
186+
neocode daemon status
187+
188+
# 生成 run 链接
189+
neocode daemon encode run --prompt "..." --workdir "..."
190+
191+
# 生成 review 链接
192+
neocode daemon encode review --path "..." --workdir "..."
193+
```
194+

docs/guides/update.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
1. `neocode` 启动时会后台检测新版本(默认 3 秒超时)。
66
2. 为避免干扰 TUI,提示在程序退出后展示。
7-
3. `url-dispatch``update``version` 子命令默认跳过静默检测(避免同次命令重复探测)。
7+
3. `update``version` 子命令默认跳过静默检测(避免同次命令重复探测)。
88

99
## 2. 版本查询
1010

docs/reference/gateway-rpc-api.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1689,7 +1689,7 @@ Request:
16891689
"params": {
16901690
"path": "README.md"
16911691
},
1692-
"raw_url": "neocode://review?path=README.md"
1692+
"raw_url": "http://neocode:18921/review?path=README.md"
16931693
}
16941694
}
16951695
```

internal/app/bootstrap.go

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"neo-code/internal/config"
1414
configstate "neo-code/internal/config/state"
1515
agentcontext "neo-code/internal/context"
16+
"neo-code/internal/gateway/protocol"
1617
"neo-code/internal/memo"
1718
"neo-code/internal/provider"
1819
"neo-code/internal/provider/builtin"
@@ -55,7 +56,9 @@ var (
5556

5657
// BootstrapOptions 描述应用启动时可注入的运行时选项。
5758
type BootstrapOptions struct {
58-
Workdir string
59+
Workdir string
60+
SessionID string
61+
WakeInputB64 string
5962
}
6063

6164
type memoExtractorScheduler interface {
@@ -265,6 +268,29 @@ func NewProgram(ctx context.Context, opts BootstrapOptions) (*tea.Program, func(
265268
}
266269
return nil, nil, err
267270
}
271+
if sessionID := strings.TrimSpace(opts.SessionID); sessionID != "" {
272+
if err := tuiApp.HydrateSession(ctx, sessionID); err != nil {
273+
if cleanup != nil {
274+
_ = cleanup()
275+
}
276+
return nil, nil, err
277+
}
278+
}
279+
if encodedWakeInput := strings.TrimSpace(opts.WakeInputB64); encodedWakeInput != "" {
280+
wakeInput, decodeErr := protocol.DecodeWakeStartupInput(encodedWakeInput)
281+
if decodeErr != nil {
282+
if cleanup != nil {
283+
_ = cleanup()
284+
}
285+
return nil, nil, decodeErr
286+
}
287+
if configureErr := tuiApp.ConfigureStartupWakeInput(wakeInput.Text, wakeInput.Workdir); configureErr != nil {
288+
if cleanup != nil {
289+
_ = cleanup()
290+
}
291+
return nil, nil, configureErr
292+
}
293+
}
268294
return tea.NewProgram(
269295
tuiApp,
270296
tea.WithAltScreen(),

0 commit comments

Comments
 (0)