How-To Guide
This guide covers local development, customization, and production deployment.
Local Development
Section titled “Local Development”Prerequisites
Section titled “Prerequisites”| Requirement | Notes |
|---|---|
| Node.js + npm | Any recent LTS version |
| Docker | Must be running for sandbox builds |
| Cloudflare account | Workers Sandbox support required |
| GitHub token | Fine-grained PAT with Copilot Requests permission; add repo read for private repos |
npm installcp .env.example .envEdit .env:
GH_TOKEN=github_pat_...SANDBOX_ENABLE_INTERNET=true| Variable | Required | Description |
|---|---|---|
GH_TOKEN | yes | GitHub PAT with Copilot access |
SANDBOX_ENABLE_INTERNET | local only | Set true for local dev (enables DNS); omit in production |
Never commit these secrets.
Run Locally
Section titled “Run Locally”npm run devFirst run builds the sandbox image from Dockerfile (extends cloudflare/sandbox:0.7.20, installs @github/copilot).
Send a Request
Section titled “Send a Request”Batch mode — wait for completion:
curl -X POST http://localhost:8787/ \ -H 'Content-Type: application/json' \ -d '{"repo": "https://github.com/owner/repo", "task": "Fix the typo in README.md"}'Streaming mode — watch progress:
curl -N -X POST http://localhost:8787/stream \ -H 'Content-Type: application/json' \ -d '{"repo": "https://github.com/owner/repo", "task": "Run the tests and fix one failing assertion"}'Alternative: Use an HTTP client like REST Client, Bruno, Postman, or HTTPie instead of curl.
Response Shape
Section titled “Response Shape”{ "success": true, "exitCode": 0, "logs": "...", "stderr": "", "diff": "diff --git ..."}Working with the Diff
Section titled “Working with the Diff”The diff field contains escaped newlines. Use jq to extract it:
# View the diffcurl -s -X POST http://localhost:8787/ \ -H 'Content-Type: application/json' \ -d '{"repo": "...", "task": "..."}' | jq -r '.diff'
# Save to file and apply locallycurl -s ... | jq -r '.diff' > fix.patchcd ~/Projects/your-repogit apply fix.patchVerify
Section titled “Verify”npm test && npm run typecheckRequest Options
Section titled “Request Options”Required Fields
Section titled “Required Fields”| Field | Description |
|---|---|
repo | GitHub repository URL: https://github.com/owner/repo |
task | What to do (max 8000 chars) |
Optional Fields
Section titled “Optional Fields”| Field | Description |
|---|---|
model | Copilot model identifier (letters, numbers, _.-:) |
prdText | Inline PRD context (max 50000 chars) |
prdPath | Repo-relative path to a PRD file (max 240 chars) |
skillPaths | Repo-relative skill files to read and follow (max 10 paths, 240 chars each) |
mcpConfig | MCP server configuration object (max 10 servers, 32KB) |
Examples
Section titled “Examples”With model:
{"repo": "https://github.com/owner/repo", "task": "Summarize the structure", "model": "gpt-5.2"}With inline PRD:
{"repo": "https://github.com/owner/repo", "task": "Implement the onboarding flow", "prdText": "Users complete setup in under five minutes."}With PRD file: (see prd.md for a simple example)
{"repo": "https://github.com/owner/repo", "task": "Implement the dashboard", "prdPath": "docs/prd.md"}With repo skill files:
{"repo": "https://github.com/owner/repo", "task": "Build the REST endpoint", "skillPaths": [".cpltbox/skills/wp-rest-api/SKILL.md"]}You can combine prdText, prdPath, and skillPaths. The Worker appends PRD and skill path context to the task before invoking Copilot.
PRD and skill content is untrusted input — never include tokens or secrets.
MCP Servers
Section titled “MCP Servers”Connect Model Context Protocol (MCP) servers to give Copilot access to external tools.
Server Types
Section titled “Server Types”| Type | Description |
|---|---|
local / stdio | Starts a local process (e.g., npx @playwright/mcp@latest) |
http / sse | Connects to a remote MCP server via URL |
Example: Local Server
Section titled “Example: Local Server”{ "repo": "https://github.com/owner/repo", "task": "Test the login page using Playwright", "mcpConfig": { "mcpServers": { "playwright": { "type": "local", "command": "npx", "args": ["@playwright/mcp@latest"], "tools": "*" } } }}Example: Remote Server
Section titled “Example: Remote Server”{ "repo": "https://github.com/owner/repo", "task": "Search for documentation", "mcpConfig": { "mcpServers": { "context7": { "type": "http", "url": "https://mcp.context7.com/mcp", "headers": {"API-KEY": "your-api-key"}, "tools": "*" } } }}Configuration Reference
Section titled “Configuration Reference”| Field | Required | Description |
|---|---|---|
type | yes | local, stdio, http, or sse |
command | local/stdio | Command to start the server (e.g., npx) |
args | no | Arguments for the command |
url | http/sse | Remote server URL |
headers | no | HTTP headers for remote servers |
env | no | Environment variables (key-value object) |
tools | no | "*" for all tools or array of tool names |
Limitations
Section titled “Limitations”- Local servers require the MCP package to be installed in the sandbox. Add packages to the Dockerfile if needed.
- Remote servers work if the URL is reachable (check network allowlisting).
- Max 10 servers per request, 32KB total config size.
How It Works
Section titled “How It Works”- Validate and normalize request input.
- Derive a stable sandbox ID from the repository URL.
- Clone the repository (token passed as command env, not baked into image).
- Run
copilot -p ... --allow-allin non-interactive mode. - Capture logs, stderr, and
git diff. - Return JSON or stream output.
Sandbox Reuse
Section titled “Sandbox Reuse”Requests for the same repository reuse the same sandbox identity. Each run removes and reclones the target directory, keeping execution predictable.
Network Allowlisting
Section titled “Network Allowlisting”The sandbox restricts network access to GitHub/Copilot hosts via COPILOT_ALLOWED_HOSTS.
Local development: Set SANDBOX_ENABLE_INTERNET=true in .env. This is required because local Docker can’t resolve DNS for allowlisted hosts without general internet access.
Production: Omit SANDBOX_ENABLE_INTERNET (defaults to false). Cloudflare’s infrastructure handles DNS resolution for allowlisted hosts correctly.
If Copilot adds endpoints, update src/copilot.ts and rerun tests.
Deployment
Section titled “Deployment”Deploy
Section titled “Deploy”npx wrangler deploynpx wrangler secret put GH_TOKENFirst deploy provisions the container (may take a few minutes):
npx wrangler containers listDeployed URL: https://cpltbox.<subdomain>.workers.dev
Dry Run
Section titled “Dry Run”npx wrangler deploy --dry-runBuilds the image and bundles without deploying.
Production Requests
Section titled “Production Requests”Same as local — just change the URL:
curl -X POST https://cpltbox.<subdomain>.workers.dev/ \ -H 'Content-Type: application/json' \ -d '{"repo": "https://github.com/owner/repo", "task": "Fix the typo in README.md"}'For complex workflows, use streaming with a detailed PRD. See prd-github-issue-pr-loop.md for an issue-to-PR completion loop example:
curl -N -X POST https://cpltbox.<subdomain>.workers.dev/stream \ -H 'Content-Type: application/json' \ -d '{ "repo": "https://github.com/owner/repo", "task": "Resolve issue #123, open a PR, respond to feedback, and continue until done.", "prdPath": "docs/prd-github-issue-pr-loop.md" }'Production Checklist
Section titled “Production Checklist”- Use a GitHub token with minimum required permissions.
- Store
GH_TOKENin Cloudflare secrets, not source control. - Test both
/and/streamlocally before deploying. - Review generated diffs before applying changes.
- Monitor for checkout failures (permissions) and network failures (changed hostnames).
- Keep
@cloudflare/sandboxand Docker base image versions aligned.
Troubleshooting
Section titled “Troubleshooting”| Error | Cause | Fix |
|---|---|---|
GH_TOKEN is not configured | Missing secret | Set in .env (local) or wrangler secret put (prod) |
repo must be an https://github.com URL | Invalid URL format | Use repository homepage, not issue/PR/file URL |
Could not resolve host: github.com | DNS blocked in sandbox | Set SANDBOX_ENABLE_INTERNET=true in .env |
| Checkout failure | Repo doesn’t exist or token lacks access | Verify repo exists and token has read permission |
Command timeout after 300000ms | Copilot did not finish within the sandbox command timeout | Retry with /stream for visibility, reduce task scope, or check Copilot/network logs |
| Empty diff | Copilot ran but made no changes | Check logs and stderr for details |
Testing
Section titled “Testing”npm test # unit testsnpm run test:acceptance # Worker-level tests with mocked sandboxnpm run typecheck # TypeScript checkAdd acceptance tests in src/index.acceptance.test.ts when changing routes, response shape, or command orchestration.
📦 Source: soderlind/cpltbox · Edit on GitHub