mirror of
https://github.com/kolbytn/mindcraft.git
synced 2025-08-30 10:53:03 +02:00
remove agent from ui, smarter viewer logic
This commit is contained in:
parent
69642d15fd
commit
b7697723bc
6 changed files with 167 additions and 53 deletions
2
main.js
2
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'));
|
||||
|
|
|
@ -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": [
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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,23 +748,12 @@
|
|||
|
||||
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) => {
|
||||
function renderAgentCard(agent) {
|
||||
const cfg = agentSettings[agent.name] || {};
|
||||
const showViewer = agent.in_game && cfg.render_bot_view === true;
|
||||
const viewerHTML = showViewer ? `<div class="agent-view-container"><iframe class="agent-viewer" src="http://localhost:${3000 + idx}"></iframe></div>` : '';
|
||||
const viewerPort = agent.viewerPort;
|
||||
const viewerHTML = showViewer ? `<div class="agent-view-container"><iframe class="agent-viewer" id="viewer-${agent.name}" src="http://localhost:${viewerPort}"></iframe></div>` : '';
|
||||
const lastMessage = agentLastMessage[agent.name] || '';
|
||||
return `
|
||||
<div class="agent" id="agent-${agent.name}">
|
||||
|
@ -773,6 +775,7 @@
|
|||
<div class="cell" id="equipped-${agent.name}">equipped: -</div>
|
||||
<div class="agent-inventory" id="inventorySection-${agent.name}" style="display:none; grid-column: 1 / -1;">
|
||||
<h3>Inventory</h3>
|
||||
<div class="cell" id="armor-${agent.name}" style="margin-bottom: 8px;">armor: -</div>
|
||||
<div class="inventory-grid" id="inventory-${agent.name}"></div>
|
||||
</div>
|
||||
<div id="lastMessage-${agent.name}" class="last-message" style="grid-column: 1 / -1;"><strong>Last Message:</strong> ${lastMessage}</div>
|
||||
|
@ -783,30 +786,118 @@
|
|||
oninput="onMsgInputChange('${agent.name}')"
|
||||
onkeydown="if(event.key === 'Enter') document.getElementById('sendBtn-${agent.name}').click()"
|
||||
${!agent.in_game ? 'disabled' : ''}>
|
||||
<button class="neutral-btn" onclick="startAgent('${agent.name}')" style="display: ${agent.in_game ? 'none' : ''}">Connect</button>
|
||||
<button class="neutral-btn" onclick="disconnectAgent('${agent.name}')" style="display: ${agent.in_game ? '' : 'none'}">Disconnect</button>
|
||||
<button class="neutral-btn" onclick="restartAgent('${agent.name}')" ${!agent.in_game ? 'disabled' : ''}>Restart</button>
|
||||
<button class="neutral-btn" onclick="sendMessage('${agent.name}', '!stop')" ${!agent.in_game ? 'disabled' : ''}>Stop Action</button>
|
||||
<button class="neutral-btn" onclick="sendMessage('${agent.name}', '!stay(-1)')" ${!agent.in_game ? 'disabled' : ''}>Stay Still</button>
|
||||
<button class="neutral-btn" onclick="restartAgent('${agent.name}')" ${!agent.in_game ? 'disabled' : ''}>Restart</button>
|
||||
<button class="neutral-btn" onclick="${agent.in_game ? 'disconnectAgent' : 'startAgent'}('${agent.name}')">${agent.in_game ? 'Disconnect' : 'Connect'}</button>
|
||||
<button class="stop-btn" onclick="destroyAgent('${agent.name}')">Remove</button>
|
||||
</div>
|
||||
</div>`;
|
||||
}).join('') :
|
||||
'<div class="agent">No agents connected</div>';
|
||||
|
||||
// Update viewers after DOM has updated
|
||||
setTimeout(updateViewers, 0);
|
||||
}
|
||||
|
||||
socket.on('agents-update', async (agents) => {
|
||||
// Fetch settings for any newly connected agents
|
||||
const newlyConnected = agents.filter(a => a.in_game && (!agentSettings[a.name] || !agentSettings[a.name].fetched));
|
||||
await Promise.all(newlyConnected.map(async (a) => {
|
||||
async function renderAgents(agents) {
|
||||
if (!agents.length) {
|
||||
agentsDiv.innerHTML = '<div class="agent">No agents connected</div>';
|
||||
return;
|
||||
}
|
||||
|
||||
// If agentsDiv is empty, do a full render
|
||||
if (!agentsDiv.children.length) {
|
||||
agentsDiv.innerHTML = agents.map(agent => renderAgentCard(agent)).join('');
|
||||
// Update all viewers after initial render
|
||||
setTimeout(() => {
|
||||
agents.forEach(a => {
|
||||
if (a.in_game) updateAgentViewer(a.name);
|
||||
});
|
||||
}, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
// Compare with current agents to find changes
|
||||
const prevAgents = currentAgents.reduce((acc, a) => ({ ...acc, [a.name]: a }), {});
|
||||
const changedAgents = agents.filter(a => {
|
||||
const prev = prevAgents[a.name];
|
||||
return !prev || prev.in_game !== a.in_game || prev.viewerPort !== a.viewerPort;
|
||||
});
|
||||
|
||||
// Update only changed agents
|
||||
changedAgents.forEach(agent => {
|
||||
const el = document.getElementById(`agent-${agent.name}`);
|
||||
if (el) {
|
||||
// Update existing card
|
||||
el.outerHTML = renderAgentCard(agent);
|
||||
if (agent.in_game) updateAgentViewer(agent.name);
|
||||
} else {
|
||||
// Add new card
|
||||
agentsDiv.insertAdjacentHTML('beforeend', renderAgentCard(agent));
|
||||
if (agent.in_game) updateAgentViewer(agent.name);
|
||||
}
|
||||
});
|
||||
|
||||
// Remove cards for agents that no longer exist
|
||||
Array.from(agentsDiv.children).forEach(el => {
|
||||
const name = el.id.replace('agent-', '');
|
||||
if (!agents.find(a => a.name === name)) {
|
||||
el.remove();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
socket.on('agents-status', async (agents) => {
|
||||
// Fetch settings for all agents that don't have current settings
|
||||
const needSettings = agents.filter(a => !agentSettings[a.name]);
|
||||
if (needSettings.length > 0) {
|
||||
await Promise.all(needSettings.map(async (a) => {
|
||||
const settings = await fetchAgentSettings(a.name);
|
||||
if (settings) {
|
||||
agentSettings[a.name] = { ...settings, fetched: true };
|
||||
agentSettings[a.name] = settings;
|
||||
}
|
||||
}));
|
||||
renderAgents(agents);
|
||||
}
|
||||
|
||||
// Compare with current agents to find changes
|
||||
const prevAgents = currentAgents.reduce((acc, a) => ({ ...acc, [a.name]: a }), {});
|
||||
const changedAgents = agents.filter(a => {
|
||||
const prev = prevAgents[a.name];
|
||||
return !prev || prev.in_game !== a.in_game || prev.viewerPort !== a.viewerPort;
|
||||
});
|
||||
|
||||
// Update current agents list
|
||||
currentAgents = agents;
|
||||
|
||||
// If agentsDiv is empty, do a full render
|
||||
if (!agentsDiv.children.length) {
|
||||
agentsDiv.innerHTML = agents.map(agent => renderAgentCard(agent)).join('');
|
||||
// Update all viewers after initial render
|
||||
setTimeout(() => {
|
||||
agents.forEach(a => {
|
||||
if (a.in_game) updateAgentViewer(a.name);
|
||||
});
|
||||
}, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
// Update only changed agents
|
||||
changedAgents.forEach(agent => {
|
||||
const el = document.getElementById(`agent-${agent.name}`);
|
||||
if (el) {
|
||||
// Update existing card
|
||||
el.outerHTML = renderAgentCard(agent);
|
||||
if (agent.in_game) updateAgentViewer(agent.name);
|
||||
} else {
|
||||
// Add new card
|
||||
agentsDiv.insertAdjacentHTML('beforeend', renderAgentCard(agent));
|
||||
if (agent.in_game) updateAgentViewer(agent.name);
|
||||
}
|
||||
});
|
||||
|
||||
// Remove cards for agents that no longer exist
|
||||
Array.from(agentsDiv.children).forEach(el => {
|
||||
const name = el.id.replace('agent-', '');
|
||||
if (!agents.find(a => a.name === name)) {
|
||||
el.remove();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
function restartAgent(n) { socket.emit('restart-agent', n); }
|
||||
|
@ -820,6 +911,7 @@
|
|||
socket.emit('start-agent', n);
|
||||
}
|
||||
function stopAgent(n) { socket.emit('stop-agent', n); }
|
||||
function destroyAgent(n) { socket.emit('destroy-agent', n); }
|
||||
function disconnectAllAgents() {
|
||||
socket.emit('stop-all-agents');
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue