Generalize MCP servers, add model refresh button, add sonnet-5
- MCP: replace fixed single Unity field with a generic list of servers
({name, url, token?}); dynamic mcp-config + allowlist glob mcp__*<name>*
per entry; new mcp-servers-ui.js editor (no data migration)
- Models: frontend refresh button wired to existing POST /api/models/refresh,
reloads optgroups and restores selection
- Model: add sonnet-5 alias + dropdown option (bare "sonnet" stays 4.6)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
1032cd5c63
commit
5f7dec6448
14 files changed
+257
-50
No files matched your search
@@ -252,6 +252,9 @@ function apiDeleteApiKey(id) { return _del('/api/admin/api-keys/' + id, 'Fehler
|
||||
function apiCheckApiKey(id) { return _post('/api/admin/api-keys/' + id + '/check', undefined, 'Fehler beim Prüfen'); }
|
||||
function apiCheckAllApiKeys() { return _post('/api/admin/api-keys/check-all', undefined, 'Fehler beim Prüfen'); }
|
||||
|
||||
// ── Models API ──
|
||||
function apiRefreshModels() { return _post('/api/models/refresh', undefined, 'Fehler beim Neu-Laden der Modelle'); }
|
||||
|
||||
// ── Key Scanner API (Admin) ──
|
||||
|
||||
function apiTriggerScan(mode) { return _post('/api/admin/api-keys/scan?mode=' + (mode || 'full'), undefined, 'Fehler beim Starten des Scans'); }
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
// ── MCP-Server UI (Liste pro Session) ──
|
||||
// Spiegelt das Muster von commands-ui.js: dynamische Add/Remove-Zeilen, JSON-Persistenz.
|
||||
// Pro Eintrag: Name, URL (http/sse), optionaler Token.
|
||||
|
||||
function createMcpServerRow(container, name, url, token) {
|
||||
name = name || '';
|
||||
url = url || '';
|
||||
token = token || '';
|
||||
|
||||
const item = document.createElement('div');
|
||||
item.className = 'mcp-server-item';
|
||||
item.innerHTML = `
|
||||
<div class="mcp-server-fields">
|
||||
<input type="text" class="mcp-name" placeholder="Name (z.B. UnityMCP)" value="${esc(name)}" autocomplete="off">
|
||||
<input type="text" class="mcp-url" placeholder="URL (z.B. http://localhost:8080)" value="${esc(url)}" autocomplete="off">
|
||||
<input type="password" class="mcp-token" placeholder="Token (optional)" value="${esc(token)}" autocomplete="off">
|
||||
</div>
|
||||
<button type="button" class="remove-mcp-btn icon-btn"><i class="fa fa-times"></i></button>
|
||||
`;
|
||||
item.querySelector('.remove-mcp-btn').addEventListener('click', function() { item.remove(); });
|
||||
container.appendChild(item);
|
||||
}
|
||||
|
||||
function getMcpServersFromUI(container) {
|
||||
var items = container.querySelectorAll('.mcp-server-item');
|
||||
var servers = [];
|
||||
for (var i = 0; i < items.length; i++) {
|
||||
var item = items[i];
|
||||
var name = item.querySelector('.mcp-name').value.trim();
|
||||
var url = item.querySelector('.mcp-url').value.trim();
|
||||
var token = item.querySelector('.mcp-token').value.trim();
|
||||
// Nur vollständige Einträge (Name + URL) übernehmen.
|
||||
if (name && url) servers.push({ name: name, url: url, token: token || null });
|
||||
}
|
||||
return servers.length > 0 ? servers : null;
|
||||
}
|
||||
|
||||
function loadMcpServersIntoUI(container, mcpServers) {
|
||||
if (!mcpServers) return;
|
||||
try {
|
||||
var list = typeof mcpServers === 'string' ? JSON.parse(mcpServers) : mcpServers;
|
||||
if (!Array.isArray(list)) return;
|
||||
for (var i = 0; i < list.length; i++) {
|
||||
var s = list[i];
|
||||
createMcpServerRow(container, s.name || '', s.url || '', s.token || '');
|
||||
}
|
||||
} catch (e) { /* ignore parse errors */ }
|
||||
}
|
||||
@@ -23,12 +23,6 @@ registerPage('agent', {
|
||||
const nameEl = document.getElementById('agentName');
|
||||
const descEl = document.getElementById('agentDescription');
|
||||
const modelEl = document.getElementById('agentModel');
|
||||
const nvidiaOptgroup = document.getElementById('nvidiaOptgroup');
|
||||
const pollinationsOptgroup = document.getElementById('pollinationsOptgroup');
|
||||
const geminiOptgroup = document.getElementById('geminiOptgroup');
|
||||
const openaiOptgroup = document.getElementById('openaiOptgroup');
|
||||
const minimaxOptgroup = document.getElementById('minimaxOptgroup');
|
||||
const glmOptgroup = document.getElementById('glmOptgroup');
|
||||
|
||||
// Modelle dynamisch laden
|
||||
const loadProviderModels = async (url, prefix, optgroup) => {
|
||||
@@ -51,14 +45,39 @@ registerPage('agent', {
|
||||
}
|
||||
};
|
||||
|
||||
await Promise.all([
|
||||
loadProviderModels('/api/models/openai', 'openai', openaiOptgroup),
|
||||
loadProviderModels('/api/models/nvidia', 'nvidia', nvidiaOptgroup),
|
||||
loadProviderModels('/api/models/pollinations', 'pollinations', pollinationsOptgroup),
|
||||
loadProviderModels('/api/models/gemini', 'gemini', geminiOptgroup),
|
||||
loadProviderModels('/api/models/minimax', 'minimax', minimaxOptgroup),
|
||||
loadProviderModels('/api/models/glm', 'glm', glmOptgroup)
|
||||
// Optgroups bei jedem Aufruf frisch abfragen (nach dem ersten Laden können leere entfernt worden sein)
|
||||
const loadAllModels = () => Promise.all([
|
||||
loadProviderModels('/api/models/openai', 'openai', document.getElementById('openaiOptgroup')),
|
||||
loadProviderModels('/api/models/nvidia', 'nvidia', document.getElementById('nvidiaOptgroup')),
|
||||
loadProviderModels('/api/models/pollinations', 'pollinations', document.getElementById('pollinationsOptgroup')),
|
||||
loadProviderModels('/api/models/gemini', 'gemini', document.getElementById('geminiOptgroup')),
|
||||
loadProviderModels('/api/models/minimax', 'minimax', document.getElementById('minimaxOptgroup')),
|
||||
loadProviderModels('/api/models/glm', 'glm', document.getElementById('glmOptgroup'))
|
||||
]);
|
||||
|
||||
await loadAllModels();
|
||||
|
||||
// Refresh-Button: Modelle live von den Providern neu laden (Backend erneuert seinen Cache)
|
||||
const refreshModelsBtn = document.getElementById('refreshModelsBtn');
|
||||
if (refreshModelsBtn) {
|
||||
refreshModelsBtn.addEventListener('click', async () => {
|
||||
const origHtml = refreshModelsBtn.innerHTML;
|
||||
refreshModelsBtn.innerHTML = '<i class="fa fa-spinner fa-spin"></i>';
|
||||
refreshModelsBtn.disabled = true;
|
||||
const prevValue = modelEl.value;
|
||||
try {
|
||||
await apiRefreshModels();
|
||||
await loadAllModels();
|
||||
modelEl.value = prevValue; // vorherige Auswahl wiederherstellen (falls noch vorhanden)
|
||||
showToast('Modell-Listen neu geladen');
|
||||
} catch (err) {
|
||||
showToast(err.message || 'Fehler beim Neu-Laden der Modelle', 'error');
|
||||
} finally {
|
||||
refreshModelsBtn.innerHTML = origHtml;
|
||||
refreshModelsBtn.disabled = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
const allowUserChoiceEl = document.getElementById('agentAllowUserChoice');
|
||||
const autoCommitEl = document.getElementById('agentAutoCommit');
|
||||
const effortEl = document.getElementById('agentEffort');
|
||||
|
||||
@@ -38,8 +38,8 @@ registerPage('session', {
|
||||
const deleteBtn = document.getElementById('sessionDeleteBtn');
|
||||
const commandsList = document.getElementById('customCommandsList');
|
||||
const addCommandBtn = document.getElementById('addCommandBtn');
|
||||
const unityMcpUrlEl = document.getElementById('sessionUnityMcpUrl');
|
||||
const unityMcpTokenEl = document.getElementById('sessionUnityMcpToken');
|
||||
const mcpServersList = document.getElementById('mcpServersList');
|
||||
const addMcpServerBtn = document.getElementById('addMcpServerBtn');
|
||||
const ownerRow = document.getElementById('sessionOwnerRow');
|
||||
const userSelectCard = document.getElementById('sessionUserSelectCard');
|
||||
const userSelect = document.getElementById('sessionUserSelect');
|
||||
@@ -64,9 +64,8 @@ registerPage('session', {
|
||||
// Custom Commands laden
|
||||
loadCommandsIntoUI(commandsList, session.customCommands);
|
||||
|
||||
// Unity MCP
|
||||
unityMcpUrlEl.value = session.unityMcpUrl || '';
|
||||
unityMcpTokenEl.value = session.unityMcpToken || '';
|
||||
// MCP-Server
|
||||
loadMcpServersIntoUI(mcpServersList, session.mcpServers);
|
||||
} else {
|
||||
titleEl.textContent = 'Neue Session';
|
||||
deleteBtn.hidden = true;
|
||||
@@ -81,6 +80,9 @@ registerPage('session', {
|
||||
// Befehl hinzufügen
|
||||
addCommandBtn.addEventListener('click', () => createCommandRow(commandsList));
|
||||
|
||||
// MCP-Server hinzufügen
|
||||
addMcpServerBtn.addEventListener('click', () => createMcpServerRow(mcpServersList));
|
||||
|
||||
// Telegram-Section nur für existierende Sessions
|
||||
if (!isNew) {
|
||||
initSessionTelegram(sessionId, session);
|
||||
@@ -217,8 +219,7 @@ registerPage('session', {
|
||||
avatarScale: _sessionAvatarEditor.getScale(),
|
||||
avatarOffsetX: _sessionAvatarEditor.getOffsetX(),
|
||||
avatarOffsetY: _sessionAvatarEditor.getOffsetY(),
|
||||
unityMcpUrl: unityMcpUrlEl.value.trim() || null,
|
||||
unityMcpToken: unityMcpTokenEl.value.trim() || null
|
||||
mcpServers: getMcpServersFromUI(mcpServersList)
|
||||
};
|
||||
|
||||
// Session einem anderen Benutzer zuweisen. Wechselt der Besitzer tatsächlich,
|
||||
|
||||
Reference in new issue
Block a user