diff --git a/main.js b/main.js index cabf295..4402cb9 100644 --- a/main.js +++ b/main.js @@ -63,7 +63,7 @@ if (process.env.LOG_ALL) { settings.log_all_prompts = process.env.LOG_ALL; } -Mindcraft.init(false, settings.mindserver_port, settings.auto_open_browser); +Mindcraft.init(false, settings.mindserver_port, settings.auto_open_ui); for (let profile of settings.profiles) { const profile_json = JSON.parse(readFileSync(profile, 'utf8')); diff --git a/settings.js b/settings.js index f5487e3..26c0347 100644 --- a/settings.js +++ b/settings.js @@ -6,7 +6,7 @@ const settings = { // the mindserver manages all agents and hosts the UI "mindserver_port": 8080, - "auto_open_browser": true, + "auto_open_ui": true, "base_profile": "assistant", // survival, assistant, creative, or god_mode "profiles": [ diff --git a/src/agent/mindserver_proxy.js b/src/agent/mindserver_proxy.js index b1043b1..68024a1 100644 --- a/src/agent/mindserver_proxy.js +++ b/src/agent/mindserver_proxy.js @@ -44,7 +44,7 @@ class MindServerProxy { convoManager.receiveFromBot(agentName, json); }); - this.socket.on('agents-update', (agents) => { + this.socket.on('agents-status', (agents) => { this.agents = agents; convoManager.updateAgents(agents); if (this.agent?.task) { diff --git a/src/mindcraft/mindcraft.js b/src/mindcraft/mindcraft.js index 640576f..b797a67 100644 --- a/src/mindcraft/mindcraft.js +++ b/src/mindcraft/mindcraft.js @@ -9,7 +9,7 @@ let agent_processes = {}; let agent_count = 0; let port = 8080; -export async function init(host_public=false, port=8080, auto_open_browser=true) { +export async function init(host_public=false, port=8080, auto_open_ui=true) { if (connected) { console.error('Already initiliazed!'); return; @@ -17,7 +17,7 @@ export async function init(host_public=false, port=8080, auto_open_browser=true) mindserver = createMindServer(host_public, port); port = port; connected = true; - if (auto_open_browser) { + if (auto_open_ui) { setTimeout(() => { // check if browser listener is already open if (numStateListeners() === 0) { @@ -34,7 +34,8 @@ export async function createAgent(settings) { } settings = JSON.parse(JSON.stringify(settings)); let agent_name = settings.profile.name; - registerAgent(settings); + const viewer_port = 3000 + agent_count; + registerAgent(settings, viewer_port); let load_memory = settings.load_memory || false; let init_message = settings.init_message || null; @@ -68,6 +69,13 @@ export function stopAgent(agentName) { } } +export function destroyAgent(agentName) { + if (agent_processes[agentName]) { + agent_processes[agentName].stop(); + delete agent_processes[agentName]; + } +} + export function shutdown() { console.log('Shutting down'); for (let agentName in agent_processes) { diff --git a/src/mindcraft/mindserver.js b/src/mindcraft/mindserver.js index c06e0b5..64af008 100644 --- a/src/mindcraft/mindserver.js +++ b/src/mindcraft/mindserver.js @@ -20,26 +20,27 @@ const agent_listeners = []; const settings_spec = JSON.parse(readFileSync(path.join(__dirname, 'public/settings_spec.json'), 'utf8')); class AgentConnection { - constructor(settings) { + constructor(settings, viewer_port) { this.socket = null; this.settings = settings; this.in_game = false; this.full_state = null; + this.viewer_port = viewer_port; } setSettings(settings) { this.settings = settings; } } -export function registerAgent(settings) { - let agentConnection = new AgentConnection(settings); +export function registerAgent(settings, viewer_port) { + let agentConnection = new AgentConnection(settings, viewer_port); agent_connections[settings.profile.name] = agentConnection; } export function logoutAgent(agentName) { if (agent_connections[agentName]) { agent_connections[agentName].in_game = false; - agentsUpdate(); + agentsStatusUpdate(); } } @@ -58,7 +59,7 @@ export function createMindServer(host_public = false, port = 8080) { let curAgentName = null; console.log('Client connected'); - agentsUpdate(socket); + agentsStatusUpdate(socket); socket.on('create-agent', (settings, callback) => { console.log('API create agent...'); @@ -105,7 +106,7 @@ export function createMindServer(host_public = false, port = 8080) { agent_connections[agentName].socket = socket; agent_connections[agentName].in_game = true; curAgentName = agentName; - agentsUpdate(); + agentsStatusUpdate(); } else { console.warn(`Unregistered agent ${agentName} tried to login`); @@ -116,7 +117,7 @@ export function createMindServer(host_public = false, port = 8080) { if (agent_connections[curAgentName]) { console.log(`Agent ${curAgentName} disconnected`); agent_connections[curAgentName].in_game = false; - agentsUpdate(); + agentsStatusUpdate(); } if (agent_listeners.includes(socket)) { removeListener(socket); @@ -153,6 +154,14 @@ export function createMindServer(host_public = false, port = 8080) { mindcraft.startAgent(agentName); }); + socket.on('destroy-agent', (agentName) => { + if (agent_connections[agentName]) { + mindcraft.destroyAgent(agentName); + delete agent_connections[agentName]; + } + agentsStatusUpdate(); + }); + socket.on('stop-all-agents', () => { console.log('Killing all agents'); for (let agentName in agent_connections) { @@ -202,15 +211,20 @@ export function createMindServer(host_public = false, port = 8080) { return server; } -function agentsUpdate(socket) { +function agentsStatusUpdate(socket) { if (!socket) { socket = io; } let agents = []; for (let agentName in agent_connections) { - agents.push({name: agentName, in_game: agent_connections[agentName].in_game}); + const conn = agent_connections[agentName]; + agents.push({ + name: agentName, + in_game: conn.in_game, + viewerPort: conn.viewer_port + }); }; - socket.emit('agents-update', agents); + socket.emit('agents-status', agents); } diff --git a/src/mindcraft/public/index.html b/src/mindcraft/public/index.html index 6e81b32..a56f498 100644 --- a/src/mindcraft/public/index.html +++ b/src/mindcraft/public/index.html @@ -174,7 +174,7 @@ .controls-row { margin-top: 8px; display: grid; - grid-template-columns: auto 1fr auto auto auto auto; + grid-template-columns: auto minmax(100px, 1fr) repeat(5, auto); gap: 8px; align-items: center; } @@ -383,6 +383,7 @@ let profileData = null; const agentSettings = {}; const agentLastMessage = {}; + let currentAgents = []; const statusEl = document.getElementById('msStatus'); function updateStatus(connected) { @@ -405,6 +406,8 @@ socket.on('connect', () => { updateStatus(true); subscribeToState(); + // Clear all cached settings on reconnect + Object.keys(agentSettings).forEach(name => delete agentSettings[name]); }); socket.on('disconnect', () => { updateStatus(false); @@ -574,6 +577,16 @@ const e = st.inventory.equipment; equippedEl.textContent = `equipped: ${e.mainHand || 'none'}`; } + const armorEl = document.getElementById(`armor-${name}`); + if (armorEl && st.inventory?.equipment) { + const e = st.inventory.equipment; + const armor = []; + if (e.helmet) armor.push(`head: ${e.helmet}`); + if (e.chestplate) armor.push(`chest: ${e.chestplate}`); + if (e.leggings) armor.push(`legs: ${e.leggings}`); + if (e.boots) armor.push(`feet: ${e.boots}`); + armorEl.textContent = `armor: ${armor.length ? armor.join(', ') : 'none'}`; + } if (actionEl && st.action) { actionEl.textContent = `${st.action.current || 'Idle'}`; } @@ -713,7 +726,7 @@ const viewerContainer = agentEl.querySelector('.agent-view-container'); if (!viewerContainer) return; - const agentState = agents.find(a => a.name === name); + const agentState = currentAgents.find(a => a.name === name); const shouldShow = agentState?.in_game && settings?.render_bot_view === true; viewerContainer.parentElement.style.display = shouldShow ? '' : 'none'; } @@ -735,25 +748,14 @@ closeAgentSettingsBtn.addEventListener('click', closeAgentSettings); - async function renderAgents(agents) { - // fetch settings for any new agents - await Promise.all(agents.map(a => fetchAgentSettings(a.name))); - // Update all agent viewers after render - const updateViewers = () => { - agents.forEach(a => { - if (a.in_game) updateAgentViewer(a.name); - }); - }; - - agentsDiv.innerHTML = agents.length ? - // Set timeout to run after DOM update - agents.map((agent, idx) => { - const cfg = agentSettings[agent.name] || {}; - const showViewer = agent.in_game && cfg.render_bot_view === true; - const viewerHTML = showViewer ? `
` : ''; - const lastMessage = agentLastMessage[agent.name] || ''; - return ` + function renderAgentCard(agent) { + const cfg = agentSettings[agent.name] || {}; + const showViewer = agent.in_game && cfg.render_bot_view === true; + const viewerPort = agent.viewerPort; + const viewerHTML = showViewer ? `` : ''; + const lastMessage = agentLastMessage[agent.name] || ''; + return `