Files
gott/wwwroot/js/api.js
T
MelekiandClaude Opus 4.8 b6e546f618 Add workflow-status telemetry view and make panel modes on-demand
Workflow-status: a new WorkflowProgressTracker singleton taps the
suppressed dynamic-workflow subagent events (those carrying
parent_tool_use_id) into per-session telemetry without altering the
existing guard; the snapshot is serialized into the new chains.workflow_info
column on FinalizeChain (ChainId-matched so a panel member of the same
session cannot clear it) and exposed via GET /api/sessions/{id}/workflow-status
(live -> persisted -> none). The header menu gains a 'Workflow-Status' entry
with a modal that live-polls every 2s while running and self-stops on close.

Panel on-demand: Fusion/MoA is now a transient per-request multiModelMode
flag (like plan/ultracode) instead of a persisted session column. Session
settings keep only the panel members + moa_layers; the mode <select> was
removed and the composer dropdown drives the mode per request.

Docs: ARCHITECTURE.md, ARCHITECTURE_QUICK.md and README.md updated for both
features plus the already-shipped z.ai/GLM provider, GLM 5.2+ effort
passthrough, web-search-prime MCP and the ultracode toggle.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 17:27:46 +02:00

310 lines
15 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// ── 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'); }
// ── 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);
}