@@ -2,25 +2,37 @@ import fs from 'fs'
22import os from 'os'
33import path from 'path'
44import { pathToFileURL } from 'url'
5+ import { build } from 'esbuild'
6+ import { createHash } from 'crypto'
7+ import { builtinModules } from 'module'
58
69export let loadedAgents : Record < string , any > = { }
710
8- const getAllTsFiles = ( dir : string ) : string [ ] => {
11+ const agentFileExtensions = new Set ( [
12+ '.ts' ,
13+ '.tsx' ,
14+ '.js' ,
15+ '.mjs' ,
16+ '.cjs' ,
17+ ] )
18+
19+ const getAllAgentFiles = ( dir : string ) : string [ ] => {
920 const files : string [ ] = [ ]
1021 try {
1122 const entries = fs . readdirSync ( dir , { withFileTypes : true } )
1223 for ( const entry of entries ) {
1324 const fullPath = path . join ( dir , entry . name )
1425 if ( entry . isDirectory ( ) ) {
15- files . push ( ...getAllTsFiles ( fullPath ) )
26+ files . push ( ...getAllAgentFiles ( fullPath ) )
1627 continue
1728 }
18- const isTsFile =
29+ const extension = path . extname ( entry . name ) . toLowerCase ( )
30+ const isAgentFile =
1931 entry . isFile ( ) &&
20- entry . name . endsWith ( '.ts' ) &&
32+ agentFileExtensions . has ( extension ) &&
2133 ! entry . name . endsWith ( '.d.ts' ) &&
2234 ! entry . name . endsWith ( '.test.ts' )
23- if ( isTsFile ) {
35+ if ( isAgentFile ) {
2436 files . push ( fullPath )
2537 }
2638 }
@@ -47,16 +59,18 @@ export async function loadLocalAgents({
4759 loadedAgents = { }
4860
4961 const agentDirs = agentsPath ? [ agentsPath ] : getDefaultAgentDirs ( )
50- const allTsFiles = agentDirs . flatMap ( ( dir ) => getAllTsFiles ( dir ) )
62+ const allAgentFiles = agentDirs . flatMap ( ( dir ) => getAllAgentFiles ( dir ) )
5163
52- if ( allTsFiles . length === 0 ) {
64+ if ( allAgentFiles . length === 0 ) {
5365 return loadedAgents
5466 }
5567
56- for ( const fullPath of allTsFiles ) {
68+ for ( const fullPath of allAgentFiles ) {
5769 try {
58- const moduleUrl = `${ pathToFileURL ( fullPath ) . href } ?update=${ Date . now ( ) } `
59- const agentModule = await import ( moduleUrl )
70+ const agentModule = await importAgentModule ( fullPath , verbose )
71+ if ( ! agentModule ) {
72+ continue
73+ }
6074 const agentDefinition = agentModule . default ?? agentModule
6175
6276 if ( ! agentDefinition ?. id || ! agentDefinition ?. model ) {
@@ -87,3 +101,72 @@ export async function loadLocalAgents({
87101
88102 return loadedAgents
89103}
104+
105+ async function importAgentModule (
106+ fullPath : string ,
107+ verbose : boolean ,
108+ ) : Promise < any | null > {
109+ const extension = path . extname ( fullPath ) . toLowerCase ( )
110+ const urlVersion = `?update=${ Date . now ( ) } `
111+
112+ if ( extension === '.ts' || extension === '.tsx' ) {
113+ const compiledPath = await transpileAgent ( fullPath , verbose )
114+ if ( ! compiledPath ) {
115+ return null
116+ }
117+ return import ( `${ pathToFileURL ( compiledPath ) . href } ${ urlVersion } ` )
118+ }
119+
120+ return import ( `${ pathToFileURL ( fullPath ) . href } ${ urlVersion } ` )
121+ }
122+
123+ async function transpileAgent (
124+ fullPath : string ,
125+ verbose : boolean ,
126+ ) : Promise < string | null > {
127+ try {
128+ const result = await build ( {
129+ entryPoints : [ fullPath ] ,
130+ bundle : true ,
131+ format : 'esm' ,
132+ platform : 'node' ,
133+ target : 'node18' ,
134+ write : false ,
135+ logLevel : verbose ? 'info' : 'silent' ,
136+ sourcemap : 'inline' ,
137+ packages : 'external' ,
138+ external : [
139+ ...builtinModules ,
140+ ...builtinModules . map ( ( mod ) => `node:${ mod } ` ) ,
141+ '@codebuff/*' ,
142+ ] ,
143+ } )
144+
145+ const jsOutput = result . outputFiles ?. find ( ( file ) =>
146+ file . path . endsWith ( '.js' ) ,
147+ )
148+ if ( ! jsOutput ?. text ) {
149+ if ( verbose ) {
150+ console . error ( `Failed to transpile agent (no output): ${ fullPath } ` )
151+ }
152+ return null
153+ }
154+
155+ const hash = createHash ( 'sha1' ) . update ( fullPath ) . digest ( 'hex' )
156+ const tempDir = path . join ( os . tmpdir ( ) , 'codebuff-agents' )
157+ const compiledPath = path . join ( tempDir , `${ hash } .mjs` )
158+
159+ await fs . promises . mkdir ( tempDir , { recursive : true } )
160+ await fs . promises . writeFile ( compiledPath , jsOutput . text , 'utf8' )
161+
162+ return compiledPath
163+ } catch ( error ) {
164+ if ( verbose ) {
165+ console . error (
166+ `Error transpiling agent ${ fullPath } :` ,
167+ error instanceof Error ? error . message : error ,
168+ )
169+ }
170+ return null
171+ }
172+ }
0 commit comments