Skip to content

Repository files navigation

First-time setup

git clone <this-repo> && cd pi-harness-nvidia-openshell
./scripts/setup.sh

This is idempotent and re-runnable: it inits the OpenShell submodule, links the relevant OpenShell agent skills (openshell-cli, generate-sandbox-policy, debug-openshell-cluster, debug-inference) into .agents/skills/, sanity-checks openshell/docker/pi, and asks whether to install the pis command (pi-in-sandbox wrapper, see below). Restart pi after running it so the new skills are picked up.

Nvidia Openshell Install

Start or restart the local gateway with: brew services restart openshell

Register it with the OpenShell CLI: openshell gateway add https://localhost:17670 --local --name openshell

For systemd service

To start nvidia/openshell/openshell now and restart at login: brew services start nvidia/openshell/openshell

For one time start:

Or, if you don't want/need a background service you can just run: /opt/homebrew/opt/openshell/libexec/openshell-gateway-homebrew-service openshell: restarting OpenShell Homebrew service...

On Success:

==> Successfully started openshell (label: homebrew.mxcl.openshell) openshell: registering local gateway as jayce... ✓ Gateway 'openshell' added and set as active Endpoint: https://localhost:17670 Type: local ✓ TLS certificates present

Inference routing (https://inference.local)

Gateway-side (run once on the host):

./scripts/create_vllm_provider.sh          # openai-compatible provider -> devbox vLLM
./scripts/set_vllm_provider_for_inference.sh  # route inference.local to it
openshell inference update --timeout 300   # long generations; hot-reloads in ~5s

Inside the sandbox, https://inference.local is the only inference path:

  • HTTPS-only, credentials injected by the gateway (no API keys in the sandbox)
  • The router strips caller Authorization and rewrites model to the configured one
  • OpenAI-compatible patterns only: /v1/chat/completions, /v1/completions, /v1/responses, /v1/embeddings, /v1/models

Pi is preconfigured via pi-config/ (baked into the image at /sandbox/.pi/agent/):

  • models.json registers provider openshell at https://inference.local/v1 (openai-completions, placeholder apiKey, Qwen3.8-27B with qwen-chat-template thinking)
  • settings.json sets it as the default model, so bare pi works
  • The npm extension packages listed in settings.json are pinned and pre-installed into /sandbox/.pi/agent/npm/ at image build time (Dockerfile). pi finds them at startup and never needs registry.npmjs.org egress — the sandbox policy grants it none. To update a package, bump the pin in pi-config/settings.json and the Dockerfile together, then rebuild.

Running pi in a sandbox

Primary workflow — scripts/pi-sandbox (installed as pis by setup.sh): fresh sandbox per session, git round-trip, automatic cleanup:

./scripts/build.sh                                   # after Dockerfile/policy changes
cd <project> && pis                                  # fresh sandbox for this project
cd <project> && git pull                             # collect the agent's commits

pis defaults to your current directory, so it works from anywhere: cd to a project, type pis, and the sandbox setup (create, upload, git config) happens automatically in the background before the pi TUI starts. The host pi binary is untouched. (setup.sh asks whether to install it; the installer drops a wrapper into a writable PATH dir, preferring ~/.local/bin. Or manually: ln -sf <repo>/scripts/pi-sandbox ~/.local/bin/pis. pi-sandbox /path also accepts an explicit project dir.)

  • Creates a one-shot sandbox with an auto-generated name (recovered by diffing sandbox list before/after create), uploads the project including .git (the uploader skips .git otherwise, so it gets a second explicit upload), and runs pi inside at /sandbox/<project>.
  • The agent commits and pushes from inside: the push rule is enabled in policy.yaml (git smart HTTP git-receive-pack), and a credential helper is preconfigured to use the token from your host gh login (injected with --env GITHUB_TOKEN, so it never lands in the sandbox's git config).
  • If your origin is an SSH URL (git@github.com:...), the sandbox copy of origin is rewritten to the equivalent https:// URL: SSH can't be pushed from the sandbox (no egress to port 22, no keys), and the credential helper only applies to HTTPS. Your host origin is untouched.
  • On clean pi exit the sandbox is deleted. On abnormal exit (crash, Ctrl-C) it is kept and the name is printed: openshell sandbox connect <name> to continue, openshell sandbox delete <name> to discard.

Why the push token is a real env var, not a provider placeholder

The clean OpenShell mechanism (provider v2: gateway holds the secret, the sandbox gets a placeholder, the proxy rewrites it) works for gh/API traffic — see provider-profiles/github-push.yaml (imported profile + github-push provider instance; placeholder resolution verified against api.github.com). But the proxy only rewrites placeholders in Bearer headers, URL paths, and queries — not inside base64-decoded Basic auth. Git over HTTPS can only authenticate via Basic, and GitHub's git endpoints reject Authorization: Bearer. So git pushes with the real token from --env; the egress policy still confines it (only git/gh may reach GitHub hosts). If the proxy ever rewrites Basic auth, the provider path becomes fully secret-free — this gap is worth an issue upstream.

Legacy: persistent named sandbox

./scripts/run_sandbox.sh /path/to/project keeps a fixed-name sandbox (pi-sandbox) alive across sessions and uploads the project (without .git) to /sandbox/<project>. Round-trip work with openshell sandbox upload/download <name> <path> <dest> (.gitignore respected on upload; download extracts contents directly into the destination dir). Useful for scratch work that does not round-trip via git.

Verify inside any sandbox (e.g. after openshell sandbox connect):

curl https://inference.local/v1/chat/completions -H 'Content-Type: application/json' \
  -d '{"messages":[{"role":"user","content":"hi"}],"max_tokens":16}'
pi --list-models | grep openshell
pi -p "Reply with exactly one word: hi"

Notes:

  • A Qwen thinking model served by vLLM thinks by default; pi maps its thinking level to chat_template_kwargs.enable_thinking (off by default). pi --thinking high to opt in.
  • openshell sandbox list shows phase Error when the main process exits (e.g. Ctrl-C in the shell). The sandbox is kept by default; use openshell sandbox start, recreate, or -- sleep infinity as the main process.

About

Turnkey Pi + Openshell Setup, Sandboxed pi agent running inside openshell.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages