DSH iOS ซิมูเลเตอร์
iOS ซิมูเลเตอร์แบบสดและโต้ตอบได้ภายในบทสนทนาของ DeepSeek Harness — พร้อม iPhone จริงของคุณผ่าน USB
22 เครื่องมือเอเจนต์ • แผงข้าง MJPEG สด • ซิมูเลเตอร์ & iPhone จริงผ่าน USB • การทำงานกับแถวรายการ/ฟีด • โหลดซ้ำทันทีของพรีวิว SwiftUI
npm: @zseven-w/dsh-ios · รีลีสปลั๊กอินปัจจุบัน: 0.1.0-rc.3 · ทดสอบกับ DSH 0.1.1-rc.1
English · 简体中文 · 繁體中文 · 日本語 · 한국어 · Français · Español · Deutsch · Português · Русский · हिन्दी · Türkçe · ไทย · Tiếng Việt · Bahasa Indonesia
npm: @zseven-w/dsh-ios · เวอร์ชันปลั๊กอินปัจจุบัน: 0.1.0-rc.3 · ทดสอบกับ DSH 0.1.1-rc.1
iPhone เครื่องจริงที่ควบคุมจากในบทสนทนา DSH — การเรียกเครื่องมืออยู่ทางซ้าย แผงอุปกรณ์สดอยู่ทางขวา
## ทำไมต้อง DSH iOS ซิมูเลเตอร์
DSH iOS ซิมูเลเตอร์มอบ iOS ซิมูเลเตอร์จริงให้เอเจนต์ภายในบทสนทนา — และมอบพิกเซลให้คุณ เอเจนต์สามารถบูตอุปกรณ์ บิลด์และรันโปรเจกต์ Xcode หรือแพ็กเกจ Swift ขับเคลื่อน UI ด้วยเอกลักษณ์การช่วยการเข้าถึงหรือข้อความ OCR อ่านล็อกรวม และตรวจสอบโพรเซส แบ็กเทรซ และหน่วยความจำรั่วได้ ในขณะที่สตรีมสดของอุปกรณ์แสดงผลอยู่ในแผงข้างแบบถาวร ซึ่งคุณแตะ ลาก หมุน และกดปุ่มโฮมบนวิดีโอได้โดยตรง คำสั่งเดียวกันนี้ใช้ได้กับ iPhone จริงที่เชื่อมต่อผ่าน USB เช่นกัน: ปลั๊กอินจะบิลด์และเปิด WebDriverAgent บนโทรศัพท์ ทันเนลพอร์ตควบคุมและพอร์ตจอผ่านลูปแบ็ก และสตรีมอุปกรณ์ลงในแผง การ์ด และเครื่องมือชุดเดียวกัน ไม่มีบล็อกภาพ ไม่มีไฟล์บันทึกหน้าจอ: ไบต์ภาพเข้าถึง UI ผ่าน URL ที่ลงนามและหมดอายุซึ่งให้บริการโดยเว็บเซิร์ฟเวอร์ DSH เท่านั้น
| | |
| --- | --- |
| 🖥️ **ซิมูเลเตอร์สดในบทสนทนา** | สตรีม MJPEG ของ serve-sim จากอุปกรณ์ที่บูตแล้ว พร็อกซีผ่านเส้นทาง `/_dsh/dsh-ios/*` ที่ลงนามเข้าสู่แผงขวาแบบถาวร — เบราว์เซอร์ไม่เคยแตะพอร์ตของ serve-sim |
| 📱 **iPhone จริงผ่าน USB** | `ios_real_start_wda` บิลด์และเปิด WebDriverAgent บนโทรศัพท์ที่เชื่อมต่อ และทันเนลพอร์ตควบคุม (REST) กับพอร์ตจอ (MJPEG) ผ่านลูปแบ็ก จากนั้นแผง เครื่องมือ การ์ด และแคปซูลสถานะชุดเดียวกันก็ขับเคลื่อนโทรศัพท์ อุปกรณ์ต้องปลดล็อกแล้ว และทุกการแตะบนบัญชีจริงถูกควบคุมด้วยกฎ “ระบุก่อน แตะทีหลัง” ของปลั๊กอิน |
| 🛠️ **22 เครื่องมือเอเจนต์** | อุปกรณ์ บูต/ปิดเครื่อง สกรีนช็อต โต้ตอบ บิลด์และรัน ล็อกรวม UI ทรีแบบ AXe และแตะตามองค์ประกอบ การทำงานกับแถวรายการ/ฟีด ค้นหา/แตะข้อความด้วย Vision OCR โหลดซ้ำทันทีของพรีวิว SwiftUI โพรเซส แบ็กเทรซ หน่วยความจำรั่ว ข้อมูลแอป |
| 👆 **แผงโต้ตอบ** | แตะและลากบนวิดีโอสด แถบไอคอนโฮม / หมุน / สกรีนช็อต / รีเฟรชพร้อมทูลทิปเมื่อชี้ โหมดขนาด (适应 · 50–125% · S/M/L) สไตล์เฟรม (无框 / 边框 / 真机框) ปรับขนาดด้วยการลากสูงสุด 960 px พร้อมดับเบิลคลิกเพื่อรีเซ็ต ขยายอัตโนมัติเมื่อแนวนอน |
| 🧾 **แถวรายการและฟีด** | `ios_sim_ui_rows` เปลี่ยนสแนปช็อตการช่วยการเข้าถึงเชิงลึกให้เป็นแถวที่มีดัชนี พร้อมป้ายกำกับและตัวนับที่แยกวิเคราะห์แบบทั่วไป `ios_sim_tap_row` แตะภายในแถวที่พิกัดสัมพัทธ์และยืนยันการกระทำด้วยการเปลี่ยนแปลง ±1 ตามที่คาดของตัวนับ — การยืนยันที่เชื่อถือได้เพียงอย่างเดียวที่แอปรายการมอบให้ |
| 🔐 **การขนส่งผ่านลูปแบ็กเท่านั้น** | serve-sim ผูกกับ 127.0.0.1 ในช่วงพอร์ตเฉพาะ ทุกเส้นทางต้องมีเพียร์ลูปแบ็ก `Host` แบบลูปแบ็ก และการตรวจสอบ Fetch-Metadata/Origin ความสามารถ HMAC หมดอายุภายใน 10 นาที ทันเนลควบคุม/MJPEG ของ WebDriverAgent บนอุปกรณ์จริงคือการส่งต่อ usbmux แบบลูปแบ็กภายใต้รั้วเดียวกัน |
| ⚡ **โหลดซ้ำทันทีของพรีวิว SwiftUI** | `ios_sim_preview` สร้างแอปโฮสต์ใช้แล้วทิ้งนอกแพ็กเกจของคุณ บิลด์พรีวิวของคุณเป็น dylib และสลับร้อนการแก้ไขเข้าเครื่องจำลองที่กำลังทำงานโดยไม่ต้องเปิดใหม่ (~2–5 วินาที) |
| 🧭 **ระบบอัตโนมัติ UI เชิงความหมาย** | `ios_sim_ui_tree` ดัมป์ทรีการช่วยการเข้าถึง (แบบ AXe) และ `ios_sim_tap_element` แตะตามป้ายกำกับหรือตัวระบุ เมื่อทรีว่างหรือเสื่อม `ios_sim_find_text` จะ OCR หน้าจอ และ `ios_sim_tap_text` แตะข้อความที่ตรงกัน — แตะตามเอกลักษณ์และข้อความแทนการเดาพิกัด |
## เครื่องมือ
เครื่องมือทั้ง 22 ตัวลงทะเบียนบนทุกโฮสต์และคืนค่า JSON ธรรมดาเท่านั้น — ไบต์ภาพเข้าถึง UI ผ่าน `presentationMeta` + เส้นทางที่ลงนามเท่านั้น ไม่มีวันเป็นบล็อกภาพ udid ของซิมูเลเตอร์ไปผ่าน simctl/serve-sim ส่วน udid ของอุปกรณ์จริงไปผ่าน WebDriverAgent โดยอัตโนมัติ บนโฮสต์ที่ไม่ใช่ macOS (หรือเมื่อ serve-sim แก้ไขไม่ได้) เครื่องมือยังลงทะเบียนอยู่แต่ล้มเหลวพร้อมข้อผิดพลาดที่อธิบายได้ ข้อยกเว้นเดียวคือ `status` ของ `ios_sim_preview` ซึ่งรายงานตามจริงว่า `{ running: false }` บนทุกโฮสต์
### เครื่องมือหลักของซิมูเลเตอร์
| เครื่องมือ | หน้าที่ | พารามิเตอร์หลัก |
| --- | --- | --- |
| `ios_sim_devices` | แสดงรายการอุปกรณ์ซิมูเลเตอร์ iOS ที่มีบน Mac เครื่องนี้ (udid ชื่อ รันไทม์ สถานะ) และตัวไหนบูตอยู่ พร้อม iPhone จริงที่เชื่อมต่อ USB ใน `realDevices` (udid ชื่อ osVersion model state developerMode) ใช้ค้นหา udid หรือชื่อที่จะส่งต่อให้เครื่องมืออื่นก่อน | — |
| `ios_sim_boot` | บูตอุปกรณ์และเริ่มสตรีมสด serve-sim ของมัน สตรีมยังคงอยู่ตลอดบทสนทนาเพื่อให้แผงแสดงซิมูเลเตอร์แบบสด | `udid` (จำเป็น — udid หรือชื่ออุปกรณ์) |
| `ios_sim_shutdown` | ปิดอุปกรณ์ และหยุดสตรีมหากสตรีมเล็งไปที่อุปกรณ์นั้น | `udid` (จำเป็น) |
| `ios_sim_screenshot` | จับภาพ PNG และคืนสรุป JSON สั้น ๆ (เส้นทาง ไบต์ ขนาด อุปกรณ์) ภาพแสดงผลในการ์ด/แผง ไม่มีวันเป็นบล็อกภาพ ใช้ได้กับซิมูเลเตอร์ที่กำลังสตรีมและโทรศัพท์ที่เชื่อมต่อ USB ผ่าน WebDriverAgent | `udid` (ไม่บังคับ — อุปกรณ์ที่กำลังสตรีม ถ้าไม่มีก็ซิมูเลเตอร์ที่บูตอยู่ตัวแรก) |
| `ios_sim_interact` | โต้ตอบกับอุปกรณ์ที่กำลังสตรีม — ซิมูเลเตอร์หรือโทรศัพท์ USB: แตะที่พิกัดนอร์มัลไลซ์ 0..1 พิมพ์ข้อความ (คีย์บอร์ดสหรัฐฯ บนซิมูเลเตอร์) กดปุ่มฮาร์ดแวร์ (`home`, `lock`, `volumeUp`…) เลื่อน หรือส่งท่าทางสัมผัส หลังการกระทำนิ่งแล้ว (~300 ms) สกรีนช็อตใหม่จะแสดงผลลัพธ์ | `action` (จำเป็น — `tap`/`type`/`button`/`gesture`/`scroll`), `x`/`y`, `text`, `name`, `json` |
| `ios_sim_list_apps` | แสดงรายการแอปที่ติดตั้งแล้วบนซิมูเลเตอร์ที่บูตอยู่หรือโทรศัพท์ที่เชื่อมต่อ (bundle id ชื่อที่แสดง เวอร์ชัน แฟล็กระบบ) — bundle id ของแอปบุคคลที่สามเดาไม่ได้ จึงต้องแสดงรายการก่อนหรือส่ง `name` ให้ `ios_sim_launch_app` การแสดงรายการที่ล้มเหลวจะโยนข้อผิดพลาด (เช่น “อุปกรณ์เข้าถึงไม่ได้ผ่าน CoreDevice”) แทนที่จะคืนรายการว่าง ดังนั้น `count: 0` จึงหมายความเสมอว่าอุปกรณ์ไม่มีแอปที่ตรงกันจริง ๆ | `udid` (ไม่บังคับ), `query` (สตริงย่อยไม่แยกตัวพิมพ์ใหญ่-เล็กบนชื่อที่แสดงและ bundle id รวม CJK), `include_system` (ค่าเริ่มต้น false) |
| `ios_sim_launch_app` | เปิดแอปที่ติดตั้งแล้วบนซิมูเลเตอร์ที่บูตอยู่หรือโทรศัพท์ที่เชื่อมต่อ — ด้วย `bundleId` หรือ `name` (สตริงย่อยของชื่อที่แสดงแบบไม่แยกตัวพิมพ์ใหญ่-เล็ก แก้ไขผ่านการแสดงรายการชุดเดียวกัน รวม CJK) ต้องเป็นหนึ่งในสองอย่างพอดี ทั้งการเปิดล้มเหลวและชื่อกำกวมจะคืนคำแนะนำว่าต้องทำอะไรต่อไป (`ios_sim_build_run` ใช้บิลด์จากซอร์ส) | `bundleId` หรือ `name` (อย่างใดอย่างหนึ่ง), `udid`, `relaunch` |
| `ios_sim_build_run` | บิลด์ `.xcodeproj`, `.xcworkspace` หรือแพ็กเกจ Swift สำหรับซิมูเลเตอร์ ติดตั้ง `.app` ที่ได้แล้วเปิดใช้งาน ส่ง udid ของอุปกรณ์จริงเพื่อบิลด์ ติดตั้ง และเปิดบนโทรศัพท์แทน (ต้องใช้การลงนาม Apple Development) เมื่อล้มเหลว ผลลัพธ์มีหางข้อผิดพลาด `xcodebuild` ที่กรองแล้ว การบิลด์เต็มใช้เวลาหลายนาที | `projectPath` (จำเป็น), `scheme`, `udid` (กำลังสตรีม → บูตอยู่ → iPhone รันไทม์ใหม่สุด ซึ่งจะถูกบูต), `configuration` (ค่าเริ่มต้น `Debug`) |
| `ios_real_start_wda` | เริ่ม WebDriverAgent (WDA) บน iPhone จริงที่เชื่อมต่อ USB — เฉพาะอุปกรณ์จริง ไม่มีวันใช้ซิมูเลเตอร์ รับช่วง WDA ที่ทำงานอยู่แล้วหากตอบสนอง ไม่เช่นนั้นรันบิลด์/เปิดของ `xcodebuild` (บิลด์เย็นใช้เวลาหลายนาที) แล้วรอจน WDA รายงานว่าพร้อมและคืนพอร์ตควบคุม/MJPEG ที่แผงสดใช้ รันตัวนี้ก่อนเมื่อ `ios_sim_screenshot` / `ios_sim_interact` / `ios_sim_ui_tree` / `ios_sim_tap_element` รายงานว่า WDA ไม่ทำงานสำหรับอุปกรณ์นั้น | `udid` (จำเป็น — udid อุปกรณ์จริงจาก `ios_sim_devices.realDevices`) |
### เครื่องมือ UI ทรี (แบบ AXe)
| เครื่องมือ | หน้าที่ | พารามิเตอร์หลัก |
| --- | --- | --- |
| `ios_sim_ui_tree` | ดัมป์ทรีองค์ประกอบการช่วยการเข้าถึงของแอปหน้าสุด (ป้ายกำกับ ตัวระบุ ค่า เฟรมหน่วยพอยต์ของอุปกรณ์) พร้อมขนาดจอเป็นพอยต์ — AXe บนซิมูเลเตอร์ WebDriverAgent บนโทรศัพท์ USB (ที่นั่นจำกัดความลึกตามค่าเริ่มต้น: สแนปช็อตไม่จำกัดของแอปที่ยุ่งวัดได้ ~32 วินาที / 751 KB จำกัดแล้ว ~2 วินาที) เอาต์พุตจำกัด ~40 KB (ระดับลึกสุดถูกตัด ตั้ง `truncated` + คำใบ้) | `udid` (ไม่บังคับ), `max_depth`, `filter` (สตริงย่อยไม่แยกตัวพิมพ์ใหญ่-เล็กบนป้ายกำกับ/ตัวระบุ/ประเภท) |
| `ios_sim_tap_element` | แตะองค์ประกอบตามเอกลักษณ์ — จับคู่ตรงทั้งหมดก่อน แล้วจึงสตริงย่อยไม่แยกตัวพิมพ์ใหญ่-เล็กบน `identifier`/`label` ตัวซ้ำซ้อนกันถูกยุบเป็นเป้าหมายเดียว การจับคู่กำกวมจะแสดงรายการผู้สมัครทั้งหมด การแตะลงกลางองค์ประกอบ (AXe HID บนซิมูเลเตอร์ WebDriverAgent บนโทรศัพท์) แล้วสกรีนช็อต ~300 ms แสดงผล ส่ง `expect_text` / `expect_gone` แล้วการแตะพร้อมการตรวจสอบจะกลายเป็นการไป-กลับครั้งเดียว (`expected.matched`) | `udid` (ไม่บังคับ), `identifier`, `label`, `expect_text`, `expect_gone` |
### แถวรายการ & ฟีด
แอปรายการ/ฟีดรวมแต่ละรายการไว้ในเซลล์การช่วยการเข้าถึงเซลล์เดียว ซึ่งป้ายกำกับบรรจุสรุปทั้งหมดและตัวนับทั้งหมด (“57 回复。18 喜欢。592 次查看”) — ไม่มีปุ่มลูกแยกตามคอนโทรลให้จับคู่ และเซลล์แถวปรากฏเฉพาะในสแนปช็อตเชิงลึก เครื่องมือสองตัวนี้เปิดโครงสร้างนั้นเป็นแถวและทำงานภายในแถว
| เครื่องมือ | หน้าที่ | พารามิเตอร์หลัก |
| --- | --- | --- |
| `ios_sim_ui_rows` | อ่านแถวรายการ/ฟีดที่มองเห็นของแอปหน้าสุดเป็นแถวแทนทรีดิบ แต่ละแถวรายงานดัชนี เฟรมหน่วยพอยต์ ป้ายกำกับที่รวมแล้ว และตัวนับที่แยกวิเคราะห์จากป้ายกำกับนั้น (ตัวเลข + โทเคนจำแนก เช่น `57 回复` → 回复=57 ภาษาจีนหรืออังกฤษ — ไม่ฮาร์ดโค้ดคำศัพท์แอปใด ๆ) แถวปรากฏเฉพาะในสแนปช็อตเชิงลึก บนโทรศัพท์ `max_depth` เริ่มต้นคือ 60 ใช้ ~15–25 วินาที / ~0.5 MB ต่อครั้ง (WDA ประมวลผลคำขอแบบอนุกรม) — ลองใช้ตัวสังเกตราคาถูกก่อน (`ios_sim_find_text` / `ios_sim_ui_tree`) ตัวนับแยกวิเคราะห์แบบฮิวริสติกและคีย์ไป-กลับได้: ส่งคีย์ให้ `ios_sim_tap_row.expect_count` ตรงตามที่แสดงในรายการ เมื่อไม่พบแถว ผลลัพธ์บอกเหตุผล (ความลึกน้อยเกินไป / ไม่ใช่จอรายการ / ไม่มีข้อมูลการช่วยการเข้าถึงจริง ๆ หลังอ่านเชิงลึก) — การอ่านตื้นไม่มีวันถูกรายงานเป็น “แอปไม่มีข้อมูลการช่วยการเข้าถึง” แถวนอกจอถูกคัดออกและนับเป็น `omittedOffscreen` | `udid` (ไม่บังคับ), `max_depth` (เฉพาะโทรศัพท์ ค่าเริ่มต้น 60) |
| `ios_sim_tap_row` | แตะที่ตำแหน่งสัมพัทธ์ภายในแถวรายการที่มองเห็นหนึ่งแถว (แถวที่ `ios_sim_ui_rows` รายงาน: ดัชนีฐาน 0 x/y เป็นเศษส่วนของเฟรมแถวนั้น — 0 = ขอบซ้าย/บน 1 = ขวา/ล่าง ค่าเริ่มต้น 0.5 = กึ่งกลาง) บนซิมูเลเตอร์ (AXe) หรือโทรศัพท์ USB (WebDriverAgent) เฟรมแถวมาจากการอ่านทรีครั้งใหม่ จึงไม่มีการเดาพิกัดจอสัมบูรณ์ ดัชนีเกินช่วงจะล้มเหลว (ไม่มีวันถูกตัดทอน) ประตูนิรภัย: ด้วย `expect_count={key,delta}` เครื่องมือตรวจสอบการกระทำโดยอ่านป้ายกำกับแถวซ้ำและยืนยันว่าตัวนับเปลี่ยน +1/−1 พอดี (`countCheck.verified`) ถ้าคีย์ไม่อยู่ในตัวนับที่แยกวิเคราะห์ของแถวนั้น การแตะจะถูกปฏิเสธก่อนเกิดขึ้น — การแตะบนอุปกรณ์จริงไม่มีวันเป็นการหยั่งเชิง การแตะยังเกิดขึ้นได้โดยไม่มี `expect_count` (ตำแหน่งสัมพัทธ์ในแถวที่ชัดเจนคือการระบุอยู่แล้ว) แต่ไม่มีอะไรถูกตรวจสอบ | `udid` (ไม่บังคับ), `index` (จำเป็น), `x`, `y` (เศษส่วน 0..1), `max_depth`, `expect_count` (`{key, delta}`) |
### เครื่องมือ OCR (Vision)
| เครื่องมือ | หน้าที่ | พารามิเตอร์หลัก |
| --- | --- | --- |
| `ios_sim_find_text` | OCR หน้าจอปัจจุบันของซิมูเลเตอร์ที่บูตอยู่หรือโทรศัพท์ USB ด้วยตัวช่วย Vision ที่ปลั๊กอินคอมไพล์ไว้ (การจดจำแม่นยำ zh-Hans + en-US คอมไพล์ด้วย `swiftc` เมื่อใช้ครั้งแรกลง `~/Library/Caches/dsh-ios/bin/ocr`) ใช้เมื่อทรีการช่วยการเข้าถึงว่างหรือเสื่อม สำหรับข้อความที่วาดเป็นกราฟิก (ตัวเลขแบดจ์ ราคาที่ฝังในภาพ) หรือตรวจสอบสิ่งที่อยู่บนจออย่างอิสระ จับสกรีนช็อตใหม่แล้วคืน `{device, size, items:[{text, confidence, rect}]}` — rect คือกล่องหน่วยพอยต์ของอุปกรณ์ (จุดกำเนิดซ้ายบน) เรียงตามความมั่นใจ จำกัดเอาต์พุต ~40 KB (`truncated` ทิ้งหางความมั่นใจต่ำสุด ใช้ `query` ให้แคบลงหรือเพิ่ม `min_confidence`) | `udid` (ไม่บังคับ), `query` (สตริงย่อยไม่แยกตัวพิมพ์ใหญ่-เล็ก), `min_confidence` (ค่าเริ่มต้น 0.3) |
| `ios_sim_tap_text` | OCR หน้าจอปัจจุบันและแตะกึ่งกลางของข้อความที่ตรงที่สุด — กฎ “ตรงทั้งหมด → มีอยู่โดยไม่แยกตัวพิมพ์ใหญ่-เล็ก → รายชื่อผู้สมัครเมื่อกำกวม” เช่นเดียวกับ `ios_sim_tap_element` สำหรับข้อความที่ทรีการช่วยการเข้าถึงมองไม่เห็น (แอปไม่มี a11y ตัวเลขแบดจ์ ข้อความที่ฝังในภาพ) บนโทรศัพท์การแตะลงที่พอยต์สัมบูรณ์ของอุปกรณ์ผ่าน WebDriverAgent บนซิมูเลเตอร์ที่กำลังสตรีมส่งแบบนอร์มัลไลซ์ผ่านการควบคุมของ serve-sim (รัน `ios_sim_boot` ก่อน) หลัง ~300 ms สกรีนช็อตใหม่แสดงผล ส่ง `expect_text` / `expect_gone` แล้วการแตะพร้อมการตรวจสอบจะกลายเป็นการไป-กลับครั้งเดียว (`expected.matched`) บนอุปกรณ์จริงทุกการแตะมีผลลัพธ์จริง — อย่าแตะคอนโทรลที่ไม่รู้จักเพื่อลองดูว่ามันทำอะไร | `udid` (ไม่บังคับ), `query` (จำเป็น), `min_confidence`, `expect_text`, `expect_gone` |
| `ios_sim_wait_for` | รอจนกว่าข้อความจะปรากฏหรือหายไปจากหน้าจอ โดยวนตรวจผ่านไปป์ไลน์จับภาพ+OCR เดียวกับ `ios_sim_find_text` จนเงื่อนไขเป็นจริงหรือหมดเวลา (ค่าเริ่มต้น 8 วินาที สูงสุด 60 วินาที) การหมดเวลาคือคำตอบปกติ `matched:false` ไม่ใช่ข้อผิดพลาด — เรียกครั้งเดียวแทนการวนลูป find_text เอง (บน iPhone จริงรอบละ ~1.2 วินาที) เมื่อพบ `item` จะมีข้อความ OCR ความเชื่อมั่น และกรอบพิกัดเป็นพอยต์ของอุปกรณ์ | `udid` (ไม่บังคับ), `text` (จำเป็น), `mode` (`appear`/`disappear`), `timeout_ms`, `min_confidence` |
### เครื่องมือล็อก
| เครื่องมือ | หน้าที่ | พารามิเตอร์หลัก |
| --- | --- | --- |
| `ios_sim_logs` | อ่านสิ่งที่แอปซิมูเลเตอร์พิมพ์ออกจากล็อกรวมของอุปกรณ์: `snapshot` (`log show --last ` ค่าเริ่มต้น 2m) หรือ `follow` (จับสดแบบมีขอบเขต `duration_seconds` ค่าเริ่มต้น 10 สูงสุด 60 — ไม่มีวันเป็นสตรีมค้าง) เอาต์พุตจำกัด ~300 บรรทัด / 30 KB พร้อมคำใบ้ให้แคบลง | `udid` (ไม่บังคับ), `mode` (`snapshot`/`follow`), `duration`, `duration_seconds`, `bundle_id`, `predicate` (NSPredicate ดิบ แทนที่ `bundle_id`), `level` (`default`/`info`/`debug`), `grep` |
### เครื่องมือพรีวิว
| เครื่องมือ | หน้าที่ | พารามิเตอร์หลัก |
| --- | --- | --- |
| `ios_sim_preview` | โหลดซ้ำทันทีของพรีวิว SwiftUI แบบสดในซิมูเลเตอร์: `start` (ค่าเริ่มต้น) ตรวจสอบแพ็กเกจ สร้างแอปโฮสต์ใช้แล้วทิ้งในแคชของปลั๊กอิน (ไม่มีวันอยู่ในแพ็กเกจของคุณ) บิลด์แพ็กเกจเป็น dylib สำหรับซิมูเลเตอร์ ติดตั้งและเปิดโฮสต์ แล้วเฝ้าดูซอร์ส — ทุกการแก้ไขบิลด์ใหม่และสลับร้อนโดยไม่ต้องเปิดใหม่ (~2–5 วินาที) ข้อผิดพลาดคอมไพเลอร์คงพรีวิวที่ดีล่าสุดไว้และแสดงผ่าน `status` ครั้งละหนึ่งเซสชัน | `packagePath` (จำเป็นสำหรับ `start`), `udid`, `action` (`start`/`status`/`stop`), `previewFilter` (สตริงย่อยไม่แยกตัวพิมพ์ใหญ่-เล็กบนชื่อพรีวิว) |
### เครื่องมือดีบัก
| เครื่องมือ | หน้าที่ | พารามิเตอร์หลัก |
| --- | --- | --- |
| `ios_sim_processes` | แสดงรายการโพรเซสแอปที่กำลังทำงานของซิมูเลเตอร์หนึ่งจาก launchd ของมันเอง (pid ที่โฮสต์เห็น ชื่อ bundle id) — แหล่ง pid สำหรับแบ็กเทรซ/หน่วยความจำรั่ว udid อุปกรณ์จริงจะแสดงรายการโพรเซสของโทรศัพท์ผ่าน devicectl แทน | `udid` (ไม่บังคับ), `filter` (สตริงย่อยไม่แยกตัวพิมพ์ใหญ่-เล็กบนชื่อ/bundle id) |
| `ios_sim_backtrace` | LLDB แบบแบตช์ครั้งเดียว (แนบ → แบ็กเทรซเธรด → ถอด ไม่มีวันโต้ตอบ) เอาต์พุตจำกัด ~200 บรรทัด เธรดหลักก่อน เป้าหมายถูกตรวจสอบเสมอว่าทำงานต่อแล้ว เมื่อ macOS ปฏิเสธการแนบ (โหมดนักพัฒนาปิด) จะลดระดับไปใช้เอนจิน `sample` ของ Xcode (ไม่ระงับ) และรายงานคำใบ้ให้เปิดใช้ เฉพาะซิมูเลเตอร์ — อุปกรณ์จริงถูกปฏิเสธพร้อมเหตุผล | `udid` (ไม่บังคับ), `pid` / `bundle_id`, `all_threads` (ค่าเริ่มต้น true) |
| `ios_sim_leaks` | วิเคราะห์หน่วยความจำรั่วด้วยเครื่องมือ `leaks` ของ Xcode: `summary` (จำนวนที่รั่ว ไบต์ที่รั่วรวม ประเภทสูงสุด ~30 ชนิด) หรือ `memgraph` (อาร์ติแฟกต์ `.memgraph` สำหรับเปิดใน Xcode Instruments ไม่มีวันถูกแยกวิเคราะห์ที่นี่) แอปถูกระงับระหว่างสแกนและกลับมาทำงานเสมอ เฉพาะซิมูเลเตอร์ | `udid` (ไม่บังคับ), `pid` / `bundle_id`, `mode` (`summary`/`memgraph`) |
| `ios_sim_app_info` | ข้อมูลแอปที่ติดตั้ง: เส้นทางแอปบันเดิล คอนเทนเนอร์ข้อมูลที่เขียนได้ และค่า Info.plist — ผ่าน `simctl appinfo` (พร้อม `get_app_container` สำรอง) บนซิมูเลเตอร์ ผ่าน `devicectl` บนโทรศัพท์ USB `installed: false` พร้อม `note` ที่ชี้ `ios_sim_list_apps` สำหรับแอปที่หายไป | `udid` (ไม่บังคับ), `bundle_id` (จำเป็น) |
## พื้นผิวการแสดงผล
- **แผงข้าง — “iOS 模拟器”.** มุมมองสดอยู่ในแผงขวาแบบถาวร (ด็อกคงที่ที่ดันบทสนทนาออกข้าง หรือโอเวอร์เลย์กึ่งกลางบนวิวพอร์ตแคบ) แผงแสดงสตรีม MJPEG สดและรับการคลิกเพื่อแตะและการลากเพื่อท่าทางบนวิดีโอโดยตรง พร้อมแถบไอคอน (โฮม สกรีนช็อต หมุน รีเฟรช) ที่ปุ่มมีทูลทิปเมื่อชี้ การควบคุมขนาดมี **适应** (พอดีความกว้างแผง) ซูม **50–125%** ของความกว้างตรรกะของอุปกรณ์ และค่าที่ตั้งไว้ล่วงหน้า **S / M / L** ที่กำหนดด้านสั้นของอุปกรณ์ (ความกว้างแนวตั้ง แนวนอนจะสเกลให้อุปกรณ์คงขนาดกายภาพ) สไตล์เฟรมคือ **无框 / 边框 / 真机框** (ไร้เฟรม / ขอบจอ / ตัวเครื่องอุปกรณ์เสมือนจริง) พร้อมรัศมีมุมตามสัดส่วน เมื่ออุปกรณ์หมุนเป็นแนวนอน แผงจะขยายอัตโนมัติเป็นขนาดสบายตาและคืนความกว้างของคุณเมื่อหมุนกลับ — การลากด้วยมือระหว่างนั้นชนะเสมอ ที่จับขอบซ้ายลากแผงให้กว้าง/แคบ (สูงสุด 960 px ดับเบิลคลิกรีเซ็ตเป็นความกว้างเริ่มต้น) เมื่อ iPhone ที่เชื่อมต่อ USB เป็นเป้าหมายสตรีม แผงเดียวกันจะแสดงสตรีม MJPEG ของ WebDriverAgent จากโทรศัพท์ด้วยการควบคุมชุดเดียวกัน
- **การ์ดสนทนาแบบกะทัดรัด.** ผลลัพธ์เครื่องมือแสดงเป็นการ์ดบรรทัดเดียวโดยไม่มีภาพในบรรทัด: ชื่อรวม **“iOS 模拟器”** ป้ายย่อยการกระทำ (บูต / สกรีนช็อต / โต้ตอบ / บิลด์และรัน / เริ่ม WebDriverAgent) ชื่ออุปกรณ์ แบดจ์สถานะ และคำใบ้ “เปิดในแผงข้าง” การคลิกแถวจะเปิดแผง การคลิกปุ่ม ลิงก์ หรือเฟรมสดเองไม่มีวันเปิด
- **แคปซูลสถานะเหนือช่องพิมพ์.** ขณะแผงปิดและมีสตรีมออนไลน์ เม็ดเล็กจุดเขียว (`` · 实时) ปรากฏเหนือช่องพิมพ์ข้อความและเปิดแผงเมื่อคลิก มันผูกกับเซสชัน: แสดงและโพลเฉพาะเมื่อบทสนทนาปัจจุบันมีผลลัพธ์ซิมูเลเตอร์ติดตั้งอยู่ และหยุดเมื่อสลับไปเซสชันที่ไม่มี
- **โหมดมาตรฐานและโหมด Code.** เซสชันมาตรฐานใช้ `presentationMeta` ที่โฮสต์ฉายให้ ดิสแพตช์แบบซ้อนของโหมด Code (PTC) ไม่มีวันพก meta ดังนั้นไคลเอนต์จึงสร้าง meta เดียวกันขึ้นใหม่จาก JSON ผลลัพธ์ถาวร — แผง การ์ด และแคปซูลทำงานได้ทั้งสองโหมด
## ความปลอดภัย
- เบราว์เซอร์ไม่มีวันคุยกับพอร์ตของ serve-sim ทุกไบต์ผ่านออริจินของเว็บเซิร์ฟเวอร์ DSH ผ่านเส้นทาง `/_dsh/dsh-ios/*` ที่ปลั๊กอินเป็นเจ้าของ: `/stream/` (พร็อกซี MJPEG), `/screenshot/` (PNG แคช), `/ws?token=…` (รีเลย์ควบคุม HID) พร้อมเอนด์พอยต์ `/grant`, `/capture` และ `/status`
- โทเคนคือความสามารถ HMAC-SHA256 (`base64url(payload).base64url(mac)`) ที่หมดอายุภายใน 10 นาที ลงนามด้วยคีย์ต่อโฮมของ DSH (`/cache/dsh-ios/stream-access.key`, 0600, สร้างแบบอะตอมมิก)
- ทุกเส้นทางใช้รั้วขนส่งลูปแบ็ก/เชื่อถือได้ก่อนตรวจสอบความสามารถใด ๆ: ที่อยู่เพียร์ลูปแบ็ก `Host` แบบลูปแบ็ก (ปฏิเสธ DNS-rebinding) และการตรวจสอบ Fetch-Metadata/Origin เส้นทางสกรีนช็อตให้บริการเฉพาะไฟล์ในไดเรกทอรีแคชของปลั๊กอิน (ปฏิเสธลิงก์สัญลักษณ์ ตรวจสอบ `realpath`)
- serve-sim รันเป็นโพรเซสลูกเบื้องหน้าบนลูปแบ็กเท่านั้น ในช่วงพอร์ตเฉพาะ (3181–3244) ดังนั้น serve-sim ของผู้ใช้เองบนพอร์ต 3100 ไม่มีวันถูกแตะ `--host` ไม่มีวันถูกใช้
- **การขนส่งอุปกรณ์จริง** — ทันเนลควบคุมของ WebDriverAgent (REST พอร์ตอุปกรณ์ 8100) และจอ (MJPEG พอร์ต 9100) คือการส่งต่อ usbmux แบบลูปแบ็กบนลิงก์ USB อยู่หลังรั้วเส้นทางที่ลงนามเดียวกัน และเบราว์เซอร์ยังคุยกับออริจินของเว็บเซิร์ฟเวอร์ DSH เท่านั้น
- **การรับเลี้ยง/เรียกคืนโพรเซสกำพร้า** — หากโฮสต์ DSH ก่อนหน้าถูกฆ่าอย่างไม่สุภาพและตัวช่วย serve-sim ของมันรอดมา อุปกรณ์เดียวกันจะถูกรับเลี้ยง (แฮนด์เชกของโพรเซสกำพร้าเชื่อถือได้) ตัวช่วยเก่าที่ครองช่องของอุปกรณ์อื่นถูกเรียกคืนผ่าน `serve-sim -k` แล้วเปิดใหม่หนึ่งครั้ง
- **Keep-alive + หยุดเมื่อว่าง** — สตรีมที่ล่มเริ่มใหม่ในเบื้องหลัง (~5 วินาที) เมื่อไม่มีผู้บริโภค สตรีมหยุดอัตโนมัติหลัง 5 นาที การหยุดโดยตั้งใจไม่มีวันถูกขัดขวาง (รันเนอร์อุปกรณ์จริงถูกยกเว้นการเก็บเกี่ยวเมื่อว่างโดยตั้งใจ: การเริ่มใหม่มีค่าเท่ากับการบิลด์ `xcodebuild` ใหม่หลายนาที)
## ข้อกำหนด
- **macOS พร้อม Xcode ตัวเต็ม** — แค่ Command Line Tools ไม่พอ `xcodebuild`, `xcrun simctl` และรันไทม์ซิมูเลเตอร์ทั้งหมดมากับ Xcode
- **รันไทม์ซิมูเลเตอร์ iOS อย่างน้อยหนึ่งตัว** ติดตั้งใน Xcode
- **DSH ≥ 0.1.0-rc.6 พร้อมเว็บบันเดิล** สำหรับแผง โปรไฟล์ headless ก็ใช้ได้: เครื่องมือทั้ง 22 ตัวทำงานปกติ แค่ไม่มีมุมมองสด
- **โฮสต์ที่ไม่ใช่ macOS**: ปลั๊กอินโหลดและเครื่องมือทั้ง 22 ตัวลงทะเบียน แต่ทุกการเรียกคืนข้อผิดพลาดที่อธิบายได้ (`iOS Simulator requires macOS with Xcode …`)
- **serve-sim** มาพร้อมเป็น dependency npm ของปลั๊กอินนี้ จึงแก้ไขในเครื่องได้เมื่อติดตั้งจริง ทางเลือกสำรอง `npx -y serve-sim` ครอบคลุมทรีพัฒนา (ใช้ครั้งแรกต้องมีเน็ต)
- **AXe** (ไม่บังคับ — เฉพาะเครื่องมือแบบ AXe เท่านั้นที่ต้องใช้: `ios_sim_ui_tree` / `ios_sim_tap_element` รวม `ios_sim_ui_rows` / `ios_sim_tap_row` บนซิมูเลเตอร์): `brew install cameroncooke/axe/axe` หรือให้ปลั๊กอินดาวน์โหลดรีลีสที่ตรึงไว้ (v1.8.0 ตรวจสอบ SHA-256) ลง `~/Library/Caches/dsh-ios/bin` อัตโนมัติ `DSH_IOS_AXE_BIN` แทนที่การแก้ไข `DSH_IOS_AXE_OFFLINE=1` ปิดการดาวน์โหลด
- **Vision OCR** (ไม่บังคับ — เฉพาะ `ios_sim_find_text` / `ios_sim_tap_text` เท่านั้นที่ต้องใช้): ปลั๊กอินคอมไพล์ `assets/ocr.swift` ที่แถมมาด้วย `swiftc` เมื่อใช้ครั้งแรกลง `~/Library/Caches/dsh-ios/bin/ocr` (จดจำ zh-Hans + en-US)
- **การแนบ lldb ต้องใช้โหมดนักพัฒนาของ macOS**: รัน `sudo DevToolsSecurity -enable` หนึ่งครั้ง ก่อนหน้านั้น `ios_sim_backtrace` ใช้เอนจิน `sample` ของ Xcode (ไม่ระงับ) และ `ios_sim_leaks` ลดระดับพร้อมคำใบ้ให้เปิดใช้
- **iPhone จริง** — iPhone ที่เชื่อมต่อ USB และปลดล็อกหน้าจอแล้ว (WebDriverAgent เริ่มบนจอล็อกไม่ได้ พิจารณา ล็อกอัตโนมัติ: ไม่ต้อง) สาย USB ที่รับส่งข้อมูลได้ (การจับคู่ผ่าน Wi-Fi เท่านั้นส่งต่อพอร์ตไม่ได้) เปิดโหมดนักพัฒนาบนอุปกรณ์ มีเช็กเอาต์ WebDriverAgent ที่ `~/Library/Caches/dsh-ios/wda/src` (ปลั๊กอินบิลด์สกีม `WebDriverAgentRunner` จากตรงนั้น — มันไม่มีวันดาวน์โหลดหรือโคลนอะไร) และ usbmux จาก libimobiledevice (`brew install libimobiledevice`) สำหรับทันเนล USB การบิลด์ WDA ครั้งแรกติดตั้ง WebDriverAgentRunner ที่ลงนามแล้ว: เชื่อถือใบรับรองบนอุปกรณ์เมื่อถูกถาม และรัน `ios_real_start_wda` ซ้ำเมื่อโปรไฟล์การลงนามทีมฟรีหมดอายุ (อายุ 7 วัน)
## ติดตั้งลงใน DSH
```sh
dsh plugin --profile web add @zseven-w/dsh-ios@latest
dsh web
```
## เริ่มต้นอย่างรวดเร็ว
บทสนทนาแรกโดยทั่วไป:
1. **ค้นหาอุปกรณ์** — “แสดงรายการซิมูเลเตอร์ที่มี” → `ios_sim_devices`
2. **บูต** — “บูต iPhone 17 Pro” → `ios_sim_boot` สตรีมเริ่มและ**แผง “iOS 模拟器”**เปิดขึ้น: อุปกรณ์แสดงสดในแผงข้าง (คลิกแถวการ์ดซิมูเลเตอร์ใดก็ได้ หรือเม็ดสถานะเหนือช่องพิมพ์ เพื่อเปิดใหม่)
3. **แตะบนวิดีโอ** — แตะหรือลากบนแผงโดยตรง หรือให้เอเจนต์ขับเคลื่อน UI: “เปิดการตั้งค่า แล้วแตะ General” → `ios_sim_interact` (หรือ `ios_sim_ui_tree` + `ios_sim_tap_element` สำหรับการแตะตามเอกลักษณ์ `ios_sim_find_text` + `ios_sim_tap_text` สำหรับการแตะตามข้อความ `ios_sim_ui_rows` + `ios_sim_tap_row` สำหรับแอปรายการ/ฟีด)
4. **บิลด์และรันแอปของคุณ** — “บิลด์และรัน /path/to/MyApp.xcodeproj” → `ios_sim_build_run` การบิลด์เต็มใช้เวลาหลายนาที เมื่อเสร็จแล้วแอปจะเปิดบนซิมูเลเตอร์และคุณดูสดในแผงได้
5. **โหลดซ้ำทันทีของพรีวิว** — “แสดงพรีวิว SwiftUI ของ /path/to/MyPackage” → `ios_sim_preview start` แก้ไฟล์ซอร์สแล้วพรีวิวจะสลับร้อนเข้าเครื่องจำลองที่กำลังทำงานใน ~2–5 วินาที — ไม่ต้องเปิดใหม่
6. **ขับเคลื่อน iPhone จริง** — เสียบโทรศัพท์ผ่าน USB (สายข้อมูล) ปลดล็อก แล้ว “เริ่ม WebDriverAgent บนโทรศัพท์” → `ios_real_start_wda` แผงสลับเป็นสตรีมสดของโทรศัพท์และทุกเครื่องมือยอมรับ udid ใน `realDevices` ของมัน เมื่อการเรียกล้มเหลว อ่านเหตุผลแบบรหัสจากสถานะของแผง (`device-locked`, `cert-untrusted`, `profile-expired`, `tunnel-failed`, `device-unplugged`)
## การแก้ปัญหา
- **แบ็กเทรซใช้ `sample` แทน lldb หรือ leaks บ่นเรื่องการตรวจสอบที่ถูกจำกัด** — โหมดนักพัฒนาของ macOS ปิดอยู่ รัน `sudo DevToolsSecurity -enable` หนึ่งครั้งแล้วลองใหม่ จนกว่าจะถึงตอนนั้นเครื่องมือลดระดับอย่างเรียบร้อย: `ios_sim_backtrace` ถอยไปใช้ `sample` ของ Xcode (มีสัญลักษณ์แล้ว ไม่ระงับ) และ `ios_sim_leaks` รายงานคำใบ้ให้เปิดใช้
- **`ios_sim_ui_tree` / `ios_sim_tap_element` ต้องใช้ AXe** — ติดตั้งด้วย `brew install cameroncooke/axe/axe` หรือให้ปลั๊กอินดาวน์โหลดรีลีสที่ตรึงไว้เมื่อใช้ครั้งแรก (ต้องมีเน็ตไป github.com) ข้อความข้อผิดพลาดมีคำใบ้การติดตั้งครบถ้วนเสมอ `DSH_IOS_AXE_BIN=/path/to/axe` แทนที่การแก้ไข เครื่องมือแถว (`ios_sim_ui_rows` / `ios_sim_tap_row`) ต้องใช้ AXe บนซิมูเลเตอร์ด้วย
- **`ios_sim_find_text` / `ios_sim_tap_text` รายงานว่าตัวช่วย OCR หายไป** — การใช้ครั้งแรกจะคอมไพล์ `assets/ocr.swift` ที่แถมมาด้วย `swiftc` (ต้องมี Xcode) ลง `~/Library/Caches/dsh-ios/bin/ocr` ข้อผิดพลาดมีเส้นทางและคำใบ้ที่แน่นอน
- **`ios_sim_ui_rows` ไม่พบแถว** — ผลลัพธ์บอกเหตุผล: ความลึกน้อยเกินไป (เพิ่ม `max_depth` บนโทรศัพท์แต่ละสแนปช็อตที่ลึกขึ้นใช้ ~15–25 วินาที) ไม่ใช่จอรายการ หรือไม่มีข้อมูลการช่วยการเข้าถึงจริง ๆ หลังอ่านเชิงลึก การอ่านตื้นไม่มีวันถูกรายงานผิดเป็น “ไม่มีการช่วยการเข้าถึง”
- **`ios_sim_leaks` บนซิมูเลเตอร์ iOS 26.2** — บนรันไทม์ iOS 26.2 `leaks` ของ Xcode อาจตรวจสอบโพรเซสซิมูเลเตอร์ไม่สำเร็จด้วยการวินิจฉัยร้ายแรง เช่น `Failed to get DYLD info` หรือข้อผิดพลาด minimal-corpse แม้เปิดโหมดนักพัฒนาแล้ว เครื่องมือลดระดับอย่างเรียบร้อย: คุณได้การวินิจฉัยดิบ เป้าหมายถูกตรวจสอบเสมอว่าทำงานต่อ และไม่มีอะไรค้าง ไม่มีการแก้ไขฝั่งปลั๊กอิน — เมื่อเจอให้ลอง `mode: "memgraph"` หรือรันไทม์อื่น
- **การเรียกอุปกรณ์จริงล้มเหลวด้วยสถานะแบบรหัส** — สถานะของแผงบอกสาเหตุแทนการเดา: `device-locked` (ปลดล็อกโทรศัพท์ มันฟื้นเอง), `cert-untrusted` (เชื่อถือใบรับรอง WebDriverAgent บนอุปกรณ์), `profile-expired` (การลงนามทีมฟรีอยู่ได้ 7 วัน — รัน `ios_real_start_wda` ซ้ำเพื่อบิลด์ใหม่), `tunnel-failed` (ตรวจลิงก์ USB/usbmuxd), `device-unplugged` (ใช้สาย USB ที่รับส่งข้อมูลได้ — การจับคู่ผ่าน Wi-Fi เท่านั้นส่งต่อพอร์ตไม่ได้)
- **สตรีมหยุดเอง** — นั่นคือนโยบายเมื่อว่าง ไม่ใช่การล่ม: เมื่อไม่มีผู้บริโภค (แผงปิด ไม่มีการ์ดติดตั้ง ไม่มีเส้นทางทำงาน) สตรีมหยุดหลัง 5 นาทีและเริ่มใหม่ในการเรียกเครื่องมือครั้งถัดไปหรือเมื่อเปิดแผง สตรีมที่ล่มเริ่มใหม่ในเบื้องหลังภายใน ~5 วินาที
## การพัฒนา
```sh
pnpm install
pnpm run build # tsc ฝั่งโฮสต์ + บันเดิลไคลเอนต์ → lib/
pnpm run typecheck
```
การทดสอบสโมกใน `scripts/` ตรวจสอบ `lib/` ที่บิลด์แล้ว (เฉพาะ macOS สำหรับส่วนที่บูตซิมูเลเตอร์หรือคุยกับโทรศัพท์ USB ตั้ง `DSH_IOS_SMOKE_SKIP_SIM=1` เพื่อข้ามส่วนเหล่านั้น):
| สคริปต์ | สิ่งที่ครอบคลุม |
| --- | --- |
| `node scripts/dev-smoke.mjs` | โฮสต์ซิม: แก้ไขไบนารี เปิดสตรีม ควบคุม keep-alive dispose |
| `node scripts/dev-tools-smoke.mjs [--full-build]` | เครื่องมือหลักกับซิมูเลเตอร์จริง (พร้อมบิลด์จริงด้วย `--full-build`) |
| `node scripts/dev-routes-smoke.mjs` | เส้นทางเว็บที่ลงนาม: grant พร็อกซีสตรีม สกรีนช็อต รีเลย์ ws รั้ว การหมดอายุ |
| `node scripts/dev-card-smoke.mjs` | การ์ดไคลเอนต์: SSR คงที่ (ไม่มี `
`) สัญญา status/capture ส่วนเน็ตเกือบสด |
| `node scripts/dev-panel-smoke.mjs` | คอมโพเนนต์แผง โหมดขนาด สไตล์เฟรม ลอจิกด็อก/ตัวกระตุ้น/แคปซูล (คงที่เท่านั้น) |
| `node scripts/dev-logs-smoke.mjs` | snapshot/follow ของ `ios_sim_logs` ตัวกรอง ขีดจำกัด การเก็บเกี่ยวโพรเซส |
| `node scripts/dev-uitree-smoke.mjs` | เครื่องมือ UI ทรี: ไปป์ไลน์แก้ไข/ดาวน์โหลด AXe ตัวเลือก ทรีและแตะบนซิมูเลเตอร์จริง |
| `node scripts/dev-debug-smoke.mjs` | เครื่องมือดีบัก: โพรเซส แบ็กเทรซ (lldb + sample) หน่วยความจำรั่ว ข้อมูลแอป |
| `node scripts/dev-preview-smoke.mjs` | โหลดซ้ำทันทีของพรีวิว: เริ่ม แก้ไข → สลับร้อนโดยไม่เปิดใหม่ กู้คืนข้อผิดพลาด หยุด |
| `node scripts/dev-orphan-smoke.mjs` | การรับเลี้ยง/เรียกคืน serve-sim กำพร้าหลังโฮสต์ถูกฆ่าอย่างไม่สุภาพ |
| `node scripts/dev-ocr-smoke.mjs` | เครื่องมือ Vision-OCR: แก้ไขตัวช่วย แคชคอมไพล์ swiftc ไปป์ไลน์จดจำ เส้นทาง tap-text |
| `node scripts/dev-wda-smoke.mjs` | โฮสต์ WebDriverAgent: แยกวิเคราะห์ `ServerURLHere` จำแนกความล้มเหลว ทันเนล keep-alive (ม็อก มีรอบสดให้เลือก) |
| `node scripts/dev-realdevice-smoke.mjs` | `xcrun devicectl` กับ iPhone ที่เชื่อมต่อ USB — เส้นทางโค้ดที่เครื่องมือใช้จริงทุกประการ |
| `node scripts/dev-realstart-smoke.mjs` | เส้นทาง `/real-start`: รั้ว การปฏิเสธแบบรหัส ประตูบิลด์/เปิด (คงที่) |
| `node scripts/dev-realtools-smoke.mjs` | แบ็กเอนด์อุปกรณ์จริงของ `ios_sim_screenshot` / `ios_sim_interact` / `ios_sim_ui_tree` / `ios_sim_tap_element` พร้อม `ios_real_start_wda` |
## ระบบนิเวศ
- [DSH Android](https://github.com/ZSeven-W/dsh-android) — อีมูเลเตอร์ Android หรืออุปกรณ์ผ่าน USB แบบสดภายในบทสนทนา ขับเคลื่อนทั้งหมดผ่าน adb
- [DSH Crew](https://github.com/ZSeven-W/dsh-crew) — มอบหมายงานให้เอเจนต์ DSH จาก Claude Code / Codex
- [DSH Noema](https://github.com/ZSeven-W/dsh-noema) — หน่วยความจำระยะยาวสำหรับ DSH
- [DSH OpenPencil](https://github.com/ZSeven-W/dsh-openpencil) — ตรวจดูและแก้ไขเอกสารออกแบบ `.op` ในบทสนทนา
## เครดิต & ใบอนุญาต
- [serve-sim](https://github.com/EvanBacon/serve-sim) — Evan Bacon — เอนจินสตรีมซิมูเลเตอร์ (Apache-2.0 dependency รันไทม์ที่แถมมา)
- [AXe](https://github.com/cameroncooke/AXe) — Cameron Cooke — CLI การช่วยการเข้าถึงเบื้องหลังเครื่องมือ UI ทรี (MIT)
- [WebDriverAgent](https://github.com/appium/WebDriverAgent) — เซิร์ฟเวอร์ WebDriver ที่ปลั๊กอินบิลด์และเปิดบนอุปกรณ์จริง (ใบอนุญาต BSD)
- สถาปัตยกรรมได้แรงบันดาลใจจากปลั๊กอิน “Build iOS Apps” ของ Codex เอนจินพรีวิว SwiftUI เป็นการสร้างใหม่แบบคลีนรูมของแนวทางที่เผยแพร่ต่อสาธารณะ — ไม่มีการคัดลอกโค้ด Codex
- ดูประกาศฉบับเต็มที่ [THIRD_PARTY_NOTICES.md](./THIRD_PARTY_NOTICES.md)
**ใบอนุญาต**: MIT