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.
Files changed (73) hide show
  1. package/.agents/skills/tauri-agent-tools/SKILL.md +104 -0
  2. package/.agents/skills/tauri-bridge-setup/SKILL.md +95 -0
  3. package/AGENTS.md +30 -0
  4. package/LICENSE +21 -0
  5. package/README.md +338 -0
  6. package/dist/bridge/client.d.ts +15 -0
  7. package/dist/bridge/client.js +119 -0
  8. package/dist/bridge/client.js.map +1 -0
  9. package/dist/bridge/tokenDiscovery.d.ts +3 -0
  10. package/dist/bridge/tokenDiscovery.js +77 -0
  11. package/dist/bridge/tokenDiscovery.js.map +1 -0
  12. package/dist/cli.d.ts +2 -0
  13. package/dist/cli.js +49 -0
  14. package/dist/cli.js.map +1 -0
  15. package/dist/commands/consoleMonitor.d.ts +2 -0
  16. package/dist/commands/consoleMonitor.js +133 -0
  17. package/dist/commands/consoleMonitor.js.map +1 -0
  18. package/dist/commands/dom.d.ts +2 -0
  19. package/dist/commands/dom.js +186 -0
  20. package/dist/commands/dom.js.map +1 -0
  21. package/dist/commands/eval.d.ts +2 -0
  22. package/dist/commands/eval.js +27 -0
  23. package/dist/commands/eval.js.map +1 -0
  24. package/dist/commands/info.d.ts +3 -0
  25. package/dist/commands/info.js +28 -0
  26. package/dist/commands/info.js.map +1 -0
  27. package/dist/commands/ipcMonitor.d.ts +2 -0
  28. package/dist/commands/ipcMonitor.js +122 -0
  29. package/dist/commands/ipcMonitor.js.map +1 -0
  30. package/dist/commands/listWindows.d.ts +3 -0
  31. package/dist/commands/listWindows.js +58 -0
  32. package/dist/commands/listWindows.js.map +1 -0
  33. package/dist/commands/pageState.d.ts +2 -0
  34. package/dist/commands/pageState.js +43 -0
  35. package/dist/commands/pageState.js.map +1 -0
  36. package/dist/commands/screenshot.d.ts +3 -0
  37. package/dist/commands/screenshot.js +81 -0
  38. package/dist/commands/screenshot.js.map +1 -0
  39. package/dist/commands/shared.d.ts +7 -0
  40. package/dist/commands/shared.js +27 -0
  41. package/dist/commands/shared.js.map +1 -0
  42. package/dist/commands/storage.d.ts +2 -0
  43. package/dist/commands/storage.js +110 -0
  44. package/dist/commands/storage.js.map +1 -0
  45. package/dist/commands/wait.d.ts +3 -0
  46. package/dist/commands/wait.js +63 -0
  47. package/dist/commands/wait.js.map +1 -0
  48. package/dist/platform/detect.d.ts +11 -0
  49. package/dist/platform/detect.js +73 -0
  50. package/dist/platform/detect.js.map +1 -0
  51. package/dist/platform/macos.d.ts +8 -0
  52. package/dist/platform/macos.js +111 -0
  53. package/dist/platform/macos.js.map +1 -0
  54. package/dist/platform/wayland.d.ts +10 -0
  55. package/dist/platform/wayland.js +98 -0
  56. package/dist/platform/wayland.js.map +1 -0
  57. package/dist/platform/x11.d.ts +8 -0
  58. package/dist/platform/x11.js +78 -0
  59. package/dist/platform/x11.js.map +1 -0
  60. package/dist/types.d.ts +32 -0
  61. package/dist/types.js +2 -0
  62. package/dist/types.js.map +1 -0
  63. package/dist/util/exec.d.ts +9 -0
  64. package/dist/util/exec.js +31 -0
  65. package/dist/util/exec.js.map +1 -0
  66. package/dist/util/image.d.ts +10 -0
  67. package/dist/util/image.js +23 -0
  68. package/dist/util/image.js.map +1 -0
  69. package/examples/tauri-bridge/Cargo.toml +13 -0
  70. package/examples/tauri-bridge/src/dev_bridge.rs +146 -0
  71. package/examples/tauri-bridge/src/main.rs +16 -0
  72. package/package.json +70 -0
  73. 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
+ ```