@mseep/anything-analyzer 3.6.50

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 (172) hide show
  1. package/.codeartsdoer/.codebaseignore +0 -0
  2. package/.codeartsdoer/AGENTS.md +12 -0
  3. package/.github/workflows/build.yml +146 -0
  4. package/README.en.md +264 -0
  5. package/README.md +276 -0
  6. package/RELEASE_NOTES.md +16 -0
  7. package/USAGE.md +490 -0
  8. package/color-preview-r3.html +414 -0
  9. package/color-preview.html +414 -0
  10. package/dev-app-update.yml +3 -0
  11. package/electron-builder.yml +36 -0
  12. package/electron.vite.config.ts +40 -0
  13. package/package.json +53 -0
  14. package/report-2026-04-13-copilot-claude-sonnet-4.6.md +955 -0
  15. package/resources/doloffer-logo.png +0 -0
  16. package/resources/entitlements.mac.plist +12 -0
  17. package/resources/icon.ico +0 -0
  18. package/resources/icon.png +0 -0
  19. package/src/main/ai/ai-analyzer.ts +517 -0
  20. package/src/main/ai/crypto-script-extractor.ts +206 -0
  21. package/src/main/ai/data-assembler.ts +205 -0
  22. package/src/main/ai/llm-router.ts +1120 -0
  23. package/src/main/ai/prompt-builder.ts +349 -0
  24. package/src/main/ai/scene-detector.ts +302 -0
  25. package/src/main/capture/capture-engine.ts +130 -0
  26. package/src/main/capture/interaction-recorder.ts +171 -0
  27. package/src/main/capture/js-injector.ts +57 -0
  28. package/src/main/capture/replay-engine.ts +256 -0
  29. package/src/main/capture/storage-collector.ts +76 -0
  30. package/src/main/cdp/cdp-manager.ts +233 -0
  31. package/src/main/db/database.ts +41 -0
  32. package/src/main/db/migrations.ts +235 -0
  33. package/src/main/db/repositories.ts +574 -0
  34. package/src/main/fingerprint/http-spoofing.ts +48 -0
  35. package/src/main/fingerprint/presets.ts +173 -0
  36. package/src/main/fingerprint/profile-generator.ts +115 -0
  37. package/src/main/fingerprint/profile-store.ts +52 -0
  38. package/src/main/index.ts +260 -0
  39. package/src/main/ipc.ts +856 -0
  40. package/src/main/logger.ts +42 -0
  41. package/src/main/mcp/mcp-config.ts +66 -0
  42. package/src/main/mcp/mcp-manager.ts +155 -0
  43. package/src/main/mcp/mcp-server.ts +1038 -0
  44. package/src/main/prompt-templates.ts +170 -0
  45. package/src/main/proxy/ca-manager.ts +204 -0
  46. package/src/main/proxy/cert-download-page.ts +171 -0
  47. package/src/main/proxy/cert-installer.ts +242 -0
  48. package/src/main/proxy/mitm-proxy-config.ts +37 -0
  49. package/src/main/proxy/mitm-proxy-server.ts +1085 -0
  50. package/src/main/proxy/system-proxy.ts +248 -0
  51. package/src/main/session/session-manager.ts +724 -0
  52. package/src/main/tab-manager.ts +582 -0
  53. package/src/main/updater.ts +111 -0
  54. package/src/main/window.ts +235 -0
  55. package/src/preload/hook-script.ts +270 -0
  56. package/src/preload/index.ts +211 -0
  57. package/src/preload/interaction-hook.ts +286 -0
  58. package/src/preload/stealth-script.ts +302 -0
  59. package/src/preload/target-preload.ts +15 -0
  60. package/src/renderer/App.tsx +656 -0
  61. package/src/renderer/components/AiLogDetail.tsx +173 -0
  62. package/src/renderer/components/AiLogList.tsx +101 -0
  63. package/src/renderer/components/AiLogView.module.css +364 -0
  64. package/src/renderer/components/AiLogView.tsx +86 -0
  65. package/src/renderer/components/AnalyzeBar.module.css +79 -0
  66. package/src/renderer/components/AnalyzeBar.tsx +104 -0
  67. package/src/renderer/components/BrowserPanel.module.css +67 -0
  68. package/src/renderer/components/BrowserPanel.tsx +90 -0
  69. package/src/renderer/components/ControlBar.module.css +47 -0
  70. package/src/renderer/components/ControlBar.tsx +205 -0
  71. package/src/renderer/components/HookLog.tsx +132 -0
  72. package/src/renderer/components/InteractionLog.tsx +183 -0
  73. package/src/renderer/components/MCPServerModal.tsx +427 -0
  74. package/src/renderer/components/PromptTemplateModal.tsx +254 -0
  75. package/src/renderer/components/ReportView.module.css +413 -0
  76. package/src/renderer/components/ReportView.tsx +429 -0
  77. package/src/renderer/components/RequestDetail.module.css +191 -0
  78. package/src/renderer/components/RequestDetail.tsx +202 -0
  79. package/src/renderer/components/RequestLog.module.css +69 -0
  80. package/src/renderer/components/RequestLog.tsx +208 -0
  81. package/src/renderer/components/SessionList.module.css +245 -0
  82. package/src/renderer/components/SessionList.tsx +247 -0
  83. package/src/renderer/components/SettingsModal.tsx +100 -0
  84. package/src/renderer/components/StatusBar.module.css +44 -0
  85. package/src/renderer/components/StatusBar.tsx +102 -0
  86. package/src/renderer/components/StorageView.module.css +41 -0
  87. package/src/renderer/components/StorageView.tsx +178 -0
  88. package/src/renderer/components/TabBar.module.css +88 -0
  89. package/src/renderer/components/TabBar.tsx +70 -0
  90. package/src/renderer/components/Titlebar.module.css +254 -0
  91. package/src/renderer/components/Titlebar.tsx +169 -0
  92. package/src/renderer/components/settings/FingerprintSection.tsx +198 -0
  93. package/src/renderer/components/settings/GeneralSection.tsx +164 -0
  94. package/src/renderer/components/settings/LLMSection.tsx +148 -0
  95. package/src/renderer/components/settings/MCPServerSection.tsx +136 -0
  96. package/src/renderer/components/settings/MitmProxySection.tsx +320 -0
  97. package/src/renderer/components/settings/ProxySection.tsx +110 -0
  98. package/src/renderer/css-modules.d.ts +4 -0
  99. package/src/renderer/hooks/useCapture.ts +383 -0
  100. package/src/renderer/hooks/useConfirm.tsx +91 -0
  101. package/src/renderer/hooks/useSession.ts +136 -0
  102. package/src/renderer/hooks/useTabs.ts +103 -0
  103. package/src/renderer/i18n/en.ts +167 -0
  104. package/src/renderer/i18n/index.ts +47 -0
  105. package/src/renderer/i18n/zh.ts +170 -0
  106. package/src/renderer/index.html +12 -0
  107. package/src/renderer/main.tsx +15 -0
  108. package/src/renderer/styles/global.css +144 -0
  109. package/src/renderer/styles/themes/ayu-dark.css +59 -0
  110. package/src/renderer/styles/themes/catppuccin.css +59 -0
  111. package/src/renderer/styles/themes/discord.css +59 -0
  112. package/src/renderer/styles/themes/dracula.css +59 -0
  113. package/src/renderer/styles/themes/github-dark.css +59 -0
  114. package/src/renderer/styles/themes/gruvbox.css +59 -0
  115. package/src/renderer/styles/themes/index.css +11 -0
  116. package/src/renderer/styles/themes/light.css +59 -0
  117. package/src/renderer/styles/themes/nord.css +59 -0
  118. package/src/renderer/styles/themes/one-dark.css +59 -0
  119. package/src/renderer/styles/themes/tokyo-night.css +59 -0
  120. package/src/renderer/styles/tokens.css +137 -0
  121. package/src/renderer/theme.ts +31 -0
  122. package/src/renderer/ui/Badge.module.css +38 -0
  123. package/src/renderer/ui/Badge.tsx +36 -0
  124. package/src/renderer/ui/Button.module.css +142 -0
  125. package/src/renderer/ui/Button.tsx +46 -0
  126. package/src/renderer/ui/Collapse.module.css +49 -0
  127. package/src/renderer/ui/Collapse.tsx +57 -0
  128. package/src/renderer/ui/CopyableBlock.module.css +56 -0
  129. package/src/renderer/ui/CopyableBlock.tsx +42 -0
  130. package/src/renderer/ui/Empty.module.css +19 -0
  131. package/src/renderer/ui/Empty.tsx +34 -0
  132. package/src/renderer/ui/Icons.tsx +346 -0
  133. package/src/renderer/ui/Input.module.css +103 -0
  134. package/src/renderer/ui/Input.tsx +94 -0
  135. package/src/renderer/ui/InputNumber.module.css +68 -0
  136. package/src/renderer/ui/InputNumber.tsx +104 -0
  137. package/src/renderer/ui/Modal.module.css +83 -0
  138. package/src/renderer/ui/Modal.tsx +67 -0
  139. package/src/renderer/ui/Popconfirm.module.css +73 -0
  140. package/src/renderer/ui/Popconfirm.tsx +74 -0
  141. package/src/renderer/ui/Progress.module.css +35 -0
  142. package/src/renderer/ui/Progress.tsx +30 -0
  143. package/src/renderer/ui/Select.module.css +91 -0
  144. package/src/renderer/ui/Select.tsx +100 -0
  145. package/src/renderer/ui/Spinner.module.css +44 -0
  146. package/src/renderer/ui/Spinner.tsx +27 -0
  147. package/src/renderer/ui/Switch.module.css +39 -0
  148. package/src/renderer/ui/Switch.tsx +43 -0
  149. package/src/renderer/ui/Tabs.module.css +76 -0
  150. package/src/renderer/ui/Tabs.tsx +53 -0
  151. package/src/renderer/ui/Tag.module.css +66 -0
  152. package/src/renderer/ui/Tag.tsx +47 -0
  153. package/src/renderer/ui/Timeline.module.css +42 -0
  154. package/src/renderer/ui/Timeline.tsx +29 -0
  155. package/src/renderer/ui/Toast.module.css +99 -0
  156. package/src/renderer/ui/Toast.tsx +90 -0
  157. package/src/renderer/ui/Tooltip.module.css +26 -0
  158. package/src/renderer/ui/Tooltip.tsx +23 -0
  159. package/src/renderer/ui/VirtualTable.module.css +230 -0
  160. package/src/renderer/ui/VirtualTable.tsx +416 -0
  161. package/src/renderer/ui/index.ts +55 -0
  162. package/src/shared/types.ts +695 -0
  163. package/tests/main/ai/crypto-script-extractor.test.ts +281 -0
  164. package/tests/main/ai/llm-router.test.ts +1537 -0
  165. package/tests/main/ai/prompt-builder.test.ts +178 -0
  166. package/tests/main/ai/scene-detector.test.ts +212 -0
  167. package/tests/main/db/migrations.test.ts +134 -0
  168. package/tests/main/release-workflow.test.ts +59 -0
  169. package/tsconfig.json +7 -0
  170. package/tsconfig.node.json +23 -0
  171. package/tsconfig.web.json +24 -0
  172. package/vitest.config.ts +13 -0
@@ -0,0 +1,302 @@
1
+ /**
2
+ * Stealth Script — Injected into page context BEFORE hook-script.
3
+ * Overrides browser APIs to match the fingerprint profile.
4
+ *
5
+ * This file is designed to be stringified and injected via executeJavaScript().
6
+ * The __FINGERPRINT_PROFILE__ placeholder is replaced at injection time.
7
+ */
8
+
9
+ export function buildStealthScript(profileJson: string): string {
10
+ // Defensive escaping: ensure JSON can be safely embedded in a JS literal
11
+ const safeJson = profileJson
12
+ .replace(/\u2028/g, '\\u2028')
13
+ .replace(/\u2029/g, '\\u2029')
14
+ .replace(/<\/script/gi, '<\\/script');
15
+ return `(function() {
16
+ 'use strict';
17
+ if (window.__stealth_applied__) return;
18
+ window.__stealth_applied__ = true;
19
+
20
+ const profile = ${safeJson};
21
+
22
+ // === Utility: make overridden functions look native ===
23
+ function makeNative(fn, name) {
24
+ const nativeToString = function() { return 'function ' + name + '() { [native code] }'; };
25
+ Object.defineProperty(nativeToString, 'name', { value: 'toString' });
26
+ fn.toString = nativeToString;
27
+ return fn;
28
+ }
29
+
30
+ function overrideGetter(obj, prop, value) {
31
+ try {
32
+ const desc = Object.getOwnPropertyDescriptor(obj, prop) ||
33
+ Object.getOwnPropertyDescriptor(Object.getPrototypeOf(obj), prop);
34
+ if (desc && desc.get) {
35
+ const newGet = makeNative(function() { return value; }, 'get ' + prop);
36
+ Object.defineProperty(obj, prop, { get: newGet, configurable: true });
37
+ } else {
38
+ Object.defineProperty(obj, prop, { value: value, writable: false, configurable: true });
39
+ }
40
+ } catch(e) {}
41
+ }
42
+
43
+ // Seeded PRNG (mulberry32) — top-level so all noise sections can use it
44
+ function mulberry32(seed) {
45
+ return function() {
46
+ seed |= 0; seed = seed + 0x6D2B79F5 | 0;
47
+ var t = Math.imul(seed ^ seed >>> 15, 1 | seed);
48
+ t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
49
+ return ((t ^ t >>> 14) >>> 0) / 4294967296;
50
+ };
51
+ }
52
+
53
+ // ========== Layer 2: Navigator / Screen Overrides ==========
54
+
55
+ // navigator.webdriver
56
+ try {
57
+ Object.defineProperty(navigator, 'webdriver', {
58
+ get: makeNative(function() { return false; }, 'get webdriver'),
59
+ configurable: true,
60
+ });
61
+ } catch(e) {}
62
+
63
+ // navigator.platform
64
+ overrideGetter(navigator, 'platform', profile.platform);
65
+
66
+ // navigator.languages
67
+ overrideGetter(navigator, 'languages', Object.freeze([...profile.languages]));
68
+ overrideGetter(navigator, 'language', profile.languages[0]);
69
+
70
+ // navigator.hardwareConcurrency
71
+ overrideGetter(navigator, 'hardwareConcurrency', profile.hardwareConcurrency);
72
+
73
+ // navigator.deviceMemory
74
+ overrideGetter(navigator, 'deviceMemory', profile.deviceMemory);
75
+
76
+ // navigator.plugins — fake PluginArray
77
+ try {
78
+ const fakePlugins = [
79
+ { name: 'Chrome PDF Plugin', filename: 'internal-pdf-viewer', description: 'Portable Document Format' },
80
+ { name: 'Chrome PDF Viewer', filename: 'mhjfbmdgcfjbbpaeojofohoefgiehjai', description: '' },
81
+ { name: 'Native Client', filename: 'internal-nacl-plugin', description: '' },
82
+ ];
83
+ const pluginArr = Object.create(PluginArray.prototype);
84
+ fakePlugins.forEach((p, i) => {
85
+ const plugin = Object.create(Plugin.prototype);
86
+ Object.defineProperty(plugin, 'name', { value: p.name });
87
+ Object.defineProperty(plugin, 'filename', { value: p.filename });
88
+ Object.defineProperty(plugin, 'description', { value: p.description });
89
+ Object.defineProperty(plugin, 'length', { value: 0 });
90
+ pluginArr[i] = plugin;
91
+ });
92
+ Object.defineProperty(pluginArr, 'length', { value: fakePlugins.length });
93
+ pluginArr.item = makeNative(function(i) { return pluginArr[i] || null; }, 'item');
94
+ pluginArr.namedItem = makeNative(function(name) {
95
+ for (let i = 0; i < pluginArr.length; i++) { if (pluginArr[i].name === name) return pluginArr[i]; }
96
+ return null;
97
+ }, 'namedItem');
98
+ pluginArr.refresh = makeNative(function() {}, 'refresh');
99
+ overrideGetter(navigator, 'plugins', pluginArr);
100
+ } catch(e) {}
101
+
102
+ // screen properties
103
+ overrideGetter(screen, 'width', profile.screenWidth);
104
+ overrideGetter(screen, 'height', profile.screenHeight);
105
+ overrideGetter(screen, 'availWidth', profile.screenWidth);
106
+ overrideGetter(screen, 'availHeight', profile.screenHeight - 40); // taskbar offset
107
+ overrideGetter(screen, 'colorDepth', profile.colorDepth);
108
+ overrideGetter(screen, 'pixelDepth', profile.colorDepth);
109
+
110
+ // window.devicePixelRatio
111
+ overrideGetter(window, 'devicePixelRatio', profile.devicePixelRatio);
112
+
113
+ // window.chrome — polyfill for non-headless detection
114
+ try {
115
+ if (!window.chrome) {
116
+ window.chrome = {};
117
+ }
118
+ if (!window.chrome.runtime) {
119
+ window.chrome.runtime = {
120
+ connect: makeNative(function() {}, 'connect'),
121
+ sendMessage: makeNative(function() {}, 'sendMessage'),
122
+ };
123
+ }
124
+ } catch(e) {}
125
+
126
+ // Timezone spoofing
127
+ try {
128
+ const origDTF = Intl.DateTimeFormat;
129
+ const newDTF = makeNative(function(...args) {
130
+ const instance = new origDTF(...args);
131
+ const origResolved = instance.resolvedOptions.bind(instance);
132
+ instance.resolvedOptions = makeNative(function() {
133
+ const opts = origResolved();
134
+ opts.timeZone = profile.timezone;
135
+ return opts;
136
+ }, 'resolvedOptions');
137
+ return instance;
138
+ }, 'DateTimeFormat');
139
+ newDTF.prototype = origDTF.prototype;
140
+ newDTF.supportedLocalesOf = origDTF.supportedLocalesOf;
141
+ Intl.DateTimeFormat = newDTF;
142
+ } catch(e) {}
143
+
144
+ try {
145
+ Date.prototype.getTimezoneOffset = makeNative(function() {
146
+ return profile.timezoneOffset;
147
+ }, 'getTimezoneOffset');
148
+ } catch(e) {}
149
+
150
+ // Permissions.query
151
+ try {
152
+ const origQuery = Permissions.prototype.query;
153
+ Permissions.prototype.query = makeNative(function(desc) {
154
+ if (desc && desc.name === 'notifications') {
155
+ return Promise.resolve({ state: 'prompt', onchange: null });
156
+ }
157
+ return origQuery.call(this, desc);
158
+ }, 'query');
159
+ } catch(e) {}
160
+
161
+ // ========== Layer 3: Canvas / WebGL / Audio / WebRTC Noise ==========
162
+
163
+ // --- Canvas fingerprint noise ---
164
+ try {
165
+ const canvasRng = mulberry32(profile.canvasNoise);
166
+
167
+ const origToDataURL = HTMLCanvasElement.prototype.toDataURL;
168
+ HTMLCanvasElement.prototype.toDataURL = makeNative(function(...args) {
169
+ try {
170
+ const ctx = this.getContext('2d');
171
+ if (ctx) {
172
+ const imageData = ctx.getImageData(0, 0, this.width, this.height);
173
+ const data = imageData.data;
174
+ // Apply subtle pixel noise
175
+ for (let i = 0; i < data.length; i += 4) {
176
+ data[i] = data[i] + Math.floor((canvasRng() - 0.5) * 2); // R
177
+ data[i+1] = data[i+1] + Math.floor((canvasRng() - 0.5) * 2); // G
178
+ }
179
+ ctx.putImageData(imageData, 0, 0);
180
+ }
181
+ } catch(e) {}
182
+ return origToDataURL.apply(this, args);
183
+ }, 'toDataURL');
184
+
185
+ const origToBlob = HTMLCanvasElement.prototype.toBlob;
186
+ HTMLCanvasElement.prototype.toBlob = makeNative(function(...args) {
187
+ try {
188
+ const ctx = this.getContext('2d');
189
+ if (ctx) {
190
+ const imageData = ctx.getImageData(0, 0, this.width, this.height);
191
+ const data = imageData.data;
192
+ for (let i = 0; i < data.length; i += 4) {
193
+ data[i] = data[i] + Math.floor((canvasRng() - 0.5) * 2);
194
+ }
195
+ ctx.putImageData(imageData, 0, 0);
196
+ }
197
+ } catch(e) {}
198
+ return origToBlob.apply(this, args);
199
+ }, 'toBlob');
200
+ } catch(e) {}
201
+
202
+ // --- WebGL fingerprint ---
203
+ try {
204
+ const origGetParam = WebGLRenderingContext.prototype.getParameter;
205
+ WebGLRenderingContext.prototype.getParameter = makeNative(function(pname) {
206
+ const UNMASKED_VENDOR = 0x9245;
207
+ const UNMASKED_RENDERER = 0x9246;
208
+ if (pname === UNMASKED_VENDOR) return profile.webglVendor;
209
+ if (pname === UNMASKED_RENDERER) return profile.webglRenderer;
210
+ return origGetParam.call(this, pname);
211
+ }, 'getParameter');
212
+
213
+ if (typeof WebGL2RenderingContext !== 'undefined') {
214
+ const origGetParam2 = WebGL2RenderingContext.prototype.getParameter;
215
+ WebGL2RenderingContext.prototype.getParameter = makeNative(function(pname) {
216
+ const UNMASKED_VENDOR = 0x9245;
217
+ const UNMASKED_RENDERER = 0x9246;
218
+ if (pname === UNMASKED_VENDOR) return profile.webglVendor;
219
+ if (pname === UNMASKED_RENDERER) return profile.webglRenderer;
220
+ return origGetParam2.call(this, pname);
221
+ }, 'getParameter');
222
+ }
223
+ } catch(e) {}
224
+
225
+ // --- AudioContext fingerprint noise ---
226
+ try {
227
+ const audioRng = mulberry32(profile.audioNoise);
228
+ const origCreateOscillator = AudioContext.prototype.createOscillator;
229
+ AudioContext.prototype.createOscillator = makeNative(function() {
230
+ const osc = origCreateOscillator.call(this);
231
+ const origConnect = osc.connect.bind(osc);
232
+ osc.connect = makeNative(function(dest, ...args) {
233
+ if (dest instanceof AnalyserNode) {
234
+ // Inject a gain node with tiny noise
235
+ const gainNode = osc.context.createGain();
236
+ gainNode.gain.value = 1 + (audioRng() - 0.5) * 0.0001;
237
+ origConnect(gainNode);
238
+ gainNode.connect(dest);
239
+ return dest;
240
+ }
241
+ return origConnect(dest, ...args);
242
+ }, 'connect');
243
+ return osc;
244
+ }, 'createOscillator');
245
+ } catch(e) {}
246
+
247
+ // --- WebRTC protection ---
248
+ try {
249
+ if (profile.webrtcPolicy === 'block') {
250
+ window.RTCPeerConnection = makeNative(function() {
251
+ throw new DOMException('WebRTC is disabled', 'NotAllowedError');
252
+ }, 'RTCPeerConnection');
253
+ window.webkitRTCPeerConnection = window.RTCPeerConnection;
254
+ if (window.RTCSessionDescription) {
255
+ window.RTCSessionDescription = makeNative(function() {
256
+ throw new DOMException('WebRTC is disabled', 'NotAllowedError');
257
+ }, 'RTCSessionDescription');
258
+ }
259
+ }
260
+ } catch(e) {}
261
+
262
+ // ========== iframe sync ==========
263
+ try {
264
+ const observer = new MutationObserver(function(mutations) {
265
+ mutations.forEach(function(mutation) {
266
+ mutation.addedNodes.forEach(function(node) {
267
+ if (node.tagName === 'IFRAME') {
268
+ injectIntoIframe(node);
269
+ }
270
+ });
271
+ });
272
+ });
273
+ observer.observe(document.documentElement, { childList: true, subtree: true });
274
+
275
+ function injectIntoIframe(iframe) {
276
+ const tryInject = function(attempt) {
277
+ try {
278
+ const win = iframe.contentWindow;
279
+ if (!win || win.__stealth_applied__) return;
280
+ // Re-apply critical overrides in iframe context
281
+ Object.defineProperty(win.navigator, 'webdriver', {
282
+ get: function() { return false; },
283
+ configurable: true,
284
+ });
285
+ win.__stealth_applied__ = true;
286
+ } catch(e) {
287
+ // Cross-origin iframe — cannot access, skip silently
288
+ if (attempt < 3) {
289
+ requestAnimationFrame(function() { tryInject(attempt + 1); });
290
+ }
291
+ }
292
+ };
293
+ iframe.addEventListener('load', function() { tryInject(0); });
294
+ tryInject(0);
295
+ }
296
+
297
+ // Process existing iframes
298
+ document.querySelectorAll('iframe').forEach(injectIntoIframe);
299
+ } catch(e) {}
300
+
301
+ })();`;
302
+ }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Minimal preload for target browser tabs.
3
+ * Forwards hook/interaction messages from page context to main process via IPC.
4
+ * Does NOT expose any electronAPI to the page — keeps the target tab sandboxed.
5
+ */
6
+ import { ipcRenderer } from "electron";
7
+
8
+ window.addEventListener("message", (event) => {
9
+ if (event.data?.type === "ar-hook") {
10
+ ipcRenderer.send("capture:hook-data", event.data);
11
+ }
12
+ if (event.data?.type === "ar-interaction") {
13
+ ipcRenderer.send("capture:hook-data", event.data);
14
+ }
15
+ });