ai-remote 0.4.14 → 0.4.15

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/README.md CHANGED
@@ -35,6 +35,69 @@ npx ai-remote exec "systemctl status nginx"
35
35
  npx ai-remote close
36
36
  ```
37
37
 
38
+ ### What a command costs, and where the time actually goes
39
+
40
+ Measured on an M4, `list` with two sessions open:
41
+
42
+ | | median |
43
+ | --- | --- |
44
+ | Node itself, starting and doing nothing | 24 ms |
45
+ | this CLI, run directly | 27 ms |
46
+ | `npx he --version` -- a package with **no dependencies** | 332 ms |
47
+ | `npx ai-remote list` | 328 ms |
48
+
49
+ The third row is the one that matters. A package with nothing in it costs the
50
+ same under `npx` as this one does, because that 300 ms is `npm exec` resolving
51
+ the spec, revalidating a packument against the registry and spawning -- work npm
52
+ does before the package it is running exists to it. No published version of any
53
+ package can be faster than that row, and this one is at it.
54
+
55
+ So the way to spend less of it is to invoke `npx` fewer times, not to make each
56
+ invocation cheaper. Batch the steps:
57
+
58
+ ```bash
59
+ # Four npx invocations: ~1200 ms, of which ~1200 ms is npm.
60
+ npx ai-remote key MetaLeft
61
+ npx ai-remote type notepad
62
+ npx ai-remote key Enter
63
+ npx ai-remote shot -o s.png
64
+
65
+ # The same four steps, one invocation: ~305 ms.
66
+ npx ai-remote do "key MetaLeft; wait 800; type notepad; key Enter; wait 1500; shot s.png"
67
+ ```
68
+
69
+ `ai-remote shell` is the same idea for a terminal: one invocation, a session
70
+ that stays open, and no npm between keystrokes.
71
+
72
+ Run `node bench/cli.bench.mjs` in this repository to get the table above for
73
+ your own machine -- including the no-dependency control, without which npm's
74
+ 300 ms reads like something this package could fix.
75
+
76
+ ### About the `npm notice run ...` lines
77
+
78
+ They are npm's, not this package's. `@npmcli/run-script` writes them
79
+ unconditionally at npm's default log level:
80
+
81
+ ```
82
+ @npmcli/run-script/lib/run-script-pkg.js:53
83
+ log.notice('run', `${cmd.trim()} ${args?.join(' ')}`.trim())
84
+ ```
85
+
86
+ There is no field in `package.json`, no `.npmrc` inside the tarball and no
87
+ publish flag that reaches them -- they are printed before this program starts.
88
+ Silencing them is a choice made where the command is run:
89
+
90
+ ```bash
91
+ npx --silent ai-remote list # this invocation only
92
+ NPM_CONFIG_LOGLEVEL=error npx ... # this shell only
93
+ npm config set loglevel error # this machine, all npm commands
94
+ npm install -g ai-remote # no npm in the path at all; use `remotectl`
95
+ ```
96
+
97
+ The last one also skips the 300 ms: `remotectl list` answers in about 27 ms. It
98
+ is a per-machine decision rather than something the package can carry, which is
99
+ why `npx` is still what the examples here use.
100
+
38
101
  ### CLI Commands
39
102
 
40
103
  ```
@@ -48,11 +111,12 @@ npx ai-remote close
48
111
  ai-remote key <Code> press keys, e.g. MetaLeft, ControlLeft+KeyA
49
112
  ai-remote exec "cmd" run a command in the terminal (SSH)
50
113
  ai-remote shell an interactive terminal
114
+ ai-remote cp <src> <dst> copy files; a remote path starts with ':'
51
115
  ai-remote do "steps" several actions in one go
52
116
  ai-remote view open the window on the session
53
117
  ai-remote view --close close the window, session keeps running
54
118
  ai-remote status what the session is doing
55
- ai-remote list every session that is open
119
+ ai-remote list every session open, and every password kept
56
120
  ai-remote saved machines with a password kept
57
121
  ai-remote forget [<host[:port]>] throw one away; --all throws all
58
122
  ai-remote close close it; --all closes every session
@@ -75,6 +139,22 @@ npx ai-remote shot --session desk
75
139
  npx ai-remote close --all
76
140
  ```
77
141
 
142
+ `list` answers both halves of "what can I get back to". Every open session, with
143
+ `password saved` against the ones that have one:
144
+
145
+ ```
146
+ * desk 192.168.1.50:3389 1920x1080 pid 6551 http://127.0.0.1:7373/?t=... password saved (login keychain)
147
+ build 192.168.1.60:3389 1280x800 pid 6808
148
+
149
+ Saved, nothing open (`ai-remote open <host>` needs no password):
150
+ owner@192.168.1.70:3389 login keychain saved 2026-08-28
151
+ ```
152
+
153
+ The second block is the machines with a password kept but nothing running --
154
+ `open` on one of those needs nothing else. It reads the index beside the
155
+ keystore rather than the keystore, so a listing never raises a keychain prompt;
156
+ `saved` shows that block on its own.
157
+
78
158
  `probe` is the one command besides `open` that still takes an address, because
79
159
  it answers whether a session could be opened at all; with no address it probes
80
160
  the current session's host.
@@ -134,6 +214,40 @@ npx ai-remote exec "uname -a"
134
214
  npx ai-remote shell # Ctrl-] to leave
135
215
  ```
136
216
 
217
+ ### Copying files
218
+
219
+ `cp` is `scp` with the session standing in for the host: a remote path starts
220
+ with a colon, and anything else is on this machine.
221
+
222
+ ```bash
223
+ npx ai-remote cp ./setup.exe :C:/Users/owner/Desktop/ # to the session in hand
224
+ npx ai-remote cp :/var/log/syslog ./syslog # from it
225
+ npx ai-remote cp -r ./site :/srv/www # a whole directory
226
+ npx ai-remote cp desk:/tmp/a.txt build:/tmp/a.txt # between two sessions
227
+ ```
228
+
229
+ SFTP underneath, which is what `scp` itself uses since OpenSSH 9 deprecated its
230
+ own wire protocol. So the host needs no `scp` binary, a filename with a space or
231
+ a quote in it is a length-prefixed string rather than a quoting problem, and
232
+ nothing passes through a shell -- which is why a Windows host needs no special
233
+ case here even though `exec` needs several.
234
+
235
+ Two rules about the grammar:
236
+
237
+ - **A one-letter prefix is a drive, not a session.** `C:/Users/owner/x.txt` is a
238
+ path on this machine, and always will be. Two or more characters before the
239
+ colon name a session -- by its name, or by its host.
240
+ - **A destination that already exists as a directory takes the source's name
241
+ under it.** `cp a.txt :/tmp` is `/tmp/a.txt`, and `cp -r ./site :/srv/www`
242
+ with `www` already there is `/srv/www/site`. A destination that does not exist
243
+ is the name the copy takes.
244
+
245
+ Chunks are 32 KB with eight in flight, so a transfer gets the bandwidth the link
246
+ has rather than one round trip per chunk. The mode travels with the file. The
247
+ transfer is its own SSH connection rather than the session's, so a large copy
248
+ does not stall the terminal somebody is watching. `--json` reports `bytes`,
249
+ `files` and `rate`.
250
+
137
251
  ### Signing in
138
252
 
139
253
  Keys first, then a password.
@@ -174,6 +288,7 @@ where a decrypted key belongs, and it is offered from there.
174
288
 
175
289
  - `--session NAME`: Which session to drive (default: the one opened last).
176
290
  - `-o, --out FILE`: Screenshot output file path (default: `screen.png`).
291
+ - `-r, --recursive`: For `cp`, copy a directory.
177
292
  - `--max-edge N`: Shrink a screenshot to fit N.
178
293
  - `--json`: Output machine-readable JSON results on stdout.
179
294
 
@@ -216,6 +331,7 @@ npm install ai-remote
216
331
  - RFC 4253 binary packet protocol with `curve25519-sha256` / `ecdh-sha2-nistp256`.
217
332
  - AEAD ciphers (`aes256-gcm@openssh.com`, `aes128-gcm@openssh.com`).
218
333
  - Terminal interactive and automated agent command execution.
334
+ - SFTP version 3 client on a subsystem channel, for file transfer.
219
335
  - **VNC Engine:**
220
336
  - RFB protocol integration with Apple Diffie-Hellman handshake support.
221
337
  - **AI Agent Automation:**
package/SKILL.md CHANGED
@@ -30,6 +30,27 @@ reconnects between acting and looking can never see the result of its own
30
30
  action. Reuse also makes each command effectively instant — a keystroke is
31
31
  ~0.15s against ~5s for a fresh RDP handshake.
32
32
 
33
+ **Batch steps into one invocation where you can.** `npx` costs about 300 ms per
34
+ call before this tool runs at all — npm resolving the package, not the package
35
+ doing anything, and the same 300 ms for a package with no code in it. The tool
36
+ itself answers in about 27 ms. So four steps run as four commands spend roughly a
37
+ second waiting on npm and a tenth of one doing the work:
38
+
39
+ ```bash
40
+ # ~1200 ms, nearly all npm
41
+ npx ai-remote key MetaLeft
42
+ npx ai-remote type notepad
43
+ npx ai-remote key Enter
44
+ npx ai-remote shot -o s.png
45
+
46
+ # ~305 ms, same four steps
47
+ npx ai-remote do "key MetaLeft; wait 800; type notepad; key Enter; wait 1500; shot s.png"
48
+ ```
49
+
50
+ Look between steps when you need to see the result before choosing the next one;
51
+ batch the runs of steps you already know. The `npm notice run ...` lines npx
52
+ prints are npm's own and harmless — ignore them, or add `--silent`.
53
+
33
54
  ## Working loop
34
55
 
35
56
  Act, then look. The session holds the framebuffer, so a screenshot taken after a
@@ -128,6 +149,27 @@ leave) — that one is for a human, not for an agent, because it never returns.
128
149
  Reach for the GUI when the task is genuinely graphical. Reach for `exec` for
129
150
  anything a shell can do.
130
151
 
152
+ ## Copying files
153
+
154
+ `cp` is scp with the session standing in for the host. A remote path starts with
155
+ a colon; anything else is on this machine.
156
+
157
+ ```bash
158
+ npx ai-remote cp ./setup.exe :C:/Users/owner/Desktop/ # to the session in hand
159
+ npx ai-remote cp :/var/log/syslog ./syslog # from it
160
+ npx ai-remote cp -r ./site :/srv/www # a directory
161
+ npx ai-remote cp desk:/tmp/a.txt build:/tmp/a.txt # between two sessions
162
+ ```
163
+
164
+ SFTP underneath, so the host needs no `scp` binary, spaces in filenames need no
165
+ quoting, and nothing goes through a shell. Two rules worth knowing: a one-letter
166
+ prefix is a drive, not a session, so `C:/Users/owner/x.txt` is local; and a
167
+ destination that already exists as a directory takes the source's name under it,
168
+ so `cp -r ./site :/srv/www` with `www` there gives `/srv/www/site`.
169
+
170
+ Uploading is how you get a script onto a machine, which is usually better than
171
+ typing one into a GUI. `--json` reports `bytes`, `files` and `rate`.
172
+
131
173
  ## A Mac, or anything else that speaks VNC
132
174
 
133
175
  `--vnc` opens the screen over VNC instead of RDP; port 5900 implies it. Every
@@ -188,10 +230,17 @@ command line is visible to every process on the machine.
188
230
 
189
231
  ```bash
190
232
  npx ai-remote open 192.168.1.20 -u owner --save # keep it, once it has worked
191
- npx ai-remote saved # what is kept, and where
233
+ npx ai-remote list # sessions, each marked if it has one
234
+ npx ai-remote saved # only what is kept, and where
192
235
  npx ai-remote forget 192.168.1.20 # one machine; --all for every one
193
236
  ```
194
237
 
238
+ `list` answers both halves of "what can I get back to": every open session, with
239
+ `password saved` against the ones that have one, and then the machines with a
240
+ password kept but nothing open — those need no `-u` or password to reopen. It
241
+ reads the index beside the keystore, not the keystore, so it never raises a
242
+ keychain prompt.
243
+
195
244
  `--save` writes to the system keystore — the login keychain on macOS, the desktop
196
245
  keyring on Linux — and where there is neither, to an encrypted file under
197
246
  `~/.config/ai-remote`; the command says which it got. That file is proof against
@@ -208,7 +257,8 @@ The session opened last is the one commands drive. With several open, name them:
208
257
  npx ai-remote open 192.168.1.20 -u owner --session desk
209
258
  npx ai-remote open 192.168.1.30 -u owner --session build
210
259
 
211
- npx ai-remote list # * marks the one commands will use
260
+ npx ai-remote list # * marks the one commands will use,
261
+ # and it also lists saved passwords
212
262
  npx ai-remote shot --session desk # a name, a host, or host:port
213
263
  npx ai-remote close --all # or `close` for the current one
214
264
  ```
@@ -240,7 +290,14 @@ fresh `shot` rather than trusting one from before you paused.
240
290
  authority either way, so a session nobody watches is not a lesser session.
241
291
  - `--fullscreen` fills the display and makes RDP use the current monitor's
242
292
  logical resolution. Add `--side-panel` to `open` to start with a fixed-width
243
- SSH terminal docked on the right; its 420 pixels are removed from RDP width.
293
+ SSH terminal docked on the right; its 610 pixels -- eighty columns, which is
294
+ what a Windows shell assumes it has -- are removed from the RDP width, and the
295
+ divider between the panes can be dragged.
296
+ - `--audio` (RDP only) asks the host to redirect the session's sound and plays
297
+ it in the window; the toolbar's speaker button mutes it. Off unless asked for:
298
+ the stream costs bandwidth in both directions, and Windows stops playing
299
+ through the host's own speakers while it is redirected. A headless session
300
+ ignores the flag, having nowhere to play anything.
244
301
  - `ai-remote view` adds a window to a headless session later (`view --watch-only`
245
302
  for an observer one); `view --close` takes it away without ending the session.
246
303
  - `--view-port N` moves the window's local server off 7373, which matters only
@@ -304,7 +361,7 @@ what `status` reports rather than what you asked for.
304
361
  ```bash
305
362
  export AI_REMOTE_PASSWORD=...
306
363
  npx ai-remote open HOST[:PORT] -u USER [-d DOMAIN] [--session NAME] [--fullscreen] [--side-panel]
307
- [-W 1920 -H 1080] [-s auto|nla|tls|rdp] [--save] [--idle N]
364
+ [-W 1920 -H 1080] [-s auto|nla|tls|rdp] [--save] [--idle N] [--audio]
308
365
  npx ai-remote open HOST --ssh -u USER [-i ~/.ssh/id_ed25519] # a terminal, no desktop
309
366
  npx ai-remote open HOST --vnc -u USER # a screen over VNC
310
367
  npx ai-remote vnc [--vnc-port N] # ...or add one to this session
@@ -314,6 +371,7 @@ npx ai-remote type "text"
314
371
  npx ai-remote key ControlLeft+KeyA
315
372
  npx ai-remote do "key MetaLeft; wait 800; type notepad; shot s.png"
316
373
  npx ai-remote exec "command"
374
+ npx ai-remote cp [-r] SRC DST # ':path' is on the session
317
375
  npx ai-remote view [--watch-only] [--close]
318
376
  npx ai-remote status | list | close [--all]
319
377
  npx ai-remote saved | forget [HOST[:PORT]] [--all]
@@ -337,5 +395,6 @@ node packages/core/dist/cli.mjs open HOST -u USER --headless
337
395
 
338
396
  The CLI's own source is [`packages/core/src/cli/`](src/cli/):
339
397
  `cli.ts` is the grammar, `sessions.ts` decides which session a command drives,
340
- `daemon.ts` is the process that holds the connection, and `viewer.ts` plus
341
- `window.ts` are the window. `pnpm test` covers them.
398
+ `daemon.ts` is the process that holds the connection, `copy.ts` is `cp` over the
399
+ SFTP client in [`protocols/ssh/sftp.ts`](src/protocols/ssh/sftp.ts), and
400
+ `viewer.ts` plus `window.ts` are the window. `pnpm test` covers them.