char-slop/berry
multipurpose self-modifiable agentic harness
git clone https://git.t4t.associates/char-slop/berry
51b5eef
main
1// Runs berry, and runs it again when it exits to restart, or crashes. 2 3import { fromFileUrl } from "@std/path" ; 4import { denoExecutable } from "./deno.ts" ; 5 6/** The exit code berry restarts with. */ 7export const RESTART = 75 ; 8 9if ( import . meta. main ) { 10const main = fromFileUrl ( new URL ( "./main.ts" , import . meta. url )); 11let stopping = false ; 12let child :Deno . ChildProcess | undefined ; 13// the terminal's ^C reaches berry too; either way, berry stops and then so does this 14for ( const signal of [ "SIGINT" , "SIGTERM" ] as const ) { 15Deno . addSignalListener ( signal , () => { 16stopping = true ; 17try { 18child ?. kill ( signal ); 19} catch { 20// already gone 21} 22}); 23} 24 25let backoff = 1_000 ; 26while ( true ) { 27const started = Date . now (); 28child = new Deno . Command ( denoExecutable (), { args :[ "run" , "-A" , main ] }). spawn (); 29const { code} = await child . status ; 30if ( stopping || code === 0 ) Deno . exit ( code ); 31if ( code === RESTART ) { 32console . log ( "[berry] restarting" ); 33backoff = 1_000 ; 34continue ; 35} 36// a long run means the crash is news, not the same broken code again 37if ( Date . now () - started > 60_000 ) backoff = 1_000 ; 38console . error ( `[berry] exited with ${ code } ; starting again in ${ backoff / 1000 } s` ); 39await new Promise (( resolve ) => setTimeout ( resolve , backoff )); 40backoff = Math . min ( backoff * 2 , 30_000 ); 41} 42}