Both scripts wrap something every agent on this repo ends up doing by hand,
and each encodes a constraint that is easy to get wrong once and expensive
to get wrong twice. Agent-agnostic on purpose -- plain scripts, plain text
output -- so opencode agents use the same ones rather than each
hand-rolling the equivalent command and getting a different detail wrong.
Indexed from AGENTS.md; the reasoning is in scripts/README.md.
scripts/sandbox.sh -- throwaway router on 8081. Encodes the port convention
(8080 is production and this cannot bind it), the kill discipline (only a
pid it recorded itself; never kill-by-name, Restart=always will fight you
and on this machine that process is the operator's model access), and the
cwd-vs-PYTHONPATH split that decides which config.yaml, which router.db and
which admin/frontend get used.
Writing it found two bugs in itself, both only visible by running it:
- `sandbox.sh start | tail` hung until the SERVER exited, because the
child inherited the script's stdout and held the pipe open. Fixed with
</dev/null and all three fds redirected.
- $! recorded a pid one off from the real uvicorn, so `stop` reported
success while the server kept running and `status` said "not running"
against a live :8081 answering 200. Fixed by backgrounding the command
directly instead of inside `( ... & echo $! )`, plus a post-start check
that the recorded pid is the one actually holding the port -- without
that check both bugs printed success.
scripts/mkpr.sh -- cut a gitea PR with a long markdown body. `tea pr
create` has no --description-file, so the body must arrive via $(cat ...),
which a worktree-pinned agent session refuses as too complex to verify;
running it from a script file sidesteps that without weakening it. Passes
--remote origin as well as --repo because AGENTS.md notes the `alee` login
is not tea's default. Refuses a head branch that is unpushed, diverged from
its remote, or not ahead of the base -- tea will cheerfully do all three.
Deliberately not scripted: merging to main (needs main checked out, which
conflicts with any agent in a worktree), Seed Energy and Restart Service
(one spends real credit, the other restarts production), and anything that
writes config.local.yaml.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VRQXz5SYZYVWscxS1QqF6U
94 lines
3.6 KiB
Bash
Executable File
94 lines
3.6 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Throwaway 6krrt router instance on 8081, for exercising the admin portal
|
|
# against real data without touching production.
|
|
#
|
|
# sandbox.sh start [worktree] start it, wait for /health, print the pid
|
|
# sandbox.sh stop kill the instance this script started
|
|
# sandbox.sh status is it up, and on what pid
|
|
#
|
|
# 8080 is production and 8081 is the throwaway, per CLAUDE.md. This script
|
|
# refuses to bind 8080 at all, and only ever kills a pid it recorded itself
|
|
# -- never one matched by name or port, because Restart=always will fight
|
|
# you and on this machine that process is the user's model access.
|
|
set -euo pipefail
|
|
|
|
REPO=/home/alee/Sources/6krrt
|
|
PORT=8081
|
|
PIDFILE="${TMPDIR:-/tmp}/6krrt-sandbox-${PORT}.pid"
|
|
LOGFILE="${TMPDIR:-/tmp}/6krrt-sandbox-${PORT}.log"
|
|
|
|
usage() { sed -n '2,12p' "$0" | sed 's/^# \{0,1\}//'; exit 2; }
|
|
|
|
start() {
|
|
local worktree="${1:-$PWD}"
|
|
[ -d "$worktree/src" ] || { echo "no src/ under $worktree" >&2; exit 1; }
|
|
if [ -f "$PIDFILE" ] && kill -0 "$(cat "$PIDFILE")" 2>/dev/null; then
|
|
echo "already running, pid $(cat "$PIDFILE")"; exit 0
|
|
fi
|
|
|
|
# cwd decides config/config.yaml and the relative router.db; PYTHONPATH
|
|
# decides which src/ and which admin/frontend/*.html get served. Pointing
|
|
# both at the worktree keeps every write inside it.
|
|
#
|
|
# Backgrounded directly rather than inside `( ... & echo $! )`. Two bugs
|
|
# came out of the subshell form, both found by running it:
|
|
# - `sandbox.sh start | tail` hung until the SERVER exited, because the
|
|
# child inherited the script's stdout and held the pipe open. Hence
|
|
# </dev/null and all three fds redirected.
|
|
# - $! recorded the wrong pid (off by one from the real uvicorn), so
|
|
# `stop` reported success while the server kept running and `status`
|
|
# said "not running" against a live :8081. Backgrounding a simple
|
|
# command in this shell makes $! exactly the process we started.
|
|
cd "$worktree"
|
|
PYTHONPATH="$worktree/src" \
|
|
HF_HOME="$REPO/.hf-cache" \
|
|
"$REPO/.venv/bin/uvicorn" dispatcher:app \
|
|
--host 127.0.0.1 --port "$PORT" --timeout-graceful-shutdown 5 \
|
|
</dev/null >"$LOGFILE" 2>&1 &
|
|
local pid=$!
|
|
echo "$pid" >"$PIDFILE"
|
|
|
|
if ! curl -s -m 30 --retry 20 --retry-delay 1 --retry-connrefused \
|
|
-o /dev/null "http://127.0.0.1:$PORT/health"; then
|
|
echo "failed to come up; last log lines:" >&2
|
|
tail -20 "$LOGFILE" >&2
|
|
exit 1
|
|
fi
|
|
|
|
# Prove the recorded pid is the process actually holding the port. Without
|
|
# this the two bugs above were silent: everything printed success while
|
|
# stop/status operated on a pid that was never the server.
|
|
local owner
|
|
owner="$(ss -ltnpH "sport = :$PORT" 2>/dev/null | grep -o 'pid=[0-9]*' | head -1 | cut -d= -f2)"
|
|
if [ -n "$owner" ] && [ "$owner" != "$pid" ]; then
|
|
echo "warning: recorded pid $pid but :$PORT is held by $owner; recording $owner" >&2
|
|
pid="$owner"
|
|
echo "$pid" >"$PIDFILE"
|
|
fi
|
|
echo "up on $PORT, pid $pid, log $LOGFILE"
|
|
}
|
|
|
|
stop() {
|
|
[ -f "$PIDFILE" ] || { echo "no pidfile; nothing this script started"; exit 0; }
|
|
local pid; pid="$(cat "$PIDFILE")"
|
|
kill "$pid" 2>/dev/null && echo "stopped pid $pid" || echo "pid $pid already gone"
|
|
rm -f "$PIDFILE"
|
|
}
|
|
|
|
status() {
|
|
if [ -f "$PIDFILE" ] && kill -0 "$(cat "$PIDFILE")" 2>/dev/null; then
|
|
echo "running, pid $(cat "$PIDFILE")"
|
|
else
|
|
echo "not running"
|
|
fi
|
|
curl -s -m 5 -o /dev/null -w "health on $PORT: %{http_code}\n" \
|
|
"http://127.0.0.1:$PORT/health" || true
|
|
}
|
|
|
|
case "${1:-}" in
|
|
start) shift; start "$@" ;;
|
|
stop) stop ;;
|
|
status) status ;;
|
|
*) usage ;;
|
|
esac
|