@dollhousemcp/mcp-server
Version:
DollhouseMCP - A Model Context Protocol (MCP) server that enables dynamic AI persona management from markdown files, allowing Claude and other compatible AI assistants to activate and switch between different behavioral personas.
114 lines • 15.8 kB
JavaScript
import { LRUCache } from '../../cache/LRUCache.js';
import { logger } from '../../utils/logger.js';
export class TriggerMetricsTracker {
options;
metricsBatch;
flushTimer = null;
logListener;
addLogListener(fn) {
this.logListener = fn;
}
constructor(options) {
this.options = options;
this.metricsBatch = new LRUCache({
name: 'trigger-metrics',
maxSize: options.cacheLimits.maxSize,
maxMemoryMB: options.cacheLimits.maxMemoryMB
});
}
async track(trigger, immediate = false) {
this.metricsBatch.set(trigger, (this.metricsBatch.get(trigger) || 0) + 1);
if (immediate || this.pendingCount >= this.options.batchSize) {
await this.flush();
return;
}
if (!this.flushTimer) {
this.flushTimer = setTimeout(() => {
this.flush().catch(error => {
logger.warn('Failed to flush metrics batch', { error });
});
}, this.options.flushIntervalMs);
if (typeof this.flushTimer.unref === 'function') {
this.flushTimer.unref();
}
}
}
async flush() {
if (this.pendingCount === 0) {
return;
}
if (this.flushTimer) {
clearTimeout(this.flushTimer);
this.flushTimer = null;
}
const entries = Array.from(this.metricsBatch.entries());
const index = await this.options.getIndex();
if (!index.metadata.trigger_metrics) {
index.metadata.trigger_metrics = {
usage_count: {},
last_used: {},
first_used: {},
daily_usage: {}
};
}
const metrics = index.metadata.trigger_metrics;
const today = new Date().toISOString().split('T')[0];
const now = new Date().toISOString();
if (!metrics.daily_usage[today]) {
metrics.daily_usage[today] = {};
}
for (const [trigger, count] of entries) {
metrics.usage_count[trigger] = (metrics.usage_count[trigger] || 0) + count;
metrics.last_used[trigger] = now;
if (!metrics.first_used[trigger]) {
metrics.first_used[trigger] = now;
}
metrics.daily_usage[today][trigger] = (metrics.daily_usage[today][trigger] || 0) + count;
logger.debug('Flushing batched metrics', {
trigger,
batch_count: count,
total_uses: metrics.usage_count[trigger]
});
}
const cutoffDate = new Date();
cutoffDate.setDate(cutoffDate.getDate() - 30);
const cutoff = cutoffDate.toISOString().split('T')[0];
for (const date in metrics.daily_usage) {
if (date < cutoff) {
delete metrics.daily_usage[date];
}
}
index.metadata.last_updated = now;
try {
await this.options.persistIndex(index);
const totalUpdates = entries.reduce((sum, [, count]) => sum + count, 0);
logger.info('Metrics batch flushed', {
triggers_updated: entries.length,
total_updates: totalUpdates
});
this.logListener?.('info', 'Flush metrics batch', {
triggers_updated: entries.length,
total_updates: totalUpdates,
});
this.metricsBatch.clear();
}
catch (error) {
logger.error('Failed to persist trigger metrics', { error });
this.logListener?.('warn', 'Fail to persist trigger metrics', {
error: error instanceof Error ? error.message : String(error),
});
// Do not clear batch so we can retry on next flush attempt
}
}
dispose() {
if (this.flushTimer) {
clearTimeout(this.flushTimer);
this.flushTimer = null;
}
this.metricsBatch.clear();
}
get pendingCount() {
return this.metricsBatch.getStats().size;
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiVHJpZ2dlck1ldHJpY3NUcmFja2VyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL3BvcnRmb2xpby9lbmhhbmNlZC1pbmRleC9UcmlnZ2VyTWV0cmljc1RyYWNrZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFFBQVEsRUFBRSxNQUFNLHlCQUF5QixDQUFDO0FBQ25ELE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQztBQWMvQyxNQUFNLE9BQU8scUJBQXFCO0lBU0g7SUFSckIsWUFBWSxDQUFtQjtJQUMvQixVQUFVLEdBQTBCLElBQUksQ0FBQztJQUN6QyxXQUFXLENBQXlHO0lBRTVILGNBQWMsQ0FBQyxFQUF5RztRQUN0SCxJQUFJLENBQUMsV0FBVyxHQUFHLEVBQUUsQ0FBQztJQUN4QixDQUFDO0lBRUQsWUFBNkIsT0FBcUM7UUFBckMsWUFBTyxHQUFQLE9BQU8sQ0FBOEI7UUFDaEUsSUFBSSxDQUFDLFlBQVksR0FBRyxJQUFJLFFBQVEsQ0FBUztZQUN2QyxJQUFJLEVBQUUsaUJBQWlCO1lBQ3ZCLE9BQU8sRUFBRSxPQUFPLENBQUMsV0FBVyxDQUFDLE9BQU87WUFDcEMsV0FBVyxFQUFFLE9BQU8sQ0FBQyxXQUFXLENBQUMsV0FBVztTQUM3QyxDQUFDLENBQUM7SUFDTCxDQUFDO0lBRU0sS0FBSyxDQUFDLEtBQUssQ0FBQyxPQUFlLEVBQUUsWUFBcUIsS0FBSztRQUM1RCxJQUFJLENBQUMsWUFBWSxDQUFDLEdBQUcsQ0FBQyxPQUFPLEVBQUUsQ0FBQyxJQUFJLENBQUMsWUFBWSxDQUFDLEdBQUcsQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQztRQUUxRSxJQUFJLFNBQVMsSUFBSSxJQUFJLENBQUMsWUFBWSxJQUFJLElBQUksQ0FBQyxPQUFPLENBQUMsU0FBUyxFQUFFLENBQUM7WUFDN0QsTUFBTSxJQUFJLENBQUMsS0FBSyxFQUFF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QUFNO2dCQUNoQyxhQUFhLEVBQUUsWUFBWTthQUM1QixDQUFDLENBQUM7WUFDSCxJQUFJLENBQUMsWUFBWSxDQUFDLEtBQUssRUFBRSxDQUFDO1FBQzVCLENBQUM7UUFBQyxPQUFPLEtBQUssRUFBRSxDQUFDO1lBQ2YsTUFBTSxDQUFDLEtBQUssQ0FBQyxtQ0FBbUMsRUFBRSxFQUFFLEtBQUssRUFBRSxDQUFDLENBQUM7WUFDN0QsSUFBSSxDQUFDLFdBQVcsRUFBRSxDQUFDLE1BQU0sRUFBRSxpQ0FBaUMsRUFBRTtnQkFDNUQsS0FBSyxFQUFFLEtBQUssWUFBWSxLQUFLLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUM7YUFDOUQsQ0FBQyxDQUFDO1lBQ0gsMkRBQTJEO1FBQzdELENBQUM7SUFDSCxDQUFDO0lBRU0sT0FBTztRQUNaLElBQUksSUFBSSxDQUFDLFVBQVUsRUFBRSxDQUFDO1lBQ3BCLFlBQVksQ0FBQyxJQUFJLENBQUMsVUFBVSxDQUFDLENBQUM7WUFDOUIsSUFBSSxDQUFDLFVBQVUsR0FBRyxJQUFJLENBQUM7UUFDekIsQ0FBQztRQUNELElBQUksQ0FBQyxZQUFZLENBQUMsS0FBSyxFQUFFLENBQUM7SUFDNUIsQ0FBQztJQUVELElBQVcsWUFBWTtRQUNyQixPQUFPLElBQUksQ0FBQyxZQUFZLENBQUMsUUFBUSxFQUFFLENBQUMsSUFBSSxDQUFDO0lBQzNDLENBQUM7Q0FDRiIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IExSVUNhY2hlIH0gZnJvbSAnLi4vLi4vY2FjaGUvTFJVQ2FjaGUuanMnO1xuaW1wb3J0IHsgbG9nZ2VyIH0gZnJvbSAnLi4vLi4vdXRpbHMvbG9nZ2VyLmpzJztcbmltcG9ydCB0eXBlIHsgRW5oYW5jZWRJbmRleCB9IGZyb20gJy4uL3R5cGVzL0luZGV4VHlwZXMuanMnO1xuXG5leHBvcnQgaW50ZXJmYWNlIFRyaWdnZXJNZXRyaWNzVHJhY2tlck9wdGlvbnMge1xuICBiYXRjaFNpemU6IG51bWJlcjtcbiAgZmx1c2hJbnRlcnZhbE1zOiBudW1iZXI7XG4gIGNhY2hlTGltaXRzOiB7XG4gICAgbWF4U2l6ZTogbnVtYmVyO1xuICAgIG1heE1lbW9yeU1COiBudW1iZXI7XG4gIH07XG4gIGdldEluZGV4OiAoKSA9PiBQcm9taXNlPEVuaGFuY2VkSW5kZXg+O1xuICBwZXJzaXN0SW5kZXg6IChpbmRleDogRW5oYW5jZWRJbmRleCkgPT4gUHJvbWlzZTx2b2lkPjtcbn1cblxuZXhwb3J0IGNsYXNzIFRyaWdnZXJNZXRyaWNzVHJhY2tlciB7XG4gIHByaXZhdGUgbWV0cmljc0JhdGNoOiBMUlVDYWNoZTxudW1iZXI+O1xuICBwcml2YXRlIGZsdXNoVGltZXI6IE5vZGVKUy5UaW1lb3V0IHwgbnVsbCA9IG51bGw7XG4gIHByaXZhdGUgbG9nTGlzdGVuZXI/OiAobGV2ZWw6ICdkZWJ1ZycgfCAnaW5mbycgfCAnd2FybicgfCAnZXJyb3InLCBtZXNzYWdlOiBzdHJpbmcsIGRhdGE/OiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPikgPT4gdm9pZDtcblxuICBhZGRMb2dMaXN0ZW5lcihmbjogKGxldmVsOiAnZGVidWcnIHwgJ2luZm8nIHwgJ3dhcm4nIHwgJ2Vycm9yJywgbWVzc2FnZTogc3RyaW5nLCBkYXRhPzogUmVjb3JkPHN0cmluZywgdW5rbm93bj4pID0+IHZvaWQpOiB2b2lkIHtcbiAgICB0aGlzLmxvZ0xpc3RlbmVyID0gZm47XG4gIH1cblxuICBjb25zdHJ1Y3Rvcihwcml2YXRlIHJlYWRvbmx5IG9wdGlvbnM6IFRyaWdnZXJNZXRyaWNzVHJhY2tlck9wdGlvbnMpIHtcbiAgICB0aGlzLm1ldHJpY3NCYXRjaCA9IG5ldyBMUlVDYWNoZTxudW1iZXI+KHtcbiAgICAgIG5hbWU6ICd0cmlnZ2VyLW1ldHJpY3MnLFxuICAgICAgbWF4U2l6ZTogb3B0aW9ucy5jYWNoZUxpbWl0cy5tYXhTaXplLFxuICAgICAgbWF4TWVtb3J5TUI6IG9wdGlvbnMuY2FjaGVMaW1pdHMubWF4TWVtb3J5TUJcbiAgICB9KTtcbiAgfVxuXG4gIHB1YmxpYyBhc3luYyB0cmFjayh0cmlnZ2VyOiBzdHJpbmcsIGltbWVkaWF0ZTogYm9vbGVhbiA9IGZhbHNlKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgdGhpcy5tZXRyaWNzQmF0Y2guc2V0KHRyaWdnZXIsICh0aGlzLm1ldHJpY3NCYXRjaC5nZXQodHJpZ2dlcikgfHwgMCkgKyAxKTtcblxuICAgIGlmIChpbW1lZGlhdGUgfHwgdGhpcy5wZW5kaW5nQ291bnQgPj0gdGhpcy5vcHRpb25zLmJhdGNoU2l6ZSkge1xuICAgICAgYXdhaXQgdGhpcy5mbHVzaCgpO1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIGlmICghdGhpcy5mbHVzaFRpbWVyKSB7XG4gICAgICB0aGlzLmZsdXNoVGltZXIgPSBzZXRUaW1lb3V0KCgpID0+IHtcbiAgICAgICAgdGhpcy5mbHVzaCgpLmNhdGNoKGVycm9yID0+IHtcbiAgICAgICAgICBsb2dnZXIud2FybignRmFpbGVkIHRvIGZsdXNoIG1ldHJpY3MgYmF0Y2gnLCB7IGVycm9yIH0pO1xuICAgICAgICB9KTtcbiAgICAgIH0sIHRoaXMub3B0aW9ucy5mbHVzaEludGVydmFsTXMpO1xuXG4gICAgICBpZiAodHlwZW9mIHRoaXMuZmx1c2hUaW1lci51bnJlZiA9PT0gJ2Z1bmN0aW9uJykge1xuICAgICAgICB0aGlzLmZsdXNoVGltZXIudW5yZWYoKTtcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICBwdWJsaWMgYXN5bmMgZmx1c2goKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgaWYgKHRoaXMucGVuZGluZ0NvdW50ID09PSAwKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgaWYgKHRoaXMuZmx1c2hUaW1lcikge1xuICAgICAgY2xlYXJUaW1lb3V0KHRoaXMuZmx1c2hUaW1lcik7XG4gICAgICB0aGlzLmZsdXNoVGltZXIgPSBudWxsO1xuICAgIH1cblxuICAgIGNvbnN0IGVudHJpZXMgPSBBcnJheS5mcm9tKHRoaXMubWV0cmljc0JhdGNoLmVudHJpZXMoKSk7XG4gICAgY29uc3QgaW5kZXggPSBhd2FpdCB0aGlzLm9wdGlvbnMuZ2V0SW5kZXgoKTtcblxuICAgIGlmICghaW5kZXgubWV0YWRhdGEudHJpZ2dlcl9tZXRyaWNzKSB7XG4gICAgICBpbmRleC5tZXRhZGF0YS50cmlnZ2VyX21ldHJpY3MgPSB7XG4gICAgICAgIHVzYWdlX2NvdW50OiB7fSxcbiAgICAgICAgbGFzdF91c2VkOiB7fSxcbiAgICAgICAgZmlyc3RfdXNlZDoge30sXG4gICAgICAgIGRhaWx5X3VzYWdlOiB7fVxuICAgICAgfTtcbiAgICB9XG5cbiAgICBjb25zdCBtZXRyaWNzID0gaW5kZXgubWV0YWRhdGEudHJpZ2dlcl9tZXRyaWNzO1xuICAgIGNvbnN0IHRvZGF5ID0gbmV3IERhdGUoKS50b0lTT1N0cmluZygpLnNwbGl0KCdUJylbMF07XG4gICAgY29uc3Qgbm93ID0gbmV3IERhdGUoKS50b0lTT1N0cmluZygpO1xuXG4gICAgaWYgKCFtZXRyaWNzLmRhaWx5X3VzYWdlW3RvZGF5XSkge1xuICAgICAgbWV0cmljcy5kYWlseV91c2FnZVt0b2RheV0gPSB7fTtcbiAgICB9XG5cbiAgICBmb3IgKGNvbnN0IFt0cmlnZ2VyLCBjb3VudF0gb2YgZW50cmllcykge1xuICAgICAgbWV0cmljcy51c2FnZV9jb3VudFt0cmlnZ2VyXSA9IChtZXRyaWNzLnVzYWdlX2NvdW50W3RyaWdnZXJdIHx8IDApICsgY291bnQ7XG4gICAgICBtZXRyaWNzLmxhc3RfdXNlZFt0cmlnZ2VyXSA9IG5vdztcbiAgICAgIGlmICghbWV0cmljcy5maXJzdF91c2VkW3RyaWdnZXJdKSB7XG4gICAgICAgIG1ldHJpY3MuZmlyc3RfdXNlZFt0cmlnZ2VyXSA9IG5vdztcbiAgICAgIH1cblxuICAgICAgbWV0cmljcy5kYWlseV91c2FnZVt0b2RheV1bdHJpZ2dlcl0gPSAobWV0cmljcy5kYWlseV91c2FnZVt0b2RheV1bdHJpZ2dlcl0gfHwgMCkgKyBjb3VudDtcblxuICAgICAgbG9nZ2VyLmRlYnVnKCdGbHVzaGluZyBiYXRjaGVkIG1ldHJpY3MnLCB7XG4gICAgICAgIHRyaWdnZXIsXG4gICAgICAgIGJhdGNoX2NvdW50OiBjb3VudCxcbiAgICAgICAgdG90YWxfdXNlczogbWV0cmljcy51c2FnZV9jb3VudFt0cmlnZ2VyXVxuICAgICAgfSk7XG4gICAgfVxuXG4gICAgY29uc3QgY3V0b2ZmRGF0ZSA9IG5ldyBEYXRlKCk7XG4gICAgY3V0b2ZmRGF0ZS5zZXREYXRlKGN1dG9mZkRhdGUuZ2V0RGF0ZSgpIC0gMzApO1xuICAgIGNvbnN0IGN1dG9mZiA9IGN1dG9mZkRhdGUudG9JU09TdHJpbmcoKS5zcGxpdCgnVCcpWzBdO1xuXG4gICAgZm9yIChjb25zdCBkYXRlIGluIG1ldHJpY3MuZGFpbHlfdXNhZ2UpIHtcbiAgICAgIGlmIChkYXRlIDwgY3V0b2ZmKSB7XG4gICAgICAgIGRlbGV0ZSBtZXRyaWNzLmRhaWx5X3VzYWdlW2RhdGVdO1xuICAgICAgfVxuICAgIH1cblxuICAgIGluZGV4Lm1ldGFkYXRhLmxhc3RfdXBkYXRlZCA9IG5vdztcblxuICAgIHRyeSB7XG4gICAgICBhd2FpdCB0aGlzLm9wdGlvbnMucGVyc2lzdEluZGV4KGluZGV4KTtcbiAgICAgIGNvbnN0IHRvdGFsVXBkYXRlcyA9IGVudHJpZXMucmVkdWNlKChzdW0sIFssIGNvdW50XSkgPT4gc3VtICsgY291bnQsIDApO1xuICAgICAgbG9nZ2VyLmluZm8oJ01ldHJpY3MgYmF0Y2ggZmx1c2hlZCcsIHtcbiAgICAgICAgdHJpZ2dlcnNfdXBkYXRlZDogZW50cmllcy5sZW5ndGgsXG4gICAgICAgIHRvdGFsX3VwZGF0ZXM6IHRvdGFsVXBkYXRlc1xuICAgICAgfSk7XG4gICAgICB0aGlzLmxvZ0xpc3RlbmVyPy4oJ2luZm8nLCAnRmx1c2ggbWV0cmljcyBiYXRjaCcsIHtcbiAgICAgICAgdHJpZ2dlcnNfdXBkYXRlZDogZW50cmllcy5sZW5ndGgsXG4gICAgICAgIHRvdGFsX3VwZGF0ZXM6IHRvdGFsVXBkYXRlcyxcbiAgICAgIH0pO1xuICAgICAgdGhpcy5tZXRyaWNzQmF0Y2guY2xlYXIoKTtcbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgbG9nZ2VyLmVycm9yKCdGYWlsZWQgdG8gcGVyc2lzdCB0cmlnZ2VyIG1ldHJpY3MnLCB7IGVycm9yIH0pO1xuICAgICAgdGhpcy5sb2dMaXN0ZW5lcj8uKCd3YXJuJywgJ0ZhaWwgdG8gcGVyc2lzdCB0cmlnZ2VyIG1ldHJpY3MnLCB7XG4gICAgICAgIGVycm9yOiBlcnJvciBpbnN0YW5jZW9mIEVycm9yID8gZXJyb3IubWVzc2FnZSA6IFN0cmluZyhlcnJvciksXG4gICAgICB9KTtcbiAgICAgIC8vIERvIG5vdCBjbGVhciBiYXRjaCBzbyB3ZSBjYW4gcmV0cnkgb24gbmV4dCBmbHVzaCBhdHRlbXB0XG4gICAgfVxuICB9XG5cbiAgcHVibGljIGRpc3Bvc2UoKTogdm9pZCB7XG4gICAgaWYgKHRoaXMuZmx1c2hUaW1lcikge1xuICAgICAgY2xlYXJUaW1lb3V0KHRoaXMuZmx1c2hUaW1lcik7XG4gICAgICB0aGlzLmZsdXNoVGltZXIgPSBudWxsO1xuICAgIH1cbiAgICB0aGlzLm1ldHJpY3NCYXRjaC5jbGVhcigpO1xuICB9XG5cbiAgcHVibGljIGdldCBwZW5kaW5nQ291bnQoKTogbnVtYmVyIHtcbiAgICByZXR1cm4gdGhpcy5tZXRyaWNzQmF0Y2guZ2V0U3RhdHMoKS5zaXplO1xuICB9XG59XG4iXX0=