111 lines
4.4 KiB
Bash
Executable File
111 lines
4.4 KiB
Bash
Executable File
#!/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
|