@mcp-shark/mcp-shark 1.4.2 → 1.5.2

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 (204) hide show
  1. package/README.md +84 -645
  2. package/bin/mcp-shark.js +30 -36
  3. package/mcp-server/index.js +115 -0
  4. package/mcp-server/lib/auditor/audit.js +22 -38
  5. package/mcp-server/lib/common/error.js +1 -1
  6. package/mcp-server/lib/server/external/all.js +5 -6
  7. package/mcp-server/lib/server/external/config.js +1 -3
  8. package/mcp-server/lib/server/external/kv.js +4 -12
  9. package/mcp-server/lib/server/external/single/request.js +3 -6
  10. package/mcp-server/lib/server/external/single/run.js +8 -19
  11. package/mcp-server/lib/server/internal/handlers/prompts-get.js +3 -13
  12. package/mcp-server/lib/server/internal/handlers/prompts-list.js +2 -6
  13. package/mcp-server/lib/server/internal/handlers/resources-list.js +2 -6
  14. package/mcp-server/lib/server/internal/handlers/resources-read.js +3 -12
  15. package/mcp-server/lib/server/internal/handlers/tools-call.js +3 -9
  16. package/mcp-server/lib/server/internal/handlers/tools-list.js +2 -2
  17. package/mcp-server/lib/server/internal/run.js +4 -16
  18. package/mcp-server/lib/server/internal/server.js +6 -7
  19. package/mcp-server/lib/server/internal/session.js +2 -15
  20. package/mcp-server/mcp-shark.js +16 -66
  21. package/package.json +23 -38
  22. package/shared/logger.js +90 -0
  23. package/ui/dist/assets/index-Cc-IUa83.css +1 -0
  24. package/ui/dist/assets/index-srLDlk97.js +35 -0
  25. package/ui/dist/index.html +17 -0
  26. package/ui/dist/og-image.png +0 -0
  27. package/ui/server/routes/backups/deleteBackup.js +54 -0
  28. package/ui/server/routes/backups/index.js +15 -0
  29. package/ui/server/routes/backups/listBackups.js +75 -0
  30. package/ui/server/routes/backups/restoreBackup.js +83 -0
  31. package/ui/server/routes/backups/viewBackup.js +47 -0
  32. package/ui/server/routes/composite/index.js +46 -0
  33. package/ui/server/routes/composite/servers.js +18 -0
  34. package/ui/server/routes/composite/setup.js +129 -0
  35. package/ui/server/routes/composite/status.js +7 -0
  36. package/ui/server/routes/composite/stop.js +39 -0
  37. package/ui/server/routes/composite/utils.js +45 -0
  38. package/ui/server/routes/config.js +34 -30
  39. package/ui/server/routes/conversations.js +3 -3
  40. package/ui/server/routes/help.js +2 -2
  41. package/ui/server/routes/logs.js +5 -5
  42. package/ui/server/routes/playground.js +45 -47
  43. package/ui/server/routes/requests.js +112 -108
  44. package/ui/server/routes/sessions.js +4 -4
  45. package/ui/server/routes/settings.js +199 -0
  46. package/ui/server/routes/smartscan/discover.js +7 -6
  47. package/ui/server/routes/smartscan/scans/clearCache.js +3 -2
  48. package/ui/server/routes/smartscan/scans/createBatchScans.js +4 -3
  49. package/ui/server/routes/smartscan/scans/createScan.js +2 -1
  50. package/ui/server/routes/smartscan/scans/getCachedResults.js +2 -1
  51. package/ui/server/routes/smartscan/scans/getScan.js +2 -1
  52. package/ui/server/routes/smartscan/scans/listScans.js +5 -4
  53. package/ui/server/routes/smartscan/scans.js +3 -3
  54. package/ui/server/routes/smartscan/token.js +4 -3
  55. package/ui/server/routes/smartscan/transport.js +1 -1
  56. package/ui/server/routes/smartscan.js +1 -1
  57. package/ui/server/routes/statistics.js +13 -10
  58. package/ui/server/utils/config-update.js +7 -6
  59. package/ui/server/utils/config.js +4 -4
  60. package/ui/server/utils/logger.js +2 -0
  61. package/ui/server/utils/paths.js +210 -2
  62. package/ui/server/utils/port.js +2 -2
  63. package/ui/server/utils/process.js +0 -67
  64. package/ui/server/utils/scan-cache/all-results.js +76 -59
  65. package/ui/server/utils/scan-cache/directory.js +1 -1
  66. package/ui/server/utils/scan-cache/file-operations.js +19 -16
  67. package/ui/server/utils/scan-cache/server-operations.js +14 -9
  68. package/ui/server/utils/serialization.js +9 -3
  69. package/ui/server/utils/smartscan-token.js +4 -3
  70. package/ui/server.js +86 -41
  71. package/ui/src/App.jsx +5 -5
  72. package/ui/src/CompositeLogs.jsx +20 -20
  73. package/ui/src/CompositeSetup.jsx +9 -9
  74. package/ui/src/HelpGuide/HelpGuideFooter.jsx +1 -0
  75. package/ui/src/HelpGuide/HelpGuideHeader.jsx +2 -1
  76. package/ui/src/HelpGuide.jsx +17 -4
  77. package/ui/src/IntroTour.jsx +19 -5
  78. package/ui/src/LogDetail.jsx +1 -0
  79. package/ui/src/LogTable.jsx +24 -6
  80. package/ui/src/PacketDetail.jsx +21 -16
  81. package/ui/src/PacketFilters.jsx +29 -14
  82. package/ui/src/PacketList.jsx +4 -5
  83. package/ui/src/SmartScan.jsx +5 -5
  84. package/ui/src/TabNavigation.jsx +5 -5
  85. package/ui/src/components/App/HelpButton.jsx +4 -0
  86. package/ui/src/components/App/TrafficTab.jsx +4 -4
  87. package/ui/src/components/App/useAppState.js +118 -24
  88. package/ui/src/components/BackupList.jsx +6 -2
  89. package/ui/src/components/CollapsibleSection.jsx +16 -2
  90. package/ui/src/components/ConfigViewerModal.jsx +17 -3
  91. package/ui/src/components/ConfirmationModal.jsx +20 -3
  92. package/ui/src/components/DetailsTab/BodySection.jsx +3 -1
  93. package/ui/src/components/DetailsTab/CollapsibleRequestResponse.jsx +14 -3
  94. package/ui/src/components/DetailsTab/InfoSection.jsx +4 -2
  95. package/ui/src/components/DetailsTab/RequestDetailsSection.jsx +7 -5
  96. package/ui/src/components/DetailsTab/ResponseDetailsSection.jsx +7 -5
  97. package/ui/src/components/DetectedPathsList.jsx +5 -2
  98. package/ui/src/components/FileInput.jsx +3 -1
  99. package/ui/src/components/GroupHeader.jsx +14 -0
  100. package/ui/src/components/GroupedByMcpView.jsx +3 -4
  101. package/ui/src/components/GroupedByServerView.jsx +1 -1
  102. package/ui/src/components/GroupedBySessionView.jsx +1 -1
  103. package/ui/src/components/HexTab.jsx +17 -4
  104. package/ui/src/components/LogsToolbar.jsx +3 -1
  105. package/ui/src/components/McpPlayground/LoadingModal.jsx +7 -3
  106. package/ui/src/components/McpPlayground/PromptsSection/PromptCallPanel.jsx +5 -0
  107. package/ui/src/components/McpPlayground/PromptsSection/PromptItem.jsx +6 -2
  108. package/ui/src/components/McpPlayground/PromptsSection/PromptsList.jsx +4 -4
  109. package/ui/src/components/McpPlayground/PromptsSection.jsx +2 -1
  110. package/ui/src/components/McpPlayground/ResourcesSection/ResourceCallPanel.jsx +3 -0
  111. package/ui/src/components/McpPlayground/ResourcesSection/ResourceItem.jsx +6 -2
  112. package/ui/src/components/McpPlayground/ResourcesSection/ResourcesList.jsx +4 -4
  113. package/ui/src/components/McpPlayground/ResourcesSection.jsx +2 -1
  114. package/ui/src/components/McpPlayground/ToolsSection/ToolCallPanel.jsx +5 -0
  115. package/ui/src/components/McpPlayground/ToolsSection/ToolItem.jsx +6 -2
  116. package/ui/src/components/McpPlayground/ToolsSection/ToolsList.jsx +4 -4
  117. package/ui/src/components/McpPlayground/ToolsSection.jsx +2 -1
  118. package/ui/src/components/McpPlayground/hooks/useMcpDataLoader.js +9 -9
  119. package/ui/src/components/McpPlayground/hooks/useMcpRequest.js +10 -5
  120. package/ui/src/components/McpPlayground/hooks/useMcpServerStatus.js +43 -23
  121. package/ui/src/components/McpPlayground/useMcpPlayground.js +72 -44
  122. package/ui/src/components/McpPlayground.jsx +5 -2
  123. package/ui/src/components/PacketDetailHeader.jsx +8 -3
  124. package/ui/src/components/PacketFilters/ExportControls.jsx +2 -1
  125. package/ui/src/components/PacketFilters/FilterInput.jsx +1 -1
  126. package/ui/src/components/RawTab.jsx +15 -2
  127. package/ui/src/components/RequestRow/OrphanedResponseRow.jsx +10 -2
  128. package/ui/src/components/RequestRow/RequestRowMain.jsx +12 -3
  129. package/ui/src/components/RequestRow/ResponseRow.jsx +11 -3
  130. package/ui/src/components/RequestRow.jsx +17 -9
  131. package/ui/src/components/ServerControl.jsx +3 -1
  132. package/ui/src/components/ServiceSelector.jsx +2 -0
  133. package/ui/src/components/SmartScan/AnalysisResult.jsx +2 -2
  134. package/ui/src/components/SmartScan/BatchResultsDisplay/BatchResultItem.jsx +2 -1
  135. package/ui/src/components/SmartScan/BatchResultsDisplay/BatchResultsHeader.jsx +1 -1
  136. package/ui/src/components/SmartScan/BatchResultsDisplay.jsx +9 -3
  137. package/ui/src/components/SmartScan/EmptyState.jsx +3 -0
  138. package/ui/src/components/SmartScan/ErrorDisplay.jsx +4 -2
  139. package/ui/src/components/SmartScan/ExpandableSection.jsx +4 -0
  140. package/ui/src/components/SmartScan/FindingsTable.jsx +46 -42
  141. package/ui/src/components/SmartScan/ListViewContent.jsx +2 -2
  142. package/ui/src/components/SmartScan/NotablePatternsSection.jsx +13 -8
  143. package/ui/src/components/SmartScan/OverallSummarySection.jsx +36 -29
  144. package/ui/src/components/SmartScan/RecommendationsSection.jsx +10 -8
  145. package/ui/src/components/SmartScan/ScanDetailHeader.jsx +2 -1
  146. package/ui/src/components/SmartScan/ScanDetailView.jsx +4 -4
  147. package/ui/src/components/SmartScan/ScanListView/ScanListHeader.jsx +2 -1
  148. package/ui/src/components/SmartScan/ScanListView/ScanListItem.jsx +15 -1
  149. package/ui/src/components/SmartScan/ScanOverviewSection.jsx +3 -1
  150. package/ui/src/components/SmartScan/ScanResultsDisplay.jsx +1 -1
  151. package/ui/src/components/SmartScan/ScanViewContent.jsx +2 -2
  152. package/ui/src/components/SmartScan/ScanningProgress.jsx +4 -2
  153. package/ui/src/components/SmartScan/ServerInfoSection.jsx +3 -1
  154. package/ui/src/components/SmartScan/ServerSelectionRow.jsx +4 -2
  155. package/ui/src/components/SmartScan/SingleResultDisplay.jsx +5 -3
  156. package/ui/src/components/SmartScan/SmartScanControls.jsx +11 -7
  157. package/ui/src/components/SmartScan/SmartScanHeader.jsx +1 -1
  158. package/ui/src/components/SmartScan/ViewModeTabs.jsx +2 -0
  159. package/ui/src/components/SmartScan/hooks/useCacheManagement.js +1 -2
  160. package/ui/src/components/SmartScan/hooks/useMcpDiscovery.js +22 -26
  161. package/ui/src/components/SmartScan/hooks/useScanList.js +10 -9
  162. package/ui/src/components/SmartScan/hooks/useScanOperations.js +23 -14
  163. package/ui/src/components/SmartScan/hooks/useServerStatus.js +2 -2
  164. package/ui/src/components/SmartScan/hooks/useTokenManagement.js +2 -2
  165. package/ui/src/components/SmartScan/scanDataUtils.js +22 -17
  166. package/ui/src/components/SmartScan/useSmartScan.js +4 -4
  167. package/ui/src/components/SmartScan/utils.js +3 -1
  168. package/ui/src/components/SmartScanIcons.jsx +6 -3
  169. package/ui/src/components/TabNavigation/DesktopTabs.jsx +8 -3
  170. package/ui/src/components/TabNavigation/MobileDropdown.jsx +3 -1
  171. package/ui/src/components/TabNavigation.jsx +8 -3
  172. package/ui/src/components/TabNavigationIcons.jsx +4 -4
  173. package/ui/src/components/TourOverlay.jsx +1 -1
  174. package/ui/src/components/TourTooltip/TourTooltipButtons.jsx +3 -0
  175. package/ui/src/components/TourTooltip/TourTooltipHeader.jsx +1 -0
  176. package/ui/src/components/TourTooltip/TourTooltipIcons.jsx +9 -0
  177. package/ui/src/components/TourTooltip/useTooltipPosition.js +63 -36
  178. package/ui/src/components/TourTooltip.jsx +11 -3
  179. package/ui/src/components/ViewModeTabs.jsx +3 -1
  180. package/ui/src/config/tourSteps.jsx +0 -2
  181. package/ui/src/hooks/useAnimation.js +15 -12
  182. package/ui/src/hooks/useConfigManagement.js +8 -8
  183. package/ui/src/hooks/useServiceExtraction.js +1 -1
  184. package/ui/src/index.css +3 -8
  185. package/ui/src/theme.js +3 -3
  186. package/ui/src/utils/hexUtils.js +11 -5
  187. package/ui/src/utils/mcpGroupingUtils.js +18 -10
  188. package/ui/src/utils/requestPairing.js +89 -0
  189. package/ui/src/utils/requestUtils.js +32 -101
  190. package/ui/vite.config.js +1 -1
  191. package/mcp-server/.editorconfig +0 -15
  192. package/mcp-server/.prettierignore +0 -11
  193. package/mcp-server/.prettierrc +0 -12
  194. package/mcp-server/README.md +0 -280
  195. package/mcp-server/commitlint.config.cjs +0 -42
  196. package/mcp-server/eslint.config.js +0 -131
  197. package/mcp-server/package-lock.json +0 -4784
  198. package/mcp-server/package.json +0 -30
  199. package/ui/README.md +0 -212
  200. package/ui/package-lock.json +0 -3574
  201. package/ui/package.json +0 -12
  202. package/ui/paths.js +0 -282
  203. package/ui/server/routes/backups.js +0 -251
  204. package/ui/server/routes/composite.js +0 -260
package/ui/package.json DELETED
@@ -1,12 +0,0 @@
1
- {
2
- "name": "mcp-shark-ui",
3
- "version": "1.0.0",
4
- "type": "module",
5
- "scripts": {
6
- "dev": "vite",
7
- "build": "vite build",
8
- "preview": "vite preview",
9
- "prestart": "npm run build",
10
- "start": "node server.js"
11
- }
12
- }
package/ui/paths.js DELETED
@@ -1,282 +0,0 @@
1
- import { execSync } from 'child_process';
2
- import * as path from 'node:path';
3
- import * as fs from 'node:fs';
4
- import * as os from 'node:os';
5
-
6
- /**
7
- * Get system PATH from the host machine's shell environment
8
- * This works in Electron by executing a shell command to get the actual PATH
9
- * Includes both system PATH and user's custom PATH from shell config files
10
- */
11
- function getSystemPath() {
12
- try {
13
- if (process.platform === 'win32') {
14
- // Windows: use cmd to get PATH (includes user PATH)
15
- const pathOutput = execSync('cmd /c echo %PATH%', {
16
- encoding: 'utf8',
17
- timeout: 2000,
18
- stdio: ['ignore', 'pipe', 'ignore'],
19
- });
20
- return pathOutput.trim();
21
- } else {
22
- // Unix-like: use shell to get PATH from user's actual shell environment
23
- // Try to detect the user's default shell first
24
- const userShell = process.env.SHELL || '/bin/zsh';
25
- const shells = [userShell, '/bin/zsh', '/bin/bash', '/bin/sh'];
26
-
27
- for (const shell of shells) {
28
- if (fs.existsSync(shell)) {
29
- try {
30
- // For zsh, we need to load both login and interactive configs
31
- // zsh -l loads .zprofile/.zlogin, but .zshrc has interactive configs
32
- // Try interactive mode first (loads .zshrc), then login mode
33
- const shellName = path.basename(shell);
34
- let pathOutput;
35
-
36
- if (shellName === 'zsh') {
37
- // For zsh, try interactive mode to get .zshrc PATH additions
38
- try {
39
- pathOutput = execSync(`${shell} -i -c 'echo $PATH'`, {
40
- encoding: 'utf8',
41
- timeout: 2000,
42
- stdio: ['ignore', 'pipe', 'ignore'],
43
- maxBuffer: 1024 * 1024,
44
- env: {
45
- ...Object.fromEntries(
46
- Object.entries(process.env).filter(([key]) => key !== 'PATH')
47
- ),
48
- },
49
- });
50
- } catch (_e) {
51
- // Fallback to login shell
52
- pathOutput = execSync(`${shell} -l -c 'echo $PATH'`, {
53
- encoding: 'utf8',
54
- timeout: 2000,
55
- stdio: ['ignore', 'pipe', 'ignore'],
56
- maxBuffer: 1024 * 1024,
57
- env: {
58
- ...Object.fromEntries(
59
- Object.entries(process.env).filter(([key]) => key !== 'PATH')
60
- ),
61
- },
62
- });
63
- }
64
- } else {
65
- // For bash/sh, use login shell
66
- pathOutput = execSync(`${shell} -l -c 'echo $PATH'`, {
67
- encoding: 'utf8',
68
- timeout: 2000,
69
- stdio: ['ignore', 'pipe', 'ignore'],
70
- maxBuffer: 1024 * 1024,
71
- env: {
72
- ...Object.fromEntries(
73
- Object.entries(process.env).filter(([key]) => key !== 'PATH')
74
- ),
75
- },
76
- });
77
- }
78
-
79
- const systemPath = pathOutput.trim();
80
- if (systemPath) {
81
- console.log(`[Server Manager] Got PATH from ${shell} (${shellName})`);
82
- return systemPath;
83
- }
84
- } catch (_e) {
85
- // Try next shell
86
- continue;
87
- }
88
- }
89
- }
90
-
91
- // Fallback: try to read from common shell config files
92
- // For zsh, check .zshrc first (interactive), then .zprofile (login)
93
- const homeDir = os.homedir();
94
- const configFiles = [
95
- { file: path.join(homeDir, '.zshrc'), shell: 'zsh', interactive: true },
96
- { file: path.join(homeDir, '.zprofile'), shell: 'zsh', interactive: false },
97
- { file: path.join(homeDir, '.zlogin'), shell: 'zsh', interactive: false },
98
- { file: path.join(homeDir, '.bashrc'), shell: 'bash', interactive: true },
99
- { file: path.join(homeDir, '.bash_profile'), shell: 'bash', interactive: false },
100
- { file: path.join(homeDir, '.profile'), shell: 'sh', interactive: false },
101
- ];
102
-
103
- for (const { file, shell: shellName, interactive } of configFiles) {
104
- if (fs.existsSync(file)) {
105
- try {
106
- // For zsh interactive configs, use -i flag
107
- const flag = shellName === 'zsh' && interactive ? '-i' : '';
108
- const pathOutput = execSync(
109
- `/bin/${shellName} ${flag} -c 'source ${file} 2>/dev/null; echo $PATH'`,
110
- {
111
- encoding: 'utf8',
112
- timeout: 2000,
113
- stdio: ['ignore', 'pipe', 'ignore'],
114
- maxBuffer: 1024 * 1024,
115
- env: {
116
- ...Object.fromEntries(
117
- Object.entries(process.env).filter(([key]) => key !== 'PATH')
118
- ),
119
- },
120
- }
121
- );
122
- const systemPath = pathOutput.trim();
123
- if (systemPath && systemPath.length > 10) {
124
- // Only use if we got a meaningful PATH
125
- console.log(`[Server Manager] Got PATH from ${file}`);
126
- return systemPath;
127
- }
128
- } catch (_e) {
129
- // Try next config file
130
- continue;
131
- }
132
- }
133
- }
134
- }
135
- } catch (error) {
136
- console.warn('[Server Manager] Could not get system PATH:', error.message);
137
- }
138
- return null;
139
- }
140
-
141
- /**
142
- * Enhance PATH environment variable to include system paths and user paths
143
- * This is especially important in Electron where PATH might not include system executables
144
- */
145
- export function enhancePath(originalPath) {
146
- const homeDir = os.homedir();
147
- const pathSeparator = process.platform === 'win32' ? ';' : ':';
148
-
149
- // Try to get the actual system PATH from the host (includes user's custom PATH)
150
- const systemPath = getSystemPath();
151
- if (systemPath) {
152
- console.log('[Server Manager] Using system PATH from host machine');
153
- // Combine system PATH with original PATH, prioritizing system PATH
154
- // Also add user-specific paths that might not be in system PATH
155
- const userPaths = [
156
- // Common user-specific binary locations
157
- path.join(homeDir, '.local', 'bin'),
158
- path.join(homeDir, '.npm-global', 'bin'),
159
- path.join(homeDir, '.cargo', 'bin'),
160
- path.join(homeDir, 'bin'),
161
- // Node version managers
162
- path.join(homeDir, '.nvm', 'current', 'bin'),
163
- // Try to find actual nvm node version (check common versions)
164
- ...(function () {
165
- try {
166
- const nvmVersionsPath = path.join(homeDir, '.nvm', 'versions', 'node');
167
- if (fs.existsSync(nvmVersionsPath)) {
168
- return fs
169
- .readdirSync(nvmVersionsPath, { withFileTypes: true })
170
- .filter((dirent) => dirent.isDirectory())
171
- .map((dirent) => path.join(nvmVersionsPath, dirent.name, 'bin'));
172
- }
173
- } catch (_e) {
174
- // Ignore errors reading nvm directory
175
- }
176
- return [];
177
- })(),
178
- path.join(homeDir, '.fnm', 'node-versions', 'v20.0.0', 'install', 'bin'), // fnm
179
- // Python version managers
180
- path.join(homeDir, '.pyenv', 'shims'),
181
- path.join(homeDir, '.pyenv', 'bin'),
182
- // Go version managers
183
- path.join(homeDir, '.gvm', 'bin'),
184
- path.join(homeDir, '.gvm', 'gos', 'current', 'bin'),
185
- // Rust/Cargo
186
- path.join(homeDir, '.cargo', 'bin'),
187
- // Go
188
- path.join(homeDir, 'go', 'bin'),
189
- path.join(homeDir, '.go', 'bin'),
190
- // iTerm utilities
191
- '/Applications/iTerm.app/Contents/Resources/utilities',
192
- // Windows user paths
193
- ...(process.platform === 'win32'
194
- ? [
195
- path.join(homeDir, 'AppData', 'Local', 'Programs'),
196
- path.join(homeDir, 'AppData', 'Roaming', 'npm'),
197
- path.join(homeDir, 'AppData', 'Local', 'Microsoft', 'WindowsApps'),
198
- ]
199
- : []),
200
- ].filter((p) => {
201
- // Filter out paths that don't exist, but allow dynamic version paths
202
- if (p.includes('v20.0.0') || p.includes('current')) {
203
- // For version manager paths, check if parent directory exists
204
- return fs.existsSync(path.dirname(p));
205
- }
206
- return fs.existsSync(p);
207
- });
208
-
209
- // Combine: system PATH (from shell) + user-specific paths + original PATH
210
- return [systemPath, ...userPaths, originalPath || ''].filter((p) => p).join(pathSeparator);
211
- }
212
-
213
- // Fallback: add common system and user locations
214
- console.log('[Server Manager] Could not get system PATH, adding common locations');
215
- const pathsToAdd = [
216
- // System binary locations
217
- '/usr/local/bin',
218
- '/usr/bin',
219
- '/opt/homebrew/bin',
220
- '/usr/local/opt/node/bin',
221
- '/opt/local/bin',
222
- '/sbin',
223
- '/usr/sbin',
224
- // macOS specific
225
- ...(process.platform === 'darwin'
226
- ? [
227
- '/opt/homebrew/opt/python/bin',
228
- '/usr/local/opt/python/bin',
229
- '/Applications/Docker.app/Contents/Resources/bin',
230
- ]
231
- : []),
232
- // Linux specific
233
- ...(process.platform === 'linux' ? ['/snap/bin', path.join(homeDir, '.local', 'bin')] : []),
234
- // Windows specific
235
- ...(process.platform === 'win32'
236
- ? [
237
- path.join(process.env.ProgramFiles || '', 'nodejs'),
238
- path.join(process.env['ProgramFiles(x86)'] || '', 'nodejs'),
239
- path.join(homeDir, 'AppData', 'Roaming', 'npm'),
240
- path.join(process.env.ProgramFiles || '', 'Docker', 'Docker', 'resources', 'bin'),
241
- ]
242
- : []),
243
- // User-specific paths (prioritize these)
244
- path.join(homeDir, '.local', 'bin'),
245
- path.join(homeDir, '.npm-global', 'bin'),
246
- path.join(homeDir, '.cargo', 'bin'),
247
- path.join(homeDir, 'bin'),
248
- path.join(homeDir, '.nvm', 'current', 'bin'),
249
- // Try to find actual nvm node version (check common versions)
250
- ...(function () {
251
- try {
252
- const nvmVersionsPath = path.join(homeDir, '.nvm', 'versions', 'node');
253
- if (fs.existsSync(nvmVersionsPath)) {
254
- return fs
255
- .readdirSync(nvmVersionsPath, { withFileTypes: true })
256
- .filter((dirent) => dirent.isDirectory())
257
- .map((dirent) => path.join(nvmVersionsPath, dirent.name, 'bin'));
258
- }
259
- } catch (_e) {
260
- // Ignore errors reading nvm directory
261
- }
262
- return [];
263
- })(),
264
- path.join(homeDir, '.pyenv', 'shims'),
265
- path.join(homeDir, '.pyenv', 'bin'),
266
- path.join(homeDir, '.gvm', 'bin'),
267
- path.join(homeDir, '.gvm', 'gos', 'current', 'bin'),
268
- path.join(homeDir, 'go', 'bin'),
269
- path.join(homeDir, '.go', 'bin'),
270
- // iTerm utilities
271
- '/Applications/iTerm.app/Contents/Resources/utilities',
272
- // Windows user paths
273
- ...(process.platform === 'win32'
274
- ? [
275
- path.join(homeDir, 'AppData', 'Local', 'Programs'),
276
- path.join(homeDir, 'AppData', 'Local', 'Microsoft', 'WindowsApps'),
277
- ]
278
- : []),
279
- ].filter((p) => p && fs.existsSync(p));
280
-
281
- return [...pathsToAdd, originalPath || ''].filter((p) => p).join(pathSeparator);
282
- }
@@ -1,251 +0,0 @@
1
- import * as path from 'node:path';
2
- import * as fs from 'node:fs';
3
- import { homedir } from 'node:os';
4
-
5
- export function createBackupRoutes() {
6
- const router = {};
7
-
8
- router.listBackups = (req, res) => {
9
- try {
10
- const backups = [];
11
- const homeDir = homedir();
12
-
13
- const commonPaths = [
14
- path.join(homeDir, '.cursor', 'mcp.json'),
15
- path.join(homeDir, '.codeium', 'windsurf', 'mcp_config.json'),
16
- ];
17
-
18
- const backupDirs = [
19
- path.join(homeDir, '.cursor'),
20
- path.join(homeDir, '.codeium', 'windsurf'),
21
- ];
22
-
23
- // Find backups with new format: .mcp.json-mcpshark.<datetime>.json
24
- backupDirs.forEach((dir) => {
25
- if (fs.existsSync(dir)) {
26
- const files = fs.readdirSync(dir);
27
- files
28
- .filter((file) => {
29
- // Match pattern: .<basename>-mcpshark.<datetime>.json
30
- return /^\.(.+)-mcpshark\.\d{4}-\d{2}-\d{2}_\d{2}-\d{2}-\d{2}\.json$/.test(file);
31
- })
32
- .forEach((file) => {
33
- const backupPath = path.join(dir, file);
34
- // Extract original filename from backup name
35
- // .mcp.json-mcpshark.<datetime>.json -> mcp.json
36
- const match = file.match(/^\.(.+)-mcpshark\./);
37
- if (match) {
38
- const originalBasename = match[1];
39
- const originalPath = path.join(dir, originalBasename);
40
- const stats = fs.statSync(backupPath);
41
- backups.push({
42
- originalPath: originalPath,
43
- backupPath: backupPath,
44
- createdAt: stats.birthtime.toISOString(),
45
- modifiedAt: stats.mtime.toISOString(),
46
- size: stats.size,
47
- displayPath: originalPath.replace(homeDir, '~'),
48
- backupFileName: file,
49
- });
50
- }
51
- });
52
- }
53
- });
54
-
55
- // Also check for old .backup format for backward compatibility
56
- commonPaths.forEach((configPath) => {
57
- const backupPath = `${configPath}.backup`;
58
- if (fs.existsSync(backupPath)) {
59
- const stats = fs.statSync(backupPath);
60
- backups.push({
61
- originalPath: configPath,
62
- backupPath: backupPath,
63
- createdAt: stats.birthtime.toISOString(),
64
- modifiedAt: stats.mtime.toISOString(),
65
- size: stats.size,
66
- displayPath: configPath.replace(homeDir, '~'),
67
- backupFileName: path.basename(backupPath),
68
- });
69
- }
70
- });
71
-
72
- // Sort by modifiedAt (latest first)
73
- res.json({
74
- backups: backups.sort(
75
- (a, b) => new Date(b.modifiedAt || b.createdAt) - new Date(a.modifiedAt || a.createdAt)
76
- ),
77
- });
78
- } catch (error) {
79
- res.status(500).json({ error: 'Failed to list backups', details: error.message });
80
- }
81
- };
82
-
83
- router.restoreBackup = (req, res, mcpSharkLogs, broadcastLogUpdate) => {
84
- try {
85
- const { backupPath, originalPath } = req.body;
86
-
87
- if (!backupPath) {
88
- return res.status(400).json({ error: 'backupPath is required' });
89
- }
90
-
91
- const resolvedBackupPath = backupPath.startsWith('~')
92
- ? path.join(homedir(), backupPath.slice(1))
93
- : backupPath;
94
-
95
- if (!fs.existsSync(resolvedBackupPath)) {
96
- return res.status(404).json({ error: 'Backup file not found', path: resolvedBackupPath });
97
- }
98
-
99
- // Determine original path
100
- let targetPath;
101
- if (originalPath) {
102
- targetPath = originalPath.startsWith('~')
103
- ? path.join(homedir(), originalPath.slice(1))
104
- : originalPath;
105
- } else {
106
- // Try to extract from backup filename
107
- if (resolvedBackupPath.endsWith('.backup')) {
108
- targetPath = resolvedBackupPath.replace('.backup', '');
109
- } else {
110
- // New format: .mcp.json-mcpshark.<datetime>.json
111
- const match = path.basename(resolvedBackupPath).match(/^\.(.+)-mcpshark\./);
112
- if (match) {
113
- const originalBasename = match[1];
114
- targetPath = path.join(path.dirname(resolvedBackupPath), originalBasename);
115
- } else {
116
- return res.status(400).json({ error: 'Could not determine original file path' });
117
- }
118
- }
119
- }
120
-
121
- const backupContent = fs.readFileSync(resolvedBackupPath, 'utf8');
122
- fs.writeFileSync(targetPath, backupContent);
123
-
124
- const timestamp = new Date().toISOString();
125
- const restoreLog = {
126
- timestamp,
127
- type: 'stdout',
128
- line: `[RESTORE] Restored config from backup: ${targetPath.replace(homedir(), '~')}`,
129
- };
130
- mcpSharkLogs.push(restoreLog);
131
- if (mcpSharkLogs.length > 10000) {
132
- mcpSharkLogs.shift();
133
- }
134
- broadcastLogUpdate(restoreLog);
135
-
136
- res.json({
137
- success: true,
138
- message: 'Config file restored from backup',
139
- originalPath: targetPath.replace(homedir(), '~'),
140
- });
141
- } catch (error) {
142
- const timestamp = new Date().toISOString();
143
- const errorLog = {
144
- timestamp,
145
- type: 'error',
146
- line: `[RESTORE ERROR] Failed to restore: ${error.message}`,
147
- };
148
- mcpSharkLogs.push(errorLog);
149
- if (mcpSharkLogs.length > 10000) {
150
- mcpSharkLogs.shift();
151
- }
152
- broadcastLogUpdate(errorLog);
153
- res.status(500).json({ error: 'Failed to restore backup', details: error.message });
154
- }
155
- };
156
-
157
- router.viewBackup = (req, res) => {
158
- try {
159
- const { backupPath } = req.query;
160
-
161
- if (!backupPath) {
162
- return res.status(400).json({ error: 'backupPath is required' });
163
- }
164
-
165
- const resolvedBackupPath = backupPath.startsWith('~')
166
- ? path.join(homedir(), backupPath.slice(1))
167
- : backupPath;
168
-
169
- if (!fs.existsSync(resolvedBackupPath)) {
170
- return res.status(404).json({ error: 'Backup file not found', path: resolvedBackupPath });
171
- }
172
-
173
- const content = fs.readFileSync(resolvedBackupPath, 'utf-8');
174
- const parsed = (() => {
175
- try {
176
- return JSON.parse(content);
177
- } catch (e) {
178
- return null;
179
- }
180
- })();
181
-
182
- const stats = fs.statSync(resolvedBackupPath);
183
-
184
- res.json({
185
- success: true,
186
- backupPath: resolvedBackupPath,
187
- displayPath: resolvedBackupPath.replace(homedir(), '~'),
188
- content: content,
189
- parsed: parsed,
190
- createdAt: stats.birthtime.toISOString(),
191
- modifiedAt: stats.mtime.toISOString(),
192
- size: stats.size,
193
- });
194
- } catch (error) {
195
- res.status(500).json({ error: 'Failed to read backup file', details: error.message });
196
- }
197
- };
198
-
199
- router.deleteBackup = (req, res, mcpSharkLogs, broadcastLogUpdate) => {
200
- try {
201
- const { backupPath } = req.body;
202
-
203
- if (!backupPath) {
204
- return res.status(400).json({ error: 'backupPath is required' });
205
- }
206
-
207
- const resolvedBackupPath = backupPath.startsWith('~')
208
- ? path.join(homedir(), backupPath.slice(1))
209
- : backupPath;
210
-
211
- if (!fs.existsSync(resolvedBackupPath)) {
212
- return res.status(404).json({ error: 'Backup file not found', path: resolvedBackupPath });
213
- }
214
-
215
- fs.unlinkSync(resolvedBackupPath);
216
-
217
- const timestamp = new Date().toISOString();
218
- const deleteLog = {
219
- timestamp,
220
- type: 'stdout',
221
- line: `[DELETE] Deleted backup: ${resolvedBackupPath.replace(homedir(), '~')}`,
222
- };
223
- mcpSharkLogs.push(deleteLog);
224
- if (mcpSharkLogs.length > 10000) {
225
- mcpSharkLogs.shift();
226
- }
227
- broadcastLogUpdate(deleteLog);
228
-
229
- res.json({
230
- success: true,
231
- message: 'Backup file deleted successfully',
232
- backupPath: resolvedBackupPath.replace(homedir(), '~'),
233
- });
234
- } catch (error) {
235
- const timestamp = new Date().toISOString();
236
- const errorLog = {
237
- timestamp,
238
- type: 'error',
239
- line: `[DELETE ERROR] Failed to delete backup: ${error.message}`,
240
- };
241
- mcpSharkLogs.push(errorLog);
242
- if (mcpSharkLogs.length > 10000) {
243
- mcpSharkLogs.shift();
244
- }
245
- broadcastLogUpdate(errorLog);
246
- res.status(500).json({ error: 'Failed to delete backup', details: error.message });
247
- }
248
- };
249
-
250
- return router;
251
- }