Skip to content

feat(app): add the js plugin runtime and file install - #183

Merged
1morr merged 4 commits into
mainfrom
feat/js-runtime
Sep 29, 2026
Merged

1morr merged 4 commits into
mainfrom
feat/js-runtime

Conversation

@1morr

@1morr 1morr commented Sep 29, 2026

Copy link
Copy Markdown
Owner

M1 PR 9a:插件的 JS 執行環境、宿主 API v1 與從檔案安裝(ADR 0014;父任務 09-28-m1-skeleton-tracer 的擁有者決定 6、7)。

做了什麼

  • 執行環境:flutter_js 0.8.7(釘死)的 QuickJS。每個插件跑在自己的背景 isolate:
    • 背景 isolate(plugin_worker.dart)只放 QuickJS、prelude 與 crypto,是唯一 import flutter_js 的檔案。
    • 主 isolate(plugin_runtime.dart)負責 spawn、轉送訊息與看門狗。宿主 API(http、storage、credentials、log)在主 isolate 執行,網域與插件 id 的檢查只在這一邊。
  • 看門狗:呼叫逾時先送存活探測。
    • 有回應:這次呼叫回 NetworkError,插件照常。
    • 沒回應,或 isolate 已結束:插件標成 unresponsive,停用到 App 重啟。
    • 這是擁有者 2026-09-30 確認的版本,ADR 0014 已補。
  • 安裝檔:單一 .js 檔,開頭的 /* ==FMP Plugin== {json} ==/FMP Plugin== */ 是 manifest。App 不執行腳本就能先驗證 manifest;載入後再檢查腳本的匯出是否和宣告的能力一致。
  • SourcePlugin:只有 search 與 resolveStream,用 v1 DTO,進出兩個方向都驗證。插件邊界上所有錯誤都轉成 AppError。另附給插件作者用的 types/fmp-plugin.d.ts。
  • 安裝:PluginInstaller 寫進 installed_plugins 並註冊到插件清單。dev 的開發入口是 --fmp-dev-plugin=<路徑> 或環境變數 FMP_DEV_PLUGIN;prod 一律不讀,有測試守著。
  • 測試插件:fmp-test,回傳合成資料、不發網路請求,串流指向自己產生的 tone.wav。
  • CI:flutter test 直接載入 flutter_js 內附的 .so,在 Linux runner 上跑真的 QuickJS。

驗證

  • dart format、dart analyze --fatal-infos、flutter analyze:零問題。
  • flutter test:449 個通過,runtime 測試連跑兩次都穩定。
  • 哨兵:13 條規則全部報出。build_runner:沒有實質變動。
  • 建置:Windows dev/prod、Android dev/prod 的 debug 都成功。
  • 實機:
    • Windows dev:用開發入口裝上 fmp-test 1.0.0;不帶參數重開後,插件從資料庫重新載入。
    • Windows prod:帶同樣的參數,不載入插件。
    • Android 模擬器 dev:前兩項都通過。模擬器上的 prod 套件是舊版,所以 prod 沒裝上去驗。
  • opus 審查修了 8 個缺陷,每個都有修正前會紅的測試:
    • expiresAt 超出範圍;
    • spawn 失敗;
    • 參數編碼失敗時殘留呼叫;
    • 同時兩次 register 互相覆蓋;
    • 插件弄壞 JSON 後被停用;
    • 參數傳函式時錯誤型別不對;
    • retryAfterSeconds 溢位;
    • prod 讀開發入口沒有閘門。

量測數字(debug 模式):

項目 Windows Android 模擬器
建立 runtime 1.5–2 ms 13–18 ms
一次 search 往返 約 0.9 ms 11–20 ms
3 個 runtime 增加的記憶體 4.6–9 MB 約 3.3 MB

詳見 .trellis/tasks/archive/2026-09/09-30-js-runtime/research/notes.md。

Review 指南

  • 先看 app/lib/plugins/runtime/:
    • worker_protocol.dart 是訊息格式;
    • plugin_runtime.dart 是看門狗與呼叫的生命週期;
    • plugin_host.dart 是安全邊界。
  • 再看 source_dto.dart 的驗證,和 manifest/plugin_file.dart 的解析。
  • 已知限制,寫在 app/AGENTS.md:
    • Isolate.kill 停不掉卡在 QuickJS 原生碼裡的執行緒,它會佔著一個核心直到 App 結束;
    • 插件如果一直呼叫宿主 API,會一直回應探測,不會被停用。這條記在 M1 implement.md 的後續。

@1morr
1morr merged commit 7f4cb39 into main Sep 29, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant