Presence

OpenClaw “Presence”는 다음에 대한 가볍고 최선 노력(best-effort) 기반의 뷰입니다:

  • 게이트웨이 자체
  • 게이트웨이에 연결된 클라이언트 (Mac 앱, WebChat, CLI 등)

Presence는 주로 macOS 앱의 Instances 탭을 렌더링하고 운영자에게 빠른 가시성을 제공하는 데 사용됩니다.

Presence 필드 (표시되는 항목)

Presence 항목은 다음과 같은 필드를 가진 구조화된 객체입니다:

  • instanceId (선택이지만 강력히 권장): 안정적인 클라이언트 식별자 (보통 connect.client.instanceId)
  • host: 사람이 읽기 쉬운 호스트 이름
  • ip: 최선 노력 기반 IP 주소
  • version: 클라이언트 버전 문자열
  • deviceFamily / modelIdentifier: 하드웨어 힌트
  • mode: ui, webchat, cli, backend, probe, test, node, …
  • lastInputSeconds: “마지막 사용자 입력 이후 경과 시간(초)” (알 수 있는 경우)
  • reason: self, connect, node-connected, periodic, …
  • ts: 마지막 업데이트 타임스탬프 (에포크 이후 밀리초)

생산자 (Presence 데이터의 출처)

Presence 항목은 여러 소스에서 생성되어 병합됩니다.

1) 게이트웨이 self 항목

게이트웨이는 시작 시 항상 “self” 항목을 초기화하여 클라이언트가 연결되기 전에도 UI에 게이트웨이 호스트가 표시되도록 합니다.

2) WebSocket 연결

모든 WS 클라이언트는 connect 요청으로 시작합니다. 핸드셰이크가 성공하면 게이트웨이는 해당 연결에 대한 Presence 항목을 업서트합니다.

일회성 CLI 명령이 표시되지 않는 이유

CLI는 짧은 일회성 명령을 위해 자주 연결합니다. Instances 목록이 불필요하게 많아지는 것을 방지하기 위해 client.mode === "cli"는 Presence 항목으로 변환되지 않습니다.

3) system-event 비콘

클라이언트는 system-event 메서드를 통해 더 풍부한 주기적 비콘을 보낼 수 있습니다. Mac 앱은 이를 사용하여 호스트 이름, IP, lastInputSeconds를 보고합니다.

4) 노드 연결 (role: node)

노드가 role: node로 게이트웨이 WebSocket에 연결하면, 게이트웨이는 해당 노드에 대한 Presence 항목을 업서트합니다(다른 WS 클라이언트와 동일한 흐름).

병합 + 중복 제거 규칙 (instanceId가 중요한 이유)

Presence 항목은 단일 인메모리 맵에 저장됩니다:

  • 항목은 Presence 키로 인덱싱됩니다.
  • 가장 좋은 키는 재시작에도 유지되는 안정적인 instanceId(connect.client.instanceId에서 가져옴)입니다.
  • 키는 대소문자를 구분하지 않습니다.

클라이언트가 안정적인 instanceId 없이 재연결하면 중복 행으로 표시될 수 있습니다.

TTL 및 크기 제한

Presence는 의도적으로 일시적입니다:

  • TTL: 5분 이상 된 항목은 제거됨
  • 최대 항목 수: 200 (가장 오래된 항목부터 삭제)

이렇게 하면 목록이 항상 최신 상태로 유지되고 메모리가 무한히 증가하는 것을 방지합니다.

원격/터널 주의사항 (루프백 IP)

클라이언트가 SSH 터널/로컬 포트 포워딩을 통해 연결하면 게이트웨이가 원격 주소를 127.0.0.1로 인식할 수 있습니다. 클라이언트가 보고한 정상적인 IP를 덮어쓰지 않도록 루프백 원격 주소는 무시됩니다.

소비자

macOS Instances 탭

macOS 앱은 system-presence의 출력을 렌더링하고 마지막 업데이트의 경과 시간에 따라 작은 상태 표시기(Active/Idle/Stale)를 적용합니다.

디버깅 팁

  • 원시 목록을 보려면 게이트웨이에 대해 system-presence를 호출하세요.
  • 중복이 보이는 경우:
    • 클라이언트가 핸드셰이크에서 안정적인 client.instanceId를 보내는지 확인
    • 주기적 비콘이 동일한 instanceId를 사용하는지 확인
    • 연결에서 파생된 항목에 instanceId가 누락되었는지 확인 (중복이 예상됨)