CursorRemote

repository·main·Indexed 41 days ago

https://github.com/len5ky/cursorremote

A remote control system for the Cursor AI agent that allows users to monitor sessions, approve steps, and send tasks via a mobile web browser or Telegram bot while Cursor runs locally. It utilizes a Node.js relay server connecting to the Cursor IDE via the Chrome DevTools Protocol (CDP) on port 9222.

Tokens
36.9K
Snippets
82
Records
169
Agent score
51%

What's inside CursorRemote

  1. Overview of the Telegram Transport Module

    main

    The Telegram transport module connects CursorRemote to a Telegram supergroup with forum topics. Each forum topic corresponds to a specific project and chat tab, allowing for organized, mobile-friendly monitoring and control of Cursor agents.

    Key capabilities include:

    • Real-time Streaming: Live chat, tool calls, and plan widgets are streamed into Telegram topics.
    • Interactive Approvals: Approve or reject tool calls, run shell commands, or interact with plan widgets using Telegram's inline keyboard buttons.
    • Agent Control: Send prompts directly via text in a topic, switch agent modes/models, and answer questionnaires using buttons.
    • Automated Organization: Auto-creates Telegram topics for new chat tabs when sync is enabled.
  2. Overview of CursorRemote

    main

    CursorRemote provides remote control for your local Cursor AI agent. It allows you to monitor sessions, approve/reject steps, inspect full plans, and send tasks from a mobile web browser or Telegram, while Cursor runs on your local machine.

    Core Architecture

    1. Cursor IDE: Must run with Chrome DevTools Protocol (CDP) enabled via --remote-debugging-port=9222.
    2. Relay Server: A Node.js server that connects via CDP to extract agent chat state from the DOM.
    3. Clients:
      • Browser Client: Real-time chat view with dark theme, plan modals, and command cards.
      • Telegram Bot: Mirrors conversations into auto-created forum topics for remote interaction.

    Setup Options

    • Extension (Recommended): Best for daily use. Includes a VS Code sidebar for lifecycle management, status monitoring, and a setup wizard. Automatically manages the server and license keys.
    • Standalone: Best for headless servers or CI. Requires manual configuration via .env and manual server management (npm start).
  3. Monitor Server Status in the Sidebar and Status Bar

    main

    The extension provides real-time visibility into the system state:

    Status Bar

    Located in the bottom status bar, it indicates the current state:

    • $(radio-tower) Remote: Running (Green): Server is healthy and CDP is connected.
    • $(radio-tower) Remote: Disconnected (Yellow): Server is running, but CDP is not connected.
    • $(radio-tower) Remote: Stopped (Default): Server is not running.
    • $(radio-tower) Remote: Error (Red): Server crashed or is unreachable.

    The cursorRemote activity bar view shows:

    • License Status: Prompt to enter a key or buy a license if unlicensed.
    • Server State: Uptime and whether the current window is the 'owner' or an 'observer'.
    • CDP Connection: Status and the active workspace name.
    • Agent Status: Current activity (e.g., idle, running_tool) and the model being used.
    • Clients: Number of active browser sessions.
    • Pending Approvals: A badge indicating actions requiring user consent.
    • Windows: A list of discovered Cursor windows.
  4. State Manager and Change Detection

    main

    The state-manager.ts module manages the application state by diffing successive CursorState objects extracted from the DOM.

    Algorithm:

    1. Receives a new CursorState.
    2. Performs a JSON.stringify-based comparison of top-level fields against the previous state.
    3. Generates a patch object containing only the changed fields.
    4. Debounces patches (default 300ms) to prevent excessive broadcasts during streaming.
    5. Emits a state:patch event.

    Emitted Events:

    • state:patch: Contains the partial state change.
    • connection:changed: Indicates a change in the CDP connection status.
  5. How the Telegram Transport architecture works

    main

    The Telegram Transport acts as a bridge between the Cursor Core System and Telegram via a bot (using the grammy framework). It translates internal state changes and commands into Telegram messages, topics, and interactive buttons, and vice versa.

    Core Components

    • TelegramTransport: Manages the lifecycle and event wiring.
    • TopicManager: Handles the bidirectional mapping between Telegram threadId and Cursor window+tab combinations.
    • MessageTracker: Tracks the relationship between a ChatElement.id and its corresponding Telegram message_id to allow for message updates.
    • Formatter: Converts ChatElement data into Telegram-compatible HTML.
    • Commands: Handles bot commands (e.g., /topics, /mode) and interactive callback queries (e.g., approve, run).

    System Integration

    The transport subscribes to events from the Core System (like StateManager events) and calls methods on the CommandExecutor, CDPBridge, and StateManager to perform actions in Cursor.

    ┌───────────────────────────────────────────────────────────┐
    │                    TelegramTransport                       │
    │                                                           │
    │  ┌─────────────┐  ┌──────────────┐  ┌─────────────────┐  │
    │  │ TopicManager │  │ MessageTracker│  │   Formatter     │  │
    │  │              │  │              │  │                 │  │
    │  │ threadId ↔   │  │ elementId →  │  │ ChatElement →   │  │
    │  │ window+tab    │  │ msgId[]      │  │ Telegram HTML   │  │
    │  └──────┬───────┘  └──────┬───────┘  └────────┬────────┘  │
    │         │                 │                    │           │
    │  ┌──────▼─────────────────▼────────────────────▼────────┐
    │  │              Bot (grammy)                            │
    │  │                                                           │
    │  │  Commands: /topics /mode /model /status              │
    │  │  Callbacks: approve, reject, run, skip, build, etc.    │
    │  │  Text: forwarded as sendMessage to Cursor            │
    │  │  Typing: sendChatAction loop while agent active       │
    │  └──────────────────────────────────────────────────────┘
    │                                                           │
    └────────────────────────┬──────────────────────────────────┘
                             │
                subscribes to│ calls
                             │
             ┌───────────────▼───────────────┐
             │         Core System           │
             │                               │
             │  StateManager  (events)       │
             │  CommandExecutor  (methods)    │
             │  CDPBridge  (switchWindow)     │
             └───────────────────────────────┘
  6. Understand the CursorRemote command data flow

    main

    Commands sent from remote clients (like a phone) are executed in the Cursor IDE through the following lifecycle:

    1. Emission: A client emits a socket.io event (e.g., command:approve, command:send_message).
    2. Validation: The relay server validates the payload and forwards it to the Command Executor.
    3. Translation: The executor translates the command into CDP actions such as Input.insertText, Input.dispatchKeyEvent, or Runtime.evaluate.
    4. Execution: CDP executes these actions against Cursor's DOM.
    5. Sync: The next observation cycle detects the resulting state change and broadcasts the updated state to all clients.
  7. High-Level Architecture of CursorRemote

    main

    CursorRemote operates using a three-tier architecture connected by two protocol bridges to allow remote control of the Cursor IDE from a browser-based phone client.

    The Three Tiers

    1. Cursor IDE: A standard Electron application launched with the --remote-debugging-port=9222 flag. This enables the Chrome DevTools Protocol (CDP) over a WebSocket without requiring any modifications to the Cursor binary itself.
    2. Relay Server: A Node.js/TypeScript process (typically running in WSL2) that acts as the central hub. It bridges the CDP protocol from the IDE with socket.io events for the client.
    3. Phone Client: A static web page (HTML/CSS/JS) served by the Relay Server. It interacts with the system exclusively through socket.io events.

    Communication Flow

    Cursor IDE <── CDP ──> Relay Server <── socket.io ──> Phone Client

    Cursor IDE  ←──CDP──→  Relay Server  ←──socket.io──→  Phone Client
    (Windows)               (WSL2/Node)                   (Browser)
  8. Understand the CursorState data model

    main

    The CursorState object is the top-level state representation of a Cursor session. It provides a comprehensive view of the connection status, agent activity, chat history, and available UI elements. Developers using this project to build remote interfaces (like Telegram bots or web dashboards) should use this object to synchronize their remote view with the actual state of the Cursor application.

    Key properties include:

    • connected: Connection status to Cursor via CDP.
    • agentStatus: The current durable status of the agent (e.g., idle, thinking, waiting_approval).
    • messages: An ordered array of ChatElement objects representing the conversation.
    • pendingApprovals: A list of tool calls currently awaiting user decision.
    • activeWindowId: The ID of the currently connected Cursor window.
    • questionnaire: The active agent questionnaire widget, if present.
    {
      "connected": boolean,
      "agentStatus": "idle" | "thinking" | "generating" | "running_tool" | "waiting_approval" | "error",
      "agentActivityText": string | null,
      "agentActivityLive": boolean,
      "agentActivitySource": "none" | "shimmer" | "loading_tool" | "loading_indicator" | "tail_thought",
      "messages": ChatElement[],
      "pendingApprovals": Approval[],
      "inputAvailable": boolean,
      "chatTabs": ChatTab[],
      "mode": ModeInfo,
      "model": ModelInfo,
      "windows": CursorWindow[],
      "activeWindowId": string,
      "composerQueue": ComposerQueueState,
      "questionnaire": Questionnaire | null
    }
  9. DOM Extraction and Element Classification

    main

    The dom-extractor.ts module periodically extracts the structured state of the Cursor UI from the DOM. It uses client.callFunction() to run extraction logic inside the renderer.

    Message Classification: Elements are identified by [data-flat-index] and classified using data-message-role and data-message-kind:

    data-message-roledata-message-kindResult Type
    humanhumanHumanMessage or PlanBlock
    aiassistantAssistantMessage
    aitoolPlanBlock, RunCommand, or ToolCallElement
    (none)(none)ThoughtBlock, LoadingIndicator, or skipped

    Tool Call Priority: Within the ai/tool branch, elements are classified in this order:

    1. .composer-create-plan-container $\rightarrow$ PlanBlock (todos, actions).
    2. .composer-terminal-tool-call-block-container $\rightarrow$ RunCommand (command text, Run/Skip/Allow).
    3. .composer-edit-file-review-wrapper $\rightarrow$ ToolCallElement (edit/review card with optional diffBlock).
    4. .composer-tool-former-message $\rightarrow$ ToolCallElement (compact summary).
    5. .ui-tool-call-line-action $\rightarrow$ ToolCallElement (expanded tool call line).
  10. Understand Telegram Callback Data Encoding

    main

    Because Telegram limits callback_data to 64 bytes, the project uses a compressed encoding scheme for interactive buttons:

    Format: {action}:{shortId}:{hash}

    • action: A short identifier for the operation (e.g., apr for approve, rej for reject, run for run command).
    • shortId: The first 8 characters of the element or approval ID.
    • hash: The first 8 characters of a hash of the selector path.

    A local Map<string, string> stores the mapping of hash $\rightarrow$ selectorPath to allow the bot to resolve which element is being acted upon.

    Examples

    • apr:abc12345:f7e3a1b2 (Approve action)
    • run:tool1234:9c8d7e6f (Run command)
    • mode:agent (Switch to agent mode - no hash required)
    • mdl:claude-4-opus (Switch to model - truncated to fit)
  11. How the VS Code / Cursor extension architecture works

    main

    The CursorRemote extension acts as a thin wrapper around the core server. It runs in the VS Code Extension Host (a Node.js process) and manages the server as a child process.

    Communication Channels

    • Configuration: Passed to the server via environment variables at spawn time.
    • Status Monitoring: The extension uses HTTP polling (GET /health every 5 seconds) to check server health.
    • Logging: Server logs (stdout/stderr) are piped to a LogOutputChannel within the editor.

    Singleton Server Pattern

    To prevent multiple instances, the extension implements a singleton pattern across all Cursor windows:

    1. Owner: The first window to start spawns the server process.
    2. Observer: Subsequent windows detect the running server via health polling and attach as observers without spawning a new process.
    3. Auto-Recovery: If the owner window closes, observers detect 3 consecutive failed health polls. After a random jitter (0–3s), one observer calls attemptTakeover() to spawn a new server and become the new owner.
    4. Race Conditions: If two windows attempt to spawn simultaneously, the extension catches EADDRINUSE errors and falls back to observer mode.