Stack: Vue 3 + Pinia + WaveSurfer.js + TypeScript, proxied to FastAPI on :8513 Components - ChainSidebar: chain list, create/delete, active chain highlight - ChainTree: spine row (committed nodes) + branch row, upload drop zone, export buttons - NodeCard: status dot + label, duration, commit/discard actions, generating spinner - BranchPanel: WaveSurfer waveform + branch form (prompt, duration, cfg, prompt window) - WavePlayer: WaveSurfer.js waveform with play/pause and time display State & SSE - Pinia chain store: REST for tree, SSE patch for live node status updates - useNodeSSE composable: EventSource per active chain, auto-reconnects on error - applyStatusEvent(): merges node-status SSE events into store without full refetch UX - Dark theme by default, light theme via prefers-color-scheme - CSS custom properties throughout for easy theming - Responsive: sidebar + main split, compact at <640px manage.sh updated: start/stop both API (:8513) and frontend (:8514) together
99 lines
2.9 KiB
Bash
Executable file
99 lines
2.9 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
REPO_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
ENV_FILE="$REPO_DIR/.env"
|
|
PID_FILE="$REPO_DIR/data/sparrow.pid"
|
|
FE_PID_FILE="$REPO_DIR/data/sparrow-fe.pid"
|
|
LOG_FILE="$REPO_DIR/data/sparrow.log"
|
|
FE_LOG_FILE="$REPO_DIR/data/sparrow-fe.log"
|
|
|
|
[[ -f "$ENV_FILE" ]] && { set -a; source "$ENV_FILE"; set +a; }
|
|
SPARROW_PORT="${SPARROW_PORT:-8513}"
|
|
SPARROW_FE_PORT="${SPARROW_FE_PORT:-8514}"
|
|
|
|
cmd="${1:-help}"
|
|
|
|
_running() { [[ -f "$1" ]] && kill -0 "$(cat "$1")" 2>/dev/null; }
|
|
|
|
start_api() {
|
|
mkdir -p "$REPO_DIR/data"
|
|
if _running "$PID_FILE"; then
|
|
echo "API already running (PID $(cat "$PID_FILE"))"
|
|
return
|
|
fi
|
|
echo "Starting Sparrow API on port $SPARROW_PORT..."
|
|
conda run -n cf \
|
|
uvicorn app.main:app --host "${SPARROW_HOST:-0.0.0.0}" --port "$SPARROW_PORT" \
|
|
>> "$LOG_FILE" 2>&1 &
|
|
echo $! > "$PID_FILE"
|
|
echo "API started (PID $!). Logs: $LOG_FILE"
|
|
}
|
|
|
|
stop_api() {
|
|
if [[ -f "$PID_FILE" ]]; then
|
|
kill "$(cat "$PID_FILE")" 2>/dev/null && echo "API stopped." || echo "API not running."
|
|
rm -f "$PID_FILE"
|
|
else
|
|
echo "API not running."
|
|
fi
|
|
}
|
|
|
|
start_fe() {
|
|
mkdir -p "$REPO_DIR/data"
|
|
if _running "$FE_PID_FILE"; then
|
|
echo "Frontend already running (PID $(cat "$FE_PID_FILE"))"
|
|
return
|
|
fi
|
|
echo "Starting Sparrow frontend on port $SPARROW_FE_PORT..."
|
|
(cd "$REPO_DIR/frontend" && npm run dev -- --port "$SPARROW_FE_PORT" --host 0.0.0.0) \
|
|
>> "$FE_LOG_FILE" 2>&1 &
|
|
echo $! > "$FE_PID_FILE"
|
|
echo "Frontend started (PID $!). Logs: $FE_LOG_FILE"
|
|
}
|
|
|
|
stop_fe() {
|
|
if [[ -f "$FE_PID_FILE" ]]; then
|
|
kill "$(cat "$FE_PID_FILE")" 2>/dev/null && echo "Frontend stopped." || echo "Frontend not running."
|
|
rm -f "$FE_PID_FILE"
|
|
else
|
|
echo "Frontend not running."
|
|
fi
|
|
}
|
|
|
|
case "$cmd" in
|
|
start)
|
|
start_api
|
|
start_fe
|
|
;;
|
|
stop)
|
|
stop_api
|
|
stop_fe
|
|
;;
|
|
restart)
|
|
stop_api; stop_fe; sleep 1; start_api; start_fe
|
|
;;
|
|
start-api) start_api ;;
|
|
stop-api) stop_api ;;
|
|
start-fe) start_fe ;;
|
|
stop-fe) stop_fe ;;
|
|
status)
|
|
_running "$PID_FILE" && echo "API: running (PID $(cat "$PID_FILE"))" || echo "API: stopped"
|
|
_running "$FE_PID_FILE" && echo "Frontend: running (PID $(cat "$FE_PID_FILE"))" || echo "Frontend: stopped"
|
|
;;
|
|
logs)
|
|
echo "=== API ===" && tail -40 "$LOG_FILE" 2>/dev/null || true
|
|
echo "=== Frontend ===" && tail -20 "$FE_LOG_FILE" 2>/dev/null || true
|
|
;;
|
|
open)
|
|
xdg-open "http://localhost:$SPARROW_FE_PORT" 2>/dev/null || \
|
|
open "http://localhost:$SPARROW_FE_PORT" 2>/dev/null || true
|
|
;;
|
|
test)
|
|
cd "$REPO_DIR" && conda run -n cf python -m pytest tests/ -v "$@"
|
|
;;
|
|
*)
|
|
echo "Usage: $0 {start|stop|restart|status|logs|open|test}"
|
|
echo " $0 {start-api|stop-api|start-fe|stop-fe}"
|
|
;;
|
|
esac
|