zeevx/php-brimble-sandbox
Composer 安装命令:
composer require zeevx/php-brimble-sandbox
包简介
Framework-agnostic PHP SDK for the Brimble Sandbox API.
README 文档
README
A framework-agnostic PHP client for the Brimble Sandbox API: ephemeral compute environments with exec, code execution, files, snapshots, and persistent volumes.
- PHP 8.2+ · Guzzle transport · typed DTOs and enums · SSE streaming exec
- Typed exception mapping, retry with backoff, and the
{ message, data }envelope handled for you
Using Laravel? See
zeevx/laravel-brimble-sandboxfor config, a facade, and the container binding.
Install
composer require zeevx/php-brimble-sandbox
Quick start
use Zeevx\BrimbleSandbox\Sandbox; use Zeevx\BrimbleSandbox\Requests\ExecInput; use Zeevx\BrimbleSandbox\Requests\CreateSandbox; $client = new Sandbox(apiKey: '...'); // or set BRIMBLE_SANDBOX_KEY $sb = $client->sandboxes()->create(new CreateSandbox(template: 'node-22')); echo $sb->exec(new ExecInput('node -v'))->stdout; // "v22.x.x" $sb->putFile('tmp/app.js', "console.log('hi')"); echo $sb->exec(new ExecInput('node /tmp/app.js'))->stdout; $sb->destroy();
The API key is resolved from the constructor argument first, then the
BRIMBLE_SANDBOX_KEY environment variable.
Running commands and code
use Zeevx\BrimbleSandbox\Requests\CodeInput; use Zeevx\BrimbleSandbox\Enums\CodeLanguage; // Shell command (pipes, &&, redirects all work) $result = $sb->exec(new ExecInput( cmd: 'npm install && npm test', timeoutSeconds: 120, cwd: '/app', env: ['NODE_ENV' => 'test'], )); $result->stdout; // string $result->exitCode; // int $result->succeeded(); // bool // Code snippet, no shell escaping needed $sb->code(new CodeInput(CodeLanguage::Python, "print('hello')"));
Streaming output
exec/code can stream stdout/stderr as Server-Sent Events. Iterate the
frames as they arrive, then read the final result:
$stream = $sb->execStream(new ExecInput('npm install')); foreach ($stream as $frame) { if ($frame->isStdout()) { echo $frame->data; } elseif ($frame->isStderr()) { fwrite(STDERR, $frame->data); } } $result = $stream->result(); // ExecResult with the captured output + exit code
Files
Paths are relative to the sandbox root (tmp/notes.txt → /tmp/notes.txt).
The parent directory must already exist.
$sb->putFile('tmp/notes.txt', 'hello'); // upload raw bytes $contents = $sb->getFile('tmp/notes.txt'); // download as string $stream = $sb->downloadStream('big.bin'); // PSR-7 stream for large files
Lifecycle, egress, and stats
use Zeevx\BrimbleSandbox\Enums\SandboxStatus; use Zeevx\BrimbleSandbox\Requests\UpdateEgress; use Zeevx\BrimbleSandbox\Enums\SandboxEgressMode; $sb->pause(); // -> ack message $sb->resume(); $sb->wait(SandboxStatus::Ready); // client-side poll after a resume $sb->updateEgress(new UpdateEgress(SandboxEgressMode::Restricted, ['api.openai.com'])); $stats = $sb->stats(hoursAgo: 6); // CPU / memory / network
Snapshots and volumes
use Zeevx\BrimbleSandbox\Requests\CreateVolume; use Zeevx\BrimbleSandbox\Enums\VolumeType; // Snapshots $snap = $sb->createSnapshot('nightly'); $sb->snapshots(); // this sandbox, paginated $client->snapshots()->list(); // all snapshots for the account $client->snapshots()->delete($snap->id); // Volumes $volume = $client->volumes()->create(new CreateVolume( name: 'node-cache', sizeGB: 10, region: $regionId, type: VolumeType::Sandbox, )); $client->volumes()->list(); $client->volumes()->delete($volume->id);
Catalog
$client->regions(); // list<Region>: sandbox-eligible regions $client->templates(); // list<Template>: available images
Pagination
List endpoints return a Paginated object you can iterate directly:
$page = $client->sandboxes()->list(page: 1, limit: 15); foreach ($page as $sandbox) { /* SandboxData */ } $page->totalCount; $page->currentPage; $page->hasMorePages();
Error handling
Every non-2xx response raises a typed exception. All API exceptions extend
SandboxApiException and carry status, method, endpoint, responseBody,
and requestId.
| Exception | Status |
|---|---|
AuthException |
401, 403 |
ValidationException |
400, 422 |
NotFoundException |
404 |
RateLimitException |
429 (with ->retryAfter) |
SandboxApiException |
any other non-2xx |
TransportException |
no HTTP response (DNS, connection, timeout) |
ConfigurationException |
bad/missing config (e.g. no API key) |
use Zeevx\BrimbleSandbox\Exceptions\NotFoundException; use Zeevx\BrimbleSandbox\Exceptions\SandboxApiException; try { $client->sandboxes()->get($id); } catch (NotFoundException $e) { // ... } catch (SandboxApiException $e) { error_log("{$e->method} {$e->endpoint} -> {$e->status}: {$e->getMessage()}"); }
Retries
Idempotent methods (GET, PUT, DELETE) are retried automatically on
408, 429, 500, 502, 503, 504 with exponential backoff (honouring
Retry-After). A POST is only retried when you pass an idempotency key:
$client->sandboxes()->create($input, idempotencyKey: 'my-unique-key');
Tune it on the client:
$client = new Sandbox(apiKey: '...', timeout: 90.0, maxRetries: 2);
Custom HTTP client
Inject your own Guzzle client (middleware, logging, a mock handler in tests):
use Zeevx\BrimbleSandbox\Config; $client = Sandbox::fromConfig(new Config(apiKey: '...'), $myGuzzleClient);
Development
composer test # Pest composer lint # Pint composer analyse # PHPStan level 8
License
MIT
统计信息
- 总下载量: 0
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 0
- 点击次数: 6
- 依赖项目数: 0
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2026-07-04