diff --git a/.github/workflows/sandbox-benchmarks.yml b/.github/workflows/sandbox-benchmarks.yml index f1a72ac..78dc3ee 100644 --- a/.github/workflows/sandbox-benchmarks.yml +++ b/.github/workflows/sandbox-benchmarks.yml @@ -58,6 +58,7 @@ jobs: - modal - namespace - runloop + - upstash-box - vercel steps: - uses: actions/checkout@v4 @@ -91,6 +92,7 @@ jobs: MODAL_TOKEN_SECRET: ${{ secrets.MODAL_TOKEN_SECRET }} NSC_TOKEN: ${{ secrets.NSC_TOKEN }} RUNLOOP_API_KEY: ${{ secrets.RUNLOOP_API_KEY }} + UPSTASH_BOX_API_KEY: ${{ secrets.UPSTASH_BOX_API_KEY }} VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }} VERCEL_TEAM_ID: ${{ secrets.VERCEL_TEAM_ID }} VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }} diff --git a/METHODOLOGY.md b/METHODOLOGY.md index 9601bdc..0fc7879 100644 --- a/METHODOLOGY.md +++ b/METHODOLOGY.md @@ -161,7 +161,7 @@ npm run bench:burst # Burst only ### Provider Integration -**ComputeSDK**: Uses ComputeSDK for consistency and ease-of-use (e2b, daytona, blaxel, modal, vercel, hopx, codesandbox, runloop, namespace) +**ComputeSDK and direct SDK adapters**: Uses ComputeSDK where available and thin direct adapters otherwise for consistency and ease-of-use (e2b, daytona, blaxel, modal, vercel, hopx, codesandbox, runloop, namespace, upstash-box) ### Provider Execution Order diff --git a/env.example b/env.example index dadfb9e..cd47b19 100644 --- a/env.example +++ b/env.example @@ -38,6 +38,9 @@ RENDER_OWNER_ID=your_render_owner_id ######### SPRITES ######## SPRITES_TOKEN=your_sprites_token +######### UPSTASH BOX ######## +UPSTASH_BOX_API_KEY=your_upstash_box_api_key + ######### STORAGE ######## S3_ACCESS_KEY_ID=your_s3_access_key_id S3_SECRET_ACCESS_KEY=your_s3_secret_access_key diff --git a/package-lock.json b/package-lock.json index c0b6ba2..257f683 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,6 +25,7 @@ "@computesdk/sprites": "^0.1.5", "@computesdk/tigris": "^1.1.1", "@computesdk/vercel": "^1.7.22", + "@upstash/box": "^0.1.32", "computesdk": "^2.5.4", "dotenv": "^17.4.0", "playwright-core": "^1.59.1" @@ -6593,6 +6594,21 @@ "@types/node": "*" } }, + "node_modules/@upstash/box": { + "version": "0.1.32", + "resolved": "https://registry.npmjs.org/@upstash/box/-/box-0.1.32.tgz", + "integrity": "sha512-O4lxv7QHiZAIufNQA48MpwsiigfONyGB2PyQyoQTn3qyMYpqyE8VwxJwGMufApwphRlo/+iTxiYCC9/37qtKpQ==", + "license": "MIT", + "dependencies": { + "zod-to-json-schema": "^3.25.1" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "zod": "^3.25.0 || ^4.0.0" + } + }, "node_modules/@vercel/oidc": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/@vercel/oidc/-/oidc-3.2.0.tgz", diff --git a/package.json b/package.json index 692079c..a94e59e 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "bench:render": "tsx src/run.ts --provider render", "bench:runloop": "tsx src/run.ts --provider runloop", "bench:vercel": "tsx src/run.ts --provider vercel", + "bench:upstash-box": "tsx src/run.ts --provider upstash-box", "bench:just-bash": "tsx src/run.ts --provider just-bash", "bench:sprites": "tsx src/run.ts --provider sprites", "bench:browser": "tsx src/run.ts --mode browser", @@ -56,6 +57,7 @@ "@computesdk/sprites": "^0.1.5", "@computesdk/tigris": "^1.1.1", "@computesdk/vercel": "^1.7.22", + "@upstash/box": "^0.1.32", "computesdk": "^2.5.4", "dotenv": "^17.4.0", "playwright-core": "^1.59.1" diff --git a/src/sandbox/providers.ts b/src/sandbox/providers.ts index bdd8fc1..65161db 100644 --- a/src/sandbox/providers.ts +++ b/src/sandbox/providers.ts @@ -11,6 +11,7 @@ import { cloudflare } from '@computesdk/cloudflare'; import { sprites } from '@computesdk/sprites'; import { compute } from 'computesdk'; import type { ProviderConfig } from './types.js'; +import { createUpstashBoxCompute } from './upstash-box.js'; /** * All provider benchmark configurations. @@ -73,6 +74,12 @@ export const providers: ProviderConfig[] = [ requiredEnvVars: ['SPRITES_TOKEN'], createCompute: () => sprites({ apiKey: process.env.SPRITES_TOKEN! }), }, + { + name: 'upstash-box', + requiredEnvVars: ['UPSTASH_BOX_API_KEY'], + createCompute: () => createUpstashBoxCompute({ apiKey: process.env.UPSTASH_BOX_API_KEY! }), + sandboxOptions: { runtime: 'node', ttl: 300 }, + }, { name: 'vercel', requiredEnvVars: ['VERCEL_TOKEN', 'VERCEL_TEAM_ID', 'VERCEL_PROJECT_ID'], diff --git a/src/sandbox/upstash-box.ts b/src/sandbox/upstash-box.ts new file mode 100644 index 0000000..c8663cd --- /dev/null +++ b/src/sandbox/upstash-box.ts @@ -0,0 +1,47 @@ +import { EphemeralBox, type EphemeralBoxConfig } from '@upstash/box'; + +type UpstashBoxRunResult = { + exitCode: number; + stderr?: string; +}; + +type UpstashBoxSandbox = { + runCommand: (command: string) => Promise; + destroy: () => Promise; +}; + +type UpstashBoxCompute = { + sandbox: { + create: (options?: Record) => Promise; + }; +}; + +const DEFAULT_EPHEMERAL_BOX_CONFIG: Pick = { + runtime: 'node', + ttl: 300, +}; + +export function createUpstashBoxCompute(connectionOptions: Pick): UpstashBoxCompute { + return { + sandbox: { + async create(options?: Record): Promise { + const box = await EphemeralBox.create({ + ...DEFAULT_EPHEMERAL_BOX_CONFIG, + ...options as Partial, + ...connectionOptions, + }); + + return { + async runCommand(command: string): Promise { + const run = await box.exec.command(command); + return { + exitCode: run.exitCode ?? 1, + stderr: run.exitCode === 0 ? undefined : run.result, + }; + }, + destroy: () => box.delete(), + }; + }, + }, + }; +}