100 lines
2.4 KiB
TypeScript
100 lines
2.4 KiB
TypeScript
// @ts-nocheck
|
|
|
|
|
|
const STORAGE_KEY = 'ccsparkle_audit_log';
|
|
const MAX_ENTRIES = 500;
|
|
|
|
let _entries = [];
|
|
let _listeners = [];
|
|
|
|
function load() {
|
|
try {
|
|
const s = localStorage.getItem(STORAGE_KEY);
|
|
_entries = s ? JSON.parse(s) : [];
|
|
} catch (e) { _entries = []; }
|
|
}
|
|
|
|
function save() {
|
|
try {
|
|
localStorage.setItem(STORAGE_KEY, JSON.stringify(_entries));
|
|
} catch (e) {
|
|
|
|
_entries = _entries.slice(-Math.floor(MAX_ENTRIES / 2));
|
|
try {
|
|
localStorage.setItem(STORAGE_KEY, JSON.stringify(_entries));
|
|
} catch (e2) {}
|
|
}
|
|
}
|
|
|
|
load();
|
|
|
|
export function log(type, summary, details = null) {
|
|
const entry = {
|
|
id: `log_${Date.now()}_${Math.random().toString(36).slice(2, 6)}`,
|
|
ts: Date.now(),
|
|
type,
|
|
summary: String(summary || '').slice(0, 200),
|
|
details: details || null,
|
|
};
|
|
_entries.push(entry);
|
|
if (_entries.length > MAX_ENTRIES) {
|
|
_entries = _entries.slice(-MAX_ENTRIES);
|
|
}
|
|
save();
|
|
_listeners.forEach(fn => fn(entry, _entries));
|
|
return entry.id;
|
|
}
|
|
|
|
export function getAll() {
|
|
return [..._entries].reverse();
|
|
}
|
|
|
|
export function clear() {
|
|
_entries = [];
|
|
save();
|
|
_listeners.forEach(fn => fn(null, _entries));
|
|
}
|
|
|
|
export function remove(id) {
|
|
_entries = _entries.filter(e => e.id !== id);
|
|
save();
|
|
_listeners.forEach(fn => fn(null, _entries));
|
|
}
|
|
|
|
export function exportJson() {
|
|
const data = JSON.stringify(_entries, null, 2);
|
|
const blob = new Blob([data], { type: 'application/json' });
|
|
const url = URL.createObjectURL(blob);
|
|
const a = document.createElement('a');
|
|
a.href = url;
|
|
a.download = `ccsparkle-audit-${new Date().toISOString().replace(/[:.]/g, '-')}.json`;
|
|
document.body.appendChild(a);
|
|
a.click();
|
|
document.body.removeChild(a);
|
|
URL.revokeObjectURL(url);
|
|
}
|
|
|
|
export function subscribe(fn) {
|
|
_listeners.push(fn);
|
|
return () => {
|
|
_listeners = _listeners.filter(f => f !== fn);
|
|
};
|
|
}
|
|
|
|
export function formatTs(ts) {
|
|
const d = new Date(ts);
|
|
const pad = n => String(n).padStart(2, '0');
|
|
return `${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
|
|
}
|
|
|
|
export function formatType(type) {
|
|
return ({
|
|
'user': '用户',
|
|
'assistant': '模型',
|
|
'tool:start': '调用',
|
|
'tool:end': '完成',
|
|
'system': '系统',
|
|
'error': '错误',
|
|
})[type] || type;
|
|
}
|