ai-remote 0.3.0 → 0.3.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/README.md CHANGED
@@ -1,32 +1,62 @@
1
1
  # ai-remote
2
2
 
3
- > Pure TypeScript protocol engines (RDP, VNC, SSH) and remote desktop vocabulary for AI agents, Node.js, and web browsers.
3
+ > Pure TypeScript protocol engines (RDP, VNC, SSH) for Node.js, Cloudflare Workers, and web browsers — plus `npx ai-remote`, a CLI that drives a remote desktop and shows you what it is doing.
4
4
 
5
5
  [![npm version](https://img.shields.io/npm/v/ai-remote.svg)](https://www.npmjs.com/package/ai-remote)
6
6
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
7
7
 
8
- ## Overview
8
+ ## 🚀 Quick Start (CLI)
9
9
 
10
- `ai-remote` is an environment-neutral core protocol library providing client-side implementations for **RDP**, **VNC**, and **SSH**. It powers browser-based remote gateways, terminal sessions, and automated AI agents driving remote desktops.
10
+ Run immediately with `npx` (zero install):
11
11
 
12
- ## Features
12
+ ```bash
13
+ # Connect and open a live supervision window:
14
+ AI_REMOTE_PASSWORD=secret npx ai-remote open 192.168.1.50 -u Administrator
13
15
 
14
- - **RDP Engine:**
15
- - Complete X.224, MCS, and GCC capability negotiation.
16
- - CredSSP v6 with NTLMv2 and TLS 1.2 client implementation over WebCrypto.
17
- - Fast-path 16-bit interleaved RLE bitmap decoding.
18
- - Shared clipboard support (`cliprdr` virtual channel).
19
- - RemoteApp (RAIL) window orders and execution.
20
- - **SSH Engine:**
21
- - RFC 4253 binary packet protocol with `curve25519-sha256` / `ecdh-sha2-nistp256`.
22
- - AEAD ciphers (`aes256-gcm@openssh.com`, `aes128-gcm@openssh.com`).
23
- - Terminal interactive and agent command execution.
24
- - **VNC Engine:**
25
- - RFB protocol integration with Apple Diffie-Hellman handshake support.
26
- - **AI Agent Tool Vocabulary:**
27
- - Standardized connection, device, protocol, and signal definitions.
16
+ # Take a screenshot:
17
+ AI_REMOTE_PASSWORD=secret npx ai-remote shot 192.168.1.50 -u Administrator -o screen.png
18
+
19
+ # Click or type on the remote desktop:
20
+ npx ai-remote click 192.168.1.50 500,300
21
+ npx ai-remote type 192.168.1.50 "notepad"
22
+ npx ai-remote key 192.168.1.50 Enter
23
+
24
+ # Run commands in the remote terminal over SSH:
25
+ npx ai-remote exec 192.168.1.50 "dir"
26
+ ```
27
+
28
+ ### CLI Commands
29
+
30
+ ```
31
+ ai-remote open <host[:port]> connect, show a window, and stay open
32
+ ai-remote shot <host[:port]> screenshot the desktop
33
+ ai-remote click <host[:port]> X,Y click, --button right|middle, --double
34
+ ai-remote type <host[:port]> "text" type into whatever has focus
35
+ ai-remote key <host[:port]> <Code> press keys, e.g. MetaLeft, ControlLeft+KeyA
36
+ ai-remote exec <host[:port]> "cmd" run a command in the terminal (SSH)
37
+ ai-remote shell <host[:port]> an interactive terminal
38
+ ai-remote do <host[:port]> "steps" several actions in one go
39
+ ai-remote view <host[:port]> open the window on a running session
40
+ ai-remote view <host[:port]> --close close the window, session keeps running
41
+ ai-remote status | list | close [<host>]
42
+ ai-remote probe <host[:port]> is anything listening?
43
+ ```
44
+
45
+ ### Common Options
46
+
47
+ - `-u, --user NAME`: Account name on the host.
48
+ - `-d, --domain NAME`: Windows domain or computer name.
49
+ - `--ssh-user NAME`: Account for the SSH terminal (defaults to `--user`).
50
+ - `--ssh-port N`: SSH port (default: 22).
51
+ - `-s, --security MODE`: `auto` | `nla` | `tls` | `rdp` (default: `auto`).
52
+ - `-W, --width N` / `-H, --height N`: Desktop resolution (default: 1280x800).
53
+ - `-o, --out FILE`: Screenshot output file path (default: `screen.png`).
54
+ - `--headless` / `--no-view`: Run headless without opening the viewer window.
55
+ - `--json`: Output machine-readable JSON results on stdout.
56
+
57
+ ---
28
58
 
29
- ## Installation
59
+ ## 📦 Library Installation
30
60
 
31
61
  ```bash
32
62
  pnpm add ai-remote
@@ -34,7 +64,7 @@ pnpm add ai-remote
34
64
  npm install ai-remote
35
65
  ```
36
66
 
37
- ## Subpath Exports
67
+ ### Subpath Exports
38
68
 
39
69
  | Export | Description |
40
70
  |---|---|
@@ -45,6 +75,25 @@ npm install ai-remote
45
75
  | `ai-remote/protocols/vnc` | VNC session and input management |
46
76
  | `ai-remote/shared/*` | Direct access to shared models and utilities |
47
77
 
78
+ ---
79
+
80
+ ## ✨ Features
81
+
82
+ - **RDP Protocol Engine:**
83
+ - Full X.224, MCS, and GCC capability negotiation.
84
+ - CredSSP v6 with NTLMv2 and TLS 1.2 client implementation over WebCrypto.
85
+ - Fast-path 16-bit interleaved RLE bitmap decoding.
86
+ - Shared clipboard support (`cliprdr` virtual channel).
87
+ - RemoteApp (RAIL) window orders and execution.
88
+ - **SSH Engine:**
89
+ - RFC 4253 binary packet protocol with `curve25519-sha256` / `ecdh-sha2-nistp256`.
90
+ - AEAD ciphers (`aes256-gcm@openssh.com`, `aes128-gcm@openssh.com`).
91
+ - Terminal interactive and automated agent command execution.
92
+ - **VNC Engine:**
93
+ - RFB protocol integration with Apple Diffie-Hellman handshake support.
94
+ - **AI Agent Automation:**
95
+ - Fast headless actions, structured screen capture, and auto-attached SSH shells.
96
+
48
97
  ## License
49
98
 
50
99
  MIT © Leon Guo
package/dist/cli.mjs CHANGED
@@ -1631,6 +1631,10 @@ var TcpTransport = class extends EventTarget {
1631
1631
  };
1632
1632
 
1633
1633
  // src/cli/shell.ts
1634
+ function bareUsername(username) {
1635
+ const withoutDomain = username.includes("\\") ? username.split("\\").pop() : username;
1636
+ return withoutDomain.split("@")[0].trim();
1637
+ }
1634
1638
  var Shell = class {
1635
1639
  options;
1636
1640
  session;
@@ -7194,7 +7198,10 @@ async function startViewer(session, port, { launch = true, openShell } = {}) {
7194
7198
  const detachShell = /* @__PURE__ */ new Map();
7195
7199
  async function attachShell(peer) {
7196
7200
  if (!openShell) {
7197
- peer.send(JSON.stringify({ type: "shell-error", message: "This session was started without a terminal. Restart it with --ssh-user NAME." }));
7201
+ peer.send(JSON.stringify({
7202
+ type: "shell-error",
7203
+ message: "This session has no terminal available."
7204
+ }));
7198
7205
  return;
7199
7206
  }
7200
7207
  if (detachShell.has(peer)) return;
@@ -7441,7 +7448,7 @@ async function runDaemon(options) {
7441
7448
  const started = new Shell({
7442
7449
  host: options.host,
7443
7450
  port: options.sshPort,
7444
- username: options.sshUsername || options.username,
7451
+ username: options.sshUsername || bareUsername(options.username),
7445
7452
  password: options.password
7446
7453
  });
7447
7454
  await started.connect();
@@ -7681,7 +7688,8 @@ Steps for "do", separated by semicolons:
7681
7688
  Options
7682
7689
  -u, --user NAME account on the host
7683
7690
  -d, --domain NAME Windows domain or computer name
7684
- --ssh-user NAME account for the terminal (default: --user)
7691
+ --ssh-user NAME only if the terminal signs in as someone else;
7692
+ by default it reuses --user and the same password
7685
7693
  --ssh-port N SSH port (default: 22)
7686
7694
  -s, --security MODE auto | nla | tls | rdp (default: auto)
7687
7695
  -W, --width N desktop width (default: 1280)
@@ -7997,7 +8005,7 @@ async function interactiveShell(args, host) {
7997
8005
  const shell = new Shell({
7998
8006
  host,
7999
8007
  port: Number(flag(args, "ssh-port") ?? 22),
8000
- username: flag(args, "ssh-user") ?? flag(args, "u", "user") ?? "",
8008
+ username: flag(args, "ssh-user") ?? bareUsername(flag(args, "u", "user") ?? ""),
8001
8009
  password: password(),
8002
8010
  columns: process.stdout.columns ?? 120,
8003
8011
  rows: process.stdout.rows ?? 30
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-remote",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "RDP, VNC and SSH protocol engines that run in Node, a Worker or a browser -- plus `npx ai-remote`, a CLI that drives a machine and shows you what it is doing",
5
5
  "type": "module",
6
6
  "bin": {
package/src/cli/cli.ts CHANGED
@@ -10,7 +10,7 @@
10
10
  import { basename, resolve as resolvePath } from 'node:path';
11
11
  import { spawn } from 'node:child_process';
12
12
  import { readFileSync, readdirSync, existsSync, openSync, unlinkSync } from 'node:fs';
13
- import { Shell } from './shell';
13
+ import { bareUsername, Shell } from './shell';
14
14
  import { runDaemon } from './daemon';
15
15
  import { request } from './ipc';
16
16
  import { ensureRunDir, logPath, metaPath, RUN_DIR, sessionName, socketPath } from './paths';
@@ -59,7 +59,8 @@ Steps for "do", separated by semicolons:
59
59
  Options
60
60
  -u, --user NAME account on the host
61
61
  -d, --domain NAME Windows domain or computer name
62
- --ssh-user NAME account for the terminal (default: --user)
62
+ --ssh-user NAME only if the terminal signs in as someone else;
63
+ by default it reuses --user and the same password
63
64
  --ssh-port N SSH port (default: 22)
64
65
  -s, --security MODE auto | nla | tls | rdp (default: auto)
65
66
  -W, --width N desktop width (default: 1280)
@@ -376,7 +377,7 @@ async function interactiveShell(args: Args, host: string): Promise<void> {
376
377
  const shell = new Shell({
377
378
  host,
378
379
  port: Number(flag(args, 'ssh-port') ?? 22),
379
- username: flag(args, 'ssh-user') ?? flag(args, 'u', 'user') ?? '',
380
+ username: flag(args, 'ssh-user') ?? bareUsername(flag(args, 'u', 'user') ?? ''),
380
381
  password: password(),
381
382
  columns: process.stdout.columns ?? 120,
382
383
  rows: process.stdout.rows ?? 30,
package/src/cli/daemon.ts CHANGED
@@ -13,7 +13,7 @@
13
13
  import net from 'node:net';
14
14
  import { unlinkSync, writeFileSync, existsSync } from 'node:fs';
15
15
  import { RdpSession, type SessionOptions } from './session';
16
- import { Shell } from './shell';
16
+ import { bareUsername, Shell } from './shell';
17
17
  import { downscale, encodePng } from './png';
18
18
  import { startViewer, type Viewer } from './viewer';
19
19
  import { readMessages, writeMessage, type Request } from './ipc';
@@ -43,6 +43,10 @@ export async function runDaemon(options: DaemonOptions): Promise<void> {
43
43
  * The terminal is opened on first use rather than at connect time. Most
44
44
  * sessions never ask for one, and a second sign-in that nobody wanted is a
45
45
  * second chance to lock an account out.
46
+ *
47
+ * It signs in as the desktop's own account and password by default. Needing a
48
+ * separate flag to get a terminal on the machine you are already looking at
49
+ * is a question with an obvious answer, so it is not asked.
46
50
  */
47
51
  function openShell(): Promise<Shell> {
48
52
  if (shell) return Promise.resolve(shell);
@@ -52,7 +56,7 @@ export async function runDaemon(options: DaemonOptions): Promise<void> {
52
56
  const started = new Shell({
53
57
  host: options.host,
54
58
  port: options.sshPort,
55
- username: options.sshUsername || options.username,
59
+ username: options.sshUsername || bareUsername(options.username),
56
60
  password: options.password,
57
61
  });
58
62
  await started.connect();
package/src/cli/shell.ts CHANGED
@@ -9,6 +9,19 @@
9
9
  import { SshSession } from '../protocols/ssh/session';
10
10
  import { TcpTransport } from './transport';
11
11
 
12
+ /**
13
+ * The account name on its own.
14
+ *
15
+ * An RDP username may carry the machine or domain with it -- `DESKTOP-01\\owner`
16
+ * or `owner@corp` -- and SSH wants neither. The terminal defaults to the same
17
+ * account as the desktop, so this is what makes that default work rather than
18
+ * fail on a name that was perfectly good for RDP.
19
+ */
20
+ export function bareUsername(username: string): string {
21
+ const withoutDomain = username.includes('\\') ? username.split('\\').pop()! : username;
22
+ return withoutDomain.split('@')[0].trim();
23
+ }
24
+
12
25
  export interface ShellOptions {
13
26
  host: string;
14
27
  port: number;
package/src/cli/viewer.ts CHANGED
@@ -76,7 +76,10 @@ export async function startViewer(
76
76
  */
77
77
  async function attachShell(peer: WebSocketPeer): Promise<void> {
78
78
  if (!openShell) {
79
- peer.send(JSON.stringify({ type: 'shell-error', message: 'This session was started without a terminal. Restart it with --ssh-user NAME.' }));
79
+ peer.send(JSON.stringify({
80
+ type: 'shell-error',
81
+ message: 'This session has no terminal available.',
82
+ }));
80
83
  return;
81
84
  }
82
85
  if (detachShell.has(peer)) return;