#!/usr/bin/env bash # opencode-tmux-dsflash-openrouter-gb10 entrypoint. Runs inside the container; takes no # arguments. opencode in a tmux session, ttyd serving that session on 0.0.0.0:7681 # ([runtime.app]: m64 exposes it on a second front port), and a health server on # $M64_PORT. 0.0.0.0 is inside the container: only the gate can reach its network. set -eu echo '{"phase":"loading","detail":"starting opencode in tmux"}' >> "$M64_STATUS" # HOME is the run's /scratch: opencode's DB, caches and the workspace go with the run. export HOME=/scratch/home export WORK_DIR=/scratch/work mkdir -p "$HOME" "$WORK_DIR" # Packages opencode installs on first start, warmed at build time (at run time only # openrouter.ai is reachable). cp -rn /opt/opencode-capsule/. "$HOME/" export OPENCODE_CONFIG=/capsule/opencode.json # opencode is a Bun binary: Bun's fetch honours HTTP_PROXY / NO_PROXY (set by m64 to the # gate). The provider's baseURL is plain http://openrouter.ai: the gate adds the key. export TERM=xterm-256color COLORTERM=truecolor LANG=C.UTF-8 cd "$WORK_DIR" # The tmux server keeps the session (and opencode) alive when the browser tab closes. # Start it now, detached, so opencode is already up when the first tab connects. TMUX_CMD="tmux -f /capsule/tmux.conf" $TMUX_CMD new-session -d -s opencode -c "$WORK_DIR" -x 160 -y 45 opencode # Every ttyd client attaches to that session (-A), or creates it again if opencode was # quit. -W: writable. No ttyd auth and no --check-origin: the app front refuses foreign # Origins itself, and ttyd's check would also drop clients that send no Origin (AGENT.md). ttyd --port 7681 --interface 0.0.0.0 --writable \ -t titleFixed=opencode -t disableLeaveAlert=true -t fontSize=14 \ $TMUX_CMD new-session -A -s opencode -c "$WORK_DIR" opencode & TTYD_PID=$! # Ready once ttyd answers (a capsule that reports a phase must report ready itself). for _ in $(seq 1 60); do curl -sf -o /dev/null http://127.0.0.1:7681/ && break; sleep 1; done echo '{"phase":"ready","detail":"opencode is running in tmux"}' >> "$M64_STATUS" # If ttyd dies, take the container down so m64 reports the error. $$ is the health # server after the exec below; a dead ttyd lingers as a zombie, so test its state. alive() { [ -r "/proc/$1/stat" ] && [ "$(cut -d')' -f2 "/proc/$1/stat" | cut -d' ' -f2)" != Z ]; } ( while alive "$TTYD_PID"; do sleep 5; done echo '{"phase":"error","detail":"ttyd exited"}' >> "$M64_STATUS" kill -TERM $$ ) & exec node /capsule/health.js