--- outline: deep --- # 在沙箱中运行程序 本文档介绍如何通过平台 API 在 Mythra Agent Studio 的沙箱中执行文件或命令,并获取其输出与退出状态。 ## 概述 在 Mythra Agent Studio 中,你可以通过 API 在指定沙箱的隔离容器内运行一个可执行文件或命令,例如: - 执行脚本文件:`python3 /data/main.py`、`node /data/index.js`、`/data/run.sh` - 执行命令:`ls`、`echo hello`、`pytest` - 运行构建、测试等工具链 平台提供了两个能力互补的端点: - `/execute`:一次性执行,等待命令结束后一次性返回完整输出。 - `/execute/sse`:通过 Server-Sent Events(SSE)逐行流式返回标准输出与标准错误,适合运行时间较长或需要实时查看输出的场景。 ## 先决条件 在调用 API 之前,你需要准备好以下信息: - `ProjectID`:项目 ID - `SandboxID`:沙箱 ID - `WDS_API_KEY`:有效的 WDS API Key,用于身份验证 ## 身份认证 两个端点都要求进行 WDS Token 认证,以下两种方式任选其一: - 通过请求头:`Authorization: Bearer ` - 通过 Cookie:`wds_api_key=` 如果认证失败,平台会返回 `401 Unauthorized`。 ## 端点概览 | 方法 | 端点 | 说明 | |------|------|------| | `POST` | `/WebIDE/api/v1/projects/{ProjectID}/sandboxes/{SandboxID}/execute` | 一次性执行,返回完整结果 | | `POST` | `/WebIDE/api/v1/projects/{ProjectID}/sandboxes/{SandboxID}/execute/sse` | 流式执行,逐行返回输出 | 两个端点的请求体格式完全相同。 ## 请求体 请求体为 JSON 对象: ```json { "executable": "node", "args": ["/data/hello.js", "--name", "Mythra"], "workDir": "/data", "env": { "NODE_ENV": "production" }, "stdin": "", "timeout": 30 } ``` ### 字段说明 | 字段 | 类型 | 必填 | 说明 | |------|------|------|------| | `executable` | string | 是 | 要运行的可执行文件或命令,例如 `node`、`python3`、`bash`、`./main` | | `args` | string[] | 否 | 传给可执行文件的参数列表 | | `workDir` | string | 否 | 容器内的工作目录,默认为 `/data` | | `env` | object | 否 | 以 key-value 形式注入到进程的环境变量 | | `stdin` | string | 否 | 写入进程标准输入的文本 | | `timeout` | int | 否 | 超时时间(秒),默认值与最大值均为 `60` | ## 执行环境 - 沙箱目录会被挂载到容器的 `/data`,默认工作目录也是 `/data`。 - 因此,沙箱根目录下的文件在容器内对应 `/data/文件名`,也可以直接用相对路径引用(例如 `python3 main.py`)。 - 平台实际执行的是 `[executable, ...args]` 组合而成的容器启动命令。 - 每次执行都会创建一个临时容器(名称形如 `mythra-exec-{userId}-{projectId}-{sandboxId}-{nonce}`)。 - 如果超过 `timeout` 指定的时间,平台会停止容器并返回 `timedOut: true`。 ## 一次性执行:`/execute` ### 使用 curl ```bash #!/bin/bash WDS_BASE_URL="https://agent.wiseverds.com" WDS_API_KEY="WDS_**************" ProjectID=2231 SandboxID=j4urFhRG curl -i \ -X POST \ -H "Authorization: Bearer $WDS_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "executable": "node", "args": ["-e", "console.log(\"hello from sandbox\")"], "workDir": "/data", "timeout": 30 }' \ "$WDS_BASE_URL/WebIDE/api/v1/projects/$ProjectID/sandboxes/$SandboxID/execute" ``` ### 响应示例 ```json { "result": { "exitCode": 0, "stdout": "hello from sandbox\n", "stderr": "", "timedOut": false, "container": "mythra-exec-12345678-projabcd-sandbox1-XyZ12345" } } ``` ### 响应字段说明 | 字段 | 类型 | 说明 | |------|------|------| | `exitCode` | int | 进程退出码,`0` 通常表示成功 | | `stdout` | string | 标准输出(超过 1MB 会被截断并追加 `[Output truncated]`) | | `stderr` | string | 标准错误(超过 1MB 会被截断并追加 `[Output truncated]`) | | `timedOut` | bool | 是否超时 | | `container` | string | 执行本次命令的容器名称 | ## 流式执行:`/execute/sse` 当需要实时查看输出、或命令运行时间较长时,推荐使用 `/execute/sse`。响应以 `text/event-stream` 流式返回。 ### 使用 curl ```bash #!/bin/bash WDS_BASE_URL="https://agent.wiseverds.com" WDS_API_KEY="WDS_**************" ProjectID=2231 SandboxID=j4urFhRG curl -N -i \ -X POST \ -H "Authorization: Bearer $WDS_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "executable": "python3", "args": ["/data/main.py"], "workDir": "/data", "timeout": 60 }' \ "$WDS_BASE_URL/WebIDE/api/v1/projects/$ProjectID/sandboxes/$SandboxID/execute/sse" ``` ### SSE 输出格式 每个事件都使用 `message` 作为事件名,`data` 为 JSON 对象: ```json { "event": "<事件类型>", "data": "<数据>" } ``` 一条常见的流示例如下: ``` event: message data: {"event":"stdout","data":"Hello from Python!"} event: message data: {"event":"stderr","data":""} event: message data: {"event":"exit","data":"{\"exitCode\":0,\"timedOut\":false,\"container\":\"mythra-exec-...\"}"} ``` ### 事件类型 | 事件类型 | 说明 | |----------|------| | `stdout` | 一条标准输出,`data` 为文本行 | | `stderr` | 一条标准错误,`data` 为文本行 | | `ping` | 心跳保活,`data` 为时间戳 | | `exit` | 进程结束,`data` 为 JSON 字符串,包含 `exitCode`、`timedOut`、`container` | | `error` | 执行失败,`data` 为错误信息 | ### 使用 JavaScript (fetch) 读取 SSE ```javascript const baseUrl = 'https://agent.wiseverds.com'; const apiKey = 'WDS_**************'; const projectId = 2231; const sandboxId = 'j4urFhRG'; const url = `${baseUrl}/WebIDE/api/v1/projects/${projectId}/sandboxes/${sandboxId}/execute/sse`; const response = await fetch(url, { method: 'POST', headers: { 'Authorization': `Bearer ${apiKey}`, 'Content-Type': 'application/json', 'Accept': 'text/event-stream', }, body: JSON.stringify({ executable: 'python3', args: ['/data/main.py'], workDir: '/data', timeout: 60, }), }); const reader = response.body.getReader(); const decoder = new TextDecoder(); while (true) { const { value, done } = await reader.read(); if (done) break; process.stdout.write(decoder.decode(value, { stream: true })); } ``` ## 完整示例 假设你的沙箱根目录下有一个 `main.py` 文件: ```python import sys print("hello from sandbox") print("args:", sys.argv[1:], file=sys.stderr) ``` 你可以通过以下请求执行它: ```bash curl -i \ -X POST \ -H "Authorization: Bearer $WDS_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "executable": "python3", "args": ["main.py", "--foo", "bar"], "workDir": "/data" }' \ "$WDS_BASE_URL/WebIDE/api/v1/projects/$ProjectID/sandboxes/$SandboxID/execute" ``` 如果文件位于沙箱子目录中,使用对应的相对路径即可,例如 `"args": ["scripts/run.py"]`(相对于 `/data`),或使用绝对路径 `"/data/scripts/run.py"`。 ## 执行 Shell 命令 平台是直接以 `[executable, ...args]` 的形式启动容器进程的,**不会经过 Shell 解析**。因此 **不能** 把 `executable` 直接写成 Shell 内置命令或复合命令,例如: ```text { "executable": "cd" } ❌ { "executable": "ls -la | grep foo > out.txt" } ❌ ``` 如果需要执行 Shell 命令,请显式调用 Shell,并把完整命令放到 `-c` 后面的参数中: ```json { "executable": "/bin/sh", "args": ["-c", "ls -la | grep foo > out.txt"] } ``` 也可以使用 `/bin/bash`,用于需要 Bash 特有特性的场景(例如 `[[ ]]`、数组、`$RANDOM` 等): ```json { "executable": "/bin/bash", "args": ["-c", "for f in *.txt; do echo \"$f\"; done"] } ``` ### Shell命令是否需要转义 分两层来看: 1. **JSON 层**:整个请求体必须是合法 JSON,命令字符串里出现的双引号、反斜杠、换行等需要按 JSON 规则转义(例如 `\"`、`\\`、`\n`)。这是唯一一层“额外”转义。 2. **Shell 层**:命令字符串本身交给 `/bin/sh -c` 解释,其中 `$`、`|`、`>`、`<`、`&&`、引号等符号会按照 Shell 的正常语义生效。也就是说,命令字符串本身就是“最终要交给 Shell 的脚本内容”,不需要再做第二层转义。 例如,以下两种写法是等价的: 写法一(JSON 中把命令里的双引号写成 `\"`): ```json { "executable": "/bin/sh", "args": ["-c", "echo \"hello\""] } ``` 写法二(命令里改用单引号,避免转义双引号): ```json { "executable": "/bin/sh", "args": ["-c", "echo 'hello'"] } ``` 两者最终交给 Shell 的都是 `echo "hello"`(写法二等价为 `echo 'hello'`),都能输出 `hello`。 ### 注意事项 - **优先使用 `/bin/sh`**:`/bin/sh`(POSIX)在绝大多数镜像中都可用;只有确实用到 Bash 独有语法时才使用 `/bin/bash`,因为部分精简镜像可能没有安装 Bash。 - **不要省略 `-c`**:Shell 的 `-c` 选项表示“把下一个参数当作命令字符串执行”。如果写成 `"args": ["ls -la"]`,Shell 会把它当作脚本文件名去执行,而不是命令。 - **多条命令请使用 `;`、`&&` 或 `||`**:例如 `"cd /data && ls -la && python3 main.py"`,控制执行顺序和错误处理。 - **建议在脚本开头使用 `set -e`**:如果希望任意一条命令失败即停止并返回非零退出码,可以写成 `"set -e; command1; command2"`。默认情况下 `/bin/sh -c` 会继续执行后续命令并返回最后一条命令的退出码。 - **环境变量可通过 `env` 字段传入**:也可以直接在 Shell 命令中使用 `VAR=value command` 的临时赋值写法。 - **重定向与管道是允许的,但写入位置要确认**:容器工作目录默认为 `/data`,重定向生成的 `out.txt` 会自动落在 `/data` 下,即对应你的沙箱根目录,之后可在沙箱中查看或同步。 - **通配符由 Shell 展开**:`*.txt`、`$HOME`、`$(...)` 等只有在走 `/bin/sh -c` 时才会被展开;直接传 `executable` + `args` 不会展开。 - **非交互式 Shell 不会加载启动文件**:`/bin/sh -c` 通常不会读取 `~/.bashrc`、`~/.profile` 等启动文件,若有依赖,请在命令中显式设置。 ## 常见问题 ### 返回 401 Unauthorized 请检查 `Authorization` 头中的 API Key 是否有效,或 Cookie 中是否正确携带了 `wds_api_key`。 ### 返回 400 Bad Request 请检查请求体是否为合法 JSON,以及 `executable` 字段是否已填写。`executable` 是必填字段。 ### 返回 404 Not Found 指定的沙箱不存在。请确认 `ProjectID` 和 `SandboxID` 是否正确。 ### 返回 500 Internal Server Error 可能原因包括:Docker 配置加载失败、命令执行失败等。响应中的 `error` 字段会包含更多信息。 ### 输出被截断 `/execute` 端点对 `stdout` 和 `stderr` 分别设置了 1MB 的返回上限,超过部分会被截断并追加 `[Output truncated]`。如果需要完整输出,建议改用 `/execute/sse` 流式读取。 ### 命令运行时间超过 600 秒 两个API端点的超时时间最大为 `600` 秒。超时后平台会停止容器并返回 `timedOut: true`。 ## 相关文档 - [沙箱](./sandbox.md) - 了解沙箱的基本概念与工作流程 - [通过 TCP 端口访问容器内服务](./sandbox-http-service-via-tcp-socket.md) - 在容器内运行 HTTP 服务并通过 TCP 访问 - [通过 UNIX Domain Socket 在沙箱中运行 HTTP 服务](./sandbox-http-service-via-unix-socket.md) - 通过 Unix Socket 运行服务