@@ -10,6 +10,7 @@ export OLLAMA_EMBED_MODEL="qwen3-embedding:8b"
1010
1111ROOT_DIR=" $( cd " $( dirname " ${BASH_SOURCE[0]} " ) /.." && pwd) "
1212CLIENT_DIR=" ${ROOT_DIR} /client"
13+ LOG_DIR=" ${ROOT_DIR} /.logs/start"
1314
1415if ! command -v uv > /dev/null 2>&1 ; then
1516 echo " uv is required. Install it from https://docs.astral.sh/uv/." >&2
@@ -21,22 +22,135 @@ if ! command -v npm >/dev/null 2>&1; then
2122 exit 1
2223fi
2324
24- echo " Starting data server (port 8000)..."
25- uv run --directory " ${ROOT_DIR} " python server_api/scripts/serve_data.py &
25+ mkdir -p " ${LOG_DIR} "
2626
27- echo " Starting API server (port 4242)..."
28- PYTHONDONTWRITEBYTECODE=1 uv run --directory " ${ROOT_DIR} " python -m server_api.main &
27+ STARTED_PIDS=()
2928
30- echo " Starting PyTC server (port 4243)..."
31- uv run --directory " ${ROOT_DIR} " python -m server_pytc.main &
29+ relative_log_path () {
30+ local path=" $1 "
31+ if [[ " ${path} " == " ${ROOT_DIR} " /* ]]; then
32+ echo " ${path# ${ROOT_DIR} / } "
33+ else
34+ echo " ${path} "
35+ fi
36+ }
37+
38+ port_is_listening () {
39+ local port=" $1 "
40+ lsof -tiTCP:" ${port} " -sTCP:LISTEN > /dev/null 2>&1
41+ }
42+
43+ wait_for_http () {
44+ local name=" $1 "
45+ local url=" $2 "
46+ local max_attempts=" ${3:- 30} "
47+ local attempt=1
48+
49+ while [[ ${attempt} -le ${max_attempts} ]]; do
50+ if curl -sf " ${url} " > /dev/null 2>&1 ; then
51+ return 0
52+ fi
53+ attempt=$(( attempt + 1 ))
54+ sleep 1
55+ done
56+
57+ echo " ERROR: ${name} did not become ready at ${url} " >&2
58+ return 1
59+ }
60+
61+ start_service () {
62+ local name=" $1 "
63+ local port=" $2 "
64+ local health_url=" $3 "
65+ local log_file=" $4 "
66+ shift 4
67+
68+ if port_is_listening " ${port} " ; then
69+ if wait_for_http " ${name} " " ${health_url} " 5; then
70+ echo " ${name} already running on :${port} ; reusing existing service."
71+ return 0
72+ fi
73+ echo " ERROR: Port ${port} is already in use, but ${name} did not respond at ${health_url} ." >&2
74+ return 1
75+ fi
76+
77+ : > " ${log_file} "
78+ " $@ " > " ${log_file} " 2>&1 &
79+ local pid=$!
80+ STARTED_PIDS+=(" ${pid} " )
81+ echo " ${name} starting on :${port} (pid ${pid} ; log: $( relative_log_path " ${log_file} " ) )"
82+
83+ if ! wait_for_http " ${name} " " ${health_url} " 30; then
84+ echo " Recent ${name} log output:" >&2
85+ tail -n 40 " ${log_file} " >&2 || true
86+ return 1
87+ fi
88+
89+ echo " ${name} ready on :${port} "
90+ }
91+
92+ cleanup () {
93+ local exit_code=$?
94+ local pid
95+ for pid in " ${STARTED_PIDS[@]:- } " ; do
96+ if ps -p " ${pid} " > /dev/null 2>&1 ; then
97+ kill " ${pid} " > /dev/null 2>&1 || true
98+ fi
99+ done
100+ wait || true
101+ exit " ${exit_code} "
102+ }
103+
104+ trap cleanup EXIT INT TERM
105+
106+ start_service \
107+ " Data server" \
108+ 8000 \
109+ " http://localhost:8000/" \
110+ " ${LOG_DIR} /data-server.log" \
111+ uv run --directory " ${ROOT_DIR} " python server_api/scripts/serve_data.py
112+
113+ start_service \
114+ " API server" \
115+ 4242 \
116+ " http://localhost:4242/health" \
117+ " ${LOG_DIR} /api-server.log" \
118+ env PYTHONDONTWRITEBYTECODE=1 uv run --directory " ${ROOT_DIR} " python -m server_api.main
119+
120+ start_service \
121+ " PyTC server" \
122+ 4243 \
123+ " http://localhost:4243/hello" \
124+ " ${LOG_DIR} /pytc-server.log" \
125+ uv run --directory " ${ROOT_DIR} " python -m server_pytc.main
32126
33- echo " Starting React server (port 3000)..."
34127pushd " ${CLIENT_DIR} " > /dev/null
35128if [[ " ${SKIP_CLIENT_BUILD:- 0} " != " 1" ]]; then
36- echo " Building React client (set SKIP_CLIENT_BUILD=1 to skip)..."
37- npm run build
129+ BUILD_LOG=" ${LOG_DIR} /react-build.log"
130+ echo " Building React client (log: $( relative_log_path " ${BUILD_LOG} " ) )..."
131+ if npm run build > " ${BUILD_LOG} " 2>&1 ; then
132+ echo " React build complete"
133+ else
134+ echo " ERROR: React build failed. Recent log output:" >&2
135+ tail -n 60 " ${BUILD_LOG} " >&2 || true
136+ exit 1
137+ fi
138+ fi
139+
140+ REACT_LOG=" ${LOG_DIR} /react-dev.log"
141+ if port_is_listening 3000; then
142+ if curl -sf http://localhost:3000 > /dev/null 2>&1 ; then
143+ echo " React server already running on :3000; reusing existing service."
144+ else
145+ echo " ERROR: Port 3000 is already in use, but React did not respond." >&2
146+ exit 1
147+ fi
148+ else
149+ : > " ${REACT_LOG} "
150+ echo " React server starting on :3000 (log: $( relative_log_path " ${REACT_LOG} " ) )"
151+ BROWSER=none npm start > " ${REACT_LOG} " 2>&1 &
152+ STARTED_PIDS+=(" $! " )
38153fi
39- BROWSER=none npm start > /dev/null 2>&1 &
40154
41155wait_for_react () {
42156 local max_attempts=60
@@ -55,6 +169,7 @@ wait_for_react() {
55169}
56170
57171if wait_for_react; then
172+ echo " Startup logs are under $( relative_log_path " ${LOG_DIR} " ) "
58173 echo " Starting Electron client..."
59174 ENVIRONMENT=development npm run electron
60175else
0 commit comments