Greasy Fork is available in English.
Add a Cost column after Requests on Cursor Usage page while keeping the original Requests column.
// ==UserScript==
// @name Cursor Usage: show Requests + Cost
// @namespace https://cursor.com/
// @version 0.1.2
// @description Add a Cost column after Requests on Cursor Usage page while keeping the original Requests column.
// @match https://cursor.com/dashboard/usage*
// @match https://cursor.com/*/dashboard/usage*
// @run-at document-start
// @grant none
// @license MIT
// ==/UserScript==
(function () {
'use strict';
const PATCH_KEY = '__tmCursorUsageRequestsAndCost';
const keyOf = (event) =>
[
event?.timestamp,
event?.model,
event?.requestsCosts,
event?.owningUser || '',
].join('|');
const formatCost = (cents) => {
if (typeof cents !== 'number' || Number.isNaN(cents)) return '-';
return new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD',
minimumFractionDigits: 2,
maximumFractionDigits: 2,
}).format(cents / 100);
};
function install() {
if (window[PATCH_KEY]?.cleanup) {
window[PATCH_KEY].cleanup();
}
const state = {
rawByKey: new Map(),
observer: null,
renderTimer: null,
originalFetch: window.fetch.bind(window),
};
function getFiberRoot() {
const table = document.querySelector('.dashboard-table-scroll-container');
if (!table) return null;
const fiberKey = Object.keys(table).find((k) => k.startsWith('__reactFiber$'));
if (!fiberKey) return null;
let fiber = table[fiberKey];
while (fiber?.return) fiber = fiber.return;
return fiber || null;
}
function walkFiber(visit) {
const root = getFiberRoot();
if (!root) return;
const seen = new Set();
const dfs = (node) => {
if (!node || seen.has(node)) return;
seen.add(node);
visit(node);
dfs(node.child);
dfs(node.sibling);
};
dfs(root);
}
function getDisplayData() {
let data = null;
walkFiber((node) => {
const props = node.memoizedProps;
if (!data && props && Array.isArray(props.data) && Array.isArray(props.columns)) {
data = props.data;
}
});
return data;
}
function getCustomRange() {
let range = null;
walkFiber((node) => {
const props = node.memoizedProps;
if (!range && props?.customRange?.from && props?.customRange?.to) {
range = props.customRange;
}
});
return range;
}
function getPageSize() {
const btn = [...document.querySelectorAll('button')].find((el) =>
/^Rows:\s*\d+$/i.test((el.textContent || '').trim())
);
const match = btn?.textContent?.match(/(\d+)/);
return match ? Number(match[1]) : 100;
}
function render() {
const table = document.querySelector('.dashboard-table-scroll-container');
const container = table?.querySelector('.dashboard-table-container');
const headerRow = container?.querySelector('.dashboard-table-header-row');
const rows = [...(container?.querySelectorAll('.dashboard-table-row') || [])];
const displayData = getDisplayData();
if (!table || !container || !headerRow || !rows.length || !displayData?.length) {
return;
}
table.setAttribute('aria-colcount', '6');
const minWidth = parseInt(container.style.minWidth || '761', 10);
if (!Number.isNaN(minWidth) && minWidth < 881) {
container.style.minWidth = '881px';
}
let headerCell = headerRow.querySelector('[data-tm-cost-header="1"]');
if (!headerCell) {
headerCell = document.createElement('div');
headerCell.setAttribute('role', 'columnheader');
headerCell.setAttribute('data-tm-cost-header', '1');
headerCell.className = 'dashboard-table-header dashboard-table-header-align-right';
headerCell.style.width = '120px';
headerCell.style.flexShrink = '0';
headerCell.style.flexGrow = '0';
const span = document.createElement('span');
span.className = 'truncate min-w-0';
span.textContent = 'Cost';
headerCell.appendChild(span);
const requestsHeader = [...headerRow.children].find(
(el) => el.textContent.trim() === 'Requests'
);
headerRow.insertBefore(headerCell, requestsHeader?.nextSibling || null);
}
rows.forEach((row, index) => {
const displayEvent = displayData[index];
if (!displayEvent) return;
const raw = state.rawByKey.get(keyOf(displayEvent));
const cents = raw?.tokenUsage?.totalCents;
let cell = row.querySelector('[data-tm-cost-cell="1"]');
if (!cell) {
cell = document.createElement('div');
cell.setAttribute('role', 'cell');
cell.setAttribute('data-tm-cost-cell', '1');
cell.className = 'dashboard-table-cell dashboard-table-cell-align-right';
cell.style.width = '120px';
cell.style.flexShrink = '0';
cell.style.flexGrow = '0';
cell.style.color = 'var(--text-secondary)';
const cells = [...row.querySelectorAll(':scope > [role="cell"]')];
const requestsCell = cells[cells.length - 1];
row.insertBefore(cell, requestsCell?.nextSibling || null);
}
cell.textContent = formatCost(cents);
cell.title = typeof cents === 'number' ? `${cents.toFixed(4)} cents` : '';
});
}
function scheduleRender() {
clearTimeout(state.renderTimer);
state.renderTimer = setTimeout(render, 0);
}
function recordRawUsage(payload) {
const events = payload?.usageEventsDisplay;
if (!Array.isArray(events)) return;
state.rawByKey.clear();
for (const event of events) {
state.rawByKey.set(keyOf(event), event);
}
scheduleRender();
}
window.fetch = async (...args) => {
const response = await state.originalFetch(...args);
try {
const input = args[0];
const url = typeof input === 'string' ? input : input?.url;
if (url && url.includes('/api/dashboard/get-filtered-usage-events')) {
response.clone().json().then(recordRawUsage).catch(() => {});
}
} catch {}
return response;
};
state.observer = new MutationObserver(() => scheduleRender());
state.observer.observe(document.documentElement, {
childList: true,
subtree: true,
});
(async () => {
try {
const range = getCustomRange();
if (!range?.from || !range?.to) return;
const pageSize = getPageSize();
const res = await state.originalFetch('/api/dashboard/get-filtered-usage-events', {
method: 'POST',
headers: { 'content-type': 'application/json' },
credentials: 'same-origin',
body: JSON.stringify({
teamId: 0,
startDate: String(new Date(range.from).getTime()),
endDate: String(new Date(range.to).getTime()),
page: 1,
pageSize,
}),
});
const payload = await res.json();
recordRawUsage(payload);
} catch {}
})();
state.cleanup = () => {
clearTimeout(state.renderTimer);
state.observer?.disconnect();
window.fetch = state.originalFetch;
document
.querySelectorAll('[data-tm-cost-cell="1"], [data-tm-cost-header="1"]')
.forEach((el) => el.remove());
const table = document.querySelector('.dashboard-table-scroll-container');
if (table) table.setAttribute('aria-colcount', '5');
};
window[PATCH_KEY] = state;
}
install();
})();