1
1
import { BaseTool } from "../tools/BaseTool.js" ;
2
- import { fileURLToPath } from "url" ;
3
2
import { dirname , join } from "path" ;
4
3
import { promises as fs } from "fs" ;
4
+ import { statSync } from "fs" ;
5
+ import { fileURLToPath } from "url" ;
5
6
import { cwd } from "process" ;
7
+ import { logger } from "./Logger.js" ;
6
8
7
9
export interface ToolLoaderOptions {
8
10
toolsDir ?: string ;
@@ -15,75 +17,137 @@ export class ToolLoader {
15
17
16
18
constructor ( options : ToolLoaderOptions = { } ) {
17
19
this . exclude = options . exclude || [ "BaseTool.js" , "*.test.js" , "*.spec.js" ] ;
18
- this . toolsDir = options . toolsDir || this . findDefaultToolsDir ( ) ;
20
+ this . toolsDir = this . resolveToolsDir ( options . toolsDir ) ;
21
+ logger . debug ( `Initialized ToolLoader with directory: ${ this . toolsDir } ` ) ;
19
22
}
20
23
21
- private findDefaultToolsDir ( ) : string {
22
- // Use current working directory + dist/tools as default
23
- return join ( cwd ( ) , "dist" , "tools" ) ;
24
+ private resolveToolsDir ( toolsDir ?: string ) : string {
25
+ if ( toolsDir ) {
26
+ logger . debug ( `Using provided tools directory: ${ toolsDir } ` ) ;
27
+ return toolsDir ;
28
+ }
29
+
30
+ const currentFilePath = fileURLToPath ( import . meta. url ) ;
31
+ const currentDir = dirname ( currentFilePath ) ;
32
+ const possiblePaths = [
33
+ join ( currentDir , ".." , "tools" ) ,
34
+ join ( currentDir , ".." , ".." , "tools" ) ,
35
+ join ( cwd ( ) , "dist" , "tools" ) ,
36
+ join ( cwd ( ) , "build" , "tools" ) ,
37
+ join ( cwd ( ) , "tools" ) ,
38
+ ] ;
39
+
40
+ logger . debug (
41
+ `Searching for tools in possible paths:\n${ possiblePaths . join ( "\n" ) } `
42
+ ) ;
43
+
44
+ for ( const path of possiblePaths ) {
45
+ try {
46
+ if ( statSync ( path ) . isDirectory ( ) ) {
47
+ logger . debug ( `Found existing tools directory: ${ path } ` ) ;
48
+ return path ;
49
+ }
50
+ } catch ( e ) {
51
+ logger . debug ( `Path ${ path } not accessible` ) ;
52
+ }
53
+ }
54
+
55
+ const fallbackPath = join ( cwd ( ) , "dist" , "tools" ) ;
56
+ logger . debug (
57
+ `No valid tools directory found, falling back to: ${ fallbackPath } `
58
+ ) ;
59
+ return fallbackPath ;
24
60
}
25
61
26
62
private isToolFile ( file : string ) : boolean {
27
63
if ( ! file . endsWith ( ".js" ) ) return false ;
28
- return ! this . exclude . some ( ( pattern ) => {
64
+ const isExcluded = this . exclude . some ( ( pattern ) => {
29
65
if ( pattern . includes ( "*" ) ) {
30
66
const regex = new RegExp ( pattern . replace ( "*" , ".*" ) ) ;
31
67
return regex . test ( file ) ;
32
68
}
33
69
return file === pattern ;
34
70
} ) ;
71
+
72
+ logger . debug (
73
+ `Checking file ${ file } : ${ isExcluded ? "excluded" : "included" } `
74
+ ) ;
75
+ return ! isExcluded ;
35
76
}
36
77
37
78
private validateTool ( tool : any ) : tool is BaseTool {
38
- return Boolean (
79
+ const isValid = Boolean (
39
80
tool &&
40
81
typeof tool . name === "string" &&
41
82
tool . toolDefinition &&
42
83
typeof tool . toolCall === "function"
43
84
) ;
85
+
86
+ if ( isValid ) {
87
+ logger . debug ( `Validated tool: ${ tool . name } ` ) ;
88
+ } else {
89
+ logger . warn ( `Invalid tool found: missing required properties` ) ;
90
+ }
91
+
92
+ return isValid ;
44
93
}
45
94
46
95
async loadTools ( ) : Promise < BaseTool [ ] > {
47
96
try {
48
- console . log ( `Loading tools from directory: ${ this . toolsDir } ` ) ;
97
+ logger . debug ( `Attempting to load tools from: ${ this . toolsDir } ` ) ;
98
+
99
+ let stats ;
100
+ try {
101
+ stats = await fs . stat ( this . toolsDir ) ;
102
+ } catch ( error ) {
103
+ logger . error ( `Error accessing tools directory: ${ error } ` ) ;
104
+ return [ ] ;
105
+ }
106
+
107
+ if ( ! stats . isDirectory ( ) ) {
108
+ logger . error ( `Path is not a directory: ${ this . toolsDir } ` ) ;
109
+ return [ ] ;
110
+ }
111
+
49
112
const files = await fs . readdir ( this . toolsDir ) ;
50
- console . log ( `Found files: ${ files . join ( ", " ) } ` ) ;
51
-
52
- const toolPromises = files
53
- . filter ( ( file ) => this . isToolFile ( file ) )
54
- . map ( async ( file ) => {
55
- try {
56
- const fullPath = join ( this . toolsDir , file ) ;
57
- console . log ( `Loading tool from: ${ fullPath } ` ) ;
58
- const { default : ToolClass } = await import ( `file://${ fullPath } ` ) ;
59
-
60
- if ( ! ToolClass ) {
61
- console . log ( `No default export found in ${ file } ` ) ;
62
- return null ;
63
- }
64
-
65
- const tool = new ToolClass ( ) ;
66
- if ( this . validateTool ( tool ) ) {
67
- console . log ( `Successfully loaded tool: ${ tool . name } ` ) ;
68
- return tool ;
69
- }
70
- console . log ( `Invalid tool found in ${ file } ` ) ;
71
- return null ;
72
- } catch ( error ) {
73
- console . error ( `Error loading tool ${ file } :` , error ) ;
74
- return null ;
113
+ logger . debug ( `Found files in directory: ${ files . join ( ", " ) } ` ) ;
114
+
115
+ const tools : BaseTool [ ] = [ ] ;
116
+
117
+ for ( const file of files ) {
118
+ if ( ! this . isToolFile ( file ) ) {
119
+ continue ;
120
+ }
121
+
122
+ try {
123
+ const fullPath = join ( this . toolsDir , file ) ;
124
+ logger . debug ( `Attempting to load tool from: ${ fullPath } ` ) ;
125
+
126
+ const importPath = `file://${ fullPath } ` ;
127
+ const { default : ToolClass } = await import ( importPath ) ;
128
+
129
+ if ( ! ToolClass ) {
130
+ logger . warn ( `No default export found in ${ file } ` ) ;
131
+ continue ;
75
132
}
76
- } ) ;
77
133
78
- const tools = ( await Promise . all ( toolPromises ) ) . filter (
79
- Boolean
80
- ) as BaseTool [ ] ;
81
- console . log (
82
- `Loaded ${ tools . length } tools: ${ tools . map ( ( t ) => t . name ) . join ( ", " ) } `
134
+ const tool = new ToolClass ( ) ;
135
+ if ( this . validateTool ( tool ) ) {
136
+ tools . push ( tool ) ;
137
+ }
138
+ } catch ( error ) {
139
+ logger . error ( `Error loading tool ${ file } : ${ error } ` ) ;
140
+ }
141
+ }
142
+
143
+ logger . debug (
144
+ `Successfully loaded ${ tools . length } tools: ${ tools
145
+ . map ( ( t ) => t . name )
146
+ . join ( ", " ) } `
83
147
) ;
84
148
return tools ;
85
149
} catch ( error ) {
86
- console . error ( `Failed to load tools from ${ this . toolsDir } :` , error ) ;
150
+ logger . error ( `Failed to load tools: ${ error } ` ) ;
87
151
return [ ] ;
88
152
}
89
153
}
0 commit comments