> ## Documentation Index
> Fetch the complete documentation index at: https://docs.meoo.com/llms.txt
> Use this file to discover all available pages before exploring further.

# 沙箱API

沙箱 API 用于准备项目运行环境、获取项目预览链接，以及销毁项目当前关联的沙箱。

基础地址：`https://meoo.com/open/v1\`

## 功能概述

沙箱为项目提供代码运行和预览环境。创建项目后，可通过预览接口触发沙箱准备；不再使用时，通过沙箱销毁接口释放当前项目关联的沙箱。

创建项目不等于创建沙箱；预览未就绪也不等于沙箱创建失败。

## 调用准备

请求使用 `Authorization: Bearer <ACCESS_TOKEN>`。成员 Token 应属于目标项目所在团队，并代表有权操作该项目的成员。

| 操作            | 所需 Scope        |
| :------------ | :-------------- |
| 创建项目          | `project.write` |
| 获取预览链接、触发沙箱准备 | `agent.read`    |
| 销毁沙箱          | `agent.run`     |

沙箱销毁还要求调用成员为项目 Owner。权限列表中没有独立的 `sandbox.kill` 权限，使用的是 `agent.run`。

所有路径中的 `project_id` 均填写项目的公开 `url_id`，不是数据库主键，也不是平台沙箱 ID。

## 创建项目

完整项目接口见[用户与项目 API](https://alidocs.dingtalk.com/i/nodes/Qnp9zOoBVBDEydnQUeDjoale81DK0g6l)。以下为沙箱调用流程所需的最小示例。

`POST /open/v1/projects`

```text theme={null}
curl -X POST 'https://meoo.com/open/v1/projects' \
  -H 'Authorization: Bearer <ACCESS_TOKEN>' \
  -H 'Content-Type: application/json' \
  -d '{"name":"sandbox-demo","type":"web"}'
```

成功返回 HTTP 200，保存其中的 `url_id`，用于后续调用。

```text theme={null}
{
  "url_id": "<PROJECT_URL_ID>",
  "name": "sandbox-demo",
  "type": "web",
  "created_at": 1789012455000
}
```

创建受当前租户项目数量权益约束；权益不足返回 `403 quota_exceeded`，权益查询失败返回 `503 service_unavailable`。

## 触发沙箱准备并获取预览链接

`POST /open/v1/projects/{project_id}/agent/preview-links`

此接口用于获取预览链接，服务端会准备当前项目的沙箱与开发服务器。它不是独立的沙箱创建接口，也不保证每次调用都会创建新沙箱。

```text theme={null}
curl -X POST 'https://meoo.com/open/v1/projects/<PROJECT_URL_ID>/agent/preview-links' \
  -H 'Authorization: Bearer <ACCESS_TOKEN>' \
  -H 'Content-Type: application/json' \
  -d '{}'
```

请求体可省略或传 `{}`；不能指定沙箱 ID、目标 URL、端口或 TTL。

| HTTP 状态                                  | 含义               | 调用方处理                           |
| :--------------------------------------- | :--------------- | :------------------------------ |
| 200                                      | 预览就绪             | 使用响应中的 `url`                    |
| 202                                      | 沙箱恢复或开发服务器启动仍在进行 | 按 `Retry-After` 响应头等待后重试        |
| 409 `preview_not_ready`                  | 项目暂不可预览          | 检查项目内容与运行状态；不能据此判断沙箱一定未创建       |
| 429 `sandbox_concurrency_limit_exceeded` | 租户运行中沙箱数量达到上限    | 根据 detail 中计数与上限处理，释放不再使用的沙箱后重试 |
| 503 `service_unavailable`                | 依赖或上游暂不可用        | 稍后重试并保留 trace\_id               |

HTTP 200 响应结构：

```text theme={null}
{
  "object": "preview_link",
  "project_id": "<PROJECT_URL_ID>",
  "status": "ready",
  "url": "<PREVIEW_URL>",
  "expires_at": 1789012800000
}
```

预览 URL 为短时链接，应完整使用，不解析或长期保存其中的 Ticket；过期后重新调用本接口。`expires_at` 为 Unix 毫秒时间戳。

HTTP 202 响应结构（等待时长仅为示例）：

```text theme={null}
{
  "object": "preview_link",
  "project_id": "<PROJECT_URL_ID>",
  "status": "starting",
  "code": "preview_starting",
  "retry_after_ms": 1000
}
```

## 销毁项目当前沙箱

### 使用场景：主动控制沙箱使用时长与计量

为保证连续使用体验，项目沙箱通常不会在一次操作结束后立即销毁。平台会综合多种运行和使用情况判断回收时机，通常在 30 分钟～6 小时之间进行回收，具体时间以实际运行情况为准。

如果业务已经明确不再使用当前沙箱，无需等待平台自动回收，可以调用 kill 接口主动销毁，更精确地控制沙箱使用时长，减少等待回收期间的资源占用及相应计量。

典型场景包括：

* 一次性生成、构建或测试任务已结束，短期内不再继续操作项目。
* 批量任务处理完成后，统一释放不再使用的项目沙箱。
* 接入方提供“结束运行”或“释放沙箱”操作，让用户主动控制资源使用。

调用前应确认沙箱内已无需要继续执行的任务。销毁后，当前沙箱中的运行任务和预览服务会停止；再次使用项目时，可能需要重新准备运行环境。

主动销毁不会撤销已经产生的用量。接口返回销毁成功后，计量明细仍需经过后续结算和上报流程更新，不一定立即体现在账单或用量查询结果中。若返回 `503`，表示销毁结果尚未确认，应按下文说明重试。

`POST /open/v1/projects/{project_id}/sandbox/kill`

接口销毁项目当前关联的沙箱，不删除项目记录。请求不需要请求体。

```text theme={null}
curl -X POST 'https://meoo.com/open/v1/projects/<PROJECT_URL_ID>/sandbox/kill' \
  -H 'Authorization: Bearer <ACCESS_TOKEN>' \
  -H 'Accept: application/json'
```

成功响应示例：

```text theme={null}
{
  "project_id": "<PROJECT_URL_ID>",
  "sandbox_id": "<SANDBOX_ID>",
  "outcome": "killed",
  "confirmed_at": 1789016400271
}
```

| 字段            | 类型     | 说明                                          |
| :------------ | :----- | :------------------------------------------ |
| project\_id   | string | 项目公开 URL ID                                 |
| sandbox\_id   | string | 本次处理的平台沙箱 ID；项目无沙箱映射时为空字符串                  |
| outcome       | string | `killed`：已执行销毁；`not_found`：平台确认不存在或项目没有沙箱映射 |
| confirmed\_at | number | 本次销毁检查/操作的确认时间，Unix 毫秒；不要作为平台精确终止时间使用       |

`outcome: "not_found"` 在此接口中为 HTTP 200 的业务结果，与 HTTP 404「项目不存在或不可见」不同。没有沙箱映射时，不代表已扫描平台确认该项目不存在任何历史沙箱。

平台超时、限流、鉴权失败或状态不明确时返回 HTTP 503，表示销毁结果尚未确认，不能按销毁成功处理。稍后使用相同项目 ID 重试；接口面向当前映射，重试前应避免并发触发新的沙箱初始化。

接口限流为：每客户端 2000 次/60 秒，每客户端下同一用户、租户组合 2000 次/60 秒。

### 常见错误

| HTTP 状态   | 说明                        |
| :-------- | :------------------------ |
| 400       | 请求参数不合法                   |
| 401       | Bearer 凭证无效或已过期           |
| 403       | 缺少 `agent.run` 或未通过项目权限校验 |
| 404       | 项目不存在、不可见或租户不一致           |
| 429       | 调用频率超过限制                  |
| 500 / 503 | 服务暂不可用或销毁结果尚未确认           |

## 常见问题

### 返回 404「项目不存在」

检查项目 url\_id、环境、Token 所代表的成员以及租户是否一致。网页登录身份与 API Token 身份可能不同，能在网页打开项目不代表当前 Token 有权访问。

### 权限列表里没有沙箱 kill

销毁操作使用 `agent.run`，同时校验项目 Owner；具有 Scope 并不替代项目权限校验。

### 预览返回 409，沙箱是否创建成功？

不能仅凭 409 判断。空项目可能已经创建沙箱，但没有可预览页面。需要结合创建日志或平台状态确认；沙箱创建成功日志会包含 `result=success` 与 `sandboxId`。

### 如何定位错误？

保留 HTTP 状态、响应体中的 `code`、`detail`、`trace_id`，以及请求时间、项目 url\_id。不要在工单或截图中暴露完整 Token。
