@denisixnpm/agent-rdp 0.0.1 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +270 -0
- package/README.md +525 -0
- package/bin/cli.js +61 -0
- package/dist/automation.d.ts +199 -0
- package/dist/automation.d.ts.map +1 -0
- package/dist/automation.js +309 -0
- package/dist/automation.js.map +1 -0
- package/dist/client.d.ts +45 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +146 -0
- package/dist/client.js.map +1 -0
- package/dist/daemon.d.ts +32 -0
- package/dist/daemon.d.ts.map +1 -0
- package/dist/daemon.js +175 -0
- package/dist/daemon.js.map +1 -0
- package/dist/generated/AccessibilityElement.d.ts +47 -0
- package/dist/generated/AccessibilityElement.d.ts.map +1 -0
- package/dist/generated/AccessibilityElement.js +2 -0
- package/dist/generated/AccessibilityElement.js.map +1 -0
- package/dist/generated/AccessibilitySnapshot.d.ts +27 -0
- package/dist/generated/AccessibilitySnapshot.d.ts.map +1 -0
- package/dist/generated/AccessibilitySnapshot.js +2 -0
- package/dist/generated/AccessibilitySnapshot.js.map +1 -0
- package/dist/generated/AutomateRequest.d.ts +192 -0
- package/dist/generated/AutomateRequest.d.ts.map +1 -0
- package/dist/generated/AutomateRequest.js +2 -0
- package/dist/generated/AutomateRequest.js.map +1 -0
- package/dist/generated/AutomationHandshake.d.ts +26 -0
- package/dist/generated/AutomationHandshake.d.ts.map +1 -0
- package/dist/generated/AutomationHandshake.js +3 -0
- package/dist/generated/AutomationHandshake.js.map +1 -0
- package/dist/generated/AutomationScrollDirection.d.ts +5 -0
- package/dist/generated/AutomationScrollDirection.d.ts.map +1 -0
- package/dist/generated/AutomationScrollDirection.js +3 -0
- package/dist/generated/AutomationScrollDirection.js.map +1 -0
- package/dist/generated/AutomationStatus.d.ts +22 -0
- package/dist/generated/AutomationStatus.d.ts.map +1 -0
- package/dist/generated/AutomationStatus.js +3 -0
- package/dist/generated/AutomationStatus.js.map +1 -0
- package/dist/generated/ClickResult.d.ts +22 -0
- package/dist/generated/ClickResult.d.ts.map +1 -0
- package/dist/generated/ClickResult.js +3 -0
- package/dist/generated/ClickResult.js.map +1 -0
- package/dist/generated/ClipboardRequest.d.ts +10 -0
- package/dist/generated/ClipboardRequest.d.ts.map +1 -0
- package/dist/generated/ClipboardRequest.js +3 -0
- package/dist/generated/ClipboardRequest.js.map +1 -0
- package/dist/generated/ConnectRequest.d.ts +68 -0
- package/dist/generated/ConnectRequest.d.ts.map +1 -0
- package/dist/generated/ConnectRequest.js +2 -0
- package/dist/generated/ConnectRequest.js.map +1 -0
- package/dist/generated/ConnectionState.d.ts +5 -0
- package/dist/generated/ConnectionState.d.ts.map +1 -0
- package/dist/generated/ConnectionState.js +3 -0
- package/dist/generated/ConnectionState.js.map +1 -0
- package/dist/generated/DriveMapping.d.ts +14 -0
- package/dist/generated/DriveMapping.d.ts.map +1 -0
- package/dist/generated/DriveMapping.js +3 -0
- package/dist/generated/DriveMapping.js.map +1 -0
- package/dist/generated/DriveRequest.d.ts +9 -0
- package/dist/generated/DriveRequest.d.ts.map +1 -0
- package/dist/generated/DriveRequest.js +3 -0
- package/dist/generated/DriveRequest.js.map +1 -0
- package/dist/generated/ElementBounds.d.ts +10 -0
- package/dist/generated/ElementBounds.d.ts.map +1 -0
- package/dist/generated/ElementBounds.js +3 -0
- package/dist/generated/ElementBounds.js.map +1 -0
- package/dist/generated/ElementValue.d.ts +23 -0
- package/dist/generated/ElementValue.d.ts.map +1 -0
- package/dist/generated/ElementValue.js +2 -0
- package/dist/generated/ElementValue.js.map +1 -0
- package/dist/generated/ErrorCode.d.ts +5 -0
- package/dist/generated/ErrorCode.d.ts.map +1 -0
- package/dist/generated/ErrorCode.js +3 -0
- package/dist/generated/ErrorCode.js.map +1 -0
- package/dist/generated/ErrorInfo.d.ts +15 -0
- package/dist/generated/ErrorInfo.d.ts.map +1 -0
- package/dist/generated/ErrorInfo.js +2 -0
- package/dist/generated/ErrorInfo.js.map +1 -0
- package/dist/generated/FileIpcError.d.ts +14 -0
- package/dist/generated/FileIpcError.d.ts.map +1 -0
- package/dist/generated/FileIpcError.js +3 -0
- package/dist/generated/FileIpcError.js.map +1 -0
- package/dist/generated/FileIpcRequest.d.ts +18 -0
- package/dist/generated/FileIpcRequest.d.ts.map +1 -0
- package/dist/generated/FileIpcRequest.js +3 -0
- package/dist/generated/FileIpcRequest.js.map +1 -0
- package/dist/generated/FileIpcResponse.d.ts +27 -0
- package/dist/generated/FileIpcResponse.d.ts.map +1 -0
- package/dist/generated/FileIpcResponse.js +2 -0
- package/dist/generated/FileIpcResponse.js.map +1 -0
- package/dist/generated/ImageFormat.d.ts +5 -0
- package/dist/generated/ImageFormat.d.ts.map +1 -0
- package/dist/generated/ImageFormat.js +3 -0
- package/dist/generated/ImageFormat.js.map +1 -0
- package/dist/generated/KeyboardRequest.d.ts +17 -0
- package/dist/generated/KeyboardRequest.d.ts.map +1 -0
- package/dist/generated/KeyboardRequest.js +3 -0
- package/dist/generated/KeyboardRequest.js.map +1 -0
- package/dist/generated/LocateRequest.d.ts +23 -0
- package/dist/generated/LocateRequest.d.ts.map +1 -0
- package/dist/generated/LocateRequest.js +3 -0
- package/dist/generated/LocateRequest.js.map +1 -0
- package/dist/generated/LocateResult.d.ts +15 -0
- package/dist/generated/LocateResult.d.ts.map +1 -0
- package/dist/generated/LocateResult.js +2 -0
- package/dist/generated/LocateResult.js.map +1 -0
- package/dist/generated/MappedDrive.d.ts +14 -0
- package/dist/generated/MappedDrive.d.ts.map +1 -0
- package/dist/generated/MappedDrive.js +3 -0
- package/dist/generated/MappedDrive.js.map +1 -0
- package/dist/generated/MouseButton.d.ts +5 -0
- package/dist/generated/MouseButton.d.ts.map +1 -0
- package/dist/generated/MouseButton.js +3 -0
- package/dist/generated/MouseButton.js.map +1 -0
- package/dist/generated/MouseRequest.d.ts +38 -0
- package/dist/generated/MouseRequest.d.ts.map +1 -0
- package/dist/generated/MouseRequest.js +2 -0
- package/dist/generated/MouseRequest.js.map +1 -0
- package/dist/generated/OcrMatch.d.ts +34 -0
- package/dist/generated/OcrMatch.d.ts.map +1 -0
- package/dist/generated/OcrMatch.js +3 -0
- package/dist/generated/OcrMatch.js.map +1 -0
- package/dist/generated/Request.d.ts +40 -0
- package/dist/generated/Request.d.ts.map +1 -0
- package/dist/generated/Request.js +2 -0
- package/dist/generated/Request.js.map +1 -0
- package/dist/generated/Response.d.ts +20 -0
- package/dist/generated/Response.d.ts.map +1 -0
- package/dist/generated/Response.js +2 -0
- package/dist/generated/Response.js.map +1 -0
- package/dist/generated/ResponseData.d.ts +92 -0
- package/dist/generated/ResponseData.d.ts.map +1 -0
- package/dist/generated/ResponseData.js +2 -0
- package/dist/generated/ResponseData.js.map +1 -0
- package/dist/generated/RunPollResult.d.ts +26 -0
- package/dist/generated/RunPollResult.d.ts.map +1 -0
- package/dist/generated/RunPollResult.js +3 -0
- package/dist/generated/RunPollResult.js.map +1 -0
- package/dist/generated/RunResult.d.ts +22 -0
- package/dist/generated/RunResult.d.ts.map +1 -0
- package/dist/generated/RunResult.js +3 -0
- package/dist/generated/RunResult.js.map +1 -0
- package/dist/generated/ScreenshotRequest.d.ts +11 -0
- package/dist/generated/ScreenshotRequest.d.ts.map +1 -0
- package/dist/generated/ScreenshotRequest.js +2 -0
- package/dist/generated/ScreenshotRequest.js.map +1 -0
- package/dist/generated/ScrollDirection.d.ts +5 -0
- package/dist/generated/ScrollDirection.d.ts.map +1 -0
- package/dist/generated/ScrollDirection.js +3 -0
- package/dist/generated/ScrollDirection.js.map +1 -0
- package/dist/generated/ScrollRequest.d.ts +20 -0
- package/dist/generated/ScrollRequest.d.ts.map +1 -0
- package/dist/generated/ScrollRequest.js +2 -0
- package/dist/generated/ScrollRequest.js.map +1 -0
- package/dist/generated/SessionInfo.d.ts +35 -0
- package/dist/generated/SessionInfo.d.ts.map +1 -0
- package/dist/generated/SessionInfo.js +2 -0
- package/dist/generated/SessionInfo.js.map +1 -0
- package/dist/generated/SessionSummary.d.ts +19 -0
- package/dist/generated/SessionSummary.d.ts.map +1 -0
- package/dist/generated/SessionSummary.js +2 -0
- package/dist/generated/SessionSummary.js.map +1 -0
- package/dist/generated/WaitState.d.ts +5 -0
- package/dist/generated/WaitState.d.ts.map +1 -0
- package/dist/generated/WaitState.js +3 -0
- package/dist/generated/WaitState.js.map +1 -0
- package/dist/generated/WindowAction.d.ts +5 -0
- package/dist/generated/WindowAction.d.ts.map +1 -0
- package/dist/generated/WindowAction.js +3 -0
- package/dist/generated/WindowAction.js.map +1 -0
- package/dist/generated/WindowInfo.d.ts +31 -0
- package/dist/generated/WindowInfo.d.ts.map +1 -0
- package/dist/generated/WindowInfo.js +2 -0
- package/dist/generated/WindowInfo.js.map +1 -0
- package/dist/generated/index.d.ts +41 -0
- package/dist/generated/index.d.ts.map +1 -0
- package/dist/generated/index.js +3 -0
- package/dist/generated/index.js.map +1 -0
- package/dist/index.d.ts +211 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +341 -0
- package/dist/index.js.map +1 -0
- package/dist/types.d.ts +125 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +17 -0
- package/dist/types.js.map +1 -0
- package/models/text-detection.rten +0 -0
- package/models/text-recognition.rten +0 -0
- package/package.json +52 -5
package/README.md
ADDED
|
@@ -0,0 +1,525 @@
|
|
|
1
|
+
# agent-rdp
|
|
2
|
+
|
|
3
|
+
A CLI tool for AI agents to control Windows Remote Desktop sessions, built on [IronRDP](https://github.com/Devolutions/IronRDP).
|
|
4
|
+
|
|
5
|
+
## Demo
|
|
6
|
+
|
|
7
|
+
Claude Code automating SQLite database and table creation via RDP:
|
|
8
|
+
|
|
9
|
+
https://github.com/user-attachments/assets/91892b39-4edb-412b-b265-55ccd75d7421
|
|
10
|
+
|
|
11
|
+
## Features
|
|
12
|
+
|
|
13
|
+
- **Connect to RDP servers** - Full RDP protocol support with TLS and CredSSP authentication
|
|
14
|
+
- **Take screenshots** - Capture the remote desktop as PNG or JPEG
|
|
15
|
+
- **Mouse control** - Click, double-click, right-click, drag, scroll
|
|
16
|
+
- **Keyboard input** - Type text, press key combinations (Ctrl+C, Alt+Tab, etc.)
|
|
17
|
+
- **Clipboard sync** - Copy/paste text between local machine and remote Windows
|
|
18
|
+
- **Drive mapping** - Map local directories as network drives on the remote machine
|
|
19
|
+
- **UI Automation** - Interact with Windows applications via accessibility API (click, select, toggle, expand)
|
|
20
|
+
- **OCR text location** - Find text on screen using OCR when UI Automation isn't available
|
|
21
|
+
- **JSON output** - Structured output for AI agent consumption
|
|
22
|
+
- **Session management** - Multiple named sessions with automatic daemon lifecycle
|
|
23
|
+
|
|
24
|
+
## Installation
|
|
25
|
+
|
|
26
|
+
### From npm
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
npm install -g @denisixnpm/agent-rdp
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### As a Claude Code skill
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
npx add-skill https://github.com/denisix/agent-rdp
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### From source
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
git clone https://github.com/denisix/agent-rdp
|
|
42
|
+
cd agent-rdp
|
|
43
|
+
bun install
|
|
44
|
+
bun run build # Build native binary
|
|
45
|
+
bun run build:ts # Build TypeScript
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Using with AI Coding Agents
|
|
49
|
+
|
|
50
|
+
### Claude Code
|
|
51
|
+
|
|
52
|
+
Fastest activation — installs the [SKILL.md](skills/agent-rdp/SKILL.md) workflow so Claude knows the commands, flags, and gotchas without you explaining them:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
npx add-skill https://github.com/denisix/agent-rdp
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Or install manually:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
mkdir -p .claude/skills/agent-rdp
|
|
62
|
+
curl -o .claude/skills/agent-rdp/SKILL.md \
|
|
63
|
+
https://raw.githubusercontent.com/denisix/agent-rdp/main/skills/agent-rdp/SKILL.md
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Then just ask Claude Code, in plain language:
|
|
67
|
+
|
|
68
|
+
```
|
|
69
|
+
Connect to 192.168.1.100 as Administrator (password: secret), open Notepad,
|
|
70
|
+
type "hello from Claude", and take a screenshot.
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Claude will run the underlying `agent-rdp connect`, `automate run`, `keyboard type`, and `screenshot` commands on its own.
|
|
74
|
+
|
|
75
|
+
### Codex
|
|
76
|
+
|
|
77
|
+
Codex doesn't have a skill-install mechanism, but it reads `AGENTS.md` for project instructions. Point it at this tool by adding a section to your `AGENTS.md`:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
cat >> AGENTS.md <<'EOF'
|
|
81
|
+
|
|
82
|
+
## Remote Windows control
|
|
83
|
+
|
|
84
|
+
Use the `agent-rdp` CLI (npm i -g @denisixnpm/agent-rdp) to control Windows machines via RDP:
|
|
85
|
+
connect, screenshot, mouse/keyboard input, and UI Automation. See
|
|
86
|
+
https://github.com/denisix/agent-rdp for the full command reference.
|
|
87
|
+
EOF
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Then prompt Codex the same way:
|
|
91
|
+
|
|
92
|
+
```
|
|
93
|
+
codex "Connect to the Windows VM at 192.168.1.100 (user Administrator, password
|
|
94
|
+
secret) using agent-rdp, open the Run dialog, launch calc.exe, and confirm it's
|
|
95
|
+
open with a screenshot."
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Codex will call `agent-rdp` as a regular shell command, same as any other CLI tool.
|
|
99
|
+
|
|
100
|
+
## Usage
|
|
101
|
+
|
|
102
|
+
### Connect to an RDP Server
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
# Using command line (password visible in process list - not recommended)
|
|
106
|
+
agent-rdp connect --host 192.168.1.100 --username Administrator --password 'secret'
|
|
107
|
+
|
|
108
|
+
# Using environment variables (recommended)
|
|
109
|
+
export AGENT_RDP_USERNAME=Administrator
|
|
110
|
+
export AGENT_RDP_PASSWORD=secret
|
|
111
|
+
agent-rdp connect --host 192.168.1.100
|
|
112
|
+
|
|
113
|
+
# Using stdin (most secure)
|
|
114
|
+
echo 'secret' | agent-rdp connect --host 192.168.1.100 --username Administrator --password-stdin
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Take a Screenshot
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
# Save to file (default: ./screenshot.png)
|
|
121
|
+
agent-rdp screenshot --output desktop.png
|
|
122
|
+
|
|
123
|
+
# JSON metadata (path/width/height — image is always written to disk)
|
|
124
|
+
agent-rdp --json screenshot --output desktop.png
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
> Note: the CLI no longer has `screenshot --base64`. For agent pipelines, write a file and encode it yourself, or use the Node.js API's `rdp.screenshot({ path })`, which writes to disk and returns `{ path, width, height }` without materializing base64 — prefer this over the default `rdp.screenshot()` (which returns `{ base64, width, height }`) when the caller doesn't need the raw bytes, since echoing a base64 image into an LLM context is expensive.
|
|
128
|
+
|
|
129
|
+
### Mouse Operations
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
# Click at position
|
|
133
|
+
agent-rdp mouse click 500 300
|
|
134
|
+
|
|
135
|
+
# Right-click
|
|
136
|
+
agent-rdp mouse right-click 500 300
|
|
137
|
+
|
|
138
|
+
# Double-click
|
|
139
|
+
agent-rdp mouse double-click 500 300
|
|
140
|
+
|
|
141
|
+
# Move cursor
|
|
142
|
+
agent-rdp mouse move 100 200
|
|
143
|
+
|
|
144
|
+
# Drag from (100,100) to (500,500)
|
|
145
|
+
agent-rdp mouse drag 100 100 500 500
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Keyboard Operations
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
# Type text (supports Unicode)
|
|
152
|
+
agent-rdp keyboard type "Hello, World!"
|
|
153
|
+
|
|
154
|
+
# Press key combinations
|
|
155
|
+
agent-rdp keyboard press "ctrl+c"
|
|
156
|
+
agent-rdp keyboard press "alt+tab"
|
|
157
|
+
agent-rdp keyboard press "ctrl+shift+esc"
|
|
158
|
+
|
|
159
|
+
# Press single keys (use press command)
|
|
160
|
+
agent-rdp keyboard press enter
|
|
161
|
+
agent-rdp keyboard press escape
|
|
162
|
+
agent-rdp keyboard press f5
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### Scroll
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
agent-rdp scroll up --amount 3
|
|
169
|
+
agent-rdp scroll down --amount 5
|
|
170
|
+
agent-rdp scroll left
|
|
171
|
+
agent-rdp scroll right
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### Locate (OCR)
|
|
175
|
+
|
|
176
|
+
Find text on screen using OCR (powered by [ocrs](https://github.com/robertknight/ocrs)). Useful when UI Automation can't access certain elements (WebView content, some dialogs).
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
# Find lines containing text
|
|
180
|
+
agent-rdp locate "Cancel"
|
|
181
|
+
|
|
182
|
+
# Pattern matching (glob-style)
|
|
183
|
+
agent-rdp locate "Save*" --pattern
|
|
184
|
+
|
|
185
|
+
# Get all text on screen
|
|
186
|
+
agent-rdp locate --all
|
|
187
|
+
|
|
188
|
+
# JSON output
|
|
189
|
+
agent-rdp locate "OK" --json
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
Returns text lines with coordinates for clicking:
|
|
193
|
+
```
|
|
194
|
+
Found 1 line(s) containing 'Cancel':
|
|
195
|
+
'Cancel Button' at (650, 420) size 80x14 - center: (690, 427)
|
|
196
|
+
|
|
197
|
+
To click the first match: agent-rdp mouse click 690 427
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### Clipboard
|
|
201
|
+
|
|
202
|
+
```bash
|
|
203
|
+
# Set clipboard text (available when you paste on Windows)
|
|
204
|
+
agent-rdp clipboard set "Hello from CLI"
|
|
205
|
+
|
|
206
|
+
# Get clipboard text (after copying on Windows)
|
|
207
|
+
agent-rdp clipboard get
|
|
208
|
+
|
|
209
|
+
# With JSON output
|
|
210
|
+
agent-rdp --json clipboard get
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
### Drive Mapping
|
|
214
|
+
|
|
215
|
+
Map local directories as network drives on the remote Windows machine. Drives must be mapped at connect time. Multiple drives can be specified.
|
|
216
|
+
|
|
217
|
+
```bash
|
|
218
|
+
# Map local directories during connection
|
|
219
|
+
agent-rdp connect --host 192.168.1.100 -u Administrator -p secret \
|
|
220
|
+
--drive /home/user/documents:Documents \
|
|
221
|
+
--drive /tmp/shared:Shared
|
|
222
|
+
|
|
223
|
+
# List mapped drives
|
|
224
|
+
agent-rdp drive list
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
On the remote Windows machine, mapped drives appear in File Explorer as network locations.
|
|
228
|
+
|
|
229
|
+
### UI Automation
|
|
230
|
+
|
|
231
|
+
Interact with Windows applications programmatically via the Windows UI Automation API using native patterns (InvokePattern, SelectionItemPattern, TogglePattern, etc.). When enabled, a PowerShell agent is injected into the remote session that captures the accessibility tree and performs actions. Communication between the CLI and the agent uses a Dynamic Virtual Channel (DVC) for fast bidirectional IPC.
|
|
232
|
+
|
|
233
|
+
For detailed documentation, see [AUTOMATION.md](https://github.com/denisix/agent-rdp/blob/main/docs/AUTOMATION.md).
|
|
234
|
+
|
|
235
|
+
```bash
|
|
236
|
+
# Connect with automation enabled
|
|
237
|
+
agent-rdp connect --host 192.168.1.100 -u Admin -p secret --enable-win-automation
|
|
238
|
+
|
|
239
|
+
# Take an accessibility tree snapshot (refs are always included)
|
|
240
|
+
agent-rdp automate snapshot
|
|
241
|
+
|
|
242
|
+
# Snapshot filtering options (like agent-browser)
|
|
243
|
+
agent-rdp automate snapshot -i # Interactive elements only
|
|
244
|
+
agent-rdp automate snapshot -c # Compact (remove empty structural elements)
|
|
245
|
+
agent-rdp automate snapshot -d 3 # Limit depth to 3 levels
|
|
246
|
+
agent-rdp automate snapshot -s "~*Notepad*" # Scope to a window/element
|
|
247
|
+
agent-rdp automate snapshot -i -c -d 5 # Combine options
|
|
248
|
+
|
|
249
|
+
# Pattern-based element operations (refs use @eN format)
|
|
250
|
+
agent-rdp automate click "#SaveButton" # Click button
|
|
251
|
+
agent-rdp automate click "@e5" # Click by ref number from snapshot
|
|
252
|
+
agent-rdp automate click "@e5" -d # Double-click (for file list items)
|
|
253
|
+
agent-rdp automate select "@e10" # Select item (SelectionItemPattern)
|
|
254
|
+
agent-rdp automate toggle "@e7" # Toggle checkbox (TogglePattern)
|
|
255
|
+
agent-rdp automate expand "@e3" # Expand menu (ExpandCollapsePattern)
|
|
256
|
+
agent-rdp automate context-menu "@e5" # Open context menu (Shift+F10)
|
|
257
|
+
|
|
258
|
+
# Fill text fields
|
|
259
|
+
agent-rdp automate fill ".Edit" "Hello World"
|
|
260
|
+
|
|
261
|
+
# Window operations
|
|
262
|
+
agent-rdp automate window list
|
|
263
|
+
agent-rdp automate window focus "~*Notepad*"
|
|
264
|
+
|
|
265
|
+
# Run PowerShell commands
|
|
266
|
+
agent-rdp automate run "Get-Process" --wait
|
|
267
|
+
agent-rdp automate run "Get-Process" --wait --process-timeout 5000 # With 5s timeout
|
|
268
|
+
agent-rdp automate run "$PSVersionTable" --wait --shell pwsh.exe # Run through PowerShell 7 instead of Windows PowerShell
|
|
269
|
+
|
|
270
|
+
# Stream output from a long-running command instead of waiting for it to exit
|
|
271
|
+
agent-rdp automate run "ping -t 127.0.0.1" --stream # Returns immediately with a pid
|
|
272
|
+
agent-rdp automate run-poll <pid> # Repeat to drain output incrementally; reports exit once the process ends
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
**Selector Types:**
|
|
276
|
+
- `@e5` or `@5` - Reference number from snapshot (e prefix recommended)
|
|
277
|
+
- `#SaveButton` - Automation ID
|
|
278
|
+
- `.Edit` - Win32 class name
|
|
279
|
+
- `~*pattern*` - Wildcard name match
|
|
280
|
+
- `File` - Element name (exact match)
|
|
281
|
+
|
|
282
|
+
**Snapshot Output Format:**
|
|
283
|
+
```
|
|
284
|
+
- Window "Notepad" [ref=e1, id=Notepad]
|
|
285
|
+
- MenuBar "Application" [ref=e2]
|
|
286
|
+
- MenuItem "File" [ref=e3]
|
|
287
|
+
- Edit "Text Editor" [ref=e5, value="Hello"]
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
### Session Management
|
|
291
|
+
|
|
292
|
+
```bash
|
|
293
|
+
# List active sessions
|
|
294
|
+
agent-rdp session list
|
|
295
|
+
|
|
296
|
+
# Get current session info
|
|
297
|
+
agent-rdp session info
|
|
298
|
+
|
|
299
|
+
# Close a session
|
|
300
|
+
agent-rdp session close
|
|
301
|
+
|
|
302
|
+
# Use a named session
|
|
303
|
+
agent-rdp --session work connect --host work-pc.local ...
|
|
304
|
+
agent-rdp --session work screenshot
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
### Disconnect
|
|
308
|
+
|
|
309
|
+
```bash
|
|
310
|
+
agent-rdp disconnect
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
### Web Viewer
|
|
314
|
+
|
|
315
|
+
Open the web-based viewer to see the remote desktop in your browser:
|
|
316
|
+
|
|
317
|
+
```bash
|
|
318
|
+
# Open viewer (connects to default streaming port 9224)
|
|
319
|
+
agent-rdp view
|
|
320
|
+
|
|
321
|
+
# Specify a different port
|
|
322
|
+
agent-rdp view --port 9224
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
The viewer requires WebSocket streaming to be enabled. Start a session with streaming:
|
|
326
|
+
|
|
327
|
+
```bash
|
|
328
|
+
agent-rdp --stream-port 9224 connect --host 192.168.1.100 -u Admin -p secret
|
|
329
|
+
agent-rdp view
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
## JSON Output
|
|
333
|
+
|
|
334
|
+
All commands support `--json` for structured output:
|
|
335
|
+
|
|
336
|
+
```bash
|
|
337
|
+
agent-rdp --json screenshot --output desktop.png
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
**Success response:**
|
|
341
|
+
```json
|
|
342
|
+
{
|
|
343
|
+
"success": true,
|
|
344
|
+
"data": {
|
|
345
|
+
"type": "screenshot",
|
|
346
|
+
"path": "desktop.png",
|
|
347
|
+
"width": 1920,
|
|
348
|
+
"height": 1080
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
**Error response:**
|
|
354
|
+
```json
|
|
355
|
+
{
|
|
356
|
+
"success": false,
|
|
357
|
+
"error": {
|
|
358
|
+
"code": "not_connected",
|
|
359
|
+
"message": "Not connected to an RDP server"
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
## Environment Variables
|
|
365
|
+
|
|
366
|
+
| Variable | Description |
|
|
367
|
+
|----------|-------------|
|
|
368
|
+
| `AGENT_RDP_HOST` | RDP server hostname or IP |
|
|
369
|
+
| `AGENT_RDP_PORT` | RDP server port (default: 3389) |
|
|
370
|
+
| `AGENT_RDP_USERNAME` | RDP username |
|
|
371
|
+
| `AGENT_RDP_PASSWORD` | RDP password |
|
|
372
|
+
| `AGENT_RDP_SESSION` | Session name (default: "default") |
|
|
373
|
+
| `AGENT_RDP_STREAM_PORT` | WebSocket streaming port (0 = disabled) |
|
|
374
|
+
| `AGENT_RDP_MODELS_DIR` | Override the OCR models directory (set automatically by the npm wrapper; useful for standalone binary installs) |
|
|
375
|
+
|
|
376
|
+
## Node.js API
|
|
377
|
+
|
|
378
|
+
Use agent-rdp programmatically from Node.js/TypeScript:
|
|
379
|
+
|
|
380
|
+
```typescript
|
|
381
|
+
import { RdpSession } from 'agent-rdp';
|
|
382
|
+
|
|
383
|
+
const rdp = new RdpSession({ session: 'default' });
|
|
384
|
+
|
|
385
|
+
await rdp.connect({
|
|
386
|
+
host: '192.168.1.100',
|
|
387
|
+
username: 'Administrator',
|
|
388
|
+
password: 'secret',
|
|
389
|
+
width: 1280,
|
|
390
|
+
height: 800,
|
|
391
|
+
drives: [{ path: '/tmp/share', name: 'Share' }],
|
|
392
|
+
enableWinAutomation: true, // Enable UI Automation
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
// Screenshot - prefer `path` so a large base64 string never has to be
|
|
396
|
+
// held in memory or echoed into an agent's context
|
|
397
|
+
const { path, width, height } = await rdp.screenshot({ format: 'png', path: 'screenshot.png' });
|
|
398
|
+
|
|
399
|
+
// Or get raw base64 directly (e.g. for further in-process processing)
|
|
400
|
+
const { base64 } = await rdp.screenshot({ format: 'png' });
|
|
401
|
+
|
|
402
|
+
// Mouse
|
|
403
|
+
await rdp.mouse.click({ x: 100, y: 200 });
|
|
404
|
+
await rdp.mouse.rightClick({ x: 100, y: 200 });
|
|
405
|
+
await rdp.mouse.doubleClick({ x: 100, y: 200 });
|
|
406
|
+
await rdp.mouse.move({ x: 150, y: 250 });
|
|
407
|
+
await rdp.mouse.drag({ from: { x: 100, y: 100 }, to: { x: 500, y: 500 } });
|
|
408
|
+
|
|
409
|
+
// Keyboard
|
|
410
|
+
await rdp.keyboard.type({ text: 'Hello World' });
|
|
411
|
+
await rdp.keyboard.press({ keys: 'ctrl+c' });
|
|
412
|
+
await rdp.keyboard.press({ keys: 'enter' }); // Single keys use press()
|
|
413
|
+
|
|
414
|
+
// Scroll
|
|
415
|
+
await rdp.scroll.up(); // Default amount: 3
|
|
416
|
+
await rdp.scroll.down({ amount: 5 }); // Custom amount
|
|
417
|
+
await rdp.scroll.up({ x: 500, y: 300 }); // Scroll at position
|
|
418
|
+
|
|
419
|
+
// Clipboard
|
|
420
|
+
await rdp.clipboard.set({ text: 'text to copy' });
|
|
421
|
+
const text = await rdp.clipboard.get();
|
|
422
|
+
|
|
423
|
+
// Locate text using OCR
|
|
424
|
+
const matches = await rdp.locate({ text: 'Cancel' });
|
|
425
|
+
if (matches.length > 0) {
|
|
426
|
+
await rdp.mouse.click({ x: matches[0].center_x, y: matches[0].center_y });
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
// Get all text on screen
|
|
430
|
+
const allText = await rdp.locate({ all: true });
|
|
431
|
+
|
|
432
|
+
// Automation (requires --enable-win-automation at connect)
|
|
433
|
+
const snapshot = await rdp.automation.snapshot({ interactive: true });
|
|
434
|
+
await rdp.automation.click('@e5'); // Click button by ref
|
|
435
|
+
await rdp.automation.click('@e5', { doubleClick: true }); // Double-click
|
|
436
|
+
await rdp.automation.select('@e10'); // Select item
|
|
437
|
+
await rdp.automation.toggle('@e7'); // Toggle checkbox
|
|
438
|
+
await rdp.automation.expand('@e3'); // Expand menu
|
|
439
|
+
await rdp.automation.contextMenu('@e5'); // Open context menu
|
|
440
|
+
await rdp.automation.fill('#input', 'text'); // Fill text field
|
|
441
|
+
await rdp.automation.run('notepad.exe'); // Run command
|
|
442
|
+
await rdp.automation.waitFor('#SaveButton', { timeout: 5000 });
|
|
443
|
+
|
|
444
|
+
// Window management
|
|
445
|
+
const windows = await rdp.automation.listWindows();
|
|
446
|
+
await rdp.automation.focusWindow('~*Notepad*');
|
|
447
|
+
await rdp.automation.maximizeWindow();
|
|
448
|
+
|
|
449
|
+
// Drives
|
|
450
|
+
const drives = await rdp.drives.list();
|
|
451
|
+
|
|
452
|
+
// Session info
|
|
453
|
+
const info = await rdp.getInfo();
|
|
454
|
+
|
|
455
|
+
// Disconnect
|
|
456
|
+
await rdp.disconnect();
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
### WebSocket Streaming
|
|
460
|
+
|
|
461
|
+
Enable WebSocket streaming for real-time screen capture and bidirectional clipboard support:
|
|
462
|
+
|
|
463
|
+
```typescript
|
|
464
|
+
const rdp = new RdpSession({
|
|
465
|
+
session: 'viewer',
|
|
466
|
+
streamPort: 9224, // Enable streaming
|
|
467
|
+
});
|
|
468
|
+
|
|
469
|
+
await rdp.connect({...});
|
|
470
|
+
|
|
471
|
+
// Connect your WebSocket client to receive JPEG frames
|
|
472
|
+
const streamUrl = rdp.getStreamUrl(); // "ws://localhost:9224"
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
For the complete WebSocket protocol specification (message types, clipboard flow, input handling), see [WEBSOCKET.md](https://github.com/denisix/agent-rdp/blob/main/docs/WEBSOCKET.md).
|
|
476
|
+
|
|
477
|
+
## Architecture
|
|
478
|
+
|
|
479
|
+
agent-rdp uses a daemon-per-session architecture:
|
|
480
|
+
|
|
481
|
+
1. **CLI** (`agent-rdp`) - Parses commands and communicates with the daemon
|
|
482
|
+
2. **Daemon** - Maintains the RDP connection and processes commands
|
|
483
|
+
3. **IPC** - Unix sockets (macOS/Linux) or TCP (Windows)
|
|
484
|
+
|
|
485
|
+
The daemon is automatically started on the first command and persists until explicitly closed or the session times out.
|
|
486
|
+
|
|
487
|
+
## Limitations
|
|
488
|
+
|
|
489
|
+
### UI Automation
|
|
490
|
+
|
|
491
|
+
- **WebViews**: UI Automation cannot interact with WebView content (e.g., Windows Start menu search, Edge browser content, Electron apps). Use `Win+R` or `automate run` to launch programs directly instead of clicking through menus.
|
|
492
|
+
- **UAC Dialogs**: User Account Control elevation prompts run on a secure desktop and are not accessible via UI Automation. There is no good workaround - the remote user must interact with UAC manually, or UAC must be disabled (not recommended for security reasons).
|
|
493
|
+
|
|
494
|
+
### OCR Fallback
|
|
495
|
+
|
|
496
|
+
When UI Automation cannot access certain elements, the `locate` command provides OCR-based text detection:
|
|
497
|
+
|
|
498
|
+
```bash
|
|
499
|
+
agent-rdp locate "Button Text" # Find text and get coordinates
|
|
500
|
+
agent-rdp mouse click <x> <y> # Click at returned coordinates
|
|
501
|
+
```
|
|
502
|
+
|
|
503
|
+
This is not highly reliable (OCR can misread characters, miss text, or return imprecise coordinates), but may work for simple cases like dialog buttons.
|
|
504
|
+
|
|
505
|
+
### Screenshot Coordinate Detection
|
|
506
|
+
|
|
507
|
+
**Claude models** (in non-computer-use mode, such as Claude Code) are poor at estimating pixel coordinates from screenshots. Do not ask Claude to look at a screenshot and guess where to click - it will likely be inaccurate.
|
|
508
|
+
|
|
509
|
+
**Gemini models** are generally good at pixel coordinate estimation from images.
|
|
510
|
+
|
|
511
|
+
If you need vision-based coordinate detection with Claude, implement your own harness using Claude's [Computer Use Tool](https://docs.anthropic.com/en/docs/agents-and-tools/computer-use) which is specifically designed for this purpose.
|
|
512
|
+
|
|
513
|
+
## Requirements
|
|
514
|
+
|
|
515
|
+
- Rust 1.75 or later
|
|
516
|
+
- Target RDP server with Network Level Authentication (NLA) enabled
|
|
517
|
+
- Target RDP server must support TLS 1.2 or later. agent-rdp uses `rustls`, which does not implement TLS 1.0/1.1, so legacy targets (e.g. Windows Server 2008 R2) are not currently supported and will fail with a TLS handshake error.
|
|
518
|
+
|
|
519
|
+
## Credits
|
|
520
|
+
|
|
521
|
+
Originally created by [Nick Yu](https://github.com/thisnick) ([thisnick/agent-rdp](https://github.com/thisnick/agent-rdp)). This fork ([denisix/agent-rdp](https://github.com/denisix/agent-rdp), published to npm as [`@denisixnpm/agent-rdp`](https://www.npmjs.com/package/@denisixnpm/agent-rdp)) is maintained independently with additional fixes and features; see [CHANGELOG.md](packages/agent-rdp/CHANGELOG.md) for what's changed.
|
|
522
|
+
|
|
523
|
+
## License
|
|
524
|
+
|
|
525
|
+
MIT OR Apache-2.0 (same as IronRDP)
|
package/bin/cli.js
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* CLI entry point for agent-rdp.
|
|
5
|
+
* Resolves and executes the platform-specific binary.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { spawnSync } from 'node:child_process';
|
|
9
|
+
import { createRequire } from 'node:module';
|
|
10
|
+
import { dirname, join } from 'node:path';
|
|
11
|
+
import { chmodSync, existsSync, statSync } from 'node:fs';
|
|
12
|
+
import { fileURLToPath } from 'node:url';
|
|
13
|
+
|
|
14
|
+
const require = createRequire(import.meta.url);
|
|
15
|
+
|
|
16
|
+
const platform = process.platform; // darwin, linux, win32
|
|
17
|
+
const arch = process.arch; // arm64, x64
|
|
18
|
+
const ext = platform === 'win32' ? '.exe' : '';
|
|
19
|
+
const platformPackage = `@denisixnpm/agent-rdp-${platform}-${arch}`;
|
|
20
|
+
|
|
21
|
+
let binaryPath;
|
|
22
|
+
|
|
23
|
+
try {
|
|
24
|
+
const packageJsonPath = require.resolve(`${platformPackage}/package.json`);
|
|
25
|
+
binaryPath = join(dirname(packageJsonPath), 'bin', `agent-rdp${ext}`);
|
|
26
|
+
} catch {
|
|
27
|
+
console.error(`Error: Platform package ${platformPackage} is not installed.`);
|
|
28
|
+
console.error(`This platform (${platform}-${arch}) may not be supported.`);
|
|
29
|
+
process.exit(1);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (!existsSync(binaryPath)) {
|
|
33
|
+
console.error(`Error: Binary not found at ${binaryPath}`);
|
|
34
|
+
console.error(`The platform package ${platformPackage} may not be installed correctly.`);
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// npm only guarantees the executable bit for files declared in a package's
|
|
39
|
+
// "bin" field, and dependency install scripts are opt-in as of npm v12, so the
|
|
40
|
+
// platform package's postinstall chmod may never run. Restore it here.
|
|
41
|
+
// Non-fatal: read-only stores (pnpm, Nix, container layers) will throw.
|
|
42
|
+
if (process.platform !== 'win32') {
|
|
43
|
+
try {
|
|
44
|
+
if (!(statSync(binaryPath).mode & 0o111)) {
|
|
45
|
+
chmodSync(binaryPath, 0o755);
|
|
46
|
+
}
|
|
47
|
+
} catch {
|
|
48
|
+
// Fall through - spawnSync will surface a clearer error if it really can't run.
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// OCR models ship in this package (they're architecture-independent, so they
|
|
53
|
+
// aren't duplicated into each platform package). Tell the binary where to find them.
|
|
54
|
+
const modelsDir = join(dirname(fileURLToPath(import.meta.url)), '..', 'models');
|
|
55
|
+
|
|
56
|
+
const result = spawnSync(binaryPath, process.argv.slice(2), {
|
|
57
|
+
stdio: 'inherit',
|
|
58
|
+
env: { ...process.env, AGENT_RDP_MODELS_DIR: process.env.AGENT_RDP_MODELS_DIR ?? modelsDir },
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
process.exit(result.status ?? 1);
|