From 5c84d9e98a99bc55ab2b0b0eb2d883aa07dd300e Mon Sep 17 00:00:00 2001 From: Neil Date: Mon, 9 Nov 2026 05:04:46 -0800 Subject: [PATCH] fix(sandbox-control): node on terminal PATH (npm agent CLIs like codex failed with env: node), workspace name from the repo URL and lazy default branch (first-boot clone is async) --- .../image/vm-scripts.generated.ts | 2 +- .../image/vm/orca-sandbox-start | 20 +++++++++++-------- .../image/vm/orca-session-env.sh | 9 ++++++++- 3 files changed, 21 insertions(+), 10 deletions(-) diff --git a/cloud/apps/sandbox-control/image/vm-scripts.generated.ts b/cloud/apps/sandbox-control/image/vm-scripts.generated.ts index 4ff8d5ded0d..0809d618130 100644 --- a/cloud/apps/sandbox-control/image/vm-scripts.generated.ts +++ b/cloud/apps/sandbox-control/image/vm-scripts.generated.ts @@ -3,4 +3,4 @@ import type { BuildInputs, GoldenImageManifest } from './golden-image' export const MANIFEST: GoldenImageManifest = {"imageVersion":"2026-11-09.1","image":"vercel/sandbox/node:24","region":"iad1","vcpus":4,"orcad":{"bundleUrl":null,"sha256":null},"gh":{"version":"2.103.3","sha256":"4c806978ffce1cec70853302a56d70ac6da4f19bae7adca4e1a8ab00633dd91d"},"chrome":{"version":"157.0.8103.38-1","sha256":"a62b277dc03da2cff50cc04589deb66f6875be339c066d46adb49b49eedb629d"},"agentCliPackages":["@anthropic-ai/claude-code@2.1.319","@openai/codex@0.166.0"]} export const PROVISION_SCRIPT: string = "#!/usr/bin/env bash\n# Runs once inside the build sandbox (as the sandbox user, with sudo). Installs the pinned orcad\n# bundle, agent CLIs and Orca's VM entry points into /opt/orca, then smoke-tests orcad.\n# Inputs (env, from image/golden-image.manifest.json via build-golden-image.ts):\n# ORCAD_BUNDLE_URL ORCAD_BUNDLE_SHA256 [GH_VERSION GH_SHA256] [CHROME_VERSION CHROME_SHA256]\n# AGENT_NPM_PACKAGES (space-separated\n# name@version) and ORCA_VM_SCRIPT_ (base64) for each /opt/orca/bin entry point.\nset -euo pipefail\nORCA_HOME=/opt/orca\nlog() { printf '[golden] %s\\n' \"$*\" >&2; }\n\nsudo mkdir -p \"$ORCA_HOME/bin\" \"$ORCA_HOME/runtime\" \"$ORCA_HOME/tools\"\nsudo chown -R \"$(id -u):$(id -g)\" \"$ORCA_HOME\"\n\nlog 'system packages'\nif command -v apt-get >/dev/null; then\n sudo DEBIAN_FRONTEND=noninteractive apt-get update -qq\n # Developer basics agents expect (Conductor parity): rg, tmux, vim, git-lfs alongside the build tools.\n sudo DEBIAN_FRONTEND=noninteractive apt-get install -y -qq --no-install-recommends \\\n ca-certificates curl git git-lfs jq ripgrep tmux vim-nox less openssh-client build-essential python3 \\\n unzip zip util-linux procps >/dev/null\n git lfs install --system --skip-repo >/dev/null\nelif command -v dnf >/dev/null; then\n sudo dnf install -y -q ca-certificates curl-minimal git git-lfs jq tmux vim-enhanced gcc gcc-c++ make python3 \\\n unzip util-linux procps-ng >/dev/null\nelse\n log 'no supported package manager'; exit 1\nfi\n\n# Managed images have no /vercel/sandbox; the project lives at /vercel/sandbox/project on every image.\nsudo install -d -o \"$(id -u)\" -g \"$(id -g)\" /vercel/sandbox\n\nfetch_verified() { # url sha256 dest\n curl -fsSL --retry 3 -o \"$3\" \"$1\"\n echo \"$2 $3\" | sha256sum -c --quiet -\n}\n\nlog 'orcad bundle'\nbundle=\"$(mktemp -d)/orcad.tgz\"\nfetch_verified \"$ORCAD_BUNDLE_URL\" \"$ORCAD_BUNDLE_SHA256\" \"$bundle\"\ntar -xzf \"$bundle\" -C \"$ORCA_HOME/runtime\"\nrm -rf \"$(dirname \"$bundle\")\"\n# Release tarballs (orca-server-linux-*) may nest the server; normalize to runtime/orcad + runtime/runtimes.\nif [ ! -s \"$ORCA_HOME/runtime/orcad/orcad-server.js\" ]; then\n server=\"$(find \"$ORCA_HOME/runtime\" -name orcad-server.js -type f | head -n 1)\"\n [ -n \"$server\" ] || { log 'bundle has no orcad-server.js'; exit 1; }\n root=\"$(dirname \"$(dirname \"$server\")\")\"\n ln -sfn \"$(dirname \"$server\")\" \"$ORCA_HOME/runtime/orcad\"\n [ -d \"$ORCA_HOME/runtime/runtimes\" ] || ln -sfn \"$root/runtimes\" \"$ORCA_HOME/runtime/runtimes\"\nfi\ntest -s \"$ORCA_HOME/runtime/orcad/orcad-server.js\"\nnode=\"$(find \"$ORCA_HOME/runtime/runtimes\" -type f -name node | head -n 1)\"\ntest -x \"$node\"\n\nif [ -n \"${GH_VERSION:-}\" ]; then\n log \"gh $GH_VERSION\"\n fetch_verified \"https://github.com/cli/cli/releases/download/v${GH_VERSION}/gh_${GH_VERSION}_linux_amd64.tar.gz\" \\\n \"$GH_SHA256\" /tmp/gh.tgz\n tar -xzf /tmp/gh.tgz -C /tmp\n mkdir -p \"$ORCA_HOME/tools/bin\"\n install -m 0755 \"/tmp/gh_${GH_VERSION}_linux_amd64/bin/gh\" \"$ORCA_HOME/tools/bin/gh\"\n rm -rf /tmp/gh.tgz \"/tmp/gh_${GH_VERSION}_linux_amd64\"\nfi\n\nif [ -n \"${CHROME_VERSION:-}\" ] && command -v apt-get >/dev/null; then\n log \"google-chrome $CHROME_VERSION (headless browser for agents and Orca browser automation)\"\n deb=\"$(mktemp -d)/chrome.deb\"\n fetch_verified \"https://dl.google.com/linux/chrome/deb/pool/main/g/google-chrome-stable/google-chrome-stable_${CHROME_VERSION}_amd64.deb\" \\\n \"$CHROME_SHA256\" \"$deb\"\n sudo DEBIAN_FRONTEND=noninteractive apt-get install -y -qq --no-install-recommends \"$deb\" >/dev/null\n rm -rf \"$(dirname \"$deb\")\"\n # The .deb adds Google's apt source; the image stays pinned, so drop it.\n sudo rm -f /etc/apt/sources.list.d/google-chrome.list /etc/cron.daily/google-chrome\n google-chrome --headless=new --no-sandbox --disable-gpu --dump-dom 'data:text/html,

ok

' 2>/dev/null | grep -q '

ok

'\nfi\n\nlog 'agent CLIs'\n# The image's system npm installs them; agents later run on whatever node orcad puts on PATH.\n# shellcheck disable=SC2086 # word splitting is the package list\nnpm install --global --no-fund --no-audit --prefix \"$ORCA_HOME/tools\" $AGENT_NPM_PACKAGES\n\nlog 'Orca entry points'\nfor name in orca-sandbox-start orca-sandbox-drain orca-sandbox-env orca-session-env.sh orca-egress-probe orca-image-wipe; do\n var=\"ORCA_VM_SCRIPT_${name//[-.]/_}\"\n printf '%s' \"${!var}\" | base64 -d >\"$ORCA_HOME/bin/$name\"\n chmod 0755 \"$ORCA_HOME/bin/$name\"\ndone\n# The orca CLI (agents drive worktrees, terminals and the browser through it) talks to this VM's orcad.\ncli=\"$(find \"$ORCA_HOME/runtime\" -name orca-cli.cjs -type f | head -n 1)\"\nif [ -n \"$cli\" ]; then\n printf '#!/usr/bin/env bash\\nexport ORCA_USER_DATA_PATH=\"${ORCA_USER_DATA_PATH:-$HOME/.orcad-data}\"\\nexec \"%s\" \"%s\" \"$@\"\\n' \"$node\" \"$cli\" >\"$ORCA_HOME/bin/orca\"\n chmod 0755 \"$ORCA_HOME/bin/orca\"\nelse\n log 'bundle has no orca CLI (orca-cli.cjs): agents will not have `orca` on PATH'\nfi\nprintf 'export PATH=%s/tools/bin:%s/bin:$PATH\\n' \"$ORCA_HOME\" \"$ORCA_HOME\" | sudo tee /etc/profile.d/orca.sh >/dev/null\n# Every new shell (login or not: /etc/bashrc sources profile.d) re-reads the session env file.\nsudo install -m 0644 \"$ORCA_HOME/bin/orca-session-env.sh\" /etc/profile.d/zz-orca-session-env.sh\nif [ -f /etc/bash.bashrc ]; then\n # Ubuntu's bash.bashrc does not source profile.d for non-login interactive shells (AL2023's bashrc does).\n grep -q zz-orca-session-env /etc/bash.bashrc ||\n echo '[ -r /etc/profile.d/zz-orca-session-env.sh ] && . /etc/profile.d/zz-orca-session-env.sh' | sudo tee -a /etc/bash.bashrc >/dev/null\nfi\nsudo chown -R root:root \"$ORCA_HOME\"\n\nlog 'smoke: orcad reaches ready on loopback (and warms the V8 compile cache: ~1.5 s off every boot)'\nsudo install -d -o \"$(id -u)\" -g \"$(id -g)\" \"$ORCA_HOME/compile-cache\"\nsmoke_data=\"$(mktemp -d)\"\nNODE_COMPILE_CACHE=\"$ORCA_HOME/compile-cache\" ORCA_USER_DATA=\"$smoke_data\" timeout 60 \"$node\" \"$ORCA_HOME/runtime/orcad/orcad-server.js\" \\\n --port 6799 --json >/tmp/orcad-smoke.out 2>/tmp/orcad-smoke.err &\nsmoke_pid=$!\nready=0\nfor _ in $(seq 1 600); do\n if grep -q '\"orca_server_ready\"' /tmp/orcad-smoke.out 2>/dev/null; then ready=1; break; fi\n sleep 0.1\ndone\nkill \"$smoke_pid\" 2>/dev/null || true\nwait \"$smoke_pid\" 2>/dev/null || true\nrm -rf \"$smoke_data\" /tmp/orcad-smoke.out\nif [ \"$ready\" != 1 ]; then\n tail -n 40 /tmp/orcad-smoke.err >&2; exit 1\nfi\n\nlog 'versions'\nversions=\"{}\"\nfor bin in claude codex opencode gemini gh; do\n if [ -x \"$ORCA_HOME/tools/bin/$bin\" ]; then\n v=\"$(\"$ORCA_HOME/tools/bin/$bin\" --version 2>/dev/null | head -n 1 || echo unknown)\"\n versions=\"$(jq -c --arg k \"$bin\" --arg v \"$v\" '. + {($k): $v}' <<<\"$versions\")\"\n fi\ndone\nfor sys in rg vim git-lfs google-chrome; do\n if command -v \"$sys\" >/dev/null; then\n v=\"$(\"$sys\" --version 2>/dev/null | head -n 1 || echo unknown)\"\n versions=\"$(jq -c --arg k \"$sys\" --arg v \"$v\" '. + {($k): $v}' <<<\"$versions\")\"\n fi\ndone\ncommand -v tmux >/dev/null && versions=\"$(jq -c --arg v \"$(tmux -V)\" '. + {tmux: $v}' <<<\"$versions\")\"\n[ -x \"$ORCA_HOME/bin/orca\" ] && versions=\"$(jq -c '. + {orca: \"bundled\"}' <<<\"$versions\")\"\njq -c --arg node \"$(\"$node\" --version)\" --arg disk \"$(df -h / | awk 'NR==2{print $2}')\" \\\n '{node: $node, disk: $disk, tools: .}' <<<\"$versions\"\n" -export const VM_SCRIPT_SOURCES: BuildInputs['vmScripts'] = {"orca-sandbox-start":"#!/usr/bin/env bash\n# Starts orcad for this boot (idempotent) and prints its recipe line:\n# {\"schemaVersion\":1,\"pairingCode\":\"orca://pair?...\",\"projectRoot\":\"/vercel/sandbox/project\"}\n# Env from the control plane: ORCA_SANDBOX_ID, ORCA_PUBLIC_WSS, optional ORCA_REPO_URL/ORCA_REPO_REF,\n# ORCA_GITHUB_PLACEHOLDER, ORCA_SECRET_ENV (`NAME=` lines: org/personal/repo secrets for\n# orcad and its agents). Never prints anything else on stdout's last line.\n# With ORCA_READY_VIA=file it prints {\"schemaVersion\":1,\"readyFile\":\"\"} instead and the control\n# plane reads the offer with the files API: the provider keeps command stdout as logs, not files.\n# Managed env for every process orcad starts: ORCA_IS_CLOUD=1, ORCA_SANDBOX_ID, ORCA_ROOT_PATH,\n# ORCA_WORKTREE_PATH, ORCA_WORKSPACE_PATH, ORCA_WORKSPACE_NAME, ORCA_DEFAULT_BRANCH, ORCA_PORT and the\n# CONDUCTOR_* names imported Conductor scripts read.\nset -euo pipefail\numask 077\nORCA_HOME=/opt/orca\nSTATE=\"$HOME/.orca-sandbox\"\nPROJECT=/vercel/sandbox/project\nmkdir -p \"$STATE\"\nexec 9>\"$STATE/start.lock\"\nflock -w 60 9\n# Background children close fd 9 (`9>&-`): an orcad that inherited it would hold the lock for its whole\n# life and every later start (resume of a running sandbox, a retried create) would wait out the 60 s.\n\nprint_ready() {\n if [ \"${ORCA_READY_VIA:-}\" = file ]; then\n printf '{\"schemaVersion\":1,\"readyFile\":\"%s\"}\\n' \"$STATE/ready.json\"\n else\n tail -n 1 \"$STATE/ready.json\"\n fi\n}\n# Command stderr is kept by the provider: strip injected values and credential shapes from log tails.\nredact_log() {\n local text kv value\n text=\"$(cat)\"\n for kv in ${agent_env[@]+\"${agent_env[@]}\"}; do\n value=\"${kv#*=}\"\n if [ \"${#value}\" -ge 8 ]; then\n text=\"${text//\"$value\"/[REDACTED]}\"\n fi\n done\n printf '%s\\n' \"$text\" | sed -E \\\n -e 's#orca://[^[:space:]]+#[REDACTED_PAIRING]#g' \\\n -e 's#(gh[pousr]_|github_pat_|sk-|vc[kpiar]_)[A-Za-z0-9._-]{16,}#[REDACTED_TOKEN]#g' \\\n -e 's#eyJ[A-Za-z0-9_-]{8,}\\.[A-Za-z0-9_-]{8,}\\.[A-Za-z0-9_-]{8,}#[REDACTED_JWT]#g' \\\n -e 's#(Authorization|authorization):[[:space:]]*[A-Za-z]+[[:space:]]+[^[:space:]]+#\\1: [REDACTED]#g'\n}\n\nboot_id=\"$(cat /proc/sys/kernel/random/boot_id)\"\nif [ \"$(cat \"$STATE/boot-id\" 2>/dev/null || true)\" = \"$boot_id\" ] && [ -s \"$STATE/ready.json\" ] \\\n && kill -0 \"$(cat \"$STATE/orcad.pid\" 2>/dev/null || echo 0)\" 2>/dev/null; then\n print_ready\n exit 0\nfi\n# Resume = reboot from disk: locks and pids from the previous session name dead processes.\nrm -f \"$STATE/ready.json\" \"$STATE/orcad.pid\"\nprintf '%s' \"$boot_id\" >\"$STATE/boot-id\"\n\nif [ -n \"${ORCA_GIT_EXTRAHEADER:-}\" ]; then\n # Every github.com request carries the placeholder; the egress policy swaps in a 1h repo token.\n git config --global http.https://github.com/.extraheader \"$ORCA_GIT_EXTRAHEADER\"\nfi\n# First boot clones while orcad starts (orcad only needs the directory to exist); ready waits for both.\nclone_pid=''\nif [ ! -d \"$PROJECT\" ]; then\n mkdir -p \"$PROJECT\"\n if [ -n \"${ORCA_REPO_URL:-}\" ]; then\n git clone --quiet ${ORCA_REPO_REF:+--branch \"$ORCA_REPO_REF\"} -- \"$ORCA_REPO_URL\" \"$PROJECT\" 2>\"$STATE/clone.log\" 9>&- &\n clone_pid=$!\n fi\nfi\n\nworkspace_name=\"$(basename \"$(git -C \"$PROJECT\" remote get-url origin 2>/dev/null || echo \"$PROJECT\")\" .git)\"\ndefault_branch=\"$(git -C \"$PROJECT\" symbolic-ref --quiet --short refs/remotes/origin/HEAD 2>/dev/null || true)\"\ndefault_branch=\"${default_branch#origin/}\"\nif [ -z \"$default_branch\" ]; then\n default_branch=\"$(git -C \"$PROJECT\" branch --show-current 2>/dev/null || true)\"\nfi\n# One workspace per VM: its preview ports start at 3000 (Conductor allots CONDUCTOR_PORT..+9).\nmanaged_env=(\n ORCA_IS_CLOUD=1\n ORCA_ROOT_PATH=\"$PROJECT\" ORCA_WORKTREE_PATH=\"$PROJECT\" ORCA_WORKSPACE_PATH=\"$PROJECT\"\n ORCA_WORKSPACE_NAME=\"$workspace_name\" ORCA_DEFAULT_BRANCH=\"${default_branch:-main}\" ORCA_PORT=3000\n CONDUCTOR_IS_CLOUD=1\n CONDUCTOR_ROOT_PATH=\"$PROJECT\" CONDUCTOR_WORKSPACE_PATH=\"$PROJECT\"\n CONDUCTOR_WORKSPACE_NAME=\"$workspace_name\" CONDUCTOR_DEFAULT_BRANCH=\"${default_branch:-main}\" CONDUCTOR_PORT=3000\n)\n\nagent_env=()\nif command -v google-chrome >/dev/null; then\n # Orca's browser automation and agent browser tools use the image's headless Chrome.\n agent_env+=(\"ORCA_BROWSER_EXECUTABLE=$(command -v google-chrome)\")\nfi\nif [ -n \"${ORCA_GITHUB_PLACEHOLDER:-}\" ]; then\n # The egress layer swaps the placeholder for a short-lived GitHub token; none lives in the VM.\n agent_env+=(\"GH_TOKEN=$ORCA_GITHUB_PLACEHOLDER\" \"GITHUB_TOKEN=$ORCA_GITHUB_PLACEHOLDER\")\nfi\n\nsecret_names=()\nif [ -n \"${ORCA_SECRET_ENV:-}\" ]; then\n # Secrets live only in memory: orcad's process env and a tmpfs file new shells re-read\n # (orca-sandbox-env), never under $HOME or the project, so the stop snapshot never carries them.\n # Resolved per start (and per edit) by the control plane: no image rebuild, no restart.\n while IFS= read -r line; do\n name=\"${line%%=*}\" b64=\"${line#*=}\" # not IFS='=': read would strip base64 padding\n case \"$name\" in '' | *[!A-Z0-9_]* | [0-9]* | ORCA_* | PATH | HOME | LD_* | DYLD_*) continue ;; esac\n agent_env+=(\"$name=$(printf '%s' \"$b64\" | base64 -d)\")\n secret_names+=(\"$name\")\n done < <(printf '%s\\n' \"$ORCA_SECRET_ENV\") # a pipe, not a here-string temp file\nfi\n# Always rewrite the file, so a resume with fewer secrets drops the old names too.\n\"$ORCA_HOME/bin/orca-sandbox-env\" >/dev/null || echo 'session env file unavailable; secrets apply at boot only' >&2\nunset ORCA_SECRET_ENV\n\nnode=\"$(find \"$ORCA_HOME/runtime/runtimes\" -type f -name node | head -n 1)\"\nenv -i HOME=\"$HOME\" USER=\"$(id -un)\" LANG=C.UTF-8 TERM=xterm-256color \\\n PATH=\"$ORCA_HOME/tools/bin:$ORCA_HOME/bin:/usr/local/bin:/usr/bin:/bin\" \\\n ORCA_USER_DATA=\"$HOME/.orcad-data\" ORCA_SANDBOX_ID=\"$ORCA_SANDBOX_ID\" \"${managed_env[@]}\" \\\n NODE_COMPILE_CACHE=\"$ORCA_HOME/compile-cache\" \\\n ORCA_SESSION_ENV_NAMES=\"${secret_names[*]}\" \"${agent_env[@]}\" \\\n setsid nohup \"$node\" \"$ORCA_HOME/runtime/orcad/orcad-server.js\" \\\n --port 6768 --bind 0.0.0.0 --pairing-address \"$ORCA_PUBLIC_WSS\" \\\n --recipe-json --project-root \"$PROJECT\" \\\n >\"$STATE/ready.json\" 2>\"$STATE/orcad.log\" &- &\necho $! >\"$STATE/orcad.pid\"\n\nfor _ in $(seq 1 1200); do\n if grep -q '\"pairingCode\"' \"$STATE/ready.json\" 2>/dev/null; then\n if [ -n \"$clone_pid\" ] && ! wait \"$clone_pid\"; then\n echo 'repository clone failed' >&2\n tail -n 5 \"$STATE/clone.log\" | redact_log >&2\n exit 1\n fi\n print_ready\n exit 0\n fi\n if ! kill -0 \"$(cat \"$STATE/orcad.pid\")\" 2>/dev/null; then\n echo 'orcad exited before ready' >&2\n tail -n 40 \"$STATE/orcad.log\" | redact_log >&2\n exit 1\n fi\n sleep 0.05\ndone\necho 'orcad start timed out' >&2\nexit 1\n","orca-sandbox-drain":"#!/usr/bin/env bash\n# Asks orcad to quit before the VM stops: its shutdown turns working agent chats into durable\n# restart offers and flushes terminal restore hints, which the desktop continues after resume.\nset -euo pipefail\nSTATE=\"$HOME/.orca-sandbox\"\nWAIT_SECONDS=\"${1:-280}\"\npid=\"$(cat \"$STATE/orcad.pid\" 2>/dev/null || true)\"\nif [ -z \"$pid\" ] || ! kill -0 \"$pid\" 2>/dev/null; then\n exit 0\nfi\nkill -TERM \"$pid\"\nfor _ in $(seq 1 $((WAIT_SECONDS * 10))); do\n if ! kill -0 \"$pid\" 2>/dev/null; then\n rm -f \"$STATE/orcad.pid\" \"$STATE/ready.json\"\n exit 0\n fi\n sleep 0.1\ndone\n# Leave it running: the provider stop ends it, and the drain already had its window.\necho 'orcad still draining at deadline' >&2\nexit 0\n","orca-sandbox-env":"#!/usr/bin/env bash\n# Writes this session's secret env where new shells and agent launches read it (orca-session-env.sh).\n# Input: ORCA_SECRET_ENV (`NAME=` lines) from the control plane, at boot (orca-sandbox-start)\n# and whenever a secret changes (POST /v1/sandboxes/:id/env/refresh). The file lives on tmpfs only:\n# never on the sandbox disk, so the stop snapshot never carries it. Prints {\"names\":N} on success.\n# Also keeps Codex pointed at a configured OpenAI-compatible provider (OPENAI_BASE_URL), since Codex\n# does not read that variable: a managed block in ~/.codex/config.toml naming only the env var.\nset -euo pipefail\numask 077\nHOME=\"${HOME:-$(getent passwd \"$(id -u)\" | cut -d: -f6)}\"\ndir=\"/dev/shm/orca-env-$(id -u)\"\nif [ \"$(stat -f -c %T /dev/shm 2>/dev/null || true)\" != tmpfs ]; then\n # No memory-only place to keep it: refuse rather than write secrets to disk.\n echo 'no tmpfs for the session env' >&2\n exit 3\nfi\nmkdir -p \"$dir\"\nchmod 700 \"$dir\"\n# Writes are ordered by the control plane's resolve time: an older refresh that lost a race is dropped.\ngeneration=\"${ORCA_ENV_GENERATION:-0}\"\ncase \"$generation\" in '' | *[!0-9]*) generation=0 ;; esac\ncurrent=\"$(cat \"$dir/generation\" 2>/dev/null || echo 0)\"\ncase \"$current\" in '' | *[!0-9]*) current=0 ;; esac\nif [ \"$generation\" -lt \"$current\" ]; then\n printf '{\"stale\":true}\\n'\n exit 0\nfi\ntmp=\"$(mktemp \"$dir/.session.env.XXXXXX\")\"\ncount=0\nopenai_base_url=''\nopenai_model=''\nwhile IFS= read -r line; do\n name=\"${line%%=*}\" b64=\"${line#*=}\"\n case \"$name\" in '' | *[!A-Z0-9_]* | [0-9]* | ORCA_* | PATH | HOME | LD_* | DYLD_*) continue ;; esac\n # Values stay base64 in the file; the shell hook decodes them.\n printf '%s=%s\\n' \"$name\" \"$b64\" >>\"$tmp\"\n count=$((count + 1))\n case \"$name\" in\n OPENAI_BASE_URL) openai_base_url=\"$(printf '%s' \"$b64\" | base64 -d)\" ;;\n OPENAI_MODEL) openai_model=\"$(printf '%s' \"$b64\" | base64 -d)\" ;;\n esac\ndone < <(printf '%s\\n' \"${ORCA_SECRET_ENV:-}\")\nmv -f \"$tmp\" \"$dir/session.env\"\nprintf '%s' \"$generation\" >\"$dir/generation\"\n\n# ---- Codex provider block (no secret values: env_key names the variable) ----\ncodex_config=\"$HOME/.codex/config.toml\"\ntoml_string() { # TOML basic string; the control plane only accepts https URLs and plain model ids\n local value=\"${1//\\\\/\\\\\\\\}\"\n printf '\"%s\"' \"${value//\\\"/\\\\\\\"}\"\n}\nstrip_managed() { # drop both managed blocks\n awk '/^# >>> orca-cloud managed/{skip=1} !skip{print} /^# <<< orca-cloud managed/{skip=0}' \"$1\"\n}\nif [ -n \"$openai_base_url\" ] || grep -q '^# >>> orca-cloud managed' \"$codex_config\" 2>/dev/null; then\n mkdir -p \"$HOME/.codex\"\n user_part=\"$(mktemp \"$HOME/.codex/.config.XXXXXX\")\"\n if [ -f \"$codex_config\" ]; then strip_managed \"$codex_config\" >\"$user_part\"; fi\n next=\"$(mktemp \"$HOME/.codex/.config.XXXXXX\")\"\n openai_host=\"${openai_base_url#https://}\"\n openai_host=\"${openai_host%%/*}\"\n if [ -n \"$openai_base_url\" ] && [ \"$openai_host\" != api.openai.com ]; then\n # Top-level keys must precede every table; skip ours if the user already chose a provider/model.\n head_keys=\"$(awk '/^\\[/{exit} {print}' \"$user_part\")\"\n {\n printf '# >>> orca-cloud managed (Orca Cloud provider; edit in Orca settings)\\n'\n if ! grep -Eq '^[[:space:]]*model_provider[[:space:]]*=' <<<\"$head_keys\"; then\n printf 'model_provider = \"orca_cloud\"\\n'\n fi\n if [ -n \"$openai_model\" ] && ! grep -Eq '^[[:space:]]*model[[:space:]]*=' <<<\"$head_keys\"; then\n printf 'model = %s\\n' \"$(toml_string \"$openai_model\")\"\n fi\n printf '# <<< orca-cloud managed\\n'\n cat \"$user_part\"\n printf '\\n# >>> orca-cloud managed provider\\n[model_providers.orca_cloud]\\nname = \"Orca Cloud\"\\n'\n printf 'base_url = %s\\nenv_key = \"OPENAI_API_KEY\"\\nwire_api = \"responses\"\\n' \"$(toml_string \"$openai_base_url\")\"\n printf '# <<< orca-cloud managed\\n'\n } >\"$next\"\n else\n cat \"$user_part\" >\"$next\"\n fi\n chmod 600 \"$next\"\n mv -f \"$next\" \"$codex_config\"\n rm -f \"$user_part\"\nfi\nprintf '{\"names\":%d}\\n' \"$count\"\n","orca-session-env.sh":"# Orca Cloud: sourced by every new bash/zsh (via /etc/profile.d) so terminals and agents started\n# after a secret edit get the current values with no restart. The file is written by\n# /opt/orca/bin/orca-sandbox-env on tmpfs; names it no longer lists are unset. Cloud secrets win\n# over same-named values already in the environment.\n__orca_env_file=\"/dev/shm/orca-env-$(id -u 2>/dev/null)/session.env\"\nif [ -r \"$__orca_env_file\" ]; then\n # $(...) word-splits in both bash and zsh.\n for __orca_n in $(printf '%s' \"${ORCA_SESSION_ENV_NAMES-}\"); do\n unset \"$__orca_n\" 2>/dev/null || true\n done\n __orca_names=''\n while IFS= read -r __orca_line || [ -n \"$__orca_line\" ]; do\n __orca_n=\"${__orca_line%%=*}\"\n case \"$__orca_n\" in '' | *[!A-Z0-9_]* | [0-9]* | ORCA_* | PATH | HOME | LD_* | DYLD_*) continue ;; esac\n export \"$__orca_n=$(printf '%s' \"${__orca_line#*=}\" | base64 -d 2>/dev/null)\"\n __orca_names=\"$__orca_names $__orca_n\"\n done <\"$__orca_env_file\"\n export ORCA_SESSION_ENV_NAMES=\"${__orca_names# }\"\nfi\nunset __orca_env_file __orca_line __orca_n __orca_names\n","orca-egress-probe":"#!/usr/bin/env bash\n# Bounded Node fetch against each credential host. Vercel transform rules have made undici hang on\n# response bodies (no content-length / transfer-encoding); report it instead of letting agents hang.\n# Prints one JSON line: {\"host\":\"api.github.com\",\"status\":\"ok\"|\"egress_framing_hang\"|\"error\"}.\nset -euo pipefail\nnode=\"$(find /opt/orca/runtime/runtimes -type f -name node | head -n 1)\"\nfor host in \"$@\"; do\n \"$node\" -e '\n const host = process.argv[1]\n const timer = setTimeout(() => { console.log(JSON.stringify({ host, status: \"egress_framing_hang\" })); process.exit(0) }, 8000)\n fetch(`https://${host}/`, { headers: { authorization: process.env.ORCA_PROBE_AUTHORIZATION ?? \"\" } })\n .then((r) => r.arrayBuffer())\n .then(() => { clearTimeout(timer); console.log(JSON.stringify({ host, status: \"ok\" })) })\n .catch(() => { clearTimeout(timer); console.log(JSON.stringify({ host, status: \"error\" })) })\n ' \"$host\"\ndone\n","orca-image-wipe":"#!/usr/bin/env bash\n# Runs last before the golden snapshot. Every sandbox cloned from the image must start without an\n# orcad identity, device registry, agent sign-in or shell history (clone-safe identity, risk #1).\nset -euo pipefail\npkill -f orcad-server.js 2>/dev/null || true\nfor path in \\\n \"$HOME/.orcad-data\" \"$HOME/.orca-sandbox\" \"$HOME/.config/orca\" \"$HOME/.config/orca-dev\" \\\n \"$HOME/.claude\" \"$HOME/.claude.json\" \"$HOME/.codex\" \"$HOME/.config/gh\" \"$HOME/.gemini\" \\\n \"$HOME/.local/share/opencode\" \"$HOME/.npm\" \"$HOME/.cache\" \"$HOME/.config/google-chrome\" \"$HOME/.bash_history\" \\\n /vercel/sandbox/project; do\n rm -rf -- \"$path\"\ndone\nsudo rm -rf /tmp/* /var/tmp/* 2>/dev/null || true\nfor leftover in \"$HOME/.orcad-data\" \"$HOME/.claude\" \"$HOME/.codex\" \"$HOME/.config/gh\"; do\n if [ -e \"$leftover\" ]; then\n echo \"identity survived wipe: $leftover\" >&2\n exit 1\n fi\ndone\necho '{\"wiped\":true}'\n"} +export const VM_SCRIPT_SOURCES: BuildInputs['vmScripts'] = {"orca-sandbox-start":"#!/usr/bin/env bash\n# Starts orcad for this boot (idempotent) and prints its recipe line:\n# {\"schemaVersion\":1,\"pairingCode\":\"orca://pair?...\",\"projectRoot\":\"/vercel/sandbox/project\"}\n# Env from the control plane: ORCA_SANDBOX_ID, ORCA_PUBLIC_WSS, optional ORCA_REPO_URL/ORCA_REPO_REF,\n# ORCA_GITHUB_PLACEHOLDER, ORCA_SECRET_ENV (`NAME=` lines: org/personal/repo secrets for\n# orcad and its agents). Never prints anything else on stdout's last line.\n# With ORCA_READY_VIA=file it prints {\"schemaVersion\":1,\"readyFile\":\"\"} instead and the control\n# plane reads the offer with the files API: the provider keeps command stdout as logs, not files.\n# Managed env for every process orcad starts: ORCA_IS_CLOUD=1, ORCA_SANDBOX_ID, ORCA_ROOT_PATH,\n# ORCA_WORKTREE_PATH, ORCA_WORKSPACE_PATH, ORCA_WORKSPACE_NAME, ORCA_DEFAULT_BRANCH, ORCA_PORT and the\n# CONDUCTOR_* names imported Conductor scripts read.\nset -euo pipefail\numask 077\nORCA_HOME=/opt/orca\nSTATE=\"$HOME/.orca-sandbox\"\nPROJECT=/vercel/sandbox/project\nmkdir -p \"$STATE\"\nexec 9>\"$STATE/start.lock\"\nflock -w 60 9\n# Background children close fd 9 (`9>&-`): an orcad that inherited it would hold the lock for its whole\n# life and every later start (resume of a running sandbox, a retried create) would wait out the 60 s.\n\nprint_ready() {\n if [ \"${ORCA_READY_VIA:-}\" = file ]; then\n printf '{\"schemaVersion\":1,\"readyFile\":\"%s\"}\\n' \"$STATE/ready.json\"\n else\n tail -n 1 \"$STATE/ready.json\"\n fi\n}\n# Command stderr is kept by the provider: strip injected values and credential shapes from log tails.\nredact_log() {\n local text kv value\n text=\"$(cat)\"\n for kv in ${agent_env[@]+\"${agent_env[@]}\"}; do\n value=\"${kv#*=}\"\n if [ \"${#value}\" -ge 8 ]; then\n text=\"${text//\"$value\"/[REDACTED]}\"\n fi\n done\n printf '%s\\n' \"$text\" | sed -E \\\n -e 's#orca://[^[:space:]]+#[REDACTED_PAIRING]#g' \\\n -e 's#(gh[pousr]_|github_pat_|sk-|vc[kpiar]_)[A-Za-z0-9._-]{16,}#[REDACTED_TOKEN]#g' \\\n -e 's#eyJ[A-Za-z0-9_-]{8,}\\.[A-Za-z0-9_-]{8,}\\.[A-Za-z0-9_-]{8,}#[REDACTED_JWT]#g' \\\n -e 's#(Authorization|authorization):[[:space:]]*[A-Za-z]+[[:space:]]+[^[:space:]]+#\\1: [REDACTED]#g'\n}\n\nboot_id=\"$(cat /proc/sys/kernel/random/boot_id)\"\nif [ \"$(cat \"$STATE/boot-id\" 2>/dev/null || true)\" = \"$boot_id\" ] && [ -s \"$STATE/ready.json\" ] \\\n && kill -0 \"$(cat \"$STATE/orcad.pid\" 2>/dev/null || echo 0)\" 2>/dev/null; then\n print_ready\n exit 0\nfi\n# Resume = reboot from disk: locks and pids from the previous session name dead processes.\nrm -f \"$STATE/ready.json\" \"$STATE/orcad.pid\"\nprintf '%s' \"$boot_id\" >\"$STATE/boot-id\"\n\nif [ -n \"${ORCA_GIT_EXTRAHEADER:-}\" ]; then\n # Every github.com request carries the placeholder; the egress policy swaps in a 1h repo token.\n git config --global http.https://github.com/.extraheader \"$ORCA_GIT_EXTRAHEADER\"\nfi\n# First boot clones while orcad starts (orcad only needs the directory to exist); ready waits for both.\nclone_pid=''\nif [ ! -d \"$PROJECT\" ]; then\n mkdir -p \"$PROJECT\"\n if [ -n \"${ORCA_REPO_URL:-}\" ]; then\n git clone --quiet ${ORCA_REPO_REF:+--branch \"$ORCA_REPO_REF\"} -- \"$ORCA_REPO_URL\" \"$PROJECT\" 2>\"$STATE/clone.log\" 9>&- &\n clone_pid=$!\n fi\nfi\n\n# The clone may still be running here, so the name comes from the URL; the default branch is\n# filled in lazily by the session-env shell hook once the clone exists.\nif [ -n \"${ORCA_REPO_URL:-}\" ]; then\n workspace_name=\"$(basename \"${ORCA_REPO_URL%/}\" .git)\"\nelse\n workspace_name=\"$(basename \"$(git -C \"$PROJECT\" remote get-url origin 2>/dev/null || echo \"$PROJECT\")\" .git)\"\nfi\n# One workspace per VM: its preview ports start at 3000 (Conductor allots CONDUCTOR_PORT..+9).\nmanaged_env=(\n ORCA_IS_CLOUD=1\n ORCA_ROOT_PATH=\"$PROJECT\" ORCA_WORKTREE_PATH=\"$PROJECT\" ORCA_WORKSPACE_PATH=\"$PROJECT\"\n ORCA_WORKSPACE_NAME=\"$workspace_name\" ORCA_PORT=3000\n CONDUCTOR_IS_CLOUD=1\n CONDUCTOR_ROOT_PATH=\"$PROJECT\" CONDUCTOR_WORKSPACE_PATH=\"$PROJECT\"\n CONDUCTOR_WORKSPACE_NAME=\"$workspace_name\" CONDUCTOR_PORT=3000\n)\n\nagent_env=()\nif command -v google-chrome >/dev/null; then\n # Orca's browser automation and agent browser tools use the image's headless Chrome.\n agent_env+=(\"ORCA_BROWSER_EXECUTABLE=$(command -v google-chrome)\")\nfi\nif [ -n \"${ORCA_GITHUB_PLACEHOLDER:-}\" ]; then\n # The egress layer swaps the placeholder for a short-lived GitHub token; none lives in the VM.\n agent_env+=(\"GH_TOKEN=$ORCA_GITHUB_PLACEHOLDER\" \"GITHUB_TOKEN=$ORCA_GITHUB_PLACEHOLDER\")\nfi\n\nsecret_names=()\nif [ -n \"${ORCA_SECRET_ENV:-}\" ]; then\n # Secrets live only in memory: orcad's process env and a tmpfs file new shells re-read\n # (orca-sandbox-env), never under $HOME or the project, so the stop snapshot never carries them.\n # Resolved per start (and per edit) by the control plane: no image rebuild, no restart.\n while IFS= read -r line; do\n name=\"${line%%=*}\" b64=\"${line#*=}\" # not IFS='=': read would strip base64 padding\n case \"$name\" in '' | *[!A-Z0-9_]* | [0-9]* | ORCA_* | PATH | HOME | LD_* | DYLD_*) continue ;; esac\n agent_env+=(\"$name=$(printf '%s' \"$b64\" | base64 -d)\")\n secret_names+=(\"$name\")\n done < <(printf '%s\\n' \"$ORCA_SECRET_ENV\") # a pipe, not a here-string temp file\nfi\n# Always rewrite the file, so a resume with fewer secrets drops the old names too.\n\"$ORCA_HOME/bin/orca-sandbox-env\" >/dev/null || echo 'session env file unavailable; secrets apply at boot only' >&2\nunset ORCA_SECRET_ENV\n\nnode=\"$(find \"$ORCA_HOME/runtime/runtimes\" -type f -name node | head -n 1)\"\n# Agent CLIs installed with the image's npm (codex, gemini, ...) are `#!/usr/bin/env node` scripts:\n# terminals need that node on PATH, not only orcad's bundled runtime.\nsystem_node=\"$(command -v node 2>/dev/null || echo \"$node\")\"\nenv -i HOME=\"$HOME\" USER=\"$(id -un)\" LANG=C.UTF-8 TERM=xterm-256color \\\n PATH=\"$ORCA_HOME/tools/bin:$ORCA_HOME/bin:$(dirname \"$system_node\"):/usr/local/bin:/usr/bin:/bin\" \\\n ORCA_USER_DATA=\"$HOME/.orcad-data\" ORCA_SANDBOX_ID=\"$ORCA_SANDBOX_ID\" \"${managed_env[@]}\" \\\n NODE_COMPILE_CACHE=\"$ORCA_HOME/compile-cache\" \\\n ORCA_SESSION_ENV_NAMES=\"${secret_names[*]}\" \"${agent_env[@]}\" \\\n setsid nohup \"$node\" \"$ORCA_HOME/runtime/orcad/orcad-server.js\" \\\n --port 6768 --bind 0.0.0.0 --pairing-address \"$ORCA_PUBLIC_WSS\" \\\n --recipe-json --project-root \"$PROJECT\" \\\n >\"$STATE/ready.json\" 2>\"$STATE/orcad.log\" &- &\necho $! >\"$STATE/orcad.pid\"\n\nfor _ in $(seq 1 1200); do\n if grep -q '\"pairingCode\"' \"$STATE/ready.json\" 2>/dev/null; then\n if [ -n \"$clone_pid\" ] && ! wait \"$clone_pid\"; then\n echo 'repository clone failed' >&2\n tail -n 5 \"$STATE/clone.log\" | redact_log >&2\n exit 1\n fi\n print_ready\n exit 0\n fi\n if ! kill -0 \"$(cat \"$STATE/orcad.pid\")\" 2>/dev/null; then\n echo 'orcad exited before ready' >&2\n tail -n 40 \"$STATE/orcad.log\" | redact_log >&2\n exit 1\n fi\n sleep 0.05\ndone\necho 'orcad start timed out' >&2\nexit 1\n","orca-sandbox-drain":"#!/usr/bin/env bash\n# Asks orcad to quit before the VM stops: its shutdown turns working agent chats into durable\n# restart offers and flushes terminal restore hints, which the desktop continues after resume.\nset -euo pipefail\nSTATE=\"$HOME/.orca-sandbox\"\nWAIT_SECONDS=\"${1:-280}\"\npid=\"$(cat \"$STATE/orcad.pid\" 2>/dev/null || true)\"\nif [ -z \"$pid\" ] || ! kill -0 \"$pid\" 2>/dev/null; then\n exit 0\nfi\nkill -TERM \"$pid\"\nfor _ in $(seq 1 $((WAIT_SECONDS * 10))); do\n if ! kill -0 \"$pid\" 2>/dev/null; then\n rm -f \"$STATE/orcad.pid\" \"$STATE/ready.json\"\n exit 0\n fi\n sleep 0.1\ndone\n# Leave it running: the provider stop ends it, and the drain already had its window.\necho 'orcad still draining at deadline' >&2\nexit 0\n","orca-sandbox-env":"#!/usr/bin/env bash\n# Writes this session's secret env where new shells and agent launches read it (orca-session-env.sh).\n# Input: ORCA_SECRET_ENV (`NAME=` lines) from the control plane, at boot (orca-sandbox-start)\n# and whenever a secret changes (POST /v1/sandboxes/:id/env/refresh). The file lives on tmpfs only:\n# never on the sandbox disk, so the stop snapshot never carries it. Prints {\"names\":N} on success.\n# Also keeps Codex pointed at a configured OpenAI-compatible provider (OPENAI_BASE_URL), since Codex\n# does not read that variable: a managed block in ~/.codex/config.toml naming only the env var.\nset -euo pipefail\numask 077\nHOME=\"${HOME:-$(getent passwd \"$(id -u)\" | cut -d: -f6)}\"\ndir=\"/dev/shm/orca-env-$(id -u)\"\nif [ \"$(stat -f -c %T /dev/shm 2>/dev/null || true)\" != tmpfs ]; then\n # No memory-only place to keep it: refuse rather than write secrets to disk.\n echo 'no tmpfs for the session env' >&2\n exit 3\nfi\nmkdir -p \"$dir\"\nchmod 700 \"$dir\"\n# Writes are ordered by the control plane's resolve time: an older refresh that lost a race is dropped.\ngeneration=\"${ORCA_ENV_GENERATION:-0}\"\ncase \"$generation\" in '' | *[!0-9]*) generation=0 ;; esac\ncurrent=\"$(cat \"$dir/generation\" 2>/dev/null || echo 0)\"\ncase \"$current\" in '' | *[!0-9]*) current=0 ;; esac\nif [ \"$generation\" -lt \"$current\" ]; then\n printf '{\"stale\":true}\\n'\n exit 0\nfi\ntmp=\"$(mktemp \"$dir/.session.env.XXXXXX\")\"\ncount=0\nopenai_base_url=''\nopenai_model=''\nwhile IFS= read -r line; do\n name=\"${line%%=*}\" b64=\"${line#*=}\"\n case \"$name\" in '' | *[!A-Z0-9_]* | [0-9]* | ORCA_* | PATH | HOME | LD_* | DYLD_*) continue ;; esac\n # Values stay base64 in the file; the shell hook decodes them.\n printf '%s=%s\\n' \"$name\" \"$b64\" >>\"$tmp\"\n count=$((count + 1))\n case \"$name\" in\n OPENAI_BASE_URL) openai_base_url=\"$(printf '%s' \"$b64\" | base64 -d)\" ;;\n OPENAI_MODEL) openai_model=\"$(printf '%s' \"$b64\" | base64 -d)\" ;;\n esac\ndone < <(printf '%s\\n' \"${ORCA_SECRET_ENV:-}\")\nmv -f \"$tmp\" \"$dir/session.env\"\nprintf '%s' \"$generation\" >\"$dir/generation\"\n\n# ---- Codex provider block (no secret values: env_key names the variable) ----\ncodex_config=\"$HOME/.codex/config.toml\"\ntoml_string() { # TOML basic string; the control plane only accepts https URLs and plain model ids\n local value=\"${1//\\\\/\\\\\\\\}\"\n printf '\"%s\"' \"${value//\\\"/\\\\\\\"}\"\n}\nstrip_managed() { # drop both managed blocks\n awk '/^# >>> orca-cloud managed/{skip=1} !skip{print} /^# <<< orca-cloud managed/{skip=0}' \"$1\"\n}\nif [ -n \"$openai_base_url\" ] || grep -q '^# >>> orca-cloud managed' \"$codex_config\" 2>/dev/null; then\n mkdir -p \"$HOME/.codex\"\n user_part=\"$(mktemp \"$HOME/.codex/.config.XXXXXX\")\"\n if [ -f \"$codex_config\" ]; then strip_managed \"$codex_config\" >\"$user_part\"; fi\n next=\"$(mktemp \"$HOME/.codex/.config.XXXXXX\")\"\n openai_host=\"${openai_base_url#https://}\"\n openai_host=\"${openai_host%%/*}\"\n if [ -n \"$openai_base_url\" ] && [ \"$openai_host\" != api.openai.com ]; then\n # Top-level keys must precede every table; skip ours if the user already chose a provider/model.\n head_keys=\"$(awk '/^\\[/{exit} {print}' \"$user_part\")\"\n {\n printf '# >>> orca-cloud managed (Orca Cloud provider; edit in Orca settings)\\n'\n if ! grep -Eq '^[[:space:]]*model_provider[[:space:]]*=' <<<\"$head_keys\"; then\n printf 'model_provider = \"orca_cloud\"\\n'\n fi\n if [ -n \"$openai_model\" ] && ! grep -Eq '^[[:space:]]*model[[:space:]]*=' <<<\"$head_keys\"; then\n printf 'model = %s\\n' \"$(toml_string \"$openai_model\")\"\n fi\n printf '# <<< orca-cloud managed\\n'\n cat \"$user_part\"\n printf '\\n# >>> orca-cloud managed provider\\n[model_providers.orca_cloud]\\nname = \"Orca Cloud\"\\n'\n printf 'base_url = %s\\nenv_key = \"OPENAI_API_KEY\"\\nwire_api = \"responses\"\\n' \"$(toml_string \"$openai_base_url\")\"\n printf '# <<< orca-cloud managed\\n'\n } >\"$next\"\n else\n cat \"$user_part\" >\"$next\"\n fi\n chmod 600 \"$next\"\n mv -f \"$next\" \"$codex_config\"\n rm -f \"$user_part\"\nfi\nprintf '{\"names\":%d}\\n' \"$count\"\n","orca-session-env.sh":"# Orca Cloud: sourced by every new bash/zsh (via /etc/profile.d) so terminals and agents started\n# after a secret edit get the current values with no restart. The file is written by\n# /opt/orca/bin/orca-sandbox-env on tmpfs; names it no longer lists are unset. Cloud secrets win\n# over same-named values already in the environment.\n__orca_env_file=\"/dev/shm/orca-env-$(id -u 2>/dev/null)/session.env\"\nif [ -r \"$__orca_env_file\" ]; then\n # $(...) word-splits in both bash and zsh.\n for __orca_n in $(printf '%s' \"${ORCA_SESSION_ENV_NAMES-}\"); do\n unset \"$__orca_n\" 2>/dev/null || true\n done\n __orca_names=''\n while IFS= read -r __orca_line || [ -n \"$__orca_line\" ]; do\n __orca_n=\"${__orca_line%%=*}\"\n case \"$__orca_n\" in '' | *[!A-Z0-9_]* | [0-9]* | ORCA_* | PATH | HOME | LD_* | DYLD_*) continue ;; esac\n export \"$__orca_n=$(printf '%s' \"${__orca_line#*=}\" | base64 -d 2>/dev/null)\"\n __orca_names=\"$__orca_names $__orca_n\"\n done <\"$__orca_env_file\"\n export ORCA_SESSION_ENV_NAMES=\"${__orca_names# }\"\nfi\n# Default branch of the cloud workspace repo, once its first-boot clone exists (computed once per shell tree).\nif [ -n \"${ORCA_WORKSPACE_PATH-}\" ] && [ -z \"${ORCA_DEFAULT_BRANCH-}\" ]; then\n __orca_branch=\"$(git -C \"$ORCA_WORKSPACE_PATH\" symbolic-ref --quiet --short refs/remotes/origin/HEAD 2>/dev/null || true)\"\n if [ -n \"$__orca_branch\" ]; then\n export ORCA_DEFAULT_BRANCH=\"${__orca_branch#origin/}\" CONDUCTOR_DEFAULT_BRANCH=\"${__orca_branch#origin/}\"\n fi\nfi\nunset __orca_env_file __orca_line __orca_n __orca_names __orca_branch\n","orca-egress-probe":"#!/usr/bin/env bash\n# Bounded Node fetch against each credential host. Vercel transform rules have made undici hang on\n# response bodies (no content-length / transfer-encoding); report it instead of letting agents hang.\n# Prints one JSON line: {\"host\":\"api.github.com\",\"status\":\"ok\"|\"egress_framing_hang\"|\"error\"}.\nset -euo pipefail\nnode=\"$(find /opt/orca/runtime/runtimes -type f -name node | head -n 1)\"\nfor host in \"$@\"; do\n \"$node\" -e '\n const host = process.argv[1]\n const timer = setTimeout(() => { console.log(JSON.stringify({ host, status: \"egress_framing_hang\" })); process.exit(0) }, 8000)\n fetch(`https://${host}/`, { headers: { authorization: process.env.ORCA_PROBE_AUTHORIZATION ?? \"\" } })\n .then((r) => r.arrayBuffer())\n .then(() => { clearTimeout(timer); console.log(JSON.stringify({ host, status: \"ok\" })) })\n .catch(() => { clearTimeout(timer); console.log(JSON.stringify({ host, status: \"error\" })) })\n ' \"$host\"\ndone\n","orca-image-wipe":"#!/usr/bin/env bash\n# Runs last before the golden snapshot. Every sandbox cloned from the image must start without an\n# orcad identity, device registry, agent sign-in or shell history (clone-safe identity, risk #1).\nset -euo pipefail\npkill -f orcad-server.js 2>/dev/null || true\nfor path in \\\n \"$HOME/.orcad-data\" \"$HOME/.orca-sandbox\" \"$HOME/.config/orca\" \"$HOME/.config/orca-dev\" \\\n \"$HOME/.claude\" \"$HOME/.claude.json\" \"$HOME/.codex\" \"$HOME/.config/gh\" \"$HOME/.gemini\" \\\n \"$HOME/.local/share/opencode\" \"$HOME/.npm\" \"$HOME/.cache\" \"$HOME/.config/google-chrome\" \"$HOME/.bash_history\" \\\n /vercel/sandbox/project; do\n rm -rf -- \"$path\"\ndone\nsudo rm -rf /tmp/* /var/tmp/* 2>/dev/null || true\nfor leftover in \"$HOME/.orcad-data\" \"$HOME/.claude\" \"$HOME/.codex\" \"$HOME/.config/gh\"; do\n if [ -e \"$leftover\" ]; then\n echo \"identity survived wipe: $leftover\" >&2\n exit 1\n fi\ndone\necho '{\"wiped\":true}'\n"} diff --git a/cloud/apps/sandbox-control/image/vm/orca-sandbox-start b/cloud/apps/sandbox-control/image/vm/orca-sandbox-start index af22fdfaf67..0c18217aecc 100755 --- a/cloud/apps/sandbox-control/image/vm/orca-sandbox-start +++ b/cloud/apps/sandbox-control/image/vm/orca-sandbox-start @@ -68,20 +68,21 @@ if [ ! -d "$PROJECT" ]; then fi fi -workspace_name="$(basename "$(git -C "$PROJECT" remote get-url origin 2>/dev/null || echo "$PROJECT")" .git)" -default_branch="$(git -C "$PROJECT" symbolic-ref --quiet --short refs/remotes/origin/HEAD 2>/dev/null || true)" -default_branch="${default_branch#origin/}" -if [ -z "$default_branch" ]; then - default_branch="$(git -C "$PROJECT" branch --show-current 2>/dev/null || true)" +# The clone may still be running here, so the name comes from the URL; the default branch is +# filled in lazily by the session-env shell hook once the clone exists. +if [ -n "${ORCA_REPO_URL:-}" ]; then + workspace_name="$(basename "${ORCA_REPO_URL%/}" .git)" +else + workspace_name="$(basename "$(git -C "$PROJECT" remote get-url origin 2>/dev/null || echo "$PROJECT")" .git)" fi # One workspace per VM: its preview ports start at 3000 (Conductor allots CONDUCTOR_PORT..+9). managed_env=( ORCA_IS_CLOUD=1 ORCA_ROOT_PATH="$PROJECT" ORCA_WORKTREE_PATH="$PROJECT" ORCA_WORKSPACE_PATH="$PROJECT" - ORCA_WORKSPACE_NAME="$workspace_name" ORCA_DEFAULT_BRANCH="${default_branch:-main}" ORCA_PORT=3000 + ORCA_WORKSPACE_NAME="$workspace_name" ORCA_PORT=3000 CONDUCTOR_IS_CLOUD=1 CONDUCTOR_ROOT_PATH="$PROJECT" CONDUCTOR_WORKSPACE_PATH="$PROJECT" - CONDUCTOR_WORKSPACE_NAME="$workspace_name" CONDUCTOR_DEFAULT_BRANCH="${default_branch:-main}" CONDUCTOR_PORT=3000 + CONDUCTOR_WORKSPACE_NAME="$workspace_name" CONDUCTOR_PORT=3000 ) agent_env=() @@ -111,8 +112,11 @@ fi unset ORCA_SECRET_ENV node="$(find "$ORCA_HOME/runtime/runtimes" -type f -name node | head -n 1)" +# Agent CLIs installed with the image's npm (codex, gemini, ...) are `#!/usr/bin/env node` scripts: +# terminals need that node on PATH, not only orcad's bundled runtime. +system_node="$(command -v node 2>/dev/null || echo "$node")" env -i HOME="$HOME" USER="$(id -un)" LANG=C.UTF-8 TERM=xterm-256color \ - PATH="$ORCA_HOME/tools/bin:$ORCA_HOME/bin:/usr/local/bin:/usr/bin:/bin" \ + PATH="$ORCA_HOME/tools/bin:$ORCA_HOME/bin:$(dirname "$system_node"):/usr/local/bin:/usr/bin:/bin" \ ORCA_USER_DATA="$HOME/.orcad-data" ORCA_SANDBOX_ID="$ORCA_SANDBOX_ID" "${managed_env[@]}" \ NODE_COMPILE_CACHE="$ORCA_HOME/compile-cache" \ ORCA_SESSION_ENV_NAMES="${secret_names[*]}" "${agent_env[@]}" \ diff --git a/cloud/apps/sandbox-control/image/vm/orca-session-env.sh b/cloud/apps/sandbox-control/image/vm/orca-session-env.sh index 792cbbe7fbc..fbd97a54457 100644 --- a/cloud/apps/sandbox-control/image/vm/orca-session-env.sh +++ b/cloud/apps/sandbox-control/image/vm/orca-session-env.sh @@ -17,4 +17,11 @@ if [ -r "$__orca_env_file" ]; then done <"$__orca_env_file" export ORCA_SESSION_ENV_NAMES="${__orca_names# }" fi -unset __orca_env_file __orca_line __orca_n __orca_names +# Default branch of the cloud workspace repo, once its first-boot clone exists (computed once per shell tree). +if [ -n "${ORCA_WORKSPACE_PATH-}" ] && [ -z "${ORCA_DEFAULT_BRANCH-}" ]; then + __orca_branch="$(git -C "$ORCA_WORKSPACE_PATH" symbolic-ref --quiet --short refs/remotes/origin/HEAD 2>/dev/null || true)" + if [ -n "$__orca_branch" ]; then + export ORCA_DEFAULT_BRANCH="${__orca_branch#origin/}" CONDUCTOR_DEFAULT_BRANCH="${__orca_branch#origin/}" + fi +fi +unset __orca_env_file __orca_line __orca_n __orca_names __orca_branch