@denisixnpm/agent-rdp 0.0.1 → 0.7.0

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.
Files changed (190) hide show
  1. package/CHANGELOG.md +263 -0
  2. package/README.md +525 -0
  3. package/bin/cli.js +61 -0
  4. package/dist/automation.d.ts +199 -0
  5. package/dist/automation.d.ts.map +1 -0
  6. package/dist/automation.js +309 -0
  7. package/dist/automation.js.map +1 -0
  8. package/dist/client.d.ts +45 -0
  9. package/dist/client.d.ts.map +1 -0
  10. package/dist/client.js +146 -0
  11. package/dist/client.js.map +1 -0
  12. package/dist/daemon.d.ts +32 -0
  13. package/dist/daemon.d.ts.map +1 -0
  14. package/dist/daemon.js +175 -0
  15. package/dist/daemon.js.map +1 -0
  16. package/dist/generated/AccessibilityElement.d.ts +47 -0
  17. package/dist/generated/AccessibilityElement.d.ts.map +1 -0
  18. package/dist/generated/AccessibilityElement.js +2 -0
  19. package/dist/generated/AccessibilityElement.js.map +1 -0
  20. package/dist/generated/AccessibilitySnapshot.d.ts +27 -0
  21. package/dist/generated/AccessibilitySnapshot.d.ts.map +1 -0
  22. package/dist/generated/AccessibilitySnapshot.js +2 -0
  23. package/dist/generated/AccessibilitySnapshot.js.map +1 -0
  24. package/dist/generated/AutomateRequest.d.ts +192 -0
  25. package/dist/generated/AutomateRequest.d.ts.map +1 -0
  26. package/dist/generated/AutomateRequest.js +2 -0
  27. package/dist/generated/AutomateRequest.js.map +1 -0
  28. package/dist/generated/AutomationHandshake.d.ts +26 -0
  29. package/dist/generated/AutomationHandshake.d.ts.map +1 -0
  30. package/dist/generated/AutomationHandshake.js +3 -0
  31. package/dist/generated/AutomationHandshake.js.map +1 -0
  32. package/dist/generated/AutomationScrollDirection.d.ts +5 -0
  33. package/dist/generated/AutomationScrollDirection.d.ts.map +1 -0
  34. package/dist/generated/AutomationScrollDirection.js +3 -0
  35. package/dist/generated/AutomationScrollDirection.js.map +1 -0
  36. package/dist/generated/AutomationStatus.d.ts +22 -0
  37. package/dist/generated/AutomationStatus.d.ts.map +1 -0
  38. package/dist/generated/AutomationStatus.js +3 -0
  39. package/dist/generated/AutomationStatus.js.map +1 -0
  40. package/dist/generated/ClickResult.d.ts +22 -0
  41. package/dist/generated/ClickResult.d.ts.map +1 -0
  42. package/dist/generated/ClickResult.js +3 -0
  43. package/dist/generated/ClickResult.js.map +1 -0
  44. package/dist/generated/ClipboardRequest.d.ts +10 -0
  45. package/dist/generated/ClipboardRequest.d.ts.map +1 -0
  46. package/dist/generated/ClipboardRequest.js +3 -0
  47. package/dist/generated/ClipboardRequest.js.map +1 -0
  48. package/dist/generated/ConnectRequest.d.ts +68 -0
  49. package/dist/generated/ConnectRequest.d.ts.map +1 -0
  50. package/dist/generated/ConnectRequest.js +2 -0
  51. package/dist/generated/ConnectRequest.js.map +1 -0
  52. package/dist/generated/ConnectionState.d.ts +5 -0
  53. package/dist/generated/ConnectionState.d.ts.map +1 -0
  54. package/dist/generated/ConnectionState.js +3 -0
  55. package/dist/generated/ConnectionState.js.map +1 -0
  56. package/dist/generated/DriveMapping.d.ts +14 -0
  57. package/dist/generated/DriveMapping.d.ts.map +1 -0
  58. package/dist/generated/DriveMapping.js +3 -0
  59. package/dist/generated/DriveMapping.js.map +1 -0
  60. package/dist/generated/DriveRequest.d.ts +9 -0
  61. package/dist/generated/DriveRequest.d.ts.map +1 -0
  62. package/dist/generated/DriveRequest.js +3 -0
  63. package/dist/generated/DriveRequest.js.map +1 -0
  64. package/dist/generated/ElementBounds.d.ts +10 -0
  65. package/dist/generated/ElementBounds.d.ts.map +1 -0
  66. package/dist/generated/ElementBounds.js +3 -0
  67. package/dist/generated/ElementBounds.js.map +1 -0
  68. package/dist/generated/ElementValue.d.ts +23 -0
  69. package/dist/generated/ElementValue.d.ts.map +1 -0
  70. package/dist/generated/ElementValue.js +2 -0
  71. package/dist/generated/ElementValue.js.map +1 -0
  72. package/dist/generated/ErrorCode.d.ts +5 -0
  73. package/dist/generated/ErrorCode.d.ts.map +1 -0
  74. package/dist/generated/ErrorCode.js +3 -0
  75. package/dist/generated/ErrorCode.js.map +1 -0
  76. package/dist/generated/ErrorInfo.d.ts +15 -0
  77. package/dist/generated/ErrorInfo.d.ts.map +1 -0
  78. package/dist/generated/ErrorInfo.js +2 -0
  79. package/dist/generated/ErrorInfo.js.map +1 -0
  80. package/dist/generated/FileIpcError.d.ts +14 -0
  81. package/dist/generated/FileIpcError.d.ts.map +1 -0
  82. package/dist/generated/FileIpcError.js +3 -0
  83. package/dist/generated/FileIpcError.js.map +1 -0
  84. package/dist/generated/FileIpcRequest.d.ts +18 -0
  85. package/dist/generated/FileIpcRequest.d.ts.map +1 -0
  86. package/dist/generated/FileIpcRequest.js +3 -0
  87. package/dist/generated/FileIpcRequest.js.map +1 -0
  88. package/dist/generated/FileIpcResponse.d.ts +27 -0
  89. package/dist/generated/FileIpcResponse.d.ts.map +1 -0
  90. package/dist/generated/FileIpcResponse.js +2 -0
  91. package/dist/generated/FileIpcResponse.js.map +1 -0
  92. package/dist/generated/ImageFormat.d.ts +5 -0
  93. package/dist/generated/ImageFormat.d.ts.map +1 -0
  94. package/dist/generated/ImageFormat.js +3 -0
  95. package/dist/generated/ImageFormat.js.map +1 -0
  96. package/dist/generated/KeyboardRequest.d.ts +17 -0
  97. package/dist/generated/KeyboardRequest.d.ts.map +1 -0
  98. package/dist/generated/KeyboardRequest.js +3 -0
  99. package/dist/generated/KeyboardRequest.js.map +1 -0
  100. package/dist/generated/LocateRequest.d.ts +23 -0
  101. package/dist/generated/LocateRequest.d.ts.map +1 -0
  102. package/dist/generated/LocateRequest.js +3 -0
  103. package/dist/generated/LocateRequest.js.map +1 -0
  104. package/dist/generated/LocateResult.d.ts +15 -0
  105. package/dist/generated/LocateResult.d.ts.map +1 -0
  106. package/dist/generated/LocateResult.js +2 -0
  107. package/dist/generated/LocateResult.js.map +1 -0
  108. package/dist/generated/MappedDrive.d.ts +14 -0
  109. package/dist/generated/MappedDrive.d.ts.map +1 -0
  110. package/dist/generated/MappedDrive.js +3 -0
  111. package/dist/generated/MappedDrive.js.map +1 -0
  112. package/dist/generated/MouseButton.d.ts +5 -0
  113. package/dist/generated/MouseButton.d.ts.map +1 -0
  114. package/dist/generated/MouseButton.js +3 -0
  115. package/dist/generated/MouseButton.js.map +1 -0
  116. package/dist/generated/MouseRequest.d.ts +38 -0
  117. package/dist/generated/MouseRequest.d.ts.map +1 -0
  118. package/dist/generated/MouseRequest.js +2 -0
  119. package/dist/generated/MouseRequest.js.map +1 -0
  120. package/dist/generated/OcrMatch.d.ts +34 -0
  121. package/dist/generated/OcrMatch.d.ts.map +1 -0
  122. package/dist/generated/OcrMatch.js +3 -0
  123. package/dist/generated/OcrMatch.js.map +1 -0
  124. package/dist/generated/Request.d.ts +40 -0
  125. package/dist/generated/Request.d.ts.map +1 -0
  126. package/dist/generated/Request.js +2 -0
  127. package/dist/generated/Request.js.map +1 -0
  128. package/dist/generated/Response.d.ts +20 -0
  129. package/dist/generated/Response.d.ts.map +1 -0
  130. package/dist/generated/Response.js +2 -0
  131. package/dist/generated/Response.js.map +1 -0
  132. package/dist/generated/ResponseData.d.ts +92 -0
  133. package/dist/generated/ResponseData.d.ts.map +1 -0
  134. package/dist/generated/ResponseData.js +2 -0
  135. package/dist/generated/ResponseData.js.map +1 -0
  136. package/dist/generated/RunPollResult.d.ts +26 -0
  137. package/dist/generated/RunPollResult.d.ts.map +1 -0
  138. package/dist/generated/RunPollResult.js +3 -0
  139. package/dist/generated/RunPollResult.js.map +1 -0
  140. package/dist/generated/RunResult.d.ts +22 -0
  141. package/dist/generated/RunResult.d.ts.map +1 -0
  142. package/dist/generated/RunResult.js +3 -0
  143. package/dist/generated/RunResult.js.map +1 -0
  144. package/dist/generated/ScreenshotRequest.d.ts +11 -0
  145. package/dist/generated/ScreenshotRequest.d.ts.map +1 -0
  146. package/dist/generated/ScreenshotRequest.js +2 -0
  147. package/dist/generated/ScreenshotRequest.js.map +1 -0
  148. package/dist/generated/ScrollDirection.d.ts +5 -0
  149. package/dist/generated/ScrollDirection.d.ts.map +1 -0
  150. package/dist/generated/ScrollDirection.js +3 -0
  151. package/dist/generated/ScrollDirection.js.map +1 -0
  152. package/dist/generated/ScrollRequest.d.ts +20 -0
  153. package/dist/generated/ScrollRequest.d.ts.map +1 -0
  154. package/dist/generated/ScrollRequest.js +2 -0
  155. package/dist/generated/ScrollRequest.js.map +1 -0
  156. package/dist/generated/SessionInfo.d.ts +35 -0
  157. package/dist/generated/SessionInfo.d.ts.map +1 -0
  158. package/dist/generated/SessionInfo.js +2 -0
  159. package/dist/generated/SessionInfo.js.map +1 -0
  160. package/dist/generated/SessionSummary.d.ts +19 -0
  161. package/dist/generated/SessionSummary.d.ts.map +1 -0
  162. package/dist/generated/SessionSummary.js +2 -0
  163. package/dist/generated/SessionSummary.js.map +1 -0
  164. package/dist/generated/WaitState.d.ts +5 -0
  165. package/dist/generated/WaitState.d.ts.map +1 -0
  166. package/dist/generated/WaitState.js +3 -0
  167. package/dist/generated/WaitState.js.map +1 -0
  168. package/dist/generated/WindowAction.d.ts +5 -0
  169. package/dist/generated/WindowAction.d.ts.map +1 -0
  170. package/dist/generated/WindowAction.js +3 -0
  171. package/dist/generated/WindowAction.js.map +1 -0
  172. package/dist/generated/WindowInfo.d.ts +31 -0
  173. package/dist/generated/WindowInfo.d.ts.map +1 -0
  174. package/dist/generated/WindowInfo.js +2 -0
  175. package/dist/generated/WindowInfo.js.map +1 -0
  176. package/dist/generated/index.d.ts +41 -0
  177. package/dist/generated/index.d.ts.map +1 -0
  178. package/dist/generated/index.js +3 -0
  179. package/dist/generated/index.js.map +1 -0
  180. package/dist/index.d.ts +211 -0
  181. package/dist/index.d.ts.map +1 -0
  182. package/dist/index.js +341 -0
  183. package/dist/index.js.map +1 -0
  184. package/dist/types.d.ts +125 -0
  185. package/dist/types.d.ts.map +1 -0
  186. package/dist/types.js +17 -0
  187. package/dist/types.js.map +1 -0
  188. package/models/text-detection.rten +0 -0
  189. package/models/text-recognition.rten +0 -0
  190. 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);