@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.
- package/README.md +84 -645
- package/bin/mcp-shark.js +30 -36
- package/mcp-server/index.js +115 -0
- package/mcp-server/lib/auditor/audit.js +22 -38
- package/mcp-server/lib/common/error.js +1 -1
- package/mcp-server/lib/server/external/all.js +5 -6
- package/mcp-server/lib/server/external/config.js +1 -3
- package/mcp-server/lib/server/external/kv.js +4 -12
- package/mcp-server/lib/server/external/single/request.js +3 -6
- package/mcp-server/lib/server/external/single/run.js +8 -19
- package/mcp-server/lib/server/internal/handlers/prompts-get.js +3 -13
- package/mcp-server/lib/server/internal/handlers/prompts-list.js +2 -6
- package/mcp-server/lib/server/internal/handlers/resources-list.js +2 -6
- package/mcp-server/lib/server/internal/handlers/resources-read.js +3 -12
- package/mcp-server/lib/server/internal/handlers/tools-call.js +3 -9
- package/mcp-server/lib/server/internal/handlers/tools-list.js +2 -2
- package/mcp-server/lib/server/internal/run.js +4 -16
- package/mcp-server/lib/server/internal/server.js +6 -7
- package/mcp-server/lib/server/internal/session.js +2 -15
- package/mcp-server/mcp-shark.js +16 -66
- package/package.json +23 -38
- package/shared/logger.js +90 -0
- package/ui/dist/assets/index-Cc-IUa83.css +1 -0
- package/ui/dist/assets/index-srLDlk97.js +35 -0
- package/ui/dist/index.html +17 -0
- package/ui/dist/og-image.png +0 -0
- package/ui/server/routes/backups/deleteBackup.js +54 -0
- package/ui/server/routes/backups/index.js +15 -0
- package/ui/server/routes/backups/listBackups.js +75 -0
- package/ui/server/routes/backups/restoreBackup.js +83 -0
- package/ui/server/routes/backups/viewBackup.js +47 -0
- package/ui/server/routes/composite/index.js +46 -0
- package/ui/server/routes/composite/servers.js +18 -0
- package/ui/server/routes/composite/setup.js +129 -0
- package/ui/server/routes/composite/status.js +7 -0
- package/ui/server/routes/composite/stop.js +39 -0
- package/ui/server/routes/composite/utils.js +45 -0
- package/ui/server/routes/config.js +34 -30
- package/ui/server/routes/conversations.js +3 -3
- package/ui/server/routes/help.js +2 -2
- package/ui/server/routes/logs.js +5 -5
- package/ui/server/routes/playground.js +45 -47
- package/ui/server/routes/requests.js +112 -108
- package/ui/server/routes/sessions.js +4 -4
- package/ui/server/routes/settings.js +199 -0
- package/ui/server/routes/smartscan/discover.js +7 -6
- package/ui/server/routes/smartscan/scans/clearCache.js +3 -2
- package/ui/server/routes/smartscan/scans/createBatchScans.js +4 -3
- package/ui/server/routes/smartscan/scans/createScan.js +2 -1
- package/ui/server/routes/smartscan/scans/getCachedResults.js +2 -1
- package/ui/server/routes/smartscan/scans/getScan.js +2 -1
- package/ui/server/routes/smartscan/scans/listScans.js +5 -4
- package/ui/server/routes/smartscan/scans.js +3 -3
- package/ui/server/routes/smartscan/token.js +4 -3
- package/ui/server/routes/smartscan/transport.js +1 -1
- package/ui/server/routes/smartscan.js +1 -1
- package/ui/server/routes/statistics.js +13 -10
- package/ui/server/utils/config-update.js +7 -6
- package/ui/server/utils/config.js +4 -4
- package/ui/server/utils/logger.js +2 -0
- package/ui/server/utils/paths.js +210 -2
- package/ui/server/utils/port.js +2 -2
- package/ui/server/utils/process.js +0 -67
- package/ui/server/utils/scan-cache/all-results.js +76 -59
- package/ui/server/utils/scan-cache/directory.js +1 -1
- package/ui/server/utils/scan-cache/file-operations.js +19 -16
- package/ui/server/utils/scan-cache/server-operations.js +14 -9
- package/ui/server/utils/serialization.js +9 -3
- package/ui/server/utils/smartscan-token.js +4 -3
- package/ui/server.js +86 -41
- package/ui/src/App.jsx +5 -5
- package/ui/src/CompositeLogs.jsx +20 -20
- package/ui/src/CompositeSetup.jsx +9 -9
- package/ui/src/HelpGuide/HelpGuideFooter.jsx +1 -0
- package/ui/src/HelpGuide/HelpGuideHeader.jsx +2 -1
- package/ui/src/HelpGuide.jsx +17 -4
- package/ui/src/IntroTour.jsx +19 -5
- package/ui/src/LogDetail.jsx +1 -0
- package/ui/src/LogTable.jsx +24 -6
- package/ui/src/PacketDetail.jsx +21 -16
- package/ui/src/PacketFilters.jsx +29 -14
- package/ui/src/PacketList.jsx +4 -5
- package/ui/src/SmartScan.jsx +5 -5
- package/ui/src/TabNavigation.jsx +5 -5
- package/ui/src/components/App/HelpButton.jsx +4 -0
- package/ui/src/components/App/TrafficTab.jsx +4 -4
- package/ui/src/components/App/useAppState.js +118 -24
- package/ui/src/components/BackupList.jsx +6 -2
- package/ui/src/components/CollapsibleSection.jsx +16 -2
- package/ui/src/components/ConfigViewerModal.jsx +17 -3
- package/ui/src/components/ConfirmationModal.jsx +20 -3
- package/ui/src/components/DetailsTab/BodySection.jsx +3 -1
- package/ui/src/components/DetailsTab/CollapsibleRequestResponse.jsx +14 -3
- package/ui/src/components/DetailsTab/InfoSection.jsx +4 -2
- package/ui/src/components/DetailsTab/RequestDetailsSection.jsx +7 -5
- package/ui/src/components/DetailsTab/ResponseDetailsSection.jsx +7 -5
- package/ui/src/components/DetectedPathsList.jsx +5 -2
- package/ui/src/components/FileInput.jsx +3 -1
- package/ui/src/components/GroupHeader.jsx +14 -0
- package/ui/src/components/GroupedByMcpView.jsx +3 -4
- package/ui/src/components/GroupedByServerView.jsx +1 -1
- package/ui/src/components/GroupedBySessionView.jsx +1 -1
- package/ui/src/components/HexTab.jsx +17 -4
- package/ui/src/components/LogsToolbar.jsx +3 -1
- package/ui/src/components/McpPlayground/LoadingModal.jsx +7 -3
- package/ui/src/components/McpPlayground/PromptsSection/PromptCallPanel.jsx +5 -0
- package/ui/src/components/McpPlayground/PromptsSection/PromptItem.jsx +6 -2
- package/ui/src/components/McpPlayground/PromptsSection/PromptsList.jsx +4 -4
- package/ui/src/components/McpPlayground/PromptsSection.jsx +2 -1
- package/ui/src/components/McpPlayground/ResourcesSection/ResourceCallPanel.jsx +3 -0
- package/ui/src/components/McpPlayground/ResourcesSection/ResourceItem.jsx +6 -2
- package/ui/src/components/McpPlayground/ResourcesSection/ResourcesList.jsx +4 -4
- package/ui/src/components/McpPlayground/ResourcesSection.jsx +2 -1
- package/ui/src/components/McpPlayground/ToolsSection/ToolCallPanel.jsx +5 -0
- package/ui/src/components/McpPlayground/ToolsSection/ToolItem.jsx +6 -2
- package/ui/src/components/McpPlayground/ToolsSection/ToolsList.jsx +4 -4
- package/ui/src/components/McpPlayground/ToolsSection.jsx +2 -1
- package/ui/src/components/McpPlayground/hooks/useMcpDataLoader.js +9 -9
- package/ui/src/components/McpPlayground/hooks/useMcpRequest.js +10 -5
- package/ui/src/components/McpPlayground/hooks/useMcpServerStatus.js +43 -23
- package/ui/src/components/McpPlayground/useMcpPlayground.js +72 -44
- package/ui/src/components/McpPlayground.jsx +5 -2
- package/ui/src/components/PacketDetailHeader.jsx +8 -3
- package/ui/src/components/PacketFilters/ExportControls.jsx +2 -1
- package/ui/src/components/PacketFilters/FilterInput.jsx +1 -1
- package/ui/src/components/RawTab.jsx +15 -2
- package/ui/src/components/RequestRow/OrphanedResponseRow.jsx +10 -2
- package/ui/src/components/RequestRow/RequestRowMain.jsx +12 -3
- package/ui/src/components/RequestRow/ResponseRow.jsx +11 -3
- package/ui/src/components/RequestRow.jsx +17 -9
- package/ui/src/components/ServerControl.jsx +3 -1
- package/ui/src/components/ServiceSelector.jsx +2 -0
- package/ui/src/components/SmartScan/AnalysisResult.jsx +2 -2
- package/ui/src/components/SmartScan/BatchResultsDisplay/BatchResultItem.jsx +2 -1
- package/ui/src/components/SmartScan/BatchResultsDisplay/BatchResultsHeader.jsx +1 -1
- package/ui/src/components/SmartScan/BatchResultsDisplay.jsx +9 -3
- package/ui/src/components/SmartScan/EmptyState.jsx +3 -0
- package/ui/src/components/SmartScan/ErrorDisplay.jsx +4 -2
- package/ui/src/components/SmartScan/ExpandableSection.jsx +4 -0
- package/ui/src/components/SmartScan/FindingsTable.jsx +46 -42
- package/ui/src/components/SmartScan/ListViewContent.jsx +2 -2
- package/ui/src/components/SmartScan/NotablePatternsSection.jsx +13 -8
- package/ui/src/components/SmartScan/OverallSummarySection.jsx +36 -29
- package/ui/src/components/SmartScan/RecommendationsSection.jsx +10 -8
- package/ui/src/components/SmartScan/ScanDetailHeader.jsx +2 -1
- package/ui/src/components/SmartScan/ScanDetailView.jsx +4 -4
- package/ui/src/components/SmartScan/ScanListView/ScanListHeader.jsx +2 -1
- package/ui/src/components/SmartScan/ScanListView/ScanListItem.jsx +15 -1
- package/ui/src/components/SmartScan/ScanOverviewSection.jsx +3 -1
- package/ui/src/components/SmartScan/ScanResultsDisplay.jsx +1 -1
- package/ui/src/components/SmartScan/ScanViewContent.jsx +2 -2
- package/ui/src/components/SmartScan/ScanningProgress.jsx +4 -2
- package/ui/src/components/SmartScan/ServerInfoSection.jsx +3 -1
- package/ui/src/components/SmartScan/ServerSelectionRow.jsx +4 -2
- package/ui/src/components/SmartScan/SingleResultDisplay.jsx +5 -3
- package/ui/src/components/SmartScan/SmartScanControls.jsx +11 -7
- package/ui/src/components/SmartScan/SmartScanHeader.jsx +1 -1
- package/ui/src/components/SmartScan/ViewModeTabs.jsx +2 -0
- package/ui/src/components/SmartScan/hooks/useCacheManagement.js +1 -2
- package/ui/src/components/SmartScan/hooks/useMcpDiscovery.js +22 -26
- package/ui/src/components/SmartScan/hooks/useScanList.js +10 -9
- package/ui/src/components/SmartScan/hooks/useScanOperations.js +23 -14
- package/ui/src/components/SmartScan/hooks/useServerStatus.js +2 -2
- package/ui/src/components/SmartScan/hooks/useTokenManagement.js +2 -2
- package/ui/src/components/SmartScan/scanDataUtils.js +22 -17
- package/ui/src/components/SmartScan/useSmartScan.js +4 -4
- package/ui/src/components/SmartScan/utils.js +3 -1
- package/ui/src/components/SmartScanIcons.jsx +6 -3
- package/ui/src/components/TabNavigation/DesktopTabs.jsx +8 -3
- package/ui/src/components/TabNavigation/MobileDropdown.jsx +3 -1
- package/ui/src/components/TabNavigation.jsx +8 -3
- package/ui/src/components/TabNavigationIcons.jsx +4 -4
- package/ui/src/components/TourOverlay.jsx +1 -1
- package/ui/src/components/TourTooltip/TourTooltipButtons.jsx +3 -0
- package/ui/src/components/TourTooltip/TourTooltipHeader.jsx +1 -0
- package/ui/src/components/TourTooltip/TourTooltipIcons.jsx +9 -0
- package/ui/src/components/TourTooltip/useTooltipPosition.js +63 -36
- package/ui/src/components/TourTooltip.jsx +11 -3
- package/ui/src/components/ViewModeTabs.jsx +3 -1
- package/ui/src/config/tourSteps.jsx +0 -2
- package/ui/src/hooks/useAnimation.js +15 -12
- package/ui/src/hooks/useConfigManagement.js +8 -8
- package/ui/src/hooks/useServiceExtraction.js +1 -1
- package/ui/src/index.css +3 -8
- package/ui/src/theme.js +3 -3
- package/ui/src/utils/hexUtils.js +11 -5
- package/ui/src/utils/mcpGroupingUtils.js +18 -10
- package/ui/src/utils/requestPairing.js +89 -0
- package/ui/src/utils/requestUtils.js +32 -101
- package/ui/vite.config.js +1 -1
- package/mcp-server/.editorconfig +0 -15
- package/mcp-server/.prettierignore +0 -11
- package/mcp-server/.prettierrc +0 -12
- package/mcp-server/README.md +0 -280
- package/mcp-server/commitlint.config.cjs +0 -42
- package/mcp-server/eslint.config.js +0 -131
- package/mcp-server/package-lock.json +0 -4784
- package/mcp-server/package.json +0 -30
- package/ui/README.md +0 -212
- package/ui/package-lock.json +0 -3574
- package/ui/package.json +0 -12
- package/ui/paths.js +0 -282
- package/ui/server/routes/backups.js +0 -251
- package/ui/server/routes/composite.js +0 -260
package/ui/package.json
DELETED
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
|
-
}
|