diff --git a/README.md b/README.md index 48bff1b..6b4bcf9 100644 --- a/README.md +++ b/README.md @@ -9,15 +9,19 @@ client drive the whole thing. ## Screenshots -Taken on a Galaxy S9 (Android 10, arm64-v8a) running the release build. +Taken on a Galaxy S9 (Android 10, arm64-v8a). | Home | Editor and console | File manager | |---|---|---| | Home screen with project stats and starter profiles | Editor running a Python script with its output in the console | File manager showing the whole project tree | -| SQLite browser | Shell | -|---|---| -| SQLite browser listing tables and query results | Shell session in the app sandbox | +| SQLite browser | Shell | Services | +|---|---|---| +| SQLite browser listing tables and query results | Shell session in the app sandbox | Supervisor showing a running service with uptime, CPU and heap | + +| System | +|---| +| System screen reporting Android version, ABIs, interpreter and memory | ## Build diff --git a/ROADMAP.md b/ROADMAP.md new file mode 100644 index 0000000..813510d --- /dev/null +++ b/ROADMAP.md @@ -0,0 +1,217 @@ +# وضعیت و نقشهٔ راه runcode + +آخرین به‌روزرسانی: ۲۰۲۶/۰۹/۲۸ — نسخهٔ منتشرشده: **v1.3.0** (`versionCode 4`) + +- مخزن: https://github.com/hami9/runcode +- آخرین نسخه: https://github.com/hami9/runcode/releases/tag/v1.3.0 +- شاخهٔ اصلی: `main` روی کامیت `929cc23` + +runcode یک محیط اجرای برنامه، ادیتور و میزبان سرویس روی خود اندروید است: پروژه‌ها داخل sandbox +برنامه زندگی می‌کنند، روی CPython تعبیه‌شده یا یک وب‌سرور داخلی اجرا می‌شوند، و یک supervisor +آن‌ها را زنده نگه می‌دارد. شل، مرورگر SQLite، بکاپ و یک پل MCP هم دارد تا هوش مصنوعی بتواند +کل این‌ها را از بیرون کنترل کند. + +--- + +## ۱. الان چه چیزی واقعاً کار می‌کند + +| بخش | وضعیت | +|---|---| +| CPython 3.12 (Chaquopy) | مفسر واقعی، کتابخانهٔ استاندارد، `sqlite3`، `ssl` | +| پکیج‌های pip | `python-telegram-bot` 21.9 و `requests` داخل APK؛ بقیه از بلوک `pip` در `app/build.gradle.kts` | +| وب‌سرور استاتیک | سرور HTTP/1.1 روی raw socket | +| Supervisor | سیاست restart، backoff، wake lock، foreground service، محدودیت CPU/حافظه/بی‌کاری | +| مانیتورینگ | CPU هر سرویس از `/proc/self/task//stat`، حافظه از `Debug.MemoryInfo` | +| فایل منیجر | ساخت/تغییر نام/جابه‌جایی/حذف، ورود و خروج فایل با انتخابگر سیستم | +| ورود و خروج پروژه | zip با تنظیمات، و وارد کردن zip دلخواه (مثل دانلود از گیت‌هاب) | +| ادیتور | شماره خط همگام، اسکرول دنبال مکان‌نما، undo/redo، ذخیرهٔ خودکار هنگام تعویض تب | +| شل | `/system/bin/sh` داخل sandbox — بدون root، بدون PTY، بدون job control | +| پل MCP | ۱۶ ابزار، JSON-RPC روی HTTP، توکن اجباری، پیش‌فرض فقط loopback | +| بکاپ | آرشیو `.rcpkg` با checksum که واقعاً بررسی می‌شود | +| امنیت | مخزن رمز با پشتوانهٔ Android Keystore، حذف رمز از لاگ‌ها، امضای v2+v3 | +| JavaScript / PHP | پیاده نشده (توضیح در بخش ۴) | + +نیازمندی ساخت: JDK 21، Android SDK با `compileSdk 36`، و یک CPython 3.12 روی سیستم برای +اجرای pip در زمان build: + +```bash +RUNCODE_BUILD_PYTHON=/path/to/python3.12 ./gradlew assembleDebug +``` + +--- + +## ۲. کارهایی که تا الان انجام شد + +### v1.0.0 — جایگزینی شبیه‌سازی با اجرای واقعی + +نقطهٔ شروع پروژه یک «موتور پایتون» بود که واقعاً پایتون اجرا نمی‌کرد و فقط خروجی از پیش +نوشته‌شده چاپ می‌کرد، در حالی که رابط کاربری می‌گفت «Python 3.11 Available». + +- CPython واقعی با Chaquopy، خروجی خط‌به‌خط و توقف واقعی اسکریپت. +- وب‌سرور استاتیک واقعی (به جای حلقهٔ چاپ پیام) — روی raw socket، چون + `com.sun.net.httpserver` روی اندروید وجود ندارد. +- شل sandbox و پل MCP با احراز هویت توکن. +- درست شدن کرش روی اندروید زیر ۳۴، قواعد backup/data-extraction فقط-استثنا، و پایداری + supervisor و ذخیرهٔ پایگاه‌داده. + +### v1.0.1 — ربات تلگرام واقعی، پایان روشن-خاموش شدن سرویس + +- خطای سینتکس دیگر باعث حلقهٔ بی‌پایان restart نمی‌شود؛ چنین خطایی `FATAL` است و یک بار + گزارش می‌شود. +- نصب پکیج‌های pip داخل APK، پس ربات تلگرام واقعاً به `api.telegram.org` وصل می‌شود. +- ثبت کرش در فایل (`files/diagnostics/last_crash.txt`) و نمایش در صفحهٔ System. +- تأیید امضای v2+v3 و بررسی واقعی checksum بکاپ‌ها. + +### v1.2.0 — پایتون ۱۰ برابر سریع‌تر، لاگ‌های تفکیک‌شده + +- حذف `sys.settrace` که روی هر خط اجرا می‌شد و پایتون را **۲۵ برابر** کند کرده بود؛ + توقف اسکریپت حالا با `PyThreadState_SetAsyncExc` است و هزینهٔ اجرایی ندارد. +- دو سرویس همزمان خروجی یکدیگر را می‌دزدیدند (`sys.stdout` سراسری است)؛ حالا خروجی + بر اساس thread تفکیک می‌شود. +- ترتیب نوشتن لاگ روی دیسک درست شد (یک مصرف‌کننده به جای یک coroutine برای هر خط). +- CPU و حافظهٔ واقعی هر سرویس به جای اعداد ثابت hardcode شده، و محدودیت CPU/heap/بی‌کاری + با توقف خودکار سرویس متخلف. + +### v1.3.0 — فایل منیجر، ورود و خروج zip + +- **فایل منیجر** (ادیتور ← آیکون پوشه): کل پروژه را نشان می‌دهد، نه فقط `source/`. + ساخت فایل و پوشه، تغییر نام، جابه‌جایی، حذف با تأیید. پوشه‌های ساختار پروژه + (`source/`, `data/`, `config/`, `logs/`, `cache/`, `backups/`) قابل حذف یا تغییر نام نیستند. +- تغییر نام یا جابه‌جایی فایل اجرایی، پروژه را با خودش می‌برد؛ گزینهٔ «Set as entry point» + برای انتخاب فایل دیگر. +- آوردن فایل از گوشی و گرفتن کپی از هر فایل (حتی دیتابیس) روی گوشی، با انتخابگر سیستم. +- خروجی و ورودی zip پروژه؛ چرخهٔ خروجی ← حذف ← ورود روی دستگاه بایت‌به‌بایت یکسان بود. + zip دانلودشده از گیت‌هاب هم به پروژه تبدیل می‌شود (پوشهٔ پوششی باز می‌شود، فایل اجرایی + تشخیص داده می‌شود). +- ورودی‌ها سخت‌گیرانه بررسی می‌شوند: مسیر بیرون از پروژه رد می‌شود (با zip مخرب تست شد)، + حجم بازشده محدود است، و پروژهٔ وارد‌شده هرگز خودکار با بوت اجرا نمی‌شود. +- خروجی‌ها هیچ رمزی ندارند: متغیر محرمانه فقط به شکل ارجاع `${SEC_...}` نوشته می‌شود. +- MCP: چهار ابزار تازه (`create_directory`, `rename_path`, `delete_path`, `set_entry_point`)، + و تغییرات MCP بلافاصله در ادیتور دیده می‌شوند. +- باگ‌های پیداشده در تست دستگاه: کرش هنگام باز شدن تب دوم (indicator پیش‌فرض Material3)، + حرکت نکردن شماره خط‌ها با اسکرول، از دست رفتن تغییرات ذخیره‌نشده با تعویض تب، پاک شدن + فایل با ساخت فایل هم‌نام، و بیرون افتادن دکمه‌های Run و حذف/بکاپ از صفحه در عرض گوشی. + +--- + +## ۳. کارهای باقی‌مانده + +هر فاز یک انتشار کوچک و مستقل است. هیچ فازی به فاز بعد وابسته نیست، پس ترتیب قابل +تغییر است و کار فشرده لازم نیست. + +### اولویت ۰ — صفحهٔ تنظیمات پروژه و رمزها (پیشنهاد من برای قدم بعدی) + +این تنها موردی است که خارج از نقشهٔ راه اولیه اضافه شده، چون در تست دیده شد که یک کمبود +واقعی است: **هیچ جایی در برنامه برای وارد کردن متغیر محیطی و رمز وجود ندارد.** قالب ربات +تلگرام دنبال `SEC_BOT_TOKEN` می‌گردد، مخزن رمز Keystore آماده است، ولی کاربر راهی برای +پر کردنش ندارد. بدون این، ربات تلگرام عملاً برای کاربر راه نمی‌افتد. + +- ویرایش متغیرهای محیطی پروژه (افزودن، حذف، علامت‌گذاری به عنوان محرمانه). +- مقدار محرمانه در مخزن Keystore ذخیره شود و هرگز به شکل متن در پایگاه‌داده یا لاگ نرود. +- ویرایش تنظیماتی که الان فقط هنگام ساخت پروژه قابل تعیین‌اند: پورت، سیاست restart، + اجرا با بوت، و محدودیت‌های CPU/heap/بی‌کاری. +- ابزار MCP معادل برای متغیرهای غیرمحرمانه (مقدار محرمانه هرگز از پل خوانده نشود). + +### فاز ۳ — عیب‌یابی برنامه و شبکه + +یک دکمهٔ «Run diagnostics» که گزارش قابل کپی بدهد، در صفحهٔ System کنار کارت کرش. + +- بررسی برنامه: نسخهٔ اندروید و ABI، فضای آزاد و RAM، اجازهٔ نوتیفیکیشن، معافیت بهینه‌سازی + باتری، وضعیت foreground service، راه افتادن مفسر پایتون، آخرین کرش. +- بررسی شبکه: IP شبکهٔ محلی، پورت‌های رزرو‌شده، اینکه پورت هر سرویس در حال اجرا واقعاً + اتصال می‌پذیرد یا نه، resolve شدن DNS، دسترسی HTTPS، و خودآزمایی پل MCP با یک + `GET /health` روی loopback. +- اشتراک‌گذاری لاگ‌ها: `LogManager.exportLogs` نوشته شده اما به جایی وصل نیست؛ به share sheet + وصل شود (رمزها همان موقع ورود حذف شده‌اند). +- ابزار MCP: `run_diagnostics`. + +**تمام است وقتی:** گزارش، نوتیفیکیشن رد‌شده، سرویسی که پورتش واقعاً گوش نمی‌دهد، و قطع بودن +شبکه را درست تشخیص بدهد — با خاموش و روشن کردن هرکدام روی دستگاه تأیید شود. + +### فاز ۴ — بکاپ در پوشه‌ای که خودت انتخاب می‌کنی + +با Storage Access Framework، نه API گوگل درایو: کاربر یک بار پوشه را انتخاب می‌کند و می‌تواند +درایو، OneDrive یا حافظهٔ داخلی باشد. بدون OAuth، بدون پروژهٔ کلود، بدون کلید API. + +- `ACTION_OPEN_DOCUMENT_TREE` یک بار، با نگه داشتن دسترسی به شکل دائمی. +- `exportTo(uri)` و `importFrom(uri)` دور توابع فعلی بکاپ؛ بررسی checksum موجود همان تضمین + را به فایل وارد‌شده هم می‌دهد. +- بکاپ خودکار اختیاری (زمان‌بندی یا بعد از N ویرایش)، پیش‌فرض خاموش. + +**تمام است وقتی:** بکاپی که در پوشهٔ درایو نوشته شده روی دستگاه دوم بازگردانی شود و checksum +همچنان درست باشد. + +### فاز ۵ — گیت و گیت‌هاب + +گیت کامل روی دستگاه با `dulwich` که پایتون خالص است و در همان بلوک pip جا می‌شود. باینری +گیت بومی روی اندروید ممکن نیست. + +- `status`, `add`, `commit`, `log`, `branch`, `checkout`, `remote`, `clone`, `push`, `pull` + در `app/src/main/python/runcode_git.py` به سبک `runcode_runner.py`، با `git/GitManager.kt`. +- توکن گیت‌هاب در همان مخزن رمز Keystore؛ هرگز در لاگ یا خروجی MCP. +- یک مقصد ناوبری تازه: صفحهٔ Git با وضعیت فایل‌ها، جعبهٔ commit، تغییر branch و push/pull. +- ابزارهای MCP: `git_status`, `git_commit`, `git_push`. + +این بزرگ‌ترین فاز است؛ اگر طول کشید، دو انتشار شود. + +**تمام است وقتی:** یک پروژه از گیت‌هاب clone شود، در برنامه ویرایش و commit و push شود، و +کامیت با نویسندهٔ درست در گیت‌هاب دیده شود. + +### فاز ۶ — دیباگر پایتون + +آخر از همه، چون سنگین‌ترین کار رابط کاربری است. + +- گسترش یک trace hook برای breakpoint روی `(file, line)`، توقف thread روی condition variable، + و نمایش متغیرهای local/global. +- کنترل‌ها: continue، step over، step into، step out، و ارزیابی عبارت در فریم متوقف‌شده. +- رابط: breakpoint در ستون شماره خط، پنل متغیرها، و call stack. +- نکته‌ای که باید در رابط گفته شود: trace hook فقط روی خطوط پایتون فعال می‌شود، پس سرویسی که + داخل یک فراخوانی C منتظر است (`time.sleep`، خواندن از سوکت) در خط بعدی متوقف می‌شود، نه + همان لحظه. +- **مهم:** از v1.2.0 دیگر trace hook به شکل همیشگی نصب نمی‌شود (۲۵ برابر کندی). دیباگر باید + آن را فقط در زمان دیباگ کردن نصب کند و بعد بردارد. + +### کارهای کوچک و بدهی فنی + +- **تست خودکار وجود ندارد:** پروژه فقط `app/src/main` دارد. حداقل تست‌های JVM برای منطق + مسیرها (`ProjectStorage.validateName`، containment path، `EntryPoints`) ارزش دارد، چون + این‌ها مرز امنیتی‌اند. +- کد نوشته‌شده اما وصل‌نشده: `LogManager.exportLogs` (فاز ۳)، + `PortManager.getActiveReservations` (فاز ۳)، `SecretStore.getAllSecretKeys` (اولویت ۰). +- صفحهٔ «لایسنس‌های متن‌باز» داخل برنامه؛ فایل `THIRD_PARTY_LICENSES.md` هست ولی از داخل اپ + دیده نمی‌شود. +- تب‌های باز ادیتور بعد از بسته شدن برنامه حفظ نمی‌شوند. +- هایلایت سینتکس در ادیتور وجود ندارد. +- نوار ناوبری با ۸ مقصد پر است و افقی اسکرول می‌شود؛ فقط فاز ۵ مقصد تازه اضافه می‌کند و + بقیهٔ قابلیت‌ها باید داخل صفحه‌های موجود جا بگیرند. + +### چیزهایی که حذف شدند و دلیلش + +- **افزودن زبان و کامپایلر جدید:** روی اندروید بدون root، کامپایلر باید از قبل داخل APK + کامپایل شده باشد، و اجرای یک باینری دانلود‌شده از API 29 به بعد مسدود است. این قابلیتی + نیست که برنامه بتواند صادقانه ارائه کند. +- **API گوگل درایو:** با SAF همان کار بدون OAuth و کلید API انجام می‌شود (فاز ۴). +- **بهینه‌ساز CPU و RAM برای کل گوشی:** هیچ API اندرویدی وجود ندارد که یک برنامه حافظهٔ + برنامهٔ دیگر را آزاد کند یا CPU را تنظیم کند. آنچه ساخته شد، مدیریت سرویس‌های **خود + runcode** است (محدودیت و توقف خودکار در v1.2.0) و رابط کاربری هم همین را می‌گوید. + +--- + +## ۴. قواعدی که در ادامه باید حفظ شوند + +این‌ها از خطاهای واقعی همین پروژه درآمده‌اند: + +1. **هیچ ادعایی در رابط کاربری بدون پشتوانهٔ کد.** ایراد اصلی نسخهٔ اول همین بود: موتور + پایتون شبیه‌سازی‌شده که «Python 3.11 Available» نشان می‌داد، و checksum‌هایی که گفته + می‌شد بررسی شده‌اند ولی هرگز مقایسه نمی‌شدند. اگر فازی نیمه‌کاره منتشر شد، رابط باید + همان را بگوید. +2. **هر فازی که قابلیت اضافه می‌کند، ابزار MCP هم اضافه می‌کند**، تا پل عقب نماند. +3. **هر فاز با یک انتشار امضاشده تمام می‌شود:** `assembleRelease`، تأیید v2+v3 با + `apksigner verify`، و پیوست APK به انتشار گیت‌هاب. +4. **هیچ کلید و رمزی در مخزن نیست.** `keystore.properties`، فایل keystore و `.env` در + `.gitignore` هستند و باید بمانند. امضا از فایل محلی یا متغیر محیطی خوانده می‌شود. +5. **تأیید روی دستگاه، نه روی کاغذ.** ترتیب کار: build و `lintDebug` بدون خطا، نصب روی + امولاتور API 36، اجرای قابلیت از رابط کاربری، همان قابلیت از پل MCP، خواندن + `files/projects//logs/runtime.log` با `run-as` به عنوان حقیقت نهایی، و در آخر نصب + نسخهٔ release امضاشده روی گوشی واقعی. نسخهٔ release دیباگ‌پذیر نیست، پس هر چیزی که + `run-as` لازم دارد باید اول روی debug تأیید شود. diff --git a/docs/screenshots/services.png b/docs/screenshots/services.png new file mode 100644 index 0000000..106867b Binary files /dev/null and b/docs/screenshots/services.png differ diff --git a/docs/screenshots/system.png b/docs/screenshots/system.png new file mode 100644 index 0000000..92040d1 Binary files /dev/null and b/docs/screenshots/system.png differ