1
+ import fs from "fs" ;
2
+ import path from "path" ;
3
+
4
+ // Function to parse agents.ts file and extract agent keys without executing
5
+ function parseAgentsFile ( ) : Array < { id : string , agentKeys : string [ ] } > {
6
+ const agentsFilePath = path . join ( __dirname , '../src/agents.ts' ) ;
7
+ const agentsContent = fs . readFileSync ( agentsFilePath , 'utf8' ) ;
8
+
9
+ const agentConfigs : Array < { id : string , agentKeys : string [ ] } > = [ ] ;
10
+
11
+ // Split the content to process each agent configuration individually
12
+ const agentBlocks = agentsContent . split ( / (? = \s * { \s * i d : \s * [ " ' ] ) / ) ;
13
+
14
+ for ( const block of agentBlocks ) {
15
+ // Extract the ID
16
+ const idMatch = block . match ( / i d : \s * [ " ' ] ( [ ^ " ' ] + ) [ " ' ] / ) ;
17
+ if ( ! idMatch ) continue ;
18
+
19
+ const id = idMatch [ 1 ] ;
20
+
21
+ // Find the return object by looking for the pattern and then manually parsing balanced braces
22
+ const returnMatch = block . match ( / a g e n t s : \s * a s y n c \s * \( \) \s * = > \s * { \s * r e t u r n \s * { / ) ;
23
+ if ( ! returnMatch ) continue ;
24
+
25
+ const startIndex = returnMatch . index ! + returnMatch [ 0 ] . length ;
26
+ const returnObjectContent = extractBalancedBraces ( block , startIndex ) ;
27
+
28
+
29
+ // Extract keys from the return object - only capture keys that are followed by a colon and then 'new'
30
+ // This ensures we only get the top-level keys like "agentic_chat: new ..." not nested keys like "url: ..."
31
+ const keyRegex = / ^ \s * ( \w + ) : \s * n e w \s + \w + / gm;
32
+ const keys : string [ ] = [ ] ;
33
+ let keyMatch ;
34
+ while ( ( keyMatch = keyRegex . exec ( returnObjectContent ) ) !== null ) {
35
+ keys . push ( keyMatch [ 1 ] ) ;
36
+ }
37
+
38
+ agentConfigs . push ( { id, agentKeys : keys } ) ;
39
+ }
40
+
41
+ return agentConfigs ;
42
+ }
43
+
44
+ // Helper function to extract content between balanced braces
45
+ function extractBalancedBraces ( text : string , startIndex : number ) : string {
46
+ let braceCount = 0 ;
47
+ let i = startIndex ;
48
+
49
+ while ( i < text . length ) {
50
+ if ( text [ i ] === '{' ) {
51
+ braceCount ++ ;
52
+ } else if ( text [ i ] === '}' ) {
53
+ if ( braceCount === 0 ) {
54
+ // Found the closing brace for the return object
55
+ return text . substring ( startIndex , i ) ;
56
+ }
57
+ braceCount -- ;
58
+ }
59
+ i ++ ;
60
+ }
61
+
62
+ return '' ;
63
+ }
64
+
65
+ const agentConfigs = parseAgentsFile ( ) ;
66
+ console . log ( "Loaded agents:" , agentConfigs . length ) ;
67
+
68
+ const featureFiles = [ "page.tsx" , "style.css" , "README.mdx" ]
69
+
70
+ function getFile ( _filePath : string | undefined , _fileName ?: string ) {
71
+ if ( ! _filePath ) {
72
+ console . warn ( `File path is undefined, skipping.` ) ;
73
+ return { }
74
+ }
75
+ const fileName = _fileName ?? _filePath . split ( '/' ) . pop ( ) ?? ''
76
+ const filePath = _fileName ? path . join ( _filePath , fileName ) : _filePath ;
77
+ if ( ! fs . existsSync ( filePath ) ) {
78
+ console . warn ( `File not found: ${ filePath } , skipping.` ) ;
79
+ return { }
80
+ }
81
+
82
+ try {
83
+ const content = fs . readFileSync ( filePath , "utf8" ) ;
84
+ const extension = fileName . split ( "." ) . pop ( ) ;
85
+ let language = extension ;
86
+ if ( extension === "py" ) language = "python" ;
87
+ else if ( extension === "css" ) language = "css" ;
88
+ else if ( extension === "md" || extension === "mdx" ) language = "markdown" ;
89
+ else if ( extension === "tsx" ) language = "typescript" ;
90
+ else if ( extension === "js" ) language = "javascript" ;
91
+ else if ( extension === "json" ) language = "json" ;
92
+ else if ( extension === "yaml" || extension === "yml" ) language = "yaml" ;
93
+ else if ( extension === "toml" ) language = "toml" ;
94
+
95
+ return {
96
+ name : fileName ,
97
+ content,
98
+ // path: path.join(demoIdWithFramework, fileName), // Store relative path within agent/demo
99
+ language,
100
+ type : 'file'
101
+ }
102
+ } catch ( error ) {
103
+ console . error ( `Error reading file ${ filePath } :` , error ) ;
104
+ }
105
+ }
106
+
107
+ function getFeatureFrontendFiles ( featureId : string ) {
108
+ const featurePath = path . join ( __dirname , `../src/app/[integrationId]/feature/${ featureId as string } ` ) ;
109
+ const retrievedFiles = [ ]
110
+
111
+ for ( const fileName of featureFiles ) {
112
+ retrievedFiles . push ( getFile ( featurePath , fileName ) )
113
+ }
114
+
115
+ return retrievedFiles ;
116
+ }
117
+
118
+ const integrationsFolderPath = '../../../integrations'
119
+ const agentFilesMapper : Record < string , ( agentKeys : string [ ] ) => Record < string , string > > = {
120
+ 'middleware-starter' : ( ) => ( {
121
+ agentic_chat : path . join ( __dirname , integrationsFolderPath , `/middleware-starter/src/index.ts` )
122
+ } ) ,
123
+ 'pydantic-ai' : ( agentKeys : string [ ] ) => {
124
+ return agentKeys . reduce ( ( acc , agentId ) => ( {
125
+ ...acc ,
126
+ [ agentId ] : `https://github.com/pydantic/pydantic-ai/blob/main/examples/pydantic_ai_examples/ag_ui/api/${ agentId } .py`
127
+ } ) , { } )
128
+ } ,
129
+ 'server-starter' : ( ) => ( {
130
+ agentic_chat : path . join ( __dirname , integrationsFolderPath , `/server-starter/server/python/example_server/__init__.py` )
131
+ } ) ,
132
+ 'server-starter-all-features' : ( agentKeys : string [ ] ) => {
133
+ return agentKeys . reduce ( ( acc , agentId ) => ( {
134
+ ...acc ,
135
+ [ agentId ] : path . join ( __dirname , integrationsFolderPath , `/server-starter/server/python/example_server/${ agentId } .py` )
136
+ } ) , { } )
137
+ } ,
138
+ 'mastra' : ( ) => ( {
139
+ agentic_chat : path . join ( __dirname , integrationsFolderPath , `/mastra/example/src/mastra/agents/weather-agent.ts` )
140
+ } ) ,
141
+ 'mastra-agent-lock' : ( ) => ( {
142
+ agentic_chat : path . join ( __dirname , integrationsFolderPath , `/mastra/example/src/mastra/agents/weather-agent.ts` )
143
+ } ) ,
144
+ 'vercel-ai-sdk' : ( ) => ( {
145
+ agentic_chat : path . join ( __dirname , integrationsFolderPath , `/vercel-ai-sdk/src/index.ts` )
146
+ } ) ,
147
+ 'langgraph' : ( agentKeys : string [ ] ) => {
148
+ return agentKeys . reduce ( ( acc , agentId ) => ( {
149
+ ...acc ,
150
+ [ agentId ] : path . join ( __dirname , integrationsFolderPath , `/langgraph/examples/agents/${ agentId } /agent.py` )
151
+ } ) , { } )
152
+ } ,
153
+ 'langgraph-fastapi' : ( agentKeys : string [ ] ) => {
154
+ return agentKeys . reduce ( ( acc , agentId ) => ( {
155
+ ...acc ,
156
+ [ agentId ] : path . join ( __dirname , integrationsFolderPath , `/langgraph/python/ag_ui_langgraph/examples/agents/${ agentId } .py` )
157
+ } ) , { } )
158
+ } ,
159
+ 'agno' : ( ) => ( { } ) ,
160
+ 'llama-index' : ( agentKeys : string [ ] ) => {
161
+ return agentKeys . reduce ( ( acc , agentId ) => ( {
162
+ ...acc ,
163
+ [ agentId ] : path . join ( __dirname , integrationsFolderPath , `/llamaindex/server-py/server/routers/${ agentId } .py` )
164
+ } ) , { } )
165
+ } ,
166
+ 'crewai' : ( agentKeys : string [ ] ) => {
167
+ return agentKeys . reduce ( ( acc , agentId ) => ( {
168
+ ...acc ,
169
+ [ agentId ] : path . join ( __dirname , integrationsFolderPath , `/crewai/python/ag_ui_crewai/examples/${ agentId } .py` )
170
+ } ) , { } )
171
+ }
172
+ }
173
+
174
+ function runGenerateContent ( ) {
175
+ const result = { }
176
+ for ( const agentConfig of agentConfigs ) {
177
+ // Use the parsed agent keys instead of executing the agents function
178
+ const agentsPerFeatures = agentConfig . agentKeys
179
+
180
+ const agentFilePaths = agentFilesMapper [ agentConfig . id ] ( agentConfig . agentKeys )
181
+ // Per feature, assign all the frontend files like page.tsx as well as all agent files
182
+ agentsPerFeatures . forEach ( featureId => {
183
+ // @ts -expect-error -- redundant error about indexing of a new object.
184
+ result [ `${ agentConfig . id } ::${ featureId } ` ] = [
185
+ // Get all frontend files for the feature
186
+ ...getFeatureFrontendFiles ( featureId ) ,
187
+ // Get the agent (python/TS) file
188
+ getFile ( agentFilePaths [ featureId ] )
189
+ ]
190
+ } )
191
+ }
192
+
193
+ return result
194
+ }
195
+
196
+ // const result = {};
197
+ // const agentDemoBaseDir = path.join(__dirname, "../agent/demo");
198
+ //
199
+ // for (const demoIdWithFramework in config) {
200
+ // const demoFilesConfig = config[demoIdWithFramework];
201
+ // const demoDirPath = path.join(agentDemoBaseDir, demoIdWithFramework);
202
+ //
203
+ // if (!fs.existsSync(demoDirPath)) {
204
+ // console.warn(`Directory not found for demo: ${demoIdWithFramework}, skipping.`);
205
+ // continue;
206
+ // }
207
+ //
208
+ // result[demoIdWithFramework] = { files: [] };
209
+ //
210
+ // for (const fileName of demoFilesConfig) {
211
+ // const filePath = path.join(demoDirPath, fileName);
212
+ // if (!fs.existsSync(filePath)) {
213
+ // console.warn(`File not found: ${filePath}, skipping.`);
214
+ // continue;
215
+ // }
216
+ //
217
+ // try {
218
+ // const content = fs.readFileSync(filePath, "utf8");
219
+ // const extension = fileName.split(".").pop();
220
+ // let language = extension;
221
+ // if (extension === "py") language = "python";
222
+ // else if (extension === "css") language = "css";
223
+ // else if (extension === "md" || extension === "mdx") language = "markdown";
224
+ // else if (extension === "tsx") language = "typescript";
225
+ // else if (extension === "js") language = "javascript";
226
+ // else if (extension === "json") language = "json";
227
+ // else if (extension === "yaml" || extension === "yml") language = "yaml";
228
+ // else if (extension === "toml") language = "toml";
229
+ //
230
+ // result[demoIdWithFramework].files.push({
231
+ // name: fileName,
232
+ // content,
233
+ // path: path.join(demoIdWithFramework, fileName), // Store relative path within agent/demo
234
+ // language,
235
+ // type: 'file'
236
+ // });
237
+ // } catch (error) {
238
+ // console.error(`Error reading file ${filePath}:`, error);
239
+ // }
240
+ // }
241
+ // }
242
+ const result = runGenerateContent ( ) ;
243
+ fs . writeFileSync (
244
+ path . join ( __dirname , "../src/files.json" ) ,
245
+ JSON . stringify ( result , null , 2 )
246
+ ) ;
247
+
248
+ console . log ( "Successfully generated src/files.json" ) ;
0 commit comments