sandbox_device
ボックスに Android エミュレーターか iPhone シミュレーターを借ります。ボックスの画面に表示されます。ボックスのデバイスの一覧と、1 台の返却もできます。
| 入力 | 出力 |
|---|---|
id、action(attach、list、release、reconnect、restart)、platform(android または ios)、model、osVersion、deviceId |
デバイスの deviceId、ボックス内での serial、model、osVersion、status。attach は bootTimings と egressIp も返す。list はボックスの devices とデバイスホストの hosts。reconnect はそのデバイスを reconnected: true 付きで、restart は restarted: true 付きで返す |
attach はボックスに別のマシンで動くスマホを貸し出し、起動が終わるまで待ちます。platform: "android" なら Android エミュレーター、platform: "ios" なら Mac 上の iPhone シミュレーターです。画面はボックスの仮想ディスプレイに映るので、sandbox_shot、録画、引き継ぎ、box の sandbox_review にも写り、人はそこでタップや入力ができます。1 つのボックスに最大 4 台つなげます。release で 1 台返却、ボックスを止めると全部返却されます。接続して動いている間は分単位で課金されます。スマホはボックスと一緒に凍結・解凍されます(ボックスの状態)。ボックスが 10 分使われないと先にスマホ、次にボックスが凍結され、凍結中のスマホはデバイスの枠を使わず課金もされません。Android エミュレーターはメモリごと保存されるので、戻ると元の画面のままです。iPhone シミュレーターはシャットダウンされ、インストールしたアプリとそのデータ、Mac 上の /work は残りますが、アプリは起動し直します。ボックスを解凍する呼び出しは、実行の前に同じホストでスマホを戻し、同じ serial でつなぎ直します(Android で約 35 秒、iPhone で約 50 秒。両方同時に戻ります)。そのときホストに空きがなければスマホは凍結されたままで(list に status: "frozen" と出ます)、ボックスが使われ続けている間(直近 10 分以内に呼び出しがある間)に空きが出しだいつながります。release で破棄できます。list は凍結したボックスを復帰させません。box のレビューが人を待っていても残しておく理由にはなりません。スマホは通常のアイドルの規則どおりボックスと一緒に凍結・回収され、人がレビューを開くとボックスと一緒に起きます。そのプラットフォームのデバイスがすべて使用中のときは、attach が all N <platform> device slots are in use; try again later or release a device with sandbox_device release で失敗します。デバイスホストにない osVersion は、起動する前に 400 osVersion <v> is not available: the <platform> device host has <version>; attach without osVersion to use it で失敗します。黙って別のバージョンになることはありません。待っている間、進捗メッセージがいまどの段階か(iPhone なら貸し出し用ユーザーの作成、シミュレーターの作成、初回起動、Safari の暖機)を伝え、結果の bootTimings に各段階の所要時間が入ります。結果の egressIp はスマホ自身のインターネット通信の出口 IP です。ボックスの IP とは違うので、IP の許可リストや地域判定はこちらで確認してください(スマホには curl がなく、ボックスで curl するとボックスの IP が返ります)。
list は { "devices": [...], "hosts": [...] } を返します。devices にはボックスのスマホとその status(ready、frozen、offline、restarting など)が並び、単に ready でないものには note が付きます。直近 1 時間に動かなくなったものも status: "failed" と reason 付きで並びます(エミュレーターがクラッシュを繰り返すとこうなります。新しく attach してください)。hosts はプラットフォームごとに、デバイスホストが online か、空きが何台(freeSlots)か、起動できる osVersions(osVersion にはこのどれかを渡すか、省略して既定の版を使います)、スマホの通信の出口 egressIps を示します。1 台もオンラインでないときは lastHeartbeatAt と note が付きます。そのプラットフォームの attach は戻るまで失敗しますが、これは ParallelSandbox 側の問題で、あなたのプロジェクトのせいではありません。そのスマホなしでテストを計画するか、あとで試してください。スマホ向けのビルドに時間をかける前に確認してください。toolchains: ["android"] で起動したボックスでは、Android のホストがオンラインでなければ sandbox_start の next もそう伝えます。
Android 版を人に試してもらいたいのにエミュレーターが借りられないときは、APK そのものを渡して本人のスマートフォンに入れてもらいます。sandbox_review にファイルの入口はないので、ページとして渡します。APK をフォルダーに置き、それへのリンクを持つ index.html(<a href="app-debug.apk" download>インストール</a>)を横に置いて、バックグラウンドでサーバーを起動し(cd /work/apk && python3 -m http.server 8090、background: true)、open: "web"、port: 8090 で sandbox_review を呼びます。what には、Android のスマートフォンで開いて APK をダウンロードし、Android に聞かれたらブラウザーからのアプリのインストールを許可するよう書きます。debug ビルドはこれで入ります。同じアプリのストア版が入っているスマートフォンでは署名が違うので、先にそれをアンインストールします。
ボックス内の adb から届かなくなった Android デバイスは自動で戻ってきます。トンネルが切れたら、ボックスが約 20 秒以内に気づいてつなぎ直し、戻るまで 30 秒ごとに再試行します。エミュレーター自体がクラッシュしたり 1 分間 adb に応答しなかったりすると、デバイスホストが同じエミュレーターをその場で再起動し、serial、入れたアプリ、データはそのまま残ります(コールドブートで約 1 分)。そのあとボックスがつなぎ直します。その間 list には status: "offline" とその adb の状態(ボックスが自分でつなぎ直したことがあれば autoReconnects)、または status: "restarting" が出ます。再起動したデバイスには restarts、restartedAt、restartReason が付きます。sandbox_device { "id": "<id>", "action": "reconnect", "deviceId": "<deviceId>" } はすぐにつなぎ直し、reconnected: true 付きで返します。15 分以内に 4 回目のクラッシュをしたエミュレーターはもう再起動せず、failed として並びます。アプリが原因で落ちていることが多いので、落ちる前に adb logcat を確認してから、新しく attach してください。
sandbox_device { "id": "<id>", "action": "restart", "deviceId": "<deviceId>" } はスマホをその場で再起動し、起動し終わってから返します(restarted: true)。serial、入れたアプリ、データはそのままで、開いていたアプリは起動し直しになります。Android エミュレーターは再起動(adb reboot のあと、ボックスがつなぎ直し、起動完了を待って画面を起こします)、iPhone シミュレーターはシャットダウンして起動し直します。simctl ui {udid} appearance dark のように新しい起動で効く設定はここで反映され、閉じられないシステムダイアログも消えます。この操作ができる前に起動したボックスは、イメージがデバイスの再起動に対応していないと返します。
Android(約 35 秒):model を指定しなければ pixel_9 プロファイルの Android 15 です(例:pixel_7 も指定可)。ボックス内の adb devices に返された serial(emulator-5554、2 台目は emulator-5556)で見え、レビューや引き継ぎの間は人がタップや入力もできます。
ボックスには artemis-adb(モデルを含まない決定的な ADB ツール)があります:hierarchy --out FILE(画面上の要素の JSON 配列。text、content-desc、resource-id、package、parsed_bounds を含み、タップは parsed_bounds の中心へ)、tap X Y、long-press X Y、swipe X1 Y1 X2 Y2、type X Y TEXT、clear-text X Y、erase、back、key KEYCODE、launch PACKAGE、stop PACKAGE、open URL、screenshot --out FILE。
adb reverse tcp:PORT tcp:PORT はトンネル越しでも動きます。スマホの localhost:PORT がボックス内のそのポートになるので、ボックスで動かした dev server に、Metro と React Native の関係と同じ形でアプリからつながります。debug ビルド、または WebView.setWebContentsDebuggingEnabled(true) を呼んだハイブリッドアプリ(Capacitor、Cordova、Ionic)は WebView を Chrome DevTools に公開します。psbx-webview <package> [port] はそのアプリの WebView をボックスの 127.0.0.1:<port>(既定 9222)に転送し、各ページの webSocketDebuggerUrl を表示します(psbx-webview だけなら WebView を持つアプリの一覧、psbx-webview chrome は Chrome 本体)。ページの URL には生の CDP、chrome-remote-interface、Puppeteer の connect({ browserWSEndpoint: <その URL> }) で接続してください。Playwright の connectOverCDP は WebView では Browser.setDownloadBehavior: Browser context management is not supported で失敗します。adb exec-out screencap -p > /work/phone.png でスマホ画面をフル解像度で保存できます。
iOS:Xcode 26.5 が入った Apple シリコンの Mac 上の iPhone シミュレーターです。model を指定しなければ iPhone 17 Pro の iOS 26.5 で、ほかのシミュレーター機種名("iPhone 17"、"iPhone Air"、"iPhone 17 Pro Max"、"iPad Pro 11-inch (M5)" など)も指定できます。Mac にある iOS のバージョンは list の hosts[].osVersions で分かります。osVersion を省略すると最新版です。iPhone の attach には 1〜4 分かかり、その大半は新しいシミュレーターの初回起動です。serial はシミュレーターの UDID です。シミュレーターはこの貸し出し専用に作った macOS ユーザーのもので、返却するとその中身ごと削除されます。Mac 側の /work のコピーとそこでビルドしたものも消えるので、もう一度 attach したら同期とビルドからやり直してください。
iOS には adb がなく、Xcode は Mac でしか動かないので、アプリは Mac 側でビルドします。ボックスの PATH には psbx-ios と、それに転送する xcodebuild・simctl のラッパーが入ります。sandbox_exec で実行してください。1 回ごとに Mac への SSH 往復があります(最初は約 3 秒、以降は 1 秒未満)。ボックス側にツールチェーンや大きい size は要りません。
psbx-iosのコマンドはどれも、Mac 上のこの貸し出し用のwork/フォルダーで動きます。これはpsbx-ios syncが埋めるボックスの/workのコピーで、パスも同じです。ボックスの/work/appは Mac ではwork/appです(psbx-ios pwdがフォルダーのフルパスを表示します)。ですからほかのコマンドには/workからの相対パスを渡します。xcodebuild -project app/Hello.xcodeproj ...や-workspace app/ios/App/App.xcworkspaceと書き、/work/app/...のようなボックスのパスは書きません。ボックスでのcdは向こうに影響しません。psbx-ios sync [PATH]はボックスのPATH(既定は/work全体。相対パスはボックスで今いるディレクトリから数え、/workの外のパスは最後の名前で置かれます)を Mac の同じ場所に写します。末尾のスラッシュの有無は関係なく、進捗と、終わったら Mac 上のフルパスを表示します。node_modulesのような大きなものはsandbox_execのbackground: trueで動かしてください。ボックスにもうないものは向こうでも消すので、ボックスで消したファイルは Mac でも消えます。ただし Mac で作られたり録画されたりしたものは別で、どの階層のdd/とDerivedData/フォルダーも、PATHの一番上のbuild/、*.mp4、*.movも Mac に残り、送られもしません。psbx-ios pull PATH [DEST]は Mac のwork/にあるファイルやフォルダーをボックスにコピーします(DESTの既定は/work/)。Mac でしか作れないものを取り出すときに使います。xcodebuild -project app/Hello.xcodeproj -scheme Hello -destination "platform=iOS Simulator,id={udid}" -derivedDataPath dd CODE_SIGN_IDENTITY=- CODE_SIGN_STYLE=Manual buildで借りたシミュレーター向けにビルドします(小さな SwiftUI アプリで約 6 秒。{udid}はこのシミュレーターの id に置き換わり、<serial>でも動きます)。成果物は Mac に残り、この例ではdd/Build/Products/Debug-iphonesimulator/Hello.appです。ボックスに戻ってくるのは各コマンドの出力(とpsbx-ios pullしたもの)だけです。ボックスのlsには見えず、psbx-ios ls dd/Build/Products/Debug-iphonesimulatorなら見えます。CODE_SIGN_IDENTITY=- CODE_SIGN_STYLE=Manual(ad hoc 署名、証明書は不要)は残してください。CODE_SIGNING_ALLOWED=NOでビルドしたアプリは動きますが、シミュレーターが署名のない launch storyboard を受け付けないため、起動画面がずっと黒いままになります。psbx-ios xcodegen DIR [ARGS]は Mac のwork/DIRで XcodeGen を動かし(既定はgenerate)、そのproject.ymlから.xcodeprojを作ります。新しいアプリでもプロジェクトファイルを手書きせずに済みます:psbx-ios sync appのあとpsbx-ios xcodegen app。psbx-ios install dd/Build/Products/Debug-iphonesimulator/Hello.app(Mac 上のパス。ボックスのパスなど存在しないパスを渡すと、パスは Mac のwork/から数えることと、psbx-ios lsで探すよう伝えます)、psbx-ios launch BUNDLE_ID、psbx-ios terminate BUNDLE_ID、画面をフル解像度で保存するpsbx-ios screenshot /work/phone.png(または> /work/phone.png)(iPhone 17 Pro で 1206 × 2622)。BUNDLE_IDはターゲットのPRODUCT_BUNDLE_IDENTIFIERです:xcodebuild -project app/Hello.xcodeproj -scheme Hello -showBuildSettings | grep PRODUCT_BUNDLE_IDENTIFIER。simctl ARGSは Mac でxcrun simctl ARGSを実行し、{udid}をこのシミュレーターに置き換えます。例:simctl openurl {udid} myapp://orders/42。psbx-ios xcrun ARGSでほかの Xcode ツールも動かせます(例:psbx-ios xcrun --sdk iphonesimulator swiftc ...)。psbx-ios boot、psbx-ios shutdown、psbx-ios ls、psbx-ios pwd、下の入力コマンド、全部を一覧するpsbx-ios helpも使えます。psbx-iosのあとがそれ以外の語だと、終了コード 126 とpsbx-lease: 不允許的指令: <語>で拒否されます。simctl privacy {udid} grant photos BUNDLE_ID(ほかにlocation、contacts、calendar、microphoneなどsimctl privacyが挙げるサービス)で権限の確認に前もって答えられます。通知はその中にないので、ダイアログで「許可」をタップする(psbx-ios tapはダイアログが出きるまで待ちます)か、テスト用ビルドで通知の許可を求めないようにしてください。simctl ui {udid} appearance darkは、デバイスをrestartしてからアプリに反映されます。- Capacitor や React Native アプリの Xcode プロジェクトは
node_modulesのパッケージを相対パスで参照します。ios/とnode_modules/の両方を含むフォルダーを同期してください(psbx-ios sync myapp)。そうしないとパッケージの依存関係が解決できません。 - Flutter アプリの iOS 版は Mac の Flutter でビルドします。バージョンはボックスの
flutterツールチェーンと同じです。psbx-ios sync appのあと、psbx-ios flutter app build ios --simulatorで Mac のapp/でflutter build ios --simulatorが走り、psbx-ios install app/build/ios/iphonesimulator/Runner.appでシミュレーターに入れます。リースで最初のpsbx-ios flutterは、そのリース用に SDK を複製するので数秒長くかかります。CocoaPods が必要なプラグインも使えます。Mac ではどのpsbx-iosの動詞でも PATH に CocoaPods と Node があります。 - React Native、Expo、Capacitor 6 以前は CocoaPods を使います。
ios/とnode_modules/の両方を含むフォルダを同期し、psbx-ios pod myapp/ios install(Mac のmyapp/iosで CocoaPods を実行。Podfile と JavaScript をバンドルする Xcode のフェーズに必要な Node もあります)のあと、xcodebuildで.xcworkspaceをビルドします。React Native は-configuration Releaseでビルドしてください。debug 版は Metro から JavaScript を読みますが、シミュレーターからボックス内の Metro には届きません。Expo は先にボックスでnpx expo prebuild --platform iosを実行します。node_modulesの Mac への同期は時間がかかります(作ったばかりの React Native アプリの 324 MB で約 4 分半)。一度同期したら、以後は変更した部分だけを同期してください。iPhone を接続中はボックスのxcodebuildが Mac に転送されるため、ボックスでnpx cap sync iosを実行するとそれも呼ばれ、App.xcodeprojが存在しないと表示されます。Web アセットはコピーされており、その後のpsbx-ios podとxcodebuild -workspaceは普通にビルドできます。 - 配布用の署名はできません。Mac に署名用の証明書はないので、App Store や TestFlight 向けの archive と App Store Connect へのアップロードは、利用者自身の Mac で行います。この Mac は iOS シミュレーターだけを動かします。macOS のデスクトップアプリはここでは試せません(貸し出しに GUI セッションがありません)。
- アプリの WebView は、ボックスから Safari の Web Inspector で開けません。出力を見るには、
background: trueのsandbox_execでsimctl launch --console --terminate-running-process {udid} BUNDLE_IDとしてアプリを起動します。アプリの stdout と stderr(Capacitor はconsole.logをここに書きます)が、アプリが終わるまでそのコマンドのログに流れます。ページからボックス内のサーバーへ送る方法もあります。 attachが返る前に Mobile Safari を一度開いて閉じるので、最初に本当に開いたときに黒い画面のまま止まりません。ページ自体の読み込みが遅いときは画面にそのページしか出ないので、迷ったらpsbx-ios screenshotで見てください。Vite の dev server は数百の別々のモジュールを配るため、Mobile Safari がボックスの URL 経由で読むと Android の Chrome よりずっと遅くなります(起動画面で 50 秒以上)。iPhone で Web アプリを試すときはvite buildしてビルド済みのファイルを配って(vite preview)ください。
ボックスの画面には、シミュレーターが毎秒 6〜10 コマ前後のライブ映像で映ります。レビューや引き継ぎの間、人はそこでタップ、スワイプ、入力ができます(中国語や絵文字を含む Unicode 入力に対応)。あなたも同じように操作できます。座標はポイント単位です(ピクセルを倍率で割った値。iPhone 17 Pro は 402 × 874)。
psbx-ios tap X Yでタップ。まず画面が止まるのを最大 2 秒待つので、遷移中やまだ滑り込んでくるシステムダイアログには当たらず、そのあと人のタップと同じ経路でタッチを送るので、ダイアログにも遮られません。psbx-ios swipe X1 Y1 X2 Y2 [秒]でスワイプ。省略時は 0.3 秒。printf 'hello 中文測試🙂' | psbx-ios typeは標準入力のテキストをフォーカス中の欄に入力します。中国語や絵文字も入ります。ASCII 以外を含むテキストは、自動でシミュレーター内の Unicode ヘルパーを通ります。psbx-ios unicodeはそのヘルパーだけを使います。tap、swipe、type、unicode、key、buttonに--wait [ミリ秒]を付けると、画面がその間(既定 500)変わらなくなってから返るので、続くsandbox_shotやpsbx-ios screenshotには一つ前のコマではなく結果が写ります(Unicode ヘルパー経由の入力は、文字が画面に出る前に返ります):psbx-ios tap 200 400 --wait、printf '你好' | psbx-ios type --wait 800。待った時間を表示し、アニメーションや動画ならさらに 5 秒待って返ります。psbx-ios settle [ミリ秒]は待つだけです。カーソルの点滅は変化に数えません。- キーと入力は、画面が遷移中なら止まるのを待ちながら再試行します。それでも失敗したときは、システムのダイアログ(通知の許可、Safari の確認)に遮られている可能性があると伝えるので、
sandbox_shotで見てください。 psbx-ios key HIDコードでキー:40 Enter、42 Backspace、43 Tab、41 Escape。psbx-ios button homeでハードウェアボタン:home、lock、side-button、siri、apple-pay。psbx-ios sizeで画面のピクセル幅・高さと倍率(1206 2622 3.000000)。
特定の画面へはディープリンク(simctl openurl)や UI テストのターゲット(xcodebuild test)のほうが速く着きます。sandbox_shot か psbx-ios screenshot で確認してください。起動画面から最初の画面への切り替わりのような短い変化を捉えるには、psbx-ios record 秒数 ファイル でシミュレーター本来のフレームレートのまま 1〜120 秒録画し、ボックスの mp4 に保存します(psbx-ios record 5 /work/launch.mp4 のあと、ffmpeg -i /work/launch.mp4 -vf fps=20 /work/f_%03d.png で 1 枚ずつに分けられます)。psbx-ios screenshot は 1 枚約 0.7 秒かかり、間に合いません。sandbox_shot の record はボックスの画面、つまり毎秒約 10 コマのライブ映像を録画します。
ネイティブアプリを人に見せるときは、まず自分でタップ・入力して試し(Android は artemis-adb tap/type、iOS は psbx-ios tap/type、そのあと sandbox_shot)、最初の画面に戻してデバイスで動かしたまま、open: "box" で sandbox_review を呼びます。カードはボックスの画面を開き、人はそこでネイティブアプリをタップ・入力して試します。レビューは何も起こしておきません。デバイスは通常のアイドルの規則どおりボックスと一緒に凍結・回収され、人がレビューを開くとボックスと一緒に戻ります。
すべてのツールとトピックはツールリファレンスに並んでいます。