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,11 @@
1
+ import type { DisplayServer } from '../types.js';
2
+ export declare function detectDisplayServer(): DisplayServer;
3
+ export interface ToolCheck {
4
+ name: string;
5
+ available: boolean;
6
+ installHint: string;
7
+ }
8
+ export declare function checkX11Tools(): Promise<ToolCheck[]>;
9
+ export declare function checkWaylandTools(): Promise<ToolCheck[]>;
10
+ export declare function checkMacOSTools(): Promise<ToolCheck[]>;
11
+ export declare function ensureTools(displayServer: DisplayServer): Promise<void>;
@@ -0,0 +1,73 @@
1
+ import { execFile } from 'node:child_process';
2
+ export function detectDisplayServer() {
3
+ if (process.platform === 'darwin')
4
+ return 'darwin';
5
+ if (process.env.WAYLAND_DISPLAY)
6
+ return 'wayland';
7
+ if (process.env.DISPLAY)
8
+ return 'x11';
9
+ const session = process.env.XDG_SESSION_TYPE;
10
+ if (session === 'wayland')
11
+ return 'wayland';
12
+ if (session === 'x11')
13
+ return 'x11';
14
+ return 'unknown';
15
+ }
16
+ function commandExists(cmd) {
17
+ const which = process.platform === 'win32' ? 'where' : 'which';
18
+ return new Promise((resolve) => {
19
+ execFile(which, [cmd], (error) => resolve(!error));
20
+ });
21
+ }
22
+ export async function checkX11Tools() {
23
+ const tools = [
24
+ { name: 'xdotool', installHint: 'sudo apt install xdotool' },
25
+ { name: 'import', installHint: 'sudo apt install imagemagick' },
26
+ { name: 'convert', installHint: 'sudo apt install imagemagick' },
27
+ ];
28
+ return Promise.all(tools.map(async (t) => ({
29
+ ...t,
30
+ available: await commandExists(t.name),
31
+ })));
32
+ }
33
+ export async function checkWaylandTools() {
34
+ const tools = [
35
+ { name: 'swaymsg', installHint: 'sudo apt install sway' },
36
+ { name: 'grim', installHint: 'sudo apt install grim' },
37
+ { name: 'convert', installHint: 'sudo apt install imagemagick' },
38
+ ];
39
+ return Promise.all(tools.map(async (t) => ({
40
+ ...t,
41
+ available: await commandExists(t.name),
42
+ })));
43
+ }
44
+ export async function checkMacOSTools() {
45
+ const tools = [
46
+ { name: 'screencapture', installHint: 'Built-in on macOS' },
47
+ { name: 'osascript', installHint: 'Built-in on macOS' },
48
+ { name: 'sips', installHint: 'Built-in on macOS' },
49
+ { name: 'convert', installHint: 'brew install imagemagick' },
50
+ ];
51
+ return Promise.all(tools.map(async (t) => ({
52
+ ...t,
53
+ available: await commandExists(t.name),
54
+ })));
55
+ }
56
+ export async function ensureTools(displayServer) {
57
+ let checks;
58
+ if (displayServer === 'darwin') {
59
+ checks = await checkMacOSTools();
60
+ }
61
+ else if (displayServer === 'wayland') {
62
+ checks = await checkWaylandTools();
63
+ }
64
+ else {
65
+ checks = await checkX11Tools();
66
+ }
67
+ const missing = checks.filter((t) => !t.available);
68
+ if (missing.length > 0) {
69
+ const hints = missing.map((t) => ` ${t.name}: ${t.installHint}`).join('\n');
70
+ throw new Error(`Missing required tools:\n${hints}`);
71
+ }
72
+ }
73
+ //# sourceMappingURL=detect.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detect.js","sourceRoot":"","sources":["../../src/platform/detect.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import type { ImageFormat, PlatformAdapter, WindowInfo } from '../types.js';
2
+ export declare class MacOSAdapter implements PlatformAdapter {
3
+ findWindow(title: string): Promise<string>;
4
+ captureWindow(windowId: string, format: ImageFormat): Promise<Buffer>;
5
+ getWindowGeometry(windowId: string): Promise<WindowInfo>;
6
+ getWindowName(windowId: string): Promise<string>;
7
+ listWindows(): Promise<WindowInfo[]>;
8
+ }
@@ -0,0 +1,111 @@
1
+ import { mkdtemp, readFile, rm } from 'node:fs/promises';
2
+ import { tmpdir } from 'node:os';
3
+ import { join } from 'node:path';
4
+ import { exec, validateWindowId } from '../util/exec.js';
5
+ async function runJxa(script) {
6
+ const { stdout } = await exec('osascript', ['-l', 'JavaScript', '-e', script]);
7
+ return stdout.toString().trim();
8
+ }
9
+ async function getWindowList() {
10
+ const script = `
11
+ ObjC.import('CoreGraphics');
12
+ var list = ObjC.deepUnwrap(
13
+ $.CGWindowListCopyWindowInfo($.kCGWindowListOptionOnScreenOnly, 0)
14
+ );
15
+ JSON.stringify(list.map(function(w) {
16
+ return {
17
+ kCGWindowNumber: w.kCGWindowNumber,
18
+ kCGWindowOwnerPID: w.kCGWindowOwnerPID || 0,
19
+ kCGWindowName: w.kCGWindowName || '',
20
+ kCGWindowOwnerName: w.kCGWindowOwnerName || '',
21
+ kCGWindowBounds: w.kCGWindowBounds
22
+ };
23
+ }));`;
24
+ const raw = await runJxa(script);
25
+ const windows = JSON.parse(raw);
26
+ // Detect Screen Recording permission issue: all names empty
27
+ const hasAnyName = windows.some((w) => (w.kCGWindowName && w.kCGWindowName.length > 0) ||
28
+ (w.kCGWindowOwnerName && w.kCGWindowOwnerName.length > 0));
29
+ if (windows.length > 0 && !hasAnyName) {
30
+ throw new Error('Screen Recording permission required. Grant access in System Settings → Privacy & Security → Screen Recording, then restart your terminal.');
31
+ }
32
+ return windows;
33
+ }
34
+ async function normalizeRetina(filePath, logicalWidth) {
35
+ const { stdout } = await exec('sips', ['-g', 'pixelWidth', filePath]);
36
+ const match = stdout.toString().match(/pixelWidth:\s*(\d+)/);
37
+ if (!match)
38
+ return;
39
+ const pixelWidth = parseInt(match[1], 10);
40
+ if (pixelWidth > logicalWidth) {
41
+ await exec('sips', ['--resampleWidth', String(logicalWidth), filePath]);
42
+ }
43
+ }
44
+ export class MacOSAdapter {
45
+ async findWindow(title) {
46
+ const windows = await getWindowList();
47
+ const match = windows.find((w) => (w.kCGWindowName && w.kCGWindowName.includes(title)) ||
48
+ (w.kCGWindowOwnerName && w.kCGWindowOwnerName.includes(title)));
49
+ if (!match) {
50
+ throw new Error(`No window found matching: ${title}`);
51
+ }
52
+ return String(match.kCGWindowNumber);
53
+ }
54
+ async captureWindow(windowId, format) {
55
+ validateWindowId(windowId);
56
+ const tmpDir = await mkdtemp(join(tmpdir(), 'tauri-cap-'));
57
+ const ext = format === 'jpg' ? 'jpg' : 'png';
58
+ const tmpFile = join(tmpDir, `capture.${ext}`);
59
+ try {
60
+ // -l captures specific window by CGWindowID, -o disables shadow, -x disables sound
61
+ await exec('screencapture', ['-l', windowId, '-o', '-x', tmpFile]);
62
+ // Get logical window width for Retina normalization
63
+ const geom = await this.getWindowGeometry(windowId);
64
+ await normalizeRetina(tmpFile, geom.width);
65
+ // Convert to jpg if requested (screencapture always writes png with -l)
66
+ if (format === 'jpg') {
67
+ await exec('sips', ['-s', 'format', 'jpeg', tmpFile, '--out', tmpFile]);
68
+ }
69
+ return await readFile(tmpFile);
70
+ }
71
+ finally {
72
+ await rm(tmpDir, { recursive: true, force: true }).catch(() => { });
73
+ }
74
+ }
75
+ async getWindowGeometry(windowId) {
76
+ validateWindowId(windowId);
77
+ const windows = await getWindowList();
78
+ const id = parseInt(windowId, 10);
79
+ const win = windows.find((w) => w.kCGWindowNumber === id);
80
+ if (!win) {
81
+ throw new Error(`Window ${windowId} not found`);
82
+ }
83
+ return {
84
+ windowId,
85
+ name: win.kCGWindowName || win.kCGWindowOwnerName || undefined,
86
+ x: win.kCGWindowBounds.X,
87
+ y: win.kCGWindowBounds.Y,
88
+ width: win.kCGWindowBounds.Width,
89
+ height: win.kCGWindowBounds.Height,
90
+ };
91
+ }
92
+ async getWindowName(windowId) {
93
+ const geom = await this.getWindowGeometry(windowId);
94
+ return geom.name ?? '';
95
+ }
96
+ async listWindows() {
97
+ const windows = await getWindowList();
98
+ return windows
99
+ .filter((w) => w.kCGWindowName || w.kCGWindowOwnerName)
100
+ .map((w) => ({
101
+ windowId: String(w.kCGWindowNumber),
102
+ pid: w.kCGWindowOwnerPID || undefined,
103
+ name: w.kCGWindowName || w.kCGWindowOwnerName || undefined,
104
+ x: w.kCGWindowBounds.X,
105
+ y: w.kCGWindowBounds.Y,
106
+ width: w.kCGWindowBounds.Width,
107
+ height: w.kCGWindowBounds.Height,
108
+ }));
109
+ }
110
+ }
111
+ //# sourceMappingURL=macos.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"macos.js","sourceRoot":"","sources":["../../src/platform/macos.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import type { ImageFormat, PlatformAdapter, WindowInfo } from '../types.js';
2
+ export declare class WaylandAdapter implements PlatformAdapter {
3
+ findWindow(title: string): Promise<string>;
4
+ captureWindow(windowId: string, format: ImageFormat): Promise<Buffer>;
5
+ getWindowGeometry(windowId: string): Promise<WindowInfo>;
6
+ getWindowName(windowId: string): Promise<string>;
7
+ listWindows(): Promise<WindowInfo[]>;
8
+ private _collectLeaves;
9
+ private _findById;
10
+ }
@@ -0,0 +1,98 @@
1
+ import { exec } from '../util/exec.js';
2
+ function findInTree(node, title) {
3
+ if (node.name && node.name.includes(title))
4
+ return node;
5
+ for (const child of node.nodes ?? []) {
6
+ const found = findInTree(child, title);
7
+ if (found)
8
+ return found;
9
+ }
10
+ for (const child of node.floating_nodes ?? []) {
11
+ const found = findInTree(child, title);
12
+ if (found)
13
+ return found;
14
+ }
15
+ return null;
16
+ }
17
+ export class WaylandAdapter {
18
+ async findWindow(title) {
19
+ const { stdout } = await exec('swaymsg', ['-t', 'get_tree', '-r']);
20
+ const tree = JSON.parse(stdout.toString());
21
+ const node = findInTree(tree, title);
22
+ if (!node) {
23
+ throw new Error(`No window found matching: ${title}`);
24
+ }
25
+ return String(node.id);
26
+ }
27
+ async captureWindow(windowId, format) {
28
+ // On Wayland, we capture the window region using grim
29
+ const geom = await this.getWindowGeometry(windowId);
30
+ const region = `${geom.x},${geom.y} ${geom.width}x${geom.height}`;
31
+ const fmt = format === 'jpg' ? 'jpeg' : 'png';
32
+ const { stdout } = await exec('grim', ['-g', region, '-t', fmt, '-']);
33
+ return stdout;
34
+ }
35
+ async getWindowGeometry(windowId) {
36
+ const { stdout } = await exec('swaymsg', ['-t', 'get_tree', '-r']);
37
+ const tree = JSON.parse(stdout.toString());
38
+ const node = this._findById(tree, parseInt(windowId, 10));
39
+ if (!node) {
40
+ throw new Error(`Window ${windowId} not found in sway tree`);
41
+ }
42
+ return {
43
+ windowId,
44
+ name: node.name ?? undefined,
45
+ x: node.rect.x,
46
+ y: node.rect.y,
47
+ width: node.rect.width,
48
+ height: node.rect.height,
49
+ };
50
+ }
51
+ async getWindowName(windowId) {
52
+ const geom = await this.getWindowGeometry(windowId);
53
+ return geom.name ?? '';
54
+ }
55
+ async listWindows() {
56
+ const { stdout } = await exec('swaymsg', ['-t', 'get_tree', '-r']);
57
+ const tree = JSON.parse(stdout.toString());
58
+ const leaves = [];
59
+ this._collectLeaves(tree, leaves);
60
+ return leaves
61
+ .filter((n) => n.name)
62
+ .map((n) => ({
63
+ windowId: String(n.id),
64
+ pid: n.pid,
65
+ name: n.name ?? undefined,
66
+ x: n.rect.x,
67
+ y: n.rect.y,
68
+ width: n.rect.width,
69
+ height: n.rect.height,
70
+ }));
71
+ }
72
+ _collectLeaves(node, out) {
73
+ const children = [...(node.nodes ?? []), ...(node.floating_nodes ?? [])];
74
+ if (children.length === 0 && node.name) {
75
+ out.push(node);
76
+ return;
77
+ }
78
+ for (const child of children) {
79
+ this._collectLeaves(child, out);
80
+ }
81
+ }
82
+ _findById(node, id) {
83
+ if (node.id === id)
84
+ return node;
85
+ for (const child of node.nodes ?? []) {
86
+ const found = this._findById(child, id);
87
+ if (found)
88
+ return found;
89
+ }
90
+ for (const child of node.floating_nodes ?? []) {
91
+ const found = this._findById(child, id);
92
+ if (found)
93
+ return found;
94
+ }
95
+ return null;
96
+ }
97
+ }
98
+ //# sourceMappingURL=wayland.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wayland.js","sourceRoot":"","sources":["../../src/platform/wayland.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import type { ImageFormat, PlatformAdapter, WindowInfo } from '../types.js';
2
+ export declare class X11Adapter implements PlatformAdapter {
3
+ findWindow(title: string): Promise<string>;
4
+ captureWindow(windowId: string, format: ImageFormat): Promise<Buffer>;
5
+ getWindowGeometry(windowId: string): Promise<WindowInfo>;
6
+ getWindowName(windowId: string): Promise<string>;
7
+ listWindows(): Promise<WindowInfo[]>;
8
+ }
@@ -0,0 +1,78 @@
1
+ import { exec, validateWindowId } from '../util/exec.js';
2
+ export class X11Adapter {
3
+ async findWindow(title) {
4
+ const { stdout } = await exec('xdotool', ['search', '--name', title]);
5
+ const ids = stdout.toString().trim().split('\n').filter(Boolean);
6
+ if (ids.length === 0) {
7
+ throw new Error(`No window found matching: ${title}`);
8
+ }
9
+ return ids[0];
10
+ }
11
+ async captureWindow(windowId, format) {
12
+ validateWindowId(windowId);
13
+ const fmt = format === 'jpg' ? 'jpg' : 'png';
14
+ const { stdout } = await exec('import', ['-window', windowId, `${fmt}:-`]);
15
+ return stdout;
16
+ }
17
+ async getWindowGeometry(windowId) {
18
+ validateWindowId(windowId);
19
+ const { stdout } = await exec('xdotool', ['getwindowgeometry', '--shell', windowId]);
20
+ const output = stdout.toString();
21
+ const parse = (key) => {
22
+ const match = output.match(new RegExp(`${key}=(\\d+)`));
23
+ if (!match)
24
+ throw new Error(`Failed to parse ${key} from xdotool output`);
25
+ return parseInt(match[1], 10);
26
+ };
27
+ return {
28
+ windowId,
29
+ x: parse('X'),
30
+ y: parse('Y'),
31
+ width: parse('WIDTH'),
32
+ height: parse('HEIGHT'),
33
+ };
34
+ }
35
+ async getWindowName(windowId) {
36
+ validateWindowId(windowId);
37
+ const { stdout } = await exec('xdotool', ['getwindowname', windowId]);
38
+ return stdout.toString().trim();
39
+ }
40
+ async listWindows() {
41
+ const { stdout } = await exec('xdotool', ['search', '--name', '']);
42
+ const ids = stdout.toString().trim().split('\n').filter(Boolean);
43
+ const windows = [];
44
+ for (const id of ids) {
45
+ try {
46
+ const [nameResult, geomResult, pidResult] = await Promise.all([
47
+ exec('xdotool', ['getwindowname', id]),
48
+ exec('xdotool', ['getwindowgeometry', '--shell', id]),
49
+ exec('xdotool', ['getwindowpid', id]),
50
+ ]);
51
+ const name = nameResult.stdout.toString().trim();
52
+ if (!name)
53
+ continue;
54
+ const geomOutput = geomResult.stdout.toString();
55
+ const parse = (key) => {
56
+ const match = geomOutput.match(new RegExp(`${key}=(\\d+)`));
57
+ return match ? parseInt(match[1], 10) : 0;
58
+ };
59
+ const pid = parseInt(pidResult.stdout.toString().trim(), 10);
60
+ windows.push({
61
+ windowId: id,
62
+ pid: isNaN(pid) ? undefined : pid,
63
+ name,
64
+ x: parse('X'),
65
+ y: parse('Y'),
66
+ width: parse('WIDTH'),
67
+ height: parse('HEIGHT'),
68
+ });
69
+ }
70
+ catch {
71
+ // Skip windows that disappeared or can't be queried
72
+ continue;
73
+ }
74
+ }
75
+ return windows;
76
+ }
77
+ }
78
+ //# sourceMappingURL=x11.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"x11.js","sourceRoot":"","sources":["../../src/platform/x11.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ export interface WindowInfo {
2
+ windowId: string;
3
+ pid?: number;
4
+ name?: string;
5
+ x: number;
6
+ y: number;
7
+ width: number;
8
+ height: number;
9
+ }
10
+ export interface ElementRect {
11
+ x: number;
12
+ y: number;
13
+ width: number;
14
+ height: number;
15
+ }
16
+ export interface BridgeConfig {
17
+ port: number;
18
+ token: string;
19
+ }
20
+ export type DisplayServer = 'x11' | 'wayland' | 'darwin' | 'unknown';
21
+ export type ImageFormat = 'png' | 'jpg';
22
+ export interface PlatformAdapter {
23
+ findWindow(title: string): Promise<string>;
24
+ captureWindow(windowId: string, format: ImageFormat): Promise<Buffer>;
25
+ getWindowGeometry(windowId: string): Promise<WindowInfo>;
26
+ getWindowName(windowId: string): Promise<string>;
27
+ listWindows(): Promise<WindowInfo[]>;
28
+ }
29
+ export interface WindowListEntry extends WindowInfo {
30
+ tauri: boolean;
31
+ bridge?: BridgeConfig;
32
+ }
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,9 @@
1
+ export declare function validateWindowId(id: string): void;
2
+ export interface ExecResult {
3
+ stdout: Buffer;
4
+ stderr: string;
5
+ }
6
+ export declare function exec(cmd: string, args: string[], options?: {
7
+ stdin?: Buffer;
8
+ timeout?: number;
9
+ }): Promise<ExecResult>;
@@ -0,0 +1,31 @@
1
+ import { execFile as cpExecFile } from 'node:child_process';
2
+ const MAX_BUFFER = 100 * 1024 * 1024; // 100MB
3
+ const WINDOW_ID_RE = /^\d+$/;
4
+ export function validateWindowId(id) {
5
+ if (!WINDOW_ID_RE.test(id)) {
6
+ throw new Error(`Invalid window ID: ${id}`);
7
+ }
8
+ }
9
+ export function exec(cmd, args, options) {
10
+ return new Promise((resolve, reject) => {
11
+ const child = cpExecFile(cmd, args, {
12
+ maxBuffer: MAX_BUFFER,
13
+ encoding: 'buffer',
14
+ timeout: options?.timeout,
15
+ }, (error, stdout, stderr) => {
16
+ if (error) {
17
+ const stderrStr = Buffer.isBuffer(stderr) ? stderr.toString() : String(stderr ?? '');
18
+ reject(new Error(`${cmd} failed: ${stderrStr || error.message}`));
19
+ return;
20
+ }
21
+ resolve({
22
+ stdout: Buffer.isBuffer(stdout) ? stdout : Buffer.from(stdout),
23
+ stderr: Buffer.isBuffer(stderr) ? stderr.toString() : String(stderr ?? ''),
24
+ });
25
+ });
26
+ if (options?.stdin && child.stdin) {
27
+ child.stdin.end(options.stdin);
28
+ }
29
+ });
30
+ }
31
+ //# sourceMappingURL=exec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"exec.js","sourceRoot":"","sources":["../../src/util/exec.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,IAAI,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAE5D,MAAM,UAAU,GAAG,GAAG,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC,QAAQ;AAC9C,MAAM,YAAY,GAAG,OAAO,CAAC;AAE7B,MAAM,UAAU,gBAAgB,CAAC,EAAU;IACzC,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC;QAC3B,MAAM,IAAI,KAAK,CAAC,sBAAsB,EAAE,EAAE,CAAC,CAAC;IAC9C,CAAC;AACH,CAAC;AAOD,MAAM,UAAU,IAAI,CAClB,GAAW,EACX,IAAc,EACd,OAA8C;IAE9C,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,KAAK,GAAG,UAAU,CACtB,GAAG,EACH,IAAI,EACJ;YACE,SAAS,EAAE,UAAU;YACrB,QAAQ,EAAE,QAAQ;YAClB,OAAO,EAAE,OAAO,EAAE,OAAO;SAC1B,EACD,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE;YACxB,IAAI,KAAK,EAAE,CAAC;gBACV,MAAM,SAAS,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC;gBACrF,MAAM,CAAC,IAAI,KAAK,CAAC,GAAG,GAAG,YAAY,SAAS,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;gBAClE,OAAO;YACT,CAAC;YACD,OAAO,CAAC;gBACN,MAAM,EAAE,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,MAA2B,CAAC;gBACnF,MAAM,EAAE,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,IAAI,EAAE,CAAC;aAC3E,CAAC,CAAC;QACL,CAAC,CACF,CAAC;QAEF,IAAI,OAAO,EAAE,KAAK,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;YAClC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QACjC,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,10 @@
1
+ import type { ElementRect, ImageFormat } from '../types.js';
2
+ export declare function cropImage(buffer: Buffer, rect: ElementRect, format: ImageFormat): Promise<Buffer>;
3
+ export declare function resizeImage(buffer: Buffer, maxWidth: number, format: ImageFormat): Promise<Buffer>;
4
+ export declare function computeCropRect(elementRect: ElementRect, viewport: {
5
+ width: number;
6
+ height: number;
7
+ }, windowGeometry: {
8
+ width: number;
9
+ height: number;
10
+ }): ElementRect;
@@ -0,0 +1,23 @@
1
+ import { exec } from './exec.js';
2
+ export async function cropImage(buffer, rect, format) {
3
+ const fmt = format === 'jpg' ? 'jpg' : 'png';
4
+ const crop = `${Math.round(rect.width)}x${Math.round(rect.height)}+${Math.round(rect.x)}+${Math.round(rect.y)}`;
5
+ const { stdout } = await exec('convert', [`${fmt}:-`, '-crop', crop, '+repage', `${fmt}:-`], { stdin: buffer });
6
+ return stdout;
7
+ }
8
+ export async function resizeImage(buffer, maxWidth, format) {
9
+ const fmt = format === 'jpg' ? 'jpg' : 'png';
10
+ const { stdout } = await exec('convert', [`${fmt}:-`, '-resize', `${maxWidth}x\\>`, `${fmt}:-`], { stdin: buffer });
11
+ return stdout;
12
+ }
13
+ export function computeCropRect(elementRect, viewport, windowGeometry) {
14
+ const decorX = windowGeometry.width - viewport.width;
15
+ const decorY = windowGeometry.height - viewport.height;
16
+ return {
17
+ x: decorX + elementRect.x,
18
+ y: decorY + elementRect.y,
19
+ width: elementRect.width,
20
+ height: elementRect.height,
21
+ };
22
+ }
23
+ //# sourceMappingURL=image.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"image.js","sourceRoot":"","sources":["../../src/util/image.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,MAAc,EACd,IAAiB,EACjB,MAAmB;IAEnB,MAAM,GAAG,GAAG,MAAM,KAAK,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC;IAC7C,MAAM,IAAI,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;IAChH,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,IAAI,CAC3B,SAAS,EACT,CAAC,GAAG,GAAG,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,GAAG,GAAG,IAAI,CAAC,EAClD,EAAE,KAAK,EAAE,MAAM,EAAE,CAClB,CAAC;IACF,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,MAAc,EACd,QAAgB,EAChB,MAAmB;IAEnB,MAAM,GAAG,GAAG,MAAM,KAAK,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC;IAC7C,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,IAAI,CAC3B,SAAS,EACT,CAAC,GAAG,GAAG,IAAI,EAAE,SAAS,EAAE,GAAG,QAAQ,MAAM,EAAE,GAAG,GAAG,IAAI,CAAC,EACtD,EAAE,KAAK,EAAE,MAAM,EAAE,CAClB,CAAC;IACF,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,eAAe,CAC7B,WAAwB,EACxB,QAA2C,EAC3C,cAAiD;IAEjD,MAAM,MAAM,GAAG,cAAc,CAAC,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC;IACrD,MAAM,MAAM,GAAG,cAAc,CAAC,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC;IACvD,OAAO;QACL,CAAC,EAAE,MAAM,GAAG,WAAW,CAAC,CAAC;QACzB,CAAC,EAAE,MAAM,GAAG,WAAW,CAAC,CAAC;QACzB,KAAK,EAAE,WAAW,CAAC,KAAK;QACxB,MAAM,EAAE,WAAW,CAAC,MAAM;KAC3B,CAAC;AACJ,CAAC"}
@@ -0,0 +1,13 @@
1
+ [package]
2
+ name = "tauri-dev-bridge-example"
3
+ version = "0.1.0"
4
+ edition = "2021"
5
+ description = "Example Tauri app with dev bridge for tauri-agent-tools"
6
+
7
+ [dependencies]
8
+ tauri = { version = "2", features = ["devtools"] }
9
+ tiny_http = "0.12"
10
+ serde = { version = "1", features = ["derive"] }
11
+ serde_json = "1"
12
+ scopeguard = "1"
13
+ rand = "0.8"