DSH Android
DeepSeek Harness बातचीत के भीतर एक लाइव Android डिवाइस — एमुलेटर हो या USB फ़ोन, पूरी तरह adb से संचालित।
20 एजेंट टूल्स • इन-प्रोसेस लाइव स्ट्रीम, कोई बाहरी हेल्पर नहीं • तीन-बटन नेविगेशन पैनल • Gradle बिल्ड & रन • Vision OCR
npm: @zseven-w/dsh-android · वर्तमान प्लगइन रिलीज़: 0.1.0-rc.4 · DSH 0.1.1-rc.1 के साथ परीक्षित
English · 简体中文 · 繁體中文 · 日本語 · 한국어 · Français · Español · Deutsch · Português · Русский · हिन्दी · Türkçe · ไทย · Tiếng Việt · Bahasa Indonesia
DSH बातचीत के भीतर से स्ट्रीम और नियंत्रित किया गया एक Android डिवाइस — बीच में एजेंट का टूल कॉल, दाईं ओर लाइव डिवाइस पैनल
## DSH Android क्यों
DSH Android एजेंट को बातचीत के भीतर एक असली Android डिवाइस देता है — और आपको पिक्सल। एजेंट एमुलेटर या USB से जुड़े फ़ोन पर स्ट्रीम शुरू कर सकता है, Gradle प्रोजेक्ट बिल्ड करके इंस्टॉल कर सकता है, UI को `resource-id`/टेक्स्ट से या OCR से चला सकता है, logcat पढ़ सकता है, और प्रोसेस व मेमोरी की जाँच कर सकता है, जबकि डिवाइस की लाइव स्ट्रीम एक स्थायी साइडबार पैनल में दिखती रहती है जहाँ आप वीडियो पर सीधे टैप, ड्रैग, घुमा सकते हैं और Back / Home / Recents दबा सकते हैं। कोई इमेज ब्लॉक नहीं और कोई स्क्रीन-रिकॉर्डिंग फ़ाइल नहीं: विज़ुअल बाइट्स UI तक केवल DSH वेबसर्वर द्वारा दिए गए साइन किए हुए, समय-सीमा वाले URL से पहुँचते हैं।
ठीक एक ही कोड पाथ है। `adb devices -l` एक **serial** बताता है, और वही serial किसी डिवाइस की एकमात्र पहचान है — `emulator-5554`, कोई USB serial, या कोई `ip:port` लक्ष्य, सब बिल्कुल एक जैसा बर्ताव करते हैं। प्लगइन किसी एमुलेटर उत्पाद (AVD, Genymotion, WSA, कोई क्लाउड डिवाइस फ़ार्म) से बंधा नहीं है, और सोचने के लिए कोई सिम्युलेटर/असली-डिवाइस विभाजन भी नहीं है।
| | |
| --- | --- |
| 📱 **बातचीत में लाइव डिवाइस** | एक `multipart/x-mixed-replace` PNG स्ट्रीम जो **इन-प्रोसेस** बनती है और साइन किए हुए `/_dsh/dsh-android/*` रूट से सीधे latest-frame बफ़र से परोसी जाती है। |
| 🔌 **कोई बाहरी स्ट्रीम हेल्पर नहीं, कोई भीतरी पोर्ट नहीं** | एक स्थायी `adb exec-out` चाइल्ड `while :; do screencap -p; done` चलाता है; होस्ट जुड़े हुए PNG को खुद फ़्रेमों में बाँटता है। प्रॉक्सी करने के लिए कोई लूपबैक स्ट्रीम सर्वर नहीं, संभालने को कोई पोर्ट रेंज नहीं, और अशिष्ट एग्ज़िट के बाद अपनाने को कुछ नहीं। |
| 🧩 **एक ही adb कोड पाथ** | adb के लिए और इस प्लगइन के लिए एमुलेटर और फ़ोन एक ही चीज़ हैं। कोई `simctl`/WebDriverAgent दोहरा स्टैक नहीं, फ़िज़िकल डिवाइस चलाने से पहले कोई बिल्ड-और-भरोसा वाली कवायद नहीं। |
| 🛠️ **20 एजेंट टूल्स** | डिवाइस, बूट/शटडाउन, स्क्रीनशॉट, इंटरैक्ट, Gradle बिल्ड & रन, ऐप सूचीकरण/लॉन्चिंग, `uiautomator` UI ट्री + एलिमेंट-से-टैप, लिस्ट/फ़ीड पंक्ति क्रियाएँ, Vision OCR खोज/टैप/प्रतीक्षा, logcat, प्रोसेस, ANR/क्रैश बैकट्रेस, meminfo, ऐप जानकारी। |
| 👆 **तीन-बटन नेविगेशन पैनल** | लाइव वीडियो पर टैप और ड्रैग करें; **◁ Back · ○ Home · □ Recents** के साथ घुमाने, स्क्रीनशॉट और रीफ़्रेश वाला टूलबार; नोटिफ़िकेशन शेड, क्विक सेटिंग्स, लॉक, वेक और असिस्टेंट के लिए एक डिवाइस मेनू। |
| 🖼️ **नेटिव मल्टीमॉडल** | इमेज समझने वाले मॉडल पर हर कैप्चर टूल (screenshot, interact, tap_element, tap_text, tap_row) स्क्रीनशॉट को ही एक image block के रूप में लौटाता है — मॉडल स्क्रीन को सीधे देखता है। OCR पिक्सेल-सटीक टेक्स्ट टैप और केवल-टेक्स्ट रूट के लिए बना रहता है; केवल-टेक्स्ट मॉडल को वही सादा JSON सारांश मिलता रहता है। |
| 🔐 **साइन किए हुए, केवल-लूपबैक रूट** | हर रूट को किसी भी कैपेबिलिटी की जाँच से पहले लूपबैक पीयर, लूपबैक `Host` (DNS रीबाइंडिंग अस्वीकृत) और Fetch-Metadata/Origin जाँच चाहिए। HMAC-SHA256 कैपेबिलिटी 10 मिनट के भीतर समाप्त हो जाती हैं। |
| 🔍 **सिमेंटिक + विज़ुअल ऑटोमेशन** | `android_ui_tree` `uiautomator` पदानुक्रम डंप करता है और `android_tap_element` `resource-id`, टेक्स्ट या content-description से टैप करता है; जब ट्री खाली हो या टेक्स्ट इमेज में पका हुआ हो, तब `android_find_text` / `android_tap_text` निर्देशांकों का अनुमान लगाने के बजाय स्क्रीन का OCR कर लेते हैं। |
## टूल्स
सभी 20 टूल हर होस्ट पर पंजीकृत होते हैं और सादा JSON लौटाते हैं — विज़ुअल बाइट्स UI तक केवल `presentationMeta` + साइन किए हुए रूट से पहुँचते हैं, कभी इमेज ब्लॉक के रूप में नहीं। जब adb हल न हो सके, तब भी टूल पंजीकृत रहते हैं और हर कॉल एक व्याख्यात्मक एरर के साथ विफल होती है जो समाधान का नाम बताती है।
निर्देशांक हर जगह **स्ट्रीम किए गए फ़्रेम के 0..1 सामान्यीकृत** होते हैं। फ़्रेम डिस्प्ले रोटेशन का अनुसरण करता है (1080×2400 डिवाइस पर लैंडस्केप ऐप 2400×1080 स्ट्रीम करता है) और `input tap` उसी स्पेस को साझा करता है, इसलिए इस प्लगइन में कहीं भी क्लाइंट-साइड रोटेशन गणित नहीं है।
### मुख्य टूल
| टूल | क्या करता है | मुख्य पैरामीटर |
| --- | --- | --- |
| `android_devices` | `adb devices -l` द्वारा बताया गया हर डिवाइस सूचीबद्ध करता है (serial, स्थिति, एमुलेटर/फ़िज़िकल, मॉडल, Android संस्करण, API स्तर, AVD नाम) और साथ में मशीन के AVD नाम `avds` के अंतर्गत। दूसरे टूल जो serial लेते हैं, उसे खोजने के लिए इसका उपयोग करें। विफल सूचीकरण खाली सूची लौटाने के बजाय एरर फेंकता है। | — |
| `android_boot` | लाइव स्ट्रीम शुरू करता है। तुरंत स्ट्रीम करने के लिए कोई ONLINE serial दें, या कोई AVD नाम दें ताकि पहले वह एमुलेटर लॉन्च हो और बूट पूरा होते ही स्ट्रीम शुरू हो जाए (ठंडी शुरुआत में कई मिनट)। स्ट्रीम बातचीत भर जीवित रहती है ताकि पैनल डिवाइस को लाइव दिखा सके। | `device` (आवश्यक — कोई serial या AVD नाम) |
| `android_shutdown` | एमुलेटर बंद करता है (`adb emu kill`) और स्ट्रीम उसी डिवाइस पर हो तो उसे रोक देता है। फ़िज़िकल डिवाइस के लिए कारण सहित मना कर दिया जाता है: adb किसी फ़ोन को बंद नहीं कर सकता। | `device` |
| `android_screenshot` | PNG कैप्चर करता है और छोटा JSON सारांश लौटाता है (पथ, बाइट्स, आयाम, डिवाइस); इमेज कार्ड और पैनल में दिखती है, कभी इमेज ब्लॉक के रूप में नहीं। | `device` (वैकल्पिक — स्ट्रीम हो रहा डिवाइस, वरना एकमात्र ऑनलाइन डिवाइस) |
| `android_interact` | स्ट्रीम हो रहे डिवाइस से इंटरैक्ट करें: 0..1 सामान्यीकृत निर्देशांक पर टैप, टेक्स्ट टाइप, कोई नेविगेशन या हार्डवेयर बटन दबाना (`back`, `home`, `recents`, `power`, `volume_up`, `volume_down`, `menu`, `enter`, `delete`), स्वाइप जेस्चर भेजना, या स्क्रॉल करना। क्रिया स्थिर होने (~300 ms) के बाद एक ताज़ा स्क्रीनशॉट प्रभाव दिखाता है। | `action` (आवश्यक — `tap`/`type`/`button`/`gesture`/`scroll`), `x`/`y`, `text`, `name`, `json`, `device` |
| `android_list_apps` | डिवाइस पर इंस्टॉल किए गए पैकेज सूचीबद्ध करता है (`pm list packages`), साथ में `dumpsys package` से संस्करण नाम और जब हल हो सके तो मानव-पठनीय लेबल — किसी थर्ड-पार्टी पैकेज का नाम अनुमान से नहीं निकाला जा सकता, इसलिए उसे सूचीबद्ध करें या `android_launch_app` को `name` दें। | `device`, `query` (केस-इनसेंसिटिव सबस्ट्रिंग, CJK सहित), `include_system` (डिफ़ॉल्ट false) |
| `android_launch_app` | इंस्टॉल किया ऐप `packageName` से लॉन्च करता है, या `name` से (उसी सूचीकरण से हल होने वाला केस-इनसेंसिटिव लेबल सबस्ट्रिंग)। दोनों में से ठीक एक। `relaunch` पहले ऐप को फ़ोर्स-स्टॉप करता है। | `packageName` या `name` (ठीक एक), `device`, `relaunch` |
| `android_build_run` | Gradle प्रोजेक्ट बिल्ड करता है (`./gradlew assembleDebug`), बनी हुई debug APK इंस्टॉल करता है (`adb install -r`), और उसे लॉन्च करता है। पूरे बिल्ड में कई मिनट लगते हैं; विफलता पर परिणाम में Gradle एरर आउटपुट का अंतिम हिस्सा आता है। | `projectPath` (आवश्यक), `device` |
### UI-ट्री और पंक्ति टूल (`uiautomator`)
| टूल | क्या करता है | मुख्य पैरामीटर |
| --- | --- | --- |
| `android_ui_tree` | फ़ोरग्राउंड ऐप का `uiautomator` पदानुक्रम नोड्स के रूप में डंप करता है — `type` (क्लास नाम का अंतिम भाग), `text`, `contentDesc`, `resourceId`, पिक्सल में `bounds`, `enabled`, `focused` — ~40 KB पर सीमित (सबसे गहरे स्तर छाँट दिए जाते हैं और `truncated` सेट हो जाता है)। | `device`, `max_depth`, `filter` (टेक्स्ट/content-description/resource-id पर केस-इनसेंसिटिव सबस्ट्रिंग) |
| `android_tap_element` | पहचान से एलिमेंट पर टैप करता है — `resource_id` नोड के `resource-id` से मेल खाता है; `text` उसके टेक्स्ट या content-description से। पहले सटीक मिलान, फिर केस-इनसेंसिटिव सबस्ट्रिंग; नेस्टेड डुप्लिकेट एक ही लक्ष्य में सिमट जाते हैं और मिलान अस्पष्ट होने पर कोई एक चुनने के बजाय 8 तक उम्मीदवार सूचीबद्ध होते हैं। निष्क्रिय एलिमेंट अस्वीकृत हैं। टैप एलिमेंट के केंद्र पर पड़ता है, फिर ~300 ms बाद का स्क्रीनशॉट प्रभाव दिखाता है; `expect_text` / `expect_gone` दें और टैप व उसका सत्यापन एक ही राउंड ट्रिप बन जाते हैं। | `device`, `resource_id`, `text`, `expect_text`, `expect_gone` |
| `android_ui_rows` | किसी लिस्ट/फ़ीड स्क्रीन (`RecyclerView` और उसके साथी) को कच्चे ट्री के बजाय पंक्तियों के रूप में पढ़ता है: एक जैसे आकार वाले दोहराए गए चाइल्ड पंक्तियाँ बन जाते हैं जिनमें इंडेक्स, पिक्सल फ़्रेम, समेकित लेबल और उसी लेबल से पार्स किए गए काउंटर (संख्या + क्लासिफ़ायर टोकन, चीनी या अंग्रेज़ी — कोई ऐप शब्दावली हार्डकोड नहीं है) होते हैं। काउंटर कुंजियाँ राउंड-ट्रिप करती हैं: जैसी सूचीबद्ध हों बिल्कुल वैसी ही `android_tap_row.expect_count` को दें। | `device`, `max_depth` |
| `android_tap_row` | एक दिखाई देने वाली पंक्ति के भीतर सापेक्ष स्थिति पर टैप करता है (`index` `android_ui_rows` से; `x`/`y` उस पंक्ति के फ़्रेम के अंश, डिफ़ॉल्ट 0.5 = केंद्र)। फ़्रेम एक ताज़ा ट्री रीड से आता है, इसलिए कोई निरपेक्ष निर्देशांक अनुमानित नहीं होता, और सीमा से बाहर का इंडेक्स क्लैंप होने के बजाय विफल होता है। `expect_count={key, delta}` के साथ टूल ~800 ms बाद पंक्ति फिर पढ़ता है और पुष्टि करता है कि काउंटर ठीक ±1 बदला; अज्ञात कुंजी होने पर टैप होने से पहले ही मना कर दिया जाता है। | `device`, `index` (आवश्यक), `x`, `y`, `expect_count` (`{key, delta}`) |
### OCR, लॉग और डीबग टूल्स
| टूल | क्या करता है | मुख्य पैरामीटर |
| --- | --- | --- |
| `android_find_text` | प्लगइन द्वारा कंपाइल किए गए Vision हेल्पर से वर्तमान स्क्रीन का OCR करता है (सटीक पहचान, zh-Hans + en-US)। इसका उपयोग तब करें जब UI ट्री खाली या विकृत हो, ग्राफ़िक के रूप में रेंडर हुए टेक्स्ट के लिए (बैज गिनती, इमेज में पकी हुई क़ीमतें), या स्क्रीन पर क्या है इसकी स्वतंत्र पुष्टि के लिए। `{device, size, items:[{text, confidence, rect}]}` लौटाता है जहाँ rect ऊपर-बाएँ मूल बिंदु वाले **पिक्सल** बॉक्स हैं, confidence के क्रम में और ~40 KB पर सीमित। केवल macOS होस्ट। | `device`, `query` (केस-इनसेंसिटिव सबस्ट्रिंग), `min_confidence` (डिफ़ॉल्ट 0.3) |
| `android_tap_text` | वर्तमान स्क्रीन का OCR करता है और सबसे बेहतर टेक्स्ट मिलान के केंद्र पर टैप करता है — वही सटीक → समाहित → उम्मीदवार-सूची नियम जो `android_tap_element` में हैं, उस टेक्स्ट के लिए जो UI ट्री को दिखता ही नहीं। मिले हुए पिक्सल केंद्र को फ़्रेम आकार के सापेक्ष सामान्यीकृत करके टैप के रूप में भेजा जाता है; ~300 ms बाद एक ताज़ा स्क्रीनशॉट प्रभाव दिखाता है। केवल macOS होस्ट। | `device`, `query` (आवश्यक), `min_confidence`, `expect_text`, `expect_gone` |
| `android_wait_for` | प्रतीक्षा करता है जब तक टेक्स्ट प्रकट या ग़ायब न हो जाए, हर 600 ms पर वही कैप्चर + OCR पाइपलाइन पोल करते हुए, जब तक शर्त पूरी न हो या टाइमआउट समाप्त न हो जाए (डिफ़ॉल्ट 8 s, अधिकतम 60 s)। टाइमआउट एक सामान्य `matched:false` उत्तर है, कभी एरर नहीं। केवल macOS होस्ट। | `device`, `text` (आवश्यक), `mode` (`appear`/`disappear`), `timeout_ms`, `min_confidence` |
| `android_logs` | डिवाइस जो लॉग करता है उसे पढ़ता है: `snapshot` (हाल की अवधि पर `logcat -d -v time`, डिफ़ॉल्ट 2m) या `follow` (`duration_seconds` के लिए सीमित लाइव कैप्चर, डिफ़ॉल्ट 10, अधिकतम 60 — कभी लटकती हुई स्ट्रीम नहीं)। `bundle_id` (Android पैकेज नाम, जो उसकी pid में हल होता है) से किसी एक ऐप तक फ़िल्टर करें। आउटपुट ~300 पंक्तियों / 30 KB पर सीमित है, साथ में दायरा घटाने का संकेत। | `device`, `mode` (`snapshot`/`follow`), `duration`, `duration_seconds`, `bundle_id`, `grep` |
| `android_processes` | डिवाइस पर चल रही प्रोसेस सूचीबद्ध करता है (`ps -A`) `{pid, name}` के रूप में — `android_backtrace` के लिए pid का स्रोत। | `device`, `filter` (प्रोसेस नाम पर केस-इनसेंसिटिव सबस्ट्रिंग) |
| `android_backtrace` | प्रोसेस से उसके स्टैक डंप करने को कहता है (`kill -3`) और `/data/anr/` से बनी ANR ट्रेस पढ़ता है। ज़्यादातर बिना-रूट डिवाइस उस निर्देशिका तक पहुँच नहीं देते, इसलिए टूल क्रैश बफ़र (`logcat -b crash -d`) पर उतर आता है और ईमानदारी से बताता है कि किस इंजन ने उत्तर दिया और वह क्या नहीं देख सकता। | `device`, `pid` या `bundle_id` |
| `android_meminfo` | `dumpsys meminfo ` पार्स करता है: कुल PSS, Java/native/graphics विभाजन, और शीर्ष श्रेणियाँ — लीक सारांश का Android वाला उत्तर। | `device`, `bundle_id` (आवश्यक) |
| `android_app_info` | `dumpsys package ` से इंस्टॉल किए ऐप के तथ्य: संस्करण नाम और कोड, डेटा निर्देशिका, कोड पथ, पहली इंस्टॉल का समय, और सिस्टम फ़्लैग। ऐप न मिलने पर `installed: false` और `android_list_apps` का नाम लेता हुआ एक नोट लौटता है — यह एरर नहीं फेंकता। | `device`, `bundle_id` (आवश्यक) |
## डिस्प्ले सतहें
- **साइडबार पैनल.** लाइव दृश्य एक स्थायी दाएँ पैनल में रहता है (एक फ़िक्स्ड डॉक जो बातचीत को किनारे कर देता है, या संकीर्ण व्यूपोर्ट पर केंद्रित ओवरले)। यह लाइव PNG स्ट्रीम दिखाता है और वीडियो पर सीधे क्लिक-से-टैप व ड्रैग-से-जेस्चर लेता है, साथ में **◁ Back**, **○ Home**, **□ Recents**, घुमाएँ, स्क्रीनशॉट और रीफ़्रेश वाला टूलबार। एक डिवाइस मेनू पाँच डिवाइस-स्तरीय क्रियाएँ चलाता है (नोटिफ़िकेशन शेड, क्विक सेटिंग्स, लॉक, वेक, असिस्टेंट)। डिवाइस पिकर हर adb डिवाइस को एक ही सूची में, प्रकार के अनुसार समूहित करके दिखाता है, और ऑफ़लाइन AVD क्लिक-पर-बूट के बजाय `android_boot` की ओर इशारा करते संकेत के रूप में दिखते हैं। साइज़ मोड और फ़्रेम स्टाइल (फ़्रेमलेस / बेज़ल / फ़ोन शेल) iOS जुड़वाँ की तरह ही काम करते हैं; पैनल अपना आस्पेक्ट रेशियो फ़्रेम के अपने स्वाभाविक आकार से लेता है, इसलिए घुमाने के लिए किसी कॉन्फ़िगरेशन की ज़रूरत नहीं पड़ती।
- **कॉम्पैक्ट बातचीत कार्ड.** टूल परिणाम बिना इनलाइन इमेजरी के एक-पंक्ति कार्ड के रूप में दिखते हैं: डिवाइस का नाम, एक क्रिया उप-लेबल, एक स्टेटस बैज, और “साइडबार में खोलें” संकेत। पंक्ति पर क्लिक करने से पैनल खुल जाता है।
- **इनपुट के ऊपर स्टेटस कैप्सूल.** जब पैनल बंद हो और कोई स्ट्रीम ऑनलाइन हो, कंपोज़र के ऊपर एक छोटी पिल दिखती है जो क्लिक करने पर पैनल खोल देती है।
- **स्टैंडर्ड मोड और Code Mode.** स्टैंडर्ड सेशन होस्ट-प्रोजेक्टेड `presentationMeta` का उपयोग करते हैं; नेस्टेड Code Mode डिस्पैच कोई meta नहीं ले जाते, इसलिए क्लाइंट टिकाऊ परिणाम JSON से वही meta फिर से बना लेता है — पैनल, कार्ड और कैप्सूल दोनों में काम करते हैं।
## सुरक्षा
- **ब्राउज़र कभी adb से बात नहीं करता, और बात करने के लिए कोई भीतरी पोर्ट है ही नहीं।** स्ट्रीम इसी प्रोसेस में बनती है और मेमोरी से परोसी जाती है; हर बाइट DSH वेबसर्वर ओरिजिन को प्लगइन-स्वामित्व वाले `/_dsh/dsh-android/*` रूट से पार करता है: `/stream/` (लाइव मल्टीपार्ट PNG), `/screenshot/` (कैश्ड PNG), साथ ही `/grant`, `/switch-device`, `/devices`, `/capture`, `/status`, `/control`, और `/device-action`। यह प्रॉक्सी किए गए लूपबैक स्ट्रीम सर्वर की तुलना में सख़्ती से छोटा अटैक सरफ़ेस है।
- **तिहरा लूपबैक घेरा, किसी भी कैपेबिलिटी को पढ़ने से पहले लागू।** ट्रांसपोर्ट पीयर लूपबैक पता होना चाहिए, `Host` हेडर किसी लूपबैक अथॉरिटी का नाम लेना चाहिए (ताकि DNS-रीबाइंडिंग वाला `Host` अस्वीकृत हो जाए), और Fetch-Metadata/`Origin` सेम-ओरिजिन होने चाहिए। Host और Origin कॉलर-नियंत्रित डेटा हैं और अकेले उन पर कभी भरोसा नहीं किया जाता।
- **HMAC-SHA256 कैपेबिलिटी जो 10 मिनट के भीतर समाप्त हो जाती हैं**, `base64url(payload).base64url(mac)` स्वरूप में और प्रति-DSH-होम 32-बाइट कुंजी से साइन की हुई (`/cache/dsh-android/stream-access.key`, मोड 0600, एटॉमिक रूप से बनाई गई)। एक डिवाइस के लिए जारी कैपेबिलिटी उसी क्षण काम करना बंद कर देती है जब कोई दूसरा डिवाइस स्ट्रीम स्लॉट ले लेता है, और स्क्रीनशॉट कैपेबिलिटी को स्ट्रीम रूट पर दोबारा नहीं चलाया जा सकता।
- **स्क्रीनशॉट रूट ठीक एक ही निर्देशिका परोसता है।** पथ `lstat` से चले जाते हैं (कोई भी सिम्बॉलिक लिंक अस्वीकृत), `realpath` कंटेनमेंट जाँच से पूरे होते हैं, `O_NOFOLLOW` से खोले जाते हैं, आकार-सीमित होते हैं, और पढ़ने के बाद फिर से सत्यापित होते हैं — इसलिए जो फ़ाइल टोकन जारी होने और लाए जाने के बीच सिमलिंक से बदल दी गई हो, वह कभी नहीं परोसी जाती।
- **`/grant` कभी कुछ बूट नहीं करता।** यह केवल पहले से ऑनलाइन डिवाइस के लिए फ़्रेम लूप शुरू करता है, और किसी दूसरे डिवाइस से स्ट्रीम छीनने से मना कर देता है (409 `device_busy`)। डिवाइस बदलने के लिए स्पष्ट `/switch-device` क्रिया चाहिए; AVD बूट करना `android_boot` टूल के ही पास रहता है।
- **कीप-अलाइव और आइडल स्टॉप.** क्रैश हुआ फ़्रेम लूप पृष्ठभूमि में फिर शुरू हो जाता है (~5 s देरी); शून्य उपभोक्ता होने पर स्ट्रीम 5 मिनट बाद खुद रुक जाती है। जानबूझकर रोकने से कभी नहीं लड़ा जाता।
## आवश्यकताएँ
- **Node ≥ 24.11.0.**
- **adb**, Android SDK platform-tools से, इसी क्रम में हल होता है: `ADB` एनवायरनमेंट वेरिएबल → `PATH` पर `adb` → ``/``/प्रति-OS डिफ़ॉल्ट SDK रूट + `/platform-tools/adb`। इसे `sdkmanager "platform-tools"` से, Android Studio से, या `brew install --cask android-platform-tools` से इंस्टॉल करें। adb के बिना भी प्लगइन लोड होता है और सभी 20 टूल पंजीकृत होते हैं; तब हर कॉल बताती है कि क्या कमी है।
- **एक डिवाइस**: किसी भी उत्पाद का एमुलेटर, या USB डीबगिंग सक्षम वाला फ़ोन। `emulator` लॉन्चर वैकल्पिक है और केवल AVD-नाम से `android_boot` को उसकी ज़रूरत पड़ती है — बाक़ी सब कुछ उसी के साथ काम करता है जो adb देख सके।
- **पैनल के लिए वेब बंडल के साथ DSH ≥ 0.1.0-rc.6**। हेडलेस प्रोफ़ाइल भी चलती हैं: सभी 20 टूल सामान्य रूप से काम करते हैं, बस लाइव दृश्य नहीं होता।
- **OCR के लिए macOS होस्ट** (केवल `android_find_text` / `android_tap_text` / `android_wait_for` को इसकी ज़रूरत है): प्लगइन पहले उपयोग पर अपनी बंडल की गई `assets/ocr.swift` को `swiftc` से कंपाइल करके `~/Library/Caches/dsh-android/bin/ocr` में रखता है। Linux और Windows होस्ट पर वे तीनों टूल बताते हैं कि OCR को macOS Vision फ़्रेमवर्क चाहिए; बाक़ी 17 अप्रभावित रहते हैं। ओवरराइड: `DSH_ANDROID_OCR_DIR`, `DSH_ANDROID_OCR_SWIFT`, `DSH_ANDROID_SWIFTC`।
- **ADBKeyboard** (वैकल्पिक, CJK और इमोजी इनपुट के लिए): `adb shell input text` केवल ASCII संभालता है। डिवाइस पर [ADBKeyboard](https://github.com/senzhk/ADBKeyBoard) इंस्टॉल करके उसे सक्रिय IME चुनें, फिर ग़ैर-ASCII टेक्स्ट उसके ब्रॉडकास्ट इंटरफ़ेस से पहुँचाया जाता है। उसके बिना ग़ैर-ASCII टाइपिंग इंस्टॉल संकेत के साथ अस्वीकृत होती है — चुपचाप ग़लत कभी नहीं लिखी जाती।
## फ़िज़िकल डिवाइस
यहाँ बिल्ड, साइन, ट्रस्ट और हर सात दिन में फिर से साइन करने वाला कोई WebDriverAgent जैसा तंत्र नहीं है। USB डीबगिंग सक्षम करें, फ़ोन प्लग करें, डिवाइस पर अधिकार-प्रॉम्प्ट स्वीकार करें, और वह `android_devices` में दिखने लगता है — हर टूल उसके साथ काम करता हुआ। अनधिकृत डिवाइस को किसी रहस्यमय विफलता के बजाय प्रॉम्प्ट संकेत के साथ वैसा ही बता दिया जाता है।
तीन ईमानदार चेतावनियाँ:
- **USB पर फ़्रेम दर कम रहती है** — फ़ोन पर लगभग 2–5 fps बनाम एमुलेटर पर 5–10 fps, क्योंकि हर फ़्रेम पूरे PNG के रूप में USB लिंक पार करता है।
- **CJK टाइपिंग को ADBKeyboard चाहिए** (ऊपर देखें); यह एमुलेटर और फ़ोन दोनों पर समान रूप से लागू होता है।
- **`android_shutdown` किसी फ़ोन को बंद नहीं कर सकता।** adb में ऐसा कोई क्रिया-शब्द है ही नहीं; टूल बहाना बनाने के बजाय यही कहता है।
## परफ़ॉर्मेंस
एमुलेटर पर मापा गया (Android 14, 1080×2400):
| | |
| --- | --- |
| स्थायी screencap लूप | ≈ 8 fps |
| `ensureStreaming` का पहला फ़्रेम | ~200 ms |
| `input tap` राउंड ट्रिप | ~130 ms |
यही एक स्थायी चाइल्ड इसे संभव बनाता है: हर फ़्रेम के लिए एक `adb` स्पॉन करने में पिक्सल हिलने से पहले ही ~50–100 ms लग जाते हैं। मशीन और स्क्रीन घनत्व के अनुसार एमुलेटर पर ~5–10 fps और USB फ़ोन पर ~2–5 fps की अपेक्षा रखें।
## DSH में इंस्टॉल करें
```sh
dsh plugin --profile web add @zseven-w/dsh-android@latest
dsh web
```
या इसे किसी मौजूदा प्रोफ़ाइल पैकेज की निर्भरता के रूप में जोड़ें:
```sh
pnpm add @zseven-w/dsh-android
```
## त्वरित शुरुआत
1. **डिवाइस खोजें** — “Android डिवाइस सूचीबद्ध करो।” → `android_devices`।
2. **स्ट्रीम शुरू करें** — “emulator-5554 स्ट्रीम करो।” → `android_boot`। पैनल खुलता है और डिवाइस लाइव दिखने लगता है। (AVD नाम देने पर पहले वह एमुलेटर बूट होता है।)
3. **वीडियो पर टैप करें** — पैनल पर सीधे टैप या ड्रैग करें, या एजेंट से चलवाएँ: “Settings खोलो, फिर Display पर टैप करो।” → `android_interact`, या पहचान-आधारित टैप के लिए `android_ui_tree` + `android_tap_element`, या जब ट्री अंधा हो तब `android_find_text` + `android_tap_text`।
4. **अपना ऐप बिल्ड करके चलाएँ** — “/path/to/MyApp बिल्ड करके चलाओ।” → `android_build_run`। पूरे Gradle बिल्ड में कई मिनट लगते हैं; पूरा होने पर ऐप लॉन्च होता है और आप उसे पैनल में लाइव देखते हैं।
5. **लॉग पढ़ें** — “com.example.app के logcat के पिछले दो मिनट दिखाओ।” → `android_logs`।
## समस्या निवारण
- **हर टूल कहता है कि adb उपलब्ध नहीं है** — एरर तीनों रिज़ॉल्यूशन स्तरों के नाम बताता है। `ADB=/path/to/adb` सेट करें, `adb` को `PATH` पर रखें, या SDK platform-tools इंस्टॉल करें (`sdkmanager "platform-tools"`)।
- **डिवाइस `unauthorized` है** — डिवाइस की स्क्रीन पर USB डीबगिंग प्रॉम्प्ट स्वीकार करें। `android_devices` डिवाइस छिपाने के बजाय उसकी स्थिति ईमानदारी से बताता है।
- **`android_boot` को कोई AVD नहीं मिलता** — `emulator` लॉन्चर खोजा नहीं जा सका। एमुलेटर किसी भी तरीक़े से शुरू करें; adb को दिखते ही वह `android_devices` में आ जाता है, और फिर `android_boot` उसका serial ले लेता है।
- **ग़ैर-ASCII टेक्स्ट अस्वीकृत हो रहा है** — ADBKeyboard इंस्टॉल करके उसे इनपुट मेथड चुनें (आवश्यकताएँ देखें)। यह अस्वीकृति जानबूझकर है: `input text` उन अक्षरों को चुपचाप गिरा या बिगाड़ देता।
- **`android_find_text` कहता है कि OCR उपलब्ध नहीं है** — OCR को macOS होस्ट चाहिए (Apple का Vision फ़्रेमवर्क)। बाक़ी 17 ग़ैर-OCR टूल हर जगह काम करते हैं।
- **स्ट्रीम अपने आप रुक जाती है** — यह आइडल नीति है, क्रैश नहीं: शून्य उपभोक्ता होने पर (पैनल बंद, कोई कार्ड माउंट नहीं, कोई रूट सक्रिय नहीं) स्ट्रीम 5 मिनट बाद रुक जाती है और अगली टूल कॉल या पैनल खुलने पर फिर शुरू हो जाती है। क्रैश हुआ लूप ~5 सेकंड के भीतर अपने आप फिर चल पड़ता है।
- **लॉन्चर पर रोटेशन ग़लत दिखता है** — लॉन्चर और Settings खुद को पोर्ट्रेट पर पिन कर लेते हैं और `user_rotation` को अनदेखा करते हैं। यह सामान्य Android व्यवहार है, प्लगइन बग नहीं; ऐसे ऐप के भीतर घुमाएँ जो इसकी अनुमति देता हो।
## डेवलपमेंट
```sh
pnpm install
pnpm run build # host tsc + client bundle → lib/
pnpm run typecheck
pnpm test # every static suite; no device required
```
`scripts/` स्मोक सुइट बने हुए `lib/` को परखते हैं। `dev-emulator-smoke.mjs` को छोड़कर वे सब स्टैटिक हैं; उसे एक डिवाइस चाहिए और कोई न होने पर वह SKIP (exit 0) बताता है।
| स्क्रिप्ट | क्या कवर करती है |
| --- | --- |
| `node scripts/dev-adb-smoke.mjs` | एक शिम बाइनरी के विरुद्ध adb रिज़ॉल्यूशन (env / PATH / SDK), `devices -l` पार्सिंग, बाइनरी-सुरक्षित `exec-out`, PNG फ़्रेम स्प्लिटर और उसका रीसिंक, input-text एस्केपिंग, और नक़ली टूलचेन के विरुद्ध होस्ट लाइफ़साइकल (स्ट्रीम, कंट्रोल, आइडल स्टॉप, डिस्पोज़)। |
| `node scripts/dev-routes-static-smoke.mjs` | नक़ली होस्ट के विरुद्ध साइन किए हुए रूट: सापेक्ष ग्रांट, समाप्त/जाली/क्रॉस-किंड टोकन, लूपबैक घेरा, 405/415/400 एनवेलप, कोडेड डिवाइस अस्वीकृतियाँ, `/control` वैलिडेशन, रोटेट शेप, स्क्रीनशॉट कंटेनमेंट, और लाइव मल्टीपार्ट स्ट्रीम। |
| `node scripts/dev-tools-smoke.mjs` | `createAndroidTools` सीम के ज़रिए नक़ली होस्ट के विरुद्ध मुख्य टूल। |
| `node scripts/dev-uitree-smoke.mjs` | UI-ट्री और पंक्ति टूल: `uiautomator` XML पार्सिंग, सेलेक्टर, गहराई की सीमा, पंक्ति व काउंटर ह्यूरिस्टिक्स। |
| `node scripts/dev-logs-smoke.mjs` | `android_logs` snapshot/follow, फ़िल्टर, सीमाएँ, और प्रोसेस रीपिंग। |
| `node scripts/dev-panel-smoke.mjs` | पैनल कंपोनेंट, साइज़ मोड, फ़्रेम स्टाइल, डॉक/ट्रिगर/कैप्सूल लॉजिक (केवल SSR)। |
| `node scripts/dev-emulator-smoke.mjs [serial]` | लाइव डिवाइस: पहला फ़्रेम, निरंतर फ़्रेम दर, टैप राउंड ट्रिप, डिस्पोज़। |
## समस्या निवारण
### एमुलेटर पर ख़ाली / सफ़ेद स्ट्रीम
अगर पैनल एकदम सफ़ेद (या काली) इमेज स्ट्रीम करता है जबकि `android_ui_tree`
अब भी असली UI एलिमेंट देख रहा है, तो आपकी मशीन पर एमुलेटर का होस्ट-GPU
फ़्रेमबफ़र रीडबैक टूटा हुआ है (कुछ macOS होस्ट पर यह एक ज्ञात gfxstream समस्या है —
`screencap` खुद ख़ाली फ़्रेम लौटाता है, इसलिए हर स्क्रीन टूल प्रभावित होता है)।
एमुलेटर को सॉफ़्टवेयर रेंडरिंग के साथ फिर से लॉन्च करें:
```bash
emulator -avd -gpu swiftshader_indirect
```
या AVD की `config.ini` में `hw.gpu.mode=swiftshader_indirect` सेट करें। फ़िज़िकल
डिवाइस कभी प्रभावित नहीं होते।
## रोडमैप
- **उच्च फ़्रेम-दर वाला स्रोत।** `StreamSource` सीम जानबूझकर प्लगेबल है: `scrcpy-server` + WebCodecs H.264 पाथ रूट, टूल या पैनल को छुए बिना प्रति-फ़्रेम PNG स्ट्रीम की जगह ले सकता है।
- **Compose प्रीव्यू हॉट रीलोड।** iOS जुड़वाँ SwiftUI प्रीव्यू को dylib के रूप में हॉट-स्वैप करता है; Compose के पास आज ऐसा कोई समकक्ष हॉट-स्वैप प्रिमिटिव नहीं है, इसलिए यह भेज-दी-गई-पर-डगमगाती सुविधा बनने के बजाय भविष्य की मद बनी रहती है।
## इकोसिस्टम
- [DSH iOS Simulator](https://github.com/ZSeven-W/dsh-ios) — iOS सिम्युलेटर और USB से जुड़े iPhone के लिए वही आर्किटेक्चर
- [DSH Crew](https://github.com/ZSeven-W/dsh-crew) — Claude Code / Codex से DSH एजेंट को काम भेजें
- [DSH Noema](https://github.com/ZSeven-W/dsh-noema) — DSH के लिए दीर्घकालिक स्मृति
- [DSH OpenPencil](https://github.com/ZSeven-W/dsh-openpencil) — बातचीत के भीतर `.op` डिज़ाइन दस्तावेज़ देखें और संपादित करें
## श्रेय & लाइसेंस
- [Android SDK platform-tools](https://developer.android.com/tools/releases/platform-tools) (`adb`) — रनटाइम पर हल होता है, कभी पुनर्वितरित नहीं: Google का SDK लाइसेंस उसे बंडल करने की अनुमति नहीं देता।
- [ADBKeyboard](https://github.com/senzhk/ADBKeyBoard) — Senzhk — ग़ैर-ASCII टाइपिंग के पीछे वैकल्पिक ऑन-डिवाइस IME (Apache-2.0; बंडल नहीं किया गया)।
- आर्किटेक्चर और रूट रुख़ [dsh-ios](https://github.com/ZSeven-W/dsh-ios) के साथ साझा हैं, जहाँ से यह प्लगइन पोर्ट किया गया है।
- पूरी सूचनाओं के लिए [THIRD_PARTY_NOTICES.md](./THIRD_PARTY_NOTICES.md) देखें।
**लाइसेंस**: MIT