Sign In

[Guide] OpenClaw 페어링과 기기 관리, CLI 스크립트로 스마트하게 해결하기

도커(Docker)로 OpenClaw를 운영하다 보면 가장 당혹스러운 순간이 있습니다. 브라우저에서 연결을 하려고 하면 출력되는 이 메시지 때문이죠.
disconnected (1008): pairing required
이 에러는 기기 인증 세션이 만료되었거나 보안상의 이유로 새로운 페어링 승인이 필요할 때 발생합니다. 하지만 매번 이 에러를 해결하기 위해 복잡한 도커 명령어를 치고, 긴 ID를 복사하는 과정은 매우 번거롭습니다. 특히 터미널 창 너비에 따라 ID가 두 줄로 쪼개져 보일 때는 수동 관리가 거의 불가능해집니다.
이를 깔끔하게 해결해 줄 대화형 CLI 매니저 활용법을 소개합니다.

1. 왜 이 스크립트가 필요한가요?

접속 즉시 대응: 브라우저에서 1008 에러를 마주했을 때, 스크립트 실행 후 1번 메뉴만으로 즉시 재인증을 마칠 수 있습니다.
줄바꿈 이슈 해결: 터미널 너비 때문에 64자리 Device ID가 쪼개져 출력되어도 awkpaste 로직으로 완벽하게 병합해 인식합니다.
직관적인 UI: 복잡한 UUID 대신 번호(1, 2...) 입력만으로 승인과 해제를 제어합니다.
환경 맞춤형: 로컬(127.0.0.1)은 물론 개인 도메인 주소까지 유연하게 대응합니다.

2. 주요 기능 및 사용법

① Pairing Required (1008) 해결하기

브라우저에서 에러가 뜨면 당황하지 말고 스크립트를 실행한 뒤 [1] Pair Device를 선택하세요.
1.
대시보드 토큰을 실시간으로 추출해 접속 URL을 생성합니다.
2.
안내된 주소로 브라우저에서 접속하여 페어링 요청을 보냅니다.
3.
스크립트가 'Pending' 목록을 불러오면 번호를 눌러 즉시 승인(Approve)합니다.

② 페어링 끊기 및 기기 관리 (Unpair)

불필요하게 쌓인 기존 기기 목록을 확인하고, 특정 기기만 골라 연결을 끊거나 clean 명령으로 모든 세션을 초기화하여 보안 상태를 유지합니다.

③ 실시간 상태 보기 (Status)

현재 페어링된 기기 수, 각 기기의 역할(Role) 및 접속 IP를 깔끔한 표 형태로 한눈에 모니터링합니다.

3. 관리 스크립트 전문 (oc-manager.sh)

아래 코드를 복사하여 서버에 저장한 후 실행하세요.
#!/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

4. 사용 방법

1.
스크립트 생성: vi oc-manager.sh
2.
코드 붙여넣기: 위 코드를 복사해 넣습니다.
3.
권한 부여: chmod +x oc-manager.sh
4.
실행: ./oc-manager.sh

마치며

이 스크립트는 단순한 자동화를 넘어, CLI 환경에서 발생할 수 있는 데이터 파싱 오류를 창의적으로 해결한 결과물입니다. OpenClaw를 사용하는 다른 개발자들에게도 큰 도움이 될 것입니다.
oc-manager.sh10.04KB