tauri-agent-tools 0.1.0
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/.agents/skills/tauri-agent-tools/SKILL.md +104 -0
- package/.agents/skills/tauri-bridge-setup/SKILL.md +95 -0
- package/AGENTS.md +30 -0
- package/LICENSE +21 -0
- package/README.md +338 -0
- package/dist/bridge/client.d.ts +15 -0
- package/dist/bridge/client.js +119 -0
- package/dist/bridge/client.js.map +1 -0
- package/dist/bridge/tokenDiscovery.d.ts +3 -0
- package/dist/bridge/tokenDiscovery.js +77 -0
- package/dist/bridge/tokenDiscovery.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +49 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/consoleMonitor.d.ts +2 -0
- package/dist/commands/consoleMonitor.js +133 -0
- package/dist/commands/consoleMonitor.js.map +1 -0
- package/dist/commands/dom.d.ts +2 -0
- package/dist/commands/dom.js +186 -0
- package/dist/commands/dom.js.map +1 -0
- package/dist/commands/eval.d.ts +2 -0
- package/dist/commands/eval.js +27 -0
- package/dist/commands/eval.js.map +1 -0
- package/dist/commands/info.d.ts +3 -0
- package/dist/commands/info.js +28 -0
- package/dist/commands/info.js.map +1 -0
- package/dist/commands/ipcMonitor.d.ts +2 -0
- package/dist/commands/ipcMonitor.js +122 -0
- package/dist/commands/ipcMonitor.js.map +1 -0
- package/dist/commands/listWindows.d.ts +3 -0
- package/dist/commands/listWindows.js +58 -0
- package/dist/commands/listWindows.js.map +1 -0
- package/dist/commands/pageState.d.ts +2 -0
- package/dist/commands/pageState.js +43 -0
- package/dist/commands/pageState.js.map +1 -0
- package/dist/commands/screenshot.d.ts +3 -0
- package/dist/commands/screenshot.js +81 -0
- package/dist/commands/screenshot.js.map +1 -0
- package/dist/commands/shared.d.ts +7 -0
- package/dist/commands/shared.js +27 -0
- package/dist/commands/shared.js.map +1 -0
- package/dist/commands/storage.d.ts +2 -0
- package/dist/commands/storage.js +110 -0
- package/dist/commands/storage.js.map +1 -0
- package/dist/commands/wait.d.ts +3 -0
- package/dist/commands/wait.js +63 -0
- package/dist/commands/wait.js.map +1 -0
- package/dist/platform/detect.d.ts +11 -0
- package/dist/platform/detect.js +73 -0
- package/dist/platform/detect.js.map +1 -0
- package/dist/platform/macos.d.ts +8 -0
- package/dist/platform/macos.js +111 -0
- package/dist/platform/macos.js.map +1 -0
- package/dist/platform/wayland.d.ts +10 -0
- package/dist/platform/wayland.js +98 -0
- package/dist/platform/wayland.js.map +1 -0
- package/dist/platform/x11.d.ts +8 -0
- package/dist/platform/x11.js +78 -0
- package/dist/platform/x11.js.map +1 -0
- package/dist/types.d.ts +32 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/util/exec.d.ts +9 -0
- package/dist/util/exec.js +31 -0
- package/dist/util/exec.js.map +1 -0
- package/dist/util/image.d.ts +10 -0
- package/dist/util/image.js +23 -0
- package/dist/util/image.js.map +1 -0
- package/examples/tauri-bridge/Cargo.toml +13 -0
- package/examples/tauri-bridge/src/dev_bridge.rs +146 -0
- package/examples/tauri-bridge/src/main.rs +16 -0
- package/package.json +70 -0
- package/rust-bridge/README.md +80 -0
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
use rand::Rng;
|
|
2
|
+
use serde::{Deserialize, Serialize};
|
|
3
|
+
use std::fs;
|
|
4
|
+
use std::io::Write;
|
|
5
|
+
use std::sync::Arc;
|
|
6
|
+
use std::thread;
|
|
7
|
+
use tauri::{AppHandle, Manager};
|
|
8
|
+
use tiny_http::{Header, Response, Server};
|
|
9
|
+
|
|
10
|
+
#[derive(Deserialize)]
|
|
11
|
+
struct EvalRequest {
|
|
12
|
+
js: String,
|
|
13
|
+
token: String,
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
#[derive(Serialize)]
|
|
17
|
+
struct EvalResponse {
|
|
18
|
+
result: serde_json::Value,
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
#[derive(Serialize)]
|
|
22
|
+
struct TokenFile {
|
|
23
|
+
port: u16,
|
|
24
|
+
token: String,
|
|
25
|
+
pid: u32,
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/// Start the development bridge HTTP server.
|
|
29
|
+
/// Returns the port number on success.
|
|
30
|
+
pub fn start_bridge(app: &AppHandle) -> Result<u16, String> {
|
|
31
|
+
let server = Server::http("127.0.0.1:0").map_err(|e| format!("Failed to start bridge: {e}"))?;
|
|
32
|
+
let port = server
|
|
33
|
+
.server_addr()
|
|
34
|
+
.to_ip()
|
|
35
|
+
.ok_or("Failed to get server address")?
|
|
36
|
+
.port();
|
|
37
|
+
|
|
38
|
+
// Generate random token
|
|
39
|
+
let token: String = rand::thread_rng()
|
|
40
|
+
.sample_iter(&rand::distributions::Alphanumeric)
|
|
41
|
+
.take(32)
|
|
42
|
+
.map(char::from)
|
|
43
|
+
.collect();
|
|
44
|
+
|
|
45
|
+
// Write token file
|
|
46
|
+
let token_file = TokenFile {
|
|
47
|
+
port,
|
|
48
|
+
token: token.clone(),
|
|
49
|
+
pid: std::process::id(),
|
|
50
|
+
};
|
|
51
|
+
let token_path = format!("/tmp/tauri-dev-bridge-{}.token", std::process::id());
|
|
52
|
+
let token_json = serde_json::to_string_pretty(&token_file).unwrap();
|
|
53
|
+
fs::write(&token_path, &token_json).map_err(|e| format!("Failed to write token file: {e}"))?;
|
|
54
|
+
|
|
55
|
+
// Clean up token file on exit
|
|
56
|
+
let cleanup_path = token_path.clone();
|
|
57
|
+
let _guard = scopeguard::guard((), move |_| {
|
|
58
|
+
let _ = fs::remove_file(&cleanup_path);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
let app_handle = app.clone();
|
|
62
|
+
let expected_token = token.clone();
|
|
63
|
+
|
|
64
|
+
thread::spawn(move || {
|
|
65
|
+
// Keep _guard alive for the lifetime of the server thread
|
|
66
|
+
let _cleanup = _guard;
|
|
67
|
+
|
|
68
|
+
for request in server.incoming_requests() {
|
|
69
|
+
if request.method().as_str() != "POST" || request.url() != "/eval" {
|
|
70
|
+
let _ = request.respond(Response::from_string("Not found").with_status_code(404));
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Read body
|
|
75
|
+
let mut body = String::new();
|
|
76
|
+
if let Err(_) = request.as_reader().read_to_string(&mut body) {
|
|
77
|
+
let _ =
|
|
78
|
+
request.respond(Response::from_string("Bad request").with_status_code(400));
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Parse request
|
|
83
|
+
let eval_req: EvalRequest = match serde_json::from_str(&body) {
|
|
84
|
+
Ok(r) => r,
|
|
85
|
+
Err(_) => {
|
|
86
|
+
let _ = request
|
|
87
|
+
.respond(Response::from_string("Invalid JSON").with_status_code(400));
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
// Verify token
|
|
93
|
+
if eval_req.token != expected_token {
|
|
94
|
+
let _ =
|
|
95
|
+
request.respond(Response::from_string("Unauthorized").with_status_code(401));
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Evaluate JS in webview
|
|
100
|
+
let (tx, rx) = std::sync::mpsc::channel();
|
|
101
|
+
let js = eval_req.js.clone();
|
|
102
|
+
|
|
103
|
+
if let Some(window) = app_handle.get_webview_window("main") {
|
|
104
|
+
let _ = window.eval(&format!(
|
|
105
|
+
r#"
|
|
106
|
+
try {{
|
|
107
|
+
const __result = eval({js});
|
|
108
|
+
window.__tauriDevBridgeResult = __result;
|
|
109
|
+
}} catch(e) {{
|
|
110
|
+
window.__tauriDevBridgeResult = "ERROR: " + e.message;
|
|
111
|
+
}}
|
|
112
|
+
"#,
|
|
113
|
+
js = serde_json::to_string(&js).unwrap()
|
|
114
|
+
));
|
|
115
|
+
|
|
116
|
+
// Give the webview a moment to evaluate
|
|
117
|
+
thread::sleep(std::time::Duration::from_millis(50));
|
|
118
|
+
|
|
119
|
+
// For simplicity, return the JS expression — in production,
|
|
120
|
+
// use a Tauri command callback to get the actual result
|
|
121
|
+
let _ = tx.send(serde_json::Value::String(js));
|
|
122
|
+
} else {
|
|
123
|
+
let _ = tx.send(serde_json::Value::Null);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
match rx.recv_timeout(std::time::Duration::from_secs(5)) {
|
|
127
|
+
Ok(result) => {
|
|
128
|
+
let resp = EvalResponse { result };
|
|
129
|
+
let json = serde_json::to_string(&resp).unwrap();
|
|
130
|
+
let header =
|
|
131
|
+
Header::from_bytes("Content-Type", "application/json").unwrap();
|
|
132
|
+
let _ = request.respond(Response::from_string(json).with_header(header));
|
|
133
|
+
}
|
|
134
|
+
Err(_) => {
|
|
135
|
+
let _ = request
|
|
136
|
+
.respond(Response::from_string("Eval timeout").with_status_code(504));
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
eprintln!("Dev bridge started on port {port}");
|
|
143
|
+
eprintln!("Token file: {token_path}");
|
|
144
|
+
|
|
145
|
+
Ok(port)
|
|
146
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
mod dev_bridge;
|
|
2
|
+
|
|
3
|
+
fn main() {
|
|
4
|
+
tauri::Builder::default()
|
|
5
|
+
.setup(|app| {
|
|
6
|
+
// Only start bridge in development
|
|
7
|
+
if cfg!(debug_assertions) {
|
|
8
|
+
if let Err(e) = dev_bridge::start_bridge(app.handle()) {
|
|
9
|
+
eprintln!("Warning: Failed to start dev bridge: {e}");
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
Ok(())
|
|
13
|
+
})
|
|
14
|
+
.run(tauri::generate_context!())
|
|
15
|
+
.expect("error while running tauri application");
|
|
16
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "tauri-agent-tools",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Agent-driven inspection toolkit for Tauri desktop apps",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"tauri-agent-tools": "./dist/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"main": "./dist/cli.js",
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc",
|
|
12
|
+
"clean": "rm -rf dist",
|
|
13
|
+
"test": "vitest run",
|
|
14
|
+
"test:watch": "vitest",
|
|
15
|
+
"dev": "tsc --watch",
|
|
16
|
+
"lint": "eslint src/",
|
|
17
|
+
"lint:fix": "eslint src/ --fix",
|
|
18
|
+
"prepublishOnly": "npm run build"
|
|
19
|
+
},
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "https://github.com/cesarandreslopez/tauri-agent-tools"
|
|
23
|
+
},
|
|
24
|
+
"homepage": "https://cesarandreslopez.github.io/tauri-agent-tools/",
|
|
25
|
+
"bugs": {
|
|
26
|
+
"url": "https://github.com/cesarandreslopez/tauri-agent-tools/issues"
|
|
27
|
+
},
|
|
28
|
+
"keywords": [
|
|
29
|
+
"tauri",
|
|
30
|
+
"screenshot",
|
|
31
|
+
"dom-inspection",
|
|
32
|
+
"desktop-app",
|
|
33
|
+
"cli",
|
|
34
|
+
"agent-tools",
|
|
35
|
+
"devtools",
|
|
36
|
+
"cross-platform"
|
|
37
|
+
],
|
|
38
|
+
"author": {
|
|
39
|
+
"name": "Cesar Andres Lopez",
|
|
40
|
+
"email": "cesarandreslopez@gmail.com"
|
|
41
|
+
},
|
|
42
|
+
"publishConfig": {
|
|
43
|
+
"access": "public"
|
|
44
|
+
},
|
|
45
|
+
"dependencies": {
|
|
46
|
+
"commander": "^14.0.0"
|
|
47
|
+
},
|
|
48
|
+
"devDependencies": {
|
|
49
|
+
"@eslint/js": "^9.0.0",
|
|
50
|
+
"@types/node": "^22.0.0",
|
|
51
|
+
"eslint": "^9.0.0",
|
|
52
|
+
"typescript": "^5.8.0",
|
|
53
|
+
"typescript-eslint": "^8.0.0",
|
|
54
|
+
"vitest": "^3.1.0"
|
|
55
|
+
},
|
|
56
|
+
"engines": {
|
|
57
|
+
"node": ">=20.0.0"
|
|
58
|
+
},
|
|
59
|
+
"files": [
|
|
60
|
+
"dist/",
|
|
61
|
+
"rust-bridge/",
|
|
62
|
+
"examples/",
|
|
63
|
+
".agents/",
|
|
64
|
+
"AGENTS.md"
|
|
65
|
+
],
|
|
66
|
+
"agents": {
|
|
67
|
+
"skills": ".agents/skills"
|
|
68
|
+
},
|
|
69
|
+
"license": "MIT"
|
|
70
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# Tauri Dev Bridge — Integration Guide
|
|
2
|
+
|
|
3
|
+
The dev bridge is a lightweight HTTP server that runs inside your Tauri app during development. It allows `tauri-agent-tools` to evaluate JavaScript in the webview for DOM-targeted screenshots and inspection.
|
|
4
|
+
|
|
5
|
+
## Quick Setup
|
|
6
|
+
|
|
7
|
+
### 1. Add dependencies to your `Cargo.toml`
|
|
8
|
+
|
|
9
|
+
```toml
|
|
10
|
+
[dependencies]
|
|
11
|
+
tiny_http = "0.12"
|
|
12
|
+
serde = { version = "1", features = ["derive"] }
|
|
13
|
+
serde_json = "1"
|
|
14
|
+
scopeguard = "1"
|
|
15
|
+
rand = "0.8"
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
### 2. Copy the bridge module
|
|
19
|
+
|
|
20
|
+
Copy `examples/tauri-bridge/src/dev_bridge.rs` into your Tauri project's `src/` directory.
|
|
21
|
+
|
|
22
|
+
### 3. Start the bridge in your app
|
|
23
|
+
|
|
24
|
+
In your `main.rs` or app setup:
|
|
25
|
+
|
|
26
|
+
```rust
|
|
27
|
+
mod dev_bridge;
|
|
28
|
+
|
|
29
|
+
fn main() {
|
|
30
|
+
tauri::Builder::default()
|
|
31
|
+
.setup(|app| {
|
|
32
|
+
if cfg!(debug_assertions) {
|
|
33
|
+
if let Err(e) = dev_bridge::start_bridge(app.handle()) {
|
|
34
|
+
eprintln!("Warning: Failed to start dev bridge: {e}");
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
Ok(())
|
|
38
|
+
})
|
|
39
|
+
.run(tauri::generate_context!())
|
|
40
|
+
.expect("error while running tauri application");
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### 4. Use tauri-agent-tools
|
|
45
|
+
|
|
46
|
+
The bridge writes a token file to `/tmp/tauri-dev-bridge-<pid>.token` which `tauri-agent-tools` auto-discovers:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
# Screenshot a specific element
|
|
50
|
+
tauri-agent-tools screenshot --selector ".toolbar" -o /tmp/toolbar.png
|
|
51
|
+
|
|
52
|
+
# Explore the DOM
|
|
53
|
+
tauri-agent-tools dom --depth 3
|
|
54
|
+
|
|
55
|
+
# Evaluate JS
|
|
56
|
+
tauri-agent-tools eval "document.title"
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## How It Works
|
|
60
|
+
|
|
61
|
+
1. Bridge starts an HTTP server on a random localhost port
|
|
62
|
+
2. A token file with `{ port, token, pid }` is written to `/tmp/`
|
|
63
|
+
3. `tauri-agent-tools` discovers the token file and authenticates via the token
|
|
64
|
+
4. Requests are `POST /eval { js, token }` — the bridge evaluates JS in the webview
|
|
65
|
+
5. The token file is cleaned up when the app exits
|
|
66
|
+
|
|
67
|
+
## Security
|
|
68
|
+
|
|
69
|
+
- **Localhost only** — the bridge binds to `127.0.0.1`
|
|
70
|
+
- **Token authenticated** — every request requires a random 32-char token
|
|
71
|
+
- **Development only** — wrapped in `cfg!(debug_assertions)`, stripped in release builds
|
|
72
|
+
- **Read-only** — `tauri-agent-tools` only reads DOM state, never injects input
|
|
73
|
+
|
|
74
|
+
## Agent-Assisted Setup
|
|
75
|
+
|
|
76
|
+
If you're using an AI coding agent (Claude Code, Codex, Cursor, etc.), the `tauri-bridge-setup` skill can guide automated setup. See `.agents/skills/tauri-bridge-setup/SKILL.md` or run:
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
cat "$(npm root -g)/tauri-agent-tools/.agents/skills/tauri-bridge-setup/SKILL.md"
|
|
80
|
+
```
|