文档 / 本地 API 参考
本地 API 参考
面向 AI Agent 与本地自动化的 HTTP REST API 规范。
概述
Metrix.Bar 在本地回环地址(127.0.0.1:6387)上运行轻量 HTTP REST 服务。该服务专为 AI 编程助手、命令行脚本及自动化工具设计,用于查询、校验、更新与管理 .bar 看板。
- 服务地址:
http://127.0.0.1:6387 - 鉴权机制:通过请求头
Authorization: Bearer <token>进行 Bearer Token 验证(GET /v0/health免鉴权) - 完整契约:OpenAPI 规范文件可在
/openapi.json获取(当前为 v0 版本规范)
本文中的端口 6387 仅为示例,请使用 App 的 Agent 设置中实际显示的地址和端口。
安全防护与状态码
本地 API 针对未授权本地网络与恶意网页跨站请求内置了安全防护:
- 严格回环与 Host 头校验:服务仅绑定
127.0.0.1,同时校验Host请求头,只接受127.0.0.1、localhost或本机回环 IP(含端口)。非回环 Host 直接返回403 Forbidden。 - 阻止浏览器跨站:携带
Origin头的请求一律拒绝(含Origin: null,返回403 Forbidden),不启用 CORS,防御 DNS Rebinding 与浏览器跨站调用。 - Bearer Token 校验:除
/v0/health外,所有请求必须携带在 Settings → Agent 中配置的密钥;缺失或无效 Token 返回401 Unauthorized。 - 请求体上限:单次请求体上限为 2 MiB。
- 密钥隔离:系统安全存储(keychain)中的密钥明文绝不回显;输入列表仅报告
is_set: true | false,value恒为null。
状态码说明
| 状态码 | 含义 | 说明 |
|---|---|---|
200 OK | 请求成功 | 返回请求的资源或操作结果。 |
201 Created | 创建成功 | 看板创建成功,响应头包含新源码的 ETag。 |
202 Accepted | 已接受 | 异步任务已接受并派发(如刷新调度、事件派发),不等待执行结果。 |
204 No Content | 处理成功 | 请求已完成,无响应体(如更新 inputs、写入 secret、删除看板)。 |
400 Bad Request | 请求错误 | 请求体格式错误、缺少必填字段或传入未声明的 input 参数。 |
401 Unauthorized | 未授权 | 缺少或无效 Bearer Token;带 WWW-Authenticate: Bearer realm="Metrix.Bar"。scheme 大小写不敏感,token 精确匹配。 |
413 Payload Too Large | 请求体过大 | 请求体超过 2 MiB。 |
415 Unsupported Media Type | 不支持的媒体类型 | 源码端点缺失、无效或不支持的 Content-Type。 |
403 Forbidden | 禁止访问 | 请求携带 Origin 请求头,或 Host 头部非本机回环地址。 |
404 Not Found | 未找到 | 指定的看板不存在,或目标 secret 参数未在 .bar 中声明。 |
412 Precondition Failed | 前置条件失败 | If-Match 乐观并发校验未通过,源码 ETag 不匹配,请重新 GET source 后重试。 |
422 Unprocessable Entity | 无法处理的实体 | .bar 源码解析失败,返回结构化诊断信息 diagnostics。 |
501 Not Implemented | 未实现 | 宿主环境未接入对应的应用层能力。 |
端点列表
所有路由均挂载在 /v0 前缀下:
| 方法 | 路径 | 描述 |
|---|---|---|
GET | /v0/health | 免鉴权健康检查,返回运行状态、运行毫秒数与协议版本。 |
POST | /v0/validate | 校验 .bar 源码格式与语法,不落盘。 |
POST | /v0/check | 隔离运行 Worker 并校验视图树,不安装看板。 |
GET | /v0/types | 返回供编辑器与 Agent 使用的 Worker TypeScript 声明。 |
GET | /v0/dashboards | 获取所有已安装看板的列表、状态与 ETag。 |
POST | /v0/dashboards | 根据传入的 .bar 源码创建新看板。 |
GET | /v0/dashboards/:id | 获取指定看板的运行状态快照与 UI 视图树。 |
DELETE | /v0/dashboards/:id | 卸载看板并清理其关联的密钥。 |
GET | /v0/dashboards/:id/source | 获取 .bar 源码文本,并在响应头返回 ETag。 |
PUT | /v0/dashboards/:id/source | 替换 .bar 源码文本(支持 If-Match 并发校验)。 |
PATCH | /v0/dashboards/:id/settings | 更新入口、默认与按来源打开方式覆盖(placements、open_mode、open_modes)。 |
GET | /v0/dashboards/:id/inputs | 获取输入参数声明、当前值及密钥配置状态。 |
POST | /v0/dashboards/:id/inputs | 更新看板的普通输入参数值。 |
PUT | /v0/dashboards/:id/secrets/:name | 向系统安全存储写入敏感密钥。 |
POST | /v0/dashboards/:id/refresh | 触发一次立即刷新周期。 |
POST | /v0/dashboards/:id/reload | 重读磁盘源码并预检 Worker;预检失败保留旧实例。 |
POST | /v0/dashboards/:id/events/:name | 向看板 Worker 派发自定义事件。 |
接口详解
健康检查
GET /v0/health
检查本地服务是否正常启动,无需携带鉴权 Token。返回当前进程运行毫秒数与协议版本号。
curl -fsS http://127.0.0.1:6387/v0/health
响应示例 (200 OK):
{
"status": "ok",
"uptime_ms": 9900000,
"protocol_version": "0",
"app_version": "0.1.0"
}
app_version为可选字段,仅在应用注入版本号时返回。
校验 .bar 源码
POST /v0/check 仅接受 application/json(可带 charset),请求为 { "source": "...", "inputs": {}, "scheduled": false, "tree": false },仅 source 必填。在隔离 Worker 中执行 render;仅显式传入 scheduled: true 才先执行计划任务。scheduled 与 tree 均默认为 false,tree: true 时返回视图树。预检返回诊断但不安装,网络、存储等外部副作用仍可能发生。GET /v0/types 返回文本格式的 metrixbar.d.ts,供编辑器补全。
外部工具已将源码保存到磁盘时,可携带 Bearer token、无请求体调用 POST /v0/dashboards/:id/reload,预检失败保留旧运行实例。
隔离 fixture 与 PNG 截图
POST /v0/check 可选 fixture 与 screenshot,树和截图来自同一次 Worker render。fixture 提供人工测试数据,整个 fixture JSON 最大 2 MiB,拒绝未知字段;now 必填且为 RFC 3339 时间,冻结 UTC 时钟。输入按声明默认值 → fixture.inputs → 请求 inputs 合并,并保持类型与 secret 输入校验。
{
"source": "<bar ...>...</bar>",
"scheduled": true,
"tree": true,
"fixture": {
"now": "2026-06-01T12:00:00Z",
"inputs": {},
"secrets": { "token": "fixture-token" },
"files": { "/fixture/value": "42" },
"icons": {
"lucide:zap": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path d=\"m13 2-3 8H4l7 12 3-8h6Z\"/></svg>"
},
"http": [{ "url": "https://metrics.example.com/value", "body": "42" }]
},
"screenshot": { "theme": "dark", "width": 320, "height": 240, "scale": 2 }
}http 精确匹配 method(默认 GET)、完整 URL 及可选 request_body,status 默认 200,body 是原始响应字符串。远程模块与 Image/Avatar 资源也使用 fixture HTTP;资源在原 Worker 关闭前通过相同权限与身份请求头加载。files 按绝对虚拟路径匹配,仍检查声明权限;secrets 只读取 fixture 值,不读系统密钥。命名 Icon(或无 src/svg 的 Image.name)必须在 icons 中提供 SVG 或图片 data URI,每项 1..256 KiB;缺失图标报错,不读 Assets、磁盘缓存或真实 Iconify 网络。内联 SVG 与图片 data URI 保持可用。fixture 模式禁用 socket;未匹配 HTTP、文件、secret 或图标必定产生诊断,即使脚本捕获错误也不能通过。fixture 不得含真实凭据。未传 fixture 时仍可能执行声明权限允许的真实 I/O。
省略 screenshot 时不生成 PNG。提供时 theme 必须为 light 或 dark;width/height 为 DIP,省略使用 panel 尺寸(无声明时 320×240),每边 1..2048;scale 默认 1、范围 0.5..4,最终像素总量不超过 16 Mi。成功响应包含 screenshot: {mime_type:"image/png", width:640, height:480, data_base64:"..."},宽高为实际 PNG 像素。使用内存绘图并等待资源,不截取桌面;资源收集失败返回 resources(未匹配 fixture 还返回 fixture),绘图失败返回 screenshot 诊断,HTTP 200、valid:false,无截图。参数错误返回 400。loopback、Bearer、Origin 和 secret 隔离规则继续生效。
POST /v0/validate
验证 .bar 源码语法与规范合法性,不写入系统。解析器采用单错误快速失败机制,diagnostics 最多包含一条诊断信息。
curl -fsS -X POST http://127.0.0.1:6387/v0/validate \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: text/plain" \
--data-binary @dashboard.bar
校验通过示例 (200 OK):
{
"valid": true,
"diagnostics": []
}校验失败示例 (200 OK):
{
"valid": false,
"diagnostics": [
{
"severity": "error",
"code": "inputs/input@type",
"message": "unknown input type \"color\"",
"path": "inputs/input@type"
}
]
}看板管理
GET /v0/dashboards
列出当前所有看板概要。状态(status)枚举值为 idle、loading、ok 或 error。若看板定义中包含可选元数据(version、icon、description、license、homepage、docs),非空时将一并返回。
curl -fsS -H "Authorization: Bearer $TOKEN" http://127.0.0.1:6387/v0/dashboards
响应示例 (200 OK):
{
"dashboards": [
{
"id": "github-repo",
"title": "GitHub Repo",
"status": "ok",
"protocol_version": "0",
"revision": 3,
"etag": "0123456789abcdef",
"version": "1.0.0",
"description": "Track GitHub repository stars, forks, and activity."
}
]
}POST /v0/dashboards
根据传入的 .bar 源码创建并安装新看板。请求体支持 text/plain、application/octet-stream 或 JSON {"source": "..."}。返回创建后的看板摘要(包含 protocol_version 及 <bar> 中声明的非空可选元数据)。
curl -fsS -X POST http://127.0.0.1:6387/v0/dashboards \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: text/plain" \
--data-binary @dashboard.bar
响应示例 (201 Created):
响应头携带 ETag: "0123456789abcdef"。
{
"id": "github-repo",
"status": "loading",
"title": "GitHub Repo",
"protocol_version": "0",
"etag": "0123456789abcdef"
}若源码解析失败,返回 422 Unprocessable Entity 并携带 diagnostics。
GET /v0/dashboards/:id
获取看板快照与最近一次成功渲染的 UI 视图树。在 API 返回的视图树中,所有事件处理函数(如 onClick)统一输出为 null。
curl -fsS -H "Authorization: Bearer $TOKEN" http://127.0.0.1:6387/v0/dashboards/github-repo
响应示例 (200 OK):
{
"id": "github-repo",
"status": "ok",
"updated_at": "2025-01-01T00:00:00Z",
"revision": 3,
"inputs": {
"repository": "golang/go"
},
"docs": "配置 GitHub 仓库路径即可开始监控。",
"view": {
"tray": {
"title": "★ 1.2k"
},
"panel": {
"type": "Column",
"props": { "padding": 16, "gap": 12 },
"children": [{ "type": "Text", "props": { "text": "egoist/mygo", "bold": true } }]
}
}
}当
status为error时,响应中会包含error字段记录最近一次运行错误;docs为来自<docs>的纯文本配置说明(为空时不返回);view保留上一次成功渲染的视图树(若尚未成功渲染过则为null)。
GET /v0/dashboards/:id/source
获取原始 .bar 源码文本。响应头包含 RFC 9110 引号格式的 ETag(源码 SHA-256 前 16 位十六进制字符)。
curl -fsS -H "Authorization: Bearer $TOKEN" -i http://127.0.0.1:6387/v0/dashboards/github-repo/source
PUT /v0/dashboards/:id/source
全量替换 .bar 源码。请求体支持 text/plain、application/octet-stream 或 JSON {"source": "..."}。新源码的 id 必须与路径中的 :id 一致。
支持通过 If-Match 进行乐观并发控制:传入 GET source 返回的带引号 ETag、逗号分隔的带引号标签列表,或单独的 * 匹配任意已存在看板。采用强比较,弱标签始终不匹配;裸标签和畸形列表返回 400。只有缺失该头才跳过标签检查,仍保护并发文件编辑。
curl -fsS -X PUT http://127.0.0.1:6387/v0/dashboards/github-repo/source \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: text/plain" \
-H 'If-Match: "0123456789abcdef"' \
--data-binary @updated.bar
更新成功响应 (200 OK):
响应头返回新源码的 ETag。
{
"id": "github-repo",
"updated": true
}ETag 不匹配响应 (412 Precondition Failed):
{
"error": "etag mismatch"
}源码解析失败响应 (422 Unprocessable Entity):
{
"error": "validation failed",
"diagnostics": [
{
"severity": "error",
"code": "bar@id",
"message": "source id \"other-repo\" does not match dashboard \"github-repo\"",
"path": "bar@id"
}
]
}DELETE /v0/dashboards/:id
卸载指定看板,停止运行实例并清理其保存在系统安全存储中的密钥。.bar 源码文件仍保留在磁盘上。
curl -fsS -X DELETE -H "Authorization: Bearer $TOKEN" http://127.0.0.1:6387/v0/dashboards/github-repo
响应示例 (204 No Content):无响应体。
配置与密钥
GET /v0/dashboards/:id/inputs
获取看板所有输入参数的声明与当前值。仅 secret 类型的 value 恒为 null,通过 is_set: true | false 反映配置状态;file 类型通过此鉴权接口正常返回本地文件路径字符串。
curl -fsS -H "Authorization: Bearer $TOKEN" http://127.0.0.1:6387/v0/dashboards/github-repo/inputs
响应示例 (200 OK):
{
"inputs": [
{
"name": "repository",
"type": "string",
"required": true,
"label": "Repository",
"help": "owner/repo",
"value": "egoist/mygo",
"is_set": true
},
{
"name": "token",
"type": "secret",
"required": false,
"label": "GitHub Token",
"value": null,
"is_set": true
}
]
}POST /v0/dashboards/:id/inputs
更新普通输入参数。请求体必须为 {"inputs": {...}} 包装对象。更新后会触发异步刷新。
输入值类型必须与声明匹配(string/file 为字符串,bool 为布尔值,integer 为整数,number 为有限数值)。不允许在此更新 secret 类型参数(须走专门的 secrets 接口),传入未声明的参数将返回 400 Bad Request。
curl -fsS -X POST http://127.0.0.1:6387/v0/dashboards/github-repo/inputs \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"inputs": {"repository": "golang/go"}}'
响应示例 (204 No Content):无响应体。
PUT /v0/dashboards/:id/secrets/:name
将敏感密钥写入系统安全存储(keychain)并触发看板刷新。目标参数必须在 .bar 中声明为 type="secret",否则返回 404 Not Found。密钥不进入普通看板记录、快照或 API 响应;看板脚本可读取凭据用于认证。
请求体为 JSON 格式 {"value": "..."}:
curl -fsS -X PUT http://127.0.0.1:6387/v0/dashboards/github-repo/secrets/token \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"value": "ghp_xxxxxxxxxxxx"}'
响应示例 (204 No Content):无响应体。
事件与控制
POST /v0/dashboards/:id/refresh
向调度器派发刷新命令,触发看板 Worker 重新拉取数据。返回 202 Accepted 表示已接受请求,不等待异步渲染完成。
curl -fsS -X POST -H "Authorization: Bearer $TOKEN" http://127.0.0.1:6387/v0/dashboards/github-repo/refresh
响应示例 (202 Accepted):无响应体。
POST /v0/dashboards/:id/events/:name
向看板 Worker 的 event(name, payload) 导出函数派发自定义事件。请求体为任意 JSON 值(可为空,空时 payload 为 null)。事件处理完成后会自动提交重新渲染。
curl -fsS -X POST http://127.0.0.1:6387/v0/dashboards/github-repo/events/notify \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"action": "toggle-mode"}'
响应示例 (202 Accepted):无响应体。
PATCH /v0/dashboards/:id/settings
请求体接受 placements、open_mode 和 open_modes,至少提供一个字段:
placements:menubar、sidebar-left、sidebar-right、sidebar-bottom的任意无重复数组,或null。open_mode:默认打开方式覆盖,值为flyout、dialog或null。open_modes:按来源配置的打开方式对象,或null:- 省略(missing):保持现有的按来源例外覆盖不变;
null或{}:清除全部 5 个来源的例外覆盖,所有来源恢复跟随默认打开方式;- 传入对象时整份替换所有来源的例外覆盖(非增量 merge)。支持的 key 仅限
menubar、sidebar-left、sidebar-right、sidebar-bottom、shortcut;value 仅限flyout、dialog。未知 key、非法值或错误类型返回 400。
省略字段保持原设置,null 清除对应字段的用户覆盖并恢复作者默认值,placements: [] 禁用全部视觉入口。各项覆盖独立保存,官方和第三方看板使用同一契约。
未知字段、错误类型、未知或重复位置、无效打开模式,以及空对象均返回 400;旧 show_in_menu_bar 字段不再接受。缺失看板返回 404,操作冲突返回 409,持久化失败返回 500,未接入设置回调返回 501。
配置入口、默认打开方式与按来源打开方式(例如菜单栏与右侧 Sidebar 入口,默认 Flyout,而快捷键与右侧 Sidebar 使用 Dialog):
curl -fsS -X PATCH http://127.0.0.1:6387/v0/dashboards/github-repo/settings \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"placements":["menubar","sidebar-right"],"open_mode":"flyout","open_modes":{"shortcut":"dialog","sidebar-right":"dialog"}}'
响应(200 OK):返回更新后的最终有效设置。无入口时 placements 保持 [],不会返回 null;open_modes 始终返回完整的五个来源有效解析模式对象,永不为 null。
{
"id": "github-repo",
"placements": ["menubar", "sidebar-right"],
"open_mode": "flyout",
"open_modes": {
"menubar": "flyout",
"sidebar-left": "flyout",
"sidebar-right": "dialog",
"sidebar-bottom": "flyout",
"shortcut": "dialog"
}
}其他请求体:
修改默认打开方式:
{ "open_mode": "flyout" }禁用全部视觉入口:
{ "placements": [] }恢复默认入口(不改变打开方式与快捷键):
{ "placements": null }恢复默认打开方式(不改变按来源例外或入口):
{ "open_mode": null }清除全部按来源例外覆盖(所有来源恢复跟随默认打开方式):
{ "open_modes": null }同时恢复默认入口、默认打开方式并清除所有来源例外:
{ "placements": null, "open_mode": null, "open_modes": null }程序化面板操作(如 $metrix.panel.open() / toggle())遵循最终有效的默认打开方式(open_mode),{open} 返回结构不变。
源码端点(validate、创建和源码替换)必须显式声明 Content-Type:text/plain、application/octet-stream,或包含 { "source": "..." } 的 application/json;允许 charset 等媒体类型参数。缺失或不支持的类型返回 415,JSON 格式错误返回 400,请求体超过 2 MiB 返回 413。