A small Bash CLI for creating and managing secondary copies of macOS apps.
- Create a
-secondary.appcopy from an original.app - Read the original
CFBundleIdentifierand rewrite it with a.secondarysuffix - Re-sign the copied app locally
- Provide a unified
clone / launch / update / removeCLI - Keep the source app untouched and avoid deleting user
~/Librarydata by default
bin/app-dual clone "/Applications/WeChat.app"
bin/app-dual launch "/Applications/WeChat.app"
bin/app-dual update "/Applications/WeChat.app"
bin/app-dual remove "/Applications/WeChat.app"For apps under /Applications, typical macOS usage requires sudo for clone, update, and remove:
sudo bin/app-dual clone "/Applications/WeChat.app"
sudo bin/app-dual update "/Applications/WeChat.app"
sudo bin/app-dual remove "/Applications/WeChat.app"Given:
/Applications/WeChat.app
com.tencent.xinWeChat
The derived secondary copy becomes:
/Applications/WeChat-secondary.app
com.tencent.xinWeChat.secondary
Rules:
- the copied app name always appends
-secondary - the copied Bundle ID always appends
.secondary clone,update, andremoveexpect the original.apppath and reject an existing-secondary.appas the source input
- validate the original app path
- copy the app bundle to
-secondary.app - rewrite the copied
CFBundleIdentifier - re-sign the copied app
Example:
sudo bin/app-dual clone "/Applications/WeChat.app"- derive the
-secondary.apppath from the original app path - launch the secondary app through
open -a - keep path-first behavior: if the secondary copy exists, launch can still work even when the original app path does not currently exist
Example:
bin/app-dual launch "/Applications/WeChat.app"- remove the old secondary copy
- rebuild the secondary copy from the original app
- keep user data directories untouched; do not clean
~/Library
Example:
sudo bin/app-dual update "/Applications/WeChat.app"- remove the derived
-secondary.app - do not remove
~/Library, Keychain, or other user data
Example:
sudo bin/app-dual remove "/Applications/WeChat.app"clonecreates a new secondary app copylaunchbehaves similarly to manually openingWeChat-secondary.appupdaterebuilds the secondary app by removing the old secondary app (if it exists) and recreating itremovedeletes only the copied app bundle and does not affect the source app
- the source app bundle is never modified
- the source app
Info.plistis never modified - the source app signature is never modified
- the tool never deletes
~/Library, Keychain, or other user data - not every macOS app can run as a second instance
Run the tests with:
bash tests/run.shCurrent automated coverage includes:
- shared path and naming helpers
clonebehavior and copied Bundle ID rewritinglaunchbehavior and the path-first contractupdate/removebehavior- the safety guarantee that the source app is not modified
See CONTRIBUTING for contribution guidelines and SECURITY for vulnerability reporting.
See LICENSE.