#!/bin/sh set -e # ---- OpenCode agent server (background, best-effort) ---- # Start `opencode serve` inside the same container so the deployed tac-app can # reach it on :4096, in the SAME working directory (/app) — sharing # opencode.json, the skill library and .opencode/. The browser talks to it via # the app's OPENCODE_BASE_URL; --cors must allow the app's own origin. # # opencode MUST NOT gate app startup. It used to: the entrypoint blocked on an # unbounded probe, so when opencode's HTTP layer accepted the TCP connection # but never answered (slow MCP cold-start) the curl hung forever, the container # never listened on :3000, Coolify's healthcheck failed, and the site stayed # down until `next start` was started manually in the terminal. OPENCODE_PORT="${OPENCODE_PORT:-4096}" OPENCODE_HOSTNAME="${OPENCODE_HOSTNAME:-0.0.0.0}" cors_origins="" cors_args="" add_cors() { for existing in $cors_origins; do [ "$existing" = "$1" ] && return done cors_origins="$cors_origins $1" cors_args="$cors_args --cors $1" } if [ -n "${OPENCODE_CORS:-}" ]; then for origin in $(echo "$OPENCODE_CORS" | tr ',' ' '); do [ -n "$origin" ] && add_cors "$origin" done else for origin in "http://localhost:3000" "https://localhost:3000" \ "${BETTER_AUTH_URL:-}" "${APP_URL:-}"; do [ -n "$origin" ] && add_cors "$origin" done fi echo "> Starting opencode serve on :$OPENCODE_PORT (auto-restart; log: /tmp/opencode-serve.log) ..." ( while :; do # shellcheck disable=SC2086 # intentional word splitting for --cors flags opencode serve --hostname "$OPENCODE_HOSTNAME" --port "$OPENCODE_PORT" $cors_args \ || echo "> opencode serve exited ($?) — restarting in 2s ..." sleep 2 done ) >/tmp/opencode-serve.log 2>&1 & OPENCODE_PID=$! # Best-effort readiness probe: bounded (10s) and every curl capped with # --max-time, so a half-open listen can never stall the container again. If # opencode is slow or down, the app still starts — agent features just degrade. i=0 while [ "$i" -lt 10 ]; do if curl -sS --max-time 2 -o /dev/null "http://127.0.0.1:$OPENCODE_PORT/"; then echo "> opencode serve ready on :$OPENCODE_PORT (pid $OPENCODE_PID)" break fi i=$((i + 1)) sleep 1 done if [ "$i" -ge 10 ]; then echo "> WARNING: opencode serve not ready after 10s — continuing anyway (tail -f /tmp/opencode-serve.log)" fi # ---- Experiments submodule (git lineage) ---- # The `experiments` submodule lives in the ephemeral container layer — it is # re-created at runtime by `trace.sh init` and is wiped on every redeploy. Ensure # it exists on each boot so /rd/graph and trace.sh work right after a deploy. # Idempotent (validates/creates against $GIT_REPO_URL) and best-effort: never # gate app startup. if [ -n "${GIT_REPO_URL:-}" ] && [ -n "${GIT_USER:-}" ]; then if [ -f /app/tac-qlib/skills/tac-qlib-custom/lib/git_exp.sh ]; then ( cd /app GIT_REPO_URL="$GIT_REPO_URL" GIT_USER="$GIT_USER" GIT_PASS="${GIT_PASS:-}" \ bash tac-qlib/skills/tac-qlib-custom/lib/git_exp.sh ensure_repo >/dev/null 2>&1 \ && GIT_REPO_URL="$GIT_REPO_URL" GIT_USER="$GIT_USER" GIT_PASS="${GIT_PASS:-}" \ bash tac-qlib/skills/tac-qlib-custom/lib/git_exp.sh ensure_base main >/dev/null 2>&1 ) || echo "> WARNING: could not ensure experiments submodule — run trace.sh init in the container" fi fi # Default: serve HTTP with `next start` (production mode). if [ "${SERVER_TLS:-false}" != "true" ]; then exec node node_modules/next/dist/bin/next start tac-app fi # ---- HTTPS mode (self-signed certificate) ---- # Set SERVER_TLS=true to serve the app over HTTPS. A self-signed cert is # generated on first start and kept under TLS_DIR; override TLS_KEY / TLS_CERT # to mount your own certificates. TLS_HOST="${TLS_HOST:-localhost}" TLS_DIR="${TLS_DIR:-/tmp/tls}" TLS_KEY="${TLS_KEY:-$TLS_DIR/key.pem}" TLS_CERT="${TLS_CERT:-$TLS_DIR/cert.pem}" if [ ! -s "$TLS_KEY" ] || [ ! -s "$TLS_CERT" ]; then echo "> Generating self-signed certificate for $TLS_HOST ..." echo "> (Browsers will warn ERR_CERT_AUTHORITY_INVALID. For a trusted cert, generate one with" echo "> mkcert on the host and mount it via TLS_KEY/TLS_CERT.)" mkdir -p "$TLS_DIR" openssl req -x509 -newkey rsa:2048 -nodes \ -keyout "$TLS_KEY" -out "$TLS_CERT" -days 825 \ -subj "/CN=$TLS_HOST" \ -addext "subjectAltName=DNS:localhost,DNS:$TLS_HOST,IP:127.0.0.1" \ >/dev/null 2>&1 fi export TLS_KEY TLS_CERT TLS_HOST exec node /app/tls-server.cjs