SYSTEM

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
bridge
localhost:3456

quick start

desktop app (recommended)

The easiest way to get started. Download, install, and the app guides you through everything.

  1. Download SYSTEM.dmg from releases
  2. Drag to Applications
  3. Launch and follow the onboarding wizard

The app lives in your menu bar and handles permissions, configuration, and auto-starts everything.

cli (advanced)

git clone https://github.com/ygwyg/system
cd system && npm install
npm run setup  # interactive wizard
npm start      # start everything

permissions

SYSTEM requires 3 macOS permissions to control your Mac. The desktop app guides you through granting these.

permission why it's needed
Accessibility Keyboard input, mouse control, window management
Screen Recording Screenshots for visual context
Automation Control apps: Finder, Safari, Notes, Calendar, Reminders, Music, Mail, System Events
granting permissions

System Settings → Privacy & Security → [Permission] → Enable SYSTEM. The desktop app will prompt you and open the right settings pane automatically.

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.

// Header
Authorization: Bearer <api_secret>

// Or query parameter
?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.

POST /chat

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"
  }]
}
POST /reset

Clear conversation history and state.

schedules

Schedule one-time or recurring tasks using natural language or cron syntax.

GET /schedules

List all scheduled tasks.

{
  "schedules": [{
    "id": "abc123",
    "description": "Play closing time",
    "scheduledAt": "2026-01-05T17:00:00Z",
    "cron": "0 17 * * *"
  }]
}
DELETE /schedules/:id

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 /state

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

computer use agent (CUA)

Visual automation tools that let AI see and interact with any application. The CUA takes screenshots, analyzes them with Claude's vision, and executes mouse/keyboard actions.

agentic loop

Use the /computer-use endpoint to run a full agentic loop: screenshot → analyze → act → verify → repeat until goal is complete.

vision & screenshots

tool description
cua_screenshot Take screenshot for visual analysis
cua_screen_info Get display information
cua_get_windows List all visible windows with positions
cua_mouse_position Get current cursor position

mouse actions

tool description
cua_click Click at coordinates (x, y)
cua_double_click Double-click at coordinates
cua_right_click Right-click (context menu)
cua_drag Click and drag from point to point
cua_scroll Scroll up/down/left/right

keyboard actions

tool description
cua_type Type text at cursor
cua_key Press key combo (e.g., "cmd+c", "return")
cua_select_all Cmd+A
cua_copy Cmd+C
cua_paste Cmd+V
cua_undo Cmd+Z
cua_save Cmd+S

app & window control

tool description
cua_focus_app Bring app to front
cua_launch_app Launch application
cua_window_manage Move, resize, minimize, maximize, close
cua_menu_click Click menu item by path (e.g., "File > Save")
cua_new_tab Cmd+T
cua_switch_tab Switch to tab 1-9
cua_close_window Cmd+W

utility

tool description
cua_wait Wait N seconds
cua_wait_for Wait for UI element to appear/disappear
cua_open_url Open URL in default browser

accessibility tools

Higher-level tools that find and interact with UI elements by role/title instead of coordinates.

tool description
ax_get_elements Get UI tree of frontmost app (roles, positions, titles)
ax_find Find elements by role (AXButton, AXTextField, etc.)
ax_click_element Find button by title and click it
ax_type_in_field Find text field and type into it
ax_focused_element Get currently focused element
ax_list_apps List running applications

computer-use endpoint

Run a full agentic loop to accomplish a visual task:

POST /computer-use
{
  "goal": "Click the Submit button",
  "maxIterations": 10,
  "app": "Safari"  // optional: focus this app first
}

response

{
  "success": true,
  "message": "Clicked Submit button",
  "iterations": 2,
  "actions": [
    { "tool": "cua_click", "args": {"x": 500, "y": 300}, "result": "Clicked" }
  ]
}
requires screen recording permission

CUA tools need Screen Recording permission to take screenshots. Grant this in System Settings → Privacy & Security → Screen Recording → Enable SYSTEM.

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

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:

// Extension: linear, Command: create-issue-for-myself
Tool name: linear_create_issue_for_myself

// Extension: spotify-player, Command: play
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:

raycast fallback
{
  "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.

GET /tools

List all available tools on the bridge.

POST /execute

Execute a specific tool.

{
  "tool": "open_app",
  "args": { "app": "Safari" }
}
GET /health

Check bridge status.

websocket

Real-time updates for scheduled tasks and notifications.

// Connect to WebSocket
const ws = new WebSocket('wss://your-agent.workers.dev/ws?token=...');

ws.onmessage = (event) => {
  const data = JSON.parse(event.data);
  // Types: scheduled_result, notification, bridge_status
  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.

🖥️ local execution

All commands run locally on your Mac. The bridge never exposes your system to the internet without the tunnel.

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

  1. Go to Cloudflare Zero Trust Dashboard
  2. Navigate to Access → Applications → Add an application
  3. Select Self-hosted and enter your worker URL
  4. Create an access policy (e.g., email = you@example.com)
  5. Save — users must now authenticate before accessing SYSTEM

automation (terraform)

# Note: wrangler doesn't support Access config directly
# Use Terraform for infrastructure-as-code

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 = ["you@example.com"]
  }
}
note

Cloudflare Access is configured separately from Workers deployment. The wrangler CLI doesn't manage Access policies — use the dashboard or Terraform.