ai-remote 0.4.5 → 0.4.6
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/SKILL.md +245 -0
- package/dist/cli.mjs +271 -52
- package/package.json +3 -2
package/SKILL.md
ADDED
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ai-remote
|
|
3
|
+
description: Drive a remote Windows, Linux or macOS desktop from the command line with `npx ai-remote` (also installed as `remotectl`) — open one session, then screenshot it, click, type, press keys, run shell commands over SSH, and watch it in a window. Use this skill whenever the user wants to control, inspect, automate or screenshot a machine over RDP/VNC/SSH — including when they never name the tool and just say "click the button on that Windows box", "what's on the screen of 192.168.x.x", "log into the VM and run this", or "take a screenshot of the remote desktop".
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Driving a remote desktop with ai-remote
|
|
7
|
+
|
|
8
|
+
## The one thing to get right
|
|
9
|
+
|
|
10
|
+
`open` is the only command that names a machine. It starts a session that
|
|
11
|
+
outlives the command, and **every command after it takes no host, no account
|
|
12
|
+
and no password** — they attach to the session that is already running.
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
export AI_REMOTE_PASSWORD=... # never a flag; argv is world-readable
|
|
16
|
+
npx ai-remote open 192.168.1.20 -u owner
|
|
17
|
+
|
|
18
|
+
npx ai-remote shot -o screen.png # not `shot 192.168.1.20`
|
|
19
|
+
npx ai-remote click 640,400
|
|
20
|
+
npx ai-remote close
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Writing `ai-remote shot 192.168.1.20` is the single most common mistake, because
|
|
24
|
+
that *was* the grammar in older versions and it is all over the internet. The
|
|
25
|
+
CLI now refuses it with a message explaining the change rather than doing
|
|
26
|
+
something surprising, so if you see that error, drop the host and try again.
|
|
27
|
+
|
|
28
|
+
State carries between commands, and that is the entire point: a tool that
|
|
29
|
+
reconnects between acting and looking can never see the result of its own
|
|
30
|
+
action. Reuse also makes each command effectively instant — a keystroke is
|
|
31
|
+
~0.15s against ~5s for a fresh RDP handshake.
|
|
32
|
+
|
|
33
|
+
## Working loop
|
|
34
|
+
|
|
35
|
+
Act, then look. The session holds the framebuffer, so a screenshot taken after a
|
|
36
|
+
click shows what the click did.
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
npx ai-remote key MetaLeft # open the Start menu
|
|
40
|
+
npx ai-remote shot -o after.png # see what happened
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
`shot` waits for the screen to stop changing before it captures, so there is
|
|
44
|
+
normally no need to sleep first. When an action starts something slow (an app
|
|
45
|
+
launching, a page loading), put an explicit wait in a `do` script instead of
|
|
46
|
+
guessing from outside.
|
|
47
|
+
|
|
48
|
+
Prefer `do` when the next few steps are already known — one round trip instead
|
|
49
|
+
of four, and the timing between steps stays inside the session rather than
|
|
50
|
+
depending on how fast the shell gets round to the next command:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
npx ai-remote do "key MetaLeft; wait 800; type notepad; wait 1200; key Enter; wait 1500; shot s.png"
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Steps, separated by `;`:
|
|
57
|
+
|
|
58
|
+
| Step | Meaning |
|
|
59
|
+
|---|---|
|
|
60
|
+
| `move X,Y` | move the pointer |
|
|
61
|
+
| `click X,Y[,right\|middle]` | click, optionally another button |
|
|
62
|
+
| `dblclick X,Y` | double click |
|
|
63
|
+
| `scroll X,Y,DX,DY` | scroll by notches at a position |
|
|
64
|
+
| `type TEXT` | type into whatever has focus |
|
|
65
|
+
| `key Code[+Code...]` | press keys; `+` is a chord |
|
|
66
|
+
| `cad` | Ctrl+Alt+Del |
|
|
67
|
+
| `wait MS` | pause |
|
|
68
|
+
| `shot FILE` | write a screenshot mid-script |
|
|
69
|
+
|
|
70
|
+
## Screenshots and coordinates
|
|
71
|
+
|
|
72
|
+
**Coordinates are always in the desktop's own pixels, never the screenshot's.**
|
|
73
|
+
This matters the moment you shrink a screenshot to save tokens:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
npx ai-remote shot --max-edge 800 --json -o s.png
|
|
77
|
+
# {"ok":true,...,"width":800,"height":500,"file":"/abs/path/s.png"}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
That image is 800x500 but the desktop is still 1280x800. A button you see at
|
|
81
|
+
(400, 250) in the image is at (640, 400) on the desktop. Scale back before
|
|
82
|
+
clicking:
|
|
83
|
+
|
|
84
|
+
```
|
|
85
|
+
desktop_x = image_x * desktop_width / image_width
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
`ai-remote status --json` reports the true desktop size (`width`, `height`), and
|
|
89
|
+
so does `shot` when you do not pass `--max-edge`. When in doubt, take the
|
|
90
|
+
screenshot full size and click the coordinates you read off it.
|
|
91
|
+
|
|
92
|
+
`--max-edge` is worth using anyway: a 1280x800 PNG is several hundred KB of
|
|
93
|
+
image tokens and a 800px one is a fraction of that, usually with no loss of
|
|
94
|
+
anything you needed to see.
|
|
95
|
+
|
|
96
|
+
## Keys
|
|
97
|
+
|
|
98
|
+
Key codes are DOM-style physical codes: `MetaLeft`, `Enter`, `Escape`, `Tab`,
|
|
99
|
+
`F5`, `KeyA`, `Digit1`, `ArrowDown`, `ControlLeft`, `AltLeft`, `ShiftLeft`.
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
npx ai-remote key ControlLeft+KeyA # a chord: Ctrl+A
|
|
103
|
+
npx ai-remote key MetaLeft KeyR # in sequence: Meta, then R
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
`+` holds the modifiers down for the last key. Separate arguments are pressed
|
|
107
|
+
one after another. An unknown code is ignored rather than guessed at, so if
|
|
108
|
+
nothing happens, check the spelling against the list above.
|
|
109
|
+
|
|
110
|
+
## Running commands instead of clicking
|
|
111
|
+
|
|
112
|
+
When the machine has SSH, a command is faster and far more reliable than driving
|
|
113
|
+
the GUI — no coordinates, no waiting, and real output to read:
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
npx ai-remote exec "hostname"
|
|
117
|
+
npx ai-remote exec "dir C:\\Users"
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
`exec` reuses the session's own account and password, so it needs no flags. Its
|
|
121
|
+
stdout is the command's output, and it exits 0 when the command did and 1 when
|
|
122
|
+
it did not — the command's own status is in `exitStatus` under `--json` if you
|
|
123
|
+
need the number. `ai-remote shell` opens an interactive terminal (Ctrl-] to
|
|
124
|
+
leave) — that one is for a human, not for an agent, because it never returns.
|
|
125
|
+
|
|
126
|
+
Reach for the GUI when the task is genuinely graphical. Reach for `exec` for
|
|
127
|
+
anything a shell can do.
|
|
128
|
+
|
|
129
|
+
## Sessions
|
|
130
|
+
|
|
131
|
+
The session opened last is the one commands drive. With several open, name them:
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
npx ai-remote open 192.168.1.20 -u owner --session desk
|
|
135
|
+
npx ai-remote open 192.168.1.30 -u owner --session build
|
|
136
|
+
|
|
137
|
+
npx ai-remote list # * marks the one commands will use
|
|
138
|
+
npx ai-remote shot --session desk # a name, a host, or host:port
|
|
139
|
+
npx ai-remote close --all # or `close` for the current one
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Nothing times out and nothing closes itself — a session that an agent left
|
|
143
|
+
mid-task is a feature, not a leak — so **close what you open** when the task is
|
|
144
|
+
done, or tell the user it is still running.
|
|
145
|
+
|
|
146
|
+
## Watching
|
|
147
|
+
|
|
148
|
+
By default `open` also shows the desktop in a real window, and whoever opens it
|
|
149
|
+
can drive it straight away. Control is not exclusive — you and the person at the
|
|
150
|
+
window can both work the same desktop at once, because your input goes over the
|
|
151
|
+
session's own socket and never through the viewer. Expect a human to move the
|
|
152
|
+
pointer out from under you: if a step depends on what was on screen, take a
|
|
153
|
+
fresh `shot` rather than trusting one from before you paused.
|
|
154
|
+
|
|
155
|
+
- Leave the window on when a human is present. Watching it happen is the reason
|
|
156
|
+
it exists.
|
|
157
|
+
- `--watch-only` starts the window as an observer instead, for a session someone
|
|
158
|
+
should supervise rather than share. Taking control is then one click in the
|
|
159
|
+
toolbar, and the gate is enforced in the session rather than in the page.
|
|
160
|
+
- `--headless` (or `--no-view`) on a server or in CI. The framebuffer is the
|
|
161
|
+
authority either way, so a session nobody watches is not a lesser session.
|
|
162
|
+
- `ai-remote view` adds a window to a headless session later (`view --watch-only`
|
|
163
|
+
for an observer one); `view --close` takes it away without ending the session.
|
|
164
|
+
|
|
165
|
+
## Reading results
|
|
166
|
+
|
|
167
|
+
Add `--json` to any command for one object on stdout; diagnostics go to stderr,
|
|
168
|
+
so the two never interleave.
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
npx ai-remote status --json
|
|
172
|
+
# {"ok":true,"host":"...","name":"desk","width":1280,"height":800,
|
|
173
|
+
# "connected":true,"viewer":"http://127.0.0.1:7373/?t=...","viewers":1,
|
|
174
|
+
# "shell":"closed","pid":54594}
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
Exit codes carry meaning, which is usually faster than parsing the message:
|
|
178
|
+
|
|
179
|
+
| Code | Meaning |
|
|
180
|
+
|---|---|
|
|
181
|
+
| 0 | fine |
|
|
182
|
+
| 10 | unreachable |
|
|
183
|
+
| 11 | credentials rejected |
|
|
184
|
+
| 12 | bad usage, or no session open |
|
|
185
|
+
| 13 | timed out |
|
|
186
|
+
|
|
187
|
+
## When it does not work
|
|
188
|
+
|
|
189
|
+
**"No session is open."** — Nothing is running. `open` first. Commands never
|
|
190
|
+
start a session implicitly, because they have no host to start one with.
|
|
191
|
+
|
|
192
|
+
**Credentials rejected (11), or a session that stops before it is ready.** —
|
|
193
|
+
Almost always `AI_REMOTE_PASSWORD` missing from *this* shell. It is deliberately
|
|
194
|
+
not a flag, and it does not carry across terminals; export it again. `open` says
|
|
195
|
+
so explicitly when it sees the host ask for NLA and finds no password.
|
|
196
|
+
|
|
197
|
+
**Unreachable (10).** — `ai-remote probe <host>` is the one command besides
|
|
198
|
+
`open` that still takes an address: it answers whether anything is listening
|
|
199
|
+
before you spend a handshake finding out.
|
|
200
|
+
|
|
201
|
+
**A session that stopped by itself.** — The host closed the connection, or the
|
|
202
|
+
machine went away. `ai-remote list` shows what is really running; stale entries
|
|
203
|
+
clean themselves up when it does.
|
|
204
|
+
|
|
205
|
+
## Getting the desktop's own size right
|
|
206
|
+
|
|
207
|
+
RDP lets the client choose the desktop size, and the host may or may not honour
|
|
208
|
+
it. Ask for what suits the task at `open` time (`-W 1920 -H 1080`), then trust
|
|
209
|
+
what `status` reports rather than what you asked for.
|
|
210
|
+
|
|
211
|
+
## Cheat sheet
|
|
212
|
+
|
|
213
|
+
```bash
|
|
214
|
+
export AI_REMOTE_PASSWORD=...
|
|
215
|
+
npx ai-remote open HOST[:PORT] -u USER [-d DOMAIN] [--session NAME] [--headless] [--watch-only]
|
|
216
|
+
npx ai-remote shot [-o FILE] [--max-edge N] [--json]
|
|
217
|
+
npx ai-remote click X,Y [--button right|middle] [--double]
|
|
218
|
+
npx ai-remote type "text"
|
|
219
|
+
npx ai-remote key ControlLeft+KeyA
|
|
220
|
+
npx ai-remote do "key MetaLeft; wait 800; type notepad; shot s.png"
|
|
221
|
+
npx ai-remote exec "command"
|
|
222
|
+
npx ai-remote view [--watch-only] [--close]
|
|
223
|
+
npx ai-remote status | list | close [--all]
|
|
224
|
+
npx ai-remote probe [HOST[:PORT]]
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
Every command takes `--session NAME` to pick between open sessions, and `--json`
|
|
228
|
+
for machine-readable output. `ai-remote --help` is the authority; this file is
|
|
229
|
+
the map.
|
|
230
|
+
|
|
231
|
+
## Working inside this repository
|
|
232
|
+
|
|
233
|
+
This repo *is* ai-remote. When testing a change, drive the local build rather
|
|
234
|
+
than the published package, or you will be exercising the last release instead
|
|
235
|
+
of your edit:
|
|
236
|
+
|
|
237
|
+
```bash
|
|
238
|
+
node packages/core/scripts/build.mjs # after editing packages/core/src/cli/*
|
|
239
|
+
node packages/core/dist/cli.mjs open HOST -u USER --headless
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
The CLI's own source is [`packages/core/src/cli/`](src/cli/):
|
|
243
|
+
`cli.ts` is the grammar, `sessions.ts` decides which session a command drives,
|
|
244
|
+
`daemon.ts` is the process that holds the connection, and `viewer.ts` plus
|
|
245
|
+
`window.ts` are the window. `pnpm test` covers them.
|