网页、桌面与编辑器
除了终端界面,noeta 还有网页版(web/)、桌面版(desktop/),以及给编辑器用的 Agent Client Protocol 接入(noeta acp)。本页讲每种怎么跑,以及怎样把它们连到服务器上。
底下只有一套接口
每个前端都只是同一套客户端接口(noeta/core/client.py:create、send、answer、cancel、read、watch)的翻译。前端都不保存任务状态,显示的内容全部从任务日志读出来,所以同一个任务可以先在终端里打开,再到浏览器里,再到编辑器里,每处看到的都是完整对话。
| 前端 | 连的是 | 代码 |
|---|---|---|
| 终端界面 | AppAPI:本进程里的 LocalApp,或加 --server 时的 RemoteApp | tui/、noeta/host/app.py、noeta/host/remote.py |
noeta -p、Agent SDK | Claude Code 的 stream-json 协议,本进程内 | noeta/cli/headless.py、noeta/host/stream_json.py |
| 网页版 | noeta serve 的 HTTP 接口 | web/src/api/client.ts、noeta/host/http.py |
| 桌面版 | 网页版,连它自己启动的 noeta serve | desktop/src/server.ts |
| 编辑器(Zed、JetBrains、Neovim 等) | stdio 上的 ACP,本进程内或 --server | noeta/host/acp.py |
| 你自己的程序 | noeta.Client / noeta.LocalApp,或 noeta.RemoteClient | Python 接口 |
要接入新的外部协议,就在 noeta/host 里再加一层翻译,绝不另起一套接口。
网页版
网页版是一个 React 页面,调用 noeta serve 的 HTTP 接口(和 RemoteClient 调的是同一批)。它有按目录分组的对话列表、流式显示的 markdown 对话、工具卡片(Bash 输出、改文件显示成 diff、子代理嵌套显示)、和 Claude Code 选项一致的审批卡片、AskUserQuestion 表单、计划审阅、任务清单面板、斜杠命令菜单、从任务目录补全的 @ 引用、粘贴和拖入图片、模式和模型选择、用量和上下文占用、回退、重命名、归档和搜索、亮暗两种主题,以及手机尺寸的布局(web/screenshots/README.md 逐项配了截图)。
先构建,再让服务器顺带提供它:
cd web && npm install && npm run build # 输出到 web/dist
noeta serve --web web/dist # 需要 server 这个 extranoeta serve 第一次生成令牌时会打印出来;打开它打印的地址(默认 http://127.0.0.1:8642/),页面会要你输入一次令牌。也可以直接放在地址里:http://127.0.0.1:8642/#token=…(# 后面的部分不会发给服务器;页面把令牌存进浏览器本地存储,并从地址栏里去掉)。点"Disconnect"会忘掉它。
页面文件本身不需要令牌就能取到(里面不含令牌),并带一条内容安全策略,只允许页面和自己的服务器通信。如果页面放在别的地方,只有你点名的来源才能调接口:noeta serve --allow-origin https://ui.example.com。
要改页面本身,在 web/ 里运行 npm run dev:它启动 Vite,把接口请求转发给一个正在运行的 noeta serve(NOETA_SERVER,默认 http://127.0.0.1:8642)。
没有的东西:云端环境、仓库和分支选择(noeta 就在服务器的目录里跑)、终端面板和文件树。对话不能删除,因为存储会保留每个任务的日志;想让它不出现在列表里就归档。
桌面版
桌面版就是装进 Electron 窗口的网页版,后面连一个它自己启动、自己关闭的本地 noeta serve。不用连接、不用粘贴任何东西:启动时它找到 noeta,挑一个空闲的本机端口,为这次运行生成一个新令牌,然后启动
noeta serve --host 127.0.0.1 --port <空闲端口> --web <构建好的网页版> [--allow-root DIR …]令牌放在服务器进程的环境变量里(NOETA_SERVER_TOKEN),从不出现在命令行上(desktop/src/server.ts)。退出应用时服务器随之停止。
cd desktop && npm install
npm run web # 先构建 ../web
npm start # 构建并打开应用
npm run dist # 安装包:macOS dmg、Linux AppImage 和 deb(未签名)它在 PATH 以及 ~/.local/bin、/opt/homebrew/bin、/usr/local/bin 里找 noeta(从程序坞启动的应用拿不到登录 shell 的 PATH);要指定别的位置,设 NOETA_DESKTOP_NOETA,或在应用的 settings.json(Settings,⌘,)里写 noetaPath。找不到时窗口里会显示安装命令,那条命令带着 server extra。File > Open Project Folder(⌘O)选新对话的工作目录。服务器在你的主目录里运行,所以主目录下的文件夹直接就能用;主目录以外的文件夹会加成服务器的 --allow-root,服务器因此要重启(有对话正在跑或在等你时会先问一句;desktop/src/main.ts)。本地服务器挂了,窗口会显示它最后的输出,View > Reload 重新启动它。窗口在后台时,某个对话需要你、或者跑完了,会弹系统通知。
桌面版总是跑自己的本地服务器;要连远端服务器,打开那台服务器提供的网页版。
编辑器:noeta acp
noeta acp 在 stdin 和 stdout 上提供 Agent Client Protocol,Zed、JetBrains 系列 IDE、Neovim 等 ACP 客户端都能把 noeta 当作自己的代理。需要 acp 这个 extra:
uv tool install --reinstall --with 'noeta-tui @ git+https://github.com/initxy/noeta#subdirectory=tui' \
'noeta[acp] @ git+https://github.com/initxy/noeta'在 Zed 里,把它加进 settings.json:
{
"agent_servers": {
"noeta": { "command": "noeta", "args": ["acp"] }
}
}模型和 provider 和终端界面一样,读 noeta 自己的设置(编辑器的环境变量会传给这个进程,所以在那里设 NOETA_MODEL 和 NOETA_API_KEY 也行)。args 里写上 --effort(或者用 NOETA_EFFORT、config.toml 的 effort)和 --permission-mode(如 ["--permission-mode", "acceptEdits", "acp"]),就定下了每个新会话起步时的推理强度和权限模式。
对应关系(noeta/host/acp.py):
- 一个 ACP 会话就是一个 noeta 任务。
session/new在编辑器的目录里建任务;session/load打开已有任务并重放历史。任务记下的东西一落进日志,就翻译成 ACP 的会话更新:文字、思考过程、带类别和 diff 的工具调用、计划(任务清单)。 - 审批以 ACP 权限请求的形式交给编辑器,选项和终端里的卡片一样(改文件的带 diff);子代理的审批也一样冒上来。ACP 没有提问表单:
AskUserQuestion以文字加选项的形式显示,你的下一句话就是回答。 - 模式和模型对应会话的 modes(任务提供的权限模式)和一个模型选项(目录里的模型和别名,外加
default,即代理自己的模型)。 - 斜杠命令是代理插件提供的命令,再加适配层自己的四个:
/model [name]、/context、/cost、/rewind [n] [both|conversation|code](文件在本地改回去;那句提问之前的对话在一个新会话里继续,需要你去加载)。 - 编辑器为会话指定的 MCP 服务器只交给这棵任务树的 MCP 插件,并且只放在内存里:它们的环境变量和请求头是凭据,绝不进任务日志。
- 提问里的图片和嵌入的上下文都能接收。
连到服务器
终端界面和 noeta acp 都可以不在本地跑,改为操作服务器上的任务;网页版和桌面版本来就是这样。
# 在服务器上
export NOETA_SERVER_TOKEN=$(openssl rand -hex 32)
noeta serve --host 0.0.0.0 --allow-host api.internal
# 在你的机器上
export NOETA_SERVER_TOKEN=... # 同一个令牌
noeta --server http://api.internal:8642 # 终端界面
noeta acp --server http://api.internal:8642 # 编辑器(写进 agent_servers)noeta serve 总是要求 bearer 令牌(只有监听本机回环地址时才能用 --no-token);没设 NOETA_SERVER_TOKEN 时,它第一次启动会生成一个、只打印这一次,~/.noeta/server-token 里只存它的 SHA-256。客户端启动前会带着令牌请求一次 GET /agents,不通就说清楚哪里不对(noeta/cli/server_token.py)。
连到服务器的前端不需要自己的模型。新任务用服务器的模型、在服务器的目录里跑,除非 --model 指定了模型,或 --cwd PATH 指定了服务器上的目录(不在服务器目录和它的 --allow-root 范围内的会被拒绝)。插件、代理、技能和设置都是服务器那边的:--plugin-dir 会被拒绝,/reload-plugins 会让你去重启工人。请求只能设置客户端被允许设置的那些任务配置(模型、推理强度、权限模式等);服务器启动时没加 --allow-bypass-permissions,bypassPermissions 一律拒绝。noeta -p 总在本地跑,拒绝 --server;程序想对服务器用无界面协议,就连它的 /stream-json WebSocket。
部署、工人、工作区和令牌的细节见作为服务端运行;所有接口见 HTTP / WebSocket 接口。