@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,173 @@
1
+ /**
2
+ * Preset device configurations for fingerprint generation.
3
+ * Each preset is a logically consistent combination of platform, screen, GPU, etc.
4
+ */
5
+
6
+ export interface DevicePreset {
7
+ platform: string;
8
+ oscpu: string;
9
+ screens: [number, number][];
10
+ dprs: number[];
11
+ webglVendors: string[];
12
+ webglRenderers: string[];
13
+ hardwareConcurrencies: number[];
14
+ deviceMemories: number[];
15
+ colorDepth: number;
16
+ }
17
+
18
+ /** Chrome version whitelist — 5 most recent stable versions */
19
+ export const CHROME_VERSIONS = [
20
+ '131.0.0.0',
21
+ '130.0.0.0',
22
+ '129.0.0.0',
23
+ '128.0.0.0',
24
+ '127.0.0.0',
25
+ ] as const;
26
+
27
+ export const WINDOWS_PRESETS: DevicePreset[] = [
28
+ {
29
+ platform: 'Win32',
30
+ oscpu: 'Windows NT 10.0; Win64; x64',
31
+ screens: [[1920, 1080], [2560, 1440], [1366, 768], [1536, 864]],
32
+ dprs: [1, 1.25, 1.5],
33
+ webglVendors: ['Google Inc. (NVIDIA)'],
34
+ webglRenderers: [
35
+ 'ANGLE (NVIDIA, NVIDIA GeForce RTX 3060 Direct3D11 vs_5_0 ps_5_0, D3D11)',
36
+ 'ANGLE (NVIDIA, NVIDIA GeForce GTX 1660 SUPER Direct3D11 vs_5_0 ps_5_0, D3D11)',
37
+ 'ANGLE (NVIDIA, NVIDIA GeForce RTX 4060 Direct3D11 vs_5_0 ps_5_0, D3D11)',
38
+ ],
39
+ hardwareConcurrencies: [4, 8, 12, 16],
40
+ deviceMemories: [8, 16, 32],
41
+ colorDepth: 24,
42
+ },
43
+ {
44
+ platform: 'Win32',
45
+ oscpu: 'Windows NT 10.0; Win64; x64',
46
+ screens: [[1920, 1080], [2560, 1440]],
47
+ dprs: [1, 1.25],
48
+ webglVendors: ['Google Inc. (Intel)'],
49
+ webglRenderers: [
50
+ 'ANGLE (Intel, Intel(R) UHD Graphics 630 Direct3D11 vs_5_0 ps_5_0, D3D11)',
51
+ 'ANGLE (Intel, Intel(R) UHD Graphics 770 Direct3D11 vs_5_0 ps_5_0, D3D11)',
52
+ ],
53
+ hardwareConcurrencies: [4, 8, 12],
54
+ deviceMemories: [8, 16],
55
+ colorDepth: 24,
56
+ },
57
+ {
58
+ platform: 'Win32',
59
+ oscpu: 'Windows NT 10.0; Win64; x64',
60
+ screens: [[1920, 1080], [2560, 1440], [3440, 1440]],
61
+ dprs: [1, 1.25, 1.5],
62
+ webglVendors: ['Google Inc. (AMD)'],
63
+ webglRenderers: [
64
+ 'ANGLE (AMD, AMD Radeon RX 6700 XT Direct3D11 vs_5_0 ps_5_0, D3D11)',
65
+ 'ANGLE (AMD, AMD Radeon RX 580 Direct3D11 vs_5_0 ps_5_0, D3D11)',
66
+ ],
67
+ hardwareConcurrencies: [8, 12, 16],
68
+ deviceMemories: [16, 32],
69
+ colorDepth: 24,
70
+ },
71
+ ];
72
+
73
+ export const MACOS_PRESETS: DevicePreset[] = [
74
+ {
75
+ platform: 'MacIntel',
76
+ oscpu: 'Intel Mac OS X 10_15_7',
77
+ screens: [[1440, 900], [1680, 1050], [2560, 1600]],
78
+ dprs: [2],
79
+ webglVendors: ['Google Inc. (Apple)'],
80
+ webglRenderers: [
81
+ 'ANGLE (Apple, Apple M1, OpenGL 4.1)',
82
+ 'ANGLE (Apple, Apple M2, OpenGL 4.1)',
83
+ 'ANGLE (Apple, Apple M3, OpenGL 4.1)',
84
+ ],
85
+ hardwareConcurrencies: [8, 10, 12],
86
+ deviceMemories: [8, 16],
87
+ colorDepth: 30,
88
+ },
89
+ {
90
+ platform: 'MacIntel',
91
+ oscpu: 'Intel Mac OS X 10_15_7',
92
+ screens: [[1440, 900], [1680, 1050]],
93
+ dprs: [2],
94
+ webglVendors: ['Google Inc. (Intel)'],
95
+ webglRenderers: [
96
+ 'ANGLE (Intel, Intel(R) Iris Plus Graphics 645, OpenGL 4.1)',
97
+ 'ANGLE (Intel, Intel(R) Iris Plus Graphics, OpenGL 4.1)',
98
+ ],
99
+ hardwareConcurrencies: [4, 8],
100
+ deviceMemories: [8, 16],
101
+ colorDepth: 24,
102
+ },
103
+ ];
104
+
105
+ export const LINUX_PRESETS: DevicePreset[] = [
106
+ {
107
+ platform: 'Linux x86_64',
108
+ oscpu: 'Linux x86_64',
109
+ screens: [[1920, 1080], [2560, 1440]],
110
+ dprs: [1],
111
+ webglVendors: ['Google Inc. (NVIDIA)'],
112
+ webglRenderers: [
113
+ 'ANGLE (NVIDIA, NVIDIA GeForce GTX 1080 Ti, OpenGL 4.5)',
114
+ 'ANGLE (NVIDIA, NVIDIA GeForce RTX 3070, OpenGL 4.5)',
115
+ ],
116
+ hardwareConcurrencies: [8, 12, 16],
117
+ deviceMemories: [16, 32],
118
+ colorDepth: 24,
119
+ },
120
+ {
121
+ platform: 'Linux x86_64',
122
+ oscpu: 'Linux x86_64',
123
+ screens: [[1920, 1080]],
124
+ dprs: [1],
125
+ webglVendors: ['Google Inc. (Intel)'],
126
+ webglRenderers: [
127
+ 'ANGLE (Intel, Mesa Intel(R) UHD Graphics 630 (CFL GT2), OpenGL 4.5)',
128
+ ],
129
+ hardwareConcurrencies: [4, 8],
130
+ deviceMemories: [8, 16],
131
+ colorDepth: 24,
132
+ },
133
+ ];
134
+
135
+ export const ALL_PRESETS: DevicePreset[] = [
136
+ ...WINDOWS_PRESETS,
137
+ ...MACOS_PRESETS,
138
+ ...LINUX_PRESETS,
139
+ ];
140
+
141
+ /** Timezone → language mapping for consistency validation */
142
+ export const TIMEZONE_LANGUAGES: Record<string, string[]> = {
143
+ 'Asia/Shanghai': ['zh-CN', 'zh', 'en'],
144
+ 'Asia/Tokyo': ['ja', 'en'],
145
+ 'Asia/Seoul': ['ko', 'en'],
146
+ 'America/New_York': ['en-US', 'en'],
147
+ 'America/Los_Angeles': ['en-US', 'en'],
148
+ 'America/Chicago': ['en-US', 'en'],
149
+ 'Europe/London': ['en-GB', 'en'],
150
+ 'Europe/Berlin': ['de-DE', 'de', 'en'],
151
+ 'Europe/Paris': ['fr-FR', 'fr', 'en'],
152
+ 'Europe/Moscow': ['ru-RU', 'ru', 'en'],
153
+ 'Asia/Singapore': ['en-SG', 'zh', 'en'],
154
+ 'Asia/Hong_Kong': ['zh-HK', 'zh', 'en'],
155
+ 'Asia/Taipei': ['zh-TW', 'zh', 'en'],
156
+ };
157
+
158
+ /** Timezone → UTC offset in minutes (negative = east of UTC) */
159
+ export const TIMEZONE_OFFSETS: Record<string, number> = {
160
+ 'Asia/Shanghai': -480,
161
+ 'Asia/Tokyo': -540,
162
+ 'Asia/Seoul': -540,
163
+ 'America/New_York': 300,
164
+ 'America/Los_Angeles': 480,
165
+ 'America/Chicago': 360,
166
+ 'Europe/London': 0,
167
+ 'Europe/Berlin': -60,
168
+ 'Europe/Paris': -60,
169
+ 'Europe/Moscow': -180,
170
+ 'Asia/Singapore': -480,
171
+ 'Asia/Hong_Kong': -480,
172
+ 'Asia/Taipei': -480,
173
+ };
@@ -0,0 +1,115 @@
1
+ import type { FingerprintProfile } from '@shared/types';
2
+ import {
3
+ ALL_PRESETS,
4
+ CHROME_VERSIONS,
5
+ TIMEZONE_LANGUAGES,
6
+ TIMEZONE_OFFSETS,
7
+ type DevicePreset,
8
+ } from './presets';
9
+
10
+ /** Pick a random element from an array */
11
+ function pick<T>(arr: readonly T[]): T {
12
+ return arr[Math.floor(Math.random() * arr.length)];
13
+ }
14
+
15
+ /** Generate a random 32-bit unsigned integer */
16
+ function randomSeed(): number {
17
+ return Math.floor(Math.random() * 0xFFFFFFFF);
18
+ }
19
+
20
+ /** Detect the system timezone */
21
+ function getSystemTimezone(): string {
22
+ try {
23
+ return Intl.DateTimeFormat().resolvedOptions().timeZone;
24
+ } catch {
25
+ return 'Asia/Shanghai';
26
+ }
27
+ }
28
+
29
+ /** Build a full Chrome user-agent string */
30
+ function buildUserAgent(preset: DevicePreset, chromeVersion: string): string {
31
+ if (preset.platform === 'MacIntel') {
32
+ return `Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/${chromeVersion} Safari/537.36`;
33
+ }
34
+ if (preset.platform === 'Linux x86_64') {
35
+ return `Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/${chromeVersion} Safari/537.36`;
36
+ }
37
+ // Windows
38
+ return `Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/${chromeVersion} Safari/537.36`;
39
+ }
40
+
41
+ /** Build the appVersion string (UA without "Mozilla/") */
42
+ function buildAppVersion(ua: string): string {
43
+ return ua.replace('Mozilla/', '');
44
+ }
45
+
46
+ /**
47
+ * Generate a complete, logically consistent FingerprintProfile.
48
+ */
49
+ export function generateProfile(sessionId: string): FingerprintProfile {
50
+ const preset = pick(ALL_PRESETS);
51
+ const chromeVersion = pick(CHROME_VERSIONS);
52
+ const screen = pick(preset.screens);
53
+ const dpr = pick(preset.dprs);
54
+ const concurrency = pick(preset.hardwareConcurrencies);
55
+ // deviceMemory should be positively correlated with concurrency
56
+ const memoryOptions = preset.deviceMemories.filter(m => m >= concurrency);
57
+ const memory = memoryOptions.length > 0 ? pick(memoryOptions) : pick(preset.deviceMemories);
58
+
59
+ const timezone = getSystemTimezone();
60
+ const languages = TIMEZONE_LANGUAGES[timezone] || ['en-US', 'en'];
61
+ const timezoneOffset = TIMEZONE_OFFSETS[timezone] ?? -new Date().getTimezoneOffset();
62
+
63
+ const ua = buildUserAgent(preset, chromeVersion);
64
+
65
+ return {
66
+ sessionId,
67
+ userAgent: ua,
68
+ platform: preset.platform,
69
+ oscpu: preset.oscpu,
70
+ appVersion: buildAppVersion(ua),
71
+ screenWidth: screen[0],
72
+ screenHeight: screen[1],
73
+ colorDepth: preset.colorDepth,
74
+ devicePixelRatio: dpr,
75
+ hardwareConcurrency: concurrency,
76
+ deviceMemory: memory,
77
+ webglVendor: pick(preset.webglVendors),
78
+ webglRenderer: pick(preset.webglRenderers),
79
+ canvasNoise: randomSeed(),
80
+ audioNoise: randomSeed(),
81
+ languages,
82
+ timezone,
83
+ timezoneOffset,
84
+ webrtcPolicy: 'block',
85
+ };
86
+ }
87
+
88
+ /**
89
+ * Validate a FingerprintProfile for internal consistency.
90
+ * Returns an array of error messages (empty = valid).
91
+ */
92
+ export function validateProfile(profile: FingerprintProfile): string[] {
93
+ const errors: string[] = [];
94
+
95
+ // Platform consistency
96
+ if (profile.platform === 'MacIntel' && profile.oscpu.includes('Windows')) {
97
+ errors.push('macOS platform has Windows oscpu');
98
+ }
99
+ if (profile.platform === 'Win32' && profile.oscpu.includes('Mac')) {
100
+ errors.push('Windows platform has macOS oscpu');
101
+ }
102
+ // UA consistency
103
+ if (profile.platform === 'Win32' && !profile.userAgent.includes('Windows')) {
104
+ errors.push('Windows platform but UA does not contain Windows');
105
+ }
106
+ if (profile.platform === 'MacIntel' && !profile.userAgent.includes('Macintosh')) {
107
+ errors.push('macOS platform but UA does not contain Macintosh');
108
+ }
109
+ // Memory vs concurrency
110
+ if (profile.deviceMemory > profile.hardwareConcurrency * 4) {
111
+ errors.push(`deviceMemory (${profile.deviceMemory}) exceeds hardwareConcurrency*4 (${profile.hardwareConcurrency * 4})`);
112
+ }
113
+
114
+ return errors;
115
+ }
@@ -0,0 +1,52 @@
1
+ import type { FingerprintProfile } from '@shared/types';
2
+ import type { FingerprintProfilesRepo } from '../db/repositories';
3
+ import { generateProfile } from './profile-generator';
4
+
5
+ /**
6
+ * ProfileStore — High-level interface for managing fingerprint profiles.
7
+ * Handles get-or-create logic and delegates persistence to the repository.
8
+ */
9
+ export class ProfileStore {
10
+ constructor(private repo: FingerprintProfilesRepo) {}
11
+
12
+ /**
13
+ * Get the profile for a session. If none exists, auto-generate one.
14
+ */
15
+ getOrCreate(sessionId: string): FingerprintProfile {
16
+ const existing = this.repo.findBySessionId(sessionId);
17
+ if (existing) return existing;
18
+ const profile = generateProfile(sessionId);
19
+ this.repo.upsert(sessionId, profile);
20
+ return profile;
21
+ }
22
+
23
+ /**
24
+ * Get the profile for a session. Returns null if none exists.
25
+ */
26
+ get(sessionId: string): FingerprintProfile | null {
27
+ return this.repo.findBySessionId(sessionId);
28
+ }
29
+
30
+ /**
31
+ * Update (or create) a profile for a session.
32
+ */
33
+ update(profile: FingerprintProfile): void {
34
+ this.repo.upsert(profile.sessionId, profile);
35
+ }
36
+
37
+ /**
38
+ * Regenerate a random profile for a session, replacing any existing one.
39
+ */
40
+ regenerate(sessionId: string): FingerprintProfile {
41
+ const profile = generateProfile(sessionId);
42
+ this.repo.upsert(sessionId, profile);
43
+ return profile;
44
+ }
45
+
46
+ /**
47
+ * Delete the profile for a session.
48
+ */
49
+ delete(sessionId: string): void {
50
+ this.repo.delete(sessionId);
51
+ }
52
+ }
@@ -0,0 +1,260 @@
1
+ import { app, BrowserWindow, crashReporter } from "electron";
2
+ import { initLogger } from "./logger";
3
+ import { getDatabase, closeDatabase } from "./db/database";
4
+ import { runMigrations } from "./db/migrations";
5
+ import {
6
+ SessionsRepo,
7
+ RequestsRepo,
8
+ JsHooksRepo,
9
+ StorageSnapshotsRepo,
10
+ AnalysisReportsRepo,
11
+ FingerprintProfilesRepo,
12
+ ChatMessagesRepo,
13
+ AiRequestLogRepo,
14
+ InteractionEventsRepo,
15
+ } from "./db/repositories";
16
+ import { CaptureEngine } from "./capture/capture-engine";
17
+ import { SessionManager } from "./session/session-manager";
18
+ import { AiAnalyzer } from "./ai/ai-analyzer";
19
+ import { WindowManager } from "./window";
20
+ import { registerIpcHandlers, loadProxyConfig, applyProxy, loadMCPServerConfig } from "./ipc";
21
+ import { Updater } from "./updater";
22
+ import { MCPClientManager } from "./mcp/mcp-manager";
23
+ import { initMCPServer, stopMCPServer } from "./mcp/mcp-server";
24
+ import { CaManager } from "./proxy/ca-manager";
25
+ import { MitmProxyServer } from "./proxy/mitm-proxy-server";
26
+ import { loadMitmProxyConfig, saveMitmProxyConfig } from "./proxy/mitm-proxy-config";
27
+ import { SystemProxy } from "./proxy/system-proxy";
28
+ import { ProfileStore } from "./fingerprint/profile-store";
29
+ import { join } from "path";
30
+
31
+ const windowManager = new WindowManager();
32
+ const mcpManager = new MCPClientManager();
33
+ let sessionManagerRef: SessionManager | null = null;
34
+ let quitInProgress = false;
35
+
36
+ // Prevent unhandled promise rejections from crashing the main process.
37
+ // Common source: executeJavaScript on crashed/destroyed WebContents.
38
+ process.on("unhandledRejection", (reason) => {
39
+ console.warn("[Main] Unhandled rejection:", reason);
40
+ });
41
+
42
+ // MITM Proxy — initialized lazily inside whenReady (app.getPath requires ready state)
43
+ let caManager: CaManager;
44
+ let mitmProxy: MitmProxyServer;
45
+
46
+ app.whenReady().then(async () => {
47
+ // Initialize structured logging (replaces console.log/warn/error globally)
48
+ initLogger();
49
+
50
+ // Enable native crash reporter — dumps go to userData/Crashpad/
51
+ crashReporter.start({ uploadToServer: false });
52
+
53
+ // Initialize MITM CA & proxy (requires app.getPath)
54
+ caManager = new CaManager(join(app.getPath("userData"), "mitm-ca"));
55
+ mitmProxy = new MitmProxyServer(caManager);
56
+ // Initialize database
57
+ const db = getDatabase();
58
+ runMigrations(db);
59
+
60
+ // Initialize repositories
61
+ const sessionsRepo = new SessionsRepo(db);
62
+ const requestsRepo = new RequestsRepo(db);
63
+ const jsHooksRepo = new JsHooksRepo(db);
64
+ const storageSnapshotsRepo = new StorageSnapshotsRepo(db);
65
+ const reportsRepo = new AnalysisReportsRepo(db);
66
+ const chatMessagesRepo = new ChatMessagesRepo(db);
67
+ const fingerprintRepo = new FingerprintProfilesRepo(db);
68
+ const aiRequestLogRepo = new AiRequestLogRepo(db);
69
+ const interactionEventsRepo = new InteractionEventsRepo(db);
70
+ const profileStore = new ProfileStore(fingerprintRepo);
71
+
72
+ // Initialize capture engine
73
+ const captureEngine = new CaptureEngine(
74
+ requestsRepo,
75
+ jsHooksRepo,
76
+ storageSnapshotsRepo,
77
+ );
78
+
79
+ // Initialize session manager
80
+ const sessionManager = new SessionManager(sessionsRepo, captureEngine, profileStore, interactionEventsRepo);
81
+ sessionManagerRef = sessionManager;
82
+
83
+ // Recover from potential crash
84
+ sessionManager.recoverFromCrash();
85
+
86
+ // Initialize AI analyzer
87
+ const aiAnalyzer = new AiAnalyzer(
88
+ sessionsRepo,
89
+ requestsRepo,
90
+ jsHooksRepo,
91
+ storageSnapshotsRepo,
92
+ reportsRepo,
93
+ aiRequestLogRepo,
94
+ );
95
+
96
+ // Create main window
97
+ windowManager.createMainWindow();
98
+
99
+ // Initialize tab manager with first tab
100
+ windowManager.initTabs();
101
+
102
+ // Apply proxy config from saved settings (before IPC handlers)
103
+ const proxyConfig = loadProxyConfig();
104
+ if (proxyConfig && proxyConfig.type !== "none") {
105
+ applyProxy(proxyConfig).catch((err) =>
106
+ console.error("Failed to apply proxy config:", err),
107
+ );
108
+ }
109
+
110
+ // Handle proxy authentication challenges globally.
111
+ // Chromium proxyRules cannot carry credentials, so we respond to 407 via this event.
112
+ app.on("login", (event, _webContents, _request, authInfo, callback) => {
113
+ if (authInfo.isProxy) {
114
+ const cfg = loadProxyConfig();
115
+ if (cfg && cfg.username && cfg.password) {
116
+ event.preventDefault();
117
+ callback(cfg.username, cfg.password);
118
+ return;
119
+ }
120
+ }
121
+ // Non-proxy auth or no credentials — let default behavior proceed
122
+ callback();
123
+ });
124
+
125
+ // Initialize auto-updater
126
+ const updater = new Updater();
127
+ const mainWin = windowManager.getMainWindow();
128
+ if (mainWin) updater.setMainWindow(mainWin);
129
+
130
+ // Inject MCP client manager into AI analyzer
131
+ aiAnalyzer.setMCPManager(mcpManager);
132
+
133
+ // Register IPC handlers
134
+ registerIpcHandlers({
135
+ sessionManager,
136
+ aiAnalyzer,
137
+ windowManager,
138
+ updater,
139
+ mcpManager,
140
+ mitmProxy,
141
+ caManager,
142
+ sessionsRepo,
143
+ requestsRepo,
144
+ jsHooksRepo,
145
+ storageSnapshotsRepo,
146
+ reportsRepo,
147
+ chatMessagesRepo,
148
+ profileStore,
149
+ aiRequestLogRepo,
150
+ interactionEventsRepo,
151
+ });
152
+
153
+ // Check for updates on startup (non-blocking, delayed 3s)
154
+ setTimeout(() => updater.checkForUpdates(), 3000);
155
+
156
+ // Start MCP Server if enabled
157
+ const mcpServerConfig = loadMCPServerConfig();
158
+ if (mcpServerConfig.enabled) {
159
+ initMCPServer(
160
+ { sessionManager, aiAnalyzer, windowManager, requestsRepo, jsHooksRepo, storageSnapshotsRepo, reportsRepo, interactionEventsRepo },
161
+ mcpServerConfig.port,
162
+ mcpServerConfig.authEnabled,
163
+ mcpServerConfig.authToken,
164
+ ).catch((err) => console.error("Failed to start MCP Server:", err));
165
+ }
166
+
167
+ // Initialize MITM Proxy
168
+ const mitmConfig = loadMitmProxyConfig();
169
+
170
+ // Apply upstream proxy config to MITM proxy so outbound traffic routes correctly
171
+ if (proxyConfig && proxyConfig.type !== "none") {
172
+ mitmProxy.setUpstreamProxy(proxyConfig);
173
+ }
174
+
175
+ // Wire proxy captured events → CaptureEngine (same data shape as CDP)
176
+ mitmProxy.on("response-captured", (data) => {
177
+ captureEngine.handleResponseCaptured({ ...data, source: "proxy" });
178
+ });
179
+
180
+ if (mitmConfig.enabled) {
181
+ caManager
182
+ .init()
183
+ .then(() => mitmProxy.start(mitmConfig.port))
184
+ .then(() => {
185
+ console.log("[Main] MITM proxy auto-started on port", mitmConfig.port);
186
+ if (mitmConfig.systemProxy) {
187
+ SystemProxy.enable(mitmConfig.port).catch((err) =>
188
+ console.error("[Main] Failed to enable system proxy:", err),
189
+ );
190
+ }
191
+ })
192
+ .catch((err) => console.error("[Main] Failed to auto-start MITM proxy:", err));
193
+ }
194
+
195
+ // Trust certificates issued by our MITM CA inside Electron.
196
+ // Without this, HTTPS requests through the MITM proxy fail with SSL errors
197
+ // that can crash Chromium's network stack (0xC0000005).
198
+ app.on("certificate-error", (event, _webContents, _url, _error, certificate, callback) => {
199
+ if (mitmProxy.isRunning() && certificate.issuerName === "Anything Analyzer CA") {
200
+ event.preventDefault();
201
+ callback(true);
202
+ } else {
203
+ callback(false);
204
+ }
205
+ });
206
+
207
+ app.on("activate", () => {
208
+ if (BrowserWindow.getAllWindows().length === 0) {
209
+ windowManager.createMainWindow();
210
+ windowManager.initTabs();
211
+ const win = windowManager.getMainWindow();
212
+ if (win) updater.setMainWindow(win);
213
+ }
214
+ });
215
+ });
216
+
217
+ app.on("window-all-closed", () => {
218
+ if (process.platform !== "darwin") {
219
+ app.quit();
220
+ }
221
+ });
222
+
223
+ app.on("before-quit", (event) => {
224
+ if (quitInProgress) return;
225
+
226
+ // Block immediate quit and perform ordered async cleanup first.
227
+ event.preventDefault();
228
+ quitInProgress = true;
229
+
230
+ (async () => {
231
+ try {
232
+ // Mark shutdown state early so tab destroy handlers don't recreate tabs.
233
+ windowManager.setShuttingDown(true);
234
+
235
+ // 1) Stop capture pipelines first, so no new DB writes are produced.
236
+ const currentSessionId = sessionManagerRef?.getCurrentSessionId();
237
+ if (sessionManagerRef && currentSessionId) {
238
+ await sessionManagerRef.stopCapture(currentSessionId);
239
+ }
240
+
241
+ // 2) Disable system proxy and persist state.
242
+ await SystemProxy.disable().catch(() => {});
243
+ const config = loadMitmProxyConfig();
244
+ if (config.systemProxy) {
245
+ saveMitmProxyConfig({ ...config, systemProxy: false });
246
+ }
247
+
248
+ // 3) Stop async services.
249
+ await mitmProxy.stop().catch(() => {});
250
+ await stopMCPServer().catch(() => {});
251
+ await mcpManager.disconnectAll().catch(() => {});
252
+ } finally {
253
+ // 4) Close DB last, then let Electron finish normal quit flow.
254
+ closeDatabase();
255
+ app.quit();
256
+ }
257
+ })().catch(() => {
258
+ // Ignore and still force-exit via finally block above.
259
+ });
260
+ });