-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathwatcher.sh
More file actions
executable file
·180 lines (155 loc) · 7.23 KB
/
Copy pathwatcher.sh
File metadata and controls
executable file
·180 lines (155 loc) · 7.23 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
#!/usr/bin/env bash
# Apollo Watcher (Cloud) - Install, Update, and Run
#
# Connects to Apollo's cloud backend — no Docker required.
#
# First run: Installs prerequisites, sets up hooks, starts the Watcher UI
# Later runs: Checks for updates, starts the Watcher UI
#
# Usage:
# ./watcher.sh # Normal start (with auto-update)
# ./watcher.sh --no-update # Skip update check
# ./watcher.sh --install-only # Install/update prerequisites without starting
# ./watcher.sh --foreground # Run in this terminal (Ctrl-C to stop); don't enable login start
# ./watcher.sh --stop # Stop the background Watcher (returns at next login unless disabled)
# ./watcher.sh --enable-autostart # Start automatically on login from now on
# ./watcher.sh --disable-autostart# Stop starting automatically on login
# ./watcher.sh -y, --yes # Non-interactive: assume "yes" to prompts (unattended/MDM installs)
# ./watcher.sh --autostart # Internal: launched by the login agent (checks the setting)
#
# By default a normal run installs the login agent and launches the Watcher in the
# background (supervised; restarts on crash, starts at login), prints the URL, and
# returns. Use --foreground to run it attached in this terminal instead.
#
# Port configuration (via .env or environment variables):
# WATCHER_BASE_PORT Base port; backend = base + 1 (default: 31410)
# WATCHER_BACKEND_PORT Watcher UI backend port (default: 31411)
# The cloud release backend serves the built frontend, so hook links use
# WATCHER_BACKEND_PORT — there is no separate frontend port to configure.
#
# Logic shared with local/watcher.sh lives in watcher-common.sh (sourced below).
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=../workos_env.sh
source "$SCRIPT_DIR/workos_env.sh"
# shellcheck source=../watcher-common.sh
source "$SCRIPT_DIR/watcher-common.sh"
WATCHER_VERSION="$(cat "$SCRIPT_DIR/.version" 2>/dev/null || true)"
if [ -z "$WATCHER_VERSION" ]; then
WATCHER_VERSION="unknown"
fi
# Ensure WATCHER_API_URL has a scheme: keep existing http(s)://, add http://
# for localhost, default to https:// for everything else (bare hostnames).
normalize_watcher_api_url() {
local raw_url="$1"
if [[ "$raw_url" =~ ^https?:// ]]; then
printf '%s\n' "$raw_url"
return 0
fi
if [[ "$raw_url" =~ ^(localhost|127\.0\.0\.1)([:/]|$) ]]; then
printf 'http://%s\n' "$raw_url"
return 0
fi
printf 'https://%s\n' "$raw_url"
}
configure_release_ports() {
WATCHER_BASE_PORT="${WATCHER_BASE_PORT:-31410}"
export WATCHER_BACKEND_PORT="${WATCHER_BACKEND_PORT:-$((WATCHER_BASE_PORT + 1))}"
# In release artifacts, the backend serves the built frontend. Hook links
# must therefore point at the backend port, not the dev Vite port.
export WATCHER_FRONTEND_PORT="$WATCHER_BACKEND_PORT"
}
if [[ "${BASH_SOURCE[0]}" != "$0" ]]; then
return 0
fi
wc_parse_flags "$@"
wc_autostart_guard
wc_stop_and_exit_if_requested
# Capture caller-provided URL overrides before sourcing .env. .env is rewritten
# on every run (see below), so without this, a value baked into .env from a prior
# run would clobber the caller's env var.
CALLER_WATCHER_API_URL="${WATCHER_API_URL:-}"
CALLER_XYLON_URL="${XYLON_URL:-}"
CALLER_VITE_WORKOS_CLIENT_ID="${VITE_WORKOS_CLIENT_ID:-}"
CALLER_WATCHER_AUTH_MODE="${WATCHER_AUTH_MODE:-}"
wc_source_dotenv
# Stop on retired basic-auth config before rewriting .env (below) or starting services.
check_legacy_basic_auth
# Precedence: caller's env vars > values sourced from .env > default. Both
# WATCHER_API_URL and XYLON_URL are accepted at each layer, in that order.
XYLON_URL="${CALLER_WATCHER_API_URL:-${CALLER_XYLON_URL:-${WATCHER_API_URL:-${XYLON_URL:-https://app.apolloresearch.ai/api}}}}"
export XYLON_URL="$(normalize_watcher_api_url "$XYLON_URL")"
# Caller wins over any stale .env value (e.g. previous run baked WATCHER_AUTH_MODE=workos
# and current invocation wants proxy). When caller didn't set anything, the .env value
# (if present) survives via the source above.
if [ -n "$CALLER_WATCHER_AUTH_MODE" ]; then
export WATCHER_AUTH_MODE="$CALLER_WATCHER_AUTH_MODE"
fi
configure_workos_client_id
# Port defaults — set WATCHER_BASE_PORT to shift all ports together,
# or override WATCHER_BACKEND_PORT for fine-grained control.
configure_release_ports
# Bail (or, for an autostart toggle, fast-path) if a watcher is already serving —
# before any prerequisite / auto-update / GitHub-auth work, so persisting a toggle
# on an already-running watcher doesn't require unrelated startup steps.
wc_collision_check
wc_apply_toggle_and_exit_if_running
# ─── Prerequisites ───
echo "Watcher v${WATCHER_VERSION}"
echo "=================="
echo ""
wc_ensure_homebrew
wc_ensure_core_tools
wc_auto_update "$@"
[ "$INSTALL_ONLY" = true ] && { echo "Install complete."; exit 0; }
wc_install_deps
# ─── Write .env for hooks ───
# Hooks run as separate processes (invoked by the agent client) and don't inherit
# our shell environment. Write the current config to .env so hooks can source it.
ENV_FILE="$SCRIPT_DIR/.env"
{
echo "# Auto-generated by watcher.sh — hooks source this file on each invocation."
echo "# Edit manually or re-run watcher.sh to regenerate."
echo ""
echo "# Watcher API (cloud)"
echo "WATCHER_API_URL=${XYLON_URL}"
echo "XYLON_URL=${XYLON_URL}"
echo ""
echo "# Auth mode (proxy = selfhost / Tailscale-fronted Xylon, no WorkOS login)"
echo "WATCHER_AUTH_MODE=${WATCHER_AUTH_MODE:-}"
echo ""
echo "# WorkOS (injected into UI by the local backend at serve time)"
echo "VITE_WORKOS_CLIENT_ID=${VITE_WORKOS_CLIENT_ID}"
echo "XYLON_WORKOS_CLIENT_ID=${XYLON_WORKOS_CLIENT_ID}"
echo ""
echo "# Ports (set WATCHER_BASE_PORT to shift all ports together)"
echo "WATCHER_BASE_PORT=${WATCHER_BASE_PORT}"
echo "WATCHER_FRONTEND_PORT=${WATCHER_FRONTEND_PORT}"
echo "WATCHER_BACKEND_PORT=${WATCHER_BACKEND_PORT}"
echo ""
echo "# Internal-CA / self-signed cert path for hooks (preserved across rewrites)."
[ -n "${SSL_CERT_FILE:-}" ] && emit_env_single_quoted "SSL_CERT_FILE" "$SSL_CERT_FILE"
echo ""
echo "# Self-host hook headers (arbitrary HTTP headers the hooks send to Xylon)."
echo "# Preserved across rewrites so customer-set WATCHER_HEADER_* proxy/identity"
echo "# headers survive re-running this launcher. Single-quoted so values round-trip through"
echo "# both bash source and python-dotenv regardless of contents (see emit_env_single_quoted)."
for _hook_header in ${!WATCHER_HEADER_@}; do
emit_env_single_quoted "$_hook_header" "${!_hook_header}"
done
} > "$ENV_FILE"
echo "Wrote $ENV_FILE"
# ─── Setup agent hooks + start Watcher ───
wc_setup_hooks_and_install_agent
# Managed background (default / --enable-autostart) hands off to the login agent and
# exits here; --autostart (supervised) and --foreground fall through to run attached.
wc_resolve_run_mode
echo ""
echo "Apollo Watcher is running!"
echo " Watcher UI: http://localhost:${WATCHER_BACKEND_PORT}"
echo " Local API: http://localhost:${WATCHER_BACKEND_PORT}"
echo " Cloud API: ${XYLON_URL}"
echo ""
echo "Press Ctrl-C to stop."
echo ""
wc_start_watcher