import http from 'node:http'; import {appendFileSync,writeFileSync} from 'node:fs'; const port=Number(process.env.PORT||3000); const token=process.env.ALE_BRIDGE_TOKEN||'ci-bridge-token'; const file=process.env.CAPTURE_FILE||'/tmp/ale-capture.jsonl'; let probeSent=false; writeFileSync(file,''); function log(kind,payload){ appendFileSync(file,JSON.stringify({at:new Date().toISOString(),kind,payload})+'\n'); } function authorized(req){ return req.headers.authorization===`Bearer ${token}`; } function read(req){ return new Promise((resolve,reject)=>{ let body=''; req.setEncoding('utf8'); req.on('data',c=>{body+=c;if(body.length>100000){reject(new Error('too large'));req.destroy();}}); req.on('end',()=>resolve(body)); req.on('error',reject); }); } const probe=`local __command_id="ci-probe" local function __stringify(v) if v==nil then return "nil" end return tostring(v) end local __ok,__result=pcall(function() local core=tostring(GetCoreName()) local version=tostring(GetCoreVersion()) local lua=tostring(GetLuaEngine()) local players=tostring(GetPlayerCount()) local pb="unknown" if type(GetRandomPlayerbotMgr)=="function" then local ok,mgr=pcall(GetRandomPlayerbotMgr) pb=ok and (mgr and "available" or "nil") or "error" end return "core="..core.."|version="..version.."|lua="..lua.."|players="..players.."|playerbots="..pb end) AzerothAIComplete(__command_id,__ok,__stringify(__result)) `; const server=http.createServer(async(req,res)=>{ try{ if(req.url==='/health'){ res.writeHead(200,{'content-type':'application/json'}); return res.end(JSON.stringify({ok:true,probeSent})); } if(!authorized(req)){ res.writeHead(401); return res.end('unauthorized'); } if(req.method==='GET'&&req.url==='/api/bridge/ale/poll'){ log('poll',{probeSent}); if(!probeSent){ probeSent=true; res.writeHead(200,{'content-type':'text/plain; charset=utf-8','x-azerothai-command':'ci-probe'}); return res.end(probe); } res.writeHead(204); return res.end(); } if(req.method==='POST'&&req.url==='/api/bridge/ale/event'){ const body=await read(req); let parsed;try{parsed=JSON.parse(body);}catch{parsed={raw:body};} log('event',parsed); res.writeHead(200,{'content-type':'application/json'}); return res.end('{"ok":true}'); } if(req.method==='POST'&&req.url==='/api/bridge/ale/result'){ const body=await read(req); let parsed;try{parsed=JSON.parse(body);}catch{parsed={raw:body};} log('result',parsed); res.writeHead(200,{'content-type':'application/json'}); return res.end('{"ok":true}'); } res.writeHead(404);res.end('not found'); }catch(e){ log('server-error',{message:e instanceof Error?e.message:String(e)}); res.writeHead(500);res.end('error'); } }); server.listen(port,'0.0.0.0',()=>console.log(`ALE capture server listening on ${port}`));