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.
- package/.agents/skills/tauri-agent-tools/SKILL.md +50 -13
- package/.agents/skills/tauri-bridge-setup/SKILL.md +5 -3
- package/.agents/skills/tauri-debug-quickstart/SKILL.md +9 -6
- package/AGENTS.md +13 -3
- package/README.md +26 -5
- package/dist/bridge/client.d.ts +8 -0
- package/dist/bridge/client.js +20 -0
- package/dist/bridge/client.js.map +1 -1
- package/dist/bridge/tokenDiscovery.js +29 -29
- package/dist/bridge/tokenDiscovery.js.map +1 -1
- package/dist/cli.js +5 -1
- package/dist/cli.js.map +1 -1
- package/dist/commands/bundle.d.ts +2 -0
- package/dist/commands/bundle.js +251 -0
- package/dist/commands/bundle.js.map +1 -0
- package/dist/commands/capabilitiesAudit.js +14 -1
- package/dist/commands/capabilitiesAudit.js.map +1 -1
- package/dist/commands/capture.js +1 -1
- package/dist/commands/health.js +19 -1
- package/dist/commands/health.js.map +1 -1
- package/dist/commands/invoke.js +14 -4
- package/dist/commands/invoke.js.map +1 -1
- package/dist/commands/ipcMonitor.d.ts +3 -0
- package/dist/commands/ipcMonitor.js +60 -14
- package/dist/commands/ipcMonitor.js.map +1 -1
- package/dist/commands/logs.d.ts +2 -0
- package/dist/commands/logs.js +193 -0
- package/dist/commands/logs.js.map +1 -0
- package/dist/commands/pageState.d.ts +1 -0
- package/dist/commands/pageState.js +2 -2
- package/dist/commands/pageState.js.map +1 -1
- package/dist/commands/processTree.js +116 -4
- package/dist/commands/processTree.js.map +1 -1
- package/dist/commands/shared.d.ts +31 -0
- package/dist/commands/shared.js +52 -1
- package/dist/commands/shared.js.map +1 -1
- package/dist/commands/snapshot.js +1 -1
- package/dist/commands/webviewAttach.js +17 -1
- package/dist/commands/webviewAttach.js.map +1 -1
- package/dist/platform/macos.js +35 -15
- package/dist/platform/macos.js.map +1 -1
- package/dist/util/logMerge.d.ts +42 -0
- package/dist/util/logMerge.js +133 -0
- package/dist/util/logMerge.js.map +1 -0
- package/dist/util/mergeByTimestamp.d.ts +18 -0
- package/dist/util/mergeByTimestamp.js +30 -0
- package/dist/util/mergeByTimestamp.js.map +1 -0
- package/dist/util/psTree.d.ts +28 -0
- package/dist/util/psTree.js +76 -0
- package/dist/util/psTree.js.map +1 -0
- package/examples/tauri-bridge/src/dev_bridge.rs +112 -32
- package/package.json +1 -1
- package/rust-bridge/README.md +10 -1
package/dist/platform/macos.js
CHANGED
|
@@ -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
|
-
|
|
12
|
-
|
|
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
|
-
|
|
15
|
-
|
|
16
|
-
);
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,kBAAkB,CAAC;AACzD,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,OAAO,EAAE,IAAI,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACzD,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAG5D,KAAK,UAAU,MAAM,CAAC,MAAc;IAClC,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;IAC/E,OAAO,MAAM,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,CAAC;AAClC,CAAC;AAED,
|
|
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":"AAsBA,MAAM,CAAC,MAAM,UAAU,GAA+B,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;AAE/F,MAAM,aAAa,GAA+B;IAChD,KAAK,EAAE,OAAO;IACd,KAAK,EAAE,OAAO;IACd,GAAG,EAAE,OAAO;IACZ,IAAI,EAAE,MAAM;IACZ,WAAW,EAAE,MAAM;IACnB,IAAI,EAAE,MAAM;IACZ,OAAO,EAAE,MAAM;IACf,KAAK,EAAE,OAAO;IACd,GAAG,EAAE,OAAO;IACZ,KAAK,EAAE,OAAO;IACd,IAAI,EAAE,OAAO;IACb,QAAQ,EAAE,OAAO;CAClB,CAAC;AAEF,SAAS,sBAAsB,CAAC,KAAa;IAC3C,OAAO,CACL,mBAAmB,CAAC,IAAI,CAAC,KAAK,CAAC;QAC/B,qBAAqB,CAAC,IAAI,CAAC,KAAK,CAAC;QACjC,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,CAC1B,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,SAAS,iBAAiB,CAAC,CAAS;IAClC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;IACjB,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,sCAAsC,EAAE,OAAO,CAAC,CAAC;IAC/D,MAAM,OAAO,GAAG,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,sBAAsB,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACzE,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,mBAAmB,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC7D,IAAI,OAAO,IAAI,CAAC,KAAK;QAAE,CAAC,IAAI,GAAG,CAAC;IAChC,OAAO,CAAC,CAAC;AACX,CAAC;AAED,SAAS,gBAAgB,CAAC,KAAe;IACvC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAClC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,EAAE,CAAC;QACxD,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,CAAE,CAAC;QACtB,MAAM,EAAE,GAAG,GAAG,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAC/D,MAAM,CAAC,GAAG,IAAI,IAAI,CAAC,EAAE,CAAC,CAAC;QACvB,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;IAC1D,CAAC;IACD,MAAM,UAAU,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,GAAG,KAAK,CAAC,CAAC;IAChF,KAAK,MAAM,CAAC,IAAI,UAAU,EAAE,CAAC;QAC3B,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,CAAC;QAC3C,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;YAAE,OAAO,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;IACzD,CAAC;IACD,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,SAAS,gBAAgB,CAAC,IAAY;IACpC,MAAM,CAAC,GAAG,kDAAkD,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACxE,IAAI,CAAC,CAAC;QAAE,OAAO,MAAM,CAAC;IACtB,OAAO,aAAa,CAAC,CAAC,CAAC,CAAC,CAAE,CAAC,WAAW,EAAE,CAAC,IAAI,MAAM,CAAC;AACtD,CAAC;AASD;;;;;GAKG;AACH,MAAM,UAAU,YAAY,CAAC,IAAY;IACvC,MAAM,CAAC,GAAG,+BAA+B,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACrD,IAAI,CAAC,CAAC,EAAE,CAAC;QACP,OAAO,EAAE,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,gBAAgB,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,EAAE,EAAE,OAAO,EAAE,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC;IACxF,CAAC;IACD,MAAM,MAAM,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAE,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC;SAChD,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAE,CAAC,IAAI,EAAE,CAAC;SACxB,MAAM,CAAC,OAAO,CAAC,CAAC;IACnB,MAAM,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IAEpC,IAAI,KAAK,GAAe,MAAM,CAAC;IAC/B,IAAI,UAAU,GAAG,KAAK,CAAC;IACvB,IAAI,SAAS,GAAG,EAAE,CAAC;IACnB,MAAM,OAAO,GAAa,EAAE,CAAC;IAE7B,KAAK,MAAM,GAAG,IAAI,MAAM,EAAE,CAAC;QACzB,MAAM,KAAK,GAAG,GAAG,CAAC,WAAW,EAAE,CAAC;QAChC,IAAI,CAAC,UAAU,IAAI,KAAK,IAAI,aAAa,EAAE,CAAC;YAC1C,KAAK,GAAG,aAAa,CAAC,KAAK,CAAE,CAAC;YAC9B,UAAU,GAAG,IAAI,CAAC;YAClB,SAAS;QACX,CAAC;QACD,IAAI,sBAAsB,CAAC,GAAG,CAAC,EAAE,CAAC;YAChC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAClB,SAAS;QACX,CAAC;QACD,IAAI,CAAC,SAAS;YAAE,SAAS,GAAG,GAAG,CAAC;IAClC,CAAC;IACD,IAAI,CAAC,UAAU;QAAE,KAAK,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAC;IAEnD,OAAO;QACL,EAAE,EAAE,gBAAgB,CAAC,OAAO,CAAC;QAC7B,KAAK;QACL,SAAS;QACT,OAAO,EAAE,OAAO,IAAI,IAAI,CAAC,IAAI,EAAE;KAChC,CAAC;AACJ,CAAC;AAED,wEAAwE;AACxE,MAAM,UAAU,gBAAgB,CAAC,CAAe;IAC9C,OAAO;QACL,EAAE,EAAE,IAAI,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,WAAW,EAAE;QACvC,KAAK,EAAE,CAAC,CAAC,KAAK,KAAK,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK;QAC9C,MAAM,EAAE,CAAC,CAAC,MAAM;QAChB,SAAS,EAAE,CAAC,CAAC,MAAM;QACnB,OAAO,EAAE,CAAC,CAAC,OAAO;QAClB,MAAM,EAAE,QAAQ;QAChB,GAAG,EAAE,CAAC;KACP,CAAC;AACJ,CAAC;AAED,MAAM,KAAK,GAAG,2DAA2D,CAAC;AAE1E;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAAC,OAAe;IAC9C,MAAM,GAAG,GAA2B,EAAE,CAAC;IACvC,KAAK,CAAC,SAAS,GAAG,CAAC,CAAC;IACpB,IAAI,CAAyB,CAAC;IAC9B,OAAO,CAAC,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;QAC1C,MAAM,MAAM,GAAG,CAAC,CAAC,CAAC,CAAE,CAAC;QACrB,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC;YAAE,SAAS;QAChC,MAAM,GAAG,GAAG,MAAM,CAAC,OAAO,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;QAC9C,IAAI,CAAC,CAAC,GAAG,IAAI,GAAG,CAAC;YAAE,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAE,CAAC;IACtC,CAAC;IACD,OAAO,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC;AACnD,CAAC"}
|
|
@@ -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
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
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(
|
|
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(
|
|
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
package/rust-bridge/README.md
CHANGED
|
@@ -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.
|
|
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:
|