@kenhkl/agent-moss 0.12.3 → 0.12.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -991,6 +991,7 @@ let tokenStatsDays = 7;
991
991
  let tokenRefreshTimer = null;
992
992
  let trendChartInstance = null;
993
993
  function render_token() {
994
+ tokenRecentPage = 1; // 切到 Token 页时重置最近调用分页到第 1 页
994
995
  el('page-token').innerHTML = `
995
996
  <div class="ag-card">
996
997
  <div class="ag-section-title">${t('Token 用量详细统计', 'Detailed Token Statistics')
@@ -1132,22 +1133,57 @@ function renderTokenTrend(data) {
1132
1133
  })),
1133
1134
  }, true);
1134
1135
  }
1136
+ // Token 最近调用分页状态
1137
+ let tokenRecentPage = 1;
1138
+ const TOKEN_RECENT_PAGE_SIZE = 10;
1139
+ let tokenRecentTotal = 0; // 最近调用总条数(供跳转页时计算总页数)
1135
1140
  async function loadTokenRecent() {
1136
1141
  const recentEl = el('token-recent');
1137
1142
  if (!recentEl) return;
1138
1143
  try {
1139
- const records = await api('token-stats/recent?limit=10');
1144
+ const offset = (tokenRecentPage - 1) * TOKEN_RECENT_PAGE_SIZE;
1145
+ const data = await api(`token-stats/recent?limit=${TOKEN_RECENT_PAGE_SIZE}&offset=${offset}`);
1146
+ const records = Array.isArray(data) ? data : (data?.records || []);
1147
+ const total = Array.isArray(data) ? records.length : (data?.total ?? records.length);
1148
+ tokenRecentTotal = total; // 供跳转页时计算总页数
1140
1149
  if (!records || records.length === 0) { recentEl.innerHTML = `<div class="ag-empty">${t('暂无记录', 'No records yet')}</div>`; return; }
1141
1150
  let html = '<table class="ag-table"><tr><th>' + t('时间', 'Time') + '</th><th>' + t('步骤', 'Step') + '</th><th>' + t('模型', 'Model') + '</th><th>' + t('输入', 'Input') + '</th><th>' + t('输出', 'Output') + '</th><th>' + t('总计', 'Total') + '</th></tr>';
1142
1151
  for (const r of records.reverse()) {
1143
1152
  const stepName = r.step === 'L3_security_judge' ? t('L3 安全判断', 'L3 Security Judge') : r.step === 'step2_policy_gen' ? t('策略生成', 'Policy Generation') : escapeHtml(r.step);
1144
- const ts = r.timestamp ? escapeHtml(r.timestamp.slice(0, 19)) : '';
1145
- html += '<tr><td>' + ts + '</td><td>' + stepName + '</td><td style="color:var(--ag-text-secondary)">' + escapeHtml(r.model || '') + '</td><td>' + r.prompt_tokens + '</td><td>' + r.completion_tokens + '</td><td><strong>' + r.total_tokens + '</strong></td></tr>';
1153
+ // timestamp 是 UTC ISO(带 Z),转本地时区显示
1154
+ let ts = '';
1155
+ try { ts = r.timestamp ? new Date(r.timestamp).toLocaleString() : ''; } catch { ts = r.timestamp || ''; }
1156
+ html += '<tr><td>' + escapeHtml(ts) + '</td><td>' + stepName + '</td><td style="color:var(--ag-text-secondary)">' + escapeHtml(r.model || '') + '</td><td>' + r.prompt_tokens + '</td><td>' + r.completion_tokens + '</td><td><strong>' + r.total_tokens + '</strong></td></tr>';
1146
1157
  }
1147
1158
  html += '</table>';
1159
+ // 分页控制条:上一页 / 页码显示 / 输入页码跳转 / 下一页
1160
+ const totalPages = Math.max(1, Math.ceil(total / TOKEN_RECENT_PAGE_SIZE));
1161
+ html += `<div style="margin-top:10px;display:inline-flex;align-items:center;gap:8px;flex-wrap:wrap">
1162
+ <button class="ag-btn ag-btn-sm" onclick="tokenRecentPage<=1?null:loadTokenRecentPage(${tokenRecentPage - 1})" ${tokenRecentPage <= 1 ? 'disabled' : ''}>${t('上一页', 'Prev')}</button>
1163
+ <span style="font-size:var(--fs-md);color:var(--ag-text-secondary)">${t('第 ', 'Page ')}${tokenRecentPage} / ${totalPages}${t('(共 ', ' (total ')}${total}${t(' 条)', ')')}</span>
1164
+ <input id="token-recent-goto" type="number" min="1" max="${totalPages}" value="${tokenRecentPage}" style="width:60px;padding:3px 6px;font-size:var(--fs-md);border:1px solid var(--ag-input-border);border-radius:6px;background:var(--ag-input-bg);color:var(--ag-text);" placeholder="${tokenRecentPage}">
1165
+ <button class="ag-btn ag-btn-sm" onclick="goTokenRecentPage()">${t('跳转', 'Go')}</button>
1166
+ <button class="ag-btn ag-btn-sm" onclick="tokenRecentPage>=${totalPages}?null:loadTokenRecentPage(${tokenRecentPage + 1})" ${tokenRecentPage >= totalPages ? 'disabled' : ''}>${t('下一页', 'Next')}</button>
1167
+ </div>`;
1148
1168
  recentEl.innerHTML = html;
1149
1169
  } catch (e) { recentEl.innerHTML = `<div class="ag-empty">${t('加载失败', 'Load failed')}</div>`; }
1150
1170
  }
1171
+ function loadTokenRecentPage(page) {
1172
+ tokenRecentPage = page;
1173
+ loadTokenRecent();
1174
+ }
1175
+ /** 跳转到用户输入的页码 */
1176
+ async function goTokenRecentPage() {
1177
+ const input = el('token-recent-goto');
1178
+ if (!input) return;
1179
+ const page = parseInt(input.value, 10);
1180
+ if (!page || page < 1) { showToast(t('页码无效', 'Invalid page number'), 'error'); return; }
1181
+ // 计算总页数,超出则跳到最后一页
1182
+ const totalPages = Math.max(1, Math.ceil(tokenRecentTotal / TOKEN_RECENT_PAGE_SIZE));
1183
+ const target = Math.min(page, totalPages);
1184
+ tokenRecentPage = target;
1185
+ loadTokenRecent();
1186
+ }
1151
1187
  async function resetTokenStats() {
1152
1188
  if (!confirm(t('确定清除所有 Token 统计数据?', 'Clear all Token statistics?'))) return;
1153
1189
  try { await apiPost('token-stats/reset'); showToast(t('Token 统计已清除', 'Token statistics cleared')); loadTokenStats(7); }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kenhkl/agent-moss",
3
- "version": "0.12.3",
3
+ "version": "0.12.5",
4
4
  "description": "Multi-layer security analysis engine for AI agents (TypeScript)",
5
5
  "type": "module",
6
6
  "main": "dist/server.js",