Skip to content

How-To Guide

This guide covers local development, customization, and production deployment.


RequirementNotes
Node.js + npmAny recent LTS version
DockerMust be running for sandbox builds
Cloudflare accountWorkers Sandbox support required
GitHub tokenFine-grained PAT with Copilot Requests permission; add repo read for private repos
Terminal window
npm install
cp .env.example .env

Edit .env:

GH_TOKEN=github_pat_...
SANDBOX_ENABLE_INTERNET=true
VariableRequiredDescription
GH_TOKENyesGitHub PAT with Copilot access
SANDBOX_ENABLE_INTERNETlocal onlySet true for local dev (enables DNS); omit in production

Never commit these secrets.

Terminal window
npm run dev

First run builds the sandbox image from Dockerfile (extends cloudflare/sandbox:0.7.20, installs @github/copilot).

Batch mode — wait for completion:

Terminal window
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:

Terminal window
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.

{
"success": true,
"exitCode": 0,
"logs": "...",
"stderr": "",
"diff": "diff --git ..."
}

The diff field contains escaped newlines. Use jq to extract it:

Terminal window
# View the diff
curl -s -X POST http://localhost:8787/ \
-H 'Content-Type: application/json' \
-d '{"repo": "...", "task": "..."}' | jq -r '.diff'
# Save to file and apply locally
curl -s ... | jq -r '.diff' > fix.patch
cd ~/Projects/your-repo
git apply fix.patch
Terminal window
npm test && npm run typecheck

FieldDescription
repoGitHub repository URL: https://github.com/owner/repo
taskWhat to do (max 8000 chars)
FieldDescription
modelCopilot model identifier (letters, numbers, _.-:)
prdTextInline PRD context (max 50000 chars)
prdPathRepo-relative path to a PRD file (max 240 chars)
skillPathsRepo-relative skill files to read and follow (max 10 paths, 240 chars each)
mcpConfigMCP server configuration object (max 10 servers, 32KB)

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.


Connect Model Context Protocol (MCP) servers to give Copilot access to external tools.

TypeDescription
local / stdioStarts a local process (e.g., npx @playwright/mcp@latest)
http / sseConnects to a remote MCP server via URL
{
"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": "*"
}
}
}
}
{
"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": "*"
}
}
}
}
FieldRequiredDescription
typeyeslocal, stdio, http, or sse
commandlocal/stdioCommand to start the server (e.g., npx)
argsnoArguments for the command
urlhttp/sseRemote server URL
headersnoHTTP headers for remote servers
envnoEnvironment variables (key-value object)
toolsno"*" for all tools or array of tool names
  • 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.

  1. Validate and normalize request input.
  2. Derive a stable sandbox ID from the repository URL.
  3. Clone the repository (token passed as command env, not baked into image).
  4. Run copilot -p ... --allow-all in non-interactive mode.
  5. Capture logs, stderr, and git diff.
  6. Return JSON or stream output.

Requests for the same repository reuse the same sandbox identity. Each run removes and reclones the target directory, keeping execution predictable.

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.


Terminal window
npx wrangler deploy
npx wrangler secret put GH_TOKEN

First deploy provisions the container (may take a few minutes):

Terminal window
npx wrangler containers list

Deployed URL: https://cpltbox.<subdomain>.workers.dev

Terminal window
npx wrangler deploy --dry-run

Builds the image and bundles without deploying.

Same as local — just change the URL:

Terminal window
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:

Terminal window
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"
}'

  • Use a GitHub token with minimum required permissions.
  • Store GH_TOKEN in Cloudflare secrets, not source control.
  • Test both / and /stream locally before deploying.
  • Review generated diffs before applying changes.
  • Monitor for checkout failures (permissions) and network failures (changed hostnames).
  • Keep @cloudflare/sandbox and Docker base image versions aligned.

ErrorCauseFix
GH_TOKEN is not configuredMissing secretSet in .env (local) or wrangler secret put (prod)
repo must be an https://github.com URLInvalid URL formatUse repository homepage, not issue/PR/file URL
Could not resolve host: github.comDNS blocked in sandboxSet SANDBOX_ENABLE_INTERNET=true in .env
Checkout failureRepo doesn’t exist or token lacks accessVerify repo exists and token has read permission
Command timeout after 300000msCopilot did not finish within the sandbox command timeoutRetry with /stream for visibility, reduce task scope, or check Copilot/network logs
Empty diffCopilot ran but made no changesCheck logs and stderr for details

Terminal window
npm test # unit tests
npm run test:acceptance # Worker-level tests with mocked sandbox
npm run typecheck # TypeScript check

Add acceptance tests in src/index.acceptance.test.ts when changing routes, response shape, or command orchestration.