콘텐츠로 이동

Dock 과 앱

Dock 은 2단이에요. 1단에는 기기와, 자주 써서 1단에 따로 고정한 앱이 있어요. 기기를 누르면 그 기기의 앱이 2단으로 펼쳐져요. 사람도 AI 에이전트도 “어느 기기에서 → 어떤 앱” 순서로 생각하게 돼요. 같은 앱이 두 기기에 있어도 헷갈리지 않고, 기기가 늘어도 1단이 넘치지 않아요.

┌─ 2단: pixel (android) ─────────────────┐
│ 📌 설정 📌 카메라 │ 모든 앱 ▸ │
└──────────────────────────────▲─────────┘
1단: [research-box · linux] [web · chrome] [pixel · android] [내 폰 · android] [설정 ⤒]
└──────────── virtual ────────────┘ └─ virtual ─┘ └── actual ──┘ └ 1단에 고정한 앱 ┘
단 무엇 저장
1단 Device(actual·virtual 모두)와 1단에도 고정한 App GET·PUT /stations/{id}/dock 의 순서 있는 목록 (최대 30칸)
2단 그 Device 의 App 전부 + android 면 “모든 앱”(설치된 앱 목록) GET /devices/{id}/apps

Device 를 만들면 1단 끝에 붙어요 — virtual 은 pin_to_dock(기본 true), actual 은 등록할 때. 내가 연결한 폰도 클라우드에 띄운 기기와 똑같이 1단 항목으로 저장돼요.

터미널 창
curl -s $OS/api/v1/stations/$STN/dock -H "Authorization: Bearer $TOKEN" \
| jq '.data.items[] | {type, label, kind, device_type, compute_status, online}'

항목마다 type(device·app)·device_id·app_id·label·kind·device_type(actual·virtual)·compute_status·online·icon_data_url 이 채워져 있어서, 화면은 이것 하나로 1단의 아이콘과 실행 중 표시를 그려요. app 항목의 device_id 는 그 앱이 있는 기기예요.

1단은 PUT /stations/{id}/dock 으로 통째로 바꿔요 — 순서 바꾸기, 1단에 넣기, 1단에서 빼기가 모두 이것이에요:

{ "items": [ { "type": "device", "device_id": "res_…" }, { "type": "app", "app_id": "app_…" } ] }

1단에서 빼도 Device·App 은 지워지지 않아요 — App 은 그 기기의 2단에 그대로 남아요. 없는·남의·지워진 항목이거나 같은 항목이 두 번이면 422 DOCK_ITEM_INVALID (details.index) 이고 Dock 은 그대로예요. 바뀔 때마다 dock.changed 이벤트가 나가요.

App 은 기기 안의 앱(android 패키지)을 저장한 것이에요(app_). Device 를 가리키기 때문에(device_id + package), 클라우드에 띄운 android 와 내가 연결한 폰이 같은 방식이에요.

  • App 은 그 기기의 2단에 늘 있어요.
  • pinned 가 true 면 1단에도 있어요 (Dock 항목이 있다는 뜻).
터미널 창
# 1. 설치된 앱 목록 (android 만) — 2단의 "모든 앱"
curl -s $OS/api/v1/devices/$DEV/installed-apps -H "Authorization: Bearer $TOKEN" | jq '.data[] | {package, label, app_id}'
# 2. 기기에 저장 (2단에만) — pin_to_dock 기본 false
export APP=$(curl -s -X POST $OS/api/v1/devices/$DEV/apps \
-H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
-d '{"package":"com.android.settings","label":"설정"}' | jq -r .data.app_id)
# 3. 1단에도 고정
curl -s -X PATCH $OS/api/v1/apps/$APP \
-H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' -d '{"pinned":true}'
# 4. 열기 — 그 기기에서 앱이 열려요
curl -s -X POST $OS/api/v1/apps/$APP/launch -H "Authorization: Bearer $TOKEN" | jq .data.status
  • POST /devices/{id}/apps {package, label?, icon_data_url?, pin_to_dock?} → 201. pin_to_dock 의 기본값은 false — 2단에만 둬요. 만들 때 true 면 1단 끝에도 붙어요. icon_data_url 에는 설치된 앱 목록의 아이콘을 그대로 넘겨요 (16 KiB 까지).
  • 같은 기기의 같은 package 로 다시 만들면 새로 만들지 않고 있는 것을 200 으로 돌려줘요. Station 당 100개 (409 APP_LIMIT_REACHED).
  • PATCH /apps/{id} {label?, pinned?} — 이름을 바꾸거나 1단에 고정·해제해요. pinned: false 여도 2단에는 남아요.
  • DELETE /apps/{id} 는 저장한 앱을 지워요 — 2단에서, 1단에 있었으면 1단에서도 빠져요. 기기 안의 앱은 그대로예요.
  • GET /stations/{id}/apps — Station 의 저장한 앱 전부.
  • 실행은 그 기기에 app_launch {package} 명령을 보내요. 10초 안에 끝나지 않으면 dispatched 로 답하고, 명령 기록에 사용자 행동으로 남아요. 명령의 오류를 그대로 돌려줘요 — 기기가 오프라인이면 409 RESOURCE_OFFLINE, 잔액이 0 이하면 402 INSUFFICIENT_CREDITS.
  • 기기를 지우면 그 기기의 App 도 지워져요.

Dock 을 누를 때 (데스크톱 화면의 동작)

섹션 제목: “Dock 을 누를 때 (데스크톱 화면의 동작)”
  • 1단의 기기 — 2단이 있으면(android 이거나 저장한 앱이 있으면) 그 기기의 2단을 펼쳐요. 2단의 “화면 열기”, 또는 2단이 없는 기기(chrome·linux)는 기기 창을 열어요. virtual 기기가 running 이 아니면 먼저 POST /devices/{id}/start 를 보내고 device.status_changed(running) 을 기다려요.
  • 2단에서 — 📌 는 설치된 앱을 그 기기에 저장하고(App 만들기), ⤒ 는 App 을 1단에 올리거나 내려요(pinned). 오른쪽 클릭 메뉴도 같은 일을 해요.
  • App(1단·2단 모두) — POST /apps/{id}/launch 후 기기 앱 창을 열고 화면을 따라가요. 그 기기가 virtual 이고 멈춰 있으면 화면이 먼저 기기를 시작해요.