@nothumanwork/sauron 0.2.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 ADDED
@@ -0,0 +1,254 @@
1
+ # sauron (Rust)
2
+
3
+ A fully Rust-native CLI for AI agents to control Chrome via the **Chrome DevTools Protocol (CDP)**.
4
+
5
+ This is a rewrite of the attached Bun/TypeScript `sauron` project as a compiled Rust binary.
6
+
7
+ ## Goals
8
+
9
+ - **Agent-friendly** JSON output for all commands (v2 envelope)
10
+ - **Fast** startup and execution (single static-ish binary)
11
+ - **Process-safe concurrency** with mandatory runtime sessions (`start` before browser commands)
12
+ - **Per-session isolation** with generated `session_id`, `instance`, and `client` IDs by default
13
+ - Filesystem-only runtime state storage under `~/.sauron/runtime/`
14
+ - Uses Chrome **`--headless=new`** only (opinionated headless runtime)
15
+ - Default viewport is **1440x900** (override with `--viewport WIDTHxHEIGHT`)
16
+
17
+ ## V2 Interface
18
+
19
+ - `v2` is the only supported interface.
20
+ - No backward compatibility layer for v1 command names or output shape.
21
+ - Migration and implementation notes: [specs/v2-integration.md](/Users/mish/projects/sauron/specs/v2-integration.md)
22
+
23
+ ## Install
24
+
25
+ ```bash
26
+ ./install.sh
27
+ ```
28
+
29
+ By default, `install.sh`:
30
+ - builds and installs for your host target to `/usr/local/bin/sauron`,
31
+ - attempts to build the release matrix for:
32
+ - `aarch64-apple-darwin`
33
+ - `x86_64-apple-darwin`
34
+ - `x86_64-unknown-linux-gnu`
35
+
36
+ Notes:
37
+ - Linux cross-builds from non-Linux hosts require `zig` and `cargo-zigbuild`.
38
+ - Missing cross-toolchain prerequisites are reported as warnings and skipped; host install still succeeds.
39
+
40
+ Useful options:
41
+
42
+ ```bash
43
+ ./install.sh --no-matrix # host-only build + install
44
+ ./install.sh --prefix "$HOME/.local" # install under custom prefix
45
+ ./install.sh --windows # best-effort Windows build too
46
+ ```
47
+
48
+ If you prefer the old local cargo install workflow:
49
+
50
+ ```bash
51
+ cargo install --path .
52
+ ```
53
+
54
+ Or run in place:
55
+
56
+ ```bash
57
+ cargo run -- --help
58
+ ```
59
+
60
+ Or install the published npm package:
61
+
62
+ ```bash
63
+ npm install -g @nothumanwork/sauron
64
+ ```
65
+
66
+ The npm package bundles prebuilt binaries for:
67
+ - `x86_64-unknown-linux-gnu`
68
+ - `x86_64-apple-darwin`
69
+ - `aarch64-apple-darwin`
70
+
71
+ The `sauron` launcher inside the package selects the matching binary for the current host OS and architecture at runtime.
72
+
73
+ ## Quick start
74
+
75
+ Each shell/process must start a runtime session first:
76
+
77
+ ```bash
78
+ sauron runtime start
79
+ ```
80
+
81
+ macOS defaults to GPU + WebGL on `runtime start` (opt out with `--no-webgl --no-gpu`).
82
+
83
+ Then run browser commands from the same project directory:
84
+
85
+ ```bash
86
+ sauron page goto https://example.com
87
+ sauron page snapshot --format json
88
+ sauron input click --ref @e1
89
+ sauron page screenshot --responsive --quality medium
90
+ ```
91
+
92
+ Clean up with:
93
+
94
+ ```bash
95
+ sauron runtime stop
96
+ ```
97
+
98
+ ## Mandatory session lifecycle
99
+
100
+ - Non-`runtime start` commands require an active runtime session.
101
+ - Session resolution order is:
102
+ - explicit `--session-id`
103
+ - current process binding
104
+ - current project binding
105
+ - `SAURON_SESSION_ID` fallback
106
+ - If none resolve to an active session, commands fail with `SESSION_REQUIRED`.
107
+ - `start` auto-generates:
108
+ - `session_id` (`sess-...`)
109
+ - `instance` (`inst-...`)
110
+ - `client` (`client-...`)
111
+ - You can still override IDs:
112
+
113
+ ```bash
114
+ sauron --session-id mysession --instance work --client alice runtime start
115
+ ```
116
+
117
+ ## Interaction Flow
118
+
119
+ ```mermaid
120
+ flowchart TD
121
+ U["User or AI Agent"] --> CLI["sauron CLI"]
122
+ CLI --> L["Lifecycle Command"]
123
+ CLI --> B["Browser Command"]
124
+ L --> R["Runtime Store"]
125
+ L --> D["Chrome Daemon"]
126
+ B --> RS["Resolve Active Session"]
127
+ RS --> R
128
+ B --> P["Page and Browser Client"]
129
+ P --> C["CDP Transport"]
130
+ C --> CH["Chrome DevTools Endpoint"]
131
+ B --> O["JSON Result Envelope"]
132
+ O --> U
133
+ ```
134
+
135
+ ## Component Data Flow
136
+
137
+ ```mermaid
138
+ flowchart LR
139
+ M["CLI Router (main.rs)"] --> RT["Runtime and Session Resolution (runtime.rs)"]
140
+ M --> DM["Daemon Control (daemon.rs)"]
141
+ M --> BR["Browser Actions (browser.rs)"]
142
+ BR --> CDP["CDP Client (cdp.rs)"]
143
+ CDP --> CH["Chrome"]
144
+ RT --> FS["Runtime Filesystem Store"]
145
+ BR --> SNAP["Snapshot and Ref State"]
146
+ BR --> SES["Saved Browser State Sessions"]
147
+ BR --> LOG["Command Logs"]
148
+ SNAP --> FS
149
+ SES --> FS
150
+ LOG --> FS
151
+ ```
152
+
153
+ ## Concurrent session workflow
154
+
155
+ Terminal A:
156
+
157
+ ```bash
158
+ sauron runtime start
159
+ sauron page goto https://example.com
160
+ sauron state save logged-in
161
+ ```
162
+
163
+ Terminal B (independent shell/process):
164
+
165
+ ```bash
166
+ sauron runtime start
167
+ sauron page goto https://news.ycombinator.com
168
+ sauron state save baseline
169
+ ```
170
+
171
+ Both sessions are isolated and can run concurrently without conflicts.
172
+
173
+ If you previously exported `SAURON_SESSION_ID`, clear it to avoid overriding project-aware routing:
174
+
175
+ ```bash
176
+ unset SAURON_SESSION_ID
177
+ ```
178
+
179
+ ## Runtime state
180
+
181
+ Runtime session state is stored on the local filesystem under `~/.sauron/runtime/`.
182
+
183
+ ## Session logs
184
+
185
+ Each session writes NDJSON logs to:
186
+
187
+ `~/.sauron/runtime/logs/<session_id>.ndjson`
188
+
189
+ Each line includes timestamp, session metadata, command name, status, and error details when present.
190
+
191
+ ## CLI flag placement
192
+
193
+ Global flags (`--session-id`, `--port`, etc.) must be placed before the subcommand:
194
+
195
+ ```bash
196
+ sauron --session-id mysession page goto https://example.com
197
+ ```
198
+
199
+ `--viewport` is global and applies to `start` and browser commands:
200
+
201
+ ```bash
202
+ sauron --viewport 1440x900 runtime start
203
+ sauron --viewport 390x844 page screenshot
204
+ ```
205
+
206
+ ## Output contract
207
+
208
+ All commands return exactly one JSON object in a unified v2 envelope:
209
+
210
+ - Success:
211
+
212
+ ```json
213
+ {
214
+ "meta": { "requestId": "...", "timestamp": "...", "durationMs": 12 },
215
+ "result": { "ok": true, "command": "page.snapshot", "data": { /* ... */ } }
216
+ }
217
+ ```
218
+
219
+ - Error:
220
+
221
+ ```json
222
+ {
223
+ "meta": { "requestId": "...", "timestamp": "...", "durationMs": 9 },
224
+ "result": {
225
+ "ok": false,
226
+ "command": "input.click",
227
+ "error": {
228
+ "code": "ELEMENT_NOT_FOUND",
229
+ "message": "...",
230
+ "hint": "...",
231
+ "recoverable": true,
232
+ "exitCode": 1,
233
+ "category": "state"
234
+ }
235
+ }
236
+ }
237
+ ```
238
+
239
+ ## Notes
240
+
241
+ - You need a local Chrome/Chromium install.
242
+ - The daemon uses `--remote-debugging-port=<port>`.
243
+
244
+ ## Automated Releases
245
+
246
+ Pushes to `main` cut the next patch version automatically. GitHub Actions then:
247
+
248
+ - builds the release binaries on GitHub-hosted Linux and macOS runners,
249
+ - stages them into the scoped npm package `@nothumanwork/sauron`,
250
+ - publishes the npm package,
251
+ - tags the repository with `v<version>`, and
252
+ - creates the matching GitHub release with per-target tarballs.
253
+
254
+ The release version is kept in sync across `Cargo.toml`, `Cargo.lock`, and `package.json`.
package/bin/sauron.js ADDED
@@ -0,0 +1,55 @@
1
+ #!/usr/bin/env node
2
+
3
+ 'use strict';
4
+
5
+ const { spawnSync } = require('node:child_process');
6
+ const { existsSync, readFileSync } = require('node:fs');
7
+ const path = require('node:path');
8
+
9
+ const repoRoot = path.resolve(__dirname, '..');
10
+ const targetConfig = JSON.parse(
11
+ readFileSync(path.join(repoRoot, 'distribution', 'targets.json'), 'utf8')
12
+ );
13
+
14
+ function resolveTarget() {
15
+ return targetConfig.targets.find(
16
+ (target) => target.platform === process.platform && target.arch === process.arch
17
+ );
18
+ }
19
+
20
+ function supportedTargetList() {
21
+ return targetConfig.targets
22
+ .map((target) => `${target.platform}/${target.arch} -> ${target.triple}`)
23
+ .join(', ');
24
+ }
25
+
26
+ const target = resolveTarget();
27
+
28
+ if (!target) {
29
+ console.error(
30
+ `Unsupported host ${process.platform}/${process.arch}. Supported targets: ${supportedTargetList()}`
31
+ );
32
+ process.exit(1);
33
+ }
34
+
35
+ const binaryPath = path.join(repoRoot, 'npm', 'bin', target.triple, targetConfig.binaryName);
36
+
37
+ if (!existsSync(binaryPath)) {
38
+ console.error(
39
+ `Missing bundled binary for ${target.triple} at ${binaryPath}. Reinstall @nothumanwork/sauron or fetch a release asset for this target.`
40
+ );
41
+ process.exit(1);
42
+ }
43
+
44
+ const result = spawnSync(binaryPath, process.argv.slice(2), { stdio: 'inherit' });
45
+
46
+ if (result.error) {
47
+ console.error(`Failed to launch ${binaryPath}: ${result.error.message}`);
48
+ process.exit(1);
49
+ }
50
+
51
+ if (result.signal) {
52
+ process.kill(process.pid, result.signal);
53
+ }
54
+
55
+ process.exit(result.status ?? 1);
@@ -0,0 +1,23 @@
1
+ {
2
+ "binaryName": "sauron",
3
+ "targets": [
4
+ {
5
+ "triple": "x86_64-unknown-linux-gnu",
6
+ "runner": "ubuntu-latest",
7
+ "platform": "linux",
8
+ "arch": "x64"
9
+ },
10
+ {
11
+ "triple": "x86_64-apple-darwin",
12
+ "runner": "macos-latest",
13
+ "platform": "darwin",
14
+ "arch": "x64"
15
+ },
16
+ {
17
+ "triple": "aarch64-apple-darwin",
18
+ "runner": "macos-latest",
19
+ "platform": "darwin",
20
+ "arch": "arm64"
21
+ }
22
+ ]
23
+ }
@@ -0,0 +1 @@
1
+
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "@nothumanwork/sauron",
3
+ "version": "0.2.1",
4
+ "description": "Rust-native CLI for controlling Chrome via CDP",
5
+ "license": "MIT",
6
+ "homepage": "https://github.com/thehumanworks/sauron#readme",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/thehumanworks/sauron.git"
10
+ },
11
+ "bugs": {
12
+ "url": "https://github.com/thehumanworks/sauron/issues"
13
+ },
14
+ "bin": {
15
+ "sauron": "./bin/sauron.js"
16
+ },
17
+ "files": [
18
+ "bin/",
19
+ "distribution/targets.json",
20
+ "npm/bin/",
21
+ "README.md"
22
+ ],
23
+ "engines": {
24
+ "node": ">=20"
25
+ },
26
+ "publishConfig": {
27
+ "access": "public"
28
+ },
29
+ "scripts": {
30
+ "release:current": "node ./scripts/release-version.mjs current",
31
+ "release:next-patch": "node ./scripts/release-version.mjs next-patch",
32
+ "release:set-version": "node ./scripts/release-version.mjs set",
33
+ "stage:binaries": "node ./scripts/stage-npm-binaries.mjs",
34
+ "pack:dry-run": "npm pack --dry-run"
35
+ },
36
+ "keywords": [
37
+ "chrome",
38
+ "cdp",
39
+ "cli",
40
+ "automation",
41
+ "rust"
42
+ ]
43
+ }