fix(ios): expose Launch/Terminate as object schemas with uri field#2383
Merged
fix(ios): expose Launch/Terminate as object schemas with uri field#2383
Conversation
The Launch and Terminate actions used a bare `z.string()` paramSchema.
Because the CLI/MCP schema layer only surfaces fields from ZodObject
shapes, there was no `--uri` flag to bind to and `launch --help` instead
listed leaked ZodString prototype methods (`--parse`, `--safeParse`,
`--_def`, ...). Running `midscene-ios launch com.xx.xx.xxx` silently
dropped the positional argument and eventually tripped
`z.string().parse({})`, surfacing as "Expected string, received object".
Switch both actions to `z.object({ uri })` to match Android/Harmony,
read `param.uri` in the `call` implementation, and keep
`agent.launch(uri)` / `agent.terminate(uri)` as string-typed wrappers so
existing programmatic callers (docs, YAML setup, tests) continue to work.
Fixes #2313.
Deploying midscene with
|
| Latest commit: |
c142f77
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://ce93cbe6.midscene.pages.dev |
| Branch Preview URL: | https://fix-ios-launch-cli-schema-23.midscene.pages.dev |
4 tasks
Before this change `extractActionSchema` fell through to `paramSchema as Record<string, z.ZodTypeAny>` when the schema wasn't a ZodObject. That cast leaked the Zod instance's prototype methods (`parse`, `safeParse`, `_def`, ...) as CLI flags and let a primitive schema like `z.string()` silently reach runtime, which is how #2313 slipped past review on iOS while Android and Harmony were fine. Throw a named, actionable error at tool-definition time when a schema isn't a ZodObject. Any future platform that copies the iOS mistake will now fail the platform's own `initTools` unit tests instead of the user's CLI invocation. Added positive and negative unit tests in `packages/shared`.
…contract - iOS device.test: add Terminate.call routing + empty-uri rejection to mirror the Launch coverage added in the fix commit. - iOS cli-launch.test (new): drive `runToolsCLI` with real argv for `launch --uri`, `terminate --uri`, `launch --help`, `terminate --help` and an invalid `--bundleId`. Guards the exact user-facing surface that regressed in #2313 — both the routing of the uri arg and the absence of ZodString prototype leaks (--parse / --safeParse / --_def / etc.) from the help output. - Android page.test + Harmony device.test: assert Launch/Terminate paramSchemas are `z.object({ uri: z.string() })`. The shared tool-generator now rejects non-object schemas, but it does not pin field names — these assertions stop a future drift like "Android keeps uri, iOS renames to bundleId" from silently breaking CLI ergonomics.
This reverts commit 1fd1b91.
yuyutaotao
approved these changes
Apr 23, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes #2313.
npx @midscene/ios launch <bundle-id>could not pass the bundle id on to the device: the CLI had no--uriflag, positional args were dropped, and the handler ended up callingz.string().parse({}), surfacing as "Expected string, received object".Root cause
packages/ios/src/device.ts:1040— theLaunchandTerminateactions used a barez.string()paramSchema, unlike Android/Harmony which usez.object({ uri: z.string() }).packages/shared/src/mcp/tool-generator.ts:158-168—extractActionSchemaonly surfaces named fields whenparamSchemais a ZodObject. For a primitive schema it casts the ZodString instance to aRecord, so every enumerable prototype property leaks into the CLI schema (--parse,--safeParse,--_def, …) and there is no--urito bind to.packages/shared/src/cli/cli-args.ts:25-27—parseCliArgsonly captures--flagargs and silently drops positional arguments, solaunch com.xx.xx.xxxnever even reaches validation.packages/core/src/common.ts:710— with empty parsed args,parseActionParam({}, z.string())throwsExpected string, received object, which is the error the reporter saw.Before the fix,
midscene-ios launch --helpsurfaced the leak:After the fix:
Changes
packages/ios/src/device.ts: switchlaunchParamSchemaandterminateParamSchematoz.object({ uri: z.string() })(matches Android/Harmony). Thecallimplementations now readparam.uriand reject empty strings with a consistent error.packages/ios/src/agent.ts: replace the auto-wrappedlaunch/terminatefields with explicitasync launch(uri: string)/async terminate(uri: string)methods that wrap the argument into{ uri }before dispatching. Preserves the existing programmatic API used by docs,agent.launch('com.apple.Preferences'), YAML CLI setup (packages/cli/src/create-yaml-player.ts:330), and existing tests.packages/ios/tests/unit-test/device.test.ts: regression tests asserting thatLaunch/TerminateparamSchemas areZodObjectwith auri: ZodStringfield, thatLaunch.calldelegatesparam.uritodevice.launch, and that emptyuriis rejected.packages/ios/tests/unit-test/agent.test.ts: update the mocked action-spacecallstubs to forwardparam.uri(mirroring the real implementation).YAML shortcuts (
ios.launch: com.xxx) already wrap string values into{ uri }viabuildShortcutActionParam(packages/core/src/yaml/player.ts:108), so no YAML changes were needed.Validation
pnpm run lintnpx nx test ios— 114 tests pass (including the new regression tests)npx nx test ios-mcp— 2 tests passnpx nx test cli— 114 tests passnpx nx build ios— success (including declaration bundling)runToolsCLIwith argv['launch', '--help'],['launch', '--uri', 'com.xx.xx.xxx'], and['launch', 'com.xx.xx.xxx'].--uriis now accepted and the only remaining error is the expected "WebDriverAgent not running" when no device is present.Test plan
midscene-ios launch --uri com.apple.Preferencesagainst a connected simulator/device and confirm it launches.midscene-ios launch --helpand confirm--uriis the only action option listed (no--parse/--safeParse/etc. leak).ios.launch: com.apple.Preferencesand confirm the app launches (covers the shortcut path).agent.launch('com.apple.Preferences')and confirm it still works (programmatic API unchanged).