1+ #!/usr/bin/env node
2+
3+
4+ // Basic usage:
5+ // cd api && node test/create-agent-from-prompt.js burn_monitor.txt
6+
7+ // With custom user ID:
8+ // cd api && node test/create-agent-from-prompt.js burn_monitor.txt 68341a46ee1d93d1f7d18834
9+
10+ // With options:
11+ // cd api && node test/create-agent-from-prompt.js burn_monitor.txt 68341a46ee1d93d1f7d18834
12+ // '{"tools":["web_search","workflows"],"provider":"anthropic","model":"claude-3-sonnet"}'
13+
14+ const path = require ( 'path' ) ;
15+
16+ // Disable MeiliSearch BEFORE loading dotenv to avoid connection errors in script
17+ process . env . SEARCH = 'false' ;
18+ process . env . MEILI_NO_SYNC = 'true' ;
19+ delete process . env . MEILI_HOST ;
20+ delete process . env . MEILI_MASTER_KEY ;
21+
22+ require ( 'dotenv' ) . config ( { path : path . join ( __dirname , '../../.env' ) } ) ;
23+
24+ // Re-disable after dotenv loads
25+ process . env . SEARCH = 'false' ;
26+ process . env . MEILI_NO_SYNC = 'true' ;
27+ delete process . env . MEILI_HOST ;
28+ delete process . env . MEILI_MASTER_KEY ;
29+
30+ const fs = require ( 'fs' ) ;
31+ const { nanoid } = require ( 'nanoid' ) ;
32+
33+ const { connectDb } = require ( '../db/connect' ) ;
34+ const { Agent } = require ( '../db/models' ) ;
35+
36+ /**
37+ * Parse agent prompt file to extract Name, Description, Instructions, and Default prompts
38+ * @param {string } filePath - Path to the prompt file
39+ * @returns {Object } Parsed agent data
40+ */
41+ function parsePromptFile ( filePath ) {
42+ if ( ! fs . existsSync ( filePath ) ) {
43+ throw new Error ( `File not found: ${ filePath } ` ) ;
44+ }
45+
46+ const content = fs . readFileSync ( filePath , 'utf8' ) ;
47+ const lines = content . split ( '\n' ) ;
48+
49+ let name = '' ;
50+ let description = '' ;
51+ let instructions = '' ;
52+ let defaultPrompts = [ ] ;
53+ let currentSection = '' ;
54+
55+ for ( let i = 0 ; i < lines . length ; i ++ ) {
56+ const line = lines [ i ] . trim ( ) ;
57+
58+ if ( line . startsWith ( 'Name:' ) ) {
59+ currentSection = 'name' ;
60+ name = line . replace ( 'Name:' , '' ) . trim ( ) ;
61+ continue ;
62+ }
63+
64+ if ( line . startsWith ( 'Description:' ) ) {
65+ currentSection = 'description' ;
66+ description = line . replace ( 'Description:' , '' ) . trim ( ) ;
67+ continue ;
68+ }
69+
70+ if ( line . startsWith ( 'Instructions:' ) ) {
71+ currentSection = 'instructions' ;
72+ instructions = line . replace ( 'Instructions:' , '' ) . trim ( ) ;
73+ continue ;
74+ }
75+
76+ if ( line . startsWith ( 'Default prompts:' ) ) {
77+ currentSection = 'default_prompts' ;
78+ continue ;
79+ }
80+
81+ // Continue reading content for current section
82+ if ( currentSection === 'name' && line && ! line . startsWith ( 'Description:' ) && ! line . startsWith ( 'Instructions:' ) && ! line . startsWith ( 'Default prompts:' ) ) {
83+ name += ( name ? ' ' : '' ) + line ;
84+ } else if ( currentSection === 'description' && line && ! line . startsWith ( 'Instructions:' ) && ! line . startsWith ( 'Default prompts:' ) ) {
85+ description += ( description ? ' ' : '' ) + line ;
86+ } else if ( currentSection === 'instructions' && line && ! line . startsWith ( 'Default prompts:' ) ) {
87+ instructions += ( instructions ? '\n' : '' ) + lines [ i ] ; // Keep original formatting for instructions
88+ } else if ( currentSection === 'default_prompts' && line ) {
89+ // Parse numbered list items or quoted strings
90+ const promptMatch = line . match ( / ^ \d + \. \s * [ " ' ] ( .+ ) [ " ' ] \s * $ / ) || line . match ( / ^ \d + \. \s * ( .+ ) $ / ) ;
91+ if ( promptMatch ) {
92+ const prompt = promptMatch [ 1 ] . trim ( ) ;
93+ if ( prompt ) {
94+ defaultPrompts . push ( prompt ) ;
95+ }
96+ } else if ( line . startsWith ( '"' ) && line . endsWith ( '"' ) ) {
97+ // Handle quoted strings without numbers
98+ defaultPrompts . push ( line . slice ( 1 , - 1 ) ) ;
99+ } else if ( line && ! line . match ( / ^ \d + \. \s * $ / ) ) {
100+ // Handle unquoted strings that aren't just numbers
101+ defaultPrompts . push ( line ) ;
102+ }
103+ }
104+ }
105+
106+ return {
107+ name : name . trim ( ) ,
108+ description : description . trim ( ) ,
109+ instructions : instructions . trim ( ) ,
110+ defaultPrompts : defaultPrompts
111+ } ;
112+ }
113+
114+ /**
115+ * Create an agent with the provided data.
116+ */
117+ async function createAgent ( agentData ) {
118+ const { author, ...versionData } = agentData ;
119+ const timestamp = new Date ( ) ;
120+ const initialAgentData = {
121+ ...agentData ,
122+ versions : [
123+ {
124+ ...versionData ,
125+ createdAt : timestamp ,
126+ updatedAt : timestamp ,
127+ } ,
128+ ] ,
129+ } ;
130+ return ( await Agent . create ( initialAgentData ) ) . toObject ( ) ;
131+ }
132+
133+ /**
134+ * Update an existing agent with a new version
135+ */
136+ async function updateAgentVersion ( existingAgent , newAgentData ) {
137+ const { author, ...versionData } = newAgentData ;
138+ const timestamp = new Date ( ) ;
139+
140+ // Create new version object
141+ const newVersion = {
142+ ...versionData ,
143+ createdAt : timestamp ,
144+ updatedAt : timestamp ,
145+ } ;
146+
147+ // Update the main agent fields with new data
148+ const updatedAgent = await Agent . findByIdAndUpdate (
149+ existingAgent . _id ,
150+ {
151+ ...newAgentData ,
152+ $push : { versions : newVersion } ,
153+ updatedAt : timestamp ,
154+ } ,
155+ { new : true }
156+ ) ;
157+
158+ return updatedAgent . toObject ( ) ;
159+ }
160+
161+ /**
162+ * Create agent in database from prompt file
163+ * @param {string } filename - Name of the prompt file
164+ * @param {string } userId - MongoDB ObjectId of the user (optional, defaults to 6831a77a46d7304e714d8248)
165+ * @param {Object } options - Additional options
166+ */
167+ async function createAgentFromPrompt ( filename , userId = '6831a77a46d7304e714d8248' , options = { } ) {
168+ try {
169+ // Connect to database
170+ await connectDb ( ) ;
171+
172+ // Build file path
173+ const promptsPath = path . join ( __dirname , '../../user_agent_system_prompts' ) ;
174+ const filePath = path . join ( promptsPath , filename ) ;
175+
176+ // Parse prompt file
177+ const { name, description, instructions, defaultPrompts } = parsePromptFile ( filePath ) ;
178+
179+ if ( ! name || ! description || ! instructions ) {
180+ throw new Error ( 'Missing required fields: Name, Description, or Instructions' ) ;
181+ }
182+
183+ // Check if an agent with the same name and author already exists
184+ const existingAgent = await Agent . findOne ( { name, author : userId } ) ;
185+
186+ let agent ;
187+ let isNewAgent = false ;
188+
189+ if ( existingAgent ) {
190+ console . log ( `🔄 Found existing agent "${ name } " - creating new version...` ) ;
191+
192+ // Prepare agent data for new version (keep existing ID)
193+ const agentData = {
194+ id : existingAgent . id , // Keep the same ID
195+ name,
196+ description,
197+ instructions,
198+ provider : options . provider || 'openAI' ,
199+ model : options . model || 'gpt-4.1-mini' ,
200+ artifacts : options . artifacts || 'default' ,
201+ author : userId ,
202+ tools : options . tools || [ 'workflows' ] ,
203+ tool_kwargs : options . tool_kwargs || [ ] ,
204+ agent_ids : options . agent_ids || [ ] ,
205+ conversation_starters : options . conversation_starters || [ ] ,
206+ default_prompts : options . default_prompts || defaultPrompts || [ ] ,
207+ projectIds : options . projectIds || [ ] ,
208+ mcp_servers : options . mcp_servers || [ ] ,
209+ model_parameters : options . model_parameters || { } ,
210+ end_after_tools : options . end_after_tools || false ,
211+ hide_sequential_outputs : options . hide_sequential_outputs || false
212+ } ;
213+
214+ // Update existing agent with new version
215+ agent = await updateAgentVersion ( existingAgent , agentData ) ;
216+
217+ } else {
218+ console . log ( `🆕 Creating new agent "${ name } "...` ) ;
219+ isNewAgent = true ;
220+
221+ // Generate unique agent ID for new agent
222+ const agentId = `agent_${ nanoid ( ) } ` ;
223+
224+ // Prepare agent data with defaults
225+ const agentData = {
226+ id : agentId ,
227+ name,
228+ description,
229+ instructions,
230+ provider : options . provider || 'openAI' ,
231+ model : options . model || 'gpt-4.1-mini' ,
232+ artifacts : options . artifacts || 'default' ,
233+ author : userId ,
234+ tools : options . tools || [ 'workflows' ] ,
235+ tool_kwargs : options . tool_kwargs || [ ] ,
236+ agent_ids : options . agent_ids || [ ] ,
237+ conversation_starters : options . conversation_starters || [ ] ,
238+ default_prompts : options . default_prompts || defaultPrompts || [ ] ,
239+ projectIds : options . projectIds || [ ] ,
240+ mcp_servers : options . mcp_servers || [ ] ,
241+ model_parameters : options . model_parameters || { } ,
242+ end_after_tools : options . end_after_tools || false ,
243+ hide_sequential_outputs : options . hide_sequential_outputs || false
244+ } ;
245+
246+ // Create new agent in database
247+ agent = await createAgent ( agentData ) ;
248+ }
249+
250+ console . log ( `✅ Agent ${ isNewAgent ? 'created' : 'updated' } successfully!` ) ;
251+ console . log ( `📄 Name: ${ agent . name } ` ) ;
252+ console . log ( `🆔 ID: ${ agent . id } ` ) ;
253+ console . log ( `👤 Author: ${ agent . author } ` ) ;
254+ console . log ( `📝 Description: ${ agent . description } ` ) ;
255+ console . log ( `📊 Versions: ${ agent . versions ? agent . versions . length : 1 } ` ) ;
256+
257+ return agent ;
258+
259+ } catch ( error ) {
260+ console . error ( '❌ Error creating agent:' , error . message ) ;
261+ throw error ;
262+ }
263+ }
264+
265+ // CLI interface
266+ if ( require . main === module ) {
267+ const args = process . argv . slice ( 2 ) ;
268+
269+ if ( args . length < 1 ) {
270+ console . log ( 'Usage: node create-agent-from-prompt.js <filename> [user_id] [options]' ) ;
271+ console . log ( '' ) ;
272+ console . log ( 'Arguments:' ) ;
273+ console . log ( ' filename - Name of the prompt file (e.g., burn_monitor.txt)' ) ;
274+ console . log ( ' user_id - MongoDB ObjectId of the user (optional, defaults to 6831a77a46d7304e714d8248)' ) ;
275+ console . log ( '' ) ;
276+ console . log ( 'Options (JSON format):' ) ;
277+ console . log ( ' --provider - AI provider (default: "openAI")' ) ;
278+ console . log ( ' --model - Model name (default: "gpt-4.1-mini")' ) ;
279+ console . log ( ' --tools - Array of tool names' ) ;
280+ console . log ( ' --mcp_servers - Array of MCP server names' ) ;
281+ console . log ( '' ) ;
282+ console . log ( 'Examples:' ) ;
283+ console . log ( ' node create-agent-from-prompt.js burn_monitor.txt' ) ;
284+ console . log ( ' node create-agent-from-prompt.js burn_monitor.txt 68341a46ee1d93d1f7d18834' ) ;
285+ console . log ( ' node create-agent-from-prompt.js burn_monitor.txt 68341a46ee1d93d1f7d18834 \'{"tools":["web_search"],"provider":"anthropic","model":"claude-3-sonnet"}\'' ) ;
286+ process . exit ( 1 ) ;
287+ }
288+
289+ const filename = args [ 0 ] ;
290+ const userId = args [ 1 ] || '6831a77a46d7304e714d8248' ; // Default user ID
291+ const optionsJson = args [ 2 ] || '{}' ;
292+
293+ let options = { } ;
294+ try {
295+ options = JSON . parse ( optionsJson ) ;
296+ } catch ( error ) {
297+ console . error ( '❌ Invalid JSON in options:' , error . message ) ;
298+ process . exit ( 1 ) ;
299+ }
300+
301+ createAgentFromPrompt ( filename , userId , options )
302+ . then ( ( ) => {
303+ console . log ( '🎉 Done!' ) ;
304+ process . exit ( 0 ) ;
305+ } )
306+ . catch ( ( error ) => {
307+ console . error ( '💥 Failed:' , error . message ) ;
308+ process . exit ( 1 ) ;
309+ } ) ;
310+ }
311+
312+ module . exports = { createAgentFromPrompt, parsePromptFile } ;
0 commit comments