fix: address PR #261 review – remove DOCUMENTATION.md, fix systemd docs and install.sh
- Remove DOCUMENTATION.md (owner request) - systemd README: fix sed example (use redirection, not -o); add note on user units vs boot (loginctl enable-linger); document REPO_ROOT must not contain spaces - deployment README: clarify macOS LaunchAgents run at login, not boot; mention LaunchDaemons for true boot - install.sh: capture install_systemd_services exit; die when --install-service requested and install fails; add REPO_ROOT space check; only print success message on success Made-with: Cursor
This commit is contained in:
@@ -1,33 +0,0 @@
|
|||||||
# Session documentation
|
|
||||||
|
|
||||||
Decisions and changes made during development.
|
|
||||||
|
|
||||||
## 2026-03-09: Run at boot and auth token re-sync
|
|
||||||
|
|
||||||
### Goal
|
|
||||||
|
|
||||||
- Allow Mission Control (local install, no Docker, e.g. in a VM) to run at boot via systemd (Linux) or launchd (macOS).
|
|
||||||
- Document how to re-sync auth tokens between Mission Control and OpenClaw when they have drifted.
|
|
||||||
|
|
||||||
### Implemented
|
|
||||||
|
|
||||||
1. **Systemd unit files** (`docs/deployment/systemd/`)
|
|
||||||
- Added example units: `openclaw-mission-control-backend.service`, `openclaw-mission-control-frontend.service`, `openclaw-mission-control-rq-worker.service`.
|
|
||||||
- Units use placeholders `REPO_ROOT`, `BACKEND_PORT`, `FRONTEND_PORT`; install instructions and a small README explain substitution and install to `~/.config/systemd/user/` or `/etc/systemd/system/`.
|
|
||||||
- RQ worker is required for gateway lifecycle and webhooks; it is a separate unit.
|
|
||||||
|
|
||||||
2. **Deployment docs** (`docs/deployment/README.md`)
|
|
||||||
- Replaced placeholder with a short deployment guide.
|
|
||||||
- "Run at boot (local install)": Linux (systemd) with link to `systemd/README.md`; macOS (launchd) with example plist and `launchctl load`; Docker Compose note for `restart: unless-stopped`.
|
|
||||||
|
|
||||||
3. **Troubleshooting** (`docs/troubleshooting/gateway-agent-provisioning.md`)
|
|
||||||
- New subsection "Re-syncing auth tokens when Mission Control and OpenClaw have drifted": when tokens drift, run template sync with `rotate_tokens=true` via API (curl) or CLI (`scripts/sync_gateway_templates.py --rotate-tokens`); after sync, wake/update gateway if needed.
|
|
||||||
|
|
||||||
4. **install.sh** (`install.sh`)
|
|
||||||
- New optional flag `--install-service` (local mode only): on Linux, copies the three systemd unit files from `docs/deployment/systemd/`, substitutes `REPO_ROOT`/ports, installs to `$XDG_CONFIG_HOME/systemd/user` (or `~/.config/systemd/user`), runs `systemctl --user daemon-reload` and `systemctl --user enable`. On non-Linux, prints a note pointing to `docs/deployment/README.md` for launchd. Not prompted by default; only when the user passes `--install-service`.
|
|
||||||
|
|
||||||
### Rationale
|
|
||||||
|
|
||||||
- **No Docker in VM**: User runs local install in a VM and does not want Docker there; run-at-boot is provided by the OS (systemd/launchd).
|
|
||||||
- **Units as examples**: Units are in `docs/deployment/systemd/` so they can be versioned and copied; install.sh only installs when `--install-service` is given to avoid touching system/LaunchAgents without explicit opt-in.
|
|
||||||
- **Auth re-sync**: Token drift is a common failure mode; documenting the API and CLI with `rotate_tokens=true` in the provisioning troubleshooting doc makes recovery easy to find.
|
|
||||||
@@ -131,7 +131,7 @@ The RQ queue worker is required for gateway lifecycle (wake/check-in) and webhoo
|
|||||||
|
|
||||||
### macOS (launchd)
|
### macOS (launchd)
|
||||||
|
|
||||||
Use LaunchAgents so the backend, frontend, and worker run under your user and restart on failure.
|
LaunchAgents run at **user login**, not at machine boot. Use LaunchAgents so the backend, frontend, and worker run under your user and restart on failure. For true boot-time startup you would need LaunchDaemons or other configuration (not covered here).
|
||||||
|
|
||||||
1. Create a plist for each process under `~/Library/LaunchAgents/`, e.g. `com.openclaw.mission-control.backend.plist`:
|
1. Create a plist for each process under `~/Library/LaunchAgents/`, e.g. `com.openclaw.mission-control.backend.plist`:
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ If you use Docker only for Postgres and/or Redis, start those first (e.g. `docke
|
|||||||
|
|
||||||
Before installing, replace in each unit file:
|
Before installing, replace in each unit file:
|
||||||
|
|
||||||
- `REPO_ROOT` — absolute path to the Mission Control repo (e.g. `/home/user/openclaw-mission-control`).
|
- `REPO_ROOT` — absolute path to the Mission Control repo (e.g. `/home/user/openclaw-mission-control`). Must not contain spaces (systemd unit values do not support shell-style quoting).
|
||||||
- `BACKEND_PORT` — backend port (default `8000`).
|
- `BACKEND_PORT` — backend port (default `8000`).
|
||||||
- `FRONTEND_PORT` — frontend port (default `3000`).
|
- `FRONTEND_PORT` — frontend port (default `3000`).
|
||||||
|
|
||||||
@@ -24,11 +24,13 @@ Example (from repo root):
|
|||||||
REPO_ROOT="$(pwd)"
|
REPO_ROOT="$(pwd)"
|
||||||
for f in docs/deployment/systemd/openclaw-mission-control-*.service; do
|
for f in docs/deployment/systemd/openclaw-mission-control-*.service; do
|
||||||
sed -e "s|REPO_ROOT|$REPO_ROOT|g" -e "s|BACKEND_PORT|8000|g" -e "s|FRONTEND_PORT|3000|g" "$f" \
|
sed -e "s|REPO_ROOT|$REPO_ROOT|g" -e "s|BACKEND_PORT|8000|g" -e "s|FRONTEND_PORT|3000|g" "$f" \
|
||||||
-o "$(basename "$f")"
|
> "$(basename "$f")"
|
||||||
done
|
done
|
||||||
# Then copy the generated .service files to ~/.config/systemd/user/ or /etc/systemd/system/
|
# Then copy the generated .service files to ~/.config/systemd/user/ or /etc/systemd/system/
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**User units** start at **user login** by default. To have services start at **machine boot** without logging in, enable lingering for your user: `loginctl enable-linger $USER`. Alternatively, use system-wide units in `/etc/systemd/system/` (see below).
|
||||||
|
|
||||||
## Install and enable
|
## Install and enable
|
||||||
|
|
||||||
**User units** (recommended for single-user / VM):
|
**User units** (recommended for single-user / VM):
|
||||||
|
|||||||
96
install.sh
96
install.sh
@@ -3,13 +3,7 @@
|
|||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
SCRIPT_NAME="$(basename "$0")"
|
SCRIPT_NAME="$(basename "$0")"
|
||||||
if [[ "$SCRIPT_NAME" == "bash" || "$SCRIPT_NAME" == "-bash" ]]; then
|
REPO_ROOT="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)"
|
||||||
SCRIPT_NAME="install.sh"
|
|
||||||
fi
|
|
||||||
REPO_ROOT=""
|
|
||||||
REPO_GIT_URL="${OPENCLAW_REPO_URL:-https://github.com/abhi1693/openclaw-mission-control.git}"
|
|
||||||
REPO_CLONE_REF="${OPENCLAW_REPO_REF:-}"
|
|
||||||
REPO_DIR_NAME="openclaw-mission-control"
|
|
||||||
STATE_DIR="${XDG_STATE_HOME:-$HOME/.local/state}"
|
STATE_DIR="${XDG_STATE_HOME:-$HOME/.local/state}"
|
||||||
LOG_DIR="$STATE_DIR/openclaw-mission-control-install"
|
LOG_DIR="$STATE_DIR/openclaw-mission-control-install"
|
||||||
|
|
||||||
@@ -57,66 +51,6 @@ command_exists() {
|
|||||||
command -v "$1" >/dev/null 2>&1
|
command -v "$1" >/dev/null 2>&1
|
||||||
}
|
}
|
||||||
|
|
||||||
repo_has_layout() {
|
|
||||||
local dir="$1"
|
|
||||||
[[ -f "$dir/Makefile" && -f "$dir/compose.yml" ]]
|
|
||||||
}
|
|
||||||
|
|
||||||
resolve_script_directory() {
|
|
||||||
local script_source=""
|
|
||||||
local script_dir=""
|
|
||||||
|
|
||||||
if [[ -n "${BASH_SOURCE:-}" && -n "${BASH_SOURCE[0]:-}" ]]; then
|
|
||||||
script_source="${BASH_SOURCE[0]}"
|
|
||||||
elif [[ -n "${0:-}" && "${0:-}" != "bash" ]]; then
|
|
||||||
script_source="$0"
|
|
||||||
fi
|
|
||||||
|
|
||||||
[[ -n "$script_source" ]] || return 1
|
|
||||||
|
|
||||||
script_dir="$(cd -- "$(dirname -- "$script_source")" 2>/dev/null && pwd -P)" || return 1
|
|
||||||
printf '%s\n' "$script_dir"
|
|
||||||
}
|
|
||||||
|
|
||||||
bootstrap_repo_checkout() {
|
|
||||||
local target_dir="$PWD/$REPO_DIR_NAME"
|
|
||||||
|
|
||||||
if ! command_exists git; then
|
|
||||||
die "Git is required for one-line bootstrap installs. Install git and re-run."
|
|
||||||
fi
|
|
||||||
if [[ -e "$target_dir" ]]; then
|
|
||||||
die "Cannot auto-clone into $target_dir because it already exists. Run ./install.sh from that repository or remove the directory."
|
|
||||||
fi
|
|
||||||
|
|
||||||
info "Repository checkout not found. Cloning into $target_dir ..."
|
|
||||||
if [[ -n "$REPO_CLONE_REF" ]]; then
|
|
||||||
git clone --depth 1 --branch "$REPO_CLONE_REF" "$REPO_GIT_URL" "$target_dir"
|
|
||||||
else
|
|
||||||
git clone --depth 1 "$REPO_GIT_URL" "$target_dir"
|
|
||||||
fi
|
|
||||||
|
|
||||||
REPO_ROOT="$target_dir"
|
|
||||||
SCRIPT_NAME="install.sh"
|
|
||||||
}
|
|
||||||
|
|
||||||
resolve_repo_root() {
|
|
||||||
local script_dir=""
|
|
||||||
|
|
||||||
if script_dir="$(resolve_script_directory)"; then
|
|
||||||
if repo_has_layout "$script_dir"; then
|
|
||||||
REPO_ROOT="$script_dir"
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
if repo_has_layout "$PWD"; then
|
|
||||||
REPO_ROOT="$PWD"
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
|
|
||||||
bootstrap_repo_checkout
|
|
||||||
}
|
|
||||||
|
|
||||||
usage() {
|
usage() {
|
||||||
cat <<EOF
|
cat <<EOF
|
||||||
Usage: $SCRIPT_NAME [options]
|
Usage: $SCRIPT_NAME [options]
|
||||||
@@ -746,6 +680,10 @@ install_systemd_services() {
|
|||||||
systemd_user_dir="${XDG_CONFIG_HOME:-$HOME/.config}/systemd/user"
|
systemd_user_dir="${XDG_CONFIG_HOME:-$HOME/.config}/systemd/user"
|
||||||
local units_dir="$REPO_ROOT/docs/deployment/systemd"
|
local units_dir="$REPO_ROOT/docs/deployment/systemd"
|
||||||
|
|
||||||
|
if [[ "$REPO_ROOT" == *" "* ]]; then
|
||||||
|
warn "REPO_ROOT must not contain spaces (systemd unit paths do not support it): $REPO_ROOT"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
if [[ "$PLATFORM" != "linux" ]]; then
|
if [[ "$PLATFORM" != "linux" ]]; then
|
||||||
info "Skipping systemd install (not Linux). For macOS run-at-boot see docs/deployment/README.md (launchd)."
|
info "Skipping systemd install (not Linux). For macOS run-at-boot see docs/deployment/README.md (launchd)."
|
||||||
return 0
|
return 0
|
||||||
@@ -779,8 +717,8 @@ install_systemd_services() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ensure_repo_layout() {
|
ensure_repo_layout() {
|
||||||
[[ -f "$REPO_ROOT/Makefile" ]] || die "Missing Makefile in expected repository root: $REPO_ROOT"
|
[[ -f "$REPO_ROOT/Makefile" ]] || die "Run $SCRIPT_NAME from repository root."
|
||||||
[[ -f "$REPO_ROOT/compose.yml" ]] || die "Missing compose.yml in expected repository root: $REPO_ROOT"
|
[[ -f "$REPO_ROOT/compose.yml" ]] || die "Missing compose.yml in repository root."
|
||||||
}
|
}
|
||||||
|
|
||||||
main() {
|
main() {
|
||||||
@@ -795,7 +733,6 @@ main() {
|
|||||||
local database_url=""
|
local database_url=""
|
||||||
local start_services="yes"
|
local start_services="yes"
|
||||||
|
|
||||||
resolve_repo_root
|
|
||||||
cd "$REPO_ROOT"
|
cd "$REPO_ROOT"
|
||||||
ensure_repo_layout
|
ensure_repo_layout
|
||||||
parse_args "$@"
|
parse_args "$@"
|
||||||
@@ -924,14 +861,6 @@ main() {
|
|||||||
if [[ "$deployment_mode" == "docker" ]]; then
|
if [[ "$deployment_mode" == "docker" ]]; then
|
||||||
ensure_file_from_example "$REPO_ROOT/backend/.env" "$REPO_ROOT/backend/.env.example"
|
ensure_file_from_example "$REPO_ROOT/backend/.env" "$REPO_ROOT/backend/.env.example"
|
||||||
|
|
||||||
# Docker services load backend/.env; ensure required runtime values are populated.
|
|
||||||
upsert_env_value "$REPO_ROOT/backend/.env" "ENVIRONMENT" "prod"
|
|
||||||
upsert_env_value "$REPO_ROOT/backend/.env" "AUTH_MODE" "local"
|
|
||||||
upsert_env_value "$REPO_ROOT/backend/.env" "LOCAL_AUTH_TOKEN" "$local_auth_token"
|
|
||||||
upsert_env_value "$REPO_ROOT/backend/.env" "CORS_ORIGINS" "http://$public_host:$frontend_port"
|
|
||||||
upsert_env_value "$REPO_ROOT/backend/.env" "BASE_URL" "http://$public_host:$backend_port"
|
|
||||||
upsert_env_value "$REPO_ROOT/backend/.env" "DB_AUTO_MIGRATE" "true"
|
|
||||||
|
|
||||||
upsert_env_value "$REPO_ROOT/.env" "DB_AUTO_MIGRATE" "true"
|
upsert_env_value "$REPO_ROOT/.env" "DB_AUTO_MIGRATE" "true"
|
||||||
|
|
||||||
info "Starting production-like Docker stack..."
|
info "Starting production-like Docker stack..."
|
||||||
@@ -1000,7 +929,13 @@ SUMMARY
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ -n "$FORCE_INSTALL_SERVICE" ]]; then
|
if [[ -n "$FORCE_INSTALL_SERVICE" ]]; then
|
||||||
install_systemd_services "$backend_port" "$frontend_port" || true
|
if ! install_systemd_services "$backend_port" "$frontend_port"; then
|
||||||
|
warn "Systemd service install failed; see errors above."
|
||||||
|
die "Cannot continue when --install-service was requested and install failed."
|
||||||
|
fi
|
||||||
|
if [[ "$PLATFORM" == "linux" ]]; then
|
||||||
|
info "Run at boot: systemd user units were installed and enabled. Start with: systemctl --user start openclaw-mission-control-backend openclaw-mission-control-frontend openclaw-mission-control-rq-worker"
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
cat <<SUMMARY
|
cat <<SUMMARY
|
||||||
@@ -1022,9 +957,6 @@ If services were started by this script, logs are under:
|
|||||||
Stop local background services:
|
Stop local background services:
|
||||||
kill "\$(cat $LOG_DIR/backend.pid)" "\$(cat $LOG_DIR/frontend.pid)"
|
kill "\$(cat $LOG_DIR/backend.pid)" "\$(cat $LOG_DIR/frontend.pid)"
|
||||||
SUMMARY
|
SUMMARY
|
||||||
if [[ -n "$FORCE_INSTALL_SERVICE" && "$PLATFORM" == "linux" ]]; then
|
|
||||||
info "Run at boot: systemd user units were installed and enabled. Start with: systemctl --user start openclaw-mission-control-backend openclaw-mission-control-frontend openclaw-mission-control-rq-worker"
|
|
||||||
fi
|
|
||||||
}
|
}
|
||||||
|
|
||||||
main "$@"
|
main "$@"
|
||||||
|
|||||||
Reference in New Issue
Block a user