# Controlează un iPhone de la distanță (/ro/docs/apps/iphone-remote)

Vezi ecranul unui iPhone în timp real și controlează-l din hub, din aplicația Android sau din orice agent codai, prin bridge-ul codai pentru iPhone — fără jailbreak, doar în același cont.

**Bridge-ul codai pentru iPhone** rulează pe PC-ul Windows cu care e asociat iPhone-ul și controlează telefonul prin USB sau Wi-Fi (pymobiledevice3, imaginea de dezvoltator, unelte fără WebDriverAgent). Cu **accesul la distanță** pornit, bridge-ul se conectează și singur la codai — fără aplicația desktop, fără port de intrare — așa că poți controla telefonul de oriunde:

- **Controlul** trece prin [releul de host-uri](../sessions/hosts.mdx): bridge-ul este un host cu capabilitățile `iphone` și `video`, iar orice client apelează `POST /v1/hosts/{deviceId}/exec` cu operația `iphone`.
- **Ecranul live** trece prin codai-relay: bridge-ul este daemonul propriului mediu **„iPhone &lt;nume&gt;”** și transmite ecranul ca video HEVC.

```text
   hub / Android / agent            gateway + relay codai                PC cu bridge-ul           iPhone
   ─────────────────────           ─────────────────────               ───────────────           ──────
   POST /v1/hosts/:dev/exec  ───▶  releul de host-uri (SSE)     ───▶  același cod de unelte ─▶  atinge, scrie,
        { op: 'iphone', … }                                            ca MCP-ul local           deschide app…
   vizualizator video (WS)   ◀───  stream relay de tip `video`  ◀───  DisplayService HEVC   ◀─  ecran
```

## Configurează bridge-ul [#configurează-bridge-ul]

Instalează mai întâi bridge-ul (vezi `apps/iphone-bridge/README.md`; vine împreună cu MCP-ul desktop `codai-iphone`). Apoi, o singură dată:

<Steps>
<Step>

### Dă-i o cheie API [#dă-i-o-cheie-api]

Creează o cheie în hub (**Chei**) și păstreaz-o într-un fișier lângă `control.json` al bridge-ului — niciodată în configurație:

```powershell
Set-Content -NoNewline "$env:LOCALAPPDATA\codai\iphone-bridge\api-key.txt" 'codai_…'
'{"relay": {"enabled": true, "api_key_file": "%LOCALAPPDATA%/codai/iphone-bridge/api-key.txt"}}' |
  Set-Content "$env:LOCALAPPDATA\codai\iphone-bridge\config.json"
```

</Step>
<Step>

### Înrolează telefonul [#înrolează-telefonul]

```powershell
& "$env:LOCALAPPDATA\codai\iphone-bridge\venv\Scripts\codai-iphone-bridge.exe" relay enroll
```

`relay enroll` creează (sau refolosește) proiectul **„codai iPhone”** și un mediu BYO **„iPhone &lt;nume&gt;”**, de care leagă id-ul stabil de dispozitiv al acestui telefon. Comanda e idempotentă; opțiuni `--udid`, `--name`, `--base-url`.

</Step>
<Step>

### Pornește-l cu acces la distanță [#pornește-l-cu-acces-la-distanță]

Repornește bridge-ul sau rulează-l cu `--relay` (echivalent cu `relay.enabled: true`). Se înregistrează cu `os=ios`, `caps=iphone,video` și id-ul mediului și se reconectează cu backoff (1 → 30 s) la erori de rețea. O cheie respinsă sau un alt bridge care preia același mediu oprește partea de la distanță până la repornire.

</Step>
</Steps>

Cheile din `config.json`, sub `relay`: `enabled`, `api_key_file` (altfel `CODAI_API_KEY`), `base_url` (implicit `https://ai.codai.ro`), `relay_url` (implicit: ce a întors gateway-ul la înrolare), `allow_sensitive` (implicit `false`) și `video` (implicit `true`; `false` = doar control).

## Folosește-l [#folosește-l]

| Unde | Cum |
| --- | --- |
| **Hub** | [hub.codai.ro/devices/iphone](https://hub.codai.ro/devices/iphone) — *iPhone-uri la distanță* listează telefoanele online; deschide unul pentru ecranul live și comenzi (atingere, derulare, Home, butoane, tastare). |
| **Aplicația Android** | **Setări → Servere remote → iPhone**: aceeași listă, un ecran live pe care atingi și tragi, plus butoanele Home / Înapoi / Blocare. |
| **Agenți** | Uneltele `iphone_*` din fiecare agent codai (desktop, Android, VS Code, headless): `iphone_hosts`, `iphone_screen`, `iphone_screenshot`, `iphone_find`, `iphone_click`, `iphone_tap`, `iphone_swipe`, `iphone_type`, `iphone_key`, `iphone_button`, `iphone_home`, `iphone_open_app`, `iphone_open_url`, `iphone_list_apps`, `iphone_wait_for`. Fiecare primește un `host` opțional (id de dispozitiv); implicit este singurul host online cu capabilitatea `iphone`. |
| **API / SDK-uri** | `POST /v1/hosts/{deviceId}/exec` cu `{ "op": "iphone", "args": { "tool": "tap", "args": { "x": 200, "y": 640 } } }`; `client.hosts.iphone(deviceId, 'tap', { x: 200, y: 640 })` în TypeScript, `client.hosts.iphone(device_id, "tap", {"x": 200, "y": 640})` în Python. |

Răspunsul este `{ ok, result: { data, image? } }`: `data` este ce a întors unealta bridge-ului, iar `image` o captură JPEG opțională ca data URL (≤ 1,5 MB, tot rezultatul ≤ 2 MB). Numele uneltelor și argumentele sunt cele ale MCP-ului local `codai-iphone`, fără prefixul `iphone_`.

## Ecran live și rezervă [#ecran-live-și-rezervă]

Un vizualizator deschide un stream `video` pe mediul telefonului. Bridge-ul pornește **CoreDevice DisplayService** (HEVC, de obicei 30–55 fps), împarte o singură sesiune între toți vizualizatorii și o oprește odată cu ultimul. Când stream-ul nu poate rula — altă aplicație folosește camera sau microfonul (eroarea 9022), telefonul nu îl suportă sau a eșuat la trei reporniri — bridge-ul trece automat pe **capturi JPEG la circa 2 fps**, iar aplicația Android afișează *Capturi de ecran* în loc de *Live*. Unui vizualizator lent i se aruncă cadre până la următorul cadru cheie, conexiunea nu se închide niciodată.

Hub-ul deschide WebSocket-ul relay-ului cu un tichet de unică folosință valabil 60 de secunde, așa că cheia ta API nu ajunge niciodată în browser.

## Securitate [#securitate]

- **Doar același utilizator.** Gateway-ul retransmite o operație `iphone` doar între dispozitivele aceluiași cont; nu există partajare între utilizatori.
- **Uneltele sensibile sunt refuzate de la distanță.** Instalarea / dezinstalarea și `wifi_setup` rulează doar local, dacă nu setezi `relay.allow_sensitive: true` în bridge.
- **Totul este jurnalizat.** Apelurile de la distanță rulează același cod ca cele locale și ajung în jurnalul bridge-ului marcate `"via": "relay"`.
- **Cheia rămâne pe PC**, în fișierul ales de tine; bridge-ul nu o afișează niciodată.

## Depanare [#depanare]

| Simptom | Soluție |
| --- | --- |
| `409 host_offline` | Bridge-ul nu este conectat cu acces la distanță: pornește-l cu `--relay` sau `relay.enabled: true`. |
| *Niciun iPhone online* în hub | La fel ca mai sus; apare la câteva secunde după ce bridge-ul se conectează. |
| *Capturi de ecran* în loc de *Live* | DisplayService nu e disponibil (cameră sau microfon ocupate, nesuportat); controlul funcționează în continuare. |
| Telefonul nu e găsit prin Wi-Fi | Ține-l treaz și în aceeași rețea sau conectează cablul o dată și apelează local `wifi_setup`. |
| Montarea imaginii de dezvoltator eșuează | Închide uneltele care țin ocupat mounter-ul imaginii (Impactor, 3uTools) înainte să pornești bridge-ul. |
