2023-11-12 13:57:22 -06:00
import { writeFile , readFile , unlink } from 'fs' ;
2023-11-07 12:00:55 -06:00
export class Coder {
constructor ( agent ) {
this . agent = agent ;
2023-11-15 17:00:02 -06:00
this . queued _code = '' ;
2023-11-07 12:00:55 -06:00
this . current _code = '' ;
2023-11-07 12:46:55 -06:00
this . file _counter = 0 ;
2023-12-21 15:11:38 -07:00
this . fp = '/bots/' + agent . name + '/action-code/' ;
2023-11-13 11:09:37 -06:00
this . agent . bot . interrupt _code = false ;
2023-11-13 00:57:20 -06:00
this . executing = false ;
2023-11-12 13:57:22 -06:00
this . agent . bot . output = '' ;
this . code _template = '' ;
2023-12-06 22:15:09 -06:00
this . timedout = false ;
2023-11-12 13:57:22 -06:00
2023-12-21 15:11:38 -07:00
readFile ( './bots/template.js' , 'utf8' , ( err , data ) => {
2023-11-12 13:57:22 -06:00
if ( err ) throw err ;
this . code _template = data ;
} ) ;
2023-11-07 12:00:55 -06:00
}
queueCode ( code ) {
2023-11-15 17:00:02 -06:00
this . queued _code = this . santitizeCode ( code ) ;
2023-11-08 19:24:24 -06:00
}
santitizeCode ( code ) {
const remove _strs = [ 'javascript' , 'js' ]
for ( let r of remove _strs ) {
if ( code . startsWith ( r ) ) {
code = code . slice ( r . length ) ;
return code ;
}
}
return code ;
2023-11-07 12:00:55 -06:00
}
2023-11-07 12:46:55 -06:00
writeFilePromise ( filename , src ) {
// makes it so we can await this function
return new Promise ( ( resolve , reject ) => {
writeFile ( filename , src , ( err ) => {
if ( err ) {
reject ( err ) ;
} else {
resolve ( ) ;
}
} ) ;
} ) ;
}
2023-11-13 00:57:20 -06:00
2023-12-08 16:18:20 -06:00
// returns {success: bool, message: string, interrupted: bool, timedout: false}
2023-11-07 12:00:55 -06:00
async execute ( ) {
2023-12-06 22:15:09 -06:00
if ( ! this . queued _code ) return { success : false , message : "No code to execute." , interrupted : false , timedout : false } ;
if ( ! this . code _template ) return { success : false , message : "Code template not loaded." , interrupted : false , timedout : false } ;
2023-11-12 13:57:22 -06:00
let src = '' ;
2023-12-04 21:33:40 -06:00
let code = this . queued _code ;
code = code . replaceAll ( 'console.log(' , 'log(bot,' ) ;
code = code . replaceAll ( 'log("' , 'log(bot,"' ) ;
2023-11-15 17:00:02 -06:00
// this may cause problems in callback functions
2023-12-04 21:33:40 -06:00
code = code . replaceAll ( ';\n' , '; if(bot.interrupt_code) {log(bot, "Code interrupted.");return;}\n' ) ;
2023-11-15 17:00:02 -06:00
for ( let line of code . split ( '\n' ) ) {
2023-11-07 12:00:55 -06:00
src += ` ${ line } \n ` ;
}
2023-11-12 13:57:22 -06:00
src = this . code _template . replace ( '/* CODE HERE */' , src ) ;
2023-11-07 12:00:55 -06:00
console . log ( "writing to file..." , src )
2023-11-07 12:46:55 -06:00
2023-12-21 15:11:38 -07:00
let filename = this . file _counter + '.js' ;
2023-11-08 19:24:24 -06:00
// if (this.file_counter > 0) {
// let prev_filename = this.fp + (this.file_counter-1) + '.js';
// unlink(prev_filename, (err) => {
// console.log("deleted file " + prev_filename);
// if (err) console.error(err);
// });
// } commented for now, useful to keep files for debugging
2023-11-07 12:46:55 -06:00
this . file _counter ++ ;
2023-12-21 15:11:38 -07:00
let write _result = await this . writeFilePromise ( '.' + this . fp + filename , src )
2023-11-07 12:00:55 -06:00
2023-11-08 19:24:24 -06:00
if ( write _result ) {
2023-11-07 12:46:55 -06:00
console . error ( 'Error writing code execution file: ' + result ) ;
2023-12-06 22:15:09 -06:00
return { success : false , message : result , interrupted : false , timedout : false } ;
2023-11-07 12:46:55 -06:00
}
2023-12-10 20:19:07 -06:00
let TIMEOUT ;
2023-11-07 12:46:55 -06:00
try {
console . log ( 'executing code...\n' ) ;
2023-12-21 15:11:38 -07:00
let execution _file = await import ( '../..' + this . fp + filename ) ;
2023-11-13 00:57:20 -06:00
await this . stop ( ) ;
2023-11-15 17:00:02 -06:00
this . current _code = this . queued _code ;
2023-11-08 21:05:18 -08:00
this . executing = true ;
2023-12-12 13:39:35 -06:00
TIMEOUT = this . _startTimeout ( 10 ) ;
2023-12-06 22:15:09 -06:00
await execution _file . main ( this . agent . bot ) ; // open fire
2023-11-08 21:05:18 -08:00
this . executing = false ;
2023-12-10 20:19:07 -06:00
clearTimeout ( TIMEOUT ) ;
2023-12-09 21:40:53 -06:00
2023-11-13 00:57:20 -06:00
let output = this . formatOutput ( this . agent . bot ) ;
2023-11-13 11:09:37 -06:00
let interrupted = this . agent . bot . interrupt _code ;
2023-12-06 22:15:09 -06:00
let timedout = this . timedout ;
2023-11-07 22:23:42 -06:00
this . clear ( ) ;
2023-12-20 16:30:05 -08:00
this . agent . bot . emit ( "code_terminated" ) ;
2023-12-06 22:15:09 -06:00
return { success : true , message : output , interrupted , timedout } ;
2023-11-07 12:46:55 -06:00
} catch ( err ) {
2023-11-08 21:05:18 -08:00
this . executing = false ;
2023-12-10 20:19:07 -06:00
clearTimeout ( TIMEOUT ) ;
console . error ( "Code execution triggered catch: " + err ) ;
2023-11-13 00:57:20 -06:00
let message = this . formatOutput ( this . agent . bot ) ;
2023-11-12 13:57:22 -06:00
message += '!!Code threw exception!! Error: ' + err ;
2023-11-13 11:09:37 -06:00
let interrupted = this . agent . bot . interrupt _code ;
2023-11-13 00:57:20 -06:00
await this . stop ( ) ;
2023-12-20 16:30:05 -08:00
this . agent . bot . emit ( "code_terminated" ) ;
2023-12-06 22:15:09 -06:00
return { success : false , message , interrupted , timedout : false } ;
2023-11-07 12:46:55 -06:00
}
2023-11-07 12:00:55 -06:00
}
2023-11-07 22:23:42 -06:00
2023-11-13 00:57:20 -06:00
formatOutput ( bot ) {
2023-12-08 16:18:20 -06:00
if ( bot . interrupt _code && ! this . timedout ) return '' ;
2023-11-13 00:57:20 -06:00
let output = bot . output ;
const MAX _OUT = 1000 ;
if ( output . length > MAX _OUT ) {
output = ` Code output is very long ( ${ output . length } chars) and has been shortened. \n
First outputs : \ n$ { output . substring ( 0 , MAX _OUT / 2 ) } \ n ... skipping many lines . \ nFinal outputs : \ n $ { output . substring ( output . length - MAX _OUT / 2 ) } ` ;
}
else {
output = 'Code output:\n' + output ;
}
return output ;
2023-11-12 13:57:22 -06:00
}
2023-11-13 00:57:20 -06:00
async stop ( ) {
2023-12-06 22:15:09 -06:00
if ( ! this . executing ) return ;
2023-11-08 21:05:18 -08:00
while ( this . executing ) {
2023-11-13 11:09:37 -06:00
this . agent . bot . interrupt _code = true ;
2023-11-08 21:05:18 -08:00
this . agent . bot . collectBlock . cancelTask ( ) ;
this . agent . bot . pathfinder . stop ( ) ;
2023-11-27 21:07:52 -06:00
this . agent . bot . pvp . stop ( ) ;
2023-11-13 11:09:37 -06:00
console . log ( 'waiting for code to finish executing... interrupt:' , this . agent . bot . interrupt _code ) ;
2023-11-12 17:41:01 -06:00
await new Promise ( resolve => setTimeout ( resolve , 1000 ) ) ;
2023-11-08 21:05:18 -08:00
}
2023-11-12 13:57:22 -06:00
this . clear ( ) ;
2023-11-13 00:57:20 -06:00
}
clear ( ) {
2023-11-07 22:23:42 -06:00
this . current _code = '' ;
2023-11-13 00:57:20 -06:00
this . agent . bot . output = '' ;
2023-11-13 11:09:37 -06:00
this . agent . bot . interrupt _code = false ;
2023-12-06 22:15:09 -06:00
this . timedout = false ;
}
_startTimeout ( TIMEOUT _MINS = 10 ) {
return setTimeout ( async ( ) => {
console . warn ( ` Code execution timed out after ${ TIMEOUT _MINS } minutes. Attempting force stop. ` ) ;
this . timedout = true ;
2023-12-08 16:18:20 -06:00
this . agent . bot . output += ` \n Action performed for ${ TIMEOUT _MINS } minutes and then timed out and stopped. You may want to continue or do something else. ` ;
2023-12-06 22:15:09 -06:00
this . stop ( ) ; // last attempt to stop
2023-12-08 16:18:20 -06:00
await new Promise ( resolve => setTimeout ( resolve , 5 * 1000 ) ) ; // wait 5 seconds
2023-12-06 22:15:09 -06:00
if ( this . executing ) {
console . error ( ` Failed to stop. Killing process. Goodbye. ` ) ;
2023-12-09 21:40:53 -06:00
this . agent . bot . output += ` \n Force stop failed! Process was killed and will be restarted. Goodbye world. ` ;
2023-12-12 13:39:35 -06:00
this . agent . bot . chat ( 'Goodbye world.' ) ;
2023-12-08 16:18:20 -06:00
let output = this . formatOutput ( this . agent . bot ) ;
this . agent . history . add ( 'system' , output ) ;
this . agent . history . save ( ) ;
2023-12-06 22:15:09 -06:00
process . exit ( 1 ) ; // force exit program
}
console . log ( 'Code execution stopped successfully.' ) ;
} , TIMEOUT _MINS * 60 * 1000 ) ;
2023-11-07 22:23:42 -06:00
}
2023-11-07 12:00:55 -06:00
}