Skip to content

Commit 9a432ef

Browse files
committed
Merge remote-tracking branch 'upstream/main'
# Conflicts: # internal/tui/core/app/app.go
2 parents ceba74a + de9e4b7 commit 9a432ef

253 files changed

Lines changed: 34369 additions & 9712 deletions

File tree

Some content is hidden

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

.github/workflows/release.yml

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,17 @@ jobs:
2727
with:
2828
go-version-file: 'go.mod' # 💡 修复点 1:让 Action 自动跟随项目真实的 Go 版本
2929

30+
- name: Set up Node.js
31+
uses: actions/setup-node@v4
32+
with:
33+
node-version: '22'
34+
35+
- name: Build web dist
36+
working-directory: web
37+
run: |
38+
npm ci
39+
npm run build
40+
3041
- name: Run GoReleaser
3142
uses: goreleaser/goreleaser-action@v5
3243
with:
@@ -35,4 +46,4 @@ jobs:
3546
args: release --clean
3647
env:
3748
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # 💡 修复点 2:补回本仓库的内置鉴权令牌
38-
TAP_GITHUB_TOKEN: ${{ secrets.TAP_GITHUB_TOKEN }}
49+
TAP_GITHUB_TOKEN: ${{ secrets.TAP_GITHUB_TOKEN }}

.goreleaser.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ builds:
1010
- id: neocode
1111
env:
1212
- CGO_ENABLED=0
13+
tags:
14+
- webembed
1315
ldflags:
1416
- -s -w -X 'neo-code/internal/version.Version={{.Version}}'
1517
goos:

README.en.md

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,11 @@
88
<a href="https://go.dev/">
99
<img src="https://img.shields.io/badge/Go-1.25%2B-00ADD8?logo=go&logoColor=white" alt="Go Version" />
1010
</a>
11-
<a href="https://github.com/1024XEngineer/neo-code/actions/workflows/ci.yml">
12-
<img src="https://img.shields.io/github/actions/workflow/status/1024XEngineer/neo-code/ci.yml?branch=main&label=CI" alt="CI Status" />
11+
<a href="https://github.com/1024XEngineer/neo-code">
12+
<img src="https://codecov.io/gh/1024XEngineer/neo-code/branch/main/graph/badge.svg" alt="Codecov Coverage" />
1313
</a>
1414
<a href="https://github.com/1024XEngineer/neo-code/blob/main/LICENSE">
15-
<img src="https://img.shields.io/github/license/1024XEngineer/neo-code?color=97CA00" alt="License" />
15+
<img src="https://img.shields.io/badge/License-MIT-purple?logo=opensourceinitiative&logoColor=white" alt="License MIT" />
1616
</a>
1717
<a href="https://neocode-docs.pages.dev/">
1818
<img src="https://img.shields.io/badge/Docs-Official-1677FF?logo=readthedocs&logoColor=white" alt="Docs" />
@@ -22,6 +22,7 @@
2222
</a>
2323
</p>
2424

25+
2526
<p align="center">
2627
<a href="https://neocode-docs.pages.dev/en/">Docs</a>
2728
·
@@ -55,6 +56,8 @@ Core loop:
5556
- Skills system for task-specific behaviors.
5657
- MCP integration via stdio servers.
5758
- Gateway mode with local JSON-RPC / SSE / WebSocket access.
59+
- Feishu Adapter: Webhook and SDK long-connection ingress with live status card updates.
60+
- Local Runner: execute tools on your local machine via WebSocket connection to a cloud Gateway — no inbound ports needed.
5861

5962
---
6063

@@ -110,6 +113,14 @@ Then start with your workspace:
110113
neocode --workdir /path/to/your/project
111114
```
112115

116+
To launch the browser-based Web UI:
117+
118+
```bash
119+
neocode web
120+
```
121+
122+
Tagged release builds already embed `web/dist` into the `neocode` binary, so the target machine does not need Node.js or npm. When running from source, missing `web/dist` still triggers the local frontend build path.
123+
113124
### 4. Common commands
114125

115126
```text
@@ -126,14 +137,25 @@ neocode --workdir /path/to/your/project
126137

127138
---
128139

129-
## Gateway / MCP / Skills
140+
## Gateway / MCP / Skills / Runner
130141

131142
Detailed docs are intentionally split out. README keeps entry links:
132143

133144
- Gateway integration and protocol: `docs/guides/gateway-integration-guide.md`
134145
- MCP configuration: `docs/guides/mcp-configuration.md`
135146
- Skills design: `docs/skills-system-design.md`
136147
- Runtime event flow: `docs/runtime-provider-event-flow.md`
148+
- Feishu remote setup: `www/guide/feishu-remote-setup.md`
149+
150+
### CLI Quick Reference
151+
152+
```bash
153+
# Start local runner daemon (connects to cloud Gateway for remote tool execution)
154+
neocode runner --gateway-address "your-gateway.com:8080" --token-file ~/.neocode/auth.json
155+
156+
# Start feishu adapter (SDK mode, no public network required)
157+
neocode feishu-adapter --ingress sdk --gateway-listen "127.0.0.1:8080"
158+
```
137159

138160
---
139161

README.md

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,21 @@
88
<a href="https://go.dev/">
99
<img src="https://img.shields.io/badge/Go-1.25%2B-00ADD8?logo=go&logoColor=white" alt="Go Version" />
1010
</a>
11-
<a href="https://github.com/1024XEngineer/neo-code/actions/workflows/ci.yml">
12-
<img src="https://img.shields.io/github/actions/workflow/status/1024XEngineer/neo-code/ci.yml?branch=main&label=CI" alt="CI Status" />
11+
<a href="https://github.com/1024XEngineer/neo-code">
12+
<img src="https://codecov.io/gh/1024XEngineer/neo-code/branch/main/graph/badge.svg" alt="Codecov Coverage" />
1313
</a>
1414
<a href="https://github.com/1024XEngineer/neo-code/blob/main/LICENSE">
15-
<img src="https://img.shields.io/github/license/1024XEngineer/neo-code?color=97CA00" alt="License" />
15+
<img src="https://img.shields.io/badge/License-MIT-purple?logo=opensourceinitiative&logoColor=white" alt="License MIT" />
1616
</a>
1717
<a href="https://neocode-docs.pages.dev/">
1818
<img src="https://img.shields.io/badge/Docs-Official-1677FF?logo=readthedocs&logoColor=white" alt="Docs" />
1919
</a>
20-
<a href="https://neocode-docs.pages.dev/guide/install">
20+
<a href="https://neocode-docs.pages.dev/en/guide/install">
2121
<img src="https://img.shields.io/badge/Platform-Windows%20%7C%20macOS%20%7C%20Linux-4EAA25" alt="Platform" />
2222
</a>
2323
</p>
2424

25+
2526
<p align="center">
2627
<a href="https://neocode-docs.pages.dev/">文档</a>
2728
·
@@ -56,6 +57,7 @@ NeoCode 是一个运行在本地开发环境中的 AI Coding Agent。
5657
- MCP 接入:通过 MCP stdio server 扩展外部工具能力。
5758
- Gateway 模式:通过本地 JSON-RPC / SSE / WebSocket 接口连接桌面端、脚本和第三方客户端。
5859
- Feishu Adapter:支持 Webhook 与 SDK 长连接接入,并用单张状态卡片持续回传 run 状态。
60+
- Local Runner:`neocode runner` 在本机执行工具,通过 WebSocket 主动连接云端 Gateway,无需开放入站端口。
5961

6062
---
6163

@@ -111,6 +113,13 @@ $env:OPENAI_API_KEY = "your_key_here"
111113
neocode --workdir /path/to/your/project
112114
```
113115

116+
如果你希望使用浏览器 Web UI,可以直接运行:
117+
```bash
118+
neocode web
119+
```
120+
121+
标签发布版已经将 Web UI 的 `web/dist` 内嵌进 `neocode` 二进制,执行 `neocode web` 时不再要求用户机器安装 Node.js 或 npm。如果你在源码仓库里运行 `go run ./cmd/neocode web`,当本地缺少 `web/dist` 时仍会自动尝试构建前端。
122+
114123
### 4. 常用命令
115124

116125
```text
@@ -176,6 +185,21 @@ neocode use <provider> --model <model-id>
176185
neocode use openai --model gpt-4.1
177186
```
178187

188+
#### Local Runner
189+
190+
在本机启动执行守护进程,主动连接云端 Gateway 接收工具执行请求。
191+
192+
```bash
193+
# 启动 runner(默认连接 127.0.0.1:8080)
194+
neocode runner
195+
196+
# 指定远程 Gateway 地址和 token
197+
neocode runner --gateway-address "your-gateway.com:8080" --token-file ~/.neocode/auth.json
198+
199+
# 指定 Runner 名称与工作目录
200+
neocode runner --runner-name "我的本机" --workdir /path/to/project
201+
```
202+
179203
### 6. Shell 诊断代理
180204

181205
用于进入代理 shell、初始化 shell integration、手动触发诊断和控制自动诊断模式。

docs/gateway-rpc-api.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -421,6 +421,27 @@ type ResolvePermissionParams struct {
421421

422422
---
423423

424+
## Method: gateway.userQuestionAnswer
425+
426+
- Stability: Beta
427+
- Auth Required: Yes
428+
- Request Schema:
429+
430+
```go
431+
type UserQuestionAnswerParams struct {
432+
RequestID string `json:"request_id"` // MUST
433+
Status string `json:"status,omitempty"` // answered|skipped,默认 answered
434+
Values []string `json:"values,omitempty"` // 可选:选择值
435+
Message string `json:"message,omitempty"` // 可选:文本回答
436+
}
437+
```
438+
439+
- Response Schema: `ack`(提交成功)或标准 `error`
440+
- Observation:
441+
- `gateway_requests_total{method="gateway.userQuestionAnswer",...}`
442+
443+
---
444+
424445
## Method: gateway.listProviders
425446

426447
- Stability: Stable
@@ -656,6 +677,14 @@ type GetRuntimeSnapshotParams struct {
656677

657678
- Response Schema:
658679
- Success: `ack` + `payload.snapshot`(runtime facts、decision、todo snapshot)
680+
- `payload.snapshot.pending_user_question`(可选):
681+
- `request_id`
682+
- `question_id`
683+
- `title` / `description`
684+
- `kind``text|single_choice|multi_choice`
685+
- `options`
686+
- `required` / `allow_skip`
687+
- `max_choices` / `timeout_sec`
659688
- Failure: 标准 `error`
660689
- Observation:
661690
- `gateway_requests_total{method="runtime.snapshot.get",...}`

docs/guides/feishu-adapter.md

Lines changed: 80 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,12 @@
1212
- `gateway.bindStream`
1313
- `gateway.run`
1414
- `gateway.resolvePermission`
15+
- `gateway.userQuestionAnswer`
1516
- `gateway.event`
1617
- 会话与运行 ID 保持实现一致:
1718
- `session_id = "feishu_" + stableHash(chat_id)`
1819
- `run_id = "feishu_" + stableHash(message_id)`
19-
- #557 只新增 SDK 入站,不包含 #555 Local Runner 主动长连。
20+
- #557 新增 SDK 入站#555 新增 Local Runner 主动长连(工具在 Runner 本机执行)
2021

2122
## 2. 事件执行顺序
2223

@@ -45,23 +46,32 @@
4546
### 4.1 Webhook 模式(#554
4647

4748
```bash
49+
# 开发模式 (go run)
50+
go run ./cmd/neocode feishu-adapter --ingress webhook
51+
52+
# 安装模式 (neocode)
4853
neocode feishu-adapter --ingress webhook
4954
```
5055

5156
通常还会覆盖地址参数:
5257

5358
```bash
54-
neocode feishu-adapter \
55-
--ingress webhook \
56-
--listen 127.0.0.1:18080 \
57-
--event-path /feishu/events \
58-
--card-path /feishu/cards
59+
# 开发模式 (go run)
60+
go run ./cmd/neocode feishu-adapter --ingress webhook --listen 127.0.0.1:18080 --event-path /feishu/events --card-path /feishu/cards
61+
62+
# 安装模式 (neocode)
63+
neocode feishu-adapter --ingress webhook --listen 127.0.0.1:18080 --event-path /feishu/events --card-path /feishu/cards
5964
```
6065

6166
### 4.2 SDK 模式(#557,本地无公网)
6267

6368
```bash
6469
export FEISHU_APP_SECRET="cli_secret_xxx"
70+
71+
# 开发模式 (go run)
72+
go run ./cmd/neocode feishu-adapter --ingress sdk
73+
74+
# 安装模式 (neocode)
6575
neocode feishu-adapter --ingress sdk
6676
```
6777

@@ -77,7 +87,7 @@ SDK 模式下不要求公网回调地址,不要求 `adapter.listen/event_path/
7787
## 6. 幂等与重试
7888

7989
- 消息去重键:`event_id + message_id`
80-
- 卡片去重键:`request_id + decision`
90+
- 卡片去重键:优先 `event_id`,无 `event_id` 时回退到动作键(如 `request_id + decision/status`
8191
- 仅当 `gateway.run` 成功受理后才标记成功;
8292
-`run` 失败会释放去重状态,Webhook 返回 `HTTP 500`,SDK 长连接回调返回失败 ACK,允许飞书重试恢复。
8393

@@ -100,8 +110,71 @@ SDK 模式下不要求公网回调地址,不要求 `adapter.listen/event_path/
100110

101111
以上两种路径都复用 `gateway.resolvePermission`,不新增 Gateway action。
102112

113+
## 7.1 ask_user 统一交互(permission + user_question)
114+
115+
飞书端卡片回调已升级为通用 `action_type`
116+
117+
- `action_type=permission` -> `gateway.resolvePermission`
118+
- `action_type=user_question` -> `gateway.userQuestionAnswer`
119+
120+
V1 交互策略:
121+
122+
- 单选/跳过:优先卡片按钮提交
123+
- 文本/多选:回退文本指令提交
124+
- `回答 <request_id> <内容>`
125+
- `跳过 <request_id>`
126+
127+
`webhook``sdk` 两条 ingress 使用同一动作映射与幂等规则,保证行为一致。
128+
103129
## 8. 安全要求
104130

105131
- 默认启用签名校验(Webhook);
106132
- 日志不会输出 `app_secret`、签名密钥、gateway token、Authorization 等敏感信息;
107133
- 用户侧只回关键状态(受理、权限请求、完成、失败),不暴露内部堆栈和控制面细节。
134+
135+
## 9. Local Runner 远程工具执行(#555
136+
137+
Runner 是部署在用户本机的执行守护进程,通过 WebSocket 主动连接云端 Gateway,接收工具执行请求并在本机完成。
138+
139+
```
140+
飞书消息 -> Feishu Adapter (cloud) -> Gateway (cloud) -> WebSocket -> Local Runner (本机)
141+
↑ 主动出站连接
142+
```
143+
144+
### 9.1 启动 Runner
145+
146+
```bash
147+
# 开发模式 (go run)
148+
go run ./cmd/neocode runner --gateway-address "your-gateway:8080" --token-file ~/.neocode/auth.json --runner-name "我的本机" --workdir /path/to/project
149+
150+
# 安装模式 (neocode)
151+
neocode runner --gateway-address "your-gateway:8080" --token-file ~/.neocode/auth.json --runner-name "我的本机" --workdir /path/to/project
152+
```
153+
154+
Runner 启动后会主动连接 Gateway,注册自身并保持心跳。当飞书消息触发工具调用时,Gateway 将工具请求推送到 Runner 本机执行。
155+
156+
### 9.2 参数说明
157+
158+
| 参数 | 必填 | 默认值 | 说明 |
159+
|------|:---:|--------|------|
160+
| `--gateway-address` || `127.0.0.1:8080` | Gateway WebSocket 地址 |
161+
| `--token-file` ||| Gateway 认证 token 文件路径 |
162+
| `--runner-id` || 本机 hostname | Runner 唯一标识 |
163+
| `--runner-name` ||| 人类可读的 Runner 名称 |
164+
| `--workdir` || 当前目录 | Runner 工作目录 |
165+
166+
### 9.3 安全模型
167+
168+
- Runner 端验证 CapabilityToken(HMAC-SHA256 签名、TTL、AllowedTools、AllowedPaths)
169+
- 支持 Workdir Allowlist 限制可访问路径
170+
- 所有工具在 Runner 本机执行,结果通过 Gateway 回传飞书
171+
172+
### 9.4 错误翻译
173+
174+
当 Runner 不可用或权限不足时,Feishu Adapter 会将错误码翻译为用户可读消息:
175+
176+
| 错误码 | 飞书消息 |
177+
|--------|----------|
178+
| `runner_offline` | 本机 Runner 未连接,请在电脑上启动 `neocode runner` |
179+
| `capability_denied` | 权限不足:当前能力令牌不允许此操作 |
180+
| `tool_execution_failed` | 工具执行失败:{详情} |

docs/guides/modelscope-provider-setup.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,11 @@
1414
2. 打开登录页:<https://www.modelscope.cn/>
1515
3. 打开 Token 页:<https://www.modelscope.cn/my/access/token>
1616
4. 在 TUI 引导面板粘贴 token 并提交校验
17+
5. 打开阿里云绑定页完成账号绑定:<https://www.modelscope.cn/my/settings/account>
1718

18-
如果返回认证或权限类错误,会自动回退并打开阿里云认证页:
19-
<https://www.modelscope.cn/my/settings/account>
19+
> **注意**:步骤 5 的阿里云账号绑定是必须步骤。ModelScope API 依赖阿里云账号体系进行鉴权与计费,
20+
> 未绑定将导致 API 调用返回认证错误。如果 token 校验时提前检测到认证问题,
21+
> TUI 会自动打开绑定页引导完成。
2022
2123
## 安全说明
2224

internal/app/bootstrap.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,15 @@ func BuildGatewayServerDeps(ctx context.Context, opts BootstrapOptions) (Runtime
230230
return RuntimeBundle{}, err
231231
}
232232

233+
// Wire ask_user broker from runtime into the pre-registered ask_user tool.
234+
if brokerAdapter := runtimeSvc.AskUserBrokerAdapter(); brokerAdapter != nil {
235+
if askTool, err := toolRegistry.Get(tools.ToolNameAskUser); err == nil {
236+
if setter, ok := askTool.(tools.AskUserBrokerSetter); ok {
237+
setter.SetAskUserBroker(brokerAdapter)
238+
}
239+
}
240+
}
241+
233242
// 注入记忆提取钩子:当 AutoExtract 启用且 memoSvc 可用时,ReAct 循环完成后异步提取记忆。
234243
if memoSvc != nil && cfg.Memo.AutoExtract {
235244
runtimeSvc.SetMemoExtractor(newMemoExtractorAdapter(
@@ -466,6 +475,7 @@ func buildToolRegistry(cfg config.Config) (*tools.Registry, func() error, error)
466475
toolRegistry.Register(codebase.NewRead(repoSvc, cfg.Workdir))
467476
toolRegistry.Register(codebase.NewSearchText(repoSvc, cfg.Workdir))
468477
toolRegistry.Register(codebase.NewSearchSymbol(repoSvc, cfg.Workdir))
478+
toolRegistry.Register(tools.NewAskUserTool(nil)) // broker injected after runtime creation
469479
mcpRegistry, err := BuildMCPRegistry(cfg)
470480
if err != nil {
471481
return nil, nil, err

0 commit comments

Comments
 (0)