Skip to content

Remote Console 需求文档

受众:zhin-console 仓库维护者与贡献者
权威方:Zhin Host(@zhin.js/host-api + @zhin.js/host-router
版本:与 Host API 同步演进;机器可读契约见运行时 GET /pub/openapi.json

本文档描述 Remote Console(独立静态站)应实现的全部功能、API 契约与验收标准。Host 不提供 Console UI,仅暴露 API;UI 在 zhin-console 维护并部署(如 console.zhin.dev)。

快速上手见 console-remote.md。本地联调见 examples/test-bot/REMOTE_CONSOLE.md


1. 背景与目标

1.1 架构

仓库 / 包职责
zhin-consoleConsole 壳层、登录、内置管理页、构建与部署
@zhin.js/host-routerKoa 监听、Router、Bearer 认证、CORS
@zhin.js/host-api管理面 REST、Console RPC、SSE、PageManager
@zhin.js/pagemanagerConsole Entry 注册、GET /entries/@dev 资源
@zhin.js/contractEntry、PluginRegisterHostApi 等共享类型
@zhin.js/client浏览器 SDK:apiFetchloadConsoleEntries

1.2 控制台职责

  • 连接用户 Host(API Base + Bearer Token)
  • 渲染内置管理页面(插件、Endpoint、配置、日志等)
  • 订阅 SSE 实时事件
  • 通过 GET /entries 动态加载插件 Console Entry 并注册路由/工具
  • 不在 Host 端口(如 :8086)提供静态 Console 站点

1.3 SDK 依赖

Console 壳层应依赖 @zhin.js/client@zhin.js/contract,不重复实现认证与 Entry 加载逻辑。参考 packages/console/client/README.md


2. 连接与认证

规范
登录字段API Base URL(如 http://127.0.0.1:8086)、Bearer Tokenzhin.config / .envhttp.tokenHTTP_TOKEN
localStorage 键zhin_api_basezhin_api_token(与 @zhin.js/client 一致)
请求头Authorization: Bearer <token>
401 行为清除 Token,派发 zhin:auth-required 事件,跳转登录页
CORSHost 需配置 http.corsOrigins,包含 https://console.zhin.dev 及本地 dev Origin(如 http://127.0.0.1:5173
深链(仅 API Base)https://console.zhin.dev/?apiBaseUrl={encodeURIComponent(origin)}Token 不得出现在 URL
健康检查GET /pub/health(无需 Token)

2.1 登录页交互

  • 表单校验 API Base 为合法 URL
  • 可选:登录前请求 /pub/health 探测连通性
  • 登录成功后持久化到 localStorage,刷新免重复登录
  • 支持修改已保存的 API Base / Token(重新登录)

2.2 Demo Console 变体(demo.zhin.dev)

公开在线 Demo 使用 独立 Console 构建(非 console.zhin.dev 登录流):

规范
站点https://demo.zhin.dev → 预填 zhin_api_base = https://demo-api.zhin.dev
Token构建时注入 VITE_API_TOKENdemo scope,见 ADR 0016
UI默认路由仅 沙盒;隐藏配置/文件/cron 写操作
CORSHost http.corsOriginshttps://demo.zhin.dev
部署deploy/zhin-demo

3. API 契约总览

3.0 机器可读契约

运行时拉取:

GET {API_BASE}/pub/openapi.json
  • OpenAPI 3.1,无需 Token
  • 内省 5 端点已有完整 components.schemasIntrospection*
  • 其余端点逐步补齐;Console 可做端点发现与类型生成

认证规则

路径前缀Bearer
/pub/*
/entries否(拉取插件 Entry)
/webhook否(平台自有签名校验)
/api/*

3.1 公开 REST(无需 Token)

方法路径Query / 说明
GET/pub/health健康检查
GET/pub/openapi.jsonOpenAPI 文档
GET/pub/marketplace/searchq/keywordpagesize/limitcategoryofficial
GET/pub/marketplace/detail/{name}插件详情(name 可含 scope)

市场搜索响应示例

json
{
  "success": true,
  "data": [
    {
      "name": "@zhin.js/adapter-icqq",
      "displayName": "ICQQ",
      "version": "1.0.0",
      "description": "...",
      "author": "",
      "isOfficial": true,
      "category": "adapter",
      "keywords": [],
      "npm": "https://www.npmjs.com/package/...",
      "downloads": { "weekly": 0, "monthly": 0 }
    }
  ],
  "total": 42,
  "page": 1,
  "size": 20
}

3.2 管理 REST(需 Bearer)

默认 API 前缀为 /api(可由 http.base 配置)。

系统与统计

方法路径响应 data 要点
GET/api/system/statusuptime, memory, osMemory, cpu, platform, nodeVersion, runtime, pid, timestamp
GET/api/statsplugins.{total,active}, endpoints.{total,online}, commands, components, uptime, memory(MB), runtime

插件

方法路径响应 data 要点
GET/api/plugins数组:name, status(active/inactive), description, features[]
GET/api/plugins/:name单插件:filename, filePath, status, features, contexts[]

Endpoint

方法路径响应 data 要点
GET/api/endpoints数组:name, adapter, connected, status(online/offline)

配置与 Schema

方法路径说明
GET/api/config主配置对象
GET/api/config/:namename=app 返回主配置;否则返回插件 name/filePath
POST/api/config/:name当前为占位(未完整实现写回)
GET/api/schemas全部 JSON Schema
GET/api/schemas/:name单个 Schema 或 null

发消息

方法路径Body
POST/api/message/send{ context, endpoint, id, type, content }typeprivate | group | channel

成功响应含 data.messageIddata.timestamp

日志

方法路径说明
GET/api/logsQuery:limit(1–1000,默认 100)、level(可选,如 info/warn/error/all
DELETE/api/logs清空日志
GET/api/logs/statstotal, byLevel, oldestTimestamp
POST/api/logs/cleanupBody:{ days?, maxRecords? }

插件市场(已安装更新)

方法路径响应
GET/api/marketplace/updatesdata[]name, latest, description

运行时内省(分页)

第 5 节

方法路径
GET/api/introspection/commands
GET/api/introspection/endpoints
GET/api/introspection/bindings
GET/api/introspection/tools
GET/api/introspection/mcp

Agent 会话树(ADR 0010)

第 6 节

方法路径
GET/api/agent/sessions/:sessionKey/tree
POST/api/agent/sessions/:sessionKey/leaf

Assistant(可选,需 Host 启用)

方法路径说明
POST/api/assistant/events事件入口;未启用时 404
GET/api/assistant/jobs任务列表;assistant.enabled=false 时 404

3.3 Console RPC

入口POST /api/console/request

请求体

json
{
  "type": "config:get",
  "requestId": 1,
  "pluginName": "optional-plugin-name",
  "data": {}
}
字段说明
typeRPC 方法名(必填)
requestId客户端关联 ID(建议递增)
pluginName部分 config/schema 方法需要
其余字段type 而定(如 yamlfilePathdata

成功响应(HTTP 200):

json
{ "success": true, "data": {}, "requestId": 1 }

失败响应(HTTP 400):

json
{ "success": false, "error": "错误说明", "requestId": 1 }

Core RPC(handlers-core)

type请求字段响应 data
ping{ type: "pong" }(经 emit,REST 层取 data)
entries:getEntry 数组
config:getpluginName?全量或单插件配置对象
config:get-all含 schema 别名映射的全量配置
config:setpluginName, data{ success, reloaded?, message? };触发 SSE config:updated
config:get-yaml{ yaml, pluginKeys[] }
config:save-yamlyaml{ success, message }(需重启生效)
schema:getpluginName?Schema JSON 或 null
schema:get-allRecord<name, schema>
files:tree{ tree: FileTreeNode[] }
files:readfilePath{ content, size }
files:savefilePath, content{ success, message }
env:list{ files: string[] }
env:getfilename{ content }
env:savefilename, content{ success }
endpoint:list{ endpoints: EndpointRow[] }(含 pending 状态)
endpoint:infodata: { adapter, endpointId }Endpoint 详情
endpoint:sendMessagedata: { adapter, endpointId, id, type, content }{ messageId }
cron:list{ memory[], persistent[] }
cron:addcronExpression, prompt, label?, notify?新建 persistent 任务记录
cron:removeid{ success }
cron:pauseid{ success }
cron:resumeid{ success }

notify 形状:{ channel: "im" \| "silent" \| "log", ... }

数据库 RPC(db:*,handlers-db / websocket 回落)

type请求字段响应 data
db:info{ dialect, type, tables[], available }
db:tables{ tables: { name, columns? }[] }
db:selecttable, page?, pageSize?, where?{ rows, total, page, pageSize }
db:inserttable, row{ success }
db:updatetable, row, where{ success, affected }
db:deletetable, where{ success, deleted }
db:drop-tabletable{ success }
db:kv:gettable, key{ key, value }
db:kv:settable, key, value, ttl?{ success }
db:kv:deletetable, key{ success }
db:kv:entriestable, page?, pageSize?KV 分页条目

typerelated | document | keyvalue,UI 应按类型展示不同编辑器。

Endpoint 社交 / Inbox RPC(websocket 回落)

以下方法通过同一 POST /api/console/request 调用,data 字段承载参数:

type用途
endpoint:friends好友列表
endpoint:groups群列表
endpoint:channels频道列表
endpoint:requests好友/群请求
endpoint:requestApprove / endpoint:requestReject处理请求
endpoint:requestConsumed标记请求已读
endpoint:noticeConsumed标记通知已读
endpoint:inboxMessages收件箱消息
endpoint:inboxRequests收件箱请求
endpoint:inboxNotices收件箱通知
endpoint:deleteFriend删除好友
endpoint:groupMembers群成员
endpoint:groupKick / endpoint:groupMute / endpoint:groupAdmin群管理
system:restart触发 Host 重启

具体 data 字段因适配器而异;Console 应按 adapter 能力降级展示。


3.4 SSE 实时

连接GET /api/events

  • Content-Type: text/event-stream
  • 支持 Last-Event-ID / lastEventId 断线续传
  • 使用 EventSource 或等价实现;Token 通过 query 或自定义头(若 Host 支持)— 当前标准做法为 Bearer 头 + fetch 流式读取

连接时初始事件

typedata
sync{ key: "entries", value: ConsoleEntry[] }
init-data{ timestamp }

运行时推送

type说明data 形状(概要)
config:updated配置变更{ pluginName, config }
endpoint:message新收件消息消息行字段(adapter, endpoint_id, sender, channel 等)
endpoint:request好友/群请求请求行 + canAct
endpoint:notice平台通知notice 行
system:restarting即将重启
data-update通用刷新信号{ timestamp }

Console 应在相关页面订阅 SSE 并增量更新列表,避免全页轮询。


3.5 插件扩展(Console Entry)

发现

GET /entries

响应(ConsoleEntriesResponse):

json
{
  "entries": [
    {
      "id": "icqq",
      "resolvedModule": "/@dev/icqq/client/index.js",
      "order": 10,
      "enabled": true,
      "meta": { "name": "ICQQ", "version": "1.0.0" }
    }
  ],
  "runtimeEnvHint": "development"
}

加载流程

  1. 登录后调用 loadConsoleEntries({ hostApi, assetOrigin, fetchInit })
  2. 对每个 entry 动态 import(resolvedModule)
  3. 调用模块导出 register(hostApi)default.register
  4. hostApi.addRoute / addTool 注册侧栏与页面

Register Host API

方法说明
addRoute({ path, name, element, icon?, parent?, meta? })注册页面路由
addPage(...)addRoute 别名
addTool({ id?, name, icon?, parent?, path? })注册侧栏工具入口

meta 支持:hideInMenu, order, group, fullWidth

已知扩展

Entry路由说明
ICQQ/icqq含「登录辅助」Tab;登录 HTTP API 由 @zhin.js/adapter-icqq 注册,不在 host-api
Sandbox/@dev 相关浏览器调试扩展

未启用对应适配器时,Entry 可能不存在或页面内显示不可用 — 属预期行为。


4. 内置页面需求

以下每页需实现:路由数据加载加载/空/错态与 SSE 联动(如适用)。

4.1 登录页

  • 路由/login 或未认证时默认页
  • 数据/pub/health(可选)
  • 交互:保存 API Base + Token;支持深链 ?apiBaseUrl=

4.2 仪表盘

  • 路由建议/dashboard/
  • 数据GET /api/statsGET /api/system/status
  • 展示:插件数、Endpoint 在线数、命令/组件数、运行时间、内存
  • 刷新:手动 + 可选定时;data-update SSE 触发刷新

4.3 插件

  • 路由建议/plugins/plugins/:name
  • 数据GET /api/pluginsGET /api/plugins/:name、RPC schema:get
  • 展示:插件列表、Feature 统计、contexts、关联 Schema
  • 空态:无子插件时提示

4.4 Endpoint 管理

  • 路由建议/endpoints/endpoints/:adapter/:endpointId
  • 数据GET /api/endpoints、RPC endpoint:list / endpoint:info / endpoint:sendMessagePOST /api/message/send
  • 展示:在线状态、发消息表单(私聊/群/频道)
  • 实时:SSE endpoint:message

4.5 收件箱

  • 路由建议/inbox 或 Endpoint 详情子 Tab
  • 数据:RPC endpoint:inboxMessages / endpoint:inboxRequests / endpoint:inboxNotices 及消费/审批方法
  • 展示:消息、好友请求、通知分栏;未读标记
  • 持久化:IndexedDB 缓存列表,刷新后仍可浏览(见验收 §8)
  • 实时:SSE endpoint:message / endpoint:request / endpoint:notice

4.6 配置编辑器

  • 路由建议/config
  • 数据:RPC config:get-allconfig:setconfig:get-yamlconfig:save-yamlschema:get-all
  • 展示:按插件分 Tab;YAML 与表单双模式(若有 Schema)
  • 保存反馈:区分 reloaded: true(热重载)与需重启提示
  • 实时:SSE config:updated

4.7 项目文件与 .env

  • 路由建议/files/env
  • 数据:RPC files:tree / files:read / files:saveenv:list / env:get / env:save
  • 限制:单文件读取上限 1MB(Host 侧限制)

4.8 数据库浏览器

  • 路由建议/database
  • 数据:RPC db:*
  • 展示:按 type(relational / document / kv)切换 UI;表浏览、分页、增删改

4.9 系统日志

  • 路由建议/logs
  • 数据GET /api/logsGET /api/logs/statsDELETE /api/logsPOST /api/logs/cleanup
  • 展示:按 level 筛选、时间倒序、统计卡片、清理操作

4.10 插件市场

  • 路由建议/marketplace
  • 数据GET /pub/marketplace/searchGET /pub/marketplace/detail/{name}GET /api/marketplace/updates
  • 展示:搜索、分页、详情、已安装更新提示

4.11 定时任务

  • 路由建议/cron
  • 数据:RPC cron:list / cron:add / cron:remove / cron:pause / cron:resume
  • 展示:memory 与 persistent 分栏;新建表单含 cron 表达式、prompt、notify

4.12 运行时内省

第 5 节

4.13 Agent 会话树

第 6 节

4.14 Assistant Jobs

  • 路由建议/assistant/jobs
  • 数据GET /api/assistant/jobs
  • 显示条件:响应非 404;或 Host 配置 assistant.enabled
  • 展示:任务列表、eventsActive 状态

4.15 通用 UX

  • 所有列表页:加载态、错误 Toast、503「依赖未就绪」文案
  • 长列表:服务端分页,禁止一次渲染千行
  • 401 统一跳转登录
  • 侧栏:内置页 + addRoute / addTool 注册的插件页,支持 meta.order / meta.group 排序分组

5. 运行时内省模块

与 IM 命令 /cmd/endpoints/bindings/tools/mcp 数据同源

5.1 端点与 Query

资源路径默认 pageSizefilter 匹配字段
命令/api/introspection/commands25pattern, desc, plugin
Endpoint/api/introspection/endpoints30adapter, name
Agent 绑定/api/introspection/bindings30name, provider, model
工具/api/introspection/tools15name, source, description
MCP/api/introspection/mcp30name

Query 参数(全部可选):

参数说明
page页码,从 1 开始,默认 1
pageSize每页条数;缺省用上表默认值
filter子串筛选,大小写不敏感

5.2 响应 Envelope

json
{
  "success": true,
  "data": {
    "items": [],
    "page": 1,
    "pageSize": 25,
    "total": 120,
    "totalPages": 5,
    "filter": "github",
    "note": "可选说明,如 MCP 未初始化"
  }
}

503(依赖未就绪,如 AI / CommandFeature 不可用):

json
{
  "success": false,
  "error": "CommandFeature 不可用",
  "data": {
    "items": [],
    "page": 1,
    "pageSize": 25,
    "total": 0,
    "totalPages": 0
  }
}

5.3 Item Schema(OpenAPI)

完整定义见 /pub/openapi.jsoncomponents.schemas

Schema字段
IntrospectionCommandItempattern, desc, plugin?
IntrospectionBotItemadapter, name, online
IntrospectionBindingItemname, provider, model, mcpServers[], hasAgentFile
IntrospectionToolItemname, description, source?
IntrospectionMcpItemname, connected, toolCount

5.4 请求/响应示例(tools)

GET /api/introspection/tools?page=2&filter=github&pageSize=15
Authorization: Bearer <token>
json
{
  "success": true,
  "data": {
    "items": [
      {
        "name": "github_create_issue",
        "source": "mcp",
        "description": "Create a GitHub issue"
      }
    ],
    "page": 2,
    "pageSize": 15,
    "total": 28,
    "totalPages": 2,
    "filter": "github"
  }
}

5.5 Console UI 要求

  • 布局:单页五 Tab 或侧栏子菜单(命令 / Endpoint / 绑定 / 工具 / MCP)
  • 搜索框:输入 debounce 后更新 filter 并重置 page=1
  • 表格列:与 OpenAPI item 字段一一对应
  • 分页器:展示 page/totalPages/total;切换页码重新请求
  • 503:展示 error 文案 + 空表,不白屏
  • 路由建议/introspection/commands?page=2&filter=github(便于分享书签)
  • 页脚(可选):展示 REST 直链,格式与 IM 页脚一致:
    • 完整列表: {apiBase}/introspection/tools?page=2&filter=github

6. Agent 会话树模块

依据 ADR 0010 D3:Host 已交付 HTTP API,图形化 UI 由 Console 实现。行为与 IM /tree 命令一致,Console 无需实现 IM 命令本身。

6.1 GET 会话树

GET /api/agent/sessions/:sessionKey/tree
  • sessionKey:URL 编码(如 private%3Auser123 表示 private:user123

成功 200

json
{
  "success": true,
  "data": {
    "sessionKey": "private:user123",
    "sessionId": "sess_abc",
    "activeLeafMessageId": 42,
    "points": [
      { "index": 1, "messageId": 10, "preview": "你好,帮我查天气" },
      { "index": 2, "messageId": 25, "preview": "改成查北京" }
    ]
  }
}
字段说明
points[].index用户消息在活跃路径上的序号(供 POST index 使用)
points[].messageId数据库消息 ID(供 POST messageId 使用)
points[].preview用户消息预览(最多约 80 字符)
activeLeafMessageId当前活跃叶节点;null 表示默认最后一条

404:无活跃会话
503Agent session tree runtime 未就绪

6.2 POST 切换叶节点

POST /api/agent/sessions/:sessionKey/leaf
Content-Type: application/json

{ "index": 2 }

{ "messageId": 25 }二选一

成功 200

json
{
  "success": true,
  "message": "已切换 active leaf 至消息 #25",
  "data": {
    "sessionKey": "private:user123",
    "sessionId": "sess_abc",
    "activeLeafMessageId": 25
  }
}

400:参数无效或切换失败
404 / 503:同 GET

6.3 Console UI 要求

  • 路由建议/agent/sessions/agent/tree
  • sessionKey 输入:文本框 + 历史记录下拉(可选)
  • 分支列表:展示 indexpreview;高亮 activeLeafMessageId 对应行
  • 操作:点击行 → 确认 → POST leaf → 刷新 GET tree
  • 错误态:404 提示「无活跃会话」;503 提示「Agent 未就绪」
  • 说明文案:切换叶节点后,后续 AI 对话从该用户消息点继续分支(与 /tree N 一致)

7. 非功能需求

类别要求
OpenAPI/pub/openapi.json 为契约来源;内省 schema 已完整
安全Token 仅存 localStorage,不得出现在 URL(apiBaseUrl 除外);生产 HTTPS
CORS仅访问用户配置的 Host Origin
性能内省 tools 默认 pageSize=15;列表虚拟滚动可选
离线收件箱等关键列表 IndexedDB 持久化
国际化UI 文案由 zhin-console 决定;API 字段名保持英文
兼容性对接 @zhin.js/client 当前 major 版本;React >= 18

8. 联调与验收

8.1 环境准备

Host(本仓库 test-bot):

bash
cd examples/test-bot
pnpm dev

zhin.config.yml 示例:

yaml
http:
  port: 8086
  token: <HTTP_TOKEN>
  corsOrigins:
    - "http://127.0.0.1:5173"
    - "https://console.zhin.dev"

Console(zhin-console 仓库):

bash
cd zhin-console && pnpm install && pnpm dev

浏览器打开开发地址,登录 API Base http://127.0.0.1:8086 + Token。

8.2 验收清单

  • [ ] 登录成功,Token 持久化,401 跳转登录
  • [ ] 仪表盘展示 stats / system status
  • [ ] 插件列表与详情可访问
  • [ ] Endpoint 列表、发消息成功
  • [ ] 收件箱加载,刷新后 IndexedDB 数据仍在
  • [ ] 配置页读写,保存后 SSE config:updated 触发刷新
  • [ ] 日志列表、筛选、清理
  • [ ] 插件市场搜索与详情
  • [ ] GET /entries 加载 ICQQ/Sandbox 等扩展路由
  • [ ] Network 命中 /api/console/request/api/events
  • [ ] 内省五 Tab:分页、filter、503 不崩溃
  • [ ] 会话树:加载 tree、切换 leaf 后 activeLeafMessageId 更新
  • [ ] /pub/openapi.jsongetIntrospectionTools schema 与实调响应一致

9. 明确不在范围

  • Host 端口提供 Console 静态站(serveClientHost: false
  • ICQQ 登录辅助 HTTP 实现(由 @zhin.js/adapter-icqq 提供;Console 只嵌扩展 Tab)
  • IM 出站消息链、Agent 推理与工具执行逻辑
  • plan mode、子 agent 编排可视化(另文档跟踪)
  • Host 侧 OpenAPI 全端点 schema 一次性补全(逐步迭代)

10. 参考索引

文档 / 代码说明
console-remote.mdRemote Console 使用说明
packages/host/api/README.mdHost API 功能列表
packages/console/client/README.md浏览器 SDK
packages/console/contract/src/index.tsEntry 与 Host API 类型
packages/host/router/src/introspection-openapi.ts内省 OpenAPI schema
ADR 0010会话树决策
{API_BASE}/pub/openapi.json运行时 OpenAPI

附录 A:提交到 zhin-console 项目

在 zhin-console 仓库创建 Issue / Epic 时,建议引用:

  • 需求文档 URL(main 分支):
    https://github.com/zhinjs/zhin/blob/main/docs/console/requirements.md
  • 联调文档examples/test-bot/REMOTE_CONSOLE.md
  • 阻塞依赖:无 — Host API 已就绪;内省与会话树 REST 已实现
  • 建议里程碑:登录与核心页 → 内省模块 → 会话树 UI → Assistant / 扩展打磨

复制 Issue 描述模板:

markdown
## 需求来源
- [Remote Console 需求文档](https://github.com/zhinjs/zhin/blob/main/docs/console/requirements.md)
- 章节:<!-- 如 §5 运行时内省 -->

## 验收
见需求文档 §8.2 相关 checkbox

## 联调
Host: examples/test-bot `pnpm dev` → :8086
Console: `pnpm dev` → 登录 API Base + HTTP_TOKEN

基于 MIT 许可发布