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 比对,接受三种携带方式:
Authorization: Bearer <token>- Cookie
xun_web_token(登录页或?token=访问/chat/后写入,HttpOnly+SameSite=Strict,HTTPS 下加Secure) ?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