tauri-agent-tools 0.7.0 → 0.8.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 (53) hide show
  1. package/.agents/skills/tauri-agent-tools/SKILL.md +50 -13
  2. package/.agents/skills/tauri-bridge-setup/SKILL.md +5 -3
  3. package/.agents/skills/tauri-debug-quickstart/SKILL.md +9 -6
  4. package/AGENTS.md +13 -3
  5. package/README.md +26 -5
  6. package/dist/bridge/client.d.ts +8 -0
  7. package/dist/bridge/client.js +20 -0
  8. package/dist/bridge/client.js.map +1 -1
  9. package/dist/bridge/tokenDiscovery.js +29 -29
  10. package/dist/bridge/tokenDiscovery.js.map +1 -1
  11. package/dist/cli.js +5 -1
  12. package/dist/cli.js.map +1 -1
  13. package/dist/commands/bundle.d.ts +2 -0
  14. package/dist/commands/bundle.js +251 -0
  15. package/dist/commands/bundle.js.map +1 -0
  16. package/dist/commands/capabilitiesAudit.js +14 -1
  17. package/dist/commands/capabilitiesAudit.js.map +1 -1
  18. package/dist/commands/capture.js +1 -1
  19. package/dist/commands/health.js +19 -1
  20. package/dist/commands/health.js.map +1 -1
  21. package/dist/commands/invoke.js +14 -4
  22. package/dist/commands/invoke.js.map +1 -1
  23. package/dist/commands/ipcMonitor.d.ts +3 -0
  24. package/dist/commands/ipcMonitor.js +60 -14
  25. package/dist/commands/ipcMonitor.js.map +1 -1
  26. package/dist/commands/logs.d.ts +2 -0
  27. package/dist/commands/logs.js +193 -0
  28. package/dist/commands/logs.js.map +1 -0
  29. package/dist/commands/pageState.d.ts +1 -0
  30. package/dist/commands/pageState.js +2 -2
  31. package/dist/commands/pageState.js.map +1 -1
  32. package/dist/commands/processTree.js +116 -4
  33. package/dist/commands/processTree.js.map +1 -1
  34. package/dist/commands/shared.d.ts +31 -0
  35. package/dist/commands/shared.js +52 -1
  36. package/dist/commands/shared.js.map +1 -1
  37. package/dist/commands/snapshot.js +1 -1
  38. package/dist/commands/webviewAttach.js +17 -1
  39. package/dist/commands/webviewAttach.js.map +1 -1
  40. package/dist/platform/macos.js +35 -15
  41. package/dist/platform/macos.js.map +1 -1
  42. package/dist/util/logMerge.d.ts +42 -0
  43. package/dist/util/logMerge.js +133 -0
  44. package/dist/util/logMerge.js.map +1 -0
  45. package/dist/util/mergeByTimestamp.d.ts +18 -0
  46. package/dist/util/mergeByTimestamp.js +30 -0
  47. package/dist/util/mergeByTimestamp.js.map +1 -0
  48. package/dist/util/psTree.d.ts +28 -0
  49. package/dist/util/psTree.js +76 -0
  50. package/dist/util/psTree.js.map +1 -0
  51. package/examples/tauri-bridge/src/dev_bridge.rs +112 -32
  52. package/package.json +1 -1
  53. package/rust-bridge/README.md +10 -1
@@ -8,22 +8,42 @@ async function runJxa(script) {
8
8
  const { stdout } = await exec('osascript', ['-l', 'JavaScript', '-e', script]);
9
9
  return stdout.toString().trim();
10
10
  }
11
- async function getWindowList() {
12
- const script = `
11
+ // JXA reads window info by iterating the CFArray element-by-element. We avoid
12
+ // ObjC.deepUnwrap (and ObjC.unwrap on the CFDictionary elements) because both
13
+ // are broken on recent macOS for CGWindowListCopyWindowInfo's CFArray result —
14
+ // deepUnwrap returns a non-array ("list.map is not a function") and unwrap loses
15
+ // all keys. Iterating with CFArrayGetCount/CFArrayGetValueAtIndex and reading
16
+ // each key via objectForKey().js works on every supported macOS version and
17
+ // keeps this path 100% dependency-free (osascript is built in).
18
+ const WINDOW_LIST_SCRIPT = `
13
19
  ObjC.import('CoreGraphics');
14
- var list = ObjC.deepUnwrap(
15
- $.CGWindowListCopyWindowInfo($.kCGWindowListOptionOnScreenOnly, 0)
16
- );
17
- JSON.stringify(list.map(function(w) {
18
- return {
19
- kCGWindowNumber: w.kCGWindowNumber,
20
- kCGWindowOwnerPID: w.kCGWindowOwnerPID || 0,
21
- kCGWindowName: w.kCGWindowName || '',
22
- kCGWindowOwnerName: w.kCGWindowOwnerName || '',
23
- kCGWindowBounds: w.kCGWindowBounds
24
- };
25
- }));`;
26
- const raw = await runJxa(script);
20
+ ObjC.import('Foundation');
21
+ function val(dict, key) {
22
+ var ref = dict.objectForKey(key);
23
+ return ref ? ref.js : null;
24
+ }
25
+ var cfList = $.CGWindowListCopyWindowInfo($.kCGWindowListOptionOnScreenOnly, 0);
26
+ var count = $.CFArrayGetCount(cfList);
27
+ var out = [];
28
+ for (var i = 0; i < count; i++) {
29
+ var w = ObjC.castRefToObject($.CFArrayGetValueAtIndex(cfList, i));
30
+ var bounds = w.objectForKey('kCGWindowBounds');
31
+ out.push({
32
+ kCGWindowNumber: val(w, 'kCGWindowNumber') || 0,
33
+ kCGWindowOwnerPID: val(w, 'kCGWindowOwnerPID') || 0,
34
+ kCGWindowName: val(w, 'kCGWindowName') || '',
35
+ kCGWindowOwnerName: val(w, 'kCGWindowOwnerName') || '',
36
+ kCGWindowBounds: {
37
+ X: (bounds ? val(bounds, 'X') : 0) || 0,
38
+ Y: (bounds ? val(bounds, 'Y') : 0) || 0,
39
+ Width: (bounds ? val(bounds, 'Width') : 0) || 0,
40
+ Height: (bounds ? val(bounds, 'Height') : 0) || 0,
41
+ },
42
+ });
43
+ }
44
+ JSON.stringify(out);`;
45
+ async function getWindowList() {
46
+ const raw = await runJxa(WINDOW_LIST_SCRIPT);
27
47
  const windows = z.array(CGWindowInfoSchema).parse(JSON.parse(raw));
28
48
  // Detect Screen Recording permission issue: all names empty
29
49
  const hasAnyName = windows.some((w) => (w.kCGWindowName && w.kCGWindowName.length > 0) ||
@@ -1 +1 @@
1
- {"version":3,"file":"macos.js","sourceRoot":"","sources":["../../src/platform/macos.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"macos.js","sourceRoot":"","sources":["../../src/platform/macos.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,42 @@
1
+ import type { OsLogLevel } from '../schemas/osLog.js';
2
+ import type { RustLogEntry } from '../schemas/bridge.js';
3
+ /**
4
+ * One entry in a merged, cross-source log timeline. Superset of the normalized
5
+ * `os-logs` shape (kept local so the shared `NormalizedLogEntry` schema — and
6
+ * the commands validated against it — are untouched).
7
+ */
8
+ export interface MergedLogEntry {
9
+ /** ISO-8601 timestamp, or '' when unknown. */
10
+ ts: string;
11
+ level: OsLogLevel;
12
+ /** 'rust' | 'sidecar:<name>' (bridge) or 'file:<basename>' (disk). */
13
+ source: string;
14
+ /** Target/module path, best-effort. */
15
+ subsystem: string;
16
+ message: string;
17
+ origin: 'bridge' | 'file';
18
+ correlation?: Record<string, string>;
19
+ raw?: unknown;
20
+ }
21
+ export declare const LEVEL_RANK: Record<OsLogLevel, number>;
22
+ export interface ParsedLine {
23
+ ts: string;
24
+ level: OsLogLevel;
25
+ subsystem: string;
26
+ message: string;
27
+ }
28
+ /**
29
+ * Best-effort parse of one on-disk log line. Pulls leading bracketed tokens
30
+ * (`[ts][target][LEVEL]` in any order) when present; otherwise treats the whole
31
+ * line as the message and infers the level from inline text. Never throws — an
32
+ * unrecognized line still yields a usable entry (ts='', level='info').
33
+ */
34
+ export declare function parseLogLine(line: string): ParsedLine;
35
+ /** Normalize a bridge /logs ring-buffer entry into a MergedLogEntry. */
36
+ export declare function normalizeRustLog(e: RustLogEntry): MergedLogEntry;
37
+ /**
38
+ * Best-effort correlation-id inference from a message (e.g. `run_id=abc`,
39
+ * `requestId: xyz`, `block-id=42`). Keys shorter than 4 chars (like `pid`) are
40
+ * ignored to cut noise. Returns undefined when nothing is found.
41
+ */
42
+ export declare function inferCorrelation(message: string): Record<string, string> | undefined;
@@ -0,0 +1,133 @@
1
+ export const LEVEL_RANK = { debug: 0, info: 1, warn: 2, error: 3 };
2
+ const LEVEL_ALIASES = {
3
+ trace: 'debug',
4
+ debug: 'debug',
5
+ dbg: 'debug',
6
+ info: 'info',
7
+ information: 'info',
8
+ warn: 'warn',
9
+ warning: 'warn',
10
+ error: 'error',
11
+ err: 'error',
12
+ fatal: 'error',
13
+ crit: 'error',
14
+ critical: 'error',
15
+ };
16
+ function looksLikeTimestampPart(token) {
17
+ return (/\d{4}-\d{2}-\d{2}/.test(token) ||
18
+ /\d{1,2}:\d{2}:\d{2}/.test(token) ||
19
+ /^\d{10,13}$/.test(token));
20
+ }
21
+ /**
22
+ * Normalize a timestamp candidate so `Date.parse` reads it deterministically:
23
+ * unify "date time" → "dateTtime" and, when a time component has no explicit
24
+ * timezone, treat it as UTC (append `Z`). This keeps cross-source ordering — and
25
+ * the emitted ISO output — independent of the host machine's timezone.
26
+ */
27
+ function normalizeForParse(s) {
28
+ let v = s.trim();
29
+ v = v.replace(/^(\d{4}-\d{2}-\d{2})[ ](\d{2}:\d{2})/, '$1T$2');
30
+ const hasTime = /T\d{2}:\d{2}/.test(v) || /^\d{1,2}:\d{2}:\d{2}/.test(v);
31
+ const hasTz = /[zZ]$/.test(v) || /[+-]\d{2}:?\d{2}$/.test(v);
32
+ if (hasTime && !hasTz)
33
+ v += 'Z';
34
+ return v;
35
+ }
36
+ function combineTimestamp(parts) {
37
+ if (parts.length === 0)
38
+ return '';
39
+ if (parts.length === 1 && /^\d{10,13}$/.test(parts[0])) {
40
+ const raw = parts[0];
41
+ const ms = raw.length <= 10 ? Number(raw) * 1000 : Number(raw);
42
+ const d = new Date(ms);
43
+ return Number.isNaN(d.getTime()) ? '' : d.toISOString();
44
+ }
45
+ const candidates = [parts.join('T'), parts.join(' '), parts.join(''), ...parts];
46
+ for (const c of candidates) {
47
+ const t = Date.parse(normalizeForParse(c));
48
+ if (!Number.isNaN(t))
49
+ return new Date(t).toISOString();
50
+ }
51
+ return '';
52
+ }
53
+ function inferInlineLevel(text) {
54
+ const m = /\b(TRACE|DEBUG|INFO|WARN(?:ING)?|ERROR|FATAL)\b/i.exec(text);
55
+ if (!m)
56
+ return 'info';
57
+ return LEVEL_ALIASES[m[1].toLowerCase()] ?? 'info';
58
+ }
59
+ /**
60
+ * Best-effort parse of one on-disk log line. Pulls leading bracketed tokens
61
+ * (`[ts][target][LEVEL]` in any order) when present; otherwise treats the whole
62
+ * line as the message and infers the level from inline text. Never throws — an
63
+ * unrecognized line still yields a usable entry (ts='', level='info').
64
+ */
65
+ export function parseLogLine(line) {
66
+ const m = /^\s*((?:\[[^\]]*\]\s*)+)(.*)$/.exec(line);
67
+ if (!m) {
68
+ return { ts: '', level: inferInlineLevel(line), subsystem: '', message: line.trim() };
69
+ }
70
+ const tokens = [...m[1].matchAll(/\[([^\]]*)\]/g)]
71
+ .map((x) => x[1].trim())
72
+ .filter(Boolean);
73
+ const message = (m[2] ?? '').trim();
74
+ let level = 'info';
75
+ let levelFound = false;
76
+ let subsystem = '';
77
+ const tsParts = [];
78
+ for (const tok of tokens) {
79
+ const lower = tok.toLowerCase();
80
+ if (!levelFound && lower in LEVEL_ALIASES) {
81
+ level = LEVEL_ALIASES[lower];
82
+ levelFound = true;
83
+ continue;
84
+ }
85
+ if (looksLikeTimestampPart(tok)) {
86
+ tsParts.push(tok);
87
+ continue;
88
+ }
89
+ if (!subsystem)
90
+ subsystem = tok;
91
+ }
92
+ if (!levelFound)
93
+ level = inferInlineLevel(message);
94
+ return {
95
+ ts: combineTimestamp(tsParts),
96
+ level,
97
+ subsystem,
98
+ message: message || line.trim(),
99
+ };
100
+ }
101
+ /** Normalize a bridge /logs ring-buffer entry into a MergedLogEntry. */
102
+ export function normalizeRustLog(e) {
103
+ return {
104
+ ts: new Date(e.timestamp).toISOString(),
105
+ level: e.level === 'trace' ? 'debug' : e.level,
106
+ source: e.source,
107
+ subsystem: e.target,
108
+ message: e.message,
109
+ origin: 'bridge',
110
+ raw: e,
111
+ };
112
+ }
113
+ const ID_RE = /\b([a-zA-Z][\w]*?(?:[_-]?id))\s*[=:]\s*["']?([\w.\-/]+)/gi;
114
+ /**
115
+ * Best-effort correlation-id inference from a message (e.g. `run_id=abc`,
116
+ * `requestId: xyz`, `block-id=42`). Keys shorter than 4 chars (like `pid`) are
117
+ * ignored to cut noise. Returns undefined when nothing is found.
118
+ */
119
+ export function inferCorrelation(message) {
120
+ const out = {};
121
+ ID_RE.lastIndex = 0;
122
+ let m;
123
+ while ((m = ID_RE.exec(message)) !== null) {
124
+ const rawKey = m[1];
125
+ if (rawKey.length < 4)
126
+ continue;
127
+ const key = rawKey.replace(/[_-]?id$/i, 'Id');
128
+ if (!(key in out))
129
+ out[key] = m[2];
130
+ }
131
+ return Object.keys(out).length ? out : undefined;
132
+ }
133
+ //# sourceMappingURL=logMerge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logMerge.js","sourceRoot":"","sources":["../../src/util/logMerge.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ /** Minimal shape needed to merge: an ISO-ish timestamp string. */
2
+ export interface HasTs {
3
+ ts: string;
4
+ }
5
+ /**
6
+ * Stable-merge multiple log arrays into one ascending-by-timestamp stream.
7
+ *
8
+ * Entries whose `ts` is unparseable sort after all parseable entries (treated
9
+ * as +∞) but keep their relative input order. Stability is guaranteed via an
10
+ * insertion-order tiebreaker, so two entries with the same millisecond — or two
11
+ * unparseable entries — keep a deterministic, source-interleaved order.
12
+ *
13
+ * A flat stable sort (rather than a k-way heap) is intentional: the inputs here
14
+ * are bounded (a ring buffer of ≤1000 plus a handful of log files), so O(n log n)
15
+ * on the merged set is simpler and just as fast, with no per-source pre-sort
16
+ * requirement.
17
+ */
18
+ export declare function mergeByTimestamp<T extends HasTs>(sources: T[][]): T[];
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Stable-merge multiple log arrays into one ascending-by-timestamp stream.
3
+ *
4
+ * Entries whose `ts` is unparseable sort after all parseable entries (treated
5
+ * as +∞) but keep their relative input order. Stability is guaranteed via an
6
+ * insertion-order tiebreaker, so two entries with the same millisecond — or two
7
+ * unparseable entries — keep a deterministic, source-interleaved order.
8
+ *
9
+ * A flat stable sort (rather than a k-way heap) is intentional: the inputs here
10
+ * are bounded (a ring buffer of ≤1000 plus a handful of log files), so O(n log n)
11
+ * on the merged set is simpler and just as fast, with no per-source pre-sort
12
+ * requirement.
13
+ */
14
+ export function mergeByTimestamp(sources) {
15
+ const tagged = [];
16
+ let order = 0;
17
+ for (const src of sources) {
18
+ for (const item of src) {
19
+ const parsed = Date.parse(item.ts);
20
+ tagged.push({
21
+ item,
22
+ key: Number.isNaN(parsed) ? Number.POSITIVE_INFINITY : parsed,
23
+ order: order++,
24
+ });
25
+ }
26
+ }
27
+ tagged.sort((a, b) => a.key - b.key || a.order - b.order);
28
+ return tagged.map((t) => t.item);
29
+ }
30
+ //# sourceMappingURL=mergeByTimestamp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mergeByTimestamp.js","sourceRoot":"","sources":["../../src/util/mergeByTimestamp.ts"],"names":[],"mappings":"AAKA;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,gBAAgB,CAAkB,OAAc;IAC9D,MAAM,MAAM,GAAmD,EAAE,CAAC;IAClE,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,KAAK,MAAM,GAAG,IAAI,OAAO,EAAE,CAAC;QAC1B,KAAK,MAAM,IAAI,IAAI,GAAG,EAAE,CAAC;YACvB,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YACnC,MAAM,CAAC,IAAI,CAAC;gBACV,IAAI;gBACJ,GAAG,EAAE,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC,CAAC,MAAM;gBAC7D,KAAK,EAAE,KAAK,EAAE;aACf,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IACD,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC;IAC1D,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;AACnC,CAAC"}
@@ -0,0 +1,28 @@
1
+ /** A single process row from a `ps` snapshot. */
2
+ export interface PsProc {
3
+ pid: number;
4
+ ppid: number;
5
+ command: string;
6
+ }
7
+ /** A node in a descendant process tree. */
8
+ export interface PsTreeNode extends PsProc {
9
+ children: PsTreeNode[];
10
+ }
11
+ /**
12
+ * Snapshot all running processes via `ps` (Unix only: macOS + Linux).
13
+ * Columns are pid, ppid, and the full command line. Returns `[]` on Windows
14
+ * (callers degrade gracefully) and never throws on parse — only the `ps`
15
+ * invocation itself can reject.
16
+ */
17
+ export declare function snapshotProcesses(timeoutMs?: number): Promise<PsProc[]>;
18
+ /**
19
+ * Build the descendant subtree rooted at `rootPid` from a flat process list.
20
+ * Returns null if `rootPid` is absent from the snapshot. Cycle-safe: a node is
21
+ * never visited twice, so pid-reuse anomalies can't cause infinite recursion.
22
+ * Children are sorted by pid for deterministic output.
23
+ */
24
+ export declare function buildDescendantTree(procs: PsProc[], rootPid: number): PsTreeNode | null;
25
+ /** Flatten a tree depth-first (root first). Cycle-safe. */
26
+ export declare function flattenTree(root: PsTreeNode): PsTreeNode[];
27
+ /** Number of descendants of `root` (the root itself excluded). */
28
+ export declare function countDescendants(root: PsTreeNode): number;
@@ -0,0 +1,76 @@
1
+ import { exec } from './exec.js';
2
+ /**
3
+ * Snapshot all running processes via `ps` (Unix only: macOS + Linux).
4
+ * Columns are pid, ppid, and the full command line. Returns `[]` on Windows
5
+ * (callers degrade gracefully) and never throws on parse — only the `ps`
6
+ * invocation itself can reject.
7
+ */
8
+ export async function snapshotProcesses(timeoutMs = 5000) {
9
+ if (process.platform === 'win32')
10
+ return [];
11
+ // `-axo pid=,ppid=,command=` works on both BSD (macOS) and procps (Linux);
12
+ // the trailing `=` on each column suppresses the header row.
13
+ const res = await exec('ps', ['-axo', 'pid=,ppid=,command='], { timeout: timeoutMs });
14
+ const out = [];
15
+ for (const line of res.stdout.toString('utf-8').split('\n')) {
16
+ const m = /^\s*(\d+)\s+(\d+)\s+(.*)$/.exec(line);
17
+ if (!m)
18
+ continue;
19
+ out.push({ pid: Number(m[1]), ppid: Number(m[2]), command: (m[3] ?? '').trim() });
20
+ }
21
+ return out;
22
+ }
23
+ /**
24
+ * Build the descendant subtree rooted at `rootPid` from a flat process list.
25
+ * Returns null if `rootPid` is absent from the snapshot. Cycle-safe: a node is
26
+ * never visited twice, so pid-reuse anomalies can't cause infinite recursion.
27
+ * Children are sorted by pid for deterministic output.
28
+ */
29
+ export function buildDescendantTree(procs, rootPid) {
30
+ const byPid = new Map();
31
+ for (const p of procs)
32
+ byPid.set(p.pid, { ...p, children: [] });
33
+ for (const node of byPid.values()) {
34
+ if (node.ppid === node.pid)
35
+ continue; // guard self-parenting
36
+ const parent = byPid.get(node.ppid);
37
+ if (parent)
38
+ parent.children.push(node);
39
+ }
40
+ const root = byPid.get(rootPid);
41
+ if (!root)
42
+ return null;
43
+ const seen = new Set();
44
+ const sortRec = (n) => {
45
+ if (seen.has(n.pid)) {
46
+ n.children = [];
47
+ return;
48
+ }
49
+ seen.add(n.pid);
50
+ n.children.sort((a, b) => a.pid - b.pid);
51
+ for (const c of n.children)
52
+ sortRec(c);
53
+ };
54
+ sortRec(root);
55
+ return root;
56
+ }
57
+ /** Flatten a tree depth-first (root first). Cycle-safe. */
58
+ export function flattenTree(root) {
59
+ const out = [];
60
+ const seen = new Set();
61
+ const walk = (n) => {
62
+ if (seen.has(n.pid))
63
+ return;
64
+ seen.add(n.pid);
65
+ out.push(n);
66
+ for (const c of n.children)
67
+ walk(c);
68
+ };
69
+ walk(root);
70
+ return out;
71
+ }
72
+ /** Number of descendants of `root` (the root itself excluded). */
73
+ export function countDescendants(root) {
74
+ return Math.max(0, flattenTree(root).length - 1);
75
+ }
76
+ //# sourceMappingURL=psTree.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"psTree.js","sourceRoot":"","sources":["../../src/util/psTree.ts"],"names":[],"mappings":"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"}
@@ -430,6 +430,57 @@ pub fn __dev_bridge_result(
430
430
  state.notify.notify_all();
431
431
  }
432
432
 
433
+ const EVAL_TIMEOUT_MESSAGE: &str = "Eval timeout: no result callback received. Re-copy examples/tauri-bridge/src/dev_bridge.rs from tauri-agent-tools 0.7.0+ and verify Tauri IPC is available.";
434
+
435
+ fn build_eval_callback_js(js: &str, request_id: &str) -> String {
436
+ format!(
437
+ r#"
438
+ (async () => {{
439
+ const __getDevBridgeInvoke = () => {{
440
+ if (window.__TAURI_INTERNALS__ && typeof window.__TAURI_INTERNALS__.invoke === "function") {{
441
+ return window.__TAURI_INTERNALS__.invoke.bind(window.__TAURI_INTERNALS__);
442
+ }}
443
+ if (window.__TAURI__ && window.__TAURI__.core && typeof window.__TAURI__.core.invoke === "function") {{
444
+ return window.__TAURI__.core.invoke.bind(window.__TAURI__.core);
445
+ }}
446
+ return null;
447
+ }};
448
+
449
+ let __devBridgeInvoke = __getDevBridgeInvoke();
450
+ try {{
451
+ if (!__devBridgeInvoke) {{
452
+ throw new Error("Tauri invoke API not found: expected window.__TAURI_INTERNALS__.invoke or window.__TAURI__.core.invoke");
453
+ }}
454
+ let __result = await eval({js});
455
+ if (typeof __result === "undefined") {{
456
+ __result = null;
457
+ }} else if (typeof __result === "object" && __result !== null) {{
458
+ __result = JSON.stringify(__result);
459
+ }} else if (typeof __result !== "string") {{
460
+ __result = String(__result);
461
+ }}
462
+ await __devBridgeInvoke("__dev_bridge_result", {{
463
+ id: {id},
464
+ value: __result
465
+ }});
466
+ }} catch(e) {{
467
+ __devBridgeInvoke = __devBridgeInvoke || __getDevBridgeInvoke();
468
+ if (!__devBridgeInvoke) {{
469
+ throw e;
470
+ }}
471
+ const __message = e && e.message ? e.message : String(e);
472
+ await __devBridgeInvoke("__dev_bridge_result", {{
473
+ id: {id},
474
+ value: "ERROR: " + __message
475
+ }});
476
+ }}
477
+ }})();
478
+ "#,
479
+ js = serde_json::to_string(js).unwrap(),
480
+ id = serde_json::to_string(request_id).unwrap(),
481
+ )
482
+ }
483
+
433
484
  /// Start the development bridge HTTP server.
434
485
  ///
435
486
  /// Returns the bound port, a shared log buffer, and a sidecar registry. Both
@@ -747,36 +798,18 @@ pub fn start_bridge(
747
798
  let window_label = eval_req.window.as_deref().unwrap_or("main");
748
799
  if let Some(window) = app_handle.get_webview_window(window_label) {
749
800
  // Build JS that evaluates the expression, then calls back into Rust
750
- // via __TAURI__.core.invoke() to deliver the result.
751
- let callback_js = format!(
752
- r#"
753
- (async () => {{
754
- try {{
755
- let __result = await eval({js});
756
- if (typeof __result === "undefined") {{
757
- __result = null;
758
- }} else if (typeof __result === "object" && __result !== null) {{
759
- __result = JSON.stringify(__result);
760
- }} else if (typeof __result !== "string") {{
761
- __result = String(__result);
762
- }}
763
- await window.__TAURI__.core.invoke("__dev_bridge_result", {{
764
- id: {id},
765
- value: __result
766
- }});
767
- }} catch(e) {{
768
- await window.__TAURI__.core.invoke("__dev_bridge_result", {{
769
- id: {id},
770
- value: "ERROR: " + e.message
771
- }});
772
- }}
773
- }})();
774
- "#,
775
- js = serde_json::to_string(&eval_req.js).unwrap(),
776
- id = serde_json::to_string(&request_id).unwrap(),
777
- );
778
-
779
- let _ = window.eval(&callback_js);
801
+ // via Tauri's invoke API to deliver the result. Prefer the
802
+ // internal global because Tauri 2 does not expose __TAURI__
803
+ // unless app.withGlobalTauri is enabled.
804
+ let callback_js = build_eval_callback_js(&eval_req.js, &request_id);
805
+
806
+ if let Err(e) = window.eval(&callback_js) {
807
+ let _ = request.respond(
808
+ Response::from_string(format!("Eval injection failed: {e}"))
809
+ .with_status_code(500),
810
+ );
811
+ continue;
812
+ }
780
813
 
781
814
  // Wait for the result with a 5-second timeout
782
815
  let mut results = pending.results.lock().unwrap();
@@ -799,7 +832,7 @@ pub fn start_bridge(
799
832
  // Timeout — clean up and respond with 504
800
833
  results.remove(&request_id);
801
834
  let _ = request.respond(
802
- Response::from_string("Eval timeout").with_status_code(504),
835
+ Response::from_string(EVAL_TIMEOUT_MESSAGE).with_status_code(504),
803
836
  );
804
837
  break;
805
838
  }
@@ -812,7 +845,7 @@ pub fn start_bridge(
812
845
  if timeout_result.timed_out() && !results.contains_key(&request_id) {
813
846
  results.remove(&request_id);
814
847
  let _ = request.respond(
815
- Response::from_string("Eval timeout").with_status_code(504),
848
+ Response::from_string(EVAL_TIMEOUT_MESSAGE).with_status_code(504),
816
849
  );
817
850
  break;
818
851
  }
@@ -834,6 +867,53 @@ pub fn start_bridge(
834
867
  Ok((port, log_buffer, sidecar_registry))
835
868
  }
836
869
 
870
+ #[cfg(test)]
871
+ mod tests {
872
+ use super::*;
873
+
874
+ #[test]
875
+ fn eval_callback_prefers_tauri_internals() {
876
+ let script = build_eval_callback_js("document.title", "request-1");
877
+ let internals = script.find("window.__TAURI_INTERNALS__.invoke").unwrap();
878
+ let global = script.find("window.__TAURI__.core.invoke").unwrap();
879
+
880
+ assert!(internals < global);
881
+ }
882
+
883
+ #[test]
884
+ fn eval_callback_keeps_global_tauri_fallback() {
885
+ let script = build_eval_callback_js("document.title", "request-1");
886
+
887
+ assert!(script.contains("window.__TAURI__.core.invoke"));
888
+ assert!(!script.contains("app.withGlobalTauri"));
889
+ }
890
+
891
+ #[test]
892
+ fn eval_callback_safely_embeds_js_and_request_id() {
893
+ let js = r#"document.querySelector("[data-name=\"x\"]").textContent"#;
894
+ let request_id = r#"request-"quoted""#;
895
+ let script = build_eval_callback_js(js, request_id);
896
+
897
+ assert!(script.contains(&serde_json::to_string(js).unwrap()));
898
+ assert!(script.contains(&serde_json::to_string(request_id).unwrap()));
899
+ }
900
+
901
+ #[test]
902
+ fn eval_callback_uses_dev_bridge_result_command() {
903
+ let script = build_eval_callback_js("1 + 1", "request-1");
904
+
905
+ assert!(script.contains("__dev_bridge_result"));
906
+ assert!(!script.contains("await window.__TAURI__.core.invoke"));
907
+ }
908
+
909
+ #[test]
910
+ fn eval_timeout_message_is_actionable() {
911
+ assert!(EVAL_TIMEOUT_MESSAGE.contains("no result callback received"));
912
+ assert!(EVAL_TIMEOUT_MESSAGE.contains("Re-copy"));
913
+ assert!(EVAL_TIMEOUT_MESSAGE.contains("dev_bridge.rs"));
914
+ }
915
+ }
916
+
837
917
  /// Read declared capabilities from tauri.conf.json via `app.config()`. Returns
838
918
  /// a flat list of capability entries. Tauri 2 lets capabilities be either bare
839
919
  /// permission identifiers (strings) or full inline definitions; we surface
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tauri-agent-tools",
3
- "version": "0.7.0",
3
+ "version": "0.8.0",
4
4
  "description": "Agent-driven inspection toolkit for Tauri desktop apps",
5
5
  "type": "module",
6
6
  "bin": {
@@ -16,6 +16,9 @@ rand = "0.8"
16
16
  uuid = { version = "1", features = ["v4"] }
17
17
  tracing = "0.1"
18
18
  tracing-subscriber = "0.3"
19
+
20
+ [target.'cfg(unix)'.dependencies]
21
+ libc = "0.2"
19
22
  ```
20
23
 
21
24
  ### 2. Copy the bridge module
@@ -84,7 +87,7 @@ tauri-agent-tools eval "document.title"
84
87
  3. `tauri-agent-tools` discovers the token file and authenticates via the token
85
88
  4. The bridge exposes four endpoints: `POST /eval` (JS evaluation), `POST /logs` (Rust log retrieval), `POST /describe` (bridge metadata), and `GET /version` (unauthenticated health check)
86
89
  5. `/eval` accepts an optional `window` field to target specific webview windows (defaults to `"main"`)
87
- 6. The injected JS evaluates the expression, then calls back into Rust via `window.__TAURI__.core.invoke("__dev_bridge_result", { id, value })` to deliver the result
90
+ 6. The injected JS evaluates the expression, then calls back into Rust via `window.__TAURI_INTERNALS__.invoke("__dev_bridge_result", { id, value })` to deliver the result, falling back to `window.__TAURI__.core.invoke()` for older/global-enabled apps
88
91
  7. The HTTP handler thread waits for the result (up to 5 seconds) and returns it as JSON
89
92
  8. `/logs` drains the ring buffer of captured `tracing` events and returns them as JSON
90
93
  9. `/describe` returns PID, window labels, and capabilities
@@ -98,6 +101,12 @@ tauri-agent-tools eval "document.title"
98
101
  - **Inspection is read-only** — inspection commands only read DOM state
99
102
  - **Interaction is debug-only** — interaction commands use eval-based DOM dispatch, sandboxed to the webview
100
103
 
104
+ ## Troubleshooting
105
+
106
+ ### `Eval timeout: no result callback received`
107
+
108
+ Re-copy `examples/tauri-bridge/src/dev_bridge.rs` from the latest package. Current bridge code uses Tauri 2's always-present `window.__TAURI_INTERNALS__.invoke()` callback path and does **not** require `app.withGlobalTauri: true`. Older copied bridge files used `window.__TAURI__.core.invoke()` and can time out in apps that keep Tauri's global API disabled.
109
+
101
110
  ## Agent-Assisted Setup
102
111
 
103
112
  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: