feat(gateway): expand sessions surface with operator metadata and paging hardening
This commit is contained in:
@@ -12,6 +12,30 @@ function escapeHtml(text) {
|
||||
|
||||
let _client = null;
|
||||
let _el = null;
|
||||
let _frontendFilter = '';
|
||||
let _includeInactive = true;
|
||||
|
||||
function formatTime(ts) {
|
||||
if (!ts || !Number.isFinite(ts)) {return '—';}
|
||||
try {
|
||||
return new Date(ts).toLocaleString();
|
||||
} catch {
|
||||
return '—';
|
||||
}
|
||||
}
|
||||
|
||||
function formatQueue(config) {
|
||||
const queue = config?.queue;
|
||||
if (!queue?.mode) {return 'default';}
|
||||
const parts = [queue.mode];
|
||||
if (typeof queue.debounceMs === 'number') {
|
||||
parts.push(`${queue.debounceMs}ms`);
|
||||
}
|
||||
if (typeof queue.cap === 'number') {
|
||||
parts.push(`cap:${queue.cap}`);
|
||||
}
|
||||
return parts.join(' · ');
|
||||
}
|
||||
|
||||
async function loadSessionList() {
|
||||
if (!_client || !_el) {return;}
|
||||
@@ -21,7 +45,11 @@ async function loadSessionList() {
|
||||
if (detailContainer) {detailContainer.innerHTML = '';}
|
||||
|
||||
try {
|
||||
const result = await _client.call('sessions.list');
|
||||
const params = {
|
||||
includePersisted: _includeInactive,
|
||||
frontend: _frontendFilter || undefined,
|
||||
};
|
||||
const result = await _client.call('sessions.list', params);
|
||||
const sessions = result.sessions ?? [];
|
||||
|
||||
if (sessions.length === 0) {
|
||||
@@ -34,7 +62,11 @@ async function loadSessionList() {
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Session ID</th>
|
||||
<th>Frontend</th>
|
||||
<th>Messages</th>
|
||||
<th>Model</th>
|
||||
<th>Queue</th>
|
||||
<th>Last Activity</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -45,7 +77,11 @@ async function loadSessionList() {
|
||||
html += `
|
||||
<tr>
|
||||
<td><a href="#" class="session-view-link" data-id="${escapeHtml(s.id)}">${escapeHtml(s.id)}</a></td>
|
||||
<td>${escapeHtml(s.frontend ?? (String(s.id).split(':')[0] || 'unknown'))}</td>
|
||||
<td>${s.messageCount ?? 0}</td>
|
||||
<td>${escapeHtml(s.config?.modelTier ?? 'default')}</td>
|
||||
<td>${escapeHtml(formatQueue(s.config))}</td>
|
||||
<td>${escapeHtml(formatTime(s.lastMessageAt))}</td>
|
||||
<td class="session-actions">
|
||||
<button class="btn btn-secondary session-view-btn" data-id="${escapeHtml(s.id)}">View</button>
|
||||
<button class="btn btn-danger session-delete-btn" data-id="${escapeHtml(s.id)}">Delete</button>
|
||||
@@ -132,10 +168,53 @@ export const SessionsPage = {
|
||||
|
||||
el.innerHTML = `
|
||||
<h1 class="page-title">Sessions</h1>
|
||||
<div class="status-row" style="margin-bottom: 0.75rem; gap: 0.75rem; flex-wrap: wrap;">
|
||||
<label class="text-sm text-muted">Frontend
|
||||
<select id="sessions-frontend-filter" style="margin-left: 0.35rem;">
|
||||
<option value="">All</option>
|
||||
<option value="ws">ws</option>
|
||||
<option value="tui">tui</option>
|
||||
<option value="telegram">telegram</option>
|
||||
<option value="slack">slack</option>
|
||||
<option value="discord">discord</option>
|
||||
<option value="mattermost">mattermost</option>
|
||||
</select>
|
||||
</label>
|
||||
<label class="text-sm text-muted">
|
||||
<input id="sessions-include-inactive" type="checkbox" checked />
|
||||
Include inactive/persisted
|
||||
</label>
|
||||
<button id="sessions-refresh-btn" class="btn btn-secondary">Refresh</button>
|
||||
</div>
|
||||
<div id="sessions-list"></div>
|
||||
<div id="session-detail"></div>
|
||||
`;
|
||||
|
||||
const frontendSelect = el.querySelector('#sessions-frontend-filter');
|
||||
if (frontendSelect) {
|
||||
frontendSelect.value = _frontendFilter;
|
||||
frontendSelect.addEventListener('change', async () => {
|
||||
_frontendFilter = frontendSelect.value;
|
||||
await loadSessionList();
|
||||
});
|
||||
}
|
||||
|
||||
const inactiveToggle = el.querySelector('#sessions-include-inactive');
|
||||
if (inactiveToggle) {
|
||||
inactiveToggle.checked = _includeInactive;
|
||||
inactiveToggle.addEventListener('change', async () => {
|
||||
_includeInactive = inactiveToggle.checked;
|
||||
await loadSessionList();
|
||||
});
|
||||
}
|
||||
|
||||
const refreshBtn = el.querySelector('#sessions-refresh-btn');
|
||||
if (refreshBtn) {
|
||||
refreshBtn.addEventListener('click', async () => {
|
||||
await loadSessionList();
|
||||
});
|
||||
}
|
||||
|
||||
await loadSessionList();
|
||||
},
|
||||
|
||||
|
||||
Reference in New Issue
Block a user