Skip to content

feat: add public --launch-arg CLI flag for iOS open#598

Open
mikegarfinkle wants to merge 1 commit into
callstackincubator:mainfrom
mikegarfinkle:feat/open-launch-args
Open

feat: add public --launch-arg CLI flag for iOS open#598
mikegarfinkle wants to merge 1 commit into
callstackincubator:mainfrom
mikegarfinkle:feat/open-launch-args

Conversation

@mikegarfinkle
Copy link
Copy Markdown

@mikegarfinkle mikegarfinkle commented May 27, 2026

Summary

Adds public --launch-arg <arg> support to agent-device open for iOS launches (Android to come as follow-up). The flag is repeatable and is forwarded through the CLI, client options, daemon request flags, and iOS launch implementation.

Before: launch arguments existed only as internal iOS plumbing and were not available from the public open command. After: callers can pass iOS launch arguments from CLI or client APIs, including dash-prefixed values such as -FeatureFlag. Empty launchArgs: [] is normalized as “not provided” for programmatic callers.

Platform behavior:

  • iOS simulator app launches append args after <device> <bundle id> in simctl launch, matching simctl's documented argv position.
  • iOS physical device launches insert -- before the positional bundle id (devicectl ... [options] -- <bundle> <args...>) so dash-prefixed app args are not parsed as devicectl options and the separator is not forwarded to the app. This also works with --payload-url.
  • iOS simulator URL opens reject non-empty launchArgs because simctl openurl ignores launch args; callers should launch the app with args first, then open the URL separately.
  • macOS rejects non-empty launchArgs; Android remains rejected by the existing platform guard and is out of scope.

Validation

End-to-End:

Simulator

sim.mov

Device

device.mov

Automated validation passed:

  • pnpm format
  • pnpm typecheck
  • pnpm exec vitest run src/platforms/ios/__tests__/index.test.ts src/utils/__tests__/args.test.ts

Behavior covered by tests:

  • CLI parsing/help for repeated --launch-arg, including single-dash and double-dash values.
  • iOS simulator app launch with empty args and dash-prefixed args.
  • iOS physical device launch with devicectl -- <bundle> boundary.
  • iOS physical device deep-link launch with --payload-url followed by -- <bundle> <args...>.
  • Rejections for simulator URL opens and macOS when launchArgs is non-empty.
  • Empty launchArgs: [] behaves like no launch args for simulator URL opens and macOS.

Copilot AI review requested due to automatic review settings May 27, 2026 19:20
@mikegarfinkle mikegarfinkle marked this pull request as draft May 27, 2026 19:20
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Adds support for passing repeatable --launch-args through the open flow so callers can append platform-specific launch arguments when launching apps on iOS/Android.

Changes:

  • Introduces --launch-args <arg> (repeatable) to the CLI flag schema and propagates it through client/daemon/context/dispatch to interactors.
  • Appends launch arguments to iOS simulator/device launch commands and Android adb shell am start invocations.
  • Adds iOS and Android test coverage validating argument ordering and simulator URL-open rejection behavior.

Reviewed changes

Copilot reviewed 18 out of 18 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/utils/command-schema.ts Adds launchArgs to CLI flags and flag definitions (--launch-args).
src/platforms/ios/apps.ts Threads launchArgs into iOS launch paths; rejects simulator URL opens with launchArgs.
src/platforms/ios/tests/index.test.ts Adds tests for iOS simulator/device argument passing and simulator URL+args rejection.
src/platforms/android/app-lifecycle.ts Updates openAndroidApp signature; appends launchArgs to am start command construction.
src/platforms/android/tests/index.test.ts Updates existing call sites and adds tests verifying launchArgs appended for multiple launch modes.
src/daemon/handlers/session-open.ts Removes launchArgs from runtime URL launch context payload.
src/daemon/context.ts Adds launchArgs to daemon context construction; introduces a complexity-ignore comment.
src/daemon-client.ts Adds launchArgs to openApp options and request flags mapping.
src/core/launch-console.ts Adds user-facing error message for unsupported simulator URL-open + --launch-args.
src/core/interactors/apple.ts Passes launchArgs into openIosApp.
src/core/interactors/android.ts Passes activity + launchArgs object into updated openAndroidApp API.
src/core/interactor-types.ts Extends Interactor.open options type with launchArgs.
src/core/dispatch.ts Adds launchArgs to interactor.open calls for both app and URL opens.
src/core/dispatch-context.ts Adds launchArgs to dispatch context type.
src/commands/session-lifecycle/definition.ts Allows launchArgs flag for the open command.
src/client-types.ts Extends client request option types with launchArgs.
src/client-normalizers.ts Adds launchArgs to normalized CommandFlags.
src/cli/commands/open.ts Passes CLI launchArgs through to the client open call.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/daemon/context.ts

export type DaemonCommandContext = DispatchContext & ScreenshotRuntimeFlags;

// fallow-ignore-next-line complexity
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this ignore comment is used in other parts of this repo for this use case

Comment thread src/platforms/ios/apps.ts Outdated
Comment on lines +1029 to +1035
const args = ['device', 'process', 'launch', '--device', device.id, bundleId];
if (options?.payloadUrl) {
args.push('--payload-url', options.payloadUrl);
}
if (options?.launchArgs && options.launchArgs.length > 0) {
args.push(...options.launchArgs);
}
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will fix this

Comment on lines 294 to 298
export async function openAndroidApp(
device: DeviceInfo,
app: string,
activity?: string,
options?: { activity?: string; launchArgs?: string[] },
): Promise<void> {
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

public api is agent device CLI itself, so this is internal and OK to make change

@thymikee
Copy link
Copy Markdown
Member

Thanks for contributing this! The feature direction makes sense, especially exposing launch args for iOS app launches and handling the iOS simulator URL limitation explicitly.

A couple of things need sorting before this can merge:

  • The PR is now stale/conflicting with current main. main already has internal Apple launch-args support from the Maestro work, and currently rejects Android launch args intentionally, so Android support needs to be rebased and treated as an explicit project decision.
  • Please preserve the newer Android app-bound URL flow from main while rebasing; the PR branch shape would otherwise risk dropping open <app> <url> behavior.
  • If --launch-args becomes a public open flag, we should add parser/help coverage in src/utils/__tests__/args.test.ts, including dash-prefixed values like --es, and either reject or implement the flag for macOS instead of silently ignoring it.

So I think the feature is promising, but it needs a rebase plus a tightened platform contract before review can continue.

@mikegarfinkle mikegarfinkle force-pushed the feat/open-launch-args branch from 4fcc27a to 4a61990 Compare May 27, 2026 19:57
@mikegarfinkle
Copy link
Copy Markdown
Author

Thanks for contributing this! The feature direction makes sense, especially exposing launch args for iOS app launches and handling the iOS simulator URL limitation explicitly.

A couple of things need sorting before this can merge:

  • The PR is now stale/conflicting with current main. main already has internal Apple launch-args support from the Maestro work, and currently rejects Android launch args intentionally, so Android support needs to be rebased and treated as an explicit project decision.
  • Please preserve the newer Android app-bound URL flow from main while rebasing; the PR branch shape would otherwise risk dropping open <app> <url> behavior.
  • If --launch-args becomes a public open flag, we should add parser/help coverage in src/utils/__tests__/args.test.ts, including dash-prefixed values like --es, and either reject or implement the flag for macOS instead of silently ignoring it.

So I think the feature is promising, but it needs a rebase plus a tightened platform contract before review can continue.

Thanks for the guidance @thymikee! Will take out of draft when the above has been completed 😃

@mikegarfinkle mikegarfinkle force-pushed the feat/open-launch-args branch from 4a61990 to d68f6f7 Compare May 27, 2026 20:14
@mikegarfinkle mikegarfinkle changed the title feat: forward --launch-args to platform launch commands on open feat: add public --launch-args CLI flag for iOS open May 27, 2026
mikegarfinkle added a commit to mikegarfinkle/agent-device that referenced this pull request May 27, 2026
Stacked on top of the iOS-only --launch-args PR (callstackincubator#598). Removes the
Android UNSUPPORTED_OPERATION guard added with the Maestro work and
threads launchArgs through all five Android open paths.

Per-path threading:

- openAndroidPackage           (-p package launch + activity-fallback)
- openAndroidPackageActivity   (-n component override)
- openAndroidIntent            (named intent action)
- openAndroidDeepLink          (-a VIEW -d <url>, with optional -p)
- openAndroidAppBoundDeepLink  (-a VIEW -d <url> -p <resolved>)

`adb shell` joins its argv with spaces and feeds the result to a
device shell, which re-tokenises. The other am-start arguments are
well-known and never contain shell-significant characters, so they
round-trip untouched. Launch arguments are user-supplied and may
contain JSON, spaces, `#`, etc.; each is single-quoted unless it
consists entirely of safe shell characters (the same approach long
used in adb-driven tooling for the same reason).

Help text on --launch-args is updated to describe the Android shape
(`adb shell am start args, e.g. --es key value` for typed Intent
extras) and macOS remains the only rejected platform.

Tests:

- src/platforms/android/__tests__/index.test.ts: five new tests
  cover package, activity-override, deep-link URL, app-bound URL,
  and JSON-with-metacharacters shell-quoting paths.
- src/core/__tests__/dispatch-open.test.ts: the previous
  "rejects Android launch arguments" test is inverted into a
  forwarding test that asserts openAndroidApp receives the args.

Validated end-to-end against an internal Android debug build: a JSON
TEST_ACCOUNT_ARGS extra containing email/password/header (with `#`,
`/`, `:`) was delivered intact to AutomationSyncActivity, which
read the extra and persisted it to AppPreferences.
mikegarfinkle added a commit to mikegarfinkle/agent-device that referenced this pull request May 27, 2026
Stacked on top of the iOS-only --launch-args PR (callstackincubator#598). Removes the
Android UNSUPPORTED_OPERATION guard added with the Maestro work and
threads launchArgs through all five Android open paths.

Per-path threading:

- openAndroidPackage           (-p package launch + activity-fallback)
- openAndroidPackageActivity   (-n component override)
- openAndroidIntent            (named intent action)
- openAndroidDeepLink          (-a VIEW -d <url>, with optional -p)
- openAndroidAppBoundDeepLink  (-a VIEW -d <url> -p <resolved>)

`adb shell` joins its argv with spaces and feeds the result to a
device shell, which re-tokenises. The other am-start arguments are
well-known and never contain shell-significant characters, so they
round-trip untouched. Launch arguments are user-supplied and may
contain JSON, spaces, `#`, etc.; each is single-quoted unless it
consists entirely of safe shell characters (the same approach long
used in adb-driven tooling for the same reason).

Help text on --launch-args is updated to describe the Android shape
(`adb shell am start args, e.g. --es key value` for typed Intent
extras) and macOS remains the only rejected platform.

Tests:

- src/platforms/android/__tests__/index.test.ts: five new tests
  cover package, activity-override, deep-link URL, app-bound URL,
  and JSON-with-shell-metacharacters quoting paths.
- src/core/__tests__/dispatch-open.test.ts: the previous
  "rejects Android launch arguments" test is inverted into a
  forwarding test that asserts openAndroidApp receives the args.

Validated end-to-end on a Pixel emulator running a debug build whose
launcher activity reads an Intent extra to bootstrap test
configuration: a JSON value containing `#`, `/`, `:` survived
single-quoted transit through `adb shell` and arrived at the
activity unchanged.
@mikegarfinkle mikegarfinkle force-pushed the feat/open-launch-args branch from d68f6f7 to 4c77aca Compare May 27, 2026 20:27
@mikegarfinkle mikegarfinkle marked this pull request as ready for review May 27, 2026 20:28
Copilot AI review requested due to automatic review settings May 27, 2026 20:28
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 10 out of 10 changed files in this pull request and generated 2 comments.

Comment thread src/platforms/ios/apps.ts
Comment thread src/core/launch-console.ts Outdated
mikegarfinkle added a commit to mikegarfinkle/agent-device that referenced this pull request May 27, 2026
Stacked on top of the iOS-only --launch-args PR (callstackincubator#598). Removes the
Android UNSUPPORTED_OPERATION guard added with the Maestro work and
threads launchArgs through all five Android open paths.

Per-path threading:

- openAndroidPackage           (-p package launch + activity-fallback)
- openAndroidPackageActivity   (-n component override)
- openAndroidIntent            (named intent action)
- openAndroidDeepLink          (-a VIEW -d <url>, with optional -p)
- openAndroidAppBoundDeepLink  (-a VIEW -d <url> -p <resolved>)

`adb shell` joins its argv with spaces and feeds the result to a
device shell, which re-tokenises. The other am-start arguments are
well-known and never contain shell-significant characters, so they
round-trip untouched. Launch arguments are user-supplied and may
contain JSON, spaces, `#`, etc.; each is single-quoted unless it
consists entirely of safe shell characters (the same approach long
used in adb-driven tooling for the same reason).

Help text on --launch-args is updated to describe the Android shape
(`adb shell am start args, e.g. --es key value` for typed Intent
extras) and macOS remains the only rejected platform.

Tests:

- src/platforms/android/__tests__/index.test.ts: five new tests
  cover package, activity-override, deep-link URL, app-bound URL,
  and JSON-with-shell-metacharacters quoting paths.
- src/core/__tests__/dispatch-open.test.ts: the previous
  "rejects Android launch arguments" test is inverted into a
  forwarding test that asserts openAndroidApp receives the args.

Validated end-to-end on a Pixel emulator running a debug build whose
launcher activity reads an Intent extra to bootstrap test
configuration: a JSON value containing `#`, `/`, `:` survived
single-quoted transit through `adb shell` and arrived at the
activity unchanged.
@mikegarfinkle mikegarfinkle force-pushed the feat/open-launch-args branch from 4c77aca to 51178ab Compare May 27, 2026 20:37
mikegarfinkle added a commit to mikegarfinkle/agent-device that referenced this pull request May 27, 2026
Stacked on top of the iOS-only --launch-args PR (callstackincubator#598). Removes the
Android UNSUPPORTED_OPERATION guard added with the Maestro work and
threads launchArgs through all five Android open paths.

Per-path threading:

- openAndroidPackage           (-p package launch + activity-fallback)
- openAndroidPackageActivity   (-n component override)
- openAndroidIntent            (named intent action)
- openAndroidDeepLink          (-a VIEW -d <url>, with optional -p)
- openAndroidAppBoundDeepLink  (-a VIEW -d <url> -p <resolved>)

`adb shell` joins its argv with spaces and feeds the result to a
device shell, which re-tokenises. The other am-start arguments are
well-known and never contain shell-significant characters, so they
round-trip untouched. Launch arguments are user-supplied and may
contain JSON, spaces, `#`, etc.; each is single-quoted unless it
consists entirely of safe shell characters (the same approach long
used in adb-driven tooling for the same reason).

Help text on --launch-args is updated to describe the Android shape
(`adb shell am start args, e.g. --es key value` for typed Intent
extras) and macOS remains the only rejected platform.

Tests:

- src/platforms/android/__tests__/index.test.ts: five new tests
  cover package, activity-override, deep-link URL, app-bound URL,
  and JSON-with-shell-metacharacters quoting paths.
- src/core/__tests__/dispatch-open.test.ts: the previous
  "rejects Android launch arguments" test is inverted into a
  forwarding test that asserts openAndroidApp receives the args.

Validated end-to-end on a Pixel emulator running a debug build whose
launcher activity reads an Intent extra to bootstrap test
configuration: a JSON value containing `#`, `/`, `:` survived
single-quoted transit through `adb shell` and arrived at the
activity unchanged.
@mikegarfinkle
Copy link
Copy Markdown
Author

@thymikee good to go. As mentioned in the updated PR description, this PR is now scoped to iOS only and Android support will now be a follow-up.

@mikegarfinkle mikegarfinkle force-pushed the feat/open-launch-args branch from 51178ab to 3a02cdc Compare May 28, 2026 15:45
Copilot AI review requested due to automatic review settings May 28, 2026 15:45
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 10 out of 10 changed files in this pull request and generated 3 comments.

surface: enumField(SURFACE_VALUES),
activity: stringField('Android activity name.'),
launchConsole: stringField('Launch console mode.'),
launchArgs: stringArrayField('Launch arguments forwarded to the app.'),
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will fix

Comment thread src/platforms/ios/apps.ts
Comment on lines +939 to +942
const previousPath = process.env.PATH;
const previousArgsFile = process.env.AGENT_DEVICE_TEST_ARGS_FILE;
process.env.PATH = `${tmpDir}${path.delimiter}${previousPath ?? ''}`;
process.env.AGENT_DEVICE_TEST_ARGS_FILE = argsLogPath;
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will fix

@mikegarfinkle mikegarfinkle force-pushed the feat/open-launch-args branch 2 times, most recently from ea66eac to fbea566 Compare May 28, 2026 15:59
Copilot AI review requested due to automatic review settings May 28, 2026 15:59
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 10 out of 10 changed files in this pull request and generated 3 comments.

Comment thread src/platforms/ios/apps.ts
Comment thread src/platforms/ios/__tests__/index.test.ts
Comment thread src/platforms/ios/apps.ts
@mikegarfinkle mikegarfinkle force-pushed the feat/open-launch-args branch from fbea566 to 7431517 Compare May 29, 2026 14:13
Copilot AI review requested due to automatic review settings May 29, 2026 14:16
@mikegarfinkle mikegarfinkle force-pushed the feat/open-launch-args branch from 7431517 to 09a90af Compare May 29, 2026 14:16
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 10 out of 10 changed files in this pull request and generated no new comments.

@mikegarfinkle mikegarfinkle force-pushed the feat/open-launch-args branch from 09a90af to 4ffcfe8 Compare May 29, 2026 14:20
Copilot AI review requested due to automatic review settings May 29, 2026 15:06
@mikegarfinkle mikegarfinkle force-pushed the feat/open-launch-args branch from 4ffcfe8 to 8a2ed84 Compare May 29, 2026 15:06
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 10 out of 10 changed files in this pull request and generated 5 comments.

Comment thread src/platforms/ios/apps.ts
Comment on lines +1085 to 1100
const args = ['device', 'process', 'launch', '--device', device.id];
if (options?.launchArgs && options.launchArgs.length > 0) {
if (options?.payloadUrl) {
args.push('--payload-url', options.payloadUrl);
}
// `devicectl` uses Swift ArgumentParser; without `--` a leading-dash app
// arg can be re-interpreted as a devicectl option. The marker must come
// before the positional bundle id so it is consumed by devicectl instead
// of being forwarded to the app as argv[1].
args.push('--', bundleId, ...options.launchArgs);
} else {
args.push(bundleId);
if (options?.payloadUrl) {
args.push('--payload-url', options.payloadUrl);
}
}
Copy link
Copy Markdown
Author

@mikegarfinkle mikegarfinkle May 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will make devicectl use one canonical order: command options first (--device, optional --payload-url), then the positional bundle/app args boundary.

Comment thread src/platforms/ios/apps.ts Outdated
Comment thread src/platforms/ios/apps.ts
Comment thread src/platforms/ios/apps.ts
Comment thread src/platforms/ios/apps.ts
@mikegarfinkle mikegarfinkle force-pushed the feat/open-launch-args branch from 8a2ed84 to 9391dc0 Compare May 29, 2026 15:13
Copilot AI review requested due to automatic review settings May 29, 2026 15:18
@mikegarfinkle mikegarfinkle force-pushed the feat/open-launch-args branch from 9391dc0 to a976fb6 Compare May 29, 2026 15:18
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 10 out of 10 changed files in this pull request and generated no new comments.

'launch',
'--device',
'ios-device-1',
'com.apple.mobilesafari',
Copy link
Copy Markdown
Author

@mikegarfinkle mikegarfinkle May 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This changed to keep the devicectl invocation in one canonical order: options first, then positionals. So --payload-url is always emitted before the bundle id, regardless of whether launch args are present.

Without launch args:
devicectl device process launch --device <udid> --payload-url <url> <bundle>

With launch args:
devicectl device process launch --device <udid> --payload-url <url> -- <bundle> <args...>

This keeps the command structure predictable. All devicectl options, including --payload-url, come before the bundle id. Then the bundle id and app launch arguments come at the end.

@mikegarfinkle
Copy link
Copy Markdown
Author

mikegarfinkle commented May 29, 2026

@thymikee I had to revise the PR again due to additional changes on main. Please see this new version. I have also included videos showing expected launch arg handling on both simulator and device.

@mikegarfinkle mikegarfinkle changed the title feat: add public --launch-args CLI flag for iOS open feat: add public --launch-arg CLI flag for iOS open May 29, 2026
@mikegarfinkle mikegarfinkle force-pushed the feat/open-launch-args branch from a976fb6 to ec0c004 Compare May 29, 2026 20:58
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.

3 participants