@@ -11,6 +11,116 @@ import { IncomingMessage } from "http";
11
11
import { Socket } from "net" ;
12
12
import os from "os" ;
13
13
14
+ /**
15
+ * Converts a file path to the appropriate format for the current platform
16
+ * Handles Windows, WSL, macOS and Linux path formats
17
+ *
18
+ * @param inputPath - The path to convert
19
+ * @returns The converted path appropriate for the current platform
20
+ */
21
+ function convertPathForCurrentPlatform ( inputPath : string ) : string {
22
+ const platform = os . platform ( ) ;
23
+
24
+ // If no path provided, return as is
25
+ if ( ! inputPath ) return inputPath ;
26
+
27
+ console . log ( `Converting path "${ inputPath } " for platform: ${ platform } ` ) ;
28
+
29
+ // Windows-specific conversion
30
+ if ( platform === "win32" ) {
31
+ // Convert forward slashes to backslashes
32
+ return inputPath . replace ( / \/ / g, "\\" ) ;
33
+ }
34
+
35
+ // Linux/Mac-specific conversion
36
+ if ( platform === "linux" || platform === "darwin" ) {
37
+ // Check if this is a Windows UNC path (starts with \\)
38
+ if ( inputPath . startsWith ( "\\\\" ) || inputPath . includes ( "\\" ) ) {
39
+ // Check if this is a WSL path (contains wsl.localhost or wsl$)
40
+ if ( inputPath . includes ( "wsl.localhost" ) || inputPath . includes ( "wsl$" ) ) {
41
+ // Extract the path after the distribution name
42
+ // Handle both \\wsl.localhost\Ubuntu\path and \\wsl$\Ubuntu\path formats
43
+ const parts = inputPath . split ( "\\" ) . filter ( ( part ) => part . length > 0 ) ;
44
+ console . log ( "Path parts:" , parts ) ;
45
+
46
+ // Find the index after the distribution name
47
+ const distNames = [
48
+ "Ubuntu" ,
49
+ "Debian" ,
50
+ "kali" ,
51
+ "openSUSE" ,
52
+ "SLES" ,
53
+ "Fedora" ,
54
+ ] ;
55
+
56
+ // Find the distribution name in the path
57
+ let distIndex = - 1 ;
58
+ for ( const dist of distNames ) {
59
+ const index = parts . findIndex (
60
+ ( part ) => part === dist || part . toLowerCase ( ) === dist . toLowerCase ( )
61
+ ) ;
62
+ if ( index !== - 1 ) {
63
+ distIndex = index ;
64
+ break ;
65
+ }
66
+ }
67
+
68
+ if ( distIndex !== - 1 && distIndex + 1 < parts . length ) {
69
+ // Reconstruct the path as a native Linux path
70
+ const linuxPath = "/" + parts . slice ( distIndex + 1 ) . join ( "/" ) ;
71
+ console . log (
72
+ `Converted Windows WSL path "${ inputPath } " to Linux path "${ linuxPath } "`
73
+ ) ;
74
+ return linuxPath ;
75
+ }
76
+
77
+ // If we couldn't find a distribution name but it's clearly a WSL path,
78
+ // try to extract everything after wsl.localhost or wsl$
79
+ const wslIndex = parts . findIndex (
80
+ ( part ) =>
81
+ part === "wsl.localhost" ||
82
+ part === "wsl$" ||
83
+ part . toLowerCase ( ) === "wsl.localhost" ||
84
+ part . toLowerCase ( ) === "wsl$"
85
+ ) ;
86
+
87
+ if ( wslIndex !== - 1 && wslIndex + 2 < parts . length ) {
88
+ // Skip the WSL prefix and distribution name
89
+ const linuxPath = "/" + parts . slice ( wslIndex + 2 ) . join ( "/" ) ;
90
+ console . log (
91
+ `Converted Windows WSL path "${ inputPath } " to Linux path "${ linuxPath } "`
92
+ ) ;
93
+ return linuxPath ;
94
+ }
95
+ }
96
+
97
+ // For non-WSL Windows paths, just normalize the slashes
98
+ const normalizedPath = inputPath
99
+ . replace ( / \\ \\ / g, "/" )
100
+ . replace ( / \\ / g, "/" ) ;
101
+ console . log (
102
+ `Converted Windows UNC path "${ inputPath } " to "${ normalizedPath } "`
103
+ ) ;
104
+ return normalizedPath ;
105
+ }
106
+
107
+ // Handle Windows drive letters (e.g., C:\path\to\file)
108
+ if ( / ^ [ A - Z ] : \\ / i. test ( inputPath ) ) {
109
+ // Convert Windows drive path to Linux/Mac compatible path
110
+ const normalizedPath = inputPath
111
+ . replace ( / ^ [ A - Z ] : \\ / i, "/" )
112
+ . replace ( / \\ / g, "/" ) ;
113
+ console . log (
114
+ `Converted Windows drive path "${ inputPath } " to "${ normalizedPath } "`
115
+ ) ;
116
+ return normalizedPath ;
117
+ }
118
+ }
119
+
120
+ // Return the original path if no conversion was needed or possible
121
+ return inputPath ;
122
+ }
123
+
14
124
// Function to get default downloads folder
15
125
function getDefaultDownloadsFolder ( ) : string {
16
126
const homeDir = os . homedir ( ) ;
@@ -36,6 +146,8 @@ let currentSettings = {
36
146
stringSizeLimit : 500 ,
37
147
maxLogSize : 20000 ,
38
148
screenshotPath : getDefaultDownloadsFolder ( ) ,
149
+ // Add server host configuration
150
+ serverHost : process . env . SERVER_HOST || "0.0.0.0" , // Default to all interfaces
39
151
} ;
40
152
41
153
// Add new storage for selected element
@@ -50,7 +162,7 @@ interface ScreenshotCallback {
50
162
const screenshotCallbacks = new Map < string , ScreenshotCallback > ( ) ;
51
163
52
164
const app = express ( ) ;
53
- const PORT = 3025 ;
165
+ const PORT = parseInt ( process . env . PORT || " 3025" , 10 ) ;
54
166
55
167
app . use ( cors ( ) ) ;
56
168
// Increase JSON body parser limit to 50MB to handle large screenshots
@@ -627,29 +739,62 @@ export class BrowserConnector {
627
739
console . log ( "Browser Connector: Received screenshot data, saving..." ) ;
628
740
console . log ( "Browser Connector: Custom path from extension:" , customPath ) ;
629
741
630
- // Determine target path
631
- const targetPath =
632
- customPath ||
633
- currentSettings . screenshotPath ||
634
- getDefaultDownloadsFolder ( ) ;
742
+ // Always prioritize the path from the Chrome extension
743
+ let targetPath = customPath ;
744
+
745
+ // If no path provided by extension, fall back to defaults
746
+ if ( ! targetPath ) {
747
+ targetPath =
748
+ currentSettings . screenshotPath || getDefaultDownloadsFolder ( ) ;
749
+ }
750
+
751
+ // Convert the path for the current platform
752
+ targetPath = convertPathForCurrentPlatform ( targetPath ) ;
753
+
635
754
console . log ( `Browser Connector: Using path: ${ targetPath } ` ) ;
636
755
637
756
if ( ! base64Data ) {
638
757
throw new Error ( "No screenshot data received from Chrome extension" ) ;
639
758
}
640
759
641
- fs . mkdirSync ( targetPath , { recursive : true } ) ;
760
+ try {
761
+ fs . mkdirSync ( targetPath , { recursive : true } ) ;
762
+ console . log ( `Browser Connector: Created directory: ${ targetPath } ` ) ;
763
+ } catch ( err ) {
764
+ console . error (
765
+ `Browser Connector: Error creating directory: ${ targetPath } ` ,
766
+ err
767
+ ) ;
768
+ throw new Error (
769
+ `Failed to create screenshot directory: ${
770
+ err instanceof Error ? err . message : String ( err )
771
+ } `
772
+ ) ;
773
+ }
642
774
643
775
const timestamp = new Date ( ) . toISOString ( ) . replace ( / [: .] / g, "-" ) ;
644
776
const filename = `screenshot-${ timestamp } .png` ;
645
777
const fullPath = path . join ( targetPath , filename ) ;
778
+ console . log ( `Browser Connector: Full screenshot path: ${ fullPath } ` ) ;
646
779
647
780
// Remove the data:image/png;base64, prefix if present
648
781
const cleanBase64 = base64Data . replace ( / ^ d a t a : i m a g e \/ p n g ; b a s e 6 4 , / , "" ) ;
649
782
650
783
// Save the file
651
- fs . writeFileSync ( fullPath , cleanBase64 , "base64" ) ;
652
- console . log ( `Browser Connector: Screenshot saved to: ${ fullPath } ` ) ;
784
+ try {
785
+ fs . writeFileSync ( fullPath , cleanBase64 , "base64" ) ;
786
+ console . log ( `Browser Connector: Screenshot saved to: ${ fullPath } ` ) ;
787
+ } catch ( err ) {
788
+ console . error (
789
+ `Browser Connector: Error saving screenshot to: ${ fullPath } ` ,
790
+ err
791
+ ) ;
792
+ throw new Error (
793
+ `Failed to save screenshot: ${
794
+ err instanceof Error ? err . message : String ( err )
795
+ } `
796
+ ) ;
797
+ }
653
798
654
799
res . json ( {
655
800
path : fullPath ,
@@ -670,8 +815,28 @@ export class BrowserConnector {
670
815
}
671
816
672
817
// Move the server creation before BrowserConnector instantiation
673
- const server = app . listen ( PORT , ( ) => {
674
- console . log ( `Aggregator listening on http://127.0.0.1:${ PORT } ` ) ;
818
+ const server = app . listen ( PORT , currentSettings . serverHost , ( ) => {
819
+ console . log ( `\n=== Browser Tools Server Started ===` ) ;
820
+ console . log (
821
+ `Aggregator listening on http://${ currentSettings . serverHost } :${ PORT } `
822
+ ) ;
823
+
824
+ // Log all available network interfaces for easier discovery
825
+ const networkInterfaces = os . networkInterfaces ( ) ;
826
+ console . log ( "\nAvailable on the following network addresses:" ) ;
827
+
828
+ Object . keys ( networkInterfaces ) . forEach ( ( interfaceName ) => {
829
+ const interfaces = networkInterfaces [ interfaceName ] ;
830
+ if ( interfaces ) {
831
+ interfaces . forEach ( ( iface ) => {
832
+ if ( ! iface . internal && iface . family === "IPv4" ) {
833
+ console . log ( ` - http://${ iface . address } :${ PORT } ` ) ;
834
+ }
835
+ } ) ;
836
+ }
837
+ } ) ;
838
+
839
+ console . log ( `\nFor local access use: http://localhost:${ PORT } ` ) ;
675
840
} ) ;
676
841
677
842
// Initialize the browser connector with the existing app AND server
0 commit comments