Simlock:開源裝置租約工具,讓 AI 代理測試各用一台模擬器

Callstack 於 10 月 30 日開源 Simlock,以裝置租約協調 AI 代理的 iOS 與 Android 測試:代理先申請才操作裝置,忙碌時排隊、釋放後清理,租約預設 15 分鐘自動續租,多台機器可透過 Gateway 接成裝置池。獨占成立靠代理遵守租約規則,工具不封鎖 xcrun simctl 等直接控制。

用 AI 摘要這篇文章:

兩個 AI 代理在同一台 Mac 上跑手機 App 測試,常常各自從系統清單挑中同一台模擬器,接著一邊把另一邊剛裝好的版本覆蓋掉、把測到一半的裝置重設,失敗看起來像隨機出現,程式本身卻沒有錯。Callstack 工程師 Szymon Chmal 在 10 月 30 日傍晚(台北時間)開源了專門處理這種衝突的工具 Simlock,版本 0.4.0,MIT 授權、免費。GitHub 正式發布、X 宣告貼文與 npm 套件上架落在同一天的三個時間點:18 時 39 分、18 時 41 分、18 時 44 分,節奏很清楚,這是準備好對外的一次發布。

Simlock 的做法是租約制:代理先申請一台裝置,拿到一台已開機、檢查過、而且在你歸還之前別人不會碰的 iOS 模擬器、Android 模擬器或實體手機。排不到就排隊,用完就清理。概念不複雜,真正值得在接入前讀懂的是它的獨占是怎麼成立的,因為官方文件把話說在前頭:這套機制不做強制隔離,獨占要靠每個代理守約才成立。

代理拿到的裝置,跟自己開的有什麼不同?

代理向 Simlock 要裝置時要講清楚條件,例如「iPhone 16、iOS 18.4」。文件裡的完整流程是:有符合條件的空閒裝置就立刻拿到;沒有就現場建立一台,上限是機器跑得動的數量;機器滿了,請求進入佇列等別人歸還。等待過程不是黑箱,命令會在 stderr 逐行輸出 JSON 進度,階段有排隊中、清理中(上一個人用完的裝置正在抹除)、準備中(provisioning)、開機中,各自帶估計秒數,代理能分辨「還沒拿到裝置」與「App 壞了」是兩回事。

Simlock 官方文件的 What a lease gives you 段落截圖,條列租約的五種情況:有空閒裝置立即取得、沒有就建立到機器上限、滿載則排隊、釋放後先清除再交給下一個代理、代理當機則靠 TTL 到期回收,段末附 Simlock works by agreement, not by force 的說明Pin
官方文件對租約的定義:五種情況各是立即取得、建立、排隊、清除後交接、TTL 到期回收;末段同時聲明它靠約定運作、不隱藏其他裝置也不封鎖 xcrun simctl。(來源:Simlock 官方文件)

拿到的是一整份 JSON 授權紀錄,裡面最重要的三個欄位是租約識別碼(用來續約與歸還)、裝置位址(iOS 是模擬器的 UDID,Android 是 adb 序號)、以及到期時間。租約預設 15 分鐘,最長可以要求 4 小時,兩個上限管理者都能調。讓命令保持常駐的話,它每三分之一租約時間自動續租一次,正常工作不會每幾分鐘就被收回。

問題在於許多代理每次執行命令都開一個用完即丟的 shell,常駐命令跟著這種短命 shell 跑,shell 一結束裝置就被收回,測試根本跨不過下一步。官方給了兩條路:--detach 讓命令印出租約後就退出,租約留下來給後續步驟用,但續租與歸還要自己負責;--bind-pid 指向代理本身那個活得比較久的主處理程序,讓租約跟著整段工作走,工作結束才釋放。哪種合適,取決於代理的工作週期橫跨幾次命令。另外有個全域地雷:release --all 會結束整台機器上的所有租約,包括別的代理正在用的,文件把它標為警示等級,一般收尾永遠只該用自己的租約識別碼歸還。

歸還之後才是容易忽略的一段:Simlock 在背景把裝置清乾淨才交給下一個人,iOS 模擬器的清理需要數十秒,所以歸還命令返回不代表下一台裝置已經就緒。先前裝的 App、檔案與設定全部消失,測試需要的資料要靠自己的流程重新放進去。實體手機的處理不同:機器本體不會被重設,只會解除安裝當初註冊進裝置池之後才裝上去的 App。

排隊也有兩種離開方式。加 --timeout 10m 會在十分鐘後放棄並回傳 QUEUE_TIMEOUT 錯誤;加 --no-wait 則完全不排,直接以 NO_CAPACITY 失敗。對有固定執行期限的持續整合工作,先想好能等多久再送請求,比讓整批工作卡在同一個空位上划算。還有一個反直覺的設計要記住:中止等待中的命令不等於退出佇列。使用 --detach 的場合,命令被殺掉後請求仍留在佇列裡,稍後照樣獲得裝置並佔用到租約到期,事後要用請求狀態查證,不能只看終端視窗關了沒有。

租約異常結束的處理也交代得完整。代理當機沒有歸還,租約會在 TTL 到期後自行結束,裝置不會被永久占用;裝置本身崩潰,Simlock 會先嘗試在同一份租約下重新開機,代理會收到警告與恢復通知,但先前啟動的 App 得重來。比較特別的是連線中斷的設計:背景程式連不上時租約並不釋放,命令會明講這份租約仍是你的、還剩多久到期,重新連上後可以接著續租;租約甚至能活過背景程式重啟。對跑長測試的團隊,這代表斷線不等於工作白費,把租約識別碼與到期時間記下來,就有機會接回去。

「獨占」到底靠什麼成立?

拆開看是三件事疊出來的。最底層是一本帳:每份租約屬於一個申請者(requester),預設一個申請者同時只能持有一份租約,第二次申請會被 REQUESTER_ALREADY_LEASED 擋下,管理者可以用設定放寬。第二件事是隔離:Simlock 自己管理的模擬器放在獨立的裝置集與獨立的 adb 伺服器裡,Xcode 的裝置清單與平常的 adb devices 都看不到它們,代理之間不會在系統清單裡撞車。第三件事是擋破壞:安裝與操作都要透過 simlock simctl 與 simlock adb 這兩個包裝指令,會弄壞裝置的命令(erase、delete、kill-server 這類)直接拒絕。

三件事合起來,守規則的代理拿到的是乾淨、專屬、可預期何時收回的裝置,裝置測試的隨機干擾變成可以對帳的紀錄。

為什麼官方說它靠約定,不靠強制?

因為這三層防護都有邊界,而文件沒有迴避。租約管理不會隱藏系統裡本來就存在的其他模擬器,也不封鎖直接執行 xcrun simctl;它協調的是使用順序,不提供作業系統層級的強制隔離。就算走包裝指令,文件也明講:別的代理的裝置在這些指令裡同樣看得到,用了 booted 這種模糊指定或漏掉 -s,還是可能摸到不屬於你的裝置。所以官方在文件裡直接給了一段放進 AGENTS.md 的規則範本,第一句就是「絕不直接使用 xcrun simctl、adb 或 emulator,絕不使用沒有租來的裝置」。獨占要成立,這行字得跟著部署。

另外一條邊界多人共用時才會碰到:在本機跑 CLI 連上背景程式(daemon)的身分是管理者,可以查所有人的租約、也可以代為續約或釋放任何租約。這份管理權來自只有同一個使用者讀得到的本機檔案,屬於管理便利,對外的身分隔離靠的是 HTTP 權杖那一層。要把裝置池開給團隊或外部使用,帳號與存取權要另外建,HTTP 遠端接入的申請者身分看的是管理者核發的權杖,不是請求裡自稱的名字,這部分有做好。

為什麼多開子代理,裝置沒有變多?

這是接入時最常見的誤會,答案藏在申請者識別的規則裡。在 Claude Code 與 Codex 底下,Simlock 自動取工作階段的識別碼當申請者身分,同一個階段裡的所有命令與子代理共用同一個身分,所以派出十個子代理,拿到的是同一份租約、同一台裝置。要讓子代理各用一台,得請管理者調高每個申請者的租約上限,或改用其他啟動方式明確給每個代理自己的 SIMLOCK_AGENT_ID。

身分設錯還有兩種安靜的失敗。把固定的 SIMLOCK_AGENT_ID 寫進共用的 shell 設定檔,所有工作階段變成同一個申請者,第二個要裝置的直接被拒絕;反過來完全沒設身分時,每條命令各自是不同申請者,一人一租約的限制形同虛設,也沒人說得清哪台裝置在誰手上。還有個 Codex 專屬的細節:Codex 不把工作階段識別傳給 MCP 伺服器,所以在 Codex 底下,MCP 伺服器與 CLI 是同一個階段的兩個不同申請者,同一階段可能同時持有兩份租約,官方建議一個階段只用其中一種。

Simlock 官方文件的代理身分表格截圖,三欄為 Agent tool、CLI、MCP server,Claude Code 兩欄都是 claude-code 加 session id,Codex 的 CLI 是 codex 加 session id 但 MCP 是 mcp 加 process id,其他工具需設定 SIMLOCK_AGENT_IDPin
申請者身分怎麼來:Claude Code 的 CLI 與 MCP 共用 session id,Codex 的兩者是不同身分,其他工具要自己設 SIMLOCK_AGENT_ID。(來源:Simlock 官方文件)

入口本身有四條路:shell 命令的 CLI、給 MCP 相容代理的伺服器(MCP 是代理呼叫外部工具的通用標準,這裡提供列裝置、租裝置、還裝置、查狀態四個工具)、給其他機器上代理的 HTTP API,以及給 Node.js 程式的套件客戶端。想看這條 MCP 生態系裡還有哪些工具可以接,可以參考先前實測過的 Awesome MCP Servers 收錄規則分析。查狀態那個工具官方特別標註「任何時候呼叫都安全」,考慮的正是代理的實際處境:長對話的脈絡被壓縮後,代理可能忘了自己手上是否還有裝置,先查再動作比自己猜可靠。管理者另外有一個網頁主控台可以看裝置、租約與佇列。對已經在用 AXe 這類 iOS 模擬器自動化工具的團隊,Simlock 補上的是它們不管的那層:誰在哪段時間有權用哪台裝置。

一台 Mac 不夠用時怎麼擴?

Simlock 的起點是一台機器,裝了 CLI 的機器就是一台 worker,背景程式在第一次用到時自動啟動,資料放在 ~/.simlock,也不註冊開機自啟。單機不夠時,另一個常駐程式 gateway 可以把多台 Mac 與 Linux 機器接成一個裝置池:代理只對 gateway 的單一位址提出請求,gateway 挑一台能服務的 worker,裝置實際在那台 worker 上跑。worker 主動向外連線,不必開任何對內連接埠,這對網路管理是好消息。

兩個成本要提前算。檔案路徑是 worker 上的路徑:透過 gateway 裝 App,安裝指令裡的 .app 檔案必須真的存在於持有裝置的那台 worker 上,剛在自己機器編好的建置產物不會自動飛過去,跨機部署要自己把產物送到位。runtime 也一樣,gateway 不會在核發租約時順手下載缺少的 iOS runtime 或 Android 系統映像,請求會以 RUNTIME_MISSING 失敗,映像要預先裝在 worker 上。管理面則有分工:gateway 是另一個獨立的常駐程式,有自己的資料夾與權杖,管裝置的命令(清理、醫生檢查、增刪裝置)在 gateway 上會直接失敗,必須回到 worker 執行;管理者得到的是一個總覽主控台,所有機器、租約與等待中的請求一頁看盡。文件也直說:一台機器裝得下所有測試,就不需要 gateway。

接入前要準備什麼,還有什麼不確定?

環境需求照官方需求表列:CLI 與 gateway 要 macOS 或 Linux 加 Node.js 22.13 以上;iOS 模擬器只有 macOS 能跑(Xcode 要裝好、授權要同意、至少裝一個 iOS runtime);Android 模擬器 macOS 與 Linux 都行(要 Android SDK 加 JDK)。Simlock 不會替你裝任何平台工具,裝完工具後要停掉背景程式再跑一次 simlock doctor,因為平台工具只在程式啟動那一刻偵測。doctor 的輸出按平台分行列問題,只測 iOS 的團隊可以把 Android 的提醒當參考不當阻擋;--fix 只修 Simlock 自己的紀錄(例如清掉已刪除的裝置、結束過期卻標成有效的租約),不會碰租約中的裝置,也不會幫你裝 Xcode。實體手機要註冊才能進池:iPhone 需 iOS 17 以上、USB 連線、信任電腦並開啟開發者模式,Android 手機則要允許 USB 偵錯;實體裝置不占容量計數,是另有的一條佇列。

資源行為有兩個預設值得知道。暖池(warm pool)預設開啟,記住過去 24 小時內各類裝置的尖峰用量,把用過的裝置保持開機待命,下次租用不必等開機;容量上限依 CPU 與記憶體推估,會保留 4 GB 給其他工作。清理分兩段:閒置 10 分鐘的裝置關機省記憶體,關機閒置 60 分鐘刪除省磁碟,剩餘磁碟低於 10 GB 時刪除加快到 10 分鐘。實體手機永遠不會被刪。0.4.0 的版本內容也對得上這些行為:HTTP 控制面、需要時才下載 runtime、省記憶體的選配 slim 模式都在這一版進來,slim 模式的作法(關掉測試用不到的背景服務)沿用開源專案 simslim 的服務清單。

成本面要分開看:工具本身 MIT 授權、永遠免費,但 Mac、實體手機、電力與代理訂閱都是自己的支出,「免費開源」買不到硬體。專案底細也透明:repo 從 7 月 17 日建立,npm 8 月 21 日首發,這次 0.4.0 才公開宣告,站上五個前人元件(iOS 畫面串流的 serve-sim、Android 畫面串流的 scrcpy、實機串流的 idevice、代理裝置整合 agent-device、slim 模式的 simslim)全部列名致謝。關注度方面,截至 10 月 31 日下午 repo 已有四百多顆星、七十幾個開啟中的議題,第一天的熱度是真的,接下來的維運才是考驗。要注意的是版本狀態:0.4.0 仍在 1.0 之前,發布當天 repo 的合併編號已經到一千零九十幾,修改節奏很快,指令名稱與預設值可能變動,接入前以 GitHub releases 與官方文件核對當下版本,該鎖版就鎖版。對照 Dagger 把 CI 拆成代理可重跑的單元的思路,兩者要解的都是「並行工作互相踩踏」,Simlock 踩的是裝置這一層。

Simlock 把「誰搶到裝置」的隨機問題換成「誰在排隊、排多久」的可追蹤問題,前提是團隊裡每個代理都遵守同一份租約規則;規則寫進代理指引的那一刻,這套獨占才真正生效。

Sliven 褚崇名
Sliven 褚崇名

每日分享科技新知、免費資源以及 WordPress、虛擬主機相關主題,任何問題歡迎在科技月球下方留言,或是發送 Email 至 [email protected] 與我聯繫。

文章: 2211

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *


Share to...