remote mac automation
Control your Mac from anywhere using natural language. Built with Cloudflare Agents SDK for intelligent scheduling, memory, and tool orchestration.
agent url
https://your-agent.workers.dev
quick start
1. clone and install
git clone https://github.com/ygwyg/system
cd system && npm install
2. run setup wizard
npm run setup
Interactive setup: Anthropic API key, Raycast extensions, remote access.
3. start system
npm start
Starts bridge, tunnel, and opens the agent UI.
architecture
SYSTEM uses a split architecture for security: the Agent (brain) runs on Cloudflare Workers, while the Bridge (body) runs locally on your Mac.
┌───────────────────────────────────────────────────────┐ │ USER │ │ (phone/browser) │ └─────────────────────┬─────────────────────────────────┘ │ HTTPS ▼ ┌───────────────────────────────────────────────────────┐ │ AGENT (Brain) │ │ Cloudflare Workers │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │ │ Claude │ │ State │ │ Schedules │ │ │ │ AI │ │ (D.O.) │ │ (D.O.) │ │ │ └─────────────┘ └─────────────┘ └─────────────┘ │ └─────────────────────┬─────────────────────────────────┘ │ Tunnel ▼ ┌───────────────────────────────────────────────────────┐ │ BRIDGE (Body) │ │ Your Mac (local) │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │ │ AppleScript │ │ Shell │ │ Raycast │ │ │ └─────────────┘ └─────────────┘ └─────────────┘ │ └───────────────────────────────────────────────────────┘
agent (brain)
• Cloudflare Workers + D.O.
• Claude for NLP
• State, memory, scheduling
• WebSocket real-time
bridge (body)
• Local Express server
• AppleScript, shell exec
• Raycast extensions
• Cloudflare Tunnel
authentication
All requests require an API secret via Bearer token or query parameter.
Authorization: Bearer <api_secret>
?token=<api_secret>
note
The API secret is generated during npm run setup and stored in bridge.config.json.
chat
Send natural language commands to control your Mac.
Send a message to the agent.
request
{
"message": "Play some jazz music"
}
response
{
"message": "Playing jazz on Apple Music",
"actions": [{
"tool": "music_play",
"args": { "query": "jazz" },
"success": true,
"result": "Now playing: Jazz Vibes"
}]
}
Clear conversation history and state.
schedules
Schedule one-time or recurring tasks using natural language or cron syntax.
List all scheduled tasks.
{
"schedules": [{
"id": "abc123",
"description": "Play closing time",
"scheduledAt": "2026-01-05T17:00:00Z",
"cron": "0 17 * * *"
}]
}
Cancel a scheduled task by ID.
natural language examples
• "Remind me to call mom in 30 minutes"
• "Every day at 5pm, play Closing Time"
• "At 9am tomorrow, open Linear"
state
The agent maintains persistent state including preferences and conversation history.
Get current agent state for debugging.
{
"preferences": { "wife": "Jane" },
"historyLength": 12,
"scheduleCount": 2
}
core tools
Foundational tools for Mac automation.
| tool | description |
|---|---|
open_app | Open any application |
open_url | Open URL in browser |
shell | Run safe shell commands |
shell_list | List available shell commands |
applescript | Execute AppleScript |
notify | Show macOS notification |
say | Text-to-speech |
clipboard_get | Get clipboard contents |
clipboard_set | Set clipboard contents |
screenshot | Take screenshot |
music
Control Apple Music playback.
| tool | description |
|---|---|
music_play | Play/search music |
music_pause | Pause playback |
music_next | Skip to next track |
music_previous | Previous track |
music_current | Get current track info |
volume_get | Get volume level |
volume_set | Set volume (0-100) |
volume_up | Increase volume 10% |
volume_down | Decrease volume 10% |
volume_mute | Toggle mute |
messaging
Send iMessages with human-in-the-loop confirmation.
| tool | description |
|---|---|
search_contacts | Find contact by name |
send_imessage | Send iMessage |
safety flow
When you say "text my wife hello", SYSTEM will: 1) resolve "wife" from preferences, 2) search contacts, 3) ask for confirmation before sending.
system
Control system settings, get status, manage files and apps.
calendar & reminders
| tool | description |
|---|---|
calendar_today | Today's events |
calendar_upcoming | Upcoming events |
calendar_next | Next event |
calendar_create | Create event |
reminders_list | List reminders |
reminders_create | Create reminder |
reminders_complete | Complete reminder |
display & focus
| tool | description |
|---|---|
brightness_set | Set brightness |
dark_mode_toggle | Toggle dark mode |
dark_mode_status | Get dark mode status |
dnd_toggle | Toggle Do Not Disturb |
lock_screen | Lock Mac |
sleep_display | Sleep display |
sleep_mac | Sleep Mac |
system status
| tool | description |
|---|---|
battery_status | Battery level & charging |
wifi_status | WiFi network info |
storage_status | Disk space |
running_apps | List running apps |
front_app | Get frontmost app |
notes
Read and write Apple Notes.
| tool | description |
|---|---|
notes_list | List recent notes |
notes_search | Search notes by keyword |
notes_create | Create a new note |
notes_read | Read note content |
notes_append | Append to existing note |
files
Search and manage files via Finder.
| tool | description |
|---|---|
finder_search | Search files by name |
finder_downloads | List recent downloads |
finder_desktop | List desktop files |
finder_reveal | Reveal file in Finder |
finder_trash | Move file to trash |
shortcuts
Run Apple Shortcuts.
| tool | description |
|---|---|
shortcut_run | Run a shortcut by name |
shortcut_list | List available shortcuts |
tip
Create powerful automations in Shortcuts.app, then trigger them via SYSTEM. Example: "Run my Morning Routine shortcut"
browser
Get info from Safari, Chrome, Arc, or other browsers.
| tool | description |
|---|---|
browser_url | Get current tab URL |
browser_tabs | List open tabs |
raycast extensions
Execute Raycast extensions for powerful integrations. SYSTEM scans your installed extensions and makes them available as tools.
how it works
During npm run setup, SYSTEM scans your Raycast extensions folder and presents compatible commands for you to enable. Each enabled command becomes a dedicated tool.
Raycast Extension SYSTEM Tool ───────────────── ─────────── spotify-player/play → spotify_play linear/create-issue → linear_create_issue slack/send-message → slack_send_message
extension discovery
SYSTEM looks in ~/.config/raycast/extensions/ and reads each extension's package.json to find commands. Only commands with mode: "no-view" or mode: "view" are compatible.
compatible extension types
| type | works? | notes |
|---|---|---|
| No-view commands | ✅ Best | Execute silently, return result |
| View commands | ⚠️ Partial | Opens Raycast UI briefly |
| Form commands | ❌ No | Requires user input in Raycast |
| Menu bar commands | ❌ No | Background only |
popular extensions that work well
| extension | commands | use case |
|---|---|---|
spotify-player | play, pause, next, like | Music control |
linear | create-issue, search | Issue tracking |
slack | send-message, set-status | Team communication |
todoist | create-task, today | Task management |
github | create-issue, search | Code management |
notion | create-page, search | Notes & docs |
tool naming
Tools are named as {extension}_{command} with hyphens replaced by underscores:
Tool name: linear_create_issue_for_myself
Tool name: spotify_player_play
using raycast tools
Once enabled, just ask naturally:
- "Create a Linear issue for fixing the login bug"
- "Send a Slack message to #general saying hello"
- "Play Daft Punk on Spotify"
- "Add 'buy groceries' to my Todoist"
generic raycast tool
For extensions not in your enabled list, use the generic raycast tool:
{
"extension": "spotify-player",
"command": "play",
"arguments": { "query": "jazz" }
}
deep link format
Under the hood, SYSTEM uses Raycast deep links:
raycast://extensions/{author}/{extension}/{command}?arguments={json}
troubleshooting
extension not found
If an extension isn't showing in setup, make sure it's installed via Raycast Store, not manually. Check ~/.config/raycast/extensions/.
command opens raycast but doesn't execute
This usually means the command requires UI interaction (forms, selections). These commands aren't fully compatible. Try a different command from the same extension.
authentication errors
Many extensions require you to authenticate in Raycast first. Open Raycast and run the command manually once to complete OAuth/login flows.
re-scanning extensions
If you install new Raycast extensions, run setup again to add them:
npm run setup
Your existing configuration will be preserved—you'll just see new extensions to enable.
bridge api
Direct API to the local bridge. Used by the agent, but also available for custom integrations.
List all available tools on the bridge.
Execute a specific tool.
{
"tool": "open_app",
"args": { "app": "Safari" }
}
websocket
Real-time updates for scheduled tasks and notifications.
const ws = new WebSocket('wss://your-agent.workers.dev/ws?token=...');
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
console.log(data.type, data.payload);
};
| event type | description |
|---|---|
scheduled_result | Result of a scheduled task |
notification | System notification |
bridge_status | Bridge online/offline |
security
SYSTEM is designed with security as a priority.
🔐 authentication
Bearer token required for all requests. Tokens are generated during setup and stored locally.
🛡️ shell safety
Only allowlisted commands can run. Dangerous patterns (rm -rf, sudo, etc.) are blocked.
🚇 tunnel security
Quick Tunnels are ephemeral — new URL each session. Bridge binds to 0.0.0.0 only when tunnel is active.
👤 human-in-the-loop
Sensitive actions like sending messages require explicit user confirmation.
cloudflare access (recommended)
If you deploy to Cloudflare Workers, add Cloudflare Access for Zero Trust authentication at the edge — before requests even reach your agent.
strongly recommended
While the API secret provides application-level auth, Cloudflare Access adds network-level protection. Only authenticated users can reach your agent at all.
setup via dashboard
- Go to Cloudflare Zero Trust Dashboard
- Navigate to Access → Applications → Add an application
- Select Self-hosted and enter your worker URL
- Create an access policy (e.g., email =
[email protected]) - Save — users must now authenticate before accessing SYSTEM
automation (terraform)
resource "cloudflare_access_application" "system" {
zone_id = var.zone_id
name = "SYSTEM"
domain = "your-agent.workers.dev"
session_duration = "24h"
}
resource "cloudflare_access_policy" "allow_me" {
application_id = cloudflare_access_application.system.id
zone_id = var.zone_id
name = "Allow specific emails"
precedence = 1
decision = "allow"
include {
email = ["[email protected]"]
}
}
note
Cloudflare Access is configured separately from Workers deployment. The wrangler CLI doesn't manage Access policies — use the dashboard or Terraform.