Skip to content

feat: local npx deployment for Scope (server + standalone CLI) - #1470

Draft
Cedric Vidal (cedricvidal) wants to merge 36 commits into
mainfrom
cedricvidal-local-scope-launcher
Draft

Cedric Vidal (cedricvidal) wants to merge 36 commits into
mainfrom
cedricvidal-local-scope-launcher

Conversation

@cedricvidal

@cedricvidal Cedric Vidal (cedricvidal) commented Oct 8, 2026 •

Copy link
Copy Markdown
Collaborator

Lets someone try Scope on their laptop with two commands and no checkout:

npm install -g --omit=optional ./scope-server-0.1.0.tgz
scope-server start

That brings up the full platform — MongoDB, Redis, Azurite, Lowkey Vault, the API, Portal, Judge, scheduler, post-processor, report generator and the capture gateway — prints a Portal and API URL, and runs real benchmarks against real coding agents.

This is a third deployment option alongside the existing Compose development setup and the Kubernetes deployment. Neither of those changes.

How it works

apps/server/ — the launcher. A Commander CLI (start, stop, restart, status) that owns the local stack. It declares the services as a small typed manifest and drives Docker through packages/docker-orchestrator/ (Dockerode) rather than shelling out to docker compose. Every container and network it creates carries a dev.scope.server.owner label, so shutdown removes exactly what it created and never the persistent bind-mounted data. Ports are allocated once and reused on later starts, so saved CLI environments and bookmarks keep working.

Packaging. pnpm pack:server bundles version-matched Scope sources, Dockerfiles, the lockfile and config assets into a private tarball. Images are built locally on first run from those bundled sources — the package never checks out Scope or pulls a prebuilt Scope image. Only public dependencies and base images are fetched. Image cache keys include the sources, package version, build timestamp and component versions.

Host coding agents. Alongside the Docker workers, two new packages (coder-acp-copilot-host, coder-acp-claude-code-host) run the user's already installed Copilot or Claude Code CLI as a benchmark worker, reusing its existing login. Scope never installs, upgrades or logs into those CLIs. Host targets require explicit per-target consent that states what it means: the agent runs as you, with your files and login. To keep results reproducible, host runs disable personal MCP servers and settings (settingSources: [] + --strict-mcp-config for Claude; --disable-builtin-mcps plus per-server disables for Copilot) while deliberately not relocating HOME or CLAUDE_CONFIG_DIR, which would break login reuse.

To make this possible, each worker's queue-processing logic moved out of its index.ts entrypoint into a reusable worker.ts, so the Docker and host entrypoints share one implementation.

apps/cli/ — a standalone client. Packaged separately so it can be installed on its own. Adds named environments (scope env add/use/set), so one CLI can target the local instance and a shared deployment without re-typing URLs, with explicit precedence between flags, the named environment and process env. Also adds scope agent setup for enabling agents from the terminal, and scope secret for credential management — which the CLI previously lacked entirely, a CLI↔Portal parity gap. A regression test pins that no secret subcommand can print a credential value in any output format.

Portal. A Set up local agents dialog (ServerAgentSetup) appears only when the launcher is driving the API, offering the same host/Docker agent setup, executable selection and consent as the CLI.

Try it

scope env add local --url http://127.0.0.1:<printed-port>
scope env use local
scope agent setup coder-acp-copilot --enable --wait
scope run submit --worker coder-acp-copilot --model gpt-5.4-mini \
  --criteria <criterion> --message "<task>"

Logs stream live; results, artifacts and reports are available from both the CLI and the Portal. Stopping leaves the data directory intact, so a later scope-server start resumes with the same projects, runs and verdicts.

Also in here

  • Capture backend default. createProxyClient() defaulted to the legacy devproxy when PROXY_BACKEND was unset, so any caller that forgot to set it silently got the legacy path. The default is now gateway, paired in the same commit with an explicit PROXY_BACKEND: devproxy pin for the ACP Claude Code worker in docker-compose.yml, which was relying on that implicit default. Behaviour-preserving for existing workers.
  • Docker streaming. Image build/pull streams no longer inherit the management socket's inactivity timeout — quietly committing a large layer could exceed it and abort the build.
  • Shutdown. Bounded graceful stop with forced removal of the already ownership-verified container, so a hung daemon can't leave orphans behind.

Validation

Exercised outside the checkout with the packed artifacts, on macOS arm64 with a Podman Docker-compatible socket:

  • Copilot in Docker — real benchmark with gateway capture: 4 tool calls extracted into the HAR, ATIF generated, and a passing verdict on a criterion that explicitly requires captured tool-call evidence. The downloaded artifact printed exactly Hello Scope!, exit 0, and its report completed.
  • Claude Code on the host, and in Docker — full coding → evaluation → artifact execution → report.
  • Lifecycle — stop/restart preserves projects, runs and verdicts; shutdown left zero owned containers.

pnpm exec vitest run — 231 files / 2,859 tests passing.

docs/architecture/scope-server.md documents packaging, storage layout, agent setup and consent, capture semantics and the observed coverage.

Before review

Expose local runtime setup through the API and Portal, and extend Secrets/Token Manager with saved Portal provider selection and compatible text transports.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Pin each command's API/auth/project context, retain explicit-URL legacy behavior, and expose agent setup and secret/provider management. Exercise the real private package outside the checkout.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Start the existing services through Dockerode with bundled sources and Dockerfiles, persistent storage, and selected host or Docker agent setup. Reuse installed Copilot and Claude CLIs through thin worker wrappers, and include the Portal shared dependency in clean container builds.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
The existing worker proxy switch treats every nonempty environment value as enabled. Emit an empty value for disabled local capture instead of the truthy string false.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Handle ky NetworkError without replaying setup or credential mutations, and clarify that run submission accepts host worker types.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Bound stop requests and force-remove only containers with verified ownership after graceful failure. Preserve both errors if removal also fails.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 0dbdb5f0-a52e-4f98-9922-b531502619b9
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 0dbdb5f0-a52e-4f98-9922-b531502619b9
Keep management-request timeouts without aborting active image streams during long compression or extraction. Reproduce the failure with the real Dockerode transport.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 0dbdb5f0-a52e-4f98-9922-b531502619b9
Document the successful private-package retry and final owned-resource cleanup while retaining the native Copilot blocker and failed Copilot Docker Judge verdict.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 0dbdb5f0-a52e-4f98-9922-b531502619b9
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 0dbdb5f0-a52e-4f98-9922-b531502619b9
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 0dbdb5f0-a52e-4f98-9922-b531502619b9
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 0dbdb5f0-a52e-4f98-9922-b531502619b9
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 0dbdb5f0-a52e-4f98-9922-b531502619b9
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 0dbdb5f0-a52e-4f98-9922-b531502619b9
Default unset proxy backend to gateway, pin the remaining Compose Claude DevProxy path explicitly, and enable gateway capture for local Docker and host workers.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 0dbdb5f0-a52e-4f98-9922-b531502619b9
Host targets run as the user and their HTTPS traffic is decrypted by the local
capture gateway's own CA. State both, plus the personal-config isolation, before
asking for consent rather than mentioning file access alone.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 0dbdb5f0-a52e-4f98-9922-b531502619b9
Restore the portal LLM chain to Foundry env, Foundry via Token Manager, then GitHub Models. Remove the provider selector, OpenAI/OpenRouter/compatible credential types, Portal AI settings endpoints, and provider transports while preserving the Foundry reasoning-model parameter normalization.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 0dbdb5f0-a52e-4f98-9922-b531502619b9
Keep the secret CRUD/preview/validate commands, remove the Portal AI subcommand group and provider presets, warn on argv secret input, and add output-format coverage proving secret values are not printed.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 0dbdb5f0-a52e-4f98-9922-b531502619b9
Update resume tests to seed the pre-update request lookup introduced by host-worker availability checks.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 0dbdb5f0-a52e-4f98-9922-b531502619b9
Feeds a value field the API should never return and asserts it reaches no output
format. Verified meaningful by mutation: adding value to the metadata allowlist
fails this test.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 0dbdb5f0-a52e-4f98-9922-b531502619b9
Both packages stay private, so registry installs do not work; document the
tarball path instead. Recommend --omit=optional for the server: it drops a
225 MB bundled Claude binary of the 618 MB install that host runs never execute,
because the ACP adapter prefers CLAUDE_CODE_EXECUTABLE.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 0dbdb5f0-a52e-4f98-9922-b531502619b9
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 0dbdb5f0-a52e-4f98-9922-b531502619b9
Routing a host worker through the gateway relies on Node-only proxy and TLS
env vars, but the worker ultimately spawns the user's installed CLI, which is a
native binary that ignores them and does not trust the interception CA. A real
Claude Code host run with capture on fails with FailedToOpenSocket and records
an empty HAR, so capture now requires SCOPE_HOST_CAPTURE=1. Docker capture,
which is where the missing-execution-history verdict came from, stays on.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 0dbdb5f0-a52e-4f98-9922-b531502619b9
Routing ACP Claude Code through the gateway makes its native CLI fail with
ConnectionRefused and record an empty HAR, which is why docker-compose still
pins that worker to DevProxy. Enable capture for the Copilot Docker worker only,
matching the migration's actual state, instead of breaking Claude benchmarks to
collect evidence the gateway cannot produce for it.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 0dbdb5f0-a52e-4f98-9922-b531502619b9
Request 0735ba0e extracted 4 tool calls from the HAR, generated ATIF 1/1 and
passed the criterion that requires captured tool-call evidence, closing the
missing-execution-history gap. Also note that Claude Docker's earlier pass ran
uncaptured and fails when routed through the gateway.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 0dbdb5f0-a52e-4f98-9922-b531502619b9
Typing 'start' printed a wall of '[service] stopping' lines, because startup
ran the full stop() path to clear a previous launcher's leftovers. That also
removed the network connect() had just created, forcing a second connect, and
duplicated work startService already does via removeOwned.

Replace it with reclaim(): it drops only stale owned containers, keeps the
network, reports them as 'reclaimed', and stays silent when there is nothing to
reclaim.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 0dbdb5f0-a52e-4f98-9922-b531502619b9
Re-adding a name failed with no way to proceed except editing fields one by one.
--force replaces the whole entry; without it the command still refuses, so a
saved token or project is never silently discarded.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 0dbdb5f0-a52e-4f98-9922-b531502619b9
Resolve conflicts with main (#1379, #1373/#1374, #1381, #1385):
- Port main's ACP worker logic into the reusable worker.ts; keep index.ts thin
- Use main's agent-registry admission; keep host checks for resume/bulk-resume
- Pin SCOPE_AGENT_VERSION for host and Docker workers to the registered version
- CLI api-client: main's auth + environment-aware client; SSE sends auth
- Drop Foundry proxy (superseded by #1379) and .scope Dockerfile rewrite
- Regenerate pnpm-lock.yaml and OpenAPI snapshot

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@github-actions github-actions Bot added type: documentation Documentation additions, corrections, and improvements. area: worker Coding agent worker runtime, task execution, and lifecycle. area: cli Scope command-line interface and terminal workflows. type: dependencies Dependency additions, removals, and version updates. area: infrastructure Cloud resources, hosting, networking, and shared infrastructure. language: javascript Work involving JavaScript code, tooling, or dependencies. area: portal Scope web portal, pages, and user-facing components. topic: testing Test coverage, test infrastructure, and validation quality. topic: ui Portal, visual presentation, layout, and interface components. labels Oct 8, 2026
@github-actions

github-actions Bot commented Oct 8, 2026

Copy link
Copy Markdown

Test Results (Node.js 22)

test: Run #210

Tests 📝 Passed ✅ Failed ❌ Skipped ⏭️ Pending ⏳ Other ❓ Flaky 🍂 Duration ⏱️
3443 3443 0 0 0 0 0 1m6s

🎉 All tests passed!

Github Test Reporter

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: cli Scope command-line interface and terminal workflows. area: infrastructure Cloud resources, hosting, networking, and shared infrastructure. area: portal Scope web portal, pages, and user-facing components. area: worker Coding agent worker runtime, task execution, and lifecycle. language: javascript Work involving JavaScript code, tooling, or dependencies. topic: testing Test coverage, test infrastructure, and validation quality. topic: ui Portal, visual presentation, layout, and interface components. type: dependencies Dependency additions, removals, and version updates. type: documentation Documentation additions, corrections, and improvements.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant