ai-remote 0.4.7 → 0.4.8

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 (47) hide show
  1. package/README.md +84 -2
  2. package/SKILL.md +49 -1
  3. package/dist/apple-dh.d.ts +27 -0
  4. package/dist/apple-dh.js +1 -0
  5. package/dist/cli.mjs +2978 -210
  6. package/dist/protocols/rdp/buffer.d.ts +38 -0
  7. package/dist/protocols/rdp/caps.d.ts +49 -0
  8. package/dist/protocols/rdp/cert.d.ts +18 -0
  9. package/dist/protocols/rdp/client.d.ts +56 -0
  10. package/dist/protocols/rdp/cliprdr.d.ts +27 -0
  11. package/dist/protocols/rdp/credssp.d.ts +34 -0
  12. package/dist/protocols/rdp/crypto.d.ts +63 -0
  13. package/dist/protocols/rdp/display.d.ts +38 -0
  14. package/dist/protocols/rdp/gcc.d.ts +23 -0
  15. package/dist/protocols/rdp/keymap.d.ts +9 -0
  16. package/dist/protocols/rdp/mcs.d.ts +40 -0
  17. package/dist/protocols/rdp/ntlm.d.ts +61 -0
  18. package/dist/protocols/rdp/pdu.d.ts +155 -0
  19. package/dist/protocols/rdp/rail.d.ts +119 -0
  20. package/dist/protocols/rdp/rle.d.ts +13 -0
  21. package/dist/protocols/rdp/sec.d.ts +59 -0
  22. package/dist/protocols/rdp/session.d.ts +62 -0
  23. package/dist/protocols/rdp/tls.d.ts +18 -0
  24. package/dist/protocols/rdp/vchannel.d.ts +28 -0
  25. package/dist/protocols/rdp/x224.d.ts +40 -0
  26. package/dist/protocols/ssh/identity.d.ts +39 -0
  27. package/dist/protocols/ssh/kex.d.ts +97 -0
  28. package/dist/protocols/ssh/messages.d.ts +52 -0
  29. package/dist/protocols/ssh/session.d.ts +67 -0
  30. package/dist/protocols/ssh/terminal.d.ts +46 -0
  31. package/dist/protocols/ssh/transport.d.ts +62 -0
  32. package/dist/protocols/ssh/wire.d.ts +52 -0
  33. package/dist/protocols/types.d.ts +127 -0
  34. package/dist/protocols/vnc/des.d.ts +25 -0
  35. package/dist/protocols/vnc/input.d.ts +5 -0
  36. package/dist/protocols/vnc/keysyms.d.ts +16 -0
  37. package/dist/protocols/vnc/rfb.d.ts +64 -0
  38. package/dist/protocols/vnc/session.d.ts +12 -0
  39. package/dist/protocols.js +13 -13
  40. package/dist/shared/connection.d.ts +81 -0
  41. package/dist/shared/device.d.ts +26 -0
  42. package/dist/shared/hosts.d.ts +63 -0
  43. package/dist/shared/icons.d.ts +46 -0
  44. package/dist/shared/protocol.d.ts +32 -0
  45. package/dist/shared/signals.d.ts +37 -0
  46. package/package.json +8 -3
  47. /package/dist/{protocols.d.ts → protocols/index.d.ts} +0 -0
package/README.md CHANGED
@@ -26,6 +26,11 @@ npx ai-remote key Enter
26
26
  # Run commands in the remote terminal over SSH:
27
27
  npx ai-remote exec "dir"
28
28
 
29
+ # A host with no desktop -- a Linux box, a Mac, a container -- is a terminal
30
+ # session. Keys are used before passwords, so this usually needs nothing set:
31
+ npx ai-remote open 192.168.1.60 --ssh -u deploy
32
+ npx ai-remote exec "systemctl status nginx"
33
+
29
34
  # Let it go when you are done -- nothing closes on its own:
30
35
  npx ai-remote close
31
36
  ```
@@ -34,6 +39,8 @@ npx ai-remote close
34
39
 
35
40
  ```
36
41
  ai-remote open <host[:port]> connect, show a window, and stay open
42
+ ai-remote open <host> --ssh a terminal only: no desktop, no RDP
43
+ ai-remote open <host> --vnc a screen over VNC (implied by port 5900)
37
44
 
38
45
  ai-remote shot screenshot the desktop
39
46
  ai-remote click X,Y click, --button right|middle, --double
@@ -46,6 +53,8 @@ npx ai-remote close
46
53
  ai-remote view --close close the window, session keeps running
47
54
  ai-remote status what the session is doing
48
55
  ai-remote list every session that is open
56
+ ai-remote saved machines with a password kept
57
+ ai-remote forget [<host[:port]>] throw one away; --all throws all
49
58
  ai-remote close close it; --all closes every session
50
59
  ai-remote probe [<host[:port]>] is anything listening?
51
60
  ```
@@ -70,9 +79,80 @@ npx ai-remote close --all
70
79
  it answers whether a session could be opened at all; with no address it probes
71
80
  the current session's host.
72
81
 
82
+ ### A screen over VNC
83
+
84
+ Macs, Linux desktops and anything else running a VNC server are opened with
85
+ `--vnc`, or just by naming port 5900. Everything else is the same as RDP -- the
86
+ same window, the same `shot`, `click`, `type`, `key` and `do`, the same session
87
+ that stays open until it is closed:
88
+
89
+ ```bash
90
+ AI_REMOTE_PASSWORD=... npx ai-remote open 192.168.1.70 --vnc -u alex
91
+ npx ai-remote shot -o screen.png
92
+ npx ai-remote click 640,400
93
+ ```
94
+
95
+ The client is this package's own: no noVNC, no browser and no gateway. It signs
96
+ in either way a host may ask -- the classic VNC password, or the account and
97
+ password macOS Screen Sharing wants -- and decodes Raw and CopyRect, which
98
+ every host implements. Raw is bandwidth-hungry by design; on a LAN a 1080p
99
+ desktop is fine, over a slow link it will feel it.
100
+
101
+ A session that is already open can be given a screen without being reopened,
102
+ which is the point of `vnc` taking no host:
103
+
104
+ ```bash
105
+ npx ai-remote open 192.168.1.70 --ssh -u alex # a terminal
106
+ npx ai-remote vnc # ...and now a screen too
107
+ npx ai-remote exec "uptime" # both, one session
108
+ ```
109
+
110
+ ### Terminal-only sessions
111
+
112
+ `--ssh` opens a session that is a terminal and nothing else: no RDP handshake,
113
+ no framebuffer, and a window that is all terminal. Port 22 implies it, so
114
+ `open box:22` and `open box --ssh` are the same thing.
115
+
116
+ `exec`, `shell`, `status`, `view` and `close` work as they always do. The
117
+ commands about pixels -- `shot`, `click`, `type`, `key`, `do` -- say there is no
118
+ desktop rather than failing obscurely.
119
+
120
+ The session lasts until `close`. If the host drops the terminal -- an idle
121
+ timeout on its side, or an `exit` typed into the window -- the session stays
122
+ where it is and the next command opens another one; `status` says `disconnected`
123
+ in between.
124
+
125
+ ```bash
126
+ npx ai-remote open 192.168.1.60 --ssh -u deploy # a terminal window
127
+ npx ai-remote exec "uname -a"
128
+ npx ai-remote shell # Ctrl-] to leave
129
+ ```
130
+
131
+ ### Signing in
132
+
133
+ Keys first, then a password.
134
+
135
+ With no `--identity`, ssh-agent is asked for whatever it is holding, and
136
+ `~/.ssh/id_ed25519`, `id_ecdsa` and `id_rsa` are read if they have no
137
+ passphrase -- so a host that already takes your key needs nothing set at all.
138
+ Ed25519, ECDSA and RSA keys are supported, in OpenSSH's own format as well as
139
+ PKCS#8 and the older PEM containers.
140
+
141
+ ```bash
142
+ npx ai-remote open box --ssh -u deploy # agent + ~/.ssh
143
+ npx ai-remote open box --ssh -u deploy -i ~/.ssh/deploy_ed25519 # one named key
144
+ ssh-add ~/.ssh/id_ed25519 # a key with a passphrase
145
+ ```
146
+
147
+ A key with a passphrase is not read off disk: add it to ssh-agent, which is
148
+ where a decrypted key belongs, and it is offered from there.
149
+
73
150
  ### Options for `open`
74
151
 
152
+ - `--ssh`: A terminal session only, with no desktop (implied by port 22).
153
+ - `--vnc`: A screen over VNC rather than RDP (implied by port 5900).
75
154
  - `-u, --user NAME`: Account name on the host.
155
+ - `-i, --identity FILE`: A private key to sign in with. Repeatable.
76
156
  - `-d, --domain NAME`: Windows domain or computer name.
77
157
  - `--session NAME`: Name this session (default: `host_port`).
78
158
  - `--ssh-user NAME`: Account for the SSH terminal (defaults to `--user`).
@@ -82,6 +162,7 @@ the current session's host.
82
162
  - `--idle MINUTES`: Close an unused session (default: 0, never).
83
163
  - `--headless` / `--no-view`: Run headless without opening the viewer window.
84
164
  - `--no-reuse`: A second session beside one that is already open.
165
+ - `--save`: Keep the password for next time, once it has worked.
85
166
 
86
167
  ### Options for the rest
87
168
 
@@ -90,8 +171,9 @@ the current session's host.
90
171
  - `--max-edge N`: Shrink a screenshot to fit N.
91
172
  - `--json`: Output machine-readable JSON results on stdout.
92
173
 
93
- The password comes from `AI_REMOTE_PASSWORD` (or `REMOTECTL_PASSWORD`). There is
94
- no `--password` flag: a command line is visible to every process on the machine.
174
+ The password, where one is wanted, comes from `AI_REMOTE_PASSWORD` (or
175
+ `REMOTECTL_PASSWORD`). There is no `--password` flag: a command line is visible
176
+ to every process on the machine.
95
177
 
96
178
  ---
97
179
 
package/SKILL.md CHANGED
@@ -126,6 +126,48 @@ leave) — that one is for a human, not for an agent, because it never returns.
126
126
  Reach for the GUI when the task is genuinely graphical. Reach for `exec` for
127
127
  anything a shell can do.
128
128
 
129
+ ## A Mac, or anything else that speaks VNC
130
+
131
+ `--vnc` opens the screen over VNC instead of RDP; port 5900 implies it. Every
132
+ desktop command works exactly as it does over RDP -- there is nothing
133
+ protocol-specific to remember:
134
+
135
+ ```bash
136
+ export AI_REMOTE_PASSWORD=... # the VNC password, or the macOS account's
137
+ npx ai-remote open 192.168.1.70 --vnc -u alex
138
+ npx ai-remote shot -o screen.png
139
+ npx ai-remote click 640,400
140
+ ```
141
+
142
+ A session that is already open gets a screen with `ai-remote vnc`, which takes
143
+ no host because the session already knows the machine. That is the way to give a
144
+ terminal session a desktop without closing it.
145
+
146
+ ## A host with no desktop
147
+
148
+ `--ssh` opens a session that is a terminal and nothing else — no RDP handshake
149
+ to wait for, and none to fail. Port 22 implies it.
150
+
151
+ ```bash
152
+ npx ai-remote open 192.168.1.60 --ssh -u deploy
153
+ npx ai-remote exec "systemctl status nginx"
154
+ npx ai-remote close
155
+ ```
156
+
157
+ `exec`, `shell`, `status`, `view` and `close` work as usual; `shot`, `click`,
158
+ `type`, `key` and `do` say there is no desktop, because there is not one.
159
+
160
+ The session lasts until `close`, not until the terminal does: if the host drops
161
+ the connection, the next `exec` opens another one and the session name, the
162
+ account and the keys stay put.
163
+
164
+ **Keys before passwords.** With no `--identity`, ssh-agent is asked for what it
165
+ holds and `~/.ssh/id_ed25519`, `id_ecdsa` and `id_rsa` are read when they have
166
+ no passphrase — so a host that already takes your key needs no environment
167
+ variable at all. `-i FILE` names a key instead, and repeats. A key with a
168
+ passphrase is never read off disk: `ssh-add` it, and it is offered from the
169
+ agent.
170
+
129
171
  ## Sessions
130
172
 
131
173
  The session opened last is the one commands drive. With several open, name them:
@@ -192,7 +234,10 @@ start a session implicitly, because they have no host to start one with.
192
234
  **Credentials rejected (11), or a session that stops before it is ready.** —
193
235
  Almost always `AI_REMOTE_PASSWORD` missing from *this* shell. It is deliberately
194
236
  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.
237
+ so explicitly when it sees the host ask for NLA and finds no password. Over SSH
238
+ the other half of the answer is a key: `ssh-add ~/.ssh/id_ed25519`, or
239
+ `-i FILE`. The session's log names every key it offered and every key it passed
240
+ over, and why.
196
241
 
197
242
  **Unreachable (10).** — `ai-remote probe <host>` is the one command besides
198
243
  `open` that still takes an address: it answers whether anything is listening
@@ -213,6 +258,9 @@ what `status` reports rather than what you asked for.
213
258
  ```bash
214
259
  export AI_REMOTE_PASSWORD=...
215
260
  npx ai-remote open HOST[:PORT] -u USER [-d DOMAIN] [--session NAME] [--headless] [--watch-only]
261
+ npx ai-remote open HOST --ssh -u USER [-i ~/.ssh/id_ed25519] # a terminal, no desktop
262
+ npx ai-remote open HOST --vnc -u USER # a screen over VNC
263
+ npx ai-remote vnc # ...or add one to this session
216
264
  npx ai-remote shot [-o FILE] [--max-edge N] [--json]
217
265
  npx ai-remote click X,Y [--button right|middle] [--double]
218
266
  npx ai-remote type "text"
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Apple's Diffie-Hellman, which is how macOS Screen Sharing signs somebody in.
3
+ *
4
+ * Security type 30: the host sends a generator, a prime and its public value;
5
+ * the client agrees a secret, keys AES with it, and sends back the account and
6
+ * password encrypted under that key followed by its own public value. None of
7
+ * it is negotiable -- the order, the widths and the padding are Apple's, and
8
+ * noVNC's `_negotiateARDAuth` is the reference every implementation follows.
9
+ *
10
+ * An entrypoint of its own because two very different programs need it: the
11
+ * Worker performs this exchange on behalf of a browser, and the CLI performs it
12
+ * for itself. One wire format, one implementation -- there is no version of
13
+ * this that is safe to have two of.
14
+ *
15
+ * Node's crypto rather than Web Crypto, because the exchange needs MD5 and
16
+ * single-block AES-ECB and Web Crypto has neither. Both callers run somewhere
17
+ * that has it: the CLI is Node, and the Worker runs with `nodejs_compat`.
18
+ */
19
+ /**
20
+ * The answer to a host's key exchange.
21
+ *
22
+ * `params` is what the host sent: two bytes of generator, two of key length,
23
+ * then the prime and the host's public value at that length -- 1028 bytes on a
24
+ * Mac. What comes back is the encrypted credentials first and this end's public
25
+ * value after, which is 640 bytes for the same Mac.
26
+ */
27
+ export declare function appleDhResponse(params: Uint8Array, username: string, password: string): Uint8Array;
@@ -0,0 +1 @@
1
+ import{createCipheriv as B,createHash as p,randomBytes as w}from"node:crypto";var u=(e,i,n)=>{let r=1n,t=e%n,o=i;for(;o>0n;)o%2n===1n&&(r=r*t%n),o/=2n,t=t*t%n;return r},s=e=>e.reduce((i,n)=>i<<8n|BigInt(n),0n);function l(e,i){let n=new Uint8Array(i),r=e;for(let t=i-1;t>=0&&r>0n;t--)n[t]=Number(r&0xffn),r>>=8n;return n}function U(e,i,n){if(e.length<4)throw new Error(`Apple DH parameters are too short: ${e.length} bytes.`);let r=BigInt(e[0]<<8|e[1]),t=e[2]<<8|e[3];if(t===0||e.length<4+t*2)throw new Error(`Apple DH parameters are truncated: a ${t}-byte prime needs ${4+t*2} bytes, and ${e.length} arrived.`);let o=s(e.subarray(4,4+t)),y=s(e.subarray(4+t,4+t*2)),a=s(w(t)),d=l(u(r,a,o),t),g=l(u(y,a,o),t),b=p("md5").update(g).digest(),c=Buffer.alloc(128,0);Buffer.from(i,"utf8").subarray(0,63).copy(c,0),Buffer.from(n,"utf8").subarray(0,63).copy(c,64);let f=B("aes-128-ecb",b,Buffer.alloc(0));f.setAutoPadding(!1);let h=Buffer.concat([f.update(c),f.final()]);return new Uint8Array(Buffer.concat([h,Buffer.from(d)]))}export{U as appleDhResponse};