Skip to content

feat(macos): run an app's App Intent by identity - #74

Open
hyprcat wants to merge 1 commit into
iFurySt:mainfrom
hyprcat:feat/run-app-intent
Open

hyprcat wants to merge 1 commit into
iFurySt:mainfrom
hyprcat:feat/run-app-intent

Conversation

@hyprcat

@hyprcat hyprcat commented Sep 15, 2026

Copy link
Copy Markdown
Contributor

What

A macOS run_intent tool that runs one of an app's App Intents by <bundle id>.<intent name>, instead of driving its interface.

open-computer-use call run_intent --args '{"bundle_id":"com.apple.Notes","action_id":"CreateNote","input":"hello"}'

Why

Some operations an app already exposes as an intent. Clicking through the interface to reach them is slower and more fragile β€” and an intent runs without the window being usable or frontmost.

How, and why it looks like this

macOS has no unentitled API that invokes an App Intent directly; the private LinkServices executor rejects any client that is not a validated bundle. The one supported route is Shortcuts, and an App Intent's workflow action identifier is exactly <bundle id>.<intent name> β€” the two fields the caller already has.

So the first call for a given intent generates a one-action workflow, signs it with shortcuts sign and opens it for a one-time Add Shortcut by the user. Later calls run it via shortcuts run. shortcuts list is the record of what is installed, so there is no separate binding table to drift from it.

The one-time approval is inherent to the platform, not a design choice I can remove.

Please review the safety boundary closely

This tool widens what the runtime can do, so the limits sit at the narrowest point:

  • Only an app's own intents run. Built-in is.workflow. Shortcuts actions are refused outright β€” notably runshellscript.
  • The identifier must be dotted with non-empty segments of letters, digits, _ and -, so /, .., spaces and newlines never reach a filename. Covered by a test that walks a list of hostile inputs.
  • Installation requires the user's own click in Shortcuts. Nothing installs silently.
  • Child stdin is /dev/null β€” shortcuts reads stdin when left attached, which would consume the runtime's own pipe.
  • A 120s deadline terminates the child, since reading to EOF only returns when it exits.

docs/SECURITY.md records these constraints.

Honest caveat

Of the changes I am upstreaming from my fork, this is the most speculative one for a general audience β€” a capability expansion with a manual handshake, and an extra tool beyond the official nine. If you would rather it be opt-in, or not land at all, that is a reasonable call.

It also adds a tool on top of #73; if both land, the count assertion becomes 11 rather than 10 β€” happy to rebase whichever merges second.

Verification

  • swift build clean
  • AppIntentExecutionTests β€” 3 tests covering identifier joining, built-in refusal, and path-character rejection
  • make check-docs passes

The install-and-run path needs a real desktop with Shortcuts and is not covered by an automated test.

Some operations an app already exposes as an App Intent. Driving its interface
to reach them is slower and more fragile than asking for the operation, and an
intent runs without the window being usable or frontmost.

macOS has no unentitled API that invokes an App Intent directly: the private
LinkServices executor rejects any client that is not a validated bundle. The one
supported route is Shortcuts, whose workflow action identifier for an App Intent
is exactly `<bundle id>.<intent name>`. So the first call for an intent generates
a one-action workflow, signs it with `shortcuts sign` and opens it for a one-time
Add Shortcut; later calls run it. `shortcuts list` is the record of what is
installed, so there is no separate binding table to drift from it.

The limits sit at the narrowest point: only an app's own intents run (built-in
`is.workflow.` actions are refused), the identifier must be dotted with non-empty
segments of letters, digits, `_` and `-` so `/`, `..`, spaces and newlines never
reach a filename, and installation needs the user's own click.

Child stdin is `/dev/null`: `shortcuts` reads stdin when it is left attached,
which would consume the runtime's own pipe. A 120s deadline terminates an intent
that sits waiting, since reading to EOF only returns when the child exits.
@hyprcat

hyprcat commented Sep 29, 2026

Copy link
Copy Markdown
Contributor Author

@iFurySt checking in: this runs an app's App Intent by identity, a direct path for actions that have no stable UI. It conflicts with main now; I'll rebase if you're interested, otherwise feel free to say no and I'll close it.

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.

1 participant