跳转至

xuns · Web 服务

在浏览器里使用同一套智能体:聊天界面、事件流、工具活动折叠块、文件面板、多会话切换与重命名。

xuns .                                   # 所有会话共用当前目录
xuns                                     # 每个会话一个临时工作目录
xuns . --host 0.0.0.0 --port 18960 --base-path /xun
xuns --no-initial-agent .                      # 不起任何智能体,只用来浏览 /docs/

参数

参数 形式 默认 说明
workdir 位置参数,可选 空 所有会话共用的工作目录;省略或空串时每个会话分配独立临时目录(退出时清理)
--host 字符串 localhost 监听地址;0.0.0.0 时启动信息会额外打印 aka localhost 形式
--port 整数 18960 端口;0 表示由系统分配随机端口,实际端口会打印出来
--token 字符串 空 访问令牌;空值自动生成 secrets.token_urlsafe(24)
--base-path 字符串 空 URL 前缀,用于子路径部署;不能包含空段、.、..
--manage-sessions / --no-manage-sessions 布尔开关 开启 是否允许在界面中创建/删除/重命名会话
--initial-agent / --no-initial-agent 布尔开关 开启 是否在启动时创建初始智能体。关闭后进程只提供静态界面与 /docs/ 文档站,无需配置 LLM 即可浏览手册;只要 --manage-sessions 仍开启,界面依然可以新建会话,而新建会话要创建智能体,需要可用的 LLM 配置

对应的 Python API:

from xun import web_session

web_session(
    workdir=".", host="localhost", port=18960, token="",
    base_path="", manage_sessions=True, initial_agent=True,
)

启动后打印每个会话的直达地址(令牌已带在查询串里):

Agents are available at the following URLs:
http://localhost:18960/chat/?session=%2F&token=...

令牌与登录

--token 是访问凭据,服务端用 hmac.compare_digest 比对,接受三种携带方式:

  1. Authorization: Bearer <token>
  2. Cookie xun_web_token(登录页或 ?token= 访问 /chat/ 后写入,HttpOnly + SameSite=Strict,HTTPS 下加 Secure)
  3. ?token=<token> 查询串——只对 /chat 页面生效:命中后写入 Cookie 并 303 跳转到去掉 token 参数的地址,因此 API 与 WebSocket 请求不能靠查询串认证

未认证的 /chat 页面请求会 303 跳转到登录页 --base-path/login;其余未认证请求(静态资源与 API)一律返回 401 {"detail":"Not authenticated"};未认证的 WebSocket 以 1008 关闭。文档站点 /docs/ 免登录,便于浏览手册。

路由一览

以下路径均带 --base-path 前缀(未设置时为空)。

路径 说明
/ 重定向到 /chat/
/chat/ 前端界面(静态资源,构建自 web/)
/docs/ 中英双语文档站点(打包在 src/xun/assets/docs),公开访问
/login 登录页;?next= 只接受指向 /chat 的站内地址,其余一律回落到 /chat/
/api/sessions 会话列表与增删改名(POST /api/sessions/create、/remove、/rename)
/session/、/session/<uuid>/ 各会话的后端显示实例(WebSocket /ws、/api/events、/api/prompts、/api/files/...)
/session/<path>/srv/<key>/ 临时静态目录托管,见下

会话状态在列表接口里以 idle / running / waiting 表示(waiting = 有提示待回答)。最后一个会话不能被删除;关闭 --manage-sessions 后增删改接口返回 405。会话名可在侧边栏直接改(去掉首尾空白后最长 80 字符,空名返回 422),只影响界面显示,不改挂载路径。

事件流展示

用户消息独立成条,不塞进回合折叠块里;工具调用与结果折叠为活动块,只有单智能体的活动块省掉内层折叠,多智能体仍需逐层展开。最新一个活动块的标题下挂着活动预览卡片:最多 3 行,每行取事件文本前 160 字符,新预览把旧的挤出去;块内只要有智能体在跑就显示「运行中」图标。

消息通道与断线重连

前端发出的每条 message / command 都带一个客户端生成的 client_id(UUID):

  • 服务端受理后才回 accepted 回执,前端收到回执才清空输入框与附件;在此之前输入内容一直留在界面上,看不出「已发送却未生效」的情况。
  • 连接断开时,未收到回执的那条在重连后自动重发;服务端按 client_id 去重(记住最近 2000 个),因此同一条消息不会执行两次。
  • 不带 client_id 的消息(例如自己写的客户端)照常处理,只是没有回执与去重。

字段与端点细节见 显示层与 Web 服务。

文件面板

xuns 启动的会话开启了 expose_files,提供目录浏览、PDF/全屏预览、文本预览语法高亮、行内新建目录与重命名/移动、目录上传(结果通知列出全部文件并自动消失;同名文件会被直接覆盖)。文件信息里的体积按量级展示(B / KiB / MiB …,最多一位小数)。所有文件操作被限制在智能体的工作目录内,越界返回 400,符号链接被跳过。

静态目录托管 /srv/

如果工作目录里生成了一个静态站点(例如本项目的 site/),可以在界面里把它挂成 GET /session/<path>/srv/<key>/:

  • key 是随机令牌,链接本身即凭据(该路径免令牌,且只放行 GET / HEAD)
  • 默认 1 小时过期(SERVE_TTL_SECONDS = 3600),同时最多 16 个(超出返回 429)
  • 传入空路径即可托管工作目录根本身

部署提示

  • 界面与后端通过 WebSocket 通信,反向代理需允许升级请求并保持 X-Forwarded-Host / X-Forwarded-Proto。
  • 子路径部署只需要 --base-path,所有资源与语言切换链接都是相对路径,可挂任意前缀。
  • --host 0.0.0.0 会把服务暴露到所有网卡,请配合防火墙或反向代理鉴权使用。

前端开发:npm run dev 会用 concurrently 并行拉起后端(xuns,需要 uv)与 Vite(Vue DevTools);只想跑界面用 npm run dev:ui。

cd web && npm install && npm run dev      # 界面 http://127.0.0.1:5173