From 260eb29a03d058d6d1b8d34e48d12936ad2b6063 Mon Sep 17 00:00:00 2001 From: Seto Elkahfi Date: Thu, 2 Jul 2026 14:52:58 +0200 Subject: [PATCH] Surface and drive real Apple TV devices Detect physical Apple TV units by product type and report platform tvos in mobile_list_available_devices instead of hardcoding ios. Route real tvOS devices through mobilecli (which installs and drives the tvOS runner) while iPhones and iPads keep using the WebDriverAgent-based IosRobot. Add a unit test for platform detection. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/ios.ts | 13 ++++++++++++- src/server.ts | 24 +++++++++++++++++++++--- test/ios-platform.ts | 20 ++++++++++++++++++++ 3 files changed, 53 insertions(+), 4 deletions(-) create mode 100644 test/ios-platform.ts diff --git a/src/ios.ts b/src/ios.ts index 774464ef..ca2d60b3 100644 --- a/src/ios.ts +++ b/src/ios.ts @@ -31,6 +31,16 @@ export interface IosDevice { deviceName: string; } +export type IosPlatform = "ios" | "tvos"; + +/** + * Real Apple TV units are enumerated over the same go-ios connection as iPhones and + * iPads, so they are distinguished by their product type (e.g. "AppleTV14,1") rather + * than a separate device class. + */ +export const platformFromProductType = (productType: string): IosPlatform => + productType.startsWith("AppleTV") ? "tvos" : "ios"; + const getGoIosPath = (): string => { if (process.env.GO_IOS_PATH) { return process.env.GO_IOS_PATH; @@ -282,7 +292,7 @@ export class IosManager { return devices; } - public listDevicesWithDetails(): Array { + public listDevicesWithDetails(): Array { if (!this.isGoIosInstalled()) { console.error("go-ios is not installed, no physical iOS devices can be detected"); return []; @@ -296,6 +306,7 @@ export class IosManager { deviceId: device, deviceName: info.DeviceName, version: info.ProductVersion, + platform: platformFromProductType(info.ProductType), }; }); diff --git a/src/server.ts b/src/server.ts index 6b0e53d1..45f44105 100644 --- a/src/server.ts +++ b/src/server.ts @@ -9,7 +9,7 @@ import { ChildProcess } from "node:child_process"; import { error, trace } from "./logger"; import { AndroidRobot, AndroidDeviceManager } from "./android"; import { ActionableError, Robot } from "./robot"; -import { IosManager, IosRobot } from "./ios"; +import { IosManager, IosRobot, platformFromProductType } from "./ios"; import { PNG } from "./png"; import { isScalingAvailable, Image } from "./image-utils"; import { Mobilecli } from "./mobilecli"; @@ -172,6 +172,24 @@ export const createMcpServer = (): McpServer => { const iosDevices = iosManager.listDevices(); const iosDevice = iosDevices.find(d => d.deviceId === deviceId); if (iosDevice) { + // Real Apple TV units are driven through mobilecli, which installs and drives + // the tvOS runner (re-signed with a provisioning profile). iPhones and iPads + // keep using the WebDriverAgent-based IosRobot. + const platform = platformFromProductType(iosManager.getDeviceInfo(deviceId).ProductType); + if (platform === "tvos") { + if (!agentVerifiedSimulators.has(deviceId)) { + const agentStatus = mobilecli.agentStatus(deviceId); + if (agentStatus.status === "fail") { + mobilecli.agentInstall(deviceId); + } + + agentVerifiedSimulators.add(deviceId); + } + + posthog("get_robot", { "DevicePlatform": "tvos", "DeviceType": "real" }).then(); + return new MobileDevice(deviceId); + } + posthog("get_robot", { "DevicePlatform": "ios", "DeviceType": "real" }).then(); return new IosRobot(deviceId); } @@ -240,14 +258,14 @@ export const createMcpServer = (): McpServer => { }); } - // Get iOS physical devices with details + // Get iOS and tvOS physical devices with details try { const iosDevices = iosManager.listDevicesWithDetails(); for (const device of iosDevices) { devices.push({ id: device.deviceId, name: device.deviceName, - platform: "ios", + platform: device.platform, type: "real", version: device.version, state: "online", diff --git a/test/ios-platform.ts b/test/ios-platform.ts new file mode 100644 index 00000000..60ca794c --- /dev/null +++ b/test/ios-platform.ts @@ -0,0 +1,20 @@ +import { test, expect } from "@playwright/test"; + +import { platformFromProductType } from "../src/ios"; + +test.describe("ios platform detection", () => { + + test("maps Apple TV product types to tvos", () => { + expect(platformFromProductType("AppleTV14,1")).toBe("tvos"); + expect(platformFromProductType("AppleTV11,1")).toBe("tvos"); + }); + + test("maps iPhone and iPad product types to ios", () => { + expect(platformFromProductType("iPhone15,3")).toBe("ios"); + expect(platformFromProductType("iPad13,1")).toBe("ios"); + }); + + test("defaults unknown product types to ios", () => { + expect(platformFromProductType("")).toBe("ios"); + }); +});