mirror of
https://github.com/dtzp555-max/ocp.git
synced 2026-07-21 21:15:09 +00:00
643 lines
22 KiB
Bash
Executable File
643 lines
22 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# ocp — OpenClaw Proxy CLI
|
|
# Usage: ocp <command> [args]
|
|
#
|
|
# Talks to the local claude-proxy at http://127.0.0.1:3456
|
|
|
|
set -euo pipefail
|
|
|
|
PROXY="http://127.0.0.1:3456"
|
|
|
|
_json() { python3 -m json.tool 2>/dev/null || cat; }
|
|
|
|
_bar() {
|
|
local pct=$1 width=20
|
|
local filled=$(( pct * width / 100 ))
|
|
local empty=$(( width - filled ))
|
|
printf '['
|
|
printf '█%.0s' $(seq 1 $filled 2>/dev/null) || true
|
|
printf '░%.0s' $(seq 1 $empty 2>/dev/null) || true
|
|
printf '] %d%%' "$pct"
|
|
}
|
|
|
|
# ── usage ────────────────────────────────────────────────────────────────
|
|
cmd_usage_help() {
|
|
cat <<'EOF'
|
|
ocp usage — Show Claude plan usage limits
|
|
|
|
Displays current session utilization, weekly limits, extra usage status,
|
|
and proxy request statistics. Data is fetched from the Anthropic API
|
|
via a minimal probe call (cached for 5 minutes).
|
|
|
|
Usage: ocp usage [--by-key]
|
|
|
|
Options:
|
|
--by-key Show per-key usage statistics
|
|
EOF
|
|
}
|
|
|
|
cmd_usage() {
|
|
if [[ "${1:-}" == "--by-key" ]]; then
|
|
local data
|
|
data=$(curl -sf --max-time 15 "$PROXY/api/usage" 2>&1) || { echo "Error: proxy unreachable or usage API not available"; exit 1; }
|
|
echo "$data" | python3 -c "
|
|
import sys, json
|
|
d = json.loads(sys.stdin.read())
|
|
by_key = d.get('byKey', [])
|
|
if not by_key:
|
|
print('No usage data yet.')
|
|
else:
|
|
print('Usage by Key')
|
|
print('─────────────────────────────────────────────────────────────────')
|
|
hdr = f' {\"Key\":<20} {\"Reqs\":>5} {\"OK\":>4} {\"Err\":>4} {\"Avg Time\":>9} {\"Last Request\":<20}'
|
|
print(hdr)
|
|
print(' ' + '─' * (len(hdr) - 2))
|
|
for k in by_key:
|
|
avg_t = f'{k[\"avg_elapsed_ms\"]/1000:.1f}s' if k['avg_elapsed_ms'] else '-'
|
|
print(f' {k[\"key_name\"]:<20} {k[\"requests\"]:>5} {k[\"successes\"]:>4} {k[\"errors\"]:>4} {avg_t:>9} {k[\"last_request\"]:<20}')
|
|
"
|
|
return
|
|
fi
|
|
|
|
local data
|
|
data=$(curl -sf --max-time 15 "$PROXY/usage" 2>&1) || { echo "Error: proxy unreachable"; exit 1; }
|
|
|
|
echo "$data" | python3 -c "
|
|
import sys, json
|
|
|
|
d = json.loads(sys.stdin.read())
|
|
p = d['plan']
|
|
s = p['currentSession']
|
|
w = p['weeklyLimits']['allModels']
|
|
e = p['extraUsage']
|
|
px = d['proxy']
|
|
models = d.get('models', {})
|
|
|
|
print('Plan Usage Limits')
|
|
print('─────────────────────────────────────')
|
|
print(f' Current session {s[\"percent\"]:>4} used')
|
|
print(f' Resets in {s[\"resetsIn\"]} ({s[\"resetsAtHuman\"]})')
|
|
print()
|
|
print(f' Weekly (all models) {w[\"percent\"]:>4} used')
|
|
print(f' Resets in {w[\"resetsIn\"]} ({w[\"resetsAtHuman\"]})')
|
|
print()
|
|
status_icon = 'on' if e['status'] == 'allowed' else 'off'
|
|
print(f' Extra usage {status_icon}')
|
|
print()
|
|
|
|
if models:
|
|
print('Model Stats (since proxy start)')
|
|
print('─────────────────────────────────────────────────────────────────────')
|
|
hdr = f' {\"Model\":<25} {\"Reqs\":>5} {\"OK\":>4} {\"Err\":>4} {\"Avg Time\":>9} {\"Max Time\":>9} {\"Avg Prompt\":>11} {\"Max Prompt\":>11}'
|
|
print(hdr)
|
|
print(' ' + '─' * (len(hdr) - 2))
|
|
total_reqs = 0
|
|
for model in sorted(models):
|
|
m = models[model]
|
|
total_reqs += m['requests']
|
|
avg_t = f'{m[\"avgElapsed\"]/1000:.0f}s' if m['avgElapsed'] else '-'
|
|
max_t = f'{m[\"maxElapsed\"]/1000:.0f}s' if m['maxElapsed'] else '-'
|
|
avg_p = f'{m[\"avgPromptChars\"]/1000:.0f}K' if m['avgPromptChars'] else '-'
|
|
max_p = f'{m[\"maxPromptChars\"]/1000:.0f}K' if m['maxPromptChars'] else '-'
|
|
print(f' {model:<25} {m[\"requests\"]:>5} {m[\"successes\"]:>4} {m[\"errors\"]:>4} {avg_t:>9} {max_t:>9} {avg_p:>11} {max_p:>11}')
|
|
print(f' {\"Total\":<25} {total_reqs:>5}')
|
|
else:
|
|
print(' No model requests yet.')
|
|
|
|
print()
|
|
print(f'Proxy: up {px[\"uptime\"]} | {px[\"totalRequests\"]} reqs | {px[\"activeRequests\"]} active | {px[\"errors\"]} err | {px[\"timeouts\"]} timeout')
|
|
"
|
|
}
|
|
|
|
# ── status ───────────────────────────────────────────────────────────────
|
|
cmd_status_help() {
|
|
cat <<'EOF'
|
|
ocp status — Quick combined overview (usage + health)
|
|
|
|
Usage: ocp status
|
|
EOF
|
|
}
|
|
|
|
cmd_status() {
|
|
curl -sf --max-time 15 "$PROXY/status" | _json
|
|
}
|
|
|
|
# ── health ───────────────────────────────────────────────────────────────
|
|
cmd_health_help() {
|
|
cat <<'EOF'
|
|
ocp health — Proxy health and diagnostics
|
|
|
|
Shows proxy status, version, uptime, auth, config, sessions, and recent errors.
|
|
|
|
Usage: ocp health
|
|
EOF
|
|
}
|
|
|
|
cmd_health() {
|
|
curl -sf --max-time 10 "$PROXY/health" | _json
|
|
}
|
|
|
|
# ── logs ─────────────────────────────────────────────────────────────────
|
|
cmd_logs_help() {
|
|
cat <<'EOF'
|
|
ocp logs — Show recent proxy log entries
|
|
|
|
Usage: ocp logs [N] [LEVEL]
|
|
|
|
Arguments:
|
|
N Number of entries to show (default: 20, max: 200)
|
|
LEVEL Filter level: error, warn, info, all (default: error)
|
|
|
|
Examples:
|
|
ocp logs Last 20 errors
|
|
ocp logs 50 all Last 50 entries of any level
|
|
ocp logs 10 warn Last 10 warnings
|
|
EOF
|
|
}
|
|
|
|
cmd_logs() {
|
|
local n=${1:-20}
|
|
local level=${2:-error}
|
|
curl -sf --max-time 10 "$PROXY/logs?n=$n&level=$level" | python3 -c "
|
|
import sys, json
|
|
d = json.loads(sys.stdin.read())
|
|
entries = d.get('entries', [])
|
|
if not entries:
|
|
print(f'No {d.get(\"level\",\"\")} log entries.')
|
|
else:
|
|
for e in entries:
|
|
if 'raw' in e:
|
|
print(e['raw'][:200])
|
|
else:
|
|
ts = e.get('ts','')[:19]
|
|
ev = e.get('event','?')
|
|
lvl = e.get('level','')
|
|
model = e.get('model','')
|
|
extra = ' '.join(f'{k}={v}' for k,v in e.items() if k not in ('ts','event','level','model'))
|
|
parts = [ts, lvl.upper(), ev]
|
|
if model: parts.append(model)
|
|
if extra: parts.append(extra)
|
|
print(' | '.join(parts))
|
|
"
|
|
}
|
|
|
|
# ── models ───────────────────────────────────────────────────────────────
|
|
cmd_models_help() {
|
|
cat <<'EOF'
|
|
ocp models — List available Claude models
|
|
|
|
Usage: ocp models
|
|
EOF
|
|
}
|
|
|
|
cmd_models() {
|
|
curl -sf --max-time 5 "$PROXY/v1/models" | python3 -c "
|
|
import sys, json
|
|
d = json.loads(sys.stdin.read())
|
|
for m in d.get('data', []):
|
|
print(f\" {m['id']}\")
|
|
"
|
|
}
|
|
|
|
# ── sessions ─────────────────────────────────────────────────────────────
|
|
cmd_sessions_help() {
|
|
cat <<'EOF'
|
|
ocp sessions — List active CLI sessions
|
|
|
|
Usage: ocp sessions
|
|
EOF
|
|
}
|
|
|
|
cmd_sessions() {
|
|
curl -sf --max-time 5 "$PROXY/sessions" | python3 -c "
|
|
import sys, json
|
|
d = json.loads(sys.stdin.read())
|
|
sessions = d.get('sessions', [])
|
|
if not sessions:
|
|
print('No active sessions.')
|
|
else:
|
|
for s in sessions:
|
|
print(f\" {s['id'][:16]}... model={s['model']} msgs={s['messages']} last={s['lastUsed']}\")
|
|
"
|
|
}
|
|
|
|
# ── clear ────────────────────────────────────────────────────────────────
|
|
cmd_clear_help() {
|
|
cat <<'EOF'
|
|
ocp clear — Clear all active CLI sessions
|
|
|
|
Usage: ocp clear
|
|
EOF
|
|
}
|
|
|
|
cmd_clear() {
|
|
local result
|
|
result=$(curl -sf --max-time 5 -X DELETE "$PROXY/sessions")
|
|
local count
|
|
count=$(echo "$result" | python3 -c "import sys,json; print(json.loads(sys.stdin.read())['cleared'])")
|
|
echo "Cleared $count sessions."
|
|
}
|
|
|
|
# ── keys ────────────────────────────────────────────────────────────────
|
|
cmd_keys_help() {
|
|
cat <<'EOF'
|
|
ocp keys — Manage API keys (multi-key auth mode)
|
|
|
|
Usage:
|
|
ocp keys List all keys
|
|
ocp keys add <name> Create a new key
|
|
ocp keys revoke <name|id> Revoke a key
|
|
|
|
Examples:
|
|
ocp keys add wife-laptop
|
|
ocp keys add son-ipad
|
|
ocp keys revoke wife-laptop
|
|
EOF
|
|
}
|
|
|
|
cmd_keys() {
|
|
case "${1:-}" in
|
|
add)
|
|
if [[ -z "${2:-}" ]]; then echo "Usage: ocp keys add <name>"; return 1; fi
|
|
local result
|
|
result=$(curl -sf --max-time 5 -X POST "$PROXY/api/keys" \
|
|
-H "Content-Type: application/json" \
|
|
-d "{\"name\": \"$2\"}" 2>&1) || { echo "Error: proxy unreachable or unauthorized"; exit 1; }
|
|
echo "$result" | python3 -c "
|
|
import sys, json
|
|
d = json.loads(sys.stdin.read())
|
|
if 'key' in d:
|
|
print(f'✓ Key created for \"{d[\"name\"]}\"')
|
|
print(f'')
|
|
print(f' API Key: {d[\"key\"]}')
|
|
print(f'')
|
|
print(f' Copy this key now — you won\\'t see it again.')
|
|
print(f' Configure in IDE: OPENAI_API_KEY={d[\"key\"]}')
|
|
else:
|
|
print(f'✗ {d.get(\"error\", \"Unknown error\")}')
|
|
"
|
|
;;
|
|
revoke)
|
|
if [[ -z "${2:-}" ]]; then echo "Usage: ocp keys revoke <name|id>"; return 1; fi
|
|
curl -sf --max-time 5 -X DELETE "$PROXY/api/keys/$2" | python3 -c "
|
|
import sys, json
|
|
d = json.loads(sys.stdin.read())
|
|
if d.get('revoked'):
|
|
print(f'✓ Key \"{d[\"idOrName\"]}\" revoked.')
|
|
else:
|
|
print(f'✗ Key not found or already revoked.')
|
|
"
|
|
;;
|
|
--help|-h)
|
|
cmd_keys_help
|
|
;;
|
|
"")
|
|
curl -sf --max-time 5 "$PROXY/api/keys" | python3 -c "
|
|
import sys, json
|
|
d = json.loads(sys.stdin.read())
|
|
keys = d.get('keys', [])
|
|
if not keys:
|
|
print('No API keys configured.')
|
|
print('Create one: ocp keys add <name>')
|
|
else:
|
|
print('API Keys')
|
|
print('─────────────────────────────────────────────────')
|
|
for k in keys:
|
|
status = '✗ revoked' if k['revoked'] else '✓ active'
|
|
print(f' {k[\"name\"]:<20} {k[\"keyPreview\"]:<20} {status} {k[\"created_at\"]}')
|
|
" 2>/dev/null || { echo "Error: proxy unreachable or key management not available"; exit 1; }
|
|
;;
|
|
*)
|
|
echo "Unknown subcommand: $1"; cmd_keys_help; return 1 ;;
|
|
esac
|
|
}
|
|
|
|
# ── lan ─────────────────────────────────────────────────────────────────
|
|
cmd_lan_help() {
|
|
cat <<'EOF'
|
|
ocp lan — Quick LAN mode setup guide
|
|
|
|
Shows current network configuration and connection instructions
|
|
for other devices on the same network.
|
|
|
|
Usage: ocp lan
|
|
EOF
|
|
}
|
|
|
|
cmd_lan() {
|
|
local ip
|
|
ip=$(ipconfig getifaddr en0 2>/dev/null || hostname -I 2>/dev/null | awk '{print $1}')
|
|
local port=3456
|
|
|
|
echo "OCP LAN Setup"
|
|
echo "─────────────────────────────────────"
|
|
echo ""
|
|
echo " Your IP: $ip"
|
|
echo " Port: $port"
|
|
echo ""
|
|
echo " For IDE users, set:"
|
|
echo " OPENAI_BASE_URL=http://$ip:$port/v1"
|
|
echo " OPENAI_API_KEY=<your-key> (if auth enabled)"
|
|
echo ""
|
|
echo " Dashboard: http://$ip:$port/dashboard"
|
|
echo ""
|
|
|
|
if curl -sf --max-time 2 "http://$ip:$port/health" > /dev/null 2>&1; then
|
|
echo " Status: ✓ LAN-accessible"
|
|
else
|
|
echo " Status: ✗ Not LAN-accessible (bound to localhost only)"
|
|
echo ""
|
|
echo " To enable LAN mode, set env var and restart:"
|
|
echo " CLAUDE_BIND=0.0.0.0"
|
|
echo " ocp restart"
|
|
fi
|
|
}
|
|
|
|
# ── restart ──────────────────────────────────────────────────────────────
|
|
cmd_restart_help() {
|
|
cat <<'EOF'
|
|
ocp restart — Restart the proxy or gateway
|
|
|
|
Usage:
|
|
ocp restart Restart the Claude proxy service
|
|
ocp restart gateway Restart the OpenClaw gateway
|
|
(briefly disconnects all Telegram/Discord bots)
|
|
EOF
|
|
}
|
|
|
|
cmd_restart() {
|
|
if [[ "${1:-}" == "gateway" ]]; then
|
|
echo "Restarting gateway..."
|
|
openclaw gateway restart 2>&1
|
|
else
|
|
echo "Restarting proxy..."
|
|
# Try current service name, then legacy, then manual restart
|
|
local uid
|
|
uid=$(id -u)
|
|
if launchctl kickstart -k "gui/$uid/dev.ocp.proxy" 2>/dev/null; then
|
|
true
|
|
elif launchctl kickstart -k "gui/$uid/ai.openclaw.proxy" 2>/dev/null; then
|
|
true
|
|
elif systemctl --user restart ocp-proxy 2>/dev/null; then
|
|
true
|
|
elif systemctl --user restart openclaw-proxy 2>/dev/null; then
|
|
true
|
|
else
|
|
echo "Service restart failed, trying kill + restart..."
|
|
pkill -f "server.mjs.*CLAUDE_PROXY" 2>/dev/null || pkill -f "claude-proxy/server.mjs" 2>/dev/null || true
|
|
sleep 1
|
|
local self_r script_dir
|
|
self_r="${BASH_SOURCE[0]}"
|
|
while [[ -L "$self_r" ]]; do self_r="$(readlink "$self_r")"; done
|
|
script_dir="$(cd "$(dirname "$self_r")" && pwd)"
|
|
nohup node "$script_dir/server.mjs" >> "$HOME/.ocp/logs/proxy.log" 2>&1 &
|
|
fi
|
|
sleep 3
|
|
if curl -sf --max-time 5 "$PROXY/health" > /dev/null 2>&1; then
|
|
echo "✓ Proxy restarted successfully."
|
|
cmd_usage
|
|
else
|
|
echo "✗ Proxy not responding after restart."
|
|
fi
|
|
fi
|
|
}
|
|
|
|
# ── settings ─────────────────────────────────────────────────────────────
|
|
cmd_settings_help() {
|
|
cat <<'EOF'
|
|
ocp settings — View or update proxy settings at runtime
|
|
|
|
Usage:
|
|
ocp settings Show all tunable settings
|
|
ocp settings <key> <value> Update a setting (no restart needed)
|
|
|
|
Tunable keys:
|
|
timeout Overall request timeout (ms) [30000 - 600000]
|
|
firstByteTimeout Base first-byte timeout (ms) [15000 - 300000]
|
|
maxConcurrent Max concurrent claude processes [1 - 32]
|
|
sessionTTL Session idle expiry (ms) [60000 - 86400000]
|
|
maxPromptChars Prompt truncation limit (chars) [10000 - 1000000]
|
|
tiers.opus.base Opus first-byte timeout base (ms) [30000 - 600000]
|
|
tiers.opus.perChar Opus per-char timeout (ms/char) [0 - 0.01]
|
|
tiers.sonnet.base Sonnet first-byte timeout base (ms) [30000 - 600000]
|
|
tiers.sonnet.perChar Sonnet per-char timeout (ms/char) [0 - 0.01]
|
|
tiers.haiku.base Haiku first-byte timeout base (ms) [15000 - 300000]
|
|
tiers.haiku.perChar Haiku per-char timeout (ms/char) [0 - 0.01]
|
|
|
|
Examples:
|
|
ocp settings maxPromptChars 200000
|
|
ocp settings tiers.sonnet.base 150000
|
|
EOF
|
|
}
|
|
|
|
cmd_settings() {
|
|
if [[ -z "${1:-}" ]]; then
|
|
# GET — show all settings
|
|
curl -sf --max-time 5 "$PROXY/settings" | python3 -c "
|
|
import sys, json
|
|
d = json.loads(sys.stdin.read())
|
|
print('OCP Settings')
|
|
print('─────────────────────────────────────')
|
|
for k in ('timeout','firstByteTimeout','maxConcurrent','sessionTTL','maxPromptChars'):
|
|
v = d.get(k, {})
|
|
val = v.get('value', '?')
|
|
unit = v.get('unit', '')
|
|
desc = v.get('desc', '')
|
|
print(f' {k:<20} {val:>8} {unit:<6} {desc}')
|
|
t = d.get('tiers', {})
|
|
print()
|
|
print('Timeout Tiers (first-byte):')
|
|
for tier in ('opus','sonnet','haiku'):
|
|
info = t.get(tier, {})
|
|
print(f' {tier:<8} base={info.get(\"base\",\"?\"):>6}ms perChar={info.get(\"perPromptChar\",\"?\")}')
|
|
"
|
|
elif [[ "${1:-}" == "--help" || "${1:-}" == "-h" ]]; then
|
|
cmd_settings_help
|
|
elif [[ -z "${2:-}" ]]; then
|
|
echo "Usage: ocp settings <key> <value>"
|
|
echo "Run 'ocp settings --help' for available keys."
|
|
return 1
|
|
else
|
|
# PATCH — update a setting
|
|
local key="$1"
|
|
local value="$2"
|
|
local result
|
|
result=$(curl -s --max-time 5 -X PATCH "$PROXY/settings" \
|
|
-H "Content-Type: application/json" \
|
|
-d "{\"$key\": $value}" 2>&1)
|
|
if echo "$result" | python3 -c "import sys,json; d=json.loads(sys.stdin.read()); errs=d.get('errors',[]); exit(1 if errs else 0)" 2>/dev/null; then
|
|
echo "✓ $key = $value"
|
|
else
|
|
echo "$result" | python3 -c "
|
|
import sys, json
|
|
d = json.loads(sys.stdin.read())
|
|
for e in d.get('errors', []):
|
|
print(f'✗ {e}')
|
|
" 2>/dev/null || echo "✗ $result"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
# ── update ──────────────────────────────────────────────────────────────
|
|
cmd_update_help() {
|
|
cat <<'EOF'
|
|
ocp update — Update OCP to the latest version
|
|
|
|
Pulls the latest code from GitHub, restarts the proxy service,
|
|
and optionally syncs the plugin to the OpenClaw extensions directory.
|
|
|
|
Usage:
|
|
ocp update Pull latest and restart
|
|
ocp update --check Check for updates without applying
|
|
EOF
|
|
}
|
|
|
|
cmd_update() {
|
|
local script_dir self
|
|
self="${BASH_SOURCE[0]}"
|
|
# Resolve symlinks (e.g. ~/.local/bin/ocp → real location)
|
|
while [[ -L "$self" ]]; do self="$(readlink "$self")"; done
|
|
script_dir="$(cd "$(dirname "$self")" && pwd)"
|
|
|
|
# Check-only mode
|
|
if [[ "${1:-}" == "--check" ]]; then
|
|
cd "$script_dir"
|
|
git fetch origin main --quiet 2>/dev/null || true
|
|
local local_ver remote_ver
|
|
local_ver=$(python3 -c "import json; print(json.load(open('package.json'))['version'])" 2>/dev/null || echo "?")
|
|
remote_ver=$(git show origin/main:package.json 2>/dev/null | python3 -c "import sys,json; print(json.load(sys.stdin)['version'])" 2>/dev/null || echo "?")
|
|
local behind
|
|
behind=$(git rev-list HEAD..origin/main --count 2>/dev/null || echo "?")
|
|
echo "OCP Update Check"
|
|
echo "─────────────────────────────────────"
|
|
echo " Current: v$local_ver"
|
|
echo " Latest: v$remote_ver"
|
|
if [[ "$behind" == "0" ]]; then
|
|
echo " Status: ✓ Up to date"
|
|
else
|
|
echo " Status: $behind commit(s) behind"
|
|
echo ""
|
|
echo " Run 'ocp update' to apply."
|
|
fi
|
|
return 0
|
|
fi
|
|
|
|
echo "Updating OCP..."
|
|
echo ""
|
|
|
|
# 1. Pull latest
|
|
cd "$script_dir"
|
|
local old_ver
|
|
old_ver=$(python3 -c "import json; print(json.load(open('package.json'))['version'])" 2>/dev/null || echo "?")
|
|
|
|
echo " Pulling latest from GitHub..."
|
|
if ! git pull origin main --ff-only 2>&1 | sed 's/^/ /'; then
|
|
echo " ✗ Git pull failed. Resolve conflicts manually in: $script_dir"
|
|
return 1
|
|
fi
|
|
|
|
local new_ver
|
|
new_ver=$(python3 -c "import json; print(json.load(open('package.json'))['version'])" 2>/dev/null || echo "?")
|
|
|
|
if [[ "$old_ver" == "$new_ver" ]]; then
|
|
echo " ✓ Already at latest (v$new_ver)"
|
|
else
|
|
echo " ✓ Updated v$old_ver → v$new_ver"
|
|
fi
|
|
|
|
# 2. Sync plugin to extensions dir
|
|
local ext_dir="$HOME/.openclaw/extensions/ocp"
|
|
if [[ -d "$ext_dir" && -d "$script_dir/ocp-plugin" ]]; then
|
|
echo ""
|
|
echo " Syncing OCP plugin..."
|
|
cp "$script_dir/ocp-plugin/index.js" "$ext_dir/index.js" 2>/dev/null
|
|
cp "$script_dir/ocp-plugin/package.json" "$ext_dir/package.json" 2>/dev/null
|
|
cp "$script_dir/ocp-plugin/openclaw.plugin.json" "$ext_dir/openclaw.plugin.json" 2>/dev/null
|
|
echo " ✓ Plugin synced to $ext_dir"
|
|
fi
|
|
|
|
# 3. Restart proxy
|
|
echo ""
|
|
echo " Restarting proxy..."
|
|
cmd_restart > /dev/null 2>&1
|
|
sleep 2
|
|
|
|
if curl -sf --max-time 5 "$PROXY/health" > /dev/null 2>&1; then
|
|
local running_ver
|
|
running_ver=$(curl -sf --max-time 5 "$PROXY/health" | python3 -c "import sys,json; print(json.loads(sys.stdin.read())['version'])" 2>/dev/null || echo "?")
|
|
echo " ✓ Proxy running (v$running_ver)"
|
|
else
|
|
echo " ⚠ Proxy not responding — check: ocp health"
|
|
fi
|
|
|
|
echo ""
|
|
echo "Done."
|
|
}
|
|
|
|
# ── help ─────────────────────────────────────────────────────────────────
|
|
cmd_help() {
|
|
cat <<'EOF'
|
|
ocp — OpenClaw Proxy CLI
|
|
|
|
Usage: ocp <command> [args]
|
|
|
|
Commands:
|
|
usage Plan usage limits (--by-key for per-key stats)
|
|
status Quick overview (usage + health)
|
|
health Proxy diagnostics
|
|
settings View or update tunable settings
|
|
logs [N] [level] Recent logs (default: 20, error)
|
|
models Available models
|
|
sessions Active sessions
|
|
clear Clear all sessions
|
|
keys Manage API keys (add/list/revoke)
|
|
lan LAN mode setup guide
|
|
restart Restart proxy
|
|
restart gateway Restart gateway
|
|
update Update OCP to latest version
|
|
update --check Check for updates
|
|
|
|
Run 'ocp <command> --help' for details on a specific command.
|
|
EOF
|
|
}
|
|
|
|
# ── dispatch ─────────────────────────────────────────────────────────────
|
|
# Check for --help/-h on any subcommand: ocp <cmd> --help
|
|
subcmd="${1:-help}"
|
|
shift 2>/dev/null || true
|
|
|
|
# Global --help
|
|
if [[ "$subcmd" == "--help" || "$subcmd" == "-h" || "$subcmd" == "help" ]]; then
|
|
cmd_help
|
|
exit 0
|
|
fi
|
|
|
|
# Per-subcommand --help
|
|
for arg in "$@"; do
|
|
if [[ "$arg" == "--help" || "$arg" == "-h" ]]; then
|
|
fn="cmd_${subcmd}_help"
|
|
if declare -f "$fn" > /dev/null 2>&1; then
|
|
"$fn"
|
|
else
|
|
echo "No help available for '$subcmd'."
|
|
fi
|
|
exit 0
|
|
fi
|
|
done
|
|
|
|
case "$subcmd" in
|
|
usage) cmd_usage "${1:-}" ;;
|
|
status) cmd_status ;;
|
|
health) cmd_health ;;
|
|
settings) cmd_settings "${1:-}" "${2:-}" ;;
|
|
logs) cmd_logs "${1:-20}" "${2:-error}" ;;
|
|
models) cmd_models ;;
|
|
sessions) cmd_sessions ;;
|
|
clear) cmd_clear ;;
|
|
keys) cmd_keys "${1:-}" "${2:-}" ;;
|
|
lan) cmd_lan ;;
|
|
restart) cmd_restart "${1:-}" ;;
|
|
update) cmd_update "${1:-}" ;;
|
|
*) echo "Unknown command: $subcmd"; echo ""; cmd_help; exit 1 ;;
|
|
esac
|