- 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>
313 lines
15 KiB
JavaScript
313 lines
15 KiB
JavaScript
// ── Global 401 Handler ──
|
||
|
||
(function() {
|
||
var _origFetch = window.fetch;
|
||
window.fetch = function(url, opts) {
|
||
return _origFetch.apply(this, arguments).then(function(resp) {
|
||
if (resp.status === 401 && typeof url === 'string' && !url.includes('/api/auth/')) {
|
||
if (typeof currentPage !== 'undefined' && currentPage !== 'login') {
|
||
navigateTo('login');
|
||
}
|
||
}
|
||
return resp;
|
||
});
|
||
};
|
||
})();
|
||
|
||
// ── Internal Fetch Helpers ──
|
||
|
||
var _jsonHeaders = { 'Content-Type': 'application/json' };
|
||
|
||
/** GET → JSON (returns fallback on error, default: throws) */
|
||
async function _get(url, fallback) {
|
||
var resp = await fetch(url);
|
||
if (!resp.ok) {
|
||
if (arguments.length >= 2) return fallback;
|
||
throw new Error('Fehler beim Laden');
|
||
}
|
||
return resp.json();
|
||
}
|
||
|
||
/** POST/PUT/DELETE with JSON body → JSON (throws on error) */
|
||
async function _send(method, url, body, errMsg) {
|
||
var opts = { method: method };
|
||
if (body !== undefined) {
|
||
opts.headers = _jsonHeaders;
|
||
opts.body = JSON.stringify(body);
|
||
}
|
||
var resp = await fetch(url, opts);
|
||
if (!resp.ok) {
|
||
var err = await resp.json().catch(function() { return {}; });
|
||
throw new Error(err.error || errMsg || 'Anfrage fehlgeschlagen');
|
||
}
|
||
return resp.json();
|
||
}
|
||
|
||
/** POST with JSON body → JSON */
|
||
function _post(url, body, errMsg) { return _send('POST', url, body, errMsg); }
|
||
|
||
/** PUT with JSON body → JSON */
|
||
function _put(url, body, errMsg) { return _send('PUT', url, body, errMsg); }
|
||
|
||
/** DELETE → JSON */
|
||
function _del(url, errMsg) { return _send('DELETE', url, undefined, errMsg); }
|
||
|
||
// ── Auth API ──
|
||
|
||
function apiLogin(u, p) { return _post('/api/auth/login', { username: u, password: p }, 'Login fehlgeschlagen'); }
|
||
function apiLogout() { return fetch('/api/auth/logout', { method: 'POST' }); }
|
||
function apiMe() { return _get('/api/auth/me', null); }
|
||
|
||
// ── Chat API ──
|
||
|
||
function apiChat(content, sessionId, signal, planMode, ultracode, multiModelMode) {
|
||
var payload = { content: content, sessionId: sessionId };
|
||
if (planMode) payload.planMode = true;
|
||
if (ultracode) payload.ultracode = true;
|
||
// Panel-Modus ist on-demand pro Anfrage (transient): nur fusion/moa durchreichen.
|
||
if (multiModelMode === 'fusion' || multiModelMode === 'moa') payload.multiModelMode = multiModelMode;
|
||
return fetch('/api/chat', { method: 'POST', headers: _jsonHeaders, body: JSON.stringify(payload), signal: signal });
|
||
}
|
||
|
||
function apiChatChoice(sessionId, choiceToolCallId, choiceResult, signal, planMode) {
|
||
var payload = { sessionId: sessionId, choiceToolCallId: choiceToolCallId, choiceResult: choiceResult };
|
||
if (planMode) payload.planMode = true;
|
||
return fetch('/api/chat', { method: 'POST', headers: _jsonHeaders, body: JSON.stringify(payload), signal: signal });
|
||
}
|
||
|
||
function apiChoiceRespond(sessionId, choice) {
|
||
return _post('/api/chat/choice-respond', { sessionId: sessionId, choice: choice });
|
||
}
|
||
|
||
function apiStopChat(sessionId) { return _post('/api/chat/stop', { sessionId: sessionId }); }
|
||
|
||
/** Steering: Nachricht in die LAUFENDE Chain einwerfen (wird an nächster Iterations-Grenze injiziert). */
|
||
function apiChatSteer(sessionId, text) { return _post('/api/chat/steer', { sessionId: sessionId, text: text }); }
|
||
|
||
// ── Agent API ──
|
||
|
||
function apiGetAgents() { return _get('/api/agents'); }
|
||
function apiGetAvailableAgents() { return _get('/api/agents/available', []); }
|
||
function apiGetAgent(id) { return _get('/api/agents/' + id, null); }
|
||
function apiCreateAgent(data) { return _post('/api/agents', data); }
|
||
function apiUpdateAgent(id, data) { return _put('/api/agents/' + id, data); }
|
||
function apiDeleteAgent(id) { return fetch('/api/agents/' + id, { method: 'DELETE' }); }
|
||
|
||
// ── Session API ──
|
||
|
||
function apiGetSessions() { return _get('/api/sessions'); }
|
||
function apiGetSession(id) { return _get('/api/sessions/' + id, null); }
|
||
function apiCreateSession(data) { return _post('/api/sessions', data || {}); }
|
||
function apiUpdateSession(id, data) { return _put('/api/sessions/' + id, data); }
|
||
function apiDeleteSession(id) { return fetch('/api/sessions/' + id, { method: 'DELETE' }); }
|
||
|
||
function apiExecCommand(sessionId, command, mode) {
|
||
mode = mode || 'background';
|
||
var resp = fetch('/api/sessions/' + sessionId + '/exec', {
|
||
method: 'POST', headers: _jsonHeaders, body: JSON.stringify({ command: command, mode: mode })
|
||
});
|
||
return mode === 'foreground' ? resp.then(function(r) { return r.json(); }) : resp;
|
||
}
|
||
|
||
function apiGetSystemPrompt(sessionId) {
|
||
return fetch('/api/sessions/' + sessionId + '/system-prompt').then(function(r) {
|
||
if (!r.ok) throw new Error('System-Prompt konnte nicht geladen werden');
|
||
return r.text();
|
||
});
|
||
}
|
||
|
||
function apiGetSystemPromptBlocks(sessionId, planMode) {
|
||
var url = '/api/sessions/' + sessionId + '/system-prompt-blocks' + (planMode ? '?planMode=true' : '');
|
||
return _get(url);
|
||
}
|
||
|
||
function apiGetPayloadPreview(sessionId, planMode) {
|
||
var url = '/api/sessions/' + sessionId + '/payload-preview' + (planMode ? '?planMode=true' : '');
|
||
return _get(url);
|
||
}
|
||
|
||
function apiGetWorkflowStatus(sessionId) {
|
||
return _get('/api/sessions/' + sessionId + '/workflow-status');
|
||
}
|
||
|
||
function apiIsStreaming(sessionId) { return _get('/api/sessions/' + sessionId + '/streaming'); }
|
||
function apiGitStatus(sessionId) { return _get('/api/sessions/' + sessionId + '/git-status', null); }
|
||
|
||
// ── History API ──
|
||
|
||
function apiHistory(sessionId, beforeSortKey, includeDeleted) {
|
||
var url = '/api/history?sessionId=' + sessionId;
|
||
if (beforeSortKey) url += '&beforeSortKey=' + beforeSortKey;
|
||
if (includeDeleted) url += '&includeDeleted=true';
|
||
return _get(url);
|
||
}
|
||
|
||
function apiHistoryPoll(sessionId, afterSortKey, includeDeleted) {
|
||
var url = '/api/history/poll?sessionId=' + sessionId + '&afterSortKey=' + afterSortKey;
|
||
if (includeDeleted) url += '&includeDeleted=true';
|
||
return _get(url);
|
||
}
|
||
|
||
// ── Archive API ──
|
||
|
||
function apiArchive(sessionId) {
|
||
return fetch('/api/archive?sessionId=' + sessionId, { method: 'POST' }).then(function(r) {
|
||
return r.status === 204 ? null : r.json();
|
||
});
|
||
}
|
||
|
||
function apiListArchives(sessionId) { return _get('/api/archives?sessionId=' + sessionId); }
|
||
function apiGetArchive(id) { return _get('/api/archive/' + id); }
|
||
|
||
// ── Chain / Message API ──
|
||
|
||
function apiGetChain(id) { return _get('/api/chains/' + id, null); }
|
||
function apiToggleMessageMark(id) { return _put('/api/messages/' + id + '/mark'); }
|
||
function apiToggleMessageDelete(id) { return _put('/api/messages/' + id + '/delete'); }
|
||
|
||
// ── Skills API ──
|
||
|
||
function apiGetSkills() { return _get('/api/skills'); }
|
||
function apiGetAgentSkills(agentId) { return _get('/api/agents/' + agentId + '/skills'); }
|
||
function apiSetAgentSkills(agentId, skills) { return _put('/api/agents/' + agentId + '/skills', { skills: skills }); }
|
||
function apiAddAgentSkill(agentId, slug) { return fetch('/api/agents/' + agentId + '/skills/' + slug, { method: 'POST' }); }
|
||
function apiRemoveAgentSkill(agentId, slug) { return fetch('/api/agents/' + agentId + '/skills/' + slug, { method: 'DELETE' }); }
|
||
|
||
// ── Profile API ──
|
||
|
||
function apiGetProfile() { return _get('/api/profile', null); }
|
||
function apiUpdateProfile(data) { return _put('/api/profile', data, 'Fehler beim Speichern'); }
|
||
function apiChangePassword(cur, pw) { return _put('/api/profile/password', { currentPassword: cur, newPassword: pw }, 'Fehler beim Ändern'); }
|
||
|
||
// ── Telegram Bot API ──
|
||
|
||
function apiGetTelegramBots() { return _get('/api/telegram/bots'); }
|
||
function apiGetTelegramBot(id) { return _get('/api/telegram/bots/' + id, null); }
|
||
function apiGetAvailableTelegramBots() { return _get('/api/telegram/bots/available'); }
|
||
function apiCreateTelegramBot(name, token) { return _post('/api/telegram/bots', { name: name, botToken: token }, 'Bot erstellen fehlgeschlagen'); }
|
||
function apiUpdateTelegramBot(id, name, token, isPublic) { return _put('/api/telegram/bots/' + id, { name: name, botToken: token, isPublic: isPublic }, 'Bot speichern fehlgeschlagen'); }
|
||
function apiDeleteTelegramBot(id) { return _del('/api/telegram/bots/' + id, 'Bot löschen fehlgeschlagen'); }
|
||
function apiTelegramBotRegisterWebhook(id, baseUrl) { return _post('/api/telegram/bots/' + id + '/register-webhook', { baseUrl: baseUrl }, 'Webhook-Registrierung fehlgeschlagen'); }
|
||
function apiTelegramBotWebhookInfo(id) { return _get('/api/telegram/bots/' + id + '/webhook-info'); }
|
||
function apiTelegramBotDeleteWebhook(id) { return _del('/api/telegram/bots/' + id + '/webhook', 'Webhook-Löschung fehlgeschlagen'); }
|
||
|
||
// ── Session Telegram Config ──
|
||
|
||
function apiSessionTelegramGet(sessionId) { return _get('/api/sessions/' + sessionId + '/telegram', { configured: false }); }
|
||
function apiSessionTelegramSave(sessionId, botId, chatId) { return _put('/api/sessions/' + sessionId + '/telegram', { telegramBotId: botId, chatId: chatId }, 'Telegram-Config speichern fehlgeschlagen'); }
|
||
|
||
// ── Settings API ──
|
||
|
||
function apiGetSettings() { return _get('/api/settings', {}); }
|
||
function apiSaveSettings(settings) { return _put('/api/settings', settings); }
|
||
|
||
// ── Cronjob API ──
|
||
|
||
function apiGetCronjobs() { return _get('/api/cronjobs'); }
|
||
function apiGetCronjob(id) { return _get('/api/cronjobs/' + id, null); }
|
||
function apiCreateCronjob(data) { return _post('/api/cronjobs', data, 'Cronjob erstellen fehlgeschlagen'); }
|
||
function apiUpdateCronjob(id, data) { return _put('/api/cronjobs/' + id, data, 'Cronjob speichern fehlgeschlagen'); }
|
||
function apiDeleteCronjob(id) { return fetch('/api/cronjobs/' + id, { method: 'DELETE' }); }
|
||
function apiToggleCronjob(id) { return _post('/api/cronjobs/' + id + '/toggle'); }
|
||
function apiRunCronjob(id) { return _post('/api/cronjobs/' + id + '/run'); }
|
||
|
||
// ── Shared Message API ──
|
||
|
||
function apiListShares() { return _get('/api/shared'); }
|
||
function apiShareMessage(id, includeContext) { return _post('/api/messages/' + id + '/share', { includeContext: includeContext || false }, 'Teilen fehlgeschlagen'); }
|
||
function apiGetShared(token) { return _get('/api/shared/' + token); }
|
||
function apiDeleteShare(token) { return fetch('/api/shared/' + token, { method: 'DELETE' }); }
|
||
|
||
// Minimales User-Verzeichnis (jeder eingeloggte User) – z. B. um eine Session zuzuweisen.
|
||
function apiGetUsers() { return _get('/api/users'); }
|
||
|
||
// ── Admin User API ──
|
||
|
||
function apiAdminGetUsers() { return _get('/api/admin/users'); }
|
||
function apiAdminCreateUser(data) { return _post('/api/admin/users', data, 'Fehler beim Erstellen'); }
|
||
function apiAdminUpdateUser(id, data) { return _put('/api/admin/users/' + id, data, 'Fehler beim Aktualisieren'); }
|
||
function apiAdminResetPassword(id, pw) { return _put('/api/admin/users/' + id + '/password', { password: pw }, 'Fehler beim Zurücksetzen'); }
|
||
function apiAdminDeleteUser(id) { return _del('/api/admin/users/' + id, 'Fehler beim Löschen'); }
|
||
function apiAdminImpersonate(id) { return _post('/api/admin/users/' + id + '/impersonate', undefined, 'Fehler beim Anmelden'); }
|
||
function apiAdminGetLoginLink(id) { return _post('/api/admin/users/' + id + '/login-link', undefined, 'Fehler beim Erstellen des Login-Links'); }
|
||
|
||
// ── Server Settings API (Admin) ──
|
||
|
||
function apiGetServerSettings() { return _get('/api/admin/server-settings'); }
|
||
function apiSaveServerSettings(data) { return _put('/api/admin/server-settings', data, 'Fehler beim Speichern'); }
|
||
|
||
// ── API Keys (Admin) ──
|
||
|
||
function apiGetApiKeys() { return _get('/api/admin/api-keys'); }
|
||
function apiCreateApiKey(data) { return _post('/api/admin/api-keys', data, 'Fehler beim Erstellen'); }
|
||
function apiUpdateApiKey(id, data) { return _put('/api/admin/api-keys/' + id, data, 'Fehler beim Aktualisieren'); }
|
||
function apiToggleApiKeyPublic(id) { return _put('/api/admin/api-keys/' + id + '/public', undefined, 'Fehler beim Umschalten'); }
|
||
|
||
// ── User Key Selection API ──
|
||
|
||
function apiGetAvailableKeys() { return _get('/api/api-keys/available', []); }
|
||
function apiSelectApiKey(keyId, agentId) { return _put('/api/api-keys/select/' + keyId + '?agentId=' + encodeURIComponent(agentId), undefined, 'Fehler beim Auswählen'); }
|
||
function apiGetSelectedKey(agentId) { return _get('/api/api-keys/selected' + (agentId ? '?agentId=' + encodeURIComponent(agentId) : ''), null); }
|
||
function apiDeleteApiKey(id) { return _del('/api/admin/api-keys/' + id, 'Fehler beim Löschen'); }
|
||
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'); }
|
||
function apiScanStatus() { return _get('/api/admin/api-keys/scan/status', {}); }
|
||
function apiGetScannedKeys(type) { return _get('/api/admin/api-keys/scanned' + (type ? '?type=' + encodeURIComponent(type) : ''), []); }
|
||
function apiGetScannedKeysSummary() { return _get('/api/admin/api-keys/scanned/summary', []); }
|
||
function apiRecheckAllScannedKeys() { return _post('/api/admin/api-keys/scanned/recheck-all', undefined, 'Fehler beim Prüfen'); }
|
||
function apiRecheckScannedKey(id) { return _post('/api/admin/api-keys/scanned/' + id + '/recheck', undefined, 'Fehler beim Prüfen'); }
|
||
function apiAdoptScannedKey(id) { return _post('/api/admin/api-keys/scanned/' + id + '/adopt', undefined, 'Fehler beim Übernehmen'); }
|
||
function apiDeleteScannedKey(id) { return _del('/api/admin/api-keys/scanned/' + id, 'Fehler beim Löschen'); }
|
||
function apiEventScannerStatus() { return _get('/api/admin/api-keys/event-scanner/status', {}); }
|
||
function apiEventScannerLog() { return _get('/api/admin/api-keys/event-scanner/log', { entries: [] }); }
|
||
|
||
|
||
// ── Krea Image API ──
|
||
|
||
function apiKreaStatus() { return _get('/api/images/status', { configured: false }); }
|
||
function apiKreaGenerate(prompt, model, width, height) {
|
||
return _post('/api/images/generate', {
|
||
prompt: prompt, model: model || 'flux', width: width || 512, height: height || 512, batchSize: 1
|
||
}, 'Bildgenerierung fehlgeschlagen');
|
||
}
|
||
|
||
// ── OpenClaw Import API ──
|
||
|
||
function apiGetOpenclawSessions() { return _get('/api/openclaw/sessions'); }
|
||
function apiImportOpenclawSession(openclawSessionId, sessionId) {
|
||
return _post('/api/openclaw/import', { openclawSessionId: openclawSessionId, sessionId: sessionId }, 'Import fehlgeschlagen');
|
||
}
|
||
|
||
// ── Remote API ──
|
||
|
||
function apiGetRemoteConnections() { return _get('/api/remote/connections', []); }
|
||
function apiAssignRemote(connectionId, sessionId) { return _post('/api/remote/assign', { connectionId: connectionId, sessionId: sessionId }); }
|
||
function apiUnassignRemote(connectionId) { return _post('/api/remote/unassign', { connectionId: connectionId }); }
|
||
|
||
// ── Backup API ──
|
||
|
||
async function apiBackupDb() {
|
||
var resp = await fetch('/api/backup', { method: 'POST', credentials: 'include' });
|
||
if (!resp.ok) throw new Error('Backup fehlgeschlagen');
|
||
var blob = await resp.blob();
|
||
var cd = resp.headers.get('content-disposition') || '';
|
||
var filename = cd.match(/filename\*=UTF-8''(.+?)(?:;|$)/i)?.[1]
|
||
|| cd.match(/filename="?([^";]+)"?/)?.[1]
|
||
|| 'gott-backup.tar.gz';
|
||
var url = URL.createObjectURL(blob);
|
||
var a = document.createElement('a');
|
||
a.href = url;
|
||
a.download = filename;
|
||
document.body.appendChild(a);
|
||
a.click();
|
||
a.remove();
|
||
URL.revokeObjectURL(url);
|
||
}
|