From 27d0afd736b0a34fa400dc8317acc54023c8f0d9 Mon Sep 17 00:00:00 2001 From: Luciano Castillo <2213102+luccast@users.noreply.github.com> Date: Sun, 1 Feb 2026 15:24:33 -0500 Subject: [PATCH] Add crabwalk CLI (#30) * feat: add crabwalk CLI - bin/crabwalk: shell wrapper with start/stop/status/update commands - daemon mode with --daemon flag - auto-detect gateway token from ~/.clawdbot/clawdbot.json - show network IPs on startup for remote access - update skill.md and README with CLI usage * auto-add ~/.local/bin to PATH during install * support zsh PATH config --- .github/workflows/release.yml | 2 +- README.md | 21 +++ bin/crabwalk | 277 ++++++++++++++++++++++++++++++++++ public/skill.json | 3 +- public/skill.md | 94 ++++++------ 5 files changed, 351 insertions(+), 46 deletions(-) create mode 100755 bin/crabwalk diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index afcaeae..0fa578e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -33,7 +33,7 @@ jobs: - name: Create build artifact run: | - tar -czvf crabwalk-${{ github.ref_name }}.tar.gz .output + tar -czvf crabwalk-${{ github.ref_name }}.tar.gz .output bin package.json - name: Upload build to release uses: softprops/action-gh-release@v1 diff --git a/README.md b/README.md index a66e739..f7ad3de 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,27 @@ Paste this link to your OpenClaw agent and ask it to install/update Crabwalk: https://raw.githubusercontent.com/luccast/crabwalk/master/public/skill.md ``` +### CLI Install + +```bash +VERSION=$(curl -s https://api.github.com/repos/luccast/crabwalk/releases/latest | grep '"tag_name"' | cut -d'"' -f4) +mkdir -p ~/.crabwalk ~/.local/bin +curl -sL "https://github.com/luccast/crabwalk/releases/download/${VERSION}/crabwalk-${VERSION}.tar.gz" | tar -xz -C ~/.crabwalk +cp ~/.crabwalk/bin/crabwalk ~/.local/bin/ +chmod +x ~/.local/bin/crabwalk +``` + +Then run: + +```bash +crabwalk # Start on 0.0.0.0:3000 +crabwalk start --daemon # Run in background +crabwalk start -p 8080 # Custom port +crabwalk stop # Stop daemon +crabwalk status # Check if running +crabwalk update # Update to latest +``` + ### Docker (recommended) ```bash diff --git a/bin/crabwalk b/bin/crabwalk new file mode 100755 index 0000000..eb9000d --- /dev/null +++ b/bin/crabwalk @@ -0,0 +1,277 @@ +#!/usr/bin/env bash +# 🦀 Crabwalk CLI + +set -e + +CRABWALK_HOME="${CRABWALK_HOME:-$HOME/.crabwalk}" +PID_FILE="$CRABWALK_HOME/crabwalk.pid" +LOG_FILE="$CRABWALK_HOME/crabwalk.log" + +# Colors +RED='\033[0;31m' +GREEN='\033[0;32m' +CYAN='\033[0;36m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +# Get version from package.json +get_version() { + cat "$CRABWALK_HOME/package.json" 2>/dev/null | grep '"version"' | cut -d'"' -f4 +} + +# Auto-detect token from OpenClaw config +auto_token() { + if [ -f "$HOME/.clawdbot/clawdbot.json" ]; then + python3 -c "import json,os; print(json.load(open(os.path.expanduser('~/.clawdbot/clawdbot.json')))['gateway']['auth']['token'])" 2>/dev/null || true + fi +} + +# Get network IPs +get_network_ips() { + if command -v hostname &>/dev/null; then + hostname -I 2>/dev/null | tr ' ' '\n' | grep -v '^$' || true + elif command -v ip &>/dev/null; then + ip -4 addr show 2>/dev/null | grep -oP '(?<=inet\s)\d+(\.\d+){3}' | grep -v '127.0.0.1' || true + fi +} + +# Check if running +is_running() { + if [ -f "$PID_FILE" ]; then + local pid=$(cat "$PID_FILE") + if kill -0 "$pid" 2>/dev/null; then + echo "$pid" + return 0 + fi + fi + return 1 +} + +# Show startup banner +show_banner() { + local port="${1:-3000}" + local host="${2:-0.0.0.0}" + local version=$(get_version) + + echo "" + echo -e "🦀 ${GREEN}Crabwalk${NC} v${version}" + echo "" + + if [ "$host" = "0.0.0.0" ]; then + echo -e " ➜ Local: ${CYAN}http://localhost:${port}/monitor${NC}" + for ip in $(get_network_ips); do + echo -e " ➜ Network: ${CYAN}http://${ip}:${port}/monitor${NC}" + done + else + echo -e " ➜ Server: ${CYAN}http://${host}:${port}/monitor${NC}" + fi + echo "" +} + +# Show help +show_help() { + local version=$(get_version) + echo "🦀 Crabwalk v${version:-unknown}" + echo "" + echo "Usage: crabwalk [options]" + echo "" + echo "Commands:" + echo " start Start server (default)" + echo " stop Stop running server" + echo " status Check if server is running" + echo " update Update to latest version" + echo " help Show this help" + echo "" + echo "Options:" + echo " -p, --port Server port (default: 3000)" + echo " -H, --host Bind address (default: 0.0.0.0)" + echo " -g, --gateway Gateway WebSocket URL" + echo " -t, --token Gateway auth token" + echo " -d, --daemon Run in background" + echo " -v, --version Show version" + echo " -h, --help Show help" + echo "" + echo "Examples:" + echo " crabwalk # Start on 0.0.0.0:3000" + echo " crabwalk start --daemon # Run in background" + echo " crabwalk start -p 8080 # Custom port" + echo " crabwalk stop # Stop daemon" + echo " crabwalk status # Check status" + echo " crabwalk update # Update to latest" +} + +# Start command +cmd_start() { + local port="${PORT:-3000}" + local host="${HOST:-0.0.0.0}" + local daemon=false + local token="${CLAWDBOT_API_TOKEN}" + local gateway="${CLAWDBOT_URL}" + + # Parse args + while [[ $# -gt 0 ]]; do + case $1 in + -p|--port) port="$2"; shift 2 ;; + -H|--host) host="$2"; shift 2 ;; + -t|--token) token="$2"; shift 2 ;; + -g|--gateway) gateway="$2"; shift 2 ;; + -d|--daemon) daemon=true; shift ;; + *) shift ;; + esac + done + + # Auto-detect token if not provided + if [ -z "$token" ]; then + token=$(auto_token) + fi + + # Check if already running + if pid=$(is_running); then + echo -e "🦀 Crabwalk is already running (PID $pid)" + echo -e " Use ${YELLOW}crabwalk stop${NC} first" + exit 1 + fi + + # Check if crabwalk is installed + if [ ! -f "$CRABWALK_HOME/.output/server/index.mjs" ]; then + echo -e "${RED}Error:${NC} Crabwalk not found at $CRABWALK_HOME" + echo "Install with: https://raw.githubusercontent.com/luccast/crabwalk/master/public/skill.md" + exit 1 + fi + + # Build env vars + export PORT="$port" + export HOST="$host" + [ -n "$token" ] && export CLAWDBOT_API_TOKEN="$token" + [ -n "$gateway" ] && export CLAWDBOT_URL="$gateway" + + if [ "$daemon" = true ]; then + # Run in background + nohup node "$CRABWALK_HOME/.output/server/index.mjs" > "$LOG_FILE" 2>&1 & + echo $! > "$PID_FILE" + + sleep 1 + if is_running >/dev/null; then + show_banner "$port" "$host" + echo -e "🦀 Running in background (PID $(cat $PID_FILE))" + echo -e " Logs: ${CYAN}$LOG_FILE${NC}" + else + echo -e "${RED}Error:${NC} Failed to start. Check $LOG_FILE" + exit 1 + fi + else + # Run in foreground + show_banner "$port" "$host" + echo -e "Press ${YELLOW}Ctrl+C${NC} to stop" + echo "" + exec node "$CRABWALK_HOME/.output/server/index.mjs" + fi +} + +# Stop command +cmd_stop() { + if pid=$(is_running); then + kill "$pid" 2>/dev/null + rm -f "$PID_FILE" + echo "🦀 Crabwalk stopped" + else + echo "🦀 Crabwalk is not running" + fi +} + +# Status command +cmd_status() { + local port="${PORT:-3000}" + + if pid=$(is_running); then + echo -e "🦀 Crabwalk is ${GREEN}running${NC} (PID $pid)" + echo -e " http://localhost:${port}/monitor" + else + echo -e "🦀 Crabwalk is ${RED}stopped${NC}" + fi +} + +# Update command +cmd_update() { + local installed=$(get_version) + local latest=$(curl -s https://api.github.com/repos/luccast/crabwalk/releases/latest | grep '"tag_name"' | cut -d'"' -f4 | tr -d 'v') + + if [ -z "$installed" ]; then + echo "🦀 Crabwalk not installed" + exit 1 + fi + + if [ -z "$latest" ]; then + echo -e "${RED}Error:${NC} Could not fetch latest version" + exit 1 + fi + + if [ "$installed" = "$latest" ]; then + echo "🦀 Already on latest: $installed" + exit 0 + fi + + echo "🦀 Update available: $installed -> $latest" + echo " https://github.com/luccast/crabwalk/releases/tag/v${latest}" + echo "" + read -p "Update now? [y/N] " -n 1 -r + echo + + if [[ $REPLY =~ ^[Yy]$ ]]; then + # Stop if running + if is_running >/dev/null; then + echo "🦀 Stopping server..." + cmd_stop + fi + + echo "🦀 Downloading v${latest}..." + local version="v${latest}" + rm -rf "$CRABWALK_HOME/.output" + curl -sL "https://github.com/luccast/crabwalk/releases/download/${version}/crabwalk-${version}.tar.gz" | tar -xz -C "$CRABWALK_HOME" + + # Update CLI if needed + if [ -f "$CRABWALK_HOME/bin/crabwalk" ]; then + cp "$CRABWALK_HOME/bin/crabwalk" ~/.local/bin/crabwalk 2>/dev/null || true + fi + + echo "🦀 Updated to v${latest}" + else + echo "🦀 Update cancelled" + fi +} + +# Main +main() { + local cmd="${1:-start}" + shift 2>/dev/null || true + + # Handle flags before command + case $cmd in + -v|--version) + echo "🦀 Crabwalk v$(get_version)" + exit 0 + ;; + -h|--help|help) + show_help + exit 0 + ;; + start) + cmd_start "$@" + ;; + stop) + cmd_stop + ;; + status) + cmd_status + ;; + update) + cmd_update + ;; + *) + # Assume it's an option for start + cmd_start "$cmd" "$@" + ;; + esac +} + +main "$@" diff --git a/public/skill.json b/public/skill.json index 69c2ce7..7b29330 100644 --- a/public/skill.json +++ b/public/skill.json @@ -11,6 +11,7 @@ "install": { "method": "tarball", "location": "~/.crabwalk", - "start_command": "node ~/.crabwalk/.output/server/index.mjs" + "cli": "~/.local/bin/crabwalk", + "start_command": "crabwalk" } } diff --git a/public/skill.md b/public/skill.md index 2ae4f5b..8d5b9fd 100644 --- a/public/skill.md +++ b/public/skill.md @@ -11,74 +11,77 @@ metadata: {"emoji":"🦀","category":"monitoring"} Real-time companion monitor for OpenClaw agents. Visualize agent sessions, tool calls, and activity in a reactive graph UI. -## Quick Install +## Install ```bash -# Get latest version +# Download latest release VERSION=$(curl -s https://api.github.com/repos/luccast/crabwalk/releases/latest | grep '"tag_name"' | cut -d'"' -f4) - -# Download and extract mkdir -p ~/.crabwalk curl -sL "https://github.com/luccast/crabwalk/releases/download/${VERSION}/crabwalk-${VERSION}.tar.gz" | tar -xz -C ~/.crabwalk -# Start server (runs on port 3000) -node ~/.crabwalk/.output/server/index.mjs +# Install CLI +mkdir -p ~/.local/bin +cp ~/.crabwalk/bin/crabwalk ~/.local/bin/crabwalk +chmod +x ~/.local/bin/crabwalk + +# Add to PATH if needed +if ! echo $PATH | grep -q "$HOME/.local/bin"; then + [ -f ~/.bashrc ] && echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc + [ -f ~/.zshrc ] && echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.zshrc + export PATH="$HOME/.local/bin:$PATH" +fi ``` ## One-Liner Install ```bash -VERSION=$(curl -s https://api.github.com/repos/luccast/crabwalk/releases/latest | grep '"tag_name"' | cut -d'"' -f4) && mkdir -p ~/.crabwalk && curl -sL "https://github.com/luccast/crabwalk/releases/download/${VERSION}/crabwalk-${VERSION}.tar.gz" | tar -xz -C ~/.crabwalk && echo "Crabwalk ${VERSION} installed to ~/.crabwalk" +VERSION=$(curl -s https://api.github.com/repos/luccast/crabwalk/releases/latest | grep '"tag_name"' | cut -d'"' -f4) && mkdir -p ~/.crabwalk ~/.local/bin && curl -sL "https://github.com/luccast/crabwalk/releases/download/${VERSION}/crabwalk-${VERSION}.tar.gz" | tar -xz -C ~/.crabwalk && cp ~/.crabwalk/bin/crabwalk ~/.local/bin/ && chmod +x ~/.local/bin/crabwalk && (echo $PATH | grep -q "$HOME/.local/bin" || ([ -f ~/.bashrc ] && echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc; [ -f ~/.zshrc ] && echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.zshrc; export PATH="$HOME/.local/bin:$PATH")) && echo "🦀 Crabwalk ${VERSION} installed" ``` -## Running +## CLI Usage ```bash -# Default (port 3000) -node ~/.crabwalk/.output/server/index.mjs - -# Custom port -PORT=8080 node ~/.crabwalk/.output/server/index.mjs - -# With gateway auth token -CLAWDBOT_API_TOKEN=your_token node ~/.crabwalk/.output/server/index.mjs +crabwalk # Start server (0.0.0.0:3000) +crabwalk start --daemon # Run in background +crabwalk start -p 8080 # Custom port +crabwalk stop # Stop background server +crabwalk status # Check if running +crabwalk update # Update to latest version +crabwalk --help # Show all options ``` -Open http://localhost:3000 in browser. +## CLI Options -## Configuration - -| Env Var | Description | Default | -|---------|-------------|---------| -| `PORT` | Server port | `3000` | -| `CLAWDBOT_API_TOKEN` | Gateway auth token | none | +``` +Options: + -p, --port Server port (default: 3000) + -H, --host Bind address (default: 0.0.0.0) + -g, --gateway Gateway WebSocket URL + -t, --token Gateway auth token (auto-detects from ~/.clawdbot/clawdbot.json) + -d, --daemon Run in background + -v, --version Show version + -h, --help Show help +``` ## Features - `/monitor` — Real-time agent activity graph (ReactFlow) - `/workspace` — File browser and markdown viewer -- Live WebSocket updates from clawdbot gateway +- Auto-detects gateway token from OpenClaw config ## Requirements - Node.js 20+ -- clawdbot gateway running on `ws://127.0.0.1:18789` +- OpenClaw gateway running on `ws://127.0.0.1:18789` ## Check for Updates ```bash -INSTALLED=$(cat ~/.crabwalk/package.json 2>/dev/null | grep '"version"' | cut -d'"' -f4) -LATEST=$(curl -s https://api.github.com/repos/luccast/crabwalk/releases/latest | grep '"tag_name"' | cut -d'"' -f4 | tr -d 'v') - -if [ -z "$INSTALLED" ]; then - echo "Crabwalk not installed" -elif [ "$INSTALLED" != "$LATEST" ]; then - echo "Update available: $INSTALLED -> $LATEST" -else - echo "Already on latest: $INSTALLED" -fi +crabwalk update ``` +This checks for updates and prompts before installing. + ## Updating **IMPORTANT: Always ask user permission before updating.** @@ -86,24 +89,27 @@ fi If update available, ask user: > "Crabwalk update available (X.X.X -> Y.Y.Y). See release: https://github.com/luccast/crabwalk/releases/tag/vY.Y.Y — Update now?" -Only proceed if user confirms: +Only proceed if user confirms. Run: + +```bash +crabwalk update +``` + +Or manually: ```bash VERSION=$(curl -s https://api.github.com/repos/luccast/crabwalk/releases/latest | grep '"tag_name"' | cut -d'"' -f4) -rm -rf ~/.crabwalk -mkdir -p ~/.crabwalk +rm -rf ~/.crabwalk/.output curl -sL "https://github.com/luccast/crabwalk/releases/download/${VERSION}/crabwalk-${VERSION}.tar.gz" | tar -xz -C ~/.crabwalk -echo "Updated to ${VERSION}" +cp ~/.crabwalk/bin/crabwalk ~/.local/bin/crabwalk +echo "🦀 Updated to ${VERSION}" ``` ## Verify Installation ```bash -# Check version -cat ~/.crabwalk/package.json | grep '"version"' - -# Test server starts -timeout 5 node ~/.crabwalk/.output/server/index.mjs 2>&1 | head -5 || true +crabwalk --version +crabwalk status ``` ---