Control a remote macOS machine from Linux/VPS via SSH — screenshots, commands, app control, file transfer.
Two shell scripts. No dependencies beyond ssh, scp, curl, and python3 (standard on any modern system).
| Script | Purpose |
|---|---|
mac_exec.sh |
Run commands, take screenshots, control apps, transfer files |
channel_check.sh |
Check SSH + bridge availability, output JSON |
Works great paired with OpenClaw as a remote Mac control layer for AI agents.
# 1. Clone
git clone https://github.com/ythx-101/remote-mac.git
cd remote-mac
# 2. Configure (copy example, fill in your SSH host/user)
cp remote-mac.conf.example ~/.remote-mac.conf
$EDITOR ~/.remote-mac.conf
# 3. Run
bash scripts/mac_exec.sh --run "sw_vers"Or configure via environment variables (no config file needed):
export MAC_SSH_HOST="your-mac-ip-or-hostname"
export MAC_SSH_USER="your-username"
export MAC_SSH_PORT="22" # default: 22bash scripts/mac_exec.sh --run "ls -la ~"
bash scripts/mac_exec.sh --run "brew list"
bash scripts/mac_exec.sh --run "some-long-script.sh" 120 # custom timeout (seconds)bash scripts/mac_exec.sh --screenshot # saves to /tmp/mac_screenshot.png
bash scripts/mac_exec.sh --screenshot /tmp/myscreen.pngRequires an active desktop session. Won't work if the screen is locked.
bash scripts/mac_exec.sh --app "open:Safari"
bash scripts/mac_exec.sh --app "activate:Finder"
bash scripts/mac_exec.sh --app "quit:Safari"
bash scripts/mac_exec.sh --app "kill:AppName" # force kill via pkill -x# Mac → local
bash scripts/mac_exec.sh --file "get:~/Desktop/report.pdf:/tmp/report.pdf"
# local → Mac
bash scripts/mac_exec.sh --file "put:/tmp/script.sh:~/Desktop/"bash scripts/channel_check.sh{
"timestamp": "2026-03-03T07:42:00Z",
"ssh": { "available": true, "latency_ms": 42, "target": "user@mac.local:22" },
"ag_bridge": { "available": false, "latency_ms": 0, "status": "" },
"preferred": "ssh"
}All config via ~/.remote-mac.conf or environment variables:
| Variable | Default | Description |
|---|---|---|
MAC_SSH_HOST |
mac.local |
SSH hostname or IP (Tailscale, LAN, etc.) |
MAC_SSH_USER |
current user | SSH username on the Mac |
MAC_SSH_PORT |
22 |
SSH port |
MAC_BRIDGE_URL |
(empty) | Optional: Antigravity bridge health URL |
REMOTE_MAC_CONF |
~/.remote-mac.conf |
Custom config file path |
REMOTE_MAC_CONTROL_DIR |
auto | Override SSH ControlPath directory |
See remote-mac.conf.example for a commented template.
- No hardcoded secrets — all config via env vars or
~/.remote-mac.conf(never committed) StrictHostKeyChecking=accept-new— non-interactive but won't silently accept changed host keys- Shell injection protection — app names are single-quoted via
shell_escape(); AppleScript usesargvpassing - JSON injection protection — all dynamic values in
channel_check.shoutput are encoded viapython3 json.dumps - Portable ControlPath — uses
XDG_RUNTIME_DIR→TMPDIR→~/.cache/remote-mac(works on both Linux and macOS)
When used with OpenClaw, remote-mac supports two independent control channels — SSH and OpenClaw Node — that serve as mutual fallbacks:
┌─────────────┐
│ VPS │
└──────┬──────┘
│
┌────────────┴────────────┐
│ │
SSH (mac_exec.sh) OpenClaw Node
port 22, any cmd Tailscale WebSocket
~50ms latency lower latency, auto-reconnect
│ │
└────────────┬────────────┘
│
┌──────▼──────┐
│ Mac │
└─────────────┘
| Channel | How | Best for |
|---|---|---|
| SSH | mac_exec.sh via ssh |
Any shell command, file transfer, screenshots |
| OpenClaw Node | nodes.run tool built into OpenClaw |
Low-latency ops, AI agent workflows, auto-reconnect |
channel_check.sh probes both channels and tells you which is available:
bash scripts/channel_check.sh
# → { "ssh": { "available": true }, "ag_bridge": { ... }, "preferred": "ssh" }If SSH is down, fall back to the Node channel. If both are up, prefer whichever has lower latency.
Keep Mac awake:
bash scripts/mac_exec.sh --run "caffeinate -d &"SSH multiplexing is enabled by default (ControlMaster=auto, ControlPersist=10m). First connection is slow; subsequent calls are near-instant.
Tailscale: Works great with Tailscale — set MAC_SSH_HOST to your Mac's Tailscale IP for reliable remote access from anywhere.
MIT