Files
AzerothAI/core/connectors/index.ts
T
Richard Clark 5f0fc86cf9
Core tests / core (push) Successful in 9m57s
Full verification / application (push) Failing after 5m16s
Full verification / local-brain (push) Failing after 6m15s
Realm simulation / simulated-live-world (push) Failing after 5m32s
WoW integration lab / real-worldserver (push) Canceled after 17m23s
Import verified AzerothAI snapshot from GitHub
Source branch: feature/ai-brain-router
Source commit: c2a09e0460465201a58aaab56c71424e9a96405e
2026-09-22 13:12:54 -04:00

50 lines
3.1 KiB
TypeScript

import { z } from 'zod';
import type { GameConnector,Bot,Decision,RuntimeConfig } from '../types';
const deathSignal=z.object({
id:z.string().min(1).max(160),
at:z.string().min(1).max(80),
cause:z.string().min(1).max(300),
killer:z.string().max(120).optional(),
zone:z.string().max(120).optional(),
verified:z.boolean()
}).strict();
const observation=z.object({
zone:z.string().max(120),
level:z.number().int().min(1).max(100),
summary:z.string().max(2000),
availableActions:z.array(z.enum(['explore','quest','socialize','trade','rest'])),
alive:z.boolean().default(true),
death:deathSignal.optional()
});
export class SimulationConnector implements GameConnector {
id='simulation';
async health(){return {ok:true,detail:'Simulation ready. No AzerothCore connection.'};}
async observe(bot:Bot){return {zone:bot.zone,level:bot.level,availableActions:bot.personality.activities,summary:'Simulated world observation. Game mechanics are not executed.',alive:bot.status!=='dead'};}
async lifecycle(){ /* Acknowledged only inside the simulation. */ }
async decide(bot:Bot,d:Decision){return {summary:`${bot.name} chose to ${d.action}: ${d.goal}`,zone:bot.zone};}
}
export class AzerothCoreConnector implements GameConnector {
id='azerothcore';
constructor(private config:RuntimeConfig){}
private async request(path:string,body?:unknown){
if(!this.config.BRIDGE_URL||!this.config.BRIDGE_TOKEN)throw new Error('Configure BRIDGE_URL and BRIDGE_TOKEN on the server first.');
const url=new URL(this.config.BRIDGE_URL); if(!['http:','https:'].includes(url.protocol))throw new Error('Invalid bridge URL');
const response=await fetch(`${url.href.replace(/\/$/,'')}/v1${path}`,{method:body?'POST':'GET',headers:{Authorization:`Bearer ${this.config.BRIDGE_TOKEN}`,'Content-Type':'application/json'},...(body?{body:JSON.stringify(body)}:{}),signal:AbortSignal.timeout(12000),redirect:'error'});
if(!response.ok)throw new Error(`Game bridge returned HTTP ${response.status}`); return response.json();
}
async health(){const r=z.object({ok:z.boolean(),detail:z.string().max(300)}).parse(await this.request('/health'));return r;}
async observe(bot:Bot){return observation.parse(await this.request(`/bots/${encodeURIComponent(bot.id)}/observation`));}
async lifecycle(bot:Bot,action:string,requestId:string){
const r=z.object({accepted:z.literal(true)}).parse(await this.request(`/bots/${encodeURIComponent(bot.id)}/lifecycle`,{action,identity:{id:bot.id,name:bot.name,race:bot.race,role:bot.role,tier:bot.tier,mortal:bot.mortal,plotArmorHpFloor:bot.plotArmorHpFloor},requestId}));
void r;
}
async decide(bot:Bot,decision:Decision,requestId:string){return z.object({accepted:z.literal(true),summary:z.string().max(1000),zone:z.string().max(120).optional()}).parse(await this.request(`/bots/${encodeURIComponent(bot.id)}/intent`,{decision,requestId}));}
}
// Add a connector factory here; personality and memory code remains game-independent.
export const connectors:Record<string,(config:RuntimeConfig)=>GameConnector>={simulation:()=>new SimulationConnector(),azerothcore:c=>new AzerothCoreConnector(c)};