diff --git a/.changeset/add-cloudflare-ai-gateway-support.md b/.changeset/add-cloudflare-ai-gateway-support.md new file mode 100644 index 00000000..a919c7bd --- /dev/null +++ b/.changeset/add-cloudflare-ai-gateway-support.md @@ -0,0 +1,5 @@ +--- +'@cloudflare/sandbox': patch +--- + +Add Cloudflare AI Gateway support to OpenCode integration. Users can now route AI provider requests through Cloudflare AI Gateway for monitoring, caching, and rate limiting by adding a `cloudflareAIGateway` provider configuration with `accountId`, `gatewayId`, and optional `apiToken`. diff --git a/examples/opencode/README.md b/examples/opencode/README.md index e48815b6..af2ec5f0 100644 --- a/examples/opencode/README.md +++ b/examples/opencode/README.md @@ -42,4 +42,33 @@ OpenCode handles everything: - **Isolated execution** - Code runs in secure sandbox containers - **Persistent sessions** - Sessions survive across requests +## Advanced: Cloudflare AI Gateway + +You can optionally route all AI provider requests through [Cloudflare AI Gateway](https://developers.cloudflare.com/ai-gateway/) for monitoring, caching, and rate limiting. Add these variables to `.dev.vars`: + +```bash +CLOUDFLARE_ACCOUNT_ID=your-account-id +CLOUDFLARE_GATEWAY_ID=your-gateway-id +CLOUDFLARE_API_TOKEN=your-api-token # Optional, for authenticated gateways +``` + +Then uncomment the `cloudflareAIGateway` section in `src/index.ts`: + +```typescript +const getConfig = (env: Env): Config => ({ + provider: { + anthropic: { + options: { apiKey: env.ANTHROPIC_API_KEY } + }, + cloudflareAIGateway: { + options: { + accountId: env.CLOUDFLARE_ACCOUNT_ID, + gatewayId: env.CLOUDFLARE_GATEWAY_ID, + apiToken: env.CLOUDFLARE_API_TOKEN + } + } + } +}); +``` + Happy hacking! diff --git a/packages/sandbox/src/opencode/opencode.ts b/packages/sandbox/src/opencode/opencode.ts index 83c8a802..f7626428 100644 --- a/packages/sandbox/src/opencode/opencode.ts +++ b/packages/sandbox/src/opencode/opencode.ts @@ -35,7 +35,7 @@ async function ensureSdkLoaded(): Promise { } catch { throw new Error( '@opencode-ai/sdk is required for OpenCode integration. ' + - 'Install it with: npm install @opencode-ai/sdk' + 'Install it with: npm install @opencode-ai/sdk' ); } } @@ -175,6 +175,10 @@ async function startOpencodeServer( for (const [providerId, providerConfig] of Object.entries( config.provider )) { + if (providerId === 'cloudflareAIGateway') { + continue; + } + // Try options.apiKey first (official Config type) let apiKey = providerConfig?.options?.apiKey; // Fall back to top-level apiKey for convenience @@ -187,6 +191,23 @@ async function startOpencodeServer( env[envVar] = apiKey; } } + + const aiGatewayConfig = config.provider.cloudflareAIGateway; + if (aiGatewayConfig?.options) { + const options = aiGatewayConfig.options as Record; + + if (typeof options.accountId === 'string') { + env.CLOUDFLARE_ACCOUNT_ID = options.accountId; + } + + if (typeof options.gatewayId === 'string') { + env.CLOUDFLARE_GATEWAY_ID = options.gatewayId; + } + + if (typeof options.apiToken === 'string') { + env.CLOUDFLARE_API_TOKEN = options.apiToken; + } + } } } @@ -244,7 +265,21 @@ async function startOpencodeServer( * const sandbox = getSandbox(env.Sandbox, 'my-agent') * const server = await createOpencodeServer(sandbox, { * directory: '/home/user/my-project', - * config: { provider: { anthropic: { options: { apiKey: env.ANTHROPIC_KEY } } } } + * config: { + * provider: { + * anthropic: { + * options: { apiKey: env.ANTHROPIC_KEY } + * }, + * // Optional: Route all providers through Cloudflare AI Gateway + * cloudflareAIGateway: { + * options: { + * accountId: env.CF_ACCOUNT_ID, + * gatewayId: env.CF_GATEWAY_ID, + * apiToken: env.CF_API_TOKEN + * } + * } + * } + * } * }) * * // Proxy requests to the web UI @@ -295,7 +330,21 @@ export async function createOpencodeServer( * const sandbox = getSandbox(env.Sandbox, 'my-agent') * const { client, server } = await createOpencode(sandbox, { * directory: '/home/user/my-project', - * config: { provider: { anthropic: { options: { apiKey: env.ANTHROPIC_KEY } } } } + * config: { + * provider: { + * anthropic: { + * options: { apiKey: env.ANTHROPIC_KEY } + * }, + * // Optional: Route all providers through Cloudflare AI Gateway + * cloudflareAIGateway: { + * options: { + * accountId: env.CF_ACCOUNT_ID, + * gatewayId: env.CF_GATEWAY_ID, + * apiToken: env.CF_API_TOKEN + * } + * } + * } + * } * }) * * // Use the SDK client for programmatic access @@ -354,7 +403,21 @@ export async function createOpencode( * const sandbox = getSandbox(env.Sandbox, 'opencode') * const server = await createOpencodeServer(sandbox, { * directory: '/home/user/project', - * config: { provider: { anthropic: { options: { apiKey: env.ANTHROPIC_KEY } } } } + * config: { + * provider: { + * anthropic: { + * options: { apiKey: env.ANTHROPIC_KEY } + * }, + * // Optional: Route all providers through Cloudflare AI Gateway + * cloudflareAIGateway: { + * options: { + * accountId: env.CF_ACCOUNT_ID, + * gatewayId: env.CF_GATEWAY_ID, + * apiToken: env.CF_API_TOKEN + * } + * } + * } + * } * }) * return proxyToOpencode(request, sandbox, server) * }