# อุปกรณ์ Linux, FreeBSD, NetBSD, Windows, macOS และ Android

> HTML: https://remote.ivjn.us/docs/th/platforms/

อุปกรณ์ Linux (glibc/musl), FreeBSD, NetBSD, Windows, macOS และ Android นำอุปกรณ์ `remote-agent`, Exchange, `remote-cli` และไคลเอนต์การดำเนินงานและการบำรุงรักษามือถือมาใช้ซ้ำ ฝั่งอุปกรณ์และ CLI ใช้ชื่อสามัญ ตัวแปรสภาพแวดล้อมมีความสม่ำเสมอ `REMOTE_*`

## การติดตั้งคำสั่งเดียว (Windows/Linux/macOS/FreeBSD/NetBSD)

สคริปต์ติดตั้งอย่างเป็นทางการจะดาวน์โหลดและตรวจสอบแพ็กเกจสำหรับระบบของคุณ วางไฟล์ไบนารีไว้ใน `~/.local/bin` แล้วเรียก `remote-agent install` เพื่อตั้งค่าให้ Agent เริ่มทำงานอัตโนมัติเมื่อเข้าสู่ระบบ:

เลือกคำสั่งสำหรับบทบาทของคุณและคัดลอกแต่ละคำสั่งแยกกัน

**ติดตั้ง Agent บนอุปกรณ์ที่ได้รับการจัดการ**

```sh
curl -fsSL https://remote.ivjn.us/install.sh | bash -s -- agent
```

**ติดตั้ง CLI บนเครื่องโอเปอเรเตอร์**

```sh
curl -fsSL https://remote.ivjn.us/install.sh | bash -s -- cli
```

Windows ทำงานใน Git Bash ส่งผ่านข้อมูลรับรอง Agent หลังไปป์: `bash -s -- agent --device-id NAME --token TOKEN` (หรือมี `config.json` / `REMOTE_CONFIG` อยู่แล้ว) ตัวแปรสภาพแวดล้อมที่เขียนหน้า `curl` ไม่สามารถเข้าถึง bash Linux systemd ระดับระบบสามารถเพิ่ม `--system` สคริปต์จะแยกความแตกต่างระหว่าง glibc และ musl (อัลไพน์ ฯลฯ) โดยอัตโนมัติ และรองรับ Linux arm64 (aarch64) ด้วย `REMOTE_LIBC=musl` สามารถใช้เพื่อบังคับการเลือกแพ็กเกจ musl FreeBSD / NetBSD: การติดตั้งที่ไม่มีสิทธิพิเศษยังคงอยู่ใน `~/.local/bin`; รัน `sudo remote-agent install --system --config CONFIG` เพื่อลงทะเบียน rc.d การควบคุมเดสก์ท็อปจำเป็นต้องมีเซสชันผู้ใช้ X11 ที่มี `python3` และ `xdotool` ใช้แพ็คเกจเฉพาะสำหรับ Android และ OpenWrt


## คำสั่งและบริการ

คำสั่งอุปกรณ์และ CLI คือ `remote-agent` และ `remote-cli` `remote-cli` เขียนไปยัง `remote-cli/config.toml` ตามค่าเริ่มต้น `REMOTE_CLI_CONFIG` ที่ชัดเจนจะมีความสำคัญกว่า

แพ็กเกจ OpenWrt, ไฟล์ UCI และบริการ init มีชื่อว่า `remote-agent` ไฟล์สถานะคือ `/var/run/remote-agent.state` และไฟล์คอนฟิกูเรชันคือ `/etc/config/remote-agent` ปลั๊กอิน LuCI `luci-app-remote` ขึ้นอยู่กับแพ็คเกจนี้ `REMOTE_AGENT_SRC` ตั้งค่าพาธซอร์สของบิลด์

| อุปกรณ์ | การดำเนินการตามคำสั่ง | เทอร์มินัลแบบโต้ตอบ | โหมดผู้อยู่อาศัย | อูบุส |
|---|---|---|---|---|
| Linux | `/bin/sh -c` | ยูนิกซ์ PTY | systemd หรือกระบวนการเบื้องหน้า | OpenWrt เท่านั้น |
| Linux musl (อัลไพน์ 3.x ฯลฯ), x64/arm64 | `/bin/sh -c` | ยูนิกซ์ PTY | กระบวนการเบื้องหน้าหรือบริการ OpenRC ที่เขียนเอง (ไม่มี systemd) | ไม่รองรับ |
| FreeBSD 14+, amd64/arm64 | `/bin/sh -c` | ยูนิกซ์ PTY | rc.d (รูท/ระบบ); เบื้องหน้า (ผู้ใช้) | ไม่รองรับ |
| NetBSD 10+, amd64 | `/bin/sh -c` | ยูนิกซ์ PTY | rc.d (รูท/ระบบ); เบื้องหน้า (ผู้ใช้) | ไม่รองรับ |
| Windows 10 1809+/11, เซิร์ฟเวอร์ 2019+ | Windows พาวเวอร์เชลล์ 5.1 | ConPTY + PowerShell | งานที่กำหนดเวลาไว้ในกระบวนการเข้าสู่ระบบหรือเบื้องหน้า | ไม่รองรับ |
| macOS 11+, Apple Silicon/Intel | `/bin/sh -c` | ยูนิกซ์ PTY | launchd LaunchAgent หรือกระบวนการเบื้องหน้า | ไม่รองรับ |
| Android 8.0+, arm64/x86_64 | `/system/bin/sh -c` | ยูนิกซ์ PTY | บริการเบื้องหน้า APK แบบสแตนด์อโลน | ไม่รองรับ |

## การกำหนดค่าทั่วไป

ขั้นแรกให้ลงทะเบียนอุปกรณ์ผ่าน `remote-cli device add <id>` และรับโทเค็นอุปกรณ์ Linux/Windows สร้าง `config.json` ที่สามารถอ่านได้โดยบัญชีที่รันอยู่ในปัจจุบันเท่านั้น:

```json
{
  "server": "wss://mgmt.example.com",
  "device_id": "my-device",
  "token": "REPLACE_WITH_DEVICE_TOKEN"
}
```

```sh
remote-agent --config /path/to/config.json
```

ลำดับความสำคัญคือตัวแปรสภาพแวดล้อมบรรทัดคำสั่ง/`REMOTE_SERVER`, `REMOTE_DEVICE_ID`, `REMOTE_TOKEN` ตามด้วย JSON และสุดท้าย UCI สำรองที่ OpenWrt เท่านั้น JSON ยังสามารถตั้งค่า `state_file` ได้อีกด้วย บรรทัดคำสั่ง `--state-file` / `REMOTE_STATE_FILE` จะแทนที่ เมื่อไม่ได้ระบุไฟล์สถานะ OpenWrt ยังคงใช้ `/var/run/remote-agent.state` แพลตฟอร์มอื่นใช้ `remote-agent/agent.state` ภายใต้ `LOCALAPPDATA`, `XDG_STATE_HOME` และ `$HOME/.local/state` ตามลำดับ และถอยกลับไปที่ไดเร็กทอรีชั่วคราวเมื่อไม่มีไดเร็กทอรีผู้ใช้ แต่ละอินสแตนซ์ควรใช้เส้นทางสถานะอิสระหลายอินสแตนซ์

การเชื่อมต่อการผลิตใช้ `wss://` `ws://127.0.0.1` ในตัวอย่างใช้สำหรับการตรวจสอบภายในเครื่องเท่านั้น

### การแจ้งเตือนลิงก์ Agent

JSON สามารถตั้งค่าเป็น `"notify_urls": ["https://example.invalid/notify"]` หรือใช้ `remote-agent --notify-url https://example.invalid/notify` (ทำซ้ำได้) ตัวแปรสภาพแวดล้อม `REMOTE_NOTIFY_URL` ยอมรับที่อยู่ที่คั่นด้วยเครื่องหมายจุลภาค รายการตัวแปรบรรทัดคำสั่ง/สภาพแวดล้อมที่ไม่ว่างเปล่าแทนที่การกำหนดค่า JSON มิฉะนั้นจะใช้ JSON และในที่สุดก็ถอยกลับไปที่ UCI `remote-agent.main.notify_url` ที่คั่นด้วยเครื่องหมายจุลภาคใน OpenWrt เท่านั้น ปิดใช้งาน POST เมื่อละเว้นแหล่งที่มาทั้งหมดหรือว่างเปล่า

`agent.connected` จะถูกส่งหลังจากการลงทะเบียนสำเร็จ `agent.disconnected` จะถูกส่งหนึ่งครั้งเมื่อเซสชันที่สร้างขึ้นสิ้นสุดลง และเฉพาะ `agent.rejected` เท่านั้นที่จะถูกส่งเมื่อการลงทะเบียนถูกปฏิเสธอย่างชัดเจน เครือข่ายขัดข้องก่อนการลงทะเบียนจะไม่ได้รับแจ้ง POST ใช้ออบเจ็กต์ JSON เดียวกันกับ Exchange Web Push (`title`, `body`, `url`, `event`, `device_id`, `occurred_at`) แต่อธิบายลิงก์ agent แทนสถานะออนไลน์ของอุปกรณ์: `event` นำหน้าด้วย `agent.` แทนที่จะเป็น `device.`, `url` คือ `""` (ไม่มีพาธคอนโซล Web Push), `tag` จะไม่ถูกส่ง เนื้อความของการตัดการเชื่อมต่อและการปฏิเสธประกอบด้วยข้อมูลสรุปโดยย่อเกี่ยวกับเหตุผลด้านความปลอดภัย และไม่มีโทเค็นของอุปกรณ์ เวลาคือ RFC3339 UTC

การแจ้งเตือนจะถูกส่งไปในเบื้องหลัง หากล้มเหลว ระบบจะบันทึกเฉพาะชื่อเหตุการณ์ ที่อยู่เป้าหมายจะไม่ถูกบันทึก และ agent จะไม่ขัดขวางการเชื่อมต่อใหม่ นโยบายการลองใหม่จะคล้ายกับ Exchange Web Push: สูงสุด 5 ครั้ง ข้อผิดพลาดของเครือข่ายและ HTTP 408/429/5xx สามารถลองใหม่ได้ 404/410 และ 4xx ที่ไม่สามารถลองใหม่ได้อื่นๆ จะถูกยกเลิก แนะนำให้ใช้ `Retry-After` จำนวนเต็มวินาที ไม่เช่นนั้นการถอยกลับแบบเอ็กซ์โปเนนเชียลจะเริ่มที่ 5 วินาที และการหน่วงเวลาจะจำกัดอยู่ที่ 1–900 วินาที ยอมรับเฉพาะ HTTP/HTTPS อย่าติดตามการเปลี่ยนเส้นทาง อย่าใส่ชื่อผู้ใช้ รหัสผ่าน หรือข้อมูลรับรองอื่นๆ ใน URL ที่อยู่ที่มีชื่อผู้ใช้/รหัสผ่านจะถูกปฏิเสธ


### พร็อกซีขาออกและการเชื่อมต่อยังคงอยู่

ทั้งไม่มีส่วนติดต่อผู้ใช้และ `--ui` เชื่อมต่อกับ Exchange ผ่าน `client::run` เดียวกัน WebSocket อ่านตัวแปรสภาพแวดล้อม agent ที่สืบทอดโดยกระบวนการ agent: ใช้ `HTTPS_PROXY` / `https_proxy` ที่ไม่ว่างเปล่าตัวแรก, `ALL_PROXY` / `all_proxy`, `HTTP_PROXY` / `http_proxy`; โฮสต์ที่ตรงกัน `NO_PROXY` / `no_proxy` จะข้ามพร็อกซี `remote-cli` ของ WebSocket ใช้กฎเดียวกัน (#36/#37, #39) การเข้าสู่ระบบบริการที่เริ่มต้นด้วยตนเองหรือในเบื้องหลังจำเป็นต้องตั้งค่าตัวแปรเหล่านี้ในสภาพแวดล้อมที่ทำงานอยู่ การตั้งค่าในเทอร์มินัลอื่นจะไม่ถูกส่งไปยังกระบวนการที่เริ่มต้น

เมื่ออุปกรณ์ WSS ผ่านพร็อกซี อุปกรณ์จะสร้างอุโมงค์ **HTTP CONNECT** ก่อน จากนั้นจึงใช้rustls เพื่อเชื่อมต่อกับเป้าหมาย ALPN ของ TLS ClientHello ** ประกาศเฉพาะ `http/1.1`** เท่านั้น ซึ่งสอดคล้องกับ CLI การเจรจาต่อรอง `h2` จะทำลายเส้นทางการอัพเกรด HTTP/1.1 WebSocket ปัจจุบัน เมื่อติดอยู่บนการเชื่อมต่อภายใต้พร็อกซีคลาส Clash คุณควรตรวจสอบว่าพร็อกซีรองรับการเชื่อมต่อหรือไม่ และใช้บิลด์ที่มีการแก้ไข #39/#42 ทั้งอุปกรณ์และพาธ WebSocket ของ CLI ไม่รองรับ SOCKS โปรดใช้พร็อกซี HTTP CONNECT (เช่น `HTTPS_PROXY=http://127.0.0.1:7890`)

หลังจากการเชื่อมต่อโดยตรงหรือ CONNECT เสร็จสมบูรณ์ agent จะเปิดใช้งาน `TCP_NODELAY` และ TCP Keepalive หลังจากไม่ได้ใช้งานเป็นเวลา 30 วินาที; นอกจากนี้ยังส่ง WebSocket Ping สำหรับฮาร์ตบีต JSON แต่ละรายการ และล้าง Pong ที่อยู่ในคิวโดยอัตโนมัติอย่างชัดเจนหลังจากได้รับ Ping การสร้างการเชื่อมต่อและการรอการตอบสนองการลงทะเบียนแต่ละครั้งจะมีการหมดเวลา 45 วินาที การส่งและหลังการลงทะเบียน Pong flushes จะมีระยะหมดเวลา 30 วินาที สำหรับรายละเอียด โปรดดูที่ [Keep-alive และ timeouts](/docs/th/websocket-protocol.md#keepalive-และหมดเวลา) (#42)

บิลด์ Windows ที่เปิดใช้ `desktop-ui` ใช้รันไทม์ Tokio แบบหลายเธรดเพื่อจัดการ axum ถาดระบบในเครื่อง และ WebSocket ของอุปกรณ์พร้อมกัน รวมถึงเมื่อทำงานแบบไม่มีส่วนติดต่อผู้ใช้ บนแพลตฟอร์มอื่นที่เปิดใช้ฟีเจอร์นี้ UI ในเครื่อง (ระบุ `--ui` หรือเปิดโดยไม่มีอาร์กิวเมนต์) ก็ใช้หลายเธรดเช่นกัน บิลด์ที่ไม่มีฟีเจอร์นี้และการทำงานแบบไม่มีส่วนติดต่อผู้ใช้พร้อมระบุการตั้งค่าบนแพลตฟอร์มอื่นจะใช้ `current_thread` ซึ่งเหมาะกับอุปกรณ์ขนาดเล็ก

## Linux / Windows / macOS อินเทอร์เฟซการกำหนดค่า

รุ่นเปิดใช้งาน `desktop-ui` ตามค่าเริ่มต้น และรุ่นเดสก์ท็อปจะต้องคง UI การกำหนดค่าในเครื่องไว้ ใช้ `--no-default-features` สำหรับเซิร์ฟเวอร์ที่ไม่มีส่วนหัวหรือบิลด์แบบฝังเท่านั้น ในขณะที่เชื่อมต่อ เซิร์ฟเวอร์การจัดการ รหัสอุปกรณ์ และอินพุตโทเค็นอุปกรณ์จะถูกซ่อนไว้ การหยุดการเข้าถึงระยะไกลจะแสดงอีกครั้ง สถานะการเชื่อมต่อ การตั้งค่า LAN และการเริ่มเมื่อเข้าสู่ระบบยังคงมองเห็นได้

```sh
cargo build --locked -p agent --release
# Linux / macOS
./target/release/remote-agent --ui
```

```powershell
# Windows
.\target\release\remote-agent.exe --ui
```

บน Linux / Windows / macOS หากเริ่มโดยไม่มีพารามิเตอร์การเชื่อมต่อ (เช่น ดับเบิลคลิก `remote-agent.exe`) และเปิดใช้ `desktop-ui`: เมื่อยังไม่มีการตั้งค่าที่ใช้ได้ ระบบจะเขียน `config.json` เริ่มต้น (กรอกเฉพาะ Exchange เริ่มต้น โดยเว้น device_id/token ว่าง) และเปิดหน้าตั้งค่าในเบราว์เซอร์ เมื่อมีการตั้งค่าแล้ว Windows ใช้ถาดระบบโดยไม่บังคับเปิดเบราว์เซอร์ ส่วน Linux/macOS พิมพ์ URL ในเครื่อง (macOS มีไอคอนบนแถบเมนูด้วย) การเปิดแบบโต้ตอบโดยไม่มีอาร์กิวเมนต์เมื่อไม่ได้เปิดใช้ `desktop-ui` จะสร้างไฟล์ตั้งค่าเริ่มต้นและพิมพ์เส้นทางก่อนออก แทนการแสดงเพียง `Missing device_id` ส่วน OpenWrt / systemd / งานตามกำหนดเวลา ควรใช้ `--config`, UCI หรือ `REMOTE_*` ต่อไป

Bare เปิดตัวและชัดเจน `--ui` ใช้กฎเดียวกัน: เปิดเบราว์เซอร์เริ่มต้นเฉพาะเมื่อการกำหนดค่าที่บันทึกไว้ไม่มี `device_id`/`token` ที่ไม่ว่างเปล่า ตัวแทนที่กำหนดค่าจะเชื่อมต่อในพื้นหลังโดยไม่ต้องเปิดเบราว์เซอร์อื่นเมื่อเข้าสู่ระบบ Windows เก็บรายการถาดไว้สำหรับการเปิดเพจด้วยตนเอง Linux/macOS ใช้ URL ภายในเครื่องที่พิมพ์ในเทอร์มินัล `--ui --no-open` ระงับการเปิดอัตโนมัติเสมอ รวมถึงการตั้งค่าครั้งแรกด้วย `--ui-port 8765` ตั้งค่าพอร์ตคงที่ มิฉะนั้นพอร์ตที่มีอยู่จะถูกเลือกแบบสุ่ม UI จะฟังบน `127.0.0.1` เท่านั้น เนื้อหาทั้งหมดถูกฝังโดยไม่มีแบบอักษรภายนอกหรือ CDN

ถาดระบบบน Windows และแถบเมนูบน macOS มีรายการสำหรับการตั้งค่า การตั้งค่าโปรแกรมรักษาหน้าจอ การจัดการงาน และการใช้งานคลิปบอร์ด รายการจัดการงานจะเปิดแผงงานในเครื่องโดยตรง ส่วน Linux ใช้หน้าตั้งค่าในเบราว์เซอร์

ขั้นแรกให้ใช้ `remote-cli device add <id>` เพื่อรับ ID อุปกรณ์และโทเค็น จากนั้นกรอกเซิร์ฟเวอร์ ID อุปกรณ์ และโทเค็นอุปกรณ์ใน **การตั้งค่าการเชื่อมต่อ**:

- **Save only**: บันทึกการกำหนดค่าโดยไม่ต้องเชื่อมต่อกับเซิร์ฟเวอร์
- **บันทึกและเชื่อมต่อ**: บันทึกและเชื่อมต่อ; เมื่อการกำหนดค่าได้รับการแก้ไขระหว่างการดำเนินการ การเชื่อมต่อเก่าจะถูกตัดการเชื่อมต่อ และการกำหนดค่าใหม่จะถูกใช้ในการเชื่อมต่อ
- **หยุดการเข้าถึงระยะไกล**: ตัดการเชื่อมต่อ ยกเลิกการดำเนินการระยะไกลปัจจุบันและเซสชันเทอร์มินัล การกำหนดค่ายังคงอยู่
- แถบสถานะจะแสดง **Connected**, กำลังเชื่อมต่อ, กำลังเชื่อมต่อใหม่ หรือ หยุดหลังจากการลงทะเบียนสำเร็จ และแสดงข้อผิดพลาดในการเชื่อมต่อล่าสุด

เมื่อเริ่มต้นการเชื่อมต่อ UI จะเขียน **Connecting** ลงในไฟล์สถานะของตัวเองก่อน จากนั้นจึงเริ่มงาน `client::run` เพื่อหลีกเลี่ยงไม่ให้เพจคงอยู่ที่ค่าเริ่มต้นเมื่อไฟล์สถานะยังไม่มีอยู่ (#42)

โทเค็นที่บันทึกไว้จะไม่ถูกส่งกลับไปยังหน้าเว็บ เว้นว่างไว้เพื่อคงโทเค็นเดิมไว้ การกำหนดค่าใช้การแทนที่ไฟล์อะตอมมิกชั่วคราว สิทธิ์ Linux คือ `0600` และ Windows ACL ให้สิทธิ์เฉพาะผู้ใช้ปัจจุบันเท่านั้น เมื่อไฟล์การกำหนดค่า JSON เสียหายหรืออ่านไม่ได้ อินเทอร์เฟซจะแจ้งให้ซ่อมแซมและไฟล์ต้นฉบับจะไม่ถูกเขียนทับ ไฟล์การกำหนดค่าเดียวกันสามารถเปิดได้โดยกระบวนการ UI เดียวเท่านั้น

พาธการกำหนดค่าเริ่มต้นคือ `$XDG_CONFIG_HOME/remote-agent/config.json` สำหรับ Linux/macOS (`~/.config/remote-agent/config.json` เมื่อไม่ได้ตั้งค่า) และ `%LOCALAPPDATA%\remote-agent\config.json` สำหรับ Windows `--ui --config <path>` หรือ `REMOTE_CONFIG` ระบุไฟล์ JSON ที่มีอยู่ UI ไม่ยอมรับ `--server`, `--device-id`, `--token`, `--state-file` และการแทนที่ตัวแปรสภาพแวดล้อมที่เกี่ยวข้อง เพื่อหลีกเลี่ยงความไม่สอดคล้องกันระหว่างค่าที่บันทึกไว้และการเชื่อมต่อจริง JSON `state_file` ยังใช้กับ UI อีกด้วย เมื่อไม่อยู่ ระบบจะใช้ค่าเริ่มต้นแบบถาวรเช่นเดียวกับการไม่มีส่วนหัวเพื่อรักษาเอกลักษณ์และความไว้วางใจที่ปิดผนึกไว้

หน้าการกำหนดค่าจะมีสวิตช์ **Start เมื่อเข้าสู่ระบบ/เข้าสู่ระบบอัตโนมัติเริ่มต้น** (`autostart: { supported, enabled, detail }` จาก `GET /api/status`, `POST /api/autostart` จาก `{ "enabled": true|false }`) ไม่จำเป็นต้องมีสิทธิพิเศษระดับสูง:

- **macOS**: ติดตั้ง/ลบ `~/Library/LaunchAgents/us.ivjn.remote-agent.plist` (เทมเพลตเหมือนกับ `install/macos/install.sh`), `launchctl bootstrap` / `bootout`; ไบนารีจะถูกคัดลอกไปที่ `~/.local/bin/remote-agent` ก่อน และการกำหนดค่าเริ่มต้นเป็น `~/.config/remote-agent/config.json` (คงไว้หากมี)
- **Windows**: ลงทะเบียน/ยกเลิกการลงทะเบียนงานที่กำหนดเวลาไว้ของผู้ใช้ปัจจุบัน `RemoteAgent` (AtLogOn), การจัดตำแหน่งแบบลอจิคัล `install/windows/install.ps1`, ไดเร็กทอรีการติดตั้ง `%LOCALAPPDATA%\remote-agent`
- **Linux**: ติดตั้ง/ลบยูนิต systemd ของผู้ใช้ (`install/linux/remote-agent.service`), `systemctl --user enable --now`/`disable`

เมื่อเปิดใช้งาน ไฟล์ปฏิบัติการปัจจุบันจะถูกคัดลอกไปยังพาธการติดตั้งที่ตกลงไว้เมื่อจำเป็น และ `config.json` ที่มีอยู่จะถูกเก็บไว้ การตอบกลับและบันทึกจะไม่พิมพ์โทเค็น UI ยังคงจัดการกระบวนการปัจจุบัน หลังจากเปิดใช้งานการเริ่มต้นด้วยตนเอง LaunchAgent/scheduled jobs/systemd จะดึงพื้นหลัง agent ขึ้นมาในการเข้าสู่ระบบครั้งถัดไปเมื่อออกจาก UI

**การปิดแท็บเบราว์เซอร์ไม่ได้หยุด Agent การออกจากกระบวนการ Agent จะหยุดการเชื่อมต่อ การเริ่มต้น UI จะเชื่อมต่ออุปกรณ์ที่กำหนดค่าโดยอัตโนมัติ การตั้งค่าครั้งแรกต้องบันทึกและเชื่อมต่อ ** UI จัดการกระบวนการปัจจุบันและสามารถเปิดหรือปิดใช้งานการเริ่มต้นการเข้าสู่ระบบอัตโนมัติ หยุดบริการพื้นหลังที่มีอยู่ก่อนที่จะเริ่มกระบวนการ UI อื่นสำหรับอุปกรณ์เดียวกัน หลังจากกำหนดค่าและเปิดใช้งานการเริ่มอัตโนมัติแล้ว คุณสามารถปิดแท็บเบราว์เซอร์ได้ การดำเนินการ Remote ใช้สิทธิ์ของบัญชีที่รัน Agent

URL ดั้งเดิมมีคีย์การเข้าถึงแบบสุ่มสำหรับ UI นี้ โปรดอย่าแชร์ API ยังตรวจสอบโฮสต์และต้นทางและปฏิเสธการโทรจากหน้าเว็บภายนอก โทเค็นอุปกรณ์ถูกจัดเก็บไว้ในไฟล์กำหนดค่าและไม่ได้จัดเก็บแบบเข้ารหัส บัญชีปัจจุบันและผู้ดูแลระบบของเครื่องยังคงสามารถอ่านได้ โครงสร้างแบบแยกส่วนโดยไม่มีคุณลักษณะ `desktop-ui` จะคงลักษณะการทำงานของบรรทัดคำสั่งดั้งเดิมไว้ และ OpenWrt และ Android ใช้อินเทอร์เฟซที่มีอยู่ตามลำดับ

## ฟรีBSD

เว็บไซต์อย่างเป็นทางการมี `remote-freebsd-x64.tar.gz` และ `remote-freebsd-arm64.tar.gz` (คอมไพล์ข้ามกับ `cargo zigbuild` บน Linux และลิงก์ไปยัง FreeBSD 14 libc) หลังการบีบอัด:

```sh
mkdir -p ~/.local/bin
cp remote-agent remote-cli ~/.local/bin/
sudo remote-agent install --system --device-id NAME --token TOKEN
```

แหล่งที่มาปัจจุบันรองรับการติดตั้งระบบ rc.d, ตัวชี้วัด sysctl, ผลรวมของอินเทอร์เฟซ `netstat -ibn`, การอ่านอุณหภูมิ/พัดลม FreeBSD และ NetBSD `envstat` การควบคุมเดสก์ท็อป X11 ได้รับการโฆษณาเมื่อมี `DISPLAY`, `python3` และ `xdotool`; ภาพหน้าจอต้องใช้หมอนเพิ่มเติม การตั้งค่าสื่อและระบบยังคงไม่พร้อมใช้งาน การอัปเดตด้วยตนเองจะจดจำไฟล์เก็บถาวร BSD x64/arm64 เมื่อมีแพ็คเกจที่ได้รับการยืนยันที่ตรงกันอยู่ในรายการเผยแพร่ การเปลี่ยนแปลงเหล่านี้จำเป็นต้องมีแพ็คเกจที่สร้างขึ้นใหม่ การตรวจสอบรันไทม์ BSD ดั้งเดิมยังคงเป็นสิ่งจำเป็น

## เน็ตบีเอสดี

เว็บไซต์อย่างเป็นทางการให้ `remote-netbsd-x64.tar.gz` (amd64 โดยใช้ zig link NetBSD 10 libc cross-compilation บน Linux การพึ่งพาแบบไดนามิกเป็นเพียง libc / libpthread / libutil) การใช้งานเหมือนกับ FreeBSD:

```sh
mkdir -p ~/.local/bin
cp remote-agent remote-cli ~/.local/bin/
sudo remote-agent install --system --device-id NAME --token TOKEN
```

ความสามารถ: เชื่อมต่อกับ Exchange, `exec`/PTY/การถ่ายโอนไฟล์; ตัวชี้วัดผ่าน sysctl (`kern.cp_time`, `hw.physmem64`, `vm.uvmexp2`); การควบคุม GUI ของเดสก์ท็อปถูกนำมาใช้เป็นแบบ stub ไม่ได้ประกาศไว้ แพลตฟอร์มอุปกรณ์รายงานว่าเป็น `netbsd` การอัปเดตตัวเองและการรีสตาร์ท rc.d ทำงานเหมือนกับบน FreeBSD (`remote-netbsd-x64.tar.gz` / `remote-netbsd-arm64.tar.gz`) หากรายการไฟล์แสดงรายการไฟล์เก็บถาวร

## แพ็คเกจ OpenWrt อย่างเป็นทางการ 25.12

เว็บไซต์อย่างเป็นทางการมีแพ็คเกจ `remote-agent` ที่สร้างด้วย OpenWrt 25.12.5 SDK อย่างเป็นทางการ (apk-tools, เวอร์ชันแพ็คเกจ `0.1.29-r1` รวมถึง agent 0.1.29):

| เป้าหมาย | สถาปัตยกรรมแพ็คเกจ | ไฟล์ |
|---|---|---|
| ipq40xx/ทั่วไป | arm_cortex-a7_neon-vfpv4 | `remote-agent-openwrt-ipq40xx-official.apk` |
| x86/64 | x86_64 | `remote-agent-openwrt-x86_64-official.apk` |
| armr/armv8 | aarch64_generic | `remote-agent-openwrt-armsr-armv8-official.apk` |
| สื่อกลาง/filogic | aarch64_cortex-a53 | `remote-agent-openwrt-mediatek-filogic-official.apk` |
| bcm27xx/bcm2711 | aarch64_cortex-a72 | `remote-agent-openwrt-bcm27xx-bcm2711-official.apk` |
| ทางลาด/mt7621 | mipsel_24kc | `remote-agent-openwrt-ramips-mt7621-official.apk` |
| ath79/ทั่วไป | mips_24kc | `remote-agent-openwrt-ath79-official.apk` |

อินเทอร์เฟซ LuCI เป็นเรื่องปกติ: `luci-app-remote-openwrt-official.apk` หลังจากตรวจสอบสถาปัตยกรรมด้วย `apk --print-arch` `apk add --allow-untrusted ./文件.apk` แล้ว แพ็คเกจ MIPS ยังขึ้นอยู่กับ `libgcc1` เพิ่มเติม (อิมเมจอย่างเป็นทางการจะมาพร้อมกับแพ็คเกจตามค่าเริ่มต้น)

## Linux musl (อัลไพน์) และ arm64

- `remote-linux-arm64.tar.gz`: aarch64, glibc 2.28+ (Debian 10+/Ubuntu 18.04+/RHEL 8+, ระบบ Raspberry Pi 64 บิต ฯลฯ) รองรับการลงทะเบียน `remote-agent install` ด้วย systemd และการอัปเดตด้วยตนเอง
- `remote-linux-musl-x64.tar.gz` / `remote-linux-musl-arm64.tar.gz`: musl เชื่อมโยงแบบคงที่สำหรับการแจกแจงโดยไม่มี glibc เช่น Alpine อัลไพน์ไม่มี systemd เป็นค่าเริ่มต้น ใช้ `remote-agent install --no-start` เพื่อเขียนการกำหนดค่า จากนั้นเพิ่มบริการ OpenRC ของคุณเอง (`/etc/init.d/remote-agent`) หรือเรียกใช้ในเบื้องหน้า musl builds รองรับ `update self` และการอัปเดตอัตโนมัติบนเดสก์ท็อปและติดตั้งเฉพาะไฟล์เก็บถาวร musl เท่านั้น หลังจากการอัปเดตครั้งเดียว พวกเขาลองใช้ systemd จากนั้น OpenRC (`rc-service remote-agent restart`) จากนั้นให้รีสตาร์ทกระบวนการ ไฟล์เก็บถาวร musl บนโฮสต์ OpenWrt ยังคงอัปเดตผ่าน opkg/apk

## Linux

ขอแนะนำให้ใช้คำสั่งย่อยในตัว (ซึ่งจะคัดลอกไฟล์ปฏิบัติการปัจจุบัน เขียนการกำหนดค่าส่วนตัว และลงทะเบียน systemd ไม่ต้องพึ่งพาไฟล์ `install/` ในที่เก็บ):

```sh
cargo build --locked -p agent --no-default-features --profile release-small
# 用户级（默认，非 root）
./target/release-small/remote-agent install \
  --config /path/to/config.json
# 或直接传凭证（无需先有文件；安装过程不访问网络）
./target/release-small/remote-agent install \
  --device-id NAME --token TOKEN

# 系统级（root 或 --system）：/usr/local/bin + /etc/remote-agent + multi-user.target
sudo ./target/release-small/remote-agent install --system \
  --device-id NAME --token TOKEN

remote-agent status                    # 安装布局、服务、进程、连接（不打印令牌）
systemctl --user status remote-agent   # 用户级
# systemctl status remote-agent        # 系统级
remote-agent uninstall                 # 卸服务；加 --force 同时删二进制与配置
```

ขั้นตอนแบบแมนนวลที่เทียบเท่า (ยังมีอยู่):

```sh
install -Dm755 target/release-small/remote-agent ~/.local/bin/remote-agent
install -Dm600 /path/to/config.json ~/.config/remote-agent/config.json
install -Dm644 install/linux/remote-agent.service ~/.config/systemd/user/remote-agent.service
systemctl --user daemon-reload
systemctl --user enable --now remote-agent
```

บริการผู้ใช้ทำงานตามค่าเริ่มต้นด้วยเซสชันการเข้าสู่ระบบ เมื่อจำเป็นต้องบูตและอาศัย ผู้ดูแลระบบสามารถดำเนินการ `loginctl enable-linger <user>` หรือใช้ยูนิตระบบ `--system` ข้างต้น (`RuntimeDirectory=remote-agent`, `WantedBy=multi-user.target`) คำสั่ง Remote ใช้สิทธิ์ของบัญชีบริการ การหยุดบริการจะยุติกระบวนการทั้งหมดภายในบริการ `install/linux/install.sh` เป็นกระดาษห่อแบบบางรอบๆ `remote-agent install --force`

## Windows

หลังจากติดตั้ง Toolchain Rust MSVC และ Visual Studio C++ Build Tools:

```powershell
cargo build --locked -p agent --release
.\target\release\remote-agent.exe --config C:\path\config.json

# 推荐：为当前登录用户安装常驻计划任务
.\target\release\remote-agent.exe install --config C:\path\config.json
# 已存在任务/文件时加 --force；仅注册不启动加 --no-start
Get-ScheduledTaskInfo -TaskName RemoteAgent
remote-agent status
remote-agent uninstall
remote-agent uninstall --force   # 同时删除 %LOCALAPPDATA%\remote-agent 下的文件
```

`install` คัดลอกไบนารีและคอนฟิกูเรชันไปยัง `%LOCALAPPDATA%\remote-agent` จำกัด ACL โปรไฟล์ และลงทะเบียนงานที่กำหนดเวลาไว้ `RemoteAgent` ที่เริ่มต้นเมื่อล็อกอิน ตามค่าเริ่มต้น หากมีงานหรือไฟล์เป้าหมายอยู่แล้ว ข้อผิดพลาดจะถูกรายงาน `--force` จะหยุดและยกเลิกการลงทะเบียนงานเก่าก่อน จากนั้นจึงเขียนทับ โหมดนี้ต้องมีการเข้าสู่ระบบของผู้ใช้ ไม่ใช่บริการระบบ Windows SCM `install/windows/install.ps1` เป็นกระดาษห่อแบบบางรอบๆ `remote-agent install` (สามารถเพิ่ม `-Force` ได้)

หากคุณใช้ UI เพื่อสร้างการกำหนดค่าก่อน `--config` จะสามารถชี้ไปยังเส้นทางที่แยกจากกัน (เช่น ไฟล์ที่สร้างโดย `--ui --config .\device-config.json`)

คำสั่ง Windows ใช้ไวยากรณ์ PowerShell เช่น `Get-Process` และเอาต์พุตจะถูกส่งในรูปแบบ UTF-8 แต่ละเซสชันการดำเนินการและเทอร์มินัลมีวัตถุงานอิสระ แผนผังกระบวนการลูกถูกล้างข้อมูลเมื่อหมดเวลา การยกเลิก การตัดการเชื่อมต่อ หรือทางออก agent

## macOS

ต้องใช้ toolchain Rust (`rustup` โดยมีเป้าหมาย `aarch64-apple-darwin` หรือ `x86_64-apple-darwin` ดีฟอลต์) ตัวบ่งชี้จะถูกรวบรวมโดย sysctl, `getloadavg(3)` และ mach `host_statistics` และข้อมูลคงที่ (โมเดล, เคอร์เนล, เวอร์ชันระบบ) จะถูกรวบรวมโดย `sysctl` และ `sw_vers`; แพลตฟอร์มรายงานว่าเป็น `macos`

เซ็นเซอร์ตามความต้องการ (ไม่ใช่ฮาร์ตบีท) พยายามอย่างเต็มที่ในการรวบรวมโดยไม่มี sudo หรือการอ้างอิง SMC เพิ่มเติม: แบตเตอรี่ `pmset -g batt` (แล็ปท็อป), CPU_Speed_Limit / Scheduler / Scheduler / Available CPU ของ `ioreg`, AppleSmartBattery `Temperature` ของ `pmset -g therm` และ RPM ของพัดลม (หากเปิดเผยเฟิร์มแวร์) คอมพิวเตอร์เดสก์ท็อป (เช่น Mac mini) มักไม่มีแบตเตอรี่ Apple Silicon อาจไม่มี CPU_Speed_Limit ที่เป็นตัวเลข อุณหภูมิดิบของ CPU die/ความใกล้เคียงต้องใช้ IOKit/SMC ส่วนตัว ซึ่งขณะนี้ยังไม่ถูกคัดลอก หากมีการอ่านค่าความดันความร้อน การควบคุมปริมาณ หรือพัดลม Agent จะไม่ทำเครื่องหมายว่า `thermal` ไม่พร้อมใช้งาน หลังจากเปิดใช้งาน `desktop-ui` แล้ว คุณสามารถใช้ `remote-agent --ui` เพื่อกำหนดค่าและสลับการเปิดใช้งานการเข้าสู่ระบบอัตโนมัติ (LaunchAgent)

```sh
cargo build --locked -p agent --release

# 推荐：安装为随登录会话运行的 LaunchAgent（无需 sudo）
./target/release/remote-agent install --config ~/.config/remote-agent/config.json
# 或：install/macos/install.sh ~/.config/remote-agent/config.json
remote-agent status
launchctl print gui/$(id -u)/us.ivjn.remote-agent | grep state
```

`remote-agent install` คัดลอกไฟล์ปฏิบัติการปัจจุบันไปที่ `~/.local/bin/remote-agent` เขียน `~/.config/remote-agent/config.json` (สิทธิ์ 600) สร้าง `~/Library/LaunchAgents/us.ivjn.remote-agent.plist` และดำเนินการ `codesign -s - --force` บนไบนารี (macOS จะฆ่าการแทนที่แทนที่ที่ไม่ได้ลงนาม) จากนั้น `launchctl bootstrap` / `kickstart`. `RunAtLoad` + `KeepAlive` อนุญาตให้ agent เริ่มต้นทันทีหลังจากเข้าสู่ระบบ หรือถูกดึงขึ้นมาโดย launchd หลังจากเกิดข้อขัดข้องหรือขาดการเชื่อมต่อ บันทึกถูกเขียนไปที่ `~/Library/Logs/remote-agent.log` `install/macos/install.sh` เป็นกระดาษห่อแบบบาง `$HOME/.local/state/remote-agent/agent.state` ถูกใช้เมื่อไม่ได้ระบุไฟล์สถานะ คำสั่ง Remote ทำงานโดยได้รับอนุญาตจากผู้ใช้ที่ล็อกอินนั้น

หยุด/ถอนการติดตั้ง:

```sh
remote-agent uninstall
# 或手动：
# launchctl bootout gui/$(id -u)/us.ivjn.remote-agent
# rm ~/Library/LaunchAgents/us.ivjn.remote-agent.plist
```

เมื่อจำเป็นต้องบูตเครื่อง (เมื่อไม่มีใครเข้าสู่ระบบ) และอยู่ที่นั่น ให้ใช้ LaunchDaemon ภายใต้ `/Library/LaunchDaemons` แทนแล้วโหลดเป็น root ในโหมดนี้ คำสั่งระยะไกลจะทำงานด้วยสิทธิ์รูท โปรดประเมินความเสี่ยงตามนั้น

## Android APK แบบสแตนด์อโลน

โปรเจ็กต์ฝั่งอุปกรณ์อยู่ที่ `android-agent/` `mobile/` ยังคงเป็นไคลเอ็นต์การดำเนินการและการบำรุงรักษา ทั้งสองสามารถติดตั้งแยกกันได้โดยไม่ต้องใช้ Expo Go, Termux หรือรูท

ที่อยู่เริ่มต้นของ Agent สำหรับทุกแพลตฟอร์มคือ `wss://exchange.ivjn.us` Android สามารถคลิก **Connect ด้วยรหัสหกหลัก** และ CLI ผู้ดูแลระบบจะป้อนรหัสหกหลักเพื่อขออนุมัติและรับข้อมูลประจำตัวของอุปกรณ์โดยอัตโนมัติ Agent ดั้งเดิม (เพจท้องถิ่น Windows/macOS, OpenWrt LuCI "เพิ่มอุปกรณ์", บรรทัดคำสั่ง `remote-agent pair` / `remote-agent join <邀请>`) สามารถแสดงรหัสหกหลักและรหัส QR เพื่อให้ Console สแกนและอนุมัติ หรือวางคำเชิญเพียงครั้งเดียวเพื่อเข้าร่วม ดูรายละเอียดใน [ การเริ่มต้นและการจับคู่](/docs/th/pairing-and-enrollment.md)

### การเชื่อมต่อครั้งแรกและหน้าจอหลัก

1. เปิด **Remote Agent** บนอุปกรณ์ Android ที่คุณต้องการจัดการ สำหรับการตั้งค่าครั้งแรก ให้เลือก **เชื่อมต่อด้วยรหัสหกหลัก** หรือ **สแกนการเชื่อมต่อ QR code** และขอรับการอนุมัติจากผู้ดูแลระบบ ดู [การเริ่มต้นและการจับคู่](/docs/th/pairing-and-enrollment.md) สำหรับบัญชี รหัส QR และเวิร์กโฟลว์รหัสหกหลัก
2. หากคุณมี ID อุปกรณ์และโทเค็นอุปกรณ์อยู่แล้ว ให้เปิด **Settings → Connection settings** ป้อนเซิร์ฟเวอร์และข้อมูลประจำตัว และเลือก **Save และเชื่อมต่อ** ช่องข้อมูลประจำตัวจะถูกซ่อนไว้ในขณะที่เปิดใช้งานการเชื่อมต่อ ตัดการเชื่อมต่อจากหน้าจอหลักก่อนที่จะเปลี่ยนข้อมูลระบุตัวตนของอุปกรณ์
3. หน้าจอหลักแสดงสถานะการเชื่อมต่อ การควบคุมการแชร์หน้าจอ และรายชื่อผู้จัดการ เลือก **Connect device** เมื่อบันทึกการกำหนดค่าแล้ว แต่การเชื่อมต่อถูกปิดใช้งาน การเปิดแอปอีกครั้งจะคืนค่าการเชื่อมต่อที่เปิดใช้งาน

Android 12 และใหม่กว่าใช้หน้าจอเริ่มต้นของระบบ Android 8–11 ใช้พื้นหลังเปิดตัวพร้อมสีของแบรนด์ที่ตรงกัน แอปจะเปิดหน้าจอหลักเมื่อเฟรมแรกพร้อม โดยไม่ต้องรอการเชื่อมต่อเครือข่ายหรือเพิ่มระยะเวลาสแปลชขั้นต่ำ การเปลี่ยนภาพจะถูกข้ามเมื่อภาพเคลื่อนไหวของระบบถูกปิดใช้งาน การตั้งค่าโหลดตามความต้องการ การเปิดหน้าจอหลักจะไม่ให้สิทธิ์ในการเข้าถึงหรือการแชร์หน้าจอ สิ่งนี้จะอธิบายพฤติกรรมของแหล่งที่มาปัจจุบัน ตรวจสอบเวอร์ชันที่ติดตั้งก่อนที่จะคาดหวังให้ดาวน์โหลดแบบสาธารณะ

### การดำเนินการในเบื้องหลัง การแจ้งเตือน และการหยุด

การย้ายแอปไปที่พื้นหลังหรือการปิดอินเทอร์เฟซไม่ได้หยุดบริการเบื้องหน้า Agent เชื่อมต่อใหม่หลังจากเครือข่ายขาดหาย เมื่อเปิดใช้งานการเชื่อมต่อ การบูตและปลดล็อคอุปกรณ์ หรือการอัปเดตแพ็คเกจแอพ ทริกเกอร์ความพยายามในการกู้คืนบริการ หลังจากบังคับหยุด ให้เปิดแอปอีกครั้งด้วยตนเอง นโยบายแบตเตอรี่ของผู้ผลิตอาจต้องได้รับอนุญาตจึงจะทำงานในเบื้องหลังได้

เมื่ออนุญาตการแจ้งเตือน ให้เลือก **จัดการการเชื่อมต่อ** เพื่อกลับไปที่แอป ขณะแชร์ การแจ้งเตือนจะเสนอ **หยุดการแชร์** ซึ่งจะสิ้นสุดการแชร์หน้าจอเท่านั้น หากต้องการยกเลิกการเชื่อมต่อระยะไกลและปิดใช้งานการกู้คืนอัตโนมัติ ให้เลือก **Disconnect** บนหน้าจอหลักแล้วยืนยัน การกำหนดค่าที่บันทึกไว้ยังคงพร้อมใช้งานสำหรับอุปกรณ์ **Connect** ในภายหลัง การกระทำ **Stop หน้าจอหลักการแชร์หน้าจอ ** ยังสิ้นสุดการแบ่งปันโดยไม่ต้องตัดการเชื่อมต่อการเข้าถึงระยะไกล

โทเค็นของอุปกรณ์ถูกเข้ารหัสด้วย Android Keystore AES-GCM การกำหนดค่าแอปไม่รวมอยู่ในการสำรองข้อมูล และกิจกรรมหรือบริการที่ส่งออกจะไม่ส่งโทเค็น คำสั่ง Remote ทำงานภายใต้ UID แอปพลิเคชัน APK ซึ่งถูกจำกัดโดยแซนด์บ็อกซ์ Android และ SELinux สิ่งนี้ไม่ให้สิทธิ์การเข้าถึงข้อมูลของแอพอื่นหรือสิทธิ์รูท การตั้งค่าระบบ การเข้าถึง และการแชร์หน้าจอจำเป็นต้องได้รับอนุญาตจากตนเอง

ในขณะที่เปิดใช้งานการเข้าถึงระยะไกล บริการเบื้องหน้าจะเก็บ CPU และล็อคการปลุกหน้าจอ ทำให้หน้าจอเปิดอยู่ในพื้นหลังและระหว่างการเชื่อมต่อใหม่โดยต้องเสียพลังงานเพิ่มเติม การยกเลิกการเชื่อมต่อหรือทำลายบริการจะปลดล็อกและเรียกคืนลักษณะการหมดเวลาของหน้าจอระบบตามปกติ ปุ่มเปิดปิดยังคงล็อคอุปกรณ์ตามปกติและไม่ข้ามการตรวจสอบสิทธิ์ การแจ้งเตือนแสดงสถานะการเชื่อมต่อหรือการแชร์ หน้าจอหลักจะแสดงการดำเนินการตัดการเชื่อมต่อ

### สร้างจากแหล่งที่มา

เริ่มตัวอย่างการดีบักหรือรีลีสแต่ละรายการด้านล่างจากรูทของที่เก็บ ตรวจสอบรายการอุปกรณ์ก่อนติดตั้งและแทนที่ DEVICE_SERIAL ด้วยซีเรียลอุปกรณ์ที่ต้องการ บรรจุภัณฑ์ การทดสอบหน่วย และผ้าสำลีทั้งหมดเป็นการตรวจสอบแยกกัน หาก Lint ล้มเหลว ให้ตรวจสอบรายงานและแก้ไขหรือบันทึกสาเหตุ การข้ามงานไม่นับเป็นการผ่านงาน

บิลด์ต้องใช้ JDK 17+, Android SDK Platform 36, NDK `28.2.13676358`, Python 3 และ Rust ตั้งค่า `ANDROID_HOME` (หรือ `ANDROID_SDK_ROOT`); เส้นทาง NDK ที่กำหนดเองใช้ `ANDROID_NDK_HOME` การแสดงผลแบบเต็มหน้าจอของ `.Aran` ขึ้นอยู่กับ Metafile Android SDK: วางที่เก็บ `Metafile` ในไดเรกทอรีเดียวกันกับ `remote-control` (Gradle อ้างอิงโมดูล `:aran` ผ่าน `../../Metafile/android-sdk/aran`)

```sh
rustup target add aarch64-linux-android x86_64-linux-android
cd android-agent
./gradlew :app:assembleDebug :app:testDebugUnitTest
# Windows: .\gradlew.bat :app:assembleDebug :app:testDebugUnitTest
./gradlew :app:lintDebug
# Windows: .\gradlew.bat :app:lintDebug
adb devices -l
adb -s DEVICE_SERIAL install -r app/build/outputs/apk/debug/remote-agent-debug.apk
```

Gradle จะเรียก `scripts/build-android-agent.py` โดยอัตโนมัติเพื่อโหลดไฟล์ปฏิบัติการ ABI Rust สองไฟล์ลงในไดเรกทอรีไลบรารีดั้งเดิมของ APK หลังจากการติดตั้ง ให้เริ่มต้นโดยตรงจากไดเร็กทอรีแบบอ่านอย่างเดียวนี้ ซึ่งเป็นไปตามข้อจำกัดของ Android ในไดเร็กทอรีข้อมูลแอปพลิเคชันที่ห้ามไม่ให้เรียกใช้ไฟล์ APK การแก้ไขข้อบกพร่องใช้สำหรับการตรวจสอบการพัฒนา การเผยแพร่อย่างเป็นทางการต้องใช้การกำหนดค่าลายเซ็นขององค์กรเพื่อสร้างการเปิดตัว และคีย์ลายเซ็นจะถูกเก็บไว้เพื่อรองรับการอัพเกรดในภายหลัง

รุ่นรีลีสจะอ่าน `android-agent/keystore.properties` (ซึ่ง Git ละเว้น) หรือการกำหนดค่าภายนอกที่ชี้โดยตัวแปรสภาพแวดล้อม `REMOTE_AGENT_SIGNING_PROPERTIES` ช่องต่างๆ ดูที่ `android-agent/keystore.properties.example`; ควรเก็บกุญแจไว้นอกพื้นที่เก็บข้อมูล รุ่นวางจำหน่ายจะล้มเหลวเมื่อการกำหนดค่าลายเซ็นหายไป และจะไม่ถอยกลับไปเป็นการเซ็นชื่อการแก้ไขข้อบกพร่อง

```powershell
cd android-agent
$env:REMOTE_AGENT_SIGNING_PROPERTIES = "$env:USERPROFILE/.remote-control/signing/remote-agent.properties"
.\gradlew.bat :app:assembleRelease :app:lintRelease :app:testReleaseUnitTest --no-daemon
# Output: app/build/outputs/apk/release/remote-agent-release.apk
```

โปรดสำรองรหัสปลดล็อคและรหัสผ่านอย่างปลอดภัย เวอร์ชันต่อมาต้องใช้คีย์เดียวกันเพื่อให้ครอบคลุมการอัพเกรด ลายเซ็นการดีบักและรีลีสแตกต่างกัน การติดตั้งดีบักที่มีอยู่ไม่สามารถเขียนทับได้โดยตรง การถอนการติดตั้งจะล้างการกำหนดค่าภายในและข้อมูลรับรองอุปกรณ์ และควรจัดเตรียมการจับคู่ใหม่ก่อน `scripts/build-site-downloads.py` ใช้ APK รุ่นที่ลงนามแล้วเป็นแพ็คเกจดาวน์โหลด Agent การลงนามรุ่นไม่รับประกันว่า Play Protect จะอนุญาต APK

### Android สแกนโค้ดเพื่อเชื่อมต่อ

**สแกนการเชื่อมต่อ QR code** บนหน้าแรกของ Remote Agent ใช้กล้องโทรศัพท์เพื่อระบุการเชื่อมต่อ QR code แบบออฟไลน์โดยไม่มีบริการ Google Play อนุญาตการอนุญาตกล้องเมื่อใช้เป็นครั้งแรก การยกเลิกการสแกนโค้ด QR จะไม่แก้ไขการกำหนดค่า หลังจากสแกนโค้ด QR สำเร็จแล้ว ID เซิร์ฟเวอร์และอุปกรณ์จะแสดงขึ้น และโทเค็นจะยังคงซ่อนอยู่ คลิก **Connect** ก่อนทำการเข้ารหัส บันทึกการกำหนดค่า และเริ่มการเชื่อมต่อ หากการเชื่อมต่อที่มีอยู่กำลังทำงานอยู่ จะต้องหยุดการเชื่อมต่อก่อนจึงจะสามารถนำเข้าการกำหนดค่าใหม่ได้ การสแกนโค้ด QR จะไม่เปิดการควบคุมหน้าจอหรือคุณสมบัติการเข้าถึงโดยอัตโนมัติ

ขั้นแรกให้ลงทะเบียนอุปกรณ์อิสระใน Exchange และรับโทเค็นอุปกรณ์ **** จากนั้นสร้างโค้ด QR ในเครื่องคอมพิวเตอร์ของคุณ (อย่าใช้โทเค็นบัญชีการดำเนินการและการบำรุงรักษาหรือเว็บไซต์โค้ด QR ออนไลน์ของบุคคลที่สาม):

```powershell
python -m pip install qrcode==8.2
python scripts/connection-qr.py --server wss://exchange.example.com --device-id android-phone --output "$env:TEMP\android-phone-connection.svg"
# 按提示输入设备令牌，输入不会显示。也可用 --token-stdin 从标准输入读取。
```

เปิด SVG ที่สร้างขึ้นบนคอมพิวเตอร์ของคุณและปล่อยให้ Remote Agent สแกน ไฟล์โค้ด QR มีโทเค็นของอุปกรณ์ และควรเก็บไว้เป็นส่วนตัวและถูกลบหลังการใช้งาน และไม่ส่งไปยัง Git เครื่องมือปฏิเสธที่จะเขียนทับไฟล์ที่มีอยู่

เนื้อหาโค้ด QR คือ UTF-8 JSON ซึ่งมีความยาวรวมสูงสุด 2,048 ไบต์:

```json
{"type":"remote-agent","version":1,"server":"wss://exchange.example.com","device_id":"android-phone","token":"DEVICE_TOKEN"}
```

การสแกนโค้ด QR จะยอมรับเฉพาะที่อยู่ `wss://` ซึ่งสามารถรวมพาธ `/ws/device` ได้ และไม่ยอมรับข้อมูลผู้ใช้ URL พารามิเตอร์การสืบค้น หรือแฟรกเมนต์ `server`, `device_id` และ `token` ต้องเป็นสตริงที่ไม่ว่างเปล่า โดยมีอักขระได้สูงสุด 512, 128 และ 512 อักขระ ตามลำดับ และต้องไม่มีอักขระควบคุมหรือช่องว่างนำหน้าและต่อท้าย การกำหนดค่าด้วยตนเองยังคงพร้อมใช้งานสำหรับสภาพแวดล้อมการทดสอบ `ws://` ภายในเครื่อง

### การเข้าถึง Android ภาพหน้าจอและการป้อนข้อความ

ใช้ `remote-cli` สำหรับคำสั่งต่อไปนี้ อุปกรณ์รายงานความสามารถของ `android_control` ผ่านบริดจ์ APK; ตรวจสอบ `status` เพื่อรับสิทธิ์

1. เชื่อมต่ออุปกรณ์ จากนั้นเปิด **Settings → หน้าจอและการโต้ตอบ** และเปิดใช้งาน **Aอนุญาตการควบคุมหน้าจอระยะไกล** (ปิดโดยค่าเริ่มต้น)
2. เลือก **การตั้งค่าการเข้าถึง** และเปิดใช้งาน Remote Agent ในการตั้งค่า Android ซึ่งช่วยให้สามารถอ่านแผนผัง UI ที่สามารถเข้าถึงได้ การแตะ การปัด การนำทางของระบบ และการแทนที่ข้อความในช่องป้อนข้อมูลที่เน้น
3. Android 11+ สามารถจับภาพหน้าจอผ่านการสนับสนุนการเข้าถึงที่เปิดใช้งาน บน Android 8–10 หรือสำหรับการจับภาพหน้าจอเพียงอย่างเดียว ให้เลือก **Share screen** บนหน้าจอหลักหรือ **การแชร์หน้าจอมาตรฐาน** ในการตั้งค่า และยืนยันกล่องโต้ตอบการแชร์หน้าจอของ Android การอนุญาตจะต้องต่ออายุหลังจากเซสชันการแชร์สิ้นสุดลงหรือกระบวนการเริ่มต้นใหม่

```sh
remote-cli ui my-device status
remote-cli ui my-device tree
remote-cli ui my-device xml > screen.xml
remote-cli ui my-device click --view-id com.example:id/search
remote-cli ui my-device click --text "Search" --description "Open search"
remote-cli ui my-device screenshot --output screen.jpg
remote-cli ui my-device tap 300 500
remote-cli ui my-device swipe 500 1500 500 500 --duration-ms 400
# 多段滑动（折线，2-64 个点）
remote-cli ui my-device swipe-path 300,1500 300,800 900,800 --duration-ms 800
remote-cli ui my-device key back
remote-cli ui my-device text "Hello"
remote-cli ui my-device text --stdin < message.txt
remote-cli ui my-device type --stdin < message.txt
```

`text` ใช้ Android `ACTION_SET_TEXT` **แทนที่ข้อความทั้งหมดในกล่องอินพุตที่โฟกัสอยู่ในปัจจุบัน** รองรับภาษาจีน อีโมติคอน และหลายบรรทัด สูงสุด 8192 UTF-8 ไบต์ ไม่อนุญาตให้ใช้ NUL คลิกช่องป้อนข้อมูลที่แก้ไขได้ก่อน ส่วนควบคุมที่ไม่ได้โฟกัส ไม่สามารถแก้ไขได้ หรือไม่รองรับการดำเนินการนี้จะส่งคืนข้อผิดพลาด `--stdin` อ่าน UTF-8 และคงการขึ้นบรรทัดใหม่ พร้อมทั้งหลีกเลี่ยงการใส่ข้อความลงในประวัติบรรทัดคำสั่ง `key` รองรับ `back`, `home`, `recents`, `notifications`, `quick-settings`

`xml` ส่งคืนแผนผังอินเทอร์เฟซ `<hierarchy><node ...>` ที่ซ้อนกัน ซึ่งรวมถึง `resource-id`, `text`, `content-desc`, `bounds` และสถานะการควบคุม มากถึง 200 โหนด 50 เลเยอร์ และอักขระแอตทริบิวต์สูงสุด 256 ตัว ทั้งการตอบกลับและองค์ประกอบรูทจะระบุว่าควรตัดทอนหรือไม่ `click` เป็นการจับคู่แบบตรงทั้งหมดโดยใช้ค่าเต็มของคุณสมบัติเหล่านี้ (ไม่ใช่ XPath): `--view-id` สอดคล้องกับ `resource-id`, `--description` สอดคล้องกับ `content-desc`; หลายเงื่อนไขจะถูกรวมเข้าด้วยกันโดย AND จับคู่เฉพาะส่วนควบคุมที่มองเห็นและเปิดใช้งานเท่านั้น การจับคู่เป็นศูนย์หรือหลายรายการทำให้เกิดข้อผิดพลาด การคลิกจะไม่ถูกดำเนินการหากการข้ามผ่านเกินขีดจำกัด ป้ายกำกับข้อความจะมองหาการควบคุมหลักที่สามารถคลิกได้ หากไม่สามารถคลิกได้ ให้ใช้ `tap X Y` ควรได้รับ XML อีกครั้งหลังจากการเปลี่ยนแปลงอินเทอร์เฟซ และสามารถวางตำแหน่งแอตทริบิวต์แบบยาวได้โดยใช้รหัสทรัพยากรแทน

สำหรับการป้อนข้อมูลวิธีการป้อนข้อมูล ให้เปิด **Settings → Remote Input** เลือก **Eเปิดใช้งาน Remote Input** เพื่อเปิดใช้งาน Remote Input ใน Android จากนั้นเลือก **เลือกวิธีการป้อนข้อมูล** เพื่อเลือก หลังจากมุ่งเน้นไปที่กล่องอินพุตแอปพลิเคชันเป้าหมายแล้ว `type` จะแทรกข้อความที่เคอร์เซอร์/ส่วนที่เลือกผ่าน `InputConnection.commitText` รองรับภาษาจีน อีโมจิ และหลายบรรทัด และใช้ขีดจำกัดไบต์ 8192 UTF-8 เช่นเดียวกับ `text` จะไม่คลิกเพื่อส่งหรือส่งแบบฟอร์มโดยอัตโนมัติ และไม่สามารถแก้ไขการกำหนดค่าของ Agent เองได้ **สวิตช์คีย์บอร์ด** บนคีย์บอร์ดจะคืนค่าวิธีป้อนข้อมูลรายวันของคุณ `status` ส่งคืน `ime_enabled`, `ime_selected` และ `ime_connected` ต้องเปิดหน้าจอและต้องปลดล็อคอุปกรณ์ก่อนที่จะอ่านและคลิกที่อินเทอร์เฟซหรือป้อนข้อความ

หน้า **Device Screen** ของ Remote Console ให้การคลิกพิกัด การได้มาของ XML และการคลิกแอททริบิวต์ และการป้อนข้อความ Remote Input การอนุญาตที่ไม่พร้อมใช้งานหรือสถานะหน้าจอจะปิดใช้งานปุ่ม การป้อนข้อมูลสำเร็จจะล้างข้อความ และภาพหน้าจอ XML และเนื้อหาที่จะป้อนจะถูกล้างเมื่อเพจเข้าสู่พื้นหลัง

### ความสามารถของระบบ

การตั้งค่าระบบทำงานตามที่ผู้ให้บริการรายงานโดยอุปกรณ์ และเนมสเปซเป็นสตริง ไม่ใช่การแจงนับแบบฮาร์ดโค้ด Android ยังคงใช้ความสามารถ `android_settings` ซึ่งจัดทำโดย `system`, `secure`, `global` ความสามารถในการใช้งาน Linux (ไม่มี OpenWrt) และ macOS `system_settings`: gsettings สำหรับผู้ใช้ปัจจุบันบน Linux, `/usr/bin/defaults` สำหรับผู้ใช้ปัจจุบันบน macOS ส่วนขยายเบราว์เซอร์ใช้ความสามารถ `browser_settings` ในการอ่านและเขียน `privacy.network`, `privacy.services` และ `privacy.websites` ผ่านเซสชันที่เข้ารหัส Windows และ OpenWrt ไม่มีชุดการตั้งค่านี้ เมื่อเพิ่มการตั้งค่าคีย์ใหม่ เพียงลงทะเบียนผู้ให้บริการใน Agent หรือส่วนขยาย จากนั้นตัวดำเนินการจะแสดงผลตามรายการ

เนมสเปซทั้งสามของ Android ไม่เกี่ยวข้องกับการสลับหน้าจอ การแสดงผล และสถานะหน้าจอล็อค และดำเนินการผ่าน ContentResolver แทนที่จะเป็นการประกบเชลล์ หากต้องการเขียนการตั้งค่า `system` จาก APK ปกติ คุณต้องคลิก **Aอนุญาตให้แก้ไขการตั้งค่าระบบ ** บน Agent และอนุญาต ในการเขียน `secure/global` คุณต้องมีสิทธิ์ระบบ `WRITE_SECURE_SETTINGS` เพิ่มเติม สถานะส่งคืนสิทธิ์ปัจจุบันและ `providers` และนโยบาย Android และผู้ผลิตยังคงสามารถปฏิเสธการอ่านและเขียนแต่ละรายการได้

`settings list --prefix` กรองชื่อการตั้งค่าบน Android; บน Linux/macOS โดยไม่มีคำนำหน้าจะส่งคืนเฉพาะสคีมาหรือชื่อโดเมน โดยที่คำนำหน้าจะส่งคืนสตริงระดับบนสุด จำนวนเต็ม และค่าบูลีนของสคีมาหรือโดเมนนี้ `settings clear` ปัจจุบันยอมรับเฉพาะรายการความเป็นส่วนตัวของเบราว์เซอร์เท่านั้น ผู้ให้บริการรายอื่นจะส่งคืนข้อผิดพลาดที่ชัดเจน การตรวจสอบจะบันทึกเนมสเปซ ชื่อการตั้งค่า และจำนวนอักขระ แต่ไม่ใช่ค่า

```sh
remote-cli ui realmev5 settings
remote-cli ui realmev5 settings list --namespace system --limit 100
remote-cli ui realmev5 settings get screen_off_timeout
remote-cli ui realmev5 settings set screen_off_timeout 600000
remote-cli ui realmev5 screen-timeout 600
remote-cli ui realmev5 settings get device_name --namespace global
remote-cli ui realmev5 settings set vendor.custom_setting --stdin
```

ค่าของ `settings set` คือสตริง และ `--stdin` คงบรรทัดใหม่ไว้ ค่าที่ตั้งไว้ไม่ได้ถูกเขียนไปยังการตรวจสอบ ค่าสามารถมีได้สูงสุด 8192 UTF-8 ไบต์ ไม่อนุญาตให้ใช้ NUL ชื่อการตั้งค่าสามารถมีอักขระ ASCII ได้สูงสุด 256 ตัว (ตัวอักษร ตัวเลข ขีดล่าง จุด ทวิภาค ขีดกลาง) รายการมีสูงสุด 500 รายการและ 512 KiB หากเกินขีดจำกัด `truncated` จะถูกส่งคืน ค่าเดี่ยวสูงสุดคือ 64 KiB การตั้งค่าที่ไม่มีอยู่จะส่งคืน `value: null` อินเทอร์เฟซจะไม่บังคับแก้ไขการตั้งค่าที่ถูกจำกัดอย่างเงียบๆ จะมีการอ่านอีกครั้งเพื่อยืนยันหลังจากเขียนแต่ละครั้ง และข้อผิดพลาดที่ชัดเจนจะถูกส่งกลับหากล้มเหลว การแก้ไขจะมีผลอย่างถาวร และค่าเก่าจะไม่ได้รับการกู้คืนเมื่อตัดการเชื่อมต่อ Agent คำสั่งทางลัด `screen-timeout` ใช้วินาที (15–86400) และ `screen_off_timeout` ที่สำคัญใช้มิลลิวินาที การปิดหน้าจออัตโนมัติและการหน่วงเวลาก่อนล็อคหลังจากปิดหน้าจอเป็นการตั้งค่าที่แยกจากกัน

### การเล่นสื่อ (สื่อ)

การอ่าน เสียง รูปภาพ (รวมถึงข้อความแบบเต็มหน้าจอ) วิดีโอ และหน้าเว็บจะรวมเป็นหนึ่งเดียวด้วยความสามารถของ `media` และได้รับการแจ้งเตือนบนตัวแทน Android และ Windows/Linux/macOS Agent OpenWrt ไม่ได้รับการแจ้งเตือน โปรโตคอลมีเพียงสามการกระทำเท่านั้น: `media_play` (พร้อมอ็อบเจ็กต์ `media`), `media_stop` และ `media_status` ซึ่งทั้งสามการดำเนินการนี้ส่งคืนสถานะ `media` เดียวกัน

| `media.type` | สนาม | ช่อง |
| --- | --- | --- |
| `speech` | `text`, อุปกรณ์เสริม `language` (BCP-47 เช่น `zh-CN`) | เสียง |
| `audio` | `url` หรือ `path` เลือกหนึ่งรายการ ซึ่งเป็นตัวเลือก `loop` | เสียง |
| `image` | เลือกหนึ่งใน `url`, `path`, `text`, ตัวเลือก `p2p` (ไม่สามารถนำ `text` มาได้) | หน้าจอ |
| `video` | `url` หรือ `path` เลือกหนึ่งรายการ ซึ่งเป็นตัวเลือก `p2p` | หน้าจอ |
| `web` | `url` (https เท่านั้น) | หน้าจอ |

มีสองช่องสัญญาณอิสระบนอุปกรณ์: **Sound** (การอ่าน เสียง) และ **Screen** (รูปภาพ ข้อความ วิดีโอ หน้าเว็บ) เนื้อหาใหม่จะแทนที่เนื้อหาที่กำลังเล่นอยู่ในช่องเดียวกันเท่านั้น ดังนั้นจึงสามารถเล่นเพลงพื้นหลังได้พร้อมกับภาพหมุน `channel` ของ `media_stop` สามารถเป็น `sound`, `screen` หรือ `all` (ค่าเริ่มต้น)

```sh
remote-cli ui realmev5 media speak "Remote 连接正常" --language zh-CN
remote-cli ui realmev5 media speak --stdin --language zh-CN
remote-cli ui realmev5 media audio --path /sdcard/Music/doorbell.mp3
remote-cli ui realmev5 media audio --url https://cdn.example.com/notice.mp3 --loop
remote-cli ui realmev5 media image --text "值班中"
remote-cli ui realmev5 media image --url https://example.com/poster.jpg
remote-cli ui realmev5 media video --path /sdcard/loop.mp4
remote-cli ui realmev5 media video --url https://files.example.org/library/clip.Aran --p2p-group library --p2p-discovery https://p2p.ivjn.us --p2p-token secret --p2p-peer 192.0.2.10:7300
remote-cli ui realmev5 media web https://status.example.com/board
remote-cli ui realmev5 media status
remote-cli ui realmev5 media stop sound      # 或 screen；不写则两个通道都停
remote-cli ui laptop media audio --path 'C:\Users\me\Music\alert.wav'
remote-cli ui laptop media image --text "Kiosk"
```

`media status` ส่งคืน:

```json
{"kind":"media",
 "sound":{"type":"audio","id":"…","state":"playing","language":null,"loop":true},
 "screen":{"type":"web","keep_awake":true},
 "speech_available":true,"media_volume":7,"media_volume_max":15}
```

`sound` เป็นรายการล่าสุดในช่องเสียง (เก็บไว้หลังสิ้นสุดเพื่อการดูผลลัพธ์ได้ง่าย) และสถานะคือ `queued` / `speaking` / `loading` / `playing` / `completed` / `stopped` / `error`; `null` เมื่อไม่เคยเล่น `screen` จะปรากฏขึ้นเฉพาะเมื่อมีการแสดงหน้าจอเท่านั้น ไม่เช่นนั้นจะเป็น `null` `speech_available=false` หมายความว่าไม่มีโปรแกรมเสียงพูดบนอุปกรณ์ ปริมาณสื่อมีค่าบน Android เท่านั้น และเป็น 0 บนเดสก์ท็อป

การตรวจสอบและการตรวจสอบ: ข้อความ (การอ่านหรือข้อความรูปภาพ) สามารถมีขนาดได้ถึง 4,000 UTF-8 ไบต์ และไม่อนุญาตให้ใช้ NUL URL ต้องเป็น `https` สูงสุด 2,048 ไบต์ และต้องไม่มีช่องว่างหรืออักขระควบคุม เส้นทางสามารถมีได้ถึง 4096 ไบต์ และไม่สามารถมีอักขระควบคุมได้ การตรวจสอบจะบันทึกเฉพาะประเภท หมวดหมู่แหล่งที่มา (`url` / `path` / `text`) จำนวนอักขระ ไม่ว่าจะวนซ้ำและจะใช้ P2P หรือไม่ โดยจะไม่มี URL เส้นทาง หรือเนื้อหา (URL อาจมีโทเค็นการลงนาม) บัญชี `readonly` ไม่สามารถเรียกใช้การดำเนินการสื่อใดๆ ได้

#### เสียง: การอ่านและเสียง

- พูดออกเสียง: Android ใช้ระบบ TTS engine; Windows ใช้ `System.Speech`; macOS ใช้ `say`; Linux ใช้ `spd-say` / `espeak-ng` / `espeak` (เมื่อไม่ได้ติดตั้ง `speech_available=false` พูดออกเสียงจะส่งกลับข้อผิดพลาดที่ชัดเจน) เดสก์ท็อปจะพยายามอย่างเต็มที่ในการจับคู่แท็กภาษากับคำพูดในท้องถิ่น Android จะไม่กลับมาจนกว่าเครื่องยนต์จะเริ่มส่งเสียง ส่งคืนข้อผิดพลาดเมื่อเริ่มต้นเอ็นจิ้นระบบหรือข้อมูลภาษาไม่พร้อมใช้งาน การหยุด Agent จะปล่อย TTS
- เสียง: แหล่งที่มาคือเส้นทางที่แน่นอนบนอุปกรณ์หรือ https URL สำหรับไฟล์ที่ไม่ได้อยู่ในอุปกรณ์ ให้ใช้ `remote-cli files upload` เพื่ออัปโหลดก่อน จากนั้นจึงเล่นตามเส้นทาง `loop` วนซ้ำจนกระทั่งหยุด
  - Android ใช้ `MediaPlayer` กับช่องปริมาณสื่อและสตรีม URL โดยตรง มันจะไม่กลับมาจนกว่าเสียงจะเริ่ม หากไม่สามารถเริ่มต้นได้ภายใน 10 วินาที ข้อผิดพลาดจะถูกส่งกลับ
  - อันดับแรกฝั่งเดสก์ท็อปจะดาวน์โหลด URL ไปยังไดเร็กทอรีสถานะชั่วคราวของผู้ใช้ปัจจุบัน (สูงสุด 50 MB การเปลี่ยนเส้นทางอนุญาตเฉพาะ https) และจะลบออกหลังจากเล่น หยุด หรือเกิดข้อผิดพลาด อินเทอร์เฟซจะกลับมาหลังจากเริ่มกระบวนการเล่น และความล้มเหลวในการดาวน์โหลดหรือถอดรหัสจะสะท้อนโดย `media status` และ `sound.state=error` ใช้ `MediaPlayer` สำหรับ Windows (รองรับ mp3, wav, m4a, aac, wma, flac) ใช้ `afplay` สำหรับ macOS; ค้นหาตามลำดับสำหรับ Linux `mpv`, `ffplay`, `paplay`, `pw-play`, `aplay` ข้อผิดพลาดจะถูกส่งกลับหากไม่มีการติดตั้ง (`paplay` / `aplay` ไม่รองรับ mp3)
  - เส้นทางจะต้องเป็นไฟล์ธรรมดาที่มีอยู่และอ่านได้ Windows ปฏิเสธเส้นทางเครือข่าย เช่น `\\server\share` เพื่อหลีกเลี่ยงการส่งข้อมูลประจำตัวไปยังเซิร์ฟเวอร์ SMB ระยะไกล
- อาจไม่ได้ยินเสียงเมื่อระดับเสียงของอุปกรณ์เป็นศูนย์

#### หน้าจอ: รูปภาพ ข้อความ วิดีโอ และหน้าเว็บ

- **image**: Native `path`, `https` URL หรือ `text` (ข้อความ UTF-8 ขนาดใหญ่บนพื้นหลังสีเข้ม ถือเป็นภาพนิ่ง) แหล่งที่มาที่มีส่วนขยาย `.Aran` บน Android จะถูกถอดรหัสโดยใช้ Metafile Android SDK ก่อนที่จะแสดง
- **video**: URL ของ `path` หรือ `https` ในเครื่องที่เล่นแบบวนซ้ำ `.Aran` บน Android ไม่สามารถใช้เป็นสตรีมสื่อ HTTPS ปกติได้ เมื่อไม่ได้แคชไว้ มันจะถูกถอดรหัสเป็นบล็อกและเล่นขณะดาวน์โหลด และข้อความธรรมดาจะถูกเขียนลงในแคชในเครื่องเพื่อการเล่นโดยตรงในครั้งต่อไป ที่อยู่บล็อก HTTP คือ `/meta/<SHA1>` ในพาธรากต้นทาง ไฟล์เนทิฟจะอ่านไดเร็กทอรี `meta/` ถัดจากไฟล์ Manifest ค่าเริ่มต้นคือการใช้คีย์ที่ฝังรายการหรือคีย์เริ่มต้นของ SDK P2P เสริม: ลำดับแหล่งที่มาของบล็อกอยู่ในเครื่อง `meta/`, UDP P2P (`LOOKUP_ONLY` ไม่ได้ลงทะเบียนใน metastore), HTTP fallback บริการการค้นพบและเพียร์คงที่จัดทำโดย `media.p2p` เช่น `--p2p-group library --p2p-discovery https://p2p.ivjn.us` เมื่อการเริ่มต้น P2P ล้มเหลว HTTP/เนทีฟจะยังคงใช้อยู่และการเล่นจะไม่ถูกขัดจังหวะ
- **web**: เปิดเพจ `https` แบบเต็มหน้าจอ Android ใช้ WebView ในกิจกรรมแบบเต็มหน้าจอ (เปิดใช้งาน JavaScript และที่เก็บข้อมูล DOM และการนำทางภายในเพจจะเป็นไปตาม https เท่านั้น เมื่อกระบวนการเรนเดอร์ขัดข้อง จอแสดงผลจะถูกปิดโดยไม่ส่งผลกระทบต่อ Agent) เดสก์ท็อปเริ่มกระบวนการเบราว์เซอร์อิสระในโหมดคีออสก์ ใช้ไดเร็กทอรีการกำหนดค่าชั่วคราว และสิ้นสุดกระบวนการพร้อมกับเบราว์เซอร์เมื่อหยุดทำงาน: Windows ค้นหา Edge, Chrome และ Firefox ตามลำดับ macOS ค้นหา Chrome, Edge, Chromium และ Firefox (Safari ไม่มีโหมดคีออสก์) Linux ค้นหา Chromium, Chrome, Edge และ Firefox ส่งกลับข้อผิดพลาดที่ชัดเจนเมื่อไม่พบเบราว์เซอร์

Android ใช้ชื่อโดเมนอื่นเป็นแหล่งสำรองสำหรับรายการ `.Aran` และบล็อกข้อมูลสำหรับ `https://cdn.ivjn.cn` และ `https://cdn.ivjn.us` โดยอัตโนมัติ โดยคงเส้นทางรายการไว้ และบล็อกข้อมูลยังคงอ่าน `/meta/<SHA1>` จากแต่ละแหล่ง ชื่อโดเมนอื่นจะไม่ถูกแมปโดยอัตโนมัติ เมื่อเกิดข้อผิดพลาดชั่วคราว (รวมถึง HTTP 554) การหมดเวลาหรือการตรวจสอบบล็อกข้อมูลล้มเหลว ระบบจะลองใช้แหล่งข้อมูลสำรองก่อน จากนั้นจึงสำรองข้อมูล 250/500 ms สูงสุด 3 รอบ แหล่งที่มานี้จะถูกใช้ก่อนหลังจากสำเร็จ การหมดเวลาการเชื่อมต่อแต่ละครั้งคือ 5 วินาที และการหมดเวลาการอ่านคือ 30 วินาที ข้อผิดพลาดถาวร เช่น 403/404 จะไม่ร้องขอแหล่งข้อมูลเดียวกันซ้ำๆ ในการอ่านครั้งเดียวกัน ขนาดบล็อกและ SHA-1 ยังคงได้รับการยืนยัน ไม่มีการติดตามการเปลี่ยนเส้นทาง และการยืนยัน HTTPS จะไม่ผ่อนปรน ลักษณะการทำงานนี้ต้องใช้ Metafile `ab2b92f` หรือเวอร์ชันที่ใหม่กว่าของ Android SDK ที่มี `MirroredHttpSource`

ช่องรูปภาพจะแสดงเพียงรายการเดียวในแต่ละครั้ง รูปภาพใหม่แทนที่รายการก่อนหน้า เปิดหน้าจอไว้ระหว่างการนำเสนอ (Android: `FLAG_KEEP_SCREEN_ON` + `turnScreenOn` / `showWhenLocked`; Windows: `SetThreadExecutionState`; macOS: `caffeinate -d`; Linux: `systemd-inhibit` หรือ `xdg-screensaver inhibit`) รูปภาพและข้อความคงที่พิกเซลเลื่อนอย่างช้าๆ เพื่อป้องกันหน้าจอไหม้ วิดีโอวนซ้ำเท่านั้น หน้าเว็บไม่เลื่อนลอย ไม่มีเส้นขอบไฮไลต์แบบคงที่ Android ผ่านกิจกรรมเต็มหน้าจอแบบสแตนด์อโลน (ไม่จำเป็นต้องมีการเข้าถึง) หน้าจอ Linux ต้องมีเซสชัน X11 ที่ปลดล็อค และวิดีโอต้องติดตั้ง `mpv` หรือ `ffplay` บนเครื่อง วิดีโอ macOS ชอบ MPV ไม่เช่นนั้น `open`

#### การควบคุม: ความคืบหน้าและหน้าเว็บ

`media_control` ควบคุมเนื้อหาที่กำลังเล่นตามช่องและกลับสู่สถานะ `media` เดียวกัน `channel` ต้องเป็น `sound` หรือ `screen`; `op` รับหนึ่งในตาราง:

| `op.type` | พารามิเตอร์ | ใช้งานได้ |
| --- | --- | --- |
| `pause` / `resume` | — | เสียง (เสียง), วิดีโอ (หน้าจอ) |
| `seek` | `position_ms` (ตำแหน่งสัมบูรณ์ สูงสุด 7 วัน) | เสียงวิดีโอ |
| `navigate` | `url` (https) | หน้าเว็บ |
| `reload` / `back` / `forward` | — | หน้าเว็บ |
| `scroll` | `dx`, `dy` (พิกเซล ไม่เป็นศูนย์ แต่ละตัวไม่เกิน 100000) | หน้าเว็บ |
| `eval` | `script` (สูงสุด 16 KiB) | หน้าเว็บส่งคืน `script_result` (การเข้ารหัส JSON ของผลลัพธ์สูงสุด 64 KiB) |

```sh
remote-cli ui realmev5 media pause sound
remote-cli ui realmev5 media seek 1:30 screen      # 也接受 90、1:02:03.5
remote-cli ui realmev5 media resume screen
remote-cli ui realmev5 media web go https://status.example.com/next
remote-cli ui realmev5 media web back
remote-cli ui realmev5 media web scroll 0 800
remote-cli ui realmev5 media web eval "document.title"
```

เพิ่มในสถานะ: `position_ms`, `duration_ms`, `paused` ของ `sound` / `screen` (เสียงและวิดีโอเมื่อผู้เล่นสามารถรายงานได้), `url` ของ `screen` บนหน้าเว็บที่มี `title` ไม่สามารถควบคุมการอ่าน รูปภาพ และข้อความได้ ข้อผิดพลาดที่ชัดเจนจะถูกส่งกลับเมื่อเนื้อหาในช่องไม่ตรงกับการดำเนินการ การตรวจสอบจะบันทึกเฉพาะช่อง ประเภทการดำเนินการ และจำนวนอักขระของสคริปต์ ไม่ใช่ URL หรือเนื้อหาสคริปต์

- Android: เสียงและวิดีโอได้รับการควบคุมโดยตรงโดย `MediaPlayer` / `VideoView` และหน้าเว็บจะควบคุม WebView ภายในกิจกรรมแบบเต็มหน้าจอ
- Windows: เสียงจะดำเนินการโดยผู้ปฏิบัติงาน `MediaPlayer` ในตัว; วิดีโอถูกควบคุมโดย IPC ไปป์ภายใต้ mpv, สามารถควบคุมเครื่องเล่นสำรอง WPF ได้, ffplay ไม่รองรับ
- Linux / macOS: สามารถควบคุมเสียงและวิดีโอได้เฉพาะเมื่อเครื่องเล่นเป็น mpv (JSON IPC) มิฉะนั้นจะกลับมา "ติดตั้ง mpv เพื่อหยุดชั่วคราวหรือค้นหาสื่อเดสก์ท็อป"; เมื่อ macOS ติดตั้ง mpv เสียงจะเล่นโดยใช้ mpv แทน
- หน้าเว็บเดสก์ท็อป: คีออสก์ของ Chrome / Edge / Chromium เริ่มต้นด้วย `--remote-debugging-port=0` (ผูกไว้กับ 127.0.0.1 เท่านั้น พอร์ตสุ่ม ไดเร็กทอรีการกำหนดค่าชั่วคราว) ดำเนินการผ่านโปรโตคอล DevTools Firefox รองรับเฉพาะ `navigate` (รีสตาร์ทคีออสก์ด้วยที่อยู่ใหม่)

โทรศัพท์มือถือ Remote Console มีแท็บ "การตั้งค่าระบบ" และ "สื่อ" บนหน้า **System Capabilities** ของรายละเอียดอุปกรณ์ หน้าสื่อสามารถอ่านสถานะของสองช่อง หยุดเสียงหรือภาพตามลำดับ เล่นการอ่าน URL เสียง ข้อความ รูปภาพ วิดีโอ และหน้าเว็บ และควบคุมความคืบหน้าของเสียงและวิดีโอและหน้าเว็บ (กระโดด ไปข้างหน้าและย้อนกลับ รีเฟรช เลื่อน ดำเนินการ JS) เมื่อเพจเข้าสู่พื้นหลัง ผลลัพธ์การตั้งค่า ค่าการตั้งค่าที่จะส่ง ข้อความที่อ่านออกเสียง และการแสดงผลอินพุตจะถูกล้าง เว็บคอนโซลจะมีรูปแบบการตั้งค่าเดียวกัน และการตั้งค่าเบราว์เซอร์จะนำเซสชันที่เข้ารหัสไว้มาใช้ซ้ำ ไม่มีหน้าการเล่นสื่อแยกต่างหากบนหน้าเว็บ

### Android ค้นหาข้อความและ XML

```sh
remote-cli ui realmev5 xml
remote-cli ui realmev5 find-text "设置"
remote-cli ui realmev5 find-text "Settings" --exact --ignore-case
```

การค้นหาข้อความจะจับคู่ทั้งข้อความและคำอธิบายของโหนดที่มองเห็นได้ โดยจะใช้การจับคู่สตริงย่อยตามค่าเริ่มต้น โดยมีตัวเลือกการจับคู่แบบตรงทั้งหมดและการจับคู่แบบคำนึงถึงตัวพิมพ์เล็กและตัวพิมพ์ใหญ่ ส่งคืน `bounds` และ `center` ที่ตรงกัน (พิกเซลหน้าจอดั้งเดิม คลิกได้ด้วย `tap X Y`) รหัสทรัพยากร ข้อความ และสถานะที่เปิดใช้งาน จุดศูนย์กลางคำนวณจากจุดตัดกับหน้าจอเพื่อหลีกเลี่ยงการส่งคืนพิกัดการคลิกนอกหน้าจอ ผลลัพธ์คือสามารถสำรวจโหนดได้มากถึง 200 โหนดและรายงานการตัดทอน โหนดรหัสผ่านและแผนผังย่อยการกำหนดค่าของ Agent เองไม่มีส่วนร่วมในการค้นหา ข้อความที่วาดเองอาจไม่ถูกอ่านโดยคุณสมบัติการเข้าถึงของ Android และอินเทอร์เฟซนี้ไม่ทำ OCR คุณควรค้นหาอีกครั้งหลังจากอินเทอร์เฟซเปลี่ยนไป

ภาพหน้าจอเป็น JPEG ซึ่งมีความยาวสูงสุด 1,600 พิกเซลในด้านที่ยาวที่สุด และ CLI ปฏิเสธที่จะเขียนทับไฟล์ที่มีอยู่ CLI แสดงภาพพร้อมกันกับขนาดหน้าจอดั้งเดิม การคลิก การปัด และโหนดต้นไม้ `bounds` ใช้พิกเซลหน้าจอดั้งเดิม เมื่อวางตำแหน่งตามภาพขนาดย่อ ให้ใช้ `屏幕坐标 = 图像坐标 × screen_width / width` (ใช้มาตราส่วนความสูงในแนวตั้ง) อ่านขนาดอีกครั้งหลังการหมุน แผนผังอินเทอร์เฟซมีโหนดได้สูงสุด 200 โหนด และแต่ละสตริงมีได้สูงสุด 256 UTF-16 หน่วย `truncated` บ่งชี้ว่าจำนวนโหนดมีจำกัด อินเทอร์เฟซที่วาดเองของแอปพลิเคชันอาจไม่สามารถเข้าถึงโหนดได้

ข้อความและคำอธิบายของโหนดรหัสผ่านและทรีย่อยการกำหนดค่าของ Remote Agent เองจะไม่ถูกส่งกลับ การป้อนข้อความระยะไกลไม่สามารถแก้ไขการกำหนดค่าของตนเองได้ Windows ป้องกันโดย Android `FLAG_SECURE` จะถูกดับลงหรือส่งคืนข้อผิดพลาดของระบบในสกรีนช็อต การแชร์หน้าจอของ Android 15 อาจบล็อกแอปที่มีเนื้อหาละเอียดอ่อน เช่น ช่องรหัสผ่านโดยอัตโนมัติ การควบคุมหน้าจอนำการดำเนินการและการตรวจสอบการบำรุงรักษาที่มีอยู่กลับมาใช้ใหม่ และบัญชี `readonly` ไม่สามารถเริ่มการดำเนินการเหล่านี้ได้ การตรวจสอบจะบันทึกเฉพาะการดำเนินการ พิกัด และจำนวนอักขระที่ป้อนเท่านั้น และไม่ได้บันทึกข้อความที่ป้อน โครงสร้างอินเทอร์เฟซ หรือภาพหน้าจอ

**หยุดการแชร์หน้าจอ** สิ้นสุดเซสชันการแชร์ MediaProjection หากยังคงเปิดใช้งานการเข้าถึง Android 11+ ภาพหน้าจอการเข้าถึงจะยังคงมีอยู่ การปิด **Aอนุญาตการควบคุมหน้าจอระยะไกล** จะป้องกันการทำงานของหน้าจอที่ตามมาทั้งหมดและการสิ้นสุดการแบ่งปัน **หยุดการเข้าถึงระยะไกล** จะตัดการเชื่อมต่อ agent ด้วย การคลิกหรือการป้อนข้อมูลที่ดำเนินการไปแล้วไม่สามารถยกเลิกได้ สิทธิ์การเข้าถึงได้รับการจัดการแยกต่างหากโดยการตั้งค่า Android การอนุญาตระบบจะยังคงอยู่เมื่อปิดสวิตช์หรือหยุดบริการ แต่ไม่ยอมรับการทำงานของหน้าจอระยะไกล

บริดจ์ในเครื่องใช้ซ็อกเก็ต Unix ที่มีชื่อแบบสุ่มและตรวจสอบว่าผู้เรียกมี UID เดียวกันกับ APK สิ่งนี้จะป้องกันการเข้าถึงโดยแอปพลิเคชันทั่วไปอื่น ๆ เชลล์ระยะไกลที่ได้รับอนุญาตแชร์ UID แอปพลิเคชันกับ APK และสวิตช์หน้าจอไม่ควรถือเป็นการแยกสิทธิ์สำหรับเชลล์นั้นหรือผู้ดูแลระบบอุปกรณ์

## ข้อมูลระบบและตัวบ่งชี้

`DeviceInfo.platform` คือ `linux`, `freebsd`, `netbsd`, `windows`, `android`, `macos` หรือ `openwrt`, `os_version` เป็นเวอร์ชันระบบทั่วไป, `capabilities` อธิบาย `exec`, `pty`, `ubus`, `android_control`. OpenWrt รายงาน `openwrt_version`

Linux/OpenWrt รวบรวมการวัดจาก `/proc`; Android จะเก็บตัววัดอื่นๆ ที่อ่านได้ไว้เมื่อพบข้อจำกัดของ SELinux และเวลาทำงานสามารถรับได้ผ่าน `CLOCK_BOOTTIME` Windows รวบรวมหน่วยความจำ, CPU, เวลาที่ผ่านไป และจำนวนการ์ดเครือข่ายผ่าน API ระบบดั้งเดิม ตัวบ่งชี้ที่ไม่สามารถรวบรวมได้จะถูกทำเครื่องหมายไว้ในรายการ `unavailable` และ CLI และเทอร์มินัลมือถือจะแสดงรายการที่ขาดหายไป Windows ไม่มีค่าเฉลี่ยโหลด Unix ดังนั้น `load` จะถูกทำเครื่องหมายว่าไม่พร้อมใช้งานเสมอ

## ตรวจสอบ

```sh
cargo test --workspace
cargo clippy --workspace --all-targets -- -D warnings
cargo test -p agent --features desktop-ui
cargo clippy -p agent --features desktop-ui --all-targets -- -D warnings
cd mobile && npm run typecheck && npm run lint && npm test
```

`scripts/agent-smoke.py` เป็นรีเลย์ทดสอบภายในเครื่อง ที่ใช้ Python `websockets` เพื่อตรวจสอบการลงทะเบียน ตัวบ่งชี้ การดำเนินการ หมดเวลา การยกเลิก PTY ปรับขนาด และ Ubus จริงของ agent ที่ไม่รองรับ ไม่ได้แทนที่การตรวจสอบความถูกต้องและการทดสอบฐานข้อมูล Exchange ใช้ข้อมูลรับรองการทดสอบแบบตายตัวตามคำแนะนำของสคริปต์ และอย่าเชื่อมต่อกับอุปกรณ์หรือบริการอย่างเป็นทางการ ทั้ง Windows และ Linux ควรทำการทดสอบเนทีฟของตัวเอง Android ควรตรวจสอบส่วนหน้า พื้นหลัง หยุดและเชื่อมต่อใหม่ในสภาพแวดล้อมการทำงานจริงหลังจากติดตั้ง APK

`scripts/agent-ui-smoke.py --agent <binary> --platform linux|windows` จะตรวจสอบการกำหนดค่าแรกของ UI แยกกัน การตรวจสอบสิทธิ์และข้อจำกัดแหล่งที่มา พื้นที่จัดเก็บไฟล์ส่วนตัว การเก็บรักษาโทเค็น การลงทะเบียนและการดำเนินการจริง การล้างข้อมูลเมื่อหยุด การเชื่อมต่อใหม่หลังจากขาดการเชื่อมต่อ และการเปิดใหม่ ต้องใช้ Python 3.11+ และ `websockets`; ต้องสร้าง agent ที่จะทดสอบด้วย `desktop-ui` ก่อน การตรวจสอบเบราว์เซอร์ควรครอบคลุมถึงปุ่มบันทึก ข้อผิดพลาดในการป้อนข้อมูล การมองเห็นโทเค็น การกู้คืนการรีเฟรช และเค้าโครงหน้าจอที่แคบ

`scripts/android-control-smoke.py` ตรวจสอบการทำงานของหน้าจอโดยใช้แอปทดสอบ Exchange, CLI และ Android แบบสแตนด์อโลนจริง เรียกใช้เฉพาะในโปรแกรมจำลองแบบใช้แล้วทิ้งโดยเฉพาะ: สคริปต์จะล้างข้อมูลแอปพลิเคชัน Remote Agent และแก้ไขการตั้งค่าการเข้าถึง ขั้นแรกให้สร้างและติดตั้ง APK สองตัว `:app:assembleDebug` และ `:control-fixture:assembleDebug` เตรียม Exchange/ฐานข้อมูลอิสระ ลงทะเบียนอุปกรณ์ทดสอบ และบันทึกโทเค็นอุปกรณ์และการกำหนดค่า CLI ต้องใช้ Python 3.11+, Pillow และ `adb`

```sh
adb -s emulator-5580 install -r android-agent/app/build/outputs/apk/debug/remote-agent-debug.apk
adb -s emulator-5580 install -r -t android-agent/control-fixture/build/outputs/apk/debug/control-fixture-debug.apk
python scripts/android-control-smoke.py --serial emulator-5580 \
  --cli target/debug/remote-cli --cli-config /private/qa-cli.toml \
  --device-token /private/qa-device-token.txt \
  --device-id android-control-qa --device-server ws://10.0.2.2:18820/ws/device
```

Windows เปลี่ยนพาธ CLI เป็น `target/debug/remote-cli.exe` ผลิตภัณฑ์ทดสอบอยู่ที่ `target/android-control-qa/screenshots/` โดยค่าเริ่มต้น ฟิกซ์เจอร์นี้ใช้สำหรับการทดสอบเท่านั้นและไม่ได้เข้าสู่ APK ของอุปกรณ์


### บันทึก Android สถานะการแสดงผล และการปลุก

Remote Console เพิ่ม **Device logs** และ **Device screen** ไปยังรายละเอียดอุปกรณ์ หน้าหน้าจอแสดงสถานะจอแสดงผล สถานะพลังงาน สถานะการล็อค ขนาด และการอนุญาต และแสดงตัวอย่าง JPEG ตามความต้องการและหน้าจอ **Wake** อุปกรณ์ที่ไม่มีจอแสดงผลหรือไม่มีอินเทอร์เฟซควบคุม Android ได้ปิดใช้งานการทำงานของหน้าจอ สิทธิ์ในการจับภาพที่ขาดหายไปจะแสดงแยกกัน อุปกรณ์ปิดหน้าจอสามารถปลุกได้เมื่อเปิดใช้งานการควบคุมหน้าจอในตัวเครื่อง Wake ไม่เคยละทิ้งคีย์การ์ด

Android Agent มีสถานะ **Screen และบันทึกอุปกรณ์ ** ภายในเครื่อง ในขณะที่เปิดใช้งานการเข้าถึงระยะไกล ช่องการเชื่อมต่อ การลงทะเบียน QR และปุ่มรหัสการเชื่อมต่อจะถูกซ่อนไว้ การยกเลิกการเชื่อมต่อต้องมีการยืนยันและรักษาข้อมูลรับรองที่บันทึกไว้ การอนุญาตการเข้าถึงและบริการที่ถูกผูกไว้จะถูกรายงานแยกกัน หากได้รับอนุญาตแต่รอการเชื่อมโยง ให้สลับเฉพาะ Remote Agent ในการตั้งค่าการเข้าถึงระบบ

```powershell
remote-cli ui realmev5 status
remote-cli ui realmev5 logs --limit 100
remote-cli ui realmev5 wake
remote-cli ui realmev5 screenshot --output screen.jpg
```

อินเทอร์เฟซ `open_android` WebSocket ที่มีอยู่ยอมรับ `{action:"logs",limit:100}` (1-300 รายการ) และ `{action:"wake"}` สถานะเพิ่มตัวเลือก `display_present`, `screen_on`, `locked`, `screen_width`, `screen_height` และ `accessibility_authorized`; `accessibility_enabled` ยังคงหมายความว่าบริการเชื่อมต่ออยู่ API ดั้งเดิมของหน้าจอ Android ยังคงต้องใช้ APK และตัวแทนที่ไม่ใช่ Android จะไม่โฆษณาอินเทอร์เฟซนี้

ขณะนี้บันทึกมาจาก `logcat -b all` โดยไม่มีการกรอง PID แพ็คเกจหรือแท็ก: ระบบ แอปพลิเคชัน เหตุการณ์ และบัฟเฟอร์ข้อขัดข้องสำหรับบันทึก Android สแน็ปช็อตแต่ละรายการส่งคืนข้อมูลได้สูงสุด 100 รายการใน UI หรือ 1-300 ด้วย CLI โดยคงการประทับเวลา ระดับ ชื่อบัฟเฟอร์ PID/TID แท็ก และข้อความต้นฉบับ นี่คือประวัติ Logcat ปัจจุบันของอุปกรณ์ ไม่ใช่การเก็บถาวรของไฟล์บันทึกที่กำหนดเองหรือบันทึก Android ได้หมุนเวียนออกไปแล้ว

ให้สิทธิ์บันทึกระบบ APK แก่อุปกรณ์ผ่านการเชื่อมต่อ ADB ที่ได้รับอนุญาต:

```sh
adb shell pm grant com.remote.agent android.permission.READ_LOGS
```

ในเวอร์ชัน Android ที่ต้องการการเข้าถึงบันทึกต่อการใช้งาน ให้เปิดหน้าการวินิจฉัย Agent และยอมรับการยืนยันระบบ จากนั้นลองคำขอระยะไกลอีกครั้ง การเข้าถึงในเบื้องหลังอาจถูกปฏิเสธหรือการอนุมัติอาจหมดอายุตามนโยบาย Android/OEM Android สามารถส่งคืนเฉพาะ UID ของผู้โทรหลังจากการปฏิเสธ ดังนั้นตัวรวบรวมจึงตรวจสอบข้อมูลเมตา UID จากรายการ logcat 301 ล่าสุดก่อนที่จะเลือกส่วนท้ายที่ร้องขอ หากไม่มี UID อื่นๆ ปรากฏขึ้น (รวมถึงบันทึกว่างเปล่า) ระบบจะรายงานว่าไม่สามารถยืนยันการเข้าถึงอุปกรณ์แบบเต็มได้ แทนที่จะแสดงภาพรวมแอปเท่านั้น การอนุญาตที่ขาดหายไปหรือความล้มเหลวของ logcat ยังส่งคืนข้อผิดพลาดที่ชัดเจนเช่นกัน ตัวรวบรวมไม่ถอยกลับไปที่วงแหวนเหตุการณ์ Agent คอลเลกชันทำงานออกจากเธรด UI หยุดเมื่อยกเลิก และมีกำหนดเวลาหกวินาทีและเอาต์พุตที่มีขอบเขต สแน็ปช็อตขนาดใหญ่รายงานการตัดทอนหรือข้อผิดพลาด บันทึกใช้เซสชันผู้ปฏิบัติงานที่ได้รับการตรวจสอบความถูกต้องที่มีอยู่ การกำหนดเส้นทางผู้เช่า และข้อจำกัดของบทบาท ข้อความบันทึกอุปกรณ์อาจมีเนื้อหาที่เขียนโดยแอปอื่น มุมมองบันทึกของเว็บและมือถือชัดเจนเมื่อออกจากหน้าหรืออยู่ในพื้นหลัง และไม่บันทึกสแนปช็อตลงในดิสก์ Remote Console ยังล้างภาพหน้าจอเมื่อออก/พื้นหลัง


**การแชร์แบบเต็มหน้าจอแบบโปร่งใส:** ใน Android Agent เลือก **แชร์แบบเต็มหน้าจอด้วยการซ้อนทับแบบโปร่งใส** ให้สิทธิ์ในการแสดงผลเหนือแอปอื่นๆ ของ Android และความยินยอมในการแชร์หน้าจอ จากนั้น Agent จะย้ายไปที่พื้นหลังและติดตั้ง `TYPE_APPLICATION_OVERLAY` แบบเต็มหน้าจอโดยมีความทึบเป็นศูนย์ และ `FLAG_NOT_TOUCHABLE | FLAG_NOT_FOCUSABLE`: การสัมผัสทั้งหมดจะไปที่แอปพลิเคชันพื้นฐาน MediaProjection บันทึกการแสดงผลแบบเต็ม (Android 14+ ขอการแสดงผลเริ่มต้นอย่างชัดเจน) หยุดแชร์จากหน้า Agent หรือการแจ้งเตือน การหยุดการแบ่งปันจะไม่ตัดการเชื่อมต่อ Agent ภาพซ้อนทับจะถูกลบออกเมื่อการแชร์สิ้นสุดลงหรือบริการหยุดลง

การแสดงตัวอย่างหน้าจอและหน้าสถานะ Agent ที่เชื่อมต่อช่วยให้สามารถจับภาพได้ การตั้งค่าการเชื่อมต่อ ตั๋วเข้าสู่ระบบ และหน้าโทเค็น QR ยังคงได้รับการปกป้อง Android อาจยังคงส่งคืนรูปภาพสีดำสำหรับหน้าต่างที่ปลอดภัยของแอปอื่น การซ้อนทับแบบโปร่งใสไม่สามารถข้ามการป้องกันของแอปนั้นได้

ก่อนที่จะจับภาพ Android Agent จะตรวจสอบทั้งสถานะการแสดงผลและสถานะการโต้ตอบของ PowerManager โดยจะปลุกการทำงานเฉพาะเมื่อปิดจอแสดงผล ตรวจสอบว่ามีการโต้ตอบ และปฏิเสธการจับในขณะที่คีย์การ์ดยังคงล็อคอยู่ เครื่องอ่าน MediaProjection จะระบายเฟรมอย่างต่อเนื่องและเก็บภาพที่ประกอบขึ้นในปัจจุบันหนึ่งภาพ แทนที่จะสร้างพื้นผิวใหม่สำหรับภาพหน้าจอทุกภาพ กรอบสีดำทั้งหมดรอกรอบที่ใช้งานได้และทำให้เกิดข้อผิดพลาดที่ดำเนินการได้เมื่อหมดเวลา แทนที่จะเป็น JPEG สีดำที่ประสบความสำเร็จ

## การเปรียบเทียบโหมด Agent

โหมดการเปิดตัวและแพลตฟอร์มอุปกรณ์เป็นมิติที่แยกจากกัน Native headless, UI ภายในและบริการของระบบจะแชร์แกนการเชื่อมต่อ `desktop-ui` เพิ่มเฉพาะการกำหนดค่าภายในเครื่อง และไม่ให้สิทธิ์ระยะไกล

| โหมด/แพลตฟอร์ม | ความสามารถ | เงื่อนไขและขอบเขต |
|---|---|---|
| UI ที่ไม่มีส่วนติดต่อผู้ใช้ / ท้องถิ่น | โปรโตคอลดั้งเดิม คำสั่ง เทอร์มินัล ไฟล์ ตัววัด การแจ้งเตือน ตัวเลือก LAN เดียวกัน | UI บน Linux/Windows/macOS เท่านั้น ถาด Windows; การกระทำบนเดสก์ท็อปจำเป็นต้องมีเซสชันผู้ใช้แบบกราฟิกที่ใช้งานได้ |
| บริการระบบ / การเริ่มต้นเข้าสู่ระบบ | Agent ดั้งเดิมเดียวกัน | สืบทอดบัญชีบริการและสภาพแวดล้อม การแทนที่การเปิดตัวจะไม่ถูกคัดลอกไปยังบริการอื่น |
| OpenWrt | แกนเนทีฟ ubus | โฆษณา ubus เมื่อมีให้บริการ ควบคุมดูแลโดย procd; ไม่มีความสามารถในการควบคุมเดสก์ท็อป |
| Linux / Windows / macOS | แกนเนทีฟ, การควบคุมเดสก์ท็อป, บันทึกระบบ, สื่อ | ต้องใช้สิทธิ์/เครื่องมือระบบปฏิบัติการ การตั้งค่าระบบที่โฆษณาเฉพาะใน Linux/macOS |
| ฟรีบีเอสดี / เน็ตบีเอสดี | Native core, เทอร์มินัล, ไฟล์, เครือข่าย, เมตริก, LAN เสริม | แบตเตอรี่ไม่พร้อมใช้งาน จำเป็นต้องมีการขึ้นต่อกัน X11 ไม่มีการตั้งค่าสื่อ/ระบบ |
| Android APK | Rust core + Java Bridge, หน้าจอ, การตั้งค่า, สื่อ, OTA | สิทธิ์ UID ของแอปและระบบปฏิบัติการ ไบนารี Rust แบบสแตนด์อโลนไม่มีบริดจ์ควบคุม APK |
| ส่วนขยายโครเมียม | URL, คุกกี้, เพจ, การตั้งค่าเบราว์เซอร์, CDP, ตัวชี้วัด | ไม่มีคำสั่ง OS/PTY/ไฟล์โฮสต์โดยพลการ/ฟัง LAN; CDP เต็มรูปแบบต้องได้รับอนุญาตจากท้องถิ่น |

ความละเอียดการกำหนดค่ารันไทม์ UI และการแบ่งปัน headless: CLI/สภาพแวดล้อมแทนที่ JSON สำหรับ LAN, Watchdog, TLS และการแจ้งเตือน การอัปเดตอัตโนมัติจะคง CLI > JSON > `REMOTE_AUTO_UPDATE` > เปิดใช้งานตามค่าเริ่มต้น `REMOTE_WATCHDOG=1` และ 0, จริง/เท็จ, ใช่/ไม่ใช่, เปิด/ปิด เป็นที่ยอมรับ UI ยังคงปฏิเสธการแทนที่การเปิดใช้งานสำหรับข้อมูลรับรองการเชื่อมต่อและเส้นทางสถานะ การแทนที่รันไทม์จะไม่ถูกบันทึกลงใน JSON และกระบวนการเริ่มต้นอัตโนมัติจำเป็นต้องมีการกำหนดค่าแยกต่างหาก สถานะ LAN แสดงค่าที่มีประสิทธิภาพ การบันทึกเพจไม่ได้ลบการแทนที่การเปิดตัว

UI และ headless ใช้ JSON `state_file` มิฉะนั้นจะเป็นค่าเริ่มต้นของระบบถาวรเดียวกัน อินสแตนซ์อุปกรณ์ที่แยกจากกันต้องมีเส้นทางที่แยกจากกัน การเชื่อมต่อจะรีเฟรชสถานะและการหยุดการแสดงหยุดลง การหยุดยกเลิกการอัปเดตอัตโนมัติในอนาคตของการเชื่อมต่อนั้นและเผยแพร่สถานะจ้องจับผิด พนักงานออกจากการตรวจสอบครั้งถัดไป และการดำเนินการที่เสร็จสมบูรณ์แล้วไม่สามารถยกเลิกได้ การเชื่อมต่อใหม่แต่ละครั้งยังรีโหลดข้อมูลระบุตัวตนของอุปกรณ์และนโยบายความน่าเชื่อถืออีกด้วย การโหลดข้อมูลประจำตัวที่ล้มเหลวจะล้างข้อมูลประจำตัวก่อนหน้า แทนที่จะคงการอนุญาตอุปกรณ์เก่าไว้

การทำงานของอุปกรณ์ทั้งหมดต้องมีเซสชันที่ปิดผนึกและการอนุญาตรูทพื้นที่ทำงาน ความยินยอมต่อการโทร/การอนุญาตอัตโนมัติของ CDP ของเบราว์เซอร์เต็มรูปแบบไม่ได้ถูกแยกออกจากขอบเขตคุกกี้ที่พิมพ์ และไม่สามารถข้ามนโยบายของ Chrome ได้ การปิดส่วนขยายหรือตัดการเชื่อมต่อจำเป็นต้องมีการจับมือใหม่ แมปไฟล์อัปเดตเดสก์ท็อปกับ Windows x64, Linux glibc x64/arm64 และ macOS arm64; แพลตฟอร์มดั้งเดิมอื่นๆ ใช้ตัวติดตั้ง/ผู้จัดการแพ็คเกจ Android ใช้ APK OTA และเบราว์เซอร์ใช้แพ็คเกจส่วนขยาย การโฆษณาความสามารถไม่รับประกันฮาร์ดแวร์ การอนุญาต หรือไฟล์เก็บถาวรที่เผยแพร่

## สกรีนเซฟเวอร์อัตโนมัติ

การตั้งค่า Agent ในเครื่องและส่วนขยายเบราว์เซอร์รองรับการไม่ได้ใช้งาน (300 วินาทีโดยค่าเริ่มต้น) การควบคุม-เริ่ม/สิ้นสุด และการเข้าทันที เลือกข้อความ รูปภาพ/วิดีโอ HTTPS หรือ HTML แบบคงที่แบบแยกส่วน วิดีโอวนซ้ำปิดเสียง; ข้อความและรูปภาพสามารถเดินทางได้เป็นบริเวณกว้าง เดสก์ท็อปและ Android มีรหัสผ่านออก หน้าต่างเต็มหน้าจอของเบราว์เซอร์ไม่มีรหัสผ่านและออกจากปุ่ม การเคลื่อนตัวชี้ หรือการปิดหน้าต่าง ครอบคลุมเฉพาะจอแสดงผลที่ใช้งานอยู่เท่านั้น นี่ไม่ใช่การล็อคระบบปฏิบัติการ Linux ต้องการ X11 และ xprintidle ขณะนี้ Headless OpenWrt, Wayland และ BSD ไม่รองรับการแสดงผลบนหน้าจอ

โหมดสีทึบจะแสดงเฉพาะพื้นหลังที่เลือก โดยไม่มีข้อความหรือสื่อ สีพื้นหลังใช้ #RRGGBB; ความสว่างอยู่ระหว่าง 0 ถึง 100% (สีดำที่ 0%) Android ยังหรี่ไฟแบ็คไลท์ของหน้าต่างสกรีนเซฟเวอร์และคืนความสว่างของระบบเมื่ออินพุต แพลตฟอร์มอื่น ๆ จะทำให้ภาพมัวลงเท่านั้น การประหยัดพลังงาน LCD ขึ้นอยู่กับการควบคุมแบ็คไลท์ ข้อความแจ้งการออกยังคงอ่านได้

ตั้งแต่ 0.1.34 การเคลื่อนไหวจะย้ายเฉพาะข้อความและรูปภาพบนพื้นที่กว้างบนพื้นหลัง เนื้อหาขนาดใหญ่จะลดขนาดลงเมื่อจำเป็นจนเหลือไม่เกิน 44% ของความกว้างและความสูงที่มีอยู่ ดังนั้นตำแหน่งมุมที่อยู่ตรงข้ามจึงไม่ทับซ้อนกัน HTML วิดีโอและสีทึบยังคงอยู่ การตั้งค่าการเคลื่อนไหวที่ลดลงหยุดการเดินทางชั่วคราว ทุกโหมดจะซ่อนตัวชี้จากเฟรมแรก คืนค่าสำหรับกล่องโต้ตอบรหัสผ่านออก และซ่อนอีกครั้งเมื่อยกเลิก Windows รีเฟรชตัวชี้ภายในหน้าต่างสกรีนเซฟเวอร์ทุกๆ 100 มิลลิวินาที โดยไม่ต้องเปลี่ยนเคอร์เซอร์ของระบบ การเปลี่ยนแปลงเหล่านี้จัดส่งก่อนในแพ็คเกจ Windows และ Browser Agent 0.1.34 ตรวจสอบรายการดาวน์โหลดสำหรับแพลตฟอร์มอื่น
