---
title: "本地 API 参考：Agent 自动化与接口规范 · Metrix.Bar"
description: "通过 127.0.0.1:6387 上的本地 HTTP API 与 Metrix.Bar 深度集成。了解端点规范、Bearer 鉴权、语法校验、源码更新与事件派发。"
canonical: "https://metrix.bar/zh/docs/local-api"
language: "zh-CN"
---

# 本地 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`](/openapi.json) 获取（当前为 v0 版本规范）

本文中的端口 `6387` 仅为示例，请使用 App 的 Agent 设置中实际显示的地址和端口。

## 安全防护与状态码

本地 API 针对未授权本地网络与恶意网页跨站请求内置了安全防护：

1. **严格回环与 Host 头校验**：服务仅绑定 `127.0.0.1`，同时校验 `Host` 请求头，只接受 `127.0.0.1`、`localhost` 或本机回环 IP（含端口）。非回环 Host 直接返回 `403 Forbidden`。
2. **阻止浏览器跨站**：携带 `Origin` 头的请求一律拒绝（含 `Origin: null`，返回 `403 Forbidden`），不启用 CORS，防御 DNS Rebinding 与浏览器跨站调用。
3. **Bearer Token 校验**：除 `/v0/health` 外，所有请求必须携带在 **Settings** → **Agent** 中配置的密钥；缺失或无效 Token 返回 `401 Unauthorized`。
4. **请求体上限**：单次请求体上限为 2 MiB。
5. **密钥隔离**：系统安全存储（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。返回当前进程运行毫秒数与协议版本号。

```bash
curl -fsS http://127.0.0.1:6387/v0/health
```

**响应示例 (`200 OK`)**：

```json
{
  "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 输入校验。

```json
{
  "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` 最多包含一条诊断信息。

```bash
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`)**：

```json
{
  "valid": true,
  "diagnostics": []
}
```

**校验失败示例 (`200 OK`)**：

```json
{
  "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`），非空时将一并返回。

```bash
curl -fsS -H "Authorization: Bearer $TOKEN" http://127.0.0.1:6387/v0/dashboards
```

**响应示例 (`200 OK`)**：

```json
{
  "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>` 中声明的非空可选元数据）。

```bash
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"`。

```json
{
  "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`。

```bash
curl -fsS -H "Authorization: Bearer $TOKEN" http://127.0.0.1:6387/v0/dashboards/github-repo
```

**响应示例 (`200 OK`)**：

```json
{
  "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 位十六进制字符）。

```bash
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。只有缺失该头才跳过标签检查，仍保护并发文件编辑。

```bash
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`。

```json
{
  "id": "github-repo",
  "updated": true
}
```

**ETag 不匹配响应 (`412 Precondition Failed`)**：

```json
{
  "error": "etag mismatch"
}
```

**源码解析失败响应 (`422 Unprocessable Entity`)**：

```json
{
  "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 源码文件仍保留在磁盘上。

```bash
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` 类型通过此鉴权接口正常返回本地文件路径字符串。

```bash
curl -fsS -H "Authorization: Bearer $TOKEN" http://127.0.0.1:6387/v0/dashboards/github-repo/inputs
```

**响应示例 (`200 OK`)**：

```json
{
  "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`。

```bash
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": "..."}`：

```bash
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` 表示已接受请求，不等待异步渲染完成。

```bash
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`）。事件处理完成后会自动提交重新渲染。

```bash
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）：

```bash
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`。

```json
{
  "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"
  }
}
```

其他请求体：

修改默认打开方式：

```json
{ "open_mode": "flyout" }
```

禁用全部视觉入口：

```json
{ "placements": [] }
```

恢复默认入口（不改变打开方式与快捷键）：

```json
{ "placements": null }
```

恢复默认打开方式（不改变按来源例外或入口）：

```json
{ "open_mode": null }
```

清除全部按来源例外覆盖（所有来源恢复跟随默认打开方式）：

```json
{ "open_modes": null }
```

同时恢复默认入口、默认打开方式并清除所有来源例外：

```json
{ "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。
