#!/bin/bash
BOLD='\033[1m'
DIM='\033[2m'
GREEN='\033[0;32m'
CYAN='\033[0;36m'
YELLOW='\033[1;33m'
RED='\033[0;31m'
NC='\033[0m'
DEFAULT_HOST="[http://127.0.0.1:18789](http://127.0.0.1:18789)"
function show_header() {
clear
echo -e "${CYAN}${BOLD}"
echo " ╭─────────────────────────────────────────────────────────╮"
echo " │ │"
echo " │ ◆ OpenClaw CLI Management System │"
echo " │ │"
printf " │ Gateway › %-40s │\n" "${DEFAULT_HOST}"
echo " │ │"
echo " ╰─────────────────────────────────────────────────────────╯"
echo -e "${NC}"
}
# 1. 페어링 하기
function action_pairing() {
show_header
echo -e " ${BOLD}[ 새로운 기기 페어링 ]${NC}\n"
echo -en " ${DIM}도메인 입력 (엔터 시 ${DEFAULT_HOST}):${NC} "
read USER_INPUT
BASE_URL=${USER_INPUT:-$DEFAULT_HOST}
echo -e "\n ${YELLOW}▸${NC} 토큰 추출 중..."
DASH_LOG=$(docker compose run --rm openclaw-cli dashboard --no-open 2>&1)
TOKEN_PART=$(echo "$DASH_LOG" | grep -oE "#token=[a-zA-Z0-9]+")
if [ -z "$TOKEN_PART" ]; then
echo -e "\n ${RED}✗ 토큰 추출 실패.${NC}"
read -p " 엔터를 누르면 돌아갑니다."
return
fi
[[ "${BASE_URL}" != */ ]] && BASE_URL="${BASE_URL}/"
echo -e "\n ${GREEN}✓ 접속 주소:${NC} ${BOLD}${BASE_URL}${TOKEN_PART}${NC}"
echo -e "\n ${DIM}브라우저 접속 후 엔터를 누르세요.${NC}"
read
RAW_LIST=$(docker compose exec -T openclaw-gateway node dist/index.js devices list 2>/dev/null)
declare -A P_MAP
I=0
echo -e "\n ${CYAN}${BOLD}[ Pending 기기 목록 ]${NC}"
echo -e " ${DIM}─────────────────────────────────────────${NC}"
while read -r ID; do
I=$((I+1))
P_MAP[$I]=$ID
echo -e " ${GREEN}[$I]${NC} $ID"
done < <(echo "$RAW_LIST" | grep -oE "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}")
if [ $I -eq 0 ]; then
echo -e "\n ${DIM}대기 중인 기기가 없습니다.${NC}"
sleep 1
return
fi
echo -e " ${DIM}─────────────────────────────────────────${NC}"
echo -en "\n ${DIM}승인: 번호 / 거부: r+번호${NC}\n ${BOLD}›${NC} "
read ACT
if [[ "$ACT" == r* ]]; then
docker compose exec -T openclaw-gateway node dist/index.js devices reject "${P_MAP[${ACT#r}]}"
echo -e "\n ${RED}✗ 거부 완료.${NC}"
else
docker compose exec -T openclaw-gateway node dist/index.js devices approve "${P_MAP[$ACT]}"
echo -e "\n ${GREEN}✓ 승인 완료.${NC}"
fi
sleep 1
}
# 2. 페어링 끊기
function action_manage() {
show_header
echo -e " ${BOLD}[ 페어링 끊기 ]${NC}\n"
RAW_LIST=$(docker compose exec -T openclaw-gateway node dist/index.js devices list 2>/dev/null)
PAIRED_BLOCK=$(echo "$RAW_LIST" | sed -n '/Paired/,$p')
DEVICE_ROWS=$(
echo "$PAIRED_BLOCK" \
| awk -F'│' 'NF>2 {
dev=$2; role=$3;
gsub(/^[[:space:]]+|[[:space:]]+$/, "", dev);
gsub(/^[[:space:]]+|[[:space:]]+$/, "", role);
if (dev ~ /^[0-9a-f]+$/) print dev ":" role
}'
)
CLEAN_PAIRS=$(
echo "$DEVICE_ROWS" | paste - - | awk -F'\t' '{
split($1, a, ":"); split($2, b, ":");
print a[1] b[1] ":" a[2]
}'
)
declare -A ID_MAP
declare -A ROLE_MAP
I=0
echo -e " ${CYAN}${BOLD}[ 연결된 기기 목록 ]${NC}"
echo -e " ${DIM}─────────────────────────────────────────${NC}"
while IFS=: read -r ID ROLE; do
if [ -n "$ID" ]; then
I=$((I+1))
ID_MAP[$I]=$ID
ROLE_MAP[$I]=${ROLE:-operator}
echo -e " ${GREEN}[$I]${NC} ${ID:0:16}...${ID: -8} ${DIM}(${ROLE:-operator})${NC}"
fi
done < <(echo "$CLEAN_PAIRS")
if [ $I -eq 0 ]; then
echo -e "\n ${DIM}연결된 기기가 없습니다.${NC}"
read -p " 엔터를 누르면 돌아갑니다."
return
fi
echo -e " ${DIM}─────────────────────────────────────────${NC}"
echo -e " ${DIM}번호: 해제 | clean: 전체삭제 | q: 취소${NC}"
echo -en " ${BOLD}›${NC} "
read ACT
if [[ "$ACT" == "clean" ]]; then
docker compose exec -T openclaw-gateway node dist/index.js devices clear --yes
echo -e "\n ${GREEN}✓ 전체 해제 완료.${NC}"
elif [[ "$ACT" != "q" && -n "${ID_MAP[$ACT]}" ]]; then
docker compose exec -T openclaw-gateway node dist/index.js devices remove "${ID_MAP[$ACT]}"
echo -e "\n ${GREEN}✓ 해제 완료.${NC}"
fi
sleep 1
}
# 메인 루프
while true; do
show_header
echo -e " ${BOLD}Select an action:${NC}"
echo ""
echo -e " ${GREEN}${BOLD}1${NC} Pair Device ${DIM}새로운 기기 페어링${NC}"
echo -e " ${RED}${BOLD}2${NC} Unpair Device ${DIM}페어링 끊기${NC}"
echo -e " ${CYAN}${BOLD}3${NC} Status ${DIM}현재 상태 보기${NC}"
echo -e " ${DIM}4 Exit 종료${NC}"
echo ""
echo -e " ${DIM}──────────────────────────────────────────${NC}"
echo -en " ${BOLD}›${NC} "
read MENU
case $MENU in
1) action_pairing ;;
2) action_manage ;;
3)
show_header
echo -e " ${BOLD}[ 현재 기기 상태 ]${NC}\n"
RAW=$(docker compose exec -T openclaw-gateway node dist/index.js devices list 2>/dev/null)
PAIRED_COUNT=$(echo "$RAW" | grep -oE 'Paired \([0-9]+\)')
echo -e " ${CYAN}${BOLD}${PAIRED_COUNT:-Paired (0)}${NC}"
echo -e " ${DIM}─────────────────────────────────────────────────────${NC}"
printf " ${BOLD}%-26s %-10s %-15s${NC}\n" "Device ID" "Role" "IP"
echo -e " ${DIM}─────────────────────────────────────────────────────${NC}"
echo "$RAW" | sed -n '/Paired/,$p' \
| awk -F'│' 'NF>2 {
dev=$2; role=$3; ip=$6;
gsub(/^[[:space:]]+|[[:space:]]+$/, "", dev);
gsub(/^[[:space:]]+|[[:space:]]+$/, "", role);
gsub(/^[[:space:]]+|[[:space:]]+$/, "", ip);
if (dev ~ /^[0-9a-f]+$/) print dev ":" role ":" ip
}' \
| paste - - \
| awk -F'\t' '{
split($1, a, ":"); split($2, b, ":");
id = a[1] b[1];
role = (a[2] != "" ? a[2] : b[2]);
ip = (a[3] != "" ? a[3] : b[3]);
short = substr(id,1,10) "..." substr(id,length(id)-7);
printf " %-26s %-10s %-15s\n", short, role, ip
}'
echo -e " ${DIM}─────────────────────────────────────────────────────${NC}"
echo ""
read -p " 엔터를 누르면 돌아갑니다."
;;
4) clear; exit 0 ;;
esac
done
//Script by Thomasjeong