@hardlydifficult/usage-tracker
v2.0.6
Published
Persisted usage counters with a simple API:
Readme
@hardlydifficult/usage-tracker
Persisted usage counters with a simple API:
UsageTracker.open(id, metrics, options)usage.track(delta)usage.currentfor this runusage.totalfor all runsusage.spend("minute" | "hour" | "day" | "week")usage.budgetfor budget status
Any metric ending in CostUsd is treated as spend automatically.
Installation
npm install @hardlydifficult/usage-trackerQuick Start
import { UsageTracker } from "@hardlydifficult/usage-tracker";
const usage = await UsageTracker.open(
"assistant",
{
api: { requests: 0, tokens: 0, costUsd: 0 },
audio: { seconds: 0 },
},
{
dir: "/path/to/state",
budget: {
hour: 2,
day: 10,
},
}
);
usage.track({
api: { requests: 1, tokens: 1200, costUsd: 0.04 },
});
console.log(usage.current.api.requests); // 1
console.log(usage.total.api.requests); // 1
console.log(usage.spend("day")); // 0.04
console.log(usage.budget.day?.remainingUsd); // 9.96API
UsageTracker.open(id, metrics, options?)
Open a tracker, load persisted totals, and start a fresh current run.
const usage = await UsageTracker.open("my-app", {
api: { requests: 0, tokens: 0, costUsd: 0 },
});Arguments:
id: persistence keymetrics: zeroed metric shapeoptions.dir: directory for persisted stateoptions.storage: custom storage adapteroptions.autoSaveMs: autosave intervaloptions.budget: budgets keyed byminute,hour,day, orweekoptions.onBudgetExceeded: fires once when a window crosses over budget
usage.track(delta)
Add numeric deltas into both current and total.
usage.track({
api: { requests: 1, tokens: 800, costUsd: 0.02 },
});usage.current
Metrics for the current process run.
usage.total
Metrics across every run that used the same id.
usage.startedAt
ISO timestamp for the current run.
usage.trackingSince
ISO timestamp for when the tracker was first created.
usage.persistent
Whether persistence is currently working.
usage.spend(window)
Spend recorded in the trailing minute, hour, day, or week.
const today = usage.spend("day");
const thisWeek = usage.spend("week");usage.budget
Live budget status keyed by window name.
const usage = await UsageTracker.open(
"assistant",
{
api: { requests: 0, costUsd: 0 },
},
{
budget: { hour: 2, day: 10 },
}
);
usage.track({ api: { costUsd: 1.5 } });
console.log(usage.budget.hour);
// {
// window: "hour",
// spentUsd: 1.5,
// limitUsd: 2,
// remainingUsd: 0.5,
// exceeded: false,
// resumesAt: null
// }usage.assertBudget()
Throw if any configured budget is exceeded.
import {
BudgetExceededError,
UsageTracker,
} from "@hardlydifficult/usage-tracker";
try {
usage.assertBudget();
} catch (error) {
if (error instanceof BudgetExceededError) {
console.log(error.status.window);
console.log(error.status.spentUsd);
}
}usage.save()
Force an immediate write to storage.
await usage.save();Cost Detection
Every numeric leaf whose name ends with CostUsd is counted as spend.
Examples:
costUsdestimatedCostUsdtotalCostUsd
This works across nested metric groups:
const usage = await UsageTracker.open("providers", {
anthropic: { tokens: 0, estimatedCostUsd: 0 },
openai: { tokens: 0, estimatedCostUsd: 0 },
claudeCode: { sessions: 0, totalCostUsd: 0 },
});Types
import type {
Budget,
BudgetSnapshot,
BudgetStatus,
BudgetWindow,
DeepPartial,
NumericRecord,
UsageTrackerOpenOptions,
} from "@hardlydifficult/usage-tracker";