Capstone project. I'm ngl it's vibe-coded and it's only here so I can mess around with it
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Simplify server startup script

- Replace the interactive server TUI with a direct `serve.sh` wrapper
- Update README usage examples to match the new port-based startup flow
- Stop tracking the stale server PID file in `.gitignore`

+28 -451
-1
.gitignore
··· 1 1 .DS_Store 2 2 .uxet-server.log 3 - .uxet-server.pid
+19 -11
README.md
··· 17 17 ### Start the server 18 18 19 19 ```bash 20 - ./scripts/server-tui.sh 20 + ./serve.sh 21 21 ``` 22 22 23 - This opens an interactive menu where you can: 23 + This runs: 24 24 25 - | Option | What it does | 26 - |--------|-------------| 27 - | **Start server** | Launches a Python HTTP server on a port you choose (default `8080`) | 28 - | **Stop managed server** | Gracefully kills the server UXET started | 29 - | **Status** | Shows whether the server is running, on which port, and its PID | 30 - | **Show log tail** | Prints the last 20 lines of the server log | 31 - | **Quit** | Exits the menu (the server keeps running in the background) | 25 + ```bash 26 + python3 -m http.server 8080 27 + ``` 32 28 33 - Once the server is up, open the URL it prints (usually `http://127.0.0.1:8080`) in your browser. 29 + You can also choose a different port: 30 + 31 + ```bash 32 + ./serve.sh 9000 33 + ``` 34 + 35 + Or via an environment variable: 36 + 37 + ```bash 38 + UXET_PORT=9000 ./serve.sh 39 + ``` 40 + 41 + Once the server is up, open the URL in your browser, usually `http://127.0.0.1:8080`. 34 42 35 43 > **Requires:** `python3` (used for `python3 -m http.server`). That's it. 36 44 ··· 68 76 The `data-win` attribute defines when the test ends automatically. Supported formats: 69 77 70 78 - `selector:.some-class` — fires when the CSS selector matches a visible element. 71 - - `text:Some text` — fires when the specified text appears in the page. 79 + - `text:Some text` — fires when the specified text appears in the page.
-439
scripts/server-tui.sh
··· 1 - #!/usr/bin/env bash 2 - 3 - set -u 4 - 5 - ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" 6 - DEFAULT_PORT="${UXET_PORT:-8080}" 7 - STATE_FILE="$ROOT_DIR/.uxet-server.state" 8 - LOG_FILE="$ROOT_DIR/.uxet-server.log" 9 - START_TIMEOUT_SECONDS=5 10 - STOP_TIMEOUT_SECONDS=5 11 - 12 - STATE_PID="" 13 - STATE_PORT="" 14 - STATE_START_TS="" 15 - STATE_ROOT_DIR="" 16 - STATE_CMD="" 17 - 18 - server_url() { 19 - printf 'http://127.0.0.1:%s\n' "$1" 20 - } 21 - 22 - prompt_for_port() { 23 - local input 24 - while true; do 25 - read -r -p "Port [$DEFAULT_PORT]: " input 26 - if [[ -z "${input:-}" ]]; then 27 - input="$DEFAULT_PORT" 28 - fi 29 - 30 - if validate_port "$input"; then 31 - printf '%s\n' "$input" 32 - return 0 33 - fi 34 - 35 - printf 'Invalid port. Enter a number between 1 and 65535.\n' 36 - done 37 - } 38 - 39 - validate_port() { 40 - local port="$1" 41 - [[ "$port" =~ ^[0-9]+$ ]] || return 1 42 - (( port >= 1 && port <= 65535 )) 43 - } 44 - 45 - write_state() { 46 - local pid="$1" 47 - local port="$2" 48 - 49 - cat >"$STATE_FILE" <<EOF 50 - PID=$pid 51 - PORT=$port 52 - START_TS=$(date +%s) 53 - ROOT_DIR=$ROOT_DIR 54 - CMD=python3 -m http.server $port 55 - EOF 56 - 57 - STATE_PID="$pid" 58 - STATE_PORT="$port" 59 - STATE_START_TS="$(date +%s)" 60 - STATE_ROOT_DIR="$ROOT_DIR" 61 - STATE_CMD="python3 -m http.server $port" 62 - } 63 - 64 - remove_state() { 65 - rm -f "$STATE_FILE" 66 - STATE_PID="" 67 - STATE_PORT="" 68 - STATE_START_TS="" 69 - STATE_ROOT_DIR="" 70 - STATE_CMD="" 71 - } 72 - 73 - load_state() { 74 - STATE_PID="" 75 - STATE_PORT="" 76 - STATE_START_TS="" 77 - STATE_ROOT_DIR="" 78 - STATE_CMD="" 79 - 80 - if [[ ! -f "$STATE_FILE" ]]; then 81 - return 1 82 - fi 83 - 84 - while IFS='=' read -r key value; do 85 - case "$key" in 86 - PID) STATE_PID="$value" ;; 87 - PORT) STATE_PORT="$value" ;; 88 - START_TS) STATE_START_TS="$value" ;; 89 - ROOT_DIR) STATE_ROOT_DIR="$value" ;; 90 - CMD) STATE_CMD="$value" ;; 91 - esac 92 - done <"$STATE_FILE" 93 - 94 - return 0 95 - } 96 - 97 - get_listening_pids() { 98 - local port="$1" 99 - lsof -nP -t -iTCP:"$port" -sTCP:LISTEN 2>/dev/null | awk '!seen[$0]++' 100 - } 101 - 102 - get_pid_cmdline() { 103 - local pid="$1" 104 - [[ -r "/proc/$pid/cmdline" ]] || return 1 105 - tr '\0' ' ' <"/proc/$pid/cmdline" | sed 's/[[:space:]]\+$//' 106 - } 107 - 108 - get_pid_cwd() { 109 - local pid="$1" 110 - [[ -L "/proc/$pid/cwd" ]] || return 1 111 - readlink -f "/proc/$pid/cwd" 112 - } 113 - 114 - describe_pid() { 115 - local pid="$1" 116 - local cmdline cwd 117 - 118 - cmdline="$(get_pid_cmdline "$pid" 2>/dev/null || true)" 119 - cwd="$(get_pid_cwd "$pid" 2>/dev/null || true)" 120 - 121 - printf 'PID: %s\n' "$pid" 122 - printf 'CWD: %s\n' "${cwd:-unknown}" 123 - printf 'CMD: %s\n' "${cmdline:-unknown}" 124 - } 125 - 126 - is_pid_listening_on_port() { 127 - local pid="$1" 128 - local port="$2" 129 - local listening_pid 130 - 131 - while IFS= read -r listening_pid; do 132 - [[ "$listening_pid" == "$pid" ]] && return 0 133 - done < <(get_listening_pids "$port") 134 - 135 - return 1 136 - } 137 - 138 - is_managed_server() { 139 - local pid="$1" 140 - local port="$2" 141 - local cmdline cwd 142 - 143 - [[ -n "$pid" && -n "$port" ]] || return 1 144 - [[ "$STATE_PID" == "$pid" ]] || return 1 145 - [[ "$STATE_PORT" == "$port" ]] || return 1 146 - is_pid_listening_on_port "$pid" "$port" || return 1 147 - 148 - cwd="$(get_pid_cwd "$pid" 2>/dev/null || true)" 149 - [[ "$cwd" == "$ROOT_DIR" ]] || return 1 150 - [[ -n "$STATE_ROOT_DIR" && "$STATE_ROOT_DIR" != "$ROOT_DIR" ]] && return 1 151 - 152 - cmdline="$(get_pid_cmdline "$pid" 2>/dev/null || true)" 153 - [[ "$cmdline" == *python3* ]] || return 1 154 - [[ "$cmdline" == *"-m http.server"* ]] || return 1 155 - [[ "$cmdline" == *" $port"* || "$cmdline" == *" $port "* ]] || return 1 156 - 157 - return 0 158 - } 159 - 160 - find_managed_server() { 161 - local pid 162 - 163 - load_state || return 1 164 - validate_port "$STATE_PORT" || return 1 165 - 166 - for pid in $(get_listening_pids "$STATE_PORT"); do 167 - if is_managed_server "$pid" "$STATE_PORT"; then 168 - printf '%s\n' "$pid" 169 - return 0 170 - fi 171 - done 172 - 173 - return 1 174 - } 175 - 176 - find_unmanaged_listener() { 177 - local port="$1" 178 - local managed_pid pid 179 - 180 - managed_pid="$(find_managed_server 2>/dev/null || true)" 181 - while IFS= read -r pid; do 182 - [[ -z "$pid" ]] && continue 183 - if [[ -n "$managed_pid" && "$pid" == "$managed_pid" ]]; then 184 - continue 185 - fi 186 - printf '%s\n' "$pid" 187 - return 0 188 - done < <(get_listening_pids "$port") 189 - 190 - return 1 191 - } 192 - 193 - wait_for_port_state() { 194 - local port="$1" 195 - local expected_state="$2" 196 - local timeout="$3" 197 - local remaining="$timeout" 198 - 199 - while (( remaining > 0 )); do 200 - if [[ "$expected_state" == "listening" ]]; then 201 - if get_listening_pids "$port" >/dev/null && [[ -n "$(get_listening_pids "$port")" ]]; then 202 - return 0 203 - fi 204 - else 205 - if [[ -z "$(get_listening_pids "$port")" ]]; then 206 - return 0 207 - fi 208 - fi 209 - sleep 1 210 - ((remaining--)) 211 - done 212 - 213 - return 1 214 - } 215 - 216 - start_server() { 217 - local port listener_pid managed_pid pid 218 - 219 - if ! command -v python3 >/dev/null 2>&1; then 220 - printf 'python3 is required but was not found.\n' 221 - return 1 222 - fi 223 - 224 - port="$(prompt_for_port)" 225 - DEFAULT_PORT="$port" 226 - 227 - managed_pid="$(find_managed_server 2>/dev/null || true)" 228 - if [[ -n "$managed_pid" ]]; then 229 - printf 'Managed UXET server already running.\n' 230 - printf 'URL: %s\n' "$(server_url "$STATE_PORT")" 231 - printf 'Log: %s\n' "$LOG_FILE" 232 - describe_pid "$managed_pid" 233 - return 0 234 - fi 235 - 236 - listener_pid="$(find_unmanaged_listener "$port" 2>/dev/null || true)" 237 - if [[ -n "$listener_pid" ]]; then 238 - printf 'Port %s is already occupied by an unmanaged process.\n' "$port" 239 - describe_pid "$listener_pid" 240 - printf 'Refusing to replace it.\n' 241 - return 1 242 - fi 243 - 244 - if load_state && [[ -n "$STATE_PORT" ]]; then 245 - if [[ -z "$(get_listening_pids "$STATE_PORT")" ]]; then 246 - printf 'Removing stale state file.\n' 247 - remove_state 248 - fi 249 - fi 250 - 251 - printf 'Starting UXET server on %s\n' "$(server_url "$port")" 252 - nohup bash -c 'cd "$1" && exec python3 -m http.server "$2"' _ "$ROOT_DIR" "$port" >>"$LOG_FILE" 2>&1 & 253 - pid=$! 254 - 255 - write_state "$pid" "$port" 256 - 257 - if wait_for_port_state "$port" listening "$START_TIMEOUT_SECONDS" && is_managed_server "$pid" "$port"; then 258 - printf 'Server started.\n' 259 - printf 'URL: %s\n' "$(server_url "$port")" 260 - printf 'Log: %s\n' "$LOG_FILE" 261 - describe_pid "$pid" 262 - return 0 263 - fi 264 - 265 - printf 'Failed to verify startup.\n' 266 - if kill -0 "$pid" 2>/dev/null; then 267 - kill "$pid" 2>/dev/null || true 268 - sleep 1 269 - kill -9 "$pid" 2>/dev/null || true 270 - fi 271 - remove_state 272 - printf 'Check log: %s\n' "$LOG_FILE" 273 - return 1 274 - } 275 - 276 - stop_server() { 277 - local managed_pid listener_pid port remaining 278 - 279 - managed_pid="$(find_managed_server 2>/dev/null || true)" 280 - 281 - if [[ -n "$managed_pid" ]]; then 282 - port="$STATE_PORT" 283 - printf 'Stopping managed UXET server on %s\n' "$(server_url "$port")" 284 - kill "$managed_pid" 2>/dev/null || true 285 - 286 - remaining="$STOP_TIMEOUT_SECONDS" 287 - while (( remaining > 0 )); do 288 - if ! is_pid_listening_on_port "$managed_pid" "$port" && ! kill -0 "$managed_pid" 2>/dev/null; then 289 - remove_state 290 - printf 'Server stopped.\n' 291 - return 0 292 - fi 293 - sleep 1 294 - ((remaining--)) 295 - done 296 - 297 - if is_pid_listening_on_port "$managed_pid" "$port" || kill -0 "$managed_pid" 2>/dev/null; then 298 - printf 'Server did not exit cleanly, sending SIGKILL.\n' 299 - kill -9 "$managed_pid" 2>/dev/null || true 300 - fi 301 - 302 - remaining="$STOP_TIMEOUT_SECONDS" 303 - while (( remaining > 0 )); do 304 - if ! is_pid_listening_on_port "$managed_pid" "$port" && ! kill -0 "$managed_pid" 2>/dev/null; then 305 - if [[ -z "$(get_listening_pids "$port")" ]]; then 306 - remove_state 307 - printf 'Server stopped.\n' 308 - return 0 309 - fi 310 - break 311 - fi 312 - sleep 1 313 - ((remaining--)) 314 - done 315 - 316 - printf 'Shutdown failed verification.\n' 317 - listener_pid="$(get_listening_pids "$port" | head -n 1)" 318 - if [[ -n "$listener_pid" ]]; then 319 - printf 'Port %s is still occupied.\n' "$port" 320 - describe_pid "$listener_pid" 321 - fi 322 - return 1 323 - fi 324 - 325 - if load_state && [[ -n "$STATE_PORT" ]]; then 326 - port="$STATE_PORT" 327 - if [[ -z "$(get_listening_pids "$port")" ]]; then 328 - printf 'State file is stale. Removing it.\n' 329 - remove_state 330 - printf 'Server is not running.\n' 331 - return 0 332 - fi 333 - 334 - listener_pid="$(get_listening_pids "$port" | head -n 1)" 335 - if [[ -n "$listener_pid" ]]; then 336 - printf 'A process is listening on %s but it is not a verified UXET-managed server.\n' "$(server_url "$port")" 337 - describe_pid "$listener_pid" 338 - printf 'Refusing to stop it.\n' 339 - return 1 340 - fi 341 - fi 342 - 343 - port="$DEFAULT_PORT" 344 - listener_pid="$(find_unmanaged_listener "$port" 2>/dev/null || true)" 345 - if [[ -n "$listener_pid" ]]; then 346 - printf 'Port %s is occupied by an unmanaged process.\n' "$port" 347 - describe_pid "$listener_pid" 348 - printf 'Refusing to stop it.\n' 349 - return 1 350 - fi 351 - 352 - printf 'Server is not running.\n' 353 - return 0 354 - } 355 - 356 - show_status() { 357 - local managed_pid listener_pid port 358 - 359 - managed_pid="$(find_managed_server 2>/dev/null || true)" 360 - if [[ -n "$managed_pid" ]]; then 361 - printf 'Status: running (managed)\n' 362 - printf 'URL: %s\n' "$(server_url "$STATE_PORT")" 363 - printf 'Log: %s\n' "$LOG_FILE" 364 - printf 'Root: %s\n' "$ROOT_DIR" 365 - describe_pid "$managed_pid" 366 - return 0 367 - fi 368 - 369 - if load_state && [[ -n "$STATE_PORT" ]]; then 370 - port="$STATE_PORT" 371 - listener_pid="$(get_listening_pids "$port" | head -n 1)" 372 - if [[ -z "$listener_pid" ]]; then 373 - printf 'Status: stale state file\n' 374 - printf 'Expected port: %s\n' "$port" 375 - printf 'Log: %s\n' "$LOG_FILE" 376 - printf 'Root: %s\n' "$ROOT_DIR" 377 - return 0 378 - fi 379 - 380 - printf 'Status: port occupied by unmanaged process\n' 381 - printf 'Port: %s\n' "$port" 382 - printf 'URL: %s\n' "$(server_url "$port")" 383 - printf 'Log: %s\n' "$LOG_FILE" 384 - printf 'Root: %s\n' "$ROOT_DIR" 385 - printf 'State file does not match the live listener.\n' 386 - describe_pid "$listener_pid" 387 - return 0 388 - fi 389 - 390 - port="$DEFAULT_PORT" 391 - listener_pid="$(find_unmanaged_listener "$port" 2>/dev/null || true)" 392 - if [[ -n "$listener_pid" ]]; then 393 - printf 'Status: port occupied by unmanaged process\n' 394 - printf 'Port: %s\n' "$port" 395 - printf 'URL: %s\n' "$(server_url "$port")" 396 - printf 'Log: %s\n' "$LOG_FILE" 397 - printf 'Root: %s\n' "$ROOT_DIR" 398 - describe_pid "$listener_pid" 399 - return 0 400 - fi 401 - 402 - printf 'Status: stopped\n' 403 - printf 'Default URL: %s\n' "$(server_url "$port")" 404 - printf 'Log: %s\n' "$LOG_FILE" 405 - printf 'Root: %s\n' "$ROOT_DIR" 406 - } 407 - 408 - show_log_tail() { 409 - if [[ ! -f "$LOG_FILE" ]]; then 410 - printf 'No log file yet.\n' 411 - return 0 412 - fi 413 - 414 - printf '\nLast 20 log lines:\n\n' 415 - tail -n 20 "$LOG_FILE" 416 - printf '\n' 417 - } 418 - 419 - main_menu() { 420 - while true; do 421 - printf '\nUXET Server Control\n' 422 - printf 'Repo: %s\n' "$ROOT_DIR" 423 - printf 'Safety: only verified UXET-managed servers will be stopped.\n\n' 424 - 425 - PS3="Choose an action: " 426 - select action in "Start server" "Stop managed server" "Status" "Show log tail" "Quit"; do 427 - case "$REPLY" in 428 - 1) start_server; break ;; 429 - 2) stop_server; break ;; 430 - 3) show_status; break ;; 431 - 4) show_log_tail; break ;; 432 - 5) exit 0 ;; 433 - *) printf 'Invalid choice.\n'; break ;; 434 - esac 435 - done 436 - done 437 - } 438 - 439 - main_menu
+9
serve.sh
··· 1 + #!/usr/bin/env bash 2 + 3 + set -euo pipefail 4 + 5 + ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 6 + PORT="${1:-${UXET_PORT:-8080}}" 7 + 8 + cd "$ROOT_DIR" 9 + exec python3 -m http.server "$PORT"