Add sender-side CLI / automation (URL scheme + launch args)#107
Conversation
Drive the Sender without the GUI — for scripting, SSH, and connecting automatically on login or wake — reusing the existing sender/session model. - targetbridge:// URL scheme and launch arguments (--connect/--disconnect), both handled in the existing app entry point and dispatched to TBDisplaySenderService.shared. They call the same session connect()/stop() the GUI uses — no separate/parallel control path. - Options: receiver (auto|id|name|ip), mode (mirror|extended), preset, transport, session, local-ip. - cli/targetbridge: thin shell wrapper over the URL scheme. - docs/Automation.md: CLI, URL scheme, launch-at-login, SSH, and wake recipes. The scheme is registered via a small base Info.plist merged with Xcode's generated one; the three INFOPLIST_KEY_* usage strings move into that file because Xcode drops them once INFOPLIST_FILE is set. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
I pushed a small maintainer-side follow-up onto this PR branch to tighten two review points before merge:\n\n- invalid \ values now fail explicitly instead of silently falling back to session 1 or disconnecting everything\n- the automation connect path now refreshes local interfaces / discovery state before resolving \ and picking the local interface, which should make login/wake/SSH-driven flows more reliable\n\nI also rebuilt the Sender after the change to make sure the branch still compiles cleanly.\n\nThanks again — the overall direction here is still very good. |
|
Small follow-up: I also pushed a maintainer-side fix on top of this PR branch to tighten two review points before merge:
I rebuilt the Sender after the change as well, so the branch still compiles cleanly. Thanks again — the overall direction here is still very good. |
|
We’re currently testing this PR on our side as well. I’m also making a few small maintainer-side adjustments to better align it with the current TargetBridge project structure and behavior, while keeping the overall scope the same. So if you notice a couple of follow-up commits on this branch, that’s just us tightening the integration and validating it before merge. |
|
Please feel free to edit, discard or integrate it as you see fit. I'm just happy to see this functionality make it in the main project and hope it expands the audience. |
Adds a focused, sender-side automation surface so TargetBridge can be driven without the GUI — for scripting, SSH, and connecting automatically on login or wake. (Following up on our discussion — thanks for the green light!)
Two equivalent entry points, both handled in the existing app entry point and dispatched to
TBDisplaySenderService.shared— they call the samesession.connect()/stop()the GUI uses, so there's no separate/parallel controlpath:
open "targetbridge://connect?receiver=auto&mode=mirror&preset=1440p"/open "targetbridge://disconnect"open -a TargetBridge --args --connect --receiver auto --preset 1440p(login item / LaunchAgent)Options:
receiver(auto | id | name | ip),mode(mirror | extended),preset,transport,session,local-ip.receiver=autowaits briefly for Bonjour and uses the first receiver; a raw IP/hostname bypasses discovery.Also included:
cli/targetbridge— a thin shell wrapper over the URL scheme.docs/Automation.md— CLI, URL scheme, launch-at-login, SSH (launchctl asuser … open), and wake recipes.Scope
Kept deliberately small per your guidance — practical sender-side connect automation that builds on the current model. Natural follow-ups (out of scope here): richer queryable control (status / wait-for-stream) and a long-lived control
socket.
Notes for review
targetbridgescheme via a small baseInfo.plistmerged with Xcode's generated one. The threeINFOPLIST_KEY_*usage strings move into that file because Xcode drops them onceINFOPLIST_FILEis set (verified thebuilt plist retains the generated keys + the scheme).
project.pbxprojis regenerated (xcodegen) to pick up the new file + theINFOPLIST_FILEsetting.connect()and bring up the capture display.How to test
Build + run the app, then:
open "targetbridge://connect?receiver=auto&mode=mirror&preset=1440p"thenopen "targetbridge://disconnect".🤖 Generated with Claude Code