
# Hub 公开 API {#hub-public-api}

Hub 公开 API 让自动化可以操作一个组织中的触发器、项目和 daemon。下文用 `PASEO_HUB_URL` 表示 Hub 的地址，例如 `https://hub.example.com`。

## API 参考 {#api-reference}

- [交互式 API 参考](https://hub.paseo.sh/api/reference)
- [OpenAPI 3.1 文档](https://hub.paseo.sh/api/openapi.json)

这些是托管版 Paseo Hub 的标准参考端点。自托管的 Hub 在它自己的地址上提供相同的 `/api/reference` 和 `/api/openapi.json` 路径。

## 身份验证 {#authentication}

运行 `paseo hub login [origin]` 获得交互式的 CLI 访问权限。在浏览器中批准后，Paseo 会在 `PASEO_HOME` 下为这个确切的地址保存一个持久、可撤销的组织凭据。未显式指定地址时，CLI 依次使用 `PASEO_HUB_URL`、当前保存的登录，然后是 `https://hub.paseo.sh`。

用于自动化时，请在 Hub 控制台的 **API keys** 下创建组织 API 密钥。两种凭据都是 bearer token：

```http
Authorization: Bearer paseo_pk_...
Content-Type: application/json
```

API 密钥的作用域是组织。拥有该密钥的组织决定了它能访问哪些项目和 daemon 注册令牌；这些请求中不需要添加组织 ID。通过该密钥无法访问其他组织的项目 slug。

每个密钥有一个或多个可选的权限范围：

| 权限范围                 | 操作                                    |
| ------------------------ | --------------------------------------- |
| `projects:read`          | 列出组织中的活动项目。                  |
| `configuration:validate` | 校验触发器或旧版配置，不改变 Hub 状态。 |
| `configuration:install`  | 安装触发器，或替换旧版项目的配置。      |
| `runs:dispatch`          | 为项目发起一个已配置的手动触发器。      |
| `daemons:enroll`         | 签发一个短期有效的 daemon 注册令牌。    |

API 密钥不授予控制台访问权限。它们不能管理连接、项目或组织成员。

CLI 凭据拥有当前 CLI 操作所需的权限范围，并且可以独立于 daemon 关系单独撤销。`paseo hub logout` 会删除当前的本地 CLI 凭据；它不会撤销或断开 daemon 身份。

API 失败时使用 RFC 9457 问题详情格式。缺失、无效或已撤销的凭据返回 `401`，类型为 `application/problem+json`：

```json
{
  "type": "https://paseo.sh/problems/unauthorized",
  "title": "Authentication required",
  "status": 401,
  "detail": "Provide an active Paseo organization credential in the Authorization: Bearer header.",
  "code": "unauthorized",
  "requestId": "5e967c44-fc22-4f6d-8fc5-1bbff33121af"
}
```

有效但缺少端点所需权限范围的密钥会以相同格式返回 `403`。

## 触发器校验和安装 {#trigger-validation-and-installation}

`paseo hub deploy --dry-run` 通过 `POST /api/v1/triggers/validate` 校验每个 `.paseo/triggers/*.yml` 文件。`paseo hub deploy` 先校验所有文件，再通过 `POST /api/v1/triggers/install` 逐个安装。

两个端点都接受一份自包含的文档：

```json
{
  "yaml": "name: manual-task\nenabled: true\non:\n  manual.run: {}\nrun:\n  target: { daemon: my-macbook, cwd: /workspace }\n  agent: { provider: codex, model: gpt-5, mode: full-access }\n  prompt: Complete the task and call hub.finish_execution.\n  max_runtime: 1h\n  idle_timeout: 5m\n"
}
```

请使用你的组织中可用的 daemon slug 和 Agent 运行时。两个操作都会请指定的 daemon 检查 Agent，因此它必须已连接并持有 `hub.execute`；它无法运行的 Agent 会作为字段问题报告，而不是在第一次运行时失败。见[保存时会对照 daemon 检查 Agent](/docs/hub/triggers#saving-checks-the-agent-against-the-daemon)。校验需要 `configuration:validate`，并返回 `200` 和 `{ "name": "manual-task", "valid": true }`。

安装需要 `configuration:install`。它按 YAML 中的 `name` 创建或更新组织的触发器，并返回 `201`：

```json
{
  "triggerId": "00000000-0000-4000-8000-000000000001",
  "name": "manual-task",
  "revisionId": "00000000-0000-4000-8000-000000000002",
  "version": 1,
  "active": true
}
```

无效的 YAML 或未知的组织资源会返回 `422` 及字段问题。每次安装都是独立的请求；后面的失败不会撤销前面已成功的安装。见[从 CLI 部署](/docs/hub/configuration#deploy-from-the-cli)。

## 项目列表 {#project-list}

`GET /api/v1/projects` 返回 bearer 凭据所属组织中的活动项目。`paseo hub projects` 以表格形式显示项目。使用 `--json` 时，它返回 `{ "origin": "...", "projects": [...] }`，因此即使结果为空，也会记录解析出的 Hub。

```json
{
  "projects": [
    {
      "id": "00000000-0000-4000-8000-000000000000",
      "slug": "my-project",
      "name": "My project"
    }
  ]
}
```

## 旧版配置校验 {#legacy-configuration-validation}

`POST /api/v1/configurations/validate` 接受与配置安装相同的 `projectSlug` 和完整的 `files` 文件包。它执行相同的编译和资源解析，但不会记录修订，也不会改变当前生效的配置。

成功时，Hub 返回 `200`：

```json
{
  "projectSlug": "my-project",
  "valid": true
}
```

`paseo hub deploy --project <slug> --dry-run` 会用与实际部署完全相同的、在本地解析出的请求体调用此端点。

## 旧版配置安装 {#legacy-configuration-install}

`configuration:install` 会校验提供的标准文件包，原样保存编写的文件，并激活一个新的修订。

```http
POST /api/v1/configurations/install
```

请求体：

```json
{
  "projectSlug": "my-project",
  "files": [
    {
      "path": ".paseo/hub.yml",
      "content": "environments:\n  production:\n    kind: daemon\n    daemon: build-server\n    cwd: /workspace\nagents:\n  codex:\n    provider: codex\n"
    },
    {
      "path": ".paseo/workflows/deploy.yml",
      "content": "name: deploy\non: manual.run\nmax_runtime: 2h\nfilters:\n  from_users: [automation]\nsteps:\n  - id: deploy\n    environment: production\n    max_runtime: 90m\n    idle_timeout: 10m\n    agent: codex\n    prompt:\n      - include: partials/safety.md\n"
    },
    {
      "path": ".paseo/workflows/partials/safety.md",
      "content": "Follow the safety checklist."
    }
  ]
}
```

`projectSlug` 选择目标项目；bearer 凭据确定组织。`files` 包含 `.paseo/hub.yml`、每个直接位于目录下的 workflow `.yml`，以及每个被引用的 workflow 片段。Hub 会拒绝缺失、多余、重复、不安全或非标准的路径。

限制：

- 文件包：最多 100 个文件。
- 文件路径：最多 512 个字符。
- 文件内容：最多 1,000,000 个字符。
- 提示词片段：每个最多 1,000,000 字节，总计最多 5,000,000 字节。

成功时，Hub 返回 `201`：

```json
{
  "projectSlug": "my-project",
  "versionId": "00000000-0000-4000-8000-000000000000",
  "version": 3,
  "active": true
}
```

常见的响应有：请求体缺失或格式错误时返回 `400`，密钥所属组织中的项目不活动或未知时返回 `404`，YAML 无效或配置无效时返回 `422`。校验的问题详情包含字段问题。安装失败不会替换当前生效的修订。

示例：

```bash
curl --fail-with-body -sS -X POST "$PASEO_HUB_URL/api/v1/configurations/install" \
  -H "Authorization: Bearer $PASEO_HUB_API_KEY" \
  -H "Content-Type: application/json" \
  --data @configuration-install.json
```

`paseo hub deploy -p <project>` 会用在本地发现的文件包调用这个旧版端点。在没有参数和环境变量凭据时，该命令使用与地址完全匹配的已保存登录。见[从 CLI 部署](/docs/hub/configuration#deploy-from-the-cli)。

## 发起手动运行 {#manual-run-dispatch}

`runs:dispatch` 为项目发起一个已配置的 `manual.run` 触发器。该触发器必须存在于当前生效的配置中，并且 `actor` 必须列在该触发器的 `filters.from_users` 白名单中。

```http
POST /api/v1/manual-runs
```

请求体：

```json
{
  "projectSlug": "my-project",
  "expectedVersionId": "00000000-0000-4000-8000-000000000000",
  "trigger": "deploy",
  "actor": "automation",
  "deliveryKey": "deploy-2026-08-04-001",
  "input": "repo=project investigate the failed sync"
}
```

- `expectedVersionId` 是可选的。提供时，如果该修订已不再生效，Hub 会拒绝这次发起。
- `input` 与 Provider 消息使用的字符串相同：开头的 `key=value` 标记会被解析为声明的输入，其余部分成为 `${{ paseo.prompt }}`。
- `deliveryKey` 对每次发起都应唯一且稳定。Hub 用它进行持久化去重，但不保证恰好发起一次，也不保证重放之前的响应。

成功时，Hub 返回 `200`：

```json
{
  "deliveryKey": "deploy-2026-08-04-001",
  "providerEventReceiptId": "00000000-0000-4000-8000-000000000000",
  "triggerRunId": "00000000-0000-4000-8000-000000000000",
  "configuredTriggerName": "deploy",
  "workflowStatus": "running"
}
```

常见的响应有：请求无效时返回 `400`，触发器不允许该 actor 时返回 `403`，项目、配置或触发器未知时返回 `404`，daemon 离线、预期的配置已不是当前配置或发起冲突时返回 `409`。它们都使用上面的问题详情格式。

示例：

```bash
curl --fail-with-body -sS -X POST "$PASEO_HUB_URL/api/v1/manual-runs" \
  -H "Authorization: Bearer $PASEO_HUB_API_KEY" \
  -H "Content-Type: application/json" \
  --data '{
    "projectSlug": "my-project",
    "trigger": "deploy",
    "actor": "automation",
    "deliveryKey": "deploy-2026-08-04-001",
    "input": "repo=project investigate the failed sync"
  }'
```

输入类型、默认值、可选值、被拒绝的输入以及手动调用示例，见 [Hub workflow](/docs/hub/workflows)。

## 注册 daemon {#daemon-enrollment}

`daemons:enroll` 为 daemon 签发一个短期有效的注册令牌。这个令牌由 API 密钥签发；它不是 daemon 的长期凭据，也不得当作长期凭据使用。

```http
POST /api/v1/daemons/enrollment-tokens
```

不需要请求体。成功时，Hub 返回 `201`：

```json
{
  "token": "short-lived-enrollment-token",
  "expiresAt": "2026-08-04T12:10:00.000Z"
}
```

令牌在 10 分钟后过期，并在 daemon 注册时被消耗。

`paseo hub connect [origin]` 会使用 `--api-key`、`PASEO_HUB_API_KEY` 或匹配的已保存登录执行此请求，然后把这个一次性令牌传给 daemon 的注册操作。daemon 会生成并保存它自己的关系凭据。

```bash
curl --fail-with-body -sS -X POST \
  "$PASEO_HUB_URL/api/v1/daemons/enrollment-tokens" \
  -H "Authorization: Bearer $PASEO_HUB_API_KEY"
```

直接使用 API 的调用方可以把返回的令牌传给 daemon 注册协议。Paseo CLI 有意不接受原始注册令牌；`connect` 负责完整的、经过身份验证的单一交换流程。

注册令牌不能重复使用。撤销 API 密钥会立即拒绝之后的 API 请求，并使该密钥签发的所有未消耗的注册令牌过期。撤销与签发之间的竞争会在新令牌被保存之前由 Hub 解决。

## 密钥、权限范围和审计信息 {#keys-scopes-and-audit-information}

完整的 API 密钥只在创建后立即显示一次。请把它保存到你的部署所用的密钥管理器中；Hub 不会再次显示它。控制台只保留密钥的前缀，并显示它所选的权限范围、创建时间、最后使用时间和状态。

密钥在一次有权限范围的 API 操作中成功通过身份验证后，Hub 会更新 `last used`。API 操作会在 Hub 的审计记录中保留密钥归属，因此配置修订、手动发起和 daemon 注册令牌都可以追溯到创建它们的组织密钥。
