From c912491365cd422e9f1033085e622942f7239e1b Mon Sep 17 00:00:00 2001 From: Koosha Paridehpour Date: Wed, 25 Feb 2026 05:44:50 -0700 Subject: [PATCH] feat: add service setup helper and homebrew launch docs --- docs/install.md | 20 +- examples/homebrew/cliproxyapi-plusplus.rb | 27 +++ scripts/service | 232 ++++++++++++++++++++++ 3 files changed, 274 insertions(+), 5 deletions(-) create mode 100644 examples/homebrew/cliproxyapi-plusplus.rb create mode 100755 scripts/service diff --git a/docs/install.md b/docs/install.md index 71ca6b2a38..91c3330d8f 100644 --- a/docs/install.md +++ b/docs/install.md @@ -89,9 +89,6 @@ process-compose -f examples/process-compose.dev.yaml up Then edit `config.yaml` or files under `auth-dir`; the running process reloads changes automatically. -<<<<<<< HEAD -## Option D: Go SDK / Embedding -======= For Antigravity quota/routing tuning, this is hot-reload friendly: - `quota-exceeded.switch-project` @@ -155,6 +152,19 @@ sudo systemctl restart cliproxyapi-plusplus sudo systemctl stop cliproxyapi-plusplus ``` +Cross-platform helper (optional): + +```bash +./scripts/service install +./scripts/service start +./scripts/service status +./scripts/service restart +./scripts/service stop +./scripts/service remove +``` + +On Linux the script writes the systemd unit to `/etc/systemd/system` and requires root privileges. + ### macOS (Homebrew + launchd) Homebrew installs typically place artifacts under `/opt/homebrew`. If installed elsewhere, keep the same launchd flow and swap the binary/config paths. @@ -166,9 +176,10 @@ launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.router-for-me.clipro launchctl kickstart -k gui/$(id -u)/com.router-for-me.cliproxyapi-plusplus ``` -If your Homebrew formula supports service hooks: +You can also use a local Homebrew formula with service hooks: ```bash +brew install --HEAD --formula examples/homebrew/cliproxyapi-plusplus.rb brew services start cliproxyapi-plusplus brew services restart cliproxyapi-plusplus ``` @@ -184,7 +195,6 @@ Run as Administrator: ``` ## Option E: Go SDK / Embedding ->>>>>>> archive/pr-234-head-20260223 ```bash go get github.com/KooshaPari/cliproxyapi-plusplus/sdk/cliproxy diff --git a/examples/homebrew/cliproxyapi-plusplus.rb b/examples/homebrew/cliproxyapi-plusplus.rb new file mode 100644 index 0000000000..61d090c00a --- /dev/null +++ b/examples/homebrew/cliproxyapi-plusplus.rb @@ -0,0 +1,27 @@ +class CliproxyapiPlusplus < Formula + desc "LLM proxy CLI for OpenAI-compatible and provider-specific APIs" + homepage "https://github.com/router-for-me/CLIProxyAPI" + head "https://github.com/router-for-me/CLIProxyAPI.git", branch: "main" + + depends_on "go" => :build + + def install + system "go", "build", "-o", bin/"cliproxyapi++", "./cmd/server" + end + + service do + run [opt_bin/"cliproxyapi++", "--config", etc/"cliproxyapi/config.yaml"] + working_dir var/"log/cliproxyapi" + keep_alive true + log_path var/"log/cliproxyapi-plusplus.log" + error_log_path var/"log/cliproxyapi-plusplus.err" + end + + def post_install + (etc/"cliproxyapi").mkpath + end + + test do + assert_predicate bin/"cliproxyapi++", :exist? + end +end diff --git a/scripts/service b/scripts/service new file mode 100755 index 0000000000..400efdfcb7 --- /dev/null +++ b/scripts/service @@ -0,0 +1,232 @@ +#!/usr/bin/env bash +set -euo pipefail + +IFS=$'\n\t' + +SERVICE_NAME="cliproxyapi-plusplus" +SERVICE_LABEL="com.router-for-me.cliproxyapi-plusplus" +SYSTEMD_UNIT="$SERVICE_NAME.service" +SYSTEMD_DEST="/etc/systemd/system" +SYSTEMD_ENV="/etc/default/cliproxyapi" + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" +LAUNCHD_SOURCE="$REPO_ROOT/examples/launchd/$SERVICE_LABEL.plist" +SYSTEMD_SOURCE="$REPO_ROOT/examples/systemd/$SYSTEMD_UNIT" +SYSTEMD_ENV_SOURCE="$REPO_ROOT/examples/systemd/cliproxyapi-plusplus.env" + +usage() { + cat <<'USAGE' +Usage: + ./scripts/service [--config PATH] + +Actions: + install register service files and enable on boot + remove disable and remove service files + start start service now + stop stop service now + restart stop then start + status show service status + +Options: + --config PATH only used for macOS launchd fallback +USAGE +} + +command_exists() { + command -v "$1" >/dev/null 2>&1 +} + +ensure_root() { + if [ "$(id -u)" -ne 0 ]; then + echo "This action requires root on Linux. Re-run with sudo." + exit 1 + fi +} + +has_brew_formula() { + command_exists brew && brew list --formula | grep -qx "cliproxyapi-plusplus" +} + +install_macos() { + local cfg="${1:-/opt/homebrew/etc/cliproxyapi/config.yaml}" + + if has_brew_formula; then + brew services start cliproxyapi-plusplus + return 0 + fi + + if ! command_exists launchctl || ! command_exists sed; then + echo "launchctl and sed are required for fallback launchd setup" + exit 1 + fi + + local prefix + local plist_dest="$HOME/Library/LaunchAgents/$SERVICE_LABEL.plist" + prefix="$(command_exists brew && brew --prefix || echo /opt/homebrew)" + mkdir -p "$HOME/Library/LaunchAgents" + sed -e "s#/opt/homebrew#$prefix#g" \ + -e "s#/opt/homebrew/etc/cliproxyapi/config.yaml#$cfg#g" \ + "$LAUNCHD_SOURCE" > "$plist_dest" + launchctl bootstrap "gui/$(id -u)" "$plist_dest" +} + +remove_macos() { + if has_brew_formula; then + brew services stop cliproxyapi-plusplus + brew services uninstall cliproxyapi-plusplus || true + return 0 + fi + + if command_exists launchctl; then + launchctl bootout "gui/$(id -u)" "$SERVICE_LABEL" || true + fi + rm -f "$HOME/Library/LaunchAgents/$SERVICE_LABEL.plist" +} + +start_macos() { + if has_brew_formula; then + brew services start cliproxyapi-plusplus + return 0 + fi + + if command_exists launchctl; then + launchctl kickstart -k "gui/$(id -u)/$SERVICE_LABEL" + fi +} + +stop_macos() { + if has_brew_formula; then + brew services stop cliproxyapi-plusplus + return 0 + fi + + if command_exists launchctl; then + launchctl stop "$SERVICE_LABEL" + fi +} + +status_macos() { + if has_brew_formula; then + brew services list | grep "cliproxyapi-plusplus" + return 0 + fi + + if command_exists launchctl; then + launchctl print "gui/$(id -u)/$SERVICE_LABEL" || true + fi +} + +install_linux() { + ensure_root + + if ! command_exists systemctl; then + echo "systemctl not available" + exit 1 + fi + if [ ! -f "$SYSTEMD_SOURCE" ]; then + echo "systemd unit template missing: $SYSTEMD_SOURCE" + exit 1 + fi + + mkdir -p "$SYSTEMD_DEST" + cp "$SYSTEMD_SOURCE" "$SYSTEMD_DEST/$SYSTEMD_UNIT" + if [ -f "$SYSTEMD_ENV_SOURCE" ]; then + mkdir -p /etc/default + cp "$SYSTEMD_ENV_SOURCE" "$SYSTEMD_ENV" + fi + systemctl daemon-reload + systemctl enable "$SYSTEMD_UNIT" +} + +remove_linux() { + ensure_root + + if ! command_exists systemctl; then + echo "systemctl not available" + exit 1 + fi + + systemctl disable "$SYSTEMD_UNIT" || true + systemctl stop "$SYSTEMD_UNIT" || true + rm -f "$SYSTEMD_DEST/$SYSTEMD_UNIT" "$SYSTEMD_ENV" + systemctl daemon-reload +} + +start_linux() { + ensure_root + systemctl start "$SYSTEMD_UNIT" +} + +stop_linux() { + ensure_root + systemctl stop "$SYSTEMD_UNIT" +} + +status_linux() { + if ! command_exists systemctl; then + echo "systemctl not available" + exit 1 + fi + systemctl status "$SYSTEMD_UNIT" --no-pager +} + +main() { + local action="${1:-}" + local config_path="/opt/homebrew/etc/cliproxyapi/config.yaml" + shift || true + + while [ "$#" -gt 0 ]; do + case "$1" in + --config) + config_path="${2:-}" + shift 2 + ;; + -h|--help) + usage + return 0 + ;; + *) + echo "Unknown option: $1" + usage + return 1 + ;; + esac + done + + if [ -z "$action" ] || [ "$action" = "--help" ] || [ "$action" = "-h" ]; then + usage + return 0 + fi + + case "$(uname -s)" in + Darwin) + case "$action" in + install) install_macos "$config_path" ;; + remove|uninstall) remove_macos ;; + start) start_macos ;; + stop) stop_macos ;; + restart) stop_macos; start_macos ;; + status) status_macos ;; + *) usage; exit 1 ;; + esac + ;; + Linux) + case "$action" in + install) install_linux ;; + remove|uninstall) remove_linux ;; + start) start_linux ;; + stop) stop_linux ;; + restart) stop_linux; start_linux ;; + status) status_linux ;; + *) usage; exit 1 ;; + esac + ;; + *) + echo "Unsupported OS: $(uname -s)" + exit 1 + ;; + esac +} + +main "$@"