#!/usr/bin/env bash set -euo pipefail if [[ $# -lt 1 || $# -gt 2 ]]; then echo "Usage: pipo-deploy [name|agent/site]" >&2 exit 1 fi port="$1" target="${2:-$port}" if ! [[ "$port" =~ ^[0-9]+$ ]]; then echo "Port must be numeric." >&2 exit 1 fi if (( port < 3001 || port > 3010 )); then echo "Port must be between 3001 and 3010." >&2 exit 1 fi if ! curl -fsS --max-time 2 "http://127.0.0.1:${port}/" >/dev/null; then echo "Nothing is responding on http://127.0.0.1:${port}/" >&2 exit 1 fi path="$( TARGET="$target" PORT="$port" python3 - <<'PY' import os import re import sys target = os.environ["TARGET"].strip() port = os.environ["PORT"].strip() def sanitize_segment(value: str) -> str: value = value.lower() value = re.sub(r"[^a-z0-9]+", "-", value) return value.strip("-") if "/" in target: raw_parts = [part for part in target.strip("/").split("/") if part] parts = [sanitize_segment(part) for part in raw_parts] parts = [part for part in parts if part] if not parts: parts = [port] print("/" + "/".join(parts) + "/") else: name = sanitize_segment(target) if not name: name = port print(f"/preview/{name}/") PY )" if [[ "$path" == "/" ]]; then echo "Refusing to publish to /." >&2 exit 1 fi "/home/ubuntu/.local/bin/openclaw-serve-guard" tailscale serve --bg --set-path "$path" "http://127.0.0.1:${port}" >/dev/null host="$( python3 - <<'PY' import json import subprocess raw = subprocess.check_output(["tailscale", "status", "--json"], text=True) data = json.loads(raw) print(data["Self"]["DNSName"].rstrip(".")) PY )" printf 'Preview available at https://%s%s\n' "$host" "$path"