mcpField
B
blaxel-sdk

blaxel-sdk

github.com/blaxel-ai云与 DevOps3622 次安装2 周前更新
代码仓库

关于 blaxel-sdk

Blaxel SDK 指南面向构建 AI Agent 和云端开发环境的开发者,介绍 TypeScript 与 Python SDK 及 `bl` 命令行。内容重点是创建沙箱、运行进程、生成预览 URL,并部署可复用模板和服务。

主要功能

  1. 01TypeScript 和 Python SDK
  2. 02创建并管理云端沙箱
  3. 03写入文件并运行进程
  4. 04生成公开或私有预览 URL
  5. 05部署沙箱模板、Agent 和任务

适用场景

  1. 01在隔离沙箱中运行开发服务器
  2. 02为应用创建实时预览链接
  3. 03复用预装依赖的沙箱环境
  4. 04部署 AI Agent、MCP 服务和批处理任务

MCP 相关

Blaxel 可将 MCP 服务部署为无服务器端点。指南也说明了通过 CLI 和 SDK 创建、部署及管理相关资源。

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/blaxel-ai/agent-skills --skill blaxel-sdk

安装后在 Claude Code 中输入/blaxel-sdk确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

metadata.json
assets/
blaxel-logo.svg
references/
sdk-python.md
sdk-typescript.md

详细介绍

Blaxel 技能参考

什么是 Blaxel

Blaxel(https://blaxel.ai)是一个云平台,为 AI Agent 提供专属计算环境。其旗舰产品是永久沙箱:可即时启动的微型虚拟机,可在 25ms 内从待机状态恢复,并在闲置几秒后缩减至零。

你主要可以使用 Blaxel 来:

  • 启动沙箱、安装依赖项、运行开发服务器,并公开实时预览 URL
  • 构建并部署沙箱模板(自定义 Docker 镜像),以复用环境
  • 将 AI Agent、MCP 服务和批处理作业部署为无服务器端点

SDK:TypeScript(@blaxel/core)和 Python(blaxel) CLI:bl(从 https://docs.blaxel.ai/cli-reference/introduction 安装) 文档:https://docs.blaxel.ai

身份验证

SDK 按以下优先顺序使用这些来源进行身份验证:

  1. 已登录的 Blaxel CLI
  2. .env 文件中的环境变量(BL_WORKSPACE、BL_API_KEY)
  3. 系统环境变量
  4. Blaxel 配置文件(~/.blaxel/config.yaml)

在本地登录(推荐用于开发):

bl login YOUR-WORKSPACE

也可以设置环境变量(用于远程/CI 环境):

export BL_WORKSPACE=your-workspace
export BL_API_KEY=your-api-key

在 Blaxel 上运行时,会自动进行身份验证。

沙箱工作流(主要用例)

这是最常见的工作流:创建沙箱,在其中运行命令,并获取预览 URL。

第 1 步:创建沙箱

使用 Blaxel Hub 中的公共镜像(https://github.com/blaxel-ai/sandbox/tree/main/hub):

  • blaxel/base-image:latest — 精简版 Linux
  • blaxel/node:latest — Node.js
  • blaxel/nextjs:latest — Next.js
  • blaxel/vite:latest — Vite
  • blaxel/expo:latest — Expo(React Native)
  • blaxel/py-app:latest — Python

也可以使用你自行部署的自定义模板镜像。

创建时声明所需的端口。创建后无法添加端口。端口 80、443 和 8080 为保留端口。

import { SandboxInstance } from "@blaxel/core";

const sandbox = await SandboxInstance.createIfNotExists({
  name: "my-sandbox",
  image: "blaxel/base-image:latest",
  memory: 4096,
  ports: [{ target: 3000, protocol: "HTTP" }],
});
from blaxel.core import SandboxInstance

sandbox = await SandboxInstance.create_if_not_exists({
  "name": "my-sandbox",
  "image": "blaxel/base-image:latest",
  "memory": 4096,
  "ports": [{"target": 3000, "protocol": "HTTP"}],
})

使用 createIfNotExists / create_if_not_exists 按名称复用现有沙箱,或创建新沙箱。

始终优先使用此方法,而不是先获取再创建的备用方案。已删除的沙箱会以 TERMINATED 状态保留几分钟(以便继续访问其日志),因此普通的 get 可能返回一个已失效的沙箱,其网关和预览 URL 会因找不到工作负载而失败。createIfNotExists 会为你检查状态,并在现有记录处于 FAILED、TERMINATED、TERMINATING 或 DELETING 状态时重新创建沙箱。从 @blaxel/core 0.2.88 / blaxel 0.2.55 起,该方法还会等待删除后短暂的 DELETING 窗口结束,因此删除后重新创建可以直接完成。在较旧的 SDK 版本中,该窗口可能会导致“Unable to create sandbox after 3 attempts”错误——请稍等片刻后重试,或升级。如果必须使用 get,请在复用实例前检查 sandbox.status。

第 2 步:写入文件并运行命令
// Write files
await sandbox.fs.write("/app/package.json", JSON.stringify({
  name: "my-app",
  scripts: { dev: "astro dev --host 0.0.0.0 --port 3000" },
  dependencies: { "astro": "latest" }
}));

// Or write multiple files at once
await sandbox.fs.writeTree([
  { path: "src/pages/index.astro", content: "<h1>Hello</h1>" },
  { path: "astro.config.mjs", content: "import { defineConfig } from 'astro/config';\nexport default defineConfig({});" },
], "/app");

// Execute a command and wait for it to finish
const install = await sandbox.process.exec({
  name: "install",
  command: "npm install",
  workingDir: "/app",
  waitForCompletion: true,
  timeout: 60, // seconds (default 600; 0 = no auto-kill with keepAlive)
});

// Start a long-running dev server (don't wait for completion)
const devServer = await sandbox.process.exec({
  name: "dev-server",
  command: "npm run dev",
  workingDir: "/app",
  waitForPorts: [3000],  // returns once port 3000 is open
});
await sandbox.fs.write("/app/package.json", '{"name":"my-app","scripts":{"dev":"astro dev --host 0.0.0.0 --port 3000"},"dependencies":{"astro":"latest"}}')

await sandbox.fs.write_tree([
  {"path": "src/pages/index.astro", "content": "<h1>Hello</h1>"},
  {"path": "astro.config.mjs", "content": "import { defineConfig } from 'astro/config';\nexport default defineConfig({});"},
], "/app")

install = await sandbox.process.exec({
  "name": "install",
  "command": "npm install",
  "working_dir": "/app",
  "wait_for_completion": True,
  "timeout": 60,  # seconds (default 600; 0 = no auto-kill with keep_alive)
})

dev_server = await sandbox.process.exec({
  "name": "dev-server",
  "command": "npm run dev",
  "working_dir": "/app",
  "wait_for_ports": [3000],
})

重要:开发服务器必须绑定到 0.0.0.0(而非 localhost),才能通过预览 URL 访问。使用 --host 0.0.0.0 或 HOST 环境变量。

第 3 步:创建预览 URL
const preview = await sandbox.previews.createIfNotExists({
  metadata: { name: "app-preview" },
  spec: { port: 3000, public: true },
});
const url = preview.spec?.url;
// url => https://xxxx.us-pdx-1.preview.bl.run
preview = await sandbox.previews.create_if_not_exists({
  "metadata": {"name": "app-preview"},
  "spec": {"port": 3000, "public": True},
})
url = preview.spec.url

如需私有预览,请设置 public: false 并创建令牌:

const preview = await sandbox.previews.createIfNotExists({
  metadata: { name: "private-preview" },
  spec: { port: 3000, public: false },
});
const token = await preview.tokens.create(new Date(Date.now() + 10 * 60 * 1000));
// Access: preview.spec?.url + "?bl_preview_token=" + token.value
第 4 步:管理沙箱
// Reconnect to an existing sandbox. Careful: get can return a recently
// deleted sandbox still in TERMINATED state — check status before reuse,
// or use createIfNotExists which handles this for you.
const sandbox = await SandboxInstance.get("my-sandbox");

// List files
const { subdirectories, files } = await sandbox.fs.ls("/app");

// Read a file
const content = await sandbox.fs.read("/app/src/pages/index.astro");

// Get process info / logs
const proc = await sandbox.process.get("dev-server");
const logs = proc.logs; // available if waitForCompletion was true

// Kill a process
await sandbox.process.kill("dev-server");

// Delete the sandbox (all data is erased)
await sandbox.delete();
sandbox = await SandboxInstance.get("my-sandbox")

result = await sandbox.fs.ls("/app")
content = await sandbox.fs.read("/app/src/pages/index.astro")

proc = await sandbox.process.get("dev-server")
# proc.logs available if wait_for_completion was True
await sandbox.process.kill("dev-server")
await sandbox.delete()

沙箱模板(自定义镜像)

如果需要可复用的环境(例如,预装了所有依赖项的 Astro 项目),请创建模板:

bl new sandbox my-astro-template
cd my-astro-template

这会创建:blaxel.toml、Dockerfile、entrypoint.sh、Makefile。

自定义 Dockerfile。务必包含 sandbox-api 二进制文件:

FROM node:22-alpine
WORKDIR /app
COPY --from=ghcr.io/blaxel-ai/sandbox:latest /sandbox-api /usr/local/bin/sandbox-api
RUN npm install -g astro
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]

entrypoint.sh 必须启动 sandbox-api:

#!/bin/sh
/usr/local/bin/sandbox-api &
while ! nc -z 127.0.0.1 8080; do sleep 0.1; done
echo "Sandbox API ready"
# Optionally start a process via the sandbox API:
# curl http://127.0.0.1:8080/process -X POST -d '{"workingDir":"/app","command":"npm run dev","waitForCompletion":false}' -H "Content-Type: application/json"
wait

部署模板:

bl deploy

然后获取 IMAGE_ID,并用它创建沙箱:

bl get sandboxes my-astro-template -ojson | jq -r '.[0].spec.runtime.image'
const sandbox = await SandboxInstance.createIfNotExists({
  name: "project-sandbox",
  image: "IMAGE_ID",
  memory: 4096,
  ports: [{ target: 3000, protocol: "HTTP" }],
});

教程和示例

沙箱

Astro:https://docs.blaxel.ai/Tutorials/Astro Expo:https://docs.blaxel.ai/Tutorials/Expo Next.js:https://docs.blaxel.ai/Tutorials/Nextjs

Agent

概览:https://docs.blaxel.ai/Tutorials/Agents-Overview

核心 CLI 命令

对于可能提示输入(如确认)的 CLI 命令,在非交互式/无 TTY 环境(例如脚本、CI、Agent)中运行时,请添加 -y 以自动确认。

命令用途
bl login验证工作区身份
bl new sandbox|agent|job|mcp NAME基于模板初始化新资源
bl deploy构建资源并部署到 Blaxel
bl deploy -d DIR从指定目录部署
bl serve在本地运行资源以进行测试
bl serve --hotreload在本地运行并启用热重载
bl get sandboxes|agents|jobs|functions列出资源
bl get sandbox NAME --watch监视沙盒部署状态
bl delete sandbox|agent|job|function NAME删除资源
bl connect sandbox NAME在沙盒中打开交互式终端
bl chat AGENT-NAME与已部署的 Agent 进行交互式聊天
bl run job NAME --data JSON执行已部署的批处理作业

blaxel.toml 结构

name = "my-resource"
type = "sandbox"  # sandbox, agent, function, job, volume-template

[env]
NODE_ENV = "development"  # NOT for secrets — use Variables-and-secrets

[runtime]
memory = 4096       # MB
generation = "mk3"
# timeout = 900     # seconds (agents max 900, jobs max 86400)

# Ports (sandbox only)
[[runtime.ports]]
name = "dev-server"
target = 3000
protocol = "tcp"

重要注意事项

  • 必须在创建沙盒时声明端口,之后无法添加
  • 端口 80、443、8080 已被 Blaxel 保留
  • 开发服务器必须绑定到 0.0.0.0,而非 localhost,预览 URL 才能正常工作
  • 沙盒约 50% 的内存会预留给内存文件系统(tmpfs)。如需额外存储,请使用卷
  • 沙盒在闲置约 5 秒后会自动缩容至零。状态会在待机期间保留,并在 <25ms 内恢复
  • 已删除的沙盒会在 TERMINATED 状态下继续显示几分钟(以便访问日志)。普通的 get 可能会返回它;重新使用前,请使用 createIfNotExists 或检查 sandbox.status
  • 进程 timeout 的单位为秒(默认值为 600)。它限制 waitForCompletion:超时后,调用会抛出超时错误(HTTP 422),但进程仍会继续运行——请使用 process.get / process.wait 重新连接,而不要假设进程已失败。使用 keepAlive 时,进程会在超时后自动终止(0 = 不自动终止)。普通的长时间运行进程(例如使用 waitForPorts 启动的开发服务器)不会因此自动终止
  • waitForCompletion 会在进程运行期间保持 HTTP 请求打开(通过沙盒 MCP 服务时最长约为 58 秒)。对于长时间运行的进程,请在启动时不要使用它,而是通过 process.wait(name, { maxWait }) 等待
  • process.exec 还接受 env(每个进程的环境变量)、restartOnFailure 和 maxRestarts
  • 默认情况下,每一行进程 stdout/stderr 都会作为遥测数据导出——大量日志会造成实际的 CPU 争用。请参阅下文的“禁用进程日志导出”
  • 切勿将机密放入 [env]——请使用 Console 中的 Variables-and-secrets 页面

沙盒最佳实践

完整参考:https://docs.blaxel.ai/Sandboxes/best-practices

批量上传文件:zip + writeBinary + unzip

切勿对多个文件逐个调用 fs.write 来写入——每次调用都需要一次网络往返(约 100ms),因此几百个文件就需要数分钟。请将它们打包成 zip,使用一次 writeBinary 调用上传,再在沙盒内解压:只需 2 次调用,而非 N 次。

import AdmZip from "adm-zip";

const zip = new AdmZip();
for (const f of files) zip.addFile(f.path, Buffer.from(f.content));

await sandbox.fs.writeBinary("/tmp/project.zip", zip.toBuffer());
await sandbox.process.exec({
  name: "unzip",
  command: "unzip -o /tmp/project.zip -d /app/project && rm /tmp/project.zip",
  waitForCompletion: true,
});
import io, zipfile

buf = io.BytesIO()
with zipfile.ZipFile(buf, "w", zipfile.ZIP_DEFLATED) as zf:
    for f in files:
        zf.writestr(f["path"], f["content"])

await sandbox.fs.write_binary("/tmp/project.zip", buf.getvalue())
await sandbox.process.exec({
    "name": "unzip",
    "command": "unzip -o /tmp/project.zip -d /app/project && rm /tmp/project.zip",
    "wait_for_completion": True,
})

少量文件可使用 fs.writeTree(一次批量调用)。如果沙盒镜像中没有 unzip,可改用 python3 -m zipfile -e /tmp/project.zip /app/project 解压,或在模板镜像中安装 unzip。

在沙盒重新创建后保留项目状态

不要在每次冷启动时都重新克隆代码仓库并运行 npm install。删除沙盒时(TTL 到期或显式删除),沙盒文件系统会被清除,但你有以下持久化选项:

  • 卷:在创建沙盒时附加的持久性块存储(volumes: [{ name, mountPath }])。将项目工作区和依赖缓存放在卷上;重新创建时会重新挂载,无需重新拉取。完整示例请参阅 ./references。
  • Agent Drive:可挂载到运行中沙盒的共享文件系统(私有预览版)。适用于在多个沙盒之间共享状态。
  • 模板镜像:将大型依赖打包到自定义沙盒镜像中,这样冷启动时就能直接使用预装环境。

另外请记住:空闲沙盒会进入待机状态(免费,恢复时间 <25ms)——与其在每个会话结束时删除并重建,不如让沙盒在设置 TTL 的情况下保持空闲。

禁用进程日志导出

默认情况下,沙盒进程的每一行 stdout/stderr 都会作为结构化遥测数据导出。输出频繁的进程(例如详细日志模式的开发服务器、跟踪级别的轮询器)每分钟可能产生数千行日志,而这种导出负载已知会导致沙盒内 CPU 争用。

有两种关闭方式:

  • 针对单个沙盒:在沙盒上设置环境变量 SANDBOX_DISABLE_PROCESS_LOGGING=true(例如,在模板 Dockerfile 中设置 ENV SANDBOX_DISABLE_PROCESS_LOGGING=true,或在创建沙盒时设置其环境变量)。
  • 针对整个工作区:管理员可以在 Console > Workspace settings 中为所有沙盒禁用进程日志记录。

你将无法在 Blaxel Console 中查看进程日志;但仍可通过 SDK/API(process.get、日志流)读取日志。

审查集成时应注意的常见反模式
反模式症状修复方法
使用 get-then-create,而不是 createIfNotExists删除后快速重新创建(沙盒处于 TERMINATED 状态)时,网关/预览 URL 返回 workload-not-found使用 createIfNotExists,或在 get 后检查 sandbox.status
每次冷启动都重新拉取代码仓库并重新安装依赖每次会话启动都要花费数分钟使用卷或 Agent Drive 存放工作区;使用模板镜像存放依赖
对多个文件逐一调用 fs.write恢复需要数分钟(N 次网络往返)使用 zip + writeBinary + unzip(2 次调用);文件数量较少时使用 writeTree
保持日志导出开启,同时输出大量 stdout编译/大量输出期间 CPU 飙升设置 SANDBOX_DISABLE_PROCESS_LOGGING=true 或使用工作区开关;同时降低应用日志详细程度
沙盒空闲时将其删除每次重新访问都要冷启动重建让待机状态处理空闲情况;使用 ttl 清理

Agent Drive(共享文件系统)

Agent Drive 是由 SeaweedFS 提供支持的分布式文件系统,可随时挂载到多个沙盒或 Agent,包括已在运行的沙盒。与仅能在创建沙盒时附加的卷(块存储)不同,Drive 支持多个沙盒并发读写访问,并且可以动态挂载或卸载。

此功能目前处于私有预览阶段。预览期间,Agent Drive 仅在 us-was-1 区域可用。Drive 和沙盒都必须位于此区域。

使用场景:

  • 在沙盒之间传递数据,无需中间服务
  • 存储其他 Agent 使用的工具输出和上下文历史
  • 在多个 Agent 之间共享数据集
  • 创建共享的依赖包缓存文件系统
创建 Drive
import { DriveInstance } from "@blaxel/core";

const drive = await DriveInstance.createIfNotExists({
  name: "my-drive",
  region: "us-was-1",
  displayName: "My Project Drive",     // optional; defaults to name
  labels: { env: "dev", project: "x" }, // optional
});
from blaxel.core.drive import DriveInstance

drive = await DriveInstance.create_if_not_exists(
    {
        "name": "my-drive",
        "region": "us-was-1",
        "display_name": "My Project Drive",
        "labels": {"env": "dev", "project": "x"},
    }
)
将 Drive 挂载到沙盒
import { SandboxInstance } from "@blaxel/core";

const sandbox = await SandboxInstance.get("my-sandbox");

await sandbox.drives.mount({
  driveName: "my-drive",
  mountPath: "/mnt/data",
  drivePath: "/",   // optional; defaults to root of the drive
});
from blaxel.core import SandboxInstance

sandbox = await SandboxInstance.get("my-sandbox")

await sandbox.drives.mount(
    drive_name="my-drive",
    mount_path="/mnt/data",
    drive_path="/",
)

挂载后,写入沙盒内挂载路径的任何文件都会存储在 Drive 上,即使删除沙盒,这些文件也会保留。

挂载子目录
await sandbox.drives.mount({
  driveName: "my-drive",
  mountPath: "/app/project",
  drivePath: "/projects/alpha",
});
await sandbox.drives.mount(
    drive_name="my-drive",
    mount_path="/app/project",
    drive_path="/projects/alpha",
)
列出、卸载和删除 Drive
// List mounted drives on a sandbox
const mounts = await sandbox.drives.list();

// List all drives
const drives = await DriveInstance.list();

// Unmount
await sandbox.drives.unmount("/mnt/data");

// Delete a drive
await DriveInstance.delete("my-drive");
// or instance-level:
const drive = await DriveInstance.get("my-drive");
await drive.delete();
mounts = await sandbox.drives.list()

drives = await DriveInstance.list()

await sandbox.drives.unmount("/mnt/data")

await DriveInstance.delete("my-drive")
# or instance-level:
drive = await DriveInstance.get("my-drive")
await drive.delete()

CLI:bl get drives

Agent Drive 完整示例
import { SandboxInstance, DriveInstance } from "@blaxel/core";

// 1. Create a drive
const drive = await DriveInstance.createIfNotExists({
  name: "agent-storage",
  region: "us-was-1",
});

// 2. Create a sandbox (use image ID from custom template)
const sandbox = await SandboxInstance.createIfNotExists({
  name: "my-agent-sandbox",
  image: "my-sandbox-image-id",
  memory: 2048,
  region: "us-was-1",
});

// 3. Mount the drive
await sandbox.drives.mount({
  driveName: "agent-storage",
  mountPath: "/mnt/storage",
  drivePath: "/",
});

// 4. Write a file to the mounted drive
await sandbox.fs.write("/mnt/storage/hello.txt", "Hello from the drive!");

// 5. Read it back
const content = await sandbox.fs.read("/mnt/storage/hello.txt");
console.log(content); // "Hello from the drive!"

// 6. List mounted drives
const mounts = await sandbox.drives.list();
console.log(mounts);
import asyncio
from blaxel.core.drive import DriveInstance
from blaxel.core import SandboxInstance

async def main():
    drive = await DriveInstance.create_if_not_exists(
        {"name": "agent-storage", "region": "us-was-1"}
    )

    sandbox = await SandboxInstance.create_if_not_exists(
        {
            "name": "my-agent-sandbox",
            "image": "my-sandbox-image-id",
            "memory": 2048,
            "region": "us-was-1",
        }
    )

    await sandbox.drives.mount(
        drive_name="agent-storage",
        mount_path="/mnt/storage",
        drive_path="/",
    )

    await sandbox.fs.write("/mnt/storage/hello.txt", "Hello from the drive!")

    content = await sandbox.fs.read("/mnt/storage/hello.txt")
    print(content)

    mounts = await sandbox.drives.list()
    print(mounts)

asyncio.run(main())

文档:https://docs.blaxel.ai/Agent-drive/Overview

其他 Blaxel 资源

Agent 托管

将 AI Agent 部署为可无服务器自动扩缩的 HTTP 端点。与框架无关(LangChain、CrewAI、Claude SDK 等)。

bl new agent
# develop in src/agent.ts or src/agent.py
bl serve            # test locally
bl deploy           # deploy
bl chat AGENT-NAME  # query

同步端点处理请求的最长时间为 100 秒,异步端点则最长为 10 分钟。 文档:https://docs.blaxel.ai/Agents/Overview

MCP 服务托管

部署遵循 MCP 协议的自定义工具服务。

bl new mcp
# implement in src/server.ts or src/server.py
bl serve --hotreload  # test locally
bl deploy

Agent 通过 SDK 连接到已部署的 MCP 服务:

const tools = await blTools(["functions/my-mcp-server"]);

每个沙盒还会通过 https://<SANDBOX_URL>/mcp 暴露自己的内置 MCP 服务,其中包含用于进程管理、文件系统和代码生成的工具。 文档:https://docs.blaxel.ai/Functions/Overview

批处理任务

用于并行后台任务(耗时数分钟到数小时)的可扩展计算。

bl new job
# implement in src/index.ts or src/index.py
bl deploy
bl run job NAME --data '{"tasks": [...]}'

每项任务最长为 24 小时。在 blaxel.toml 中设置 maxConcurrentTasks。 文档:https://docs.blaxel.ai/Jobs/Overview

资源

请阅读各 SDK 文件,了解详细说明和代码示例:

  • ./references/sdk-python.md
  • ./references/sdk-typescript.md

每个 SDK README 都包含:

  • SDK 概览
  • 要求
  • 使用沙盒、卷、Agent、批处理任务和 MCP 的代码示例
  • 其他实用信息

如需更多文档,请参阅:https://docs.blaxel.ai/llms.txt

评论

支持 Markdown