| 1 | #!/usr/bin/env bash |
| 2 | # Start a local Codewhale runtime and the Weixin bridge in one terminal. |
| 3 | # |
| 4 | # Generates a shared CODEWHALE_RUNTIME_TOKEN, starts `codewhale serve --http` in |
| 5 | # the background, waits for it to answer /health, then runs the bridge in the |
| 6 | # foreground. Both processes share the generated token, so no copy/paste. |
| 7 | # |
| 8 | # Ctrl-C stops both. Override defaults with the env vars below: |
| 9 | # CODEWHALE_RUNTIME_PORT runtime port (default 7878) |
| 10 | # CODEWHALE_RUNTIME_TOKEN reuse an existing token (default: generated) |
| 11 | # WEIXIN_ALLOW_UNLISTED first-pairing mode (default true) |
| 12 | # WEIXIN_STATE_DIR state directory (default: ./.state) |
| 13 | |
| 14 | set -euo pipefail |
| 15 | |
| 16 | script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" |
| 17 | bridge_dir="$(cd "$script_dir/.." && pwd)" |
| 18 | |
| 19 | port="${CODEWHALE_RUNTIME_PORT:-7878}" |
| 20 | runtime_url="http://127.0.0.1:${port}" |
| 21 | |
| 22 | if [[ -z "${CODEWHALE_RUNTIME_TOKEN:-}" ]]; then |
| 23 | CODEWHALE_RUNTIME_TOKEN="$(openssl rand -hex 32)" |
| 24 | echo "Generated CODEWHALE_RUNTIME_TOKEN for this session." |
| 25 | fi |
| 26 | export CODEWHALE_RUNTIME_TOKEN |
| 27 | |
| 28 | export CODEWHALE_RUNTIME_URL="$runtime_url" |
| 29 | export WEIXIN_ALLOW_UNLISTED="${WEIXIN_ALLOW_UNLISTED:-true}" |
| 30 | export WEIXIN_STATE_DIR="${WEIXIN_STATE_DIR:-$bridge_dir/.state}" |
| 31 | |
| 32 | runtime_pid="" |
| 33 | |
| 34 | cleanup() { |
| 35 | if [[ -n "$runtime_pid" ]] && kill -0 "$runtime_pid" 2>/dev/null; then |
| 36 | echo "" |
| 37 | echo "Stopping runtime (pid $runtime_pid)..." |
| 38 | kill "$runtime_pid" 2>/dev/null || true |
| 39 | wait "$runtime_pid" 2>/dev/null || true |
| 40 | fi |
| 41 | } |
| 42 | trap cleanup EXIT INT TERM |
| 43 | |
| 44 | echo "Starting runtime on $runtime_url ..." |
| 45 | codewhale serve --http \ |
| 46 | --host 127.0.0.1 \ |
| 47 | --port "$port" \ |
| 48 | --auth-token "$CODEWHALE_RUNTIME_TOKEN" & |
| 49 | runtime_pid=$! |
| 50 | |
| 51 | # Wait for /health before handing over to the bridge, so the first pairing |
| 52 | # message does not race a runtime that has not bound its port yet. |
| 53 | for _ in $(seq 1 60); do |
| 54 | if curl -fsS "$runtime_url/health" >/dev/null 2>&1; then |
| 55 | break |
| 56 | fi |
| 57 | if ! kill -0 "$runtime_pid" 2>/dev/null; then |
| 58 | echo "Runtime exited before becoming healthy." >&2 |
| 59 | exit 1 |
| 60 | fi |
| 61 | sleep 0.5 |
| 62 | done |
| 63 | |
| 64 | if ! curl -fsS "$runtime_url/health" >/dev/null 2>&1; then |
| 65 | echo "Runtime did not become healthy at $runtime_url/health within 30s." >&2 |
| 66 | exit 1 |
| 67 | fi |
| 68 | |
| 69 | echo "Runtime is healthy. Starting Weixin bridge..." |
| 70 | echo "Allow-unlisted (first pairing): $WEIXIN_ALLOW_UNLISTED" |
| 71 | echo "State dir: $WEIXIN_STATE_DIR" |
| 72 | echo "" |
| 73 | |
| 74 | cd "$bridge_dir" |
| 75 | node src/index.mjs |
| 76 |