routstrd 0.3.2 → 0.3.3
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.
- package/bun.lock +2 -2
- package/dist/daemon/index.js +787 -135
- package/dist/index.js +61 -275
- package/package.json +2 -2
- package/src/daemon/http/usage-summary.test.ts +259 -0
- package/src/daemon/http/usage-summary.ts +361 -0
- package/src/daemon/request-response-log-sink.ts +269 -0
- package/src/tui/usage/app.ts +2 -2
- package/src/tui/usage/data.ts +12 -181
- package/src/tui/usage/render.ts +57 -107
- package/src/tui/usage/types.ts +4 -45
- package/src/utils/config.ts +8 -0
package/dist/index.js
CHANGED
|
@@ -2188,7 +2188,7 @@ var init_logger = __esm(() => {
|
|
|
2188
2188
|
});
|
|
2189
2189
|
|
|
2190
2190
|
// src/utils/config.ts
|
|
2191
|
-
var HOME2, CONFIG_DIR, SOCKET_PATH, PID_FILE, DB_PATH, CONFIG_FILE, LOGS_DIR2, DEFAULT_CONFIG;
|
|
2191
|
+
var HOME2, CONFIG_DIR, SOCKET_PATH, PID_FILE, DB_PATH, CONFIG_FILE, LOGS_DIR2, REQUEST_RESPONSE_LOGS_DIR, DEFAULT_CONFIG;
|
|
2192
2192
|
var init_config = __esm(() => {
|
|
2193
2193
|
HOME2 = process.env.HOME || process.env.USERPROFILE || "";
|
|
2194
2194
|
CONFIG_DIR = process.env.ROUTSTRD_DIR || `${HOME2}/.routstrd`;
|
|
@@ -2197,6 +2197,7 @@ var init_config = __esm(() => {
|
|
|
2197
2197
|
DB_PATH = `${CONFIG_DIR}/routstr.db`;
|
|
2198
2198
|
CONFIG_FILE = `${CONFIG_DIR}/config.json`;
|
|
2199
2199
|
LOGS_DIR2 = `${CONFIG_DIR}/logs`;
|
|
2200
|
+
REQUEST_RESPONSE_LOGS_DIR = `${CONFIG_DIR}/request-response-logs`;
|
|
2200
2201
|
DEFAULT_CONFIG = {
|
|
2201
2202
|
port: 8008,
|
|
2202
2203
|
provider: null,
|
|
@@ -13911,42 +13912,30 @@ async function fetchBalance() {
|
|
|
13911
13912
|
return null;
|
|
13912
13913
|
}
|
|
13913
13914
|
}
|
|
13914
|
-
async function
|
|
13915
|
+
async function fetchUsageSummary() {
|
|
13915
13916
|
try {
|
|
13916
13917
|
const running = await isDaemonRunning();
|
|
13917
13918
|
if (!running)
|
|
13918
13919
|
return null;
|
|
13919
|
-
const
|
|
13920
|
+
const tz = new Date().getTimezoneOffset();
|
|
13921
|
+
const result = await callDaemon(`/usage/summary?tz=${tz}`);
|
|
13920
13922
|
if (result.error)
|
|
13921
13923
|
return null;
|
|
13922
|
-
const
|
|
13923
|
-
|
|
13924
|
-
|
|
13925
|
-
promptTokens: acc.promptTokens + entry.promptTokens,
|
|
13926
|
-
completionTokens: acc.completionTokens + entry.completionTokens,
|
|
13927
|
-
totalTokens: acc.totalTokens + entry.totalTokens,
|
|
13928
|
-
satsCost: acc.satsCost + entry.satsCost
|
|
13929
|
-
}), { promptTokens: 0, completionTokens: 0, totalTokens: 0, satsCost: 0 });
|
|
13924
|
+
const summary = result.output;
|
|
13925
|
+
if (!summary || typeof summary.totals !== "object")
|
|
13926
|
+
return null;
|
|
13930
13927
|
return {
|
|
13931
|
-
entries:
|
|
13932
|
-
totalEntries:
|
|
13933
|
-
totalSatsCost: totals.satsCost,
|
|
13934
|
-
recentSatsCost: totals.satsCost,
|
|
13935
|
-
limit
|
|
13928
|
+
entries: summary.recent,
|
|
13929
|
+
totalEntries: summary.totals.requests,
|
|
13930
|
+
totalSatsCost: summary.totals.satsCost,
|
|
13931
|
+
recentSatsCost: summary.totals.satsCost,
|
|
13932
|
+
limit: 50,
|
|
13933
|
+
summary
|
|
13936
13934
|
};
|
|
13937
13935
|
} catch {
|
|
13938
13936
|
return null;
|
|
13939
13937
|
}
|
|
13940
13938
|
}
|
|
13941
|
-
function getTodayStart() {
|
|
13942
|
-
const now2 = new Date;
|
|
13943
|
-
return new Date(now2.getFullYear(), now2.getMonth(), now2.getDate()).getTime();
|
|
13944
|
-
}
|
|
13945
|
-
function formatDate(timestamp) {
|
|
13946
|
-
const d = new Date(timestamp);
|
|
13947
|
-
const p = (n) => String(n).padStart(2, "0");
|
|
13948
|
-
return `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())}`;
|
|
13949
|
-
}
|
|
13950
13939
|
function formatTime(timestamp) {
|
|
13951
13940
|
const d = new Date(timestamp);
|
|
13952
13941
|
const p = (n) => String(n).padStart(2, "0");
|
|
@@ -13959,116 +13948,6 @@ function formatNumber(n) {
|
|
|
13959
13948
|
return (n / 1000).toFixed(1) + "K";
|
|
13960
13949
|
return n.toString();
|
|
13961
13950
|
}
|
|
13962
|
-
function getDayStats(entries) {
|
|
13963
|
-
const days = new Map;
|
|
13964
|
-
for (const entry of entries) {
|
|
13965
|
-
const date = formatDate(entry.timestamp);
|
|
13966
|
-
const existing = days.get(date) || { date, requests: 0, satsCost: 0, promptTokens: 0, completionTokens: 0, totalTokens: 0 };
|
|
13967
|
-
days.set(date, {
|
|
13968
|
-
...existing,
|
|
13969
|
-
requests: existing.requests + 1,
|
|
13970
|
-
satsCost: existing.satsCost + entry.satsCost,
|
|
13971
|
-
promptTokens: existing.promptTokens + entry.promptTokens,
|
|
13972
|
-
completionTokens: existing.completionTokens + entry.completionTokens,
|
|
13973
|
-
totalTokens: existing.totalTokens + entry.totalTokens
|
|
13974
|
-
});
|
|
13975
|
-
}
|
|
13976
|
-
return days;
|
|
13977
|
-
}
|
|
13978
|
-
function getHourlyToday(entries) {
|
|
13979
|
-
const todayStart = getTodayStart();
|
|
13980
|
-
const hours = new Map;
|
|
13981
|
-
for (const entry of entries) {
|
|
13982
|
-
if (entry.timestamp < todayStart)
|
|
13983
|
-
continue;
|
|
13984
|
-
const hour = new Date(entry.timestamp).getHours();
|
|
13985
|
-
const existing = hours.get(hour) || {
|
|
13986
|
-
date: formatDate(entry.timestamp),
|
|
13987
|
-
requests: 0,
|
|
13988
|
-
satsCost: 0,
|
|
13989
|
-
promptTokens: 0,
|
|
13990
|
-
completionTokens: 0,
|
|
13991
|
-
totalTokens: 0
|
|
13992
|
-
};
|
|
13993
|
-
hours.set(hour, {
|
|
13994
|
-
...existing,
|
|
13995
|
-
requests: existing.requests + 1,
|
|
13996
|
-
satsCost: existing.satsCost + entry.satsCost,
|
|
13997
|
-
promptTokens: existing.promptTokens + entry.promptTokens,
|
|
13998
|
-
completionTokens: existing.completionTokens + entry.completionTokens,
|
|
13999
|
-
totalTokens: existing.totalTokens + entry.totalTokens
|
|
14000
|
-
});
|
|
14001
|
-
}
|
|
14002
|
-
return hours;
|
|
14003
|
-
}
|
|
14004
|
-
function getModelStats(entries) {
|
|
14005
|
-
const models = new Map;
|
|
14006
|
-
for (const entry of entries) {
|
|
14007
|
-
const existing = models.get(entry.modelId) || {
|
|
14008
|
-
modelId: entry.modelId,
|
|
14009
|
-
requests: 0,
|
|
14010
|
-
satsCost: 0,
|
|
14011
|
-
promptTokens: 0,
|
|
14012
|
-
completionTokens: 0,
|
|
14013
|
-
totalTokens: 0
|
|
14014
|
-
};
|
|
14015
|
-
models.set(entry.modelId, {
|
|
14016
|
-
...existing,
|
|
14017
|
-
requests: existing.requests + 1,
|
|
14018
|
-
satsCost: existing.satsCost + entry.satsCost,
|
|
14019
|
-
promptTokens: existing.promptTokens + entry.promptTokens,
|
|
14020
|
-
completionTokens: existing.completionTokens + entry.completionTokens,
|
|
14021
|
-
totalTokens: existing.totalTokens + entry.totalTokens
|
|
14022
|
-
});
|
|
14023
|
-
}
|
|
14024
|
-
return Array.from(models.values()).sort((a, b) => b.satsCost - a.satsCost);
|
|
14025
|
-
}
|
|
14026
|
-
function getProviderStats(entries) {
|
|
14027
|
-
const providers = new Map;
|
|
14028
|
-
for (const entry of entries) {
|
|
14029
|
-
const url = entry.baseUrl || "unknown";
|
|
14030
|
-
const existing = providers.get(url) || {
|
|
14031
|
-
baseUrl: url,
|
|
14032
|
-
requests: 0,
|
|
14033
|
-
satsCost: 0,
|
|
14034
|
-
promptTokens: 0,
|
|
14035
|
-
completionTokens: 0,
|
|
14036
|
-
totalTokens: 0
|
|
14037
|
-
};
|
|
14038
|
-
providers.set(url, {
|
|
14039
|
-
...existing,
|
|
14040
|
-
requests: existing.requests + 1,
|
|
14041
|
-
satsCost: existing.satsCost + entry.satsCost,
|
|
14042
|
-
promptTokens: existing.promptTokens + entry.promptTokens,
|
|
14043
|
-
completionTokens: existing.completionTokens + entry.completionTokens,
|
|
14044
|
-
totalTokens: existing.totalTokens + entry.totalTokens
|
|
14045
|
-
});
|
|
14046
|
-
}
|
|
14047
|
-
return Array.from(providers.values()).sort((a, b) => b.satsCost - a.satsCost);
|
|
14048
|
-
}
|
|
14049
|
-
function getClientStats(entries) {
|
|
14050
|
-
const clients = new Map;
|
|
14051
|
-
for (const entry of entries) {
|
|
14052
|
-
const client = entry.client || "unknown";
|
|
14053
|
-
const existing = clients.get(client) || {
|
|
14054
|
-
client,
|
|
14055
|
-
requests: 0,
|
|
14056
|
-
satsCost: 0,
|
|
14057
|
-
promptTokens: 0,
|
|
14058
|
-
completionTokens: 0,
|
|
14059
|
-
totalTokens: 0
|
|
14060
|
-
};
|
|
14061
|
-
clients.set(client, {
|
|
14062
|
-
...existing,
|
|
14063
|
-
requests: existing.requests + 1,
|
|
14064
|
-
satsCost: existing.satsCost + entry.satsCost,
|
|
14065
|
-
promptTokens: existing.promptTokens + entry.promptTokens,
|
|
14066
|
-
completionTokens: existing.completionTokens + entry.completionTokens,
|
|
14067
|
-
totalTokens: existing.totalTokens + entry.totalTokens
|
|
14068
|
-
});
|
|
14069
|
-
}
|
|
14070
|
-
return Array.from(clients.values()).sort((a, b) => b.satsCost - a.satsCost);
|
|
14071
|
-
}
|
|
14072
13951
|
async function fetchClients() {
|
|
14073
13952
|
try {
|
|
14074
13953
|
const running = await isDaemonRunning();
|
|
@@ -14090,48 +13969,6 @@ async function fetchClients() {
|
|
|
14090
13969
|
function hasAnyNpubs(clients) {
|
|
14091
13970
|
return clients.some((c) => !!c.ownerNpub);
|
|
14092
13971
|
}
|
|
14093
|
-
function getNpubStats(entries, clients) {
|
|
14094
|
-
const clientNpubMap = new Map;
|
|
14095
|
-
for (const c of clients) {
|
|
14096
|
-
if (c.ownerNpub) {
|
|
14097
|
-
clientNpubMap.set(c.clientId, c.ownerNpub);
|
|
14098
|
-
}
|
|
14099
|
-
}
|
|
14100
|
-
const npubs = new Map;
|
|
14101
|
-
for (const entry of entries) {
|
|
14102
|
-
const npub = clientNpubMap.get(entry.client || "");
|
|
14103
|
-
if (!npub)
|
|
14104
|
-
continue;
|
|
14105
|
-
const existing = npubs.get(npub) || {
|
|
14106
|
-
npub,
|
|
14107
|
-
requests: 0,
|
|
14108
|
-
satsCost: 0,
|
|
14109
|
-
promptTokens: 0,
|
|
14110
|
-
completionTokens: 0,
|
|
14111
|
-
totalTokens: 0
|
|
14112
|
-
};
|
|
14113
|
-
npubs.set(npub, {
|
|
14114
|
-
...existing,
|
|
14115
|
-
requests: existing.requests + 1,
|
|
14116
|
-
satsCost: existing.satsCost + entry.satsCost,
|
|
14117
|
-
promptTokens: existing.promptTokens + entry.promptTokens,
|
|
14118
|
-
completionTokens: existing.completionTokens + entry.completionTokens,
|
|
14119
|
-
totalTokens: existing.totalTokens + entry.totalTokens
|
|
14120
|
-
});
|
|
14121
|
-
}
|
|
14122
|
-
return Array.from(npubs.values()).sort((a, b) => b.satsCost - a.satsCost);
|
|
14123
|
-
}
|
|
14124
|
-
function getTotals(entries) {
|
|
14125
|
-
if (!entries || !Array.isArray(entries)) {
|
|
14126
|
-
return { promptTokens: 0, completionTokens: 0, totalTokens: 0, satsCost: 0 };
|
|
14127
|
-
}
|
|
14128
|
-
return entries.reduce((acc, entry) => ({
|
|
14129
|
-
promptTokens: acc.promptTokens + entry.promptTokens,
|
|
14130
|
-
completionTokens: acc.completionTokens + entry.completionTokens,
|
|
14131
|
-
totalTokens: acc.totalTokens + entry.totalTokens,
|
|
14132
|
-
satsCost: acc.satsCost + entry.satsCost
|
|
14133
|
-
}), { promptTokens: 0, completionTokens: 0, totalTokens: 0, satsCost: 0 });
|
|
14134
|
-
}
|
|
14135
13972
|
var init_data = __esm(() => {
|
|
14136
13973
|
init_daemon_client();
|
|
14137
13974
|
});
|
|
@@ -14331,7 +14168,7 @@ function renderBarChart(label, value, maxValue, width, color, percentageValue, s
|
|
|
14331
14168
|
return `${paddedLabel} ${bar}${suffix}`;
|
|
14332
14169
|
}
|
|
14333
14170
|
function renderOverview(stats, balance, status, width) {
|
|
14334
|
-
const totals =
|
|
14171
|
+
const totals = stats.summary.totals;
|
|
14335
14172
|
const totalRequests = stats.totalEntries;
|
|
14336
14173
|
const totalVisibleCost = stats.totalSatsCost;
|
|
14337
14174
|
const avgCost = totalRequests > 0 ? totalVisibleCost / totalRequests : 0;
|
|
@@ -14440,7 +14277,7 @@ function renderOverview(stats, balance, status, width) {
|
|
|
14440
14277
|
output = renderBox(statusLines, width, "System Status") + `
|
|
14441
14278
|
` + output;
|
|
14442
14279
|
}
|
|
14443
|
-
const modelStats =
|
|
14280
|
+
const modelStats = stats.summary.models;
|
|
14444
14281
|
if (modelStats.length > 0) {
|
|
14445
14282
|
const maxCost = modelStats[0].satsCost;
|
|
14446
14283
|
const totalCost = Math.max(totalVisibleCost, 1);
|
|
@@ -14451,7 +14288,7 @@ function renderOverview(stats, balance, status, width) {
|
|
|
14451
14288
|
output += `
|
|
14452
14289
|
` + renderBox(modelLines, width, "Top Models by Cost");
|
|
14453
14290
|
}
|
|
14454
|
-
const clientStats =
|
|
14291
|
+
const clientStats = stats.summary.clients;
|
|
14455
14292
|
if (clientStats.length > 0) {
|
|
14456
14293
|
const maxCost = clientStats[0].satsCost;
|
|
14457
14294
|
const totalCost = Math.max(totalVisibleCost, 1);
|
|
@@ -14465,18 +14302,18 @@ function renderOverview(stats, balance, status, width) {
|
|
|
14465
14302
|
return output;
|
|
14466
14303
|
}
|
|
14467
14304
|
function renderToday(stats, width) {
|
|
14468
|
-
const hourly = getHourlyToday(stats.entries);
|
|
14469
|
-
const todayStart = getTodayStart();
|
|
14470
14305
|
const currentHour = new Date().getHours();
|
|
14471
|
-
const
|
|
14472
|
-
|
|
14473
|
-
|
|
14474
|
-
|
|
14475
|
-
|
|
14476
|
-
|
|
14477
|
-
|
|
14478
|
-
|
|
14479
|
-
}
|
|
14306
|
+
const _now = new Date;
|
|
14307
|
+
const todayDateStr = `${_now.getFullYear()}-${String(_now.getMonth() + 1).padStart(2, "0")}-${String(_now.getDate()).padStart(2, "0")}`;
|
|
14308
|
+
let todayStats;
|
|
14309
|
+
let recentDays;
|
|
14310
|
+
let hourlyMap;
|
|
14311
|
+
if (stats.summary) {
|
|
14312
|
+
const { days, hoursToday } = stats.summary;
|
|
14313
|
+
const todayDayStat = days[0]?.date === todayDateStr ? days[0] : undefined;
|
|
14314
|
+
todayStats = todayDayStat ? { date: todayDayStat.date, requests: todayDayStat.requests, satsCost: todayDayStat.satsCost, promptTokens: todayDayStat.promptTokens, completionTokens: todayDayStat.completionTokens, totalTokens: todayDayStat.totalTokens } : { date: todayDateStr, requests: 0, satsCost: 0, promptTokens: 0, completionTokens: 0, totalTokens: 0 };
|
|
14315
|
+
recentDays = days.filter((d) => d.date !== todayDateStr).slice(0, 6);
|
|
14316
|
+
hourlyMap = new Map(hoursToday.map((h) => [h.hour, { requests: h.requests, satsCost: h.satsCost }]));
|
|
14480
14317
|
}
|
|
14481
14318
|
const summaryLines = [
|
|
14482
14319
|
`${COLORS.bold}Date:${COLORS.reset} ${todayStats.date}`,
|
|
@@ -14485,18 +14322,17 @@ function renderToday(stats, width) {
|
|
|
14485
14322
|
`${COLORS.bold}Tokens:${COLORS.reset} ${formatNumber(todayStats.totalTokens)} (p: ${formatNumber(todayStats.promptTokens)} + c: ${formatNumber(todayStats.completionTokens)})`
|
|
14486
14323
|
];
|
|
14487
14324
|
let output = renderBox(summaryLines, width, "Today");
|
|
14488
|
-
|
|
14489
|
-
|
|
14490
|
-
const dayLines = days.slice(1).map((d) => `${d.date}: ${formatReqs(d.requests)} req, ${formatCost(d.satsCost)} sats, ${formatNumber(d.totalTokens)} tokens`);
|
|
14325
|
+
if (recentDays.length > 0) {
|
|
14326
|
+
const dayLines = recentDays.map((d) => `${d.date}: ${formatReqs(d.requests)} req, ${formatCost(d.satsCost)} sats, ${formatNumber(d.totalTokens)} tokens`);
|
|
14491
14327
|
output += `
|
|
14492
14328
|
` + renderBox(dayLines, width, "Recent Days");
|
|
14493
14329
|
}
|
|
14494
14330
|
const hourLines = [];
|
|
14495
|
-
const maxHourCost = Math.max(...Array.from(
|
|
14331
|
+
const maxHourCost = Math.max(...Array.from(hourlyMap.values()).map((h) => h.satsCost), 1);
|
|
14496
14332
|
const totalTodayCost = Math.max(todayStats.satsCost, 1);
|
|
14497
14333
|
const hourLabels = [];
|
|
14498
14334
|
for (let h = currentHour;h >= 0; h--) {
|
|
14499
|
-
const hStat =
|
|
14335
|
+
const hStat = hourlyMap.get(h);
|
|
14500
14336
|
const reqs = hStat?.requests || 0;
|
|
14501
14337
|
const cost = hStat?.satsCost || 0;
|
|
14502
14338
|
hourLabels.push(`${h.toString().padStart(2, "0")}:00 (${formatReqs(reqs)} req, ${formatCost(cost)} sats) `);
|
|
@@ -14504,7 +14340,7 @@ function renderToday(stats, width) {
|
|
|
14504
14340
|
const maxHourLabel = Math.max(...hourLabels.map((l) => l.length));
|
|
14505
14341
|
startBarSection("hourly", maxHourLabel);
|
|
14506
14342
|
for (let i2 = currentHour;i2 >= 0; i2--) {
|
|
14507
|
-
const hStat =
|
|
14343
|
+
const hStat = hourlyMap.get(i2);
|
|
14508
14344
|
const reqs = hStat?.requests || 0;
|
|
14509
14345
|
const cost = hStat?.satsCost || 0;
|
|
14510
14346
|
hourLines.push(renderBarChart(hourLabels[currentHour - i2], cost, maxHourCost, width - 4, i2 === currentHour ? COLORS.green : COLORS.cyan, cost / totalTodayCost * 100, "hourly"));
|
|
@@ -14515,7 +14351,7 @@ function renderToday(stats, width) {
|
|
|
14515
14351
|
return output;
|
|
14516
14352
|
}
|
|
14517
14353
|
function renderModels(stats, width) {
|
|
14518
|
-
const modelStats =
|
|
14354
|
+
const modelStats = stats.summary.models;
|
|
14519
14355
|
if (modelStats.length === 0)
|
|
14520
14356
|
return renderBox(["No model data available"], width, "Models");
|
|
14521
14357
|
const totalCost = stats.totalSatsCost;
|
|
@@ -14538,7 +14374,7 @@ function renderModels(stats, width) {
|
|
|
14538
14374
|
return renderBox(lines, width, "Model Breakdown");
|
|
14539
14375
|
}
|
|
14540
14376
|
function renderProviders(stats, width) {
|
|
14541
|
-
const providerStats =
|
|
14377
|
+
const providerStats = stats.summary.providers;
|
|
14542
14378
|
if (providerStats.length === 0)
|
|
14543
14379
|
return renderBox(["No provider data available"], width, "Providers");
|
|
14544
14380
|
const lines = [];
|
|
@@ -14553,8 +14389,8 @@ function renderProviders(stats, width) {
|
|
|
14553
14389
|
return renderBox(lines, width, "Provider Breakdown");
|
|
14554
14390
|
}
|
|
14555
14391
|
function renderTokens(stats, width) {
|
|
14556
|
-
const totals =
|
|
14557
|
-
const modelStats =
|
|
14392
|
+
const totals = stats.summary.totals;
|
|
14393
|
+
const modelStats = stats.summary.models;
|
|
14558
14394
|
const summaryLines = [
|
|
14559
14395
|
`${COLORS.bold}Total Prompt Tokens:${COLORS.reset} ${formatNumber(totals.promptTokens)}`,
|
|
14560
14396
|
`${COLORS.bold}Total Completion Tokens:${COLORS.reset} ${formatNumber(totals.completionTokens)}`,
|
|
@@ -14572,29 +14408,14 @@ function renderTokens(stats, width) {
|
|
|
14572
14408
|
output += `
|
|
14573
14409
|
` + renderBox(tokenLines, width, "Tokens by Model");
|
|
14574
14410
|
}
|
|
14575
|
-
const sizeBuckets =
|
|
14576
|
-
tiny: { min: 0, max: 1000, count: 0, cost: 0 },
|
|
14577
|
-
small: { min: 1000, max: 1e4, count: 0, cost: 0 },
|
|
14578
|
-
medium: { min: 1e4, max: 50000, count: 0, cost: 0 },
|
|
14579
|
-
large: { min: 50000, max: 1e5, count: 0, cost: 0 },
|
|
14580
|
-
huge: { min: 1e5, max: Infinity, count: 0, cost: 0 }
|
|
14581
|
-
};
|
|
14582
|
-
for (const entry of stats.entries) {
|
|
14583
|
-
for (const bucket of Object.values(sizeBuckets)) {
|
|
14584
|
-
if (entry.totalTokens >= bucket.min && entry.totalTokens < bucket.max) {
|
|
14585
|
-
bucket.count++;
|
|
14586
|
-
bucket.cost += entry.satsCost;
|
|
14587
|
-
break;
|
|
14588
|
-
}
|
|
14589
|
-
}
|
|
14590
|
-
}
|
|
14411
|
+
const sizeBuckets = stats.summary.sizeBuckets;
|
|
14591
14412
|
const sizeLines = Object.entries(sizeBuckets).map(([name, bucket]) => `${name.padEnd(6)}: ${formatReqs(bucket.count).padStart(5)} reqs, ${formatCost(bucket.cost)} sats`);
|
|
14592
14413
|
output += `
|
|
14593
14414
|
` + renderBox(sizeLines, width, "Request Size Distribution");
|
|
14594
14415
|
return output;
|
|
14595
14416
|
}
|
|
14596
14417
|
function renderClients(stats, width) {
|
|
14597
|
-
const clientStats =
|
|
14418
|
+
const clientStats = stats.summary.clients;
|
|
14598
14419
|
if (clientStats.length === 0)
|
|
14599
14420
|
return renderBox(["No client data available (API key auth not used)"], width, "Client Breakdown");
|
|
14600
14421
|
const totalCost = stats.totalSatsCost;
|
|
@@ -14626,31 +14447,17 @@ function renderClients(stats, width) {
|
|
|
14626
14447
|
}
|
|
14627
14448
|
endBarSection("client-detail");
|
|
14628
14449
|
let output = renderBox(lines, width, "Client Breakdown");
|
|
14629
|
-
const clientModelMap = new Map;
|
|
14630
|
-
for (const entry of stats.entries) {
|
|
14631
|
-
const client = entry.client || "unknown";
|
|
14632
|
-
const model = entry.modelId;
|
|
14633
|
-
if (!clientModelMap.has(client))
|
|
14634
|
-
clientModelMap.set(client, new Map);
|
|
14635
|
-
const modelMap = clientModelMap.get(client);
|
|
14636
|
-
const existing = modelMap.get(model) || { requests: 0, satsCost: 0, tokens: 0 };
|
|
14637
|
-
modelMap.set(model, {
|
|
14638
|
-
requests: existing.requests + 1,
|
|
14639
|
-
satsCost: existing.satsCost + entry.satsCost,
|
|
14640
|
-
tokens: existing.tokens + entry.totalTokens
|
|
14641
|
-
});
|
|
14642
|
-
}
|
|
14643
14450
|
const clientModelLines = [];
|
|
14644
|
-
|
|
14645
|
-
const
|
|
14646
|
-
|
|
14647
|
-
|
|
14648
|
-
|
|
14649
|
-
|
|
14650
|
-
|
|
14651
|
-
|
|
14451
|
+
if (stats.summary) {
|
|
14452
|
+
for (const topClient of stats.summary.clients.filter((c) => c.client !== "unknown").slice(0, 3)) {
|
|
14453
|
+
if (topClient.topModels.length === 0)
|
|
14454
|
+
continue;
|
|
14455
|
+
clientModelLines.push(`${COLORS.bold}${topClient.client}${COLORS.reset} (${formatReqs(topClient.requests)} reqs, ${formatCost(topClient.satsCost)} sats)`);
|
|
14456
|
+
for (const m of topClient.topModels) {
|
|
14457
|
+
clientModelLines.push(` ${MODEL_COLORS[m.modelId] || MODEL_COLORS.default}${m.modelId.padEnd(18)}${COLORS.reset} ${formatNumber(m.totalTokens).padEnd(8)} tokens ${formatCost(m.satsCost)} sats`);
|
|
14458
|
+
}
|
|
14459
|
+
clientModelLines.push("");
|
|
14652
14460
|
}
|
|
14653
|
-
clientModelLines.push("");
|
|
14654
14461
|
}
|
|
14655
14462
|
if (clientModelLines.length > 0) {
|
|
14656
14463
|
output += `
|
|
@@ -14659,7 +14466,7 @@ function renderClients(stats, width) {
|
|
|
14659
14466
|
return output;
|
|
14660
14467
|
}
|
|
14661
14468
|
function renderNpubs(stats, clients, width) {
|
|
14662
|
-
const npubStats =
|
|
14469
|
+
const npubStats = stats.summary.npubs;
|
|
14663
14470
|
if (npubStats.length === 0)
|
|
14664
14471
|
return renderBox(["No npub data available"], width, "Npub Breakdown");
|
|
14665
14472
|
const totalCost = stats.totalSatsCost;
|
|
@@ -14692,38 +14499,17 @@ function renderNpubs(stats, clients, width) {
|
|
|
14692
14499
|
}
|
|
14693
14500
|
endBarSection("npub-detail");
|
|
14694
14501
|
let output = renderBox(lines, width, "Npub Breakdown");
|
|
14695
|
-
const npubModelMap = new Map;
|
|
14696
|
-
const clientNpubMap = new Map;
|
|
14697
|
-
for (const c of clients) {
|
|
14698
|
-
if (c.ownerNpub)
|
|
14699
|
-
clientNpubMap.set(c.clientId, c.ownerNpub);
|
|
14700
|
-
}
|
|
14701
|
-
for (const entry of stats.entries) {
|
|
14702
|
-
const npub = clientNpubMap.get(entry.client || "");
|
|
14703
|
-
if (!npub)
|
|
14704
|
-
continue;
|
|
14705
|
-
const model = entry.modelId;
|
|
14706
|
-
if (!npubModelMap.has(npub))
|
|
14707
|
-
npubModelMap.set(npub, new Map);
|
|
14708
|
-
const modelMap = npubModelMap.get(npub);
|
|
14709
|
-
const existing = modelMap.get(model) || { requests: 0, satsCost: 0, tokens: 0 };
|
|
14710
|
-
modelMap.set(model, {
|
|
14711
|
-
requests: existing.requests + 1,
|
|
14712
|
-
satsCost: existing.satsCost + entry.satsCost,
|
|
14713
|
-
tokens: existing.tokens + entry.totalTokens
|
|
14714
|
-
});
|
|
14715
|
-
}
|
|
14716
14502
|
const npubModelLines = [];
|
|
14717
|
-
|
|
14718
|
-
const
|
|
14719
|
-
|
|
14720
|
-
|
|
14721
|
-
|
|
14722
|
-
|
|
14723
|
-
|
|
14724
|
-
|
|
14503
|
+
if (stats.summary) {
|
|
14504
|
+
for (const topNpub of stats.summary.npubs.slice(0, 5)) {
|
|
14505
|
+
if (topNpub.topModels.length === 0)
|
|
14506
|
+
continue;
|
|
14507
|
+
npubModelLines.push(`${COLORS.bold}${truncateNpub(topNpub.npub)}${COLORS.reset} (${formatReqs(topNpub.requests)} reqs, ${formatCost(topNpub.satsCost)} sats)`);
|
|
14508
|
+
for (const m of topNpub.topModels) {
|
|
14509
|
+
npubModelLines.push(` ${MODEL_COLORS[m.modelId] || MODEL_COLORS.default}${m.modelId.padEnd(18)}${COLORS.reset} ${formatNumber(m.totalTokens).padEnd(8)} tokens ${formatCost(m.satsCost)} sats`);
|
|
14510
|
+
}
|
|
14511
|
+
npubModelLines.push("");
|
|
14725
14512
|
}
|
|
14726
|
-
npubModelLines.push("");
|
|
14727
14513
|
}
|
|
14728
14514
|
if (npubModelLines.length > 0) {
|
|
14729
14515
|
output += `
|
|
@@ -14855,7 +14641,7 @@ async function runUsageTui() {
|
|
|
14855
14641
|
const width = getWidth();
|
|
14856
14642
|
const height = getHeight();
|
|
14857
14643
|
if (forceFetch || shouldUpdate) {
|
|
14858
|
-
stats = await
|
|
14644
|
+
stats = await fetchUsageSummary();
|
|
14859
14645
|
balance = await fetchBalance();
|
|
14860
14646
|
status = await fetchStatus();
|
|
14861
14647
|
clients = await fetchClients();
|
|
@@ -15802,7 +15588,7 @@ async function isCocodInstalled(cocodPath) {
|
|
|
15802
15588
|
// package.json
|
|
15803
15589
|
var package_default = {
|
|
15804
15590
|
name: "routstrd",
|
|
15805
|
-
version: "0.3.
|
|
15591
|
+
version: "0.3.3",
|
|
15806
15592
|
module: "src/index.ts",
|
|
15807
15593
|
type: "module",
|
|
15808
15594
|
private: false,
|
|
@@ -15826,7 +15612,7 @@ var package_default = {
|
|
|
15826
15612
|
},
|
|
15827
15613
|
dependencies: {
|
|
15828
15614
|
"@cashu/cashu-ts": "^4.3.0",
|
|
15829
|
-
"@routstr/sdk": "^0.3.
|
|
15615
|
+
"@routstr/sdk": "^0.3.11",
|
|
15830
15616
|
"applesauce-core": "^5.1.0",
|
|
15831
15617
|
"applesauce-relay": "^5.1.0",
|
|
15832
15618
|
"applesauce-wallet-connect": "^6.0.0",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "routstrd",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.3",
|
|
4
4
|
"module": "src/index.ts",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"private": false,
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
26
|
"@cashu/cashu-ts": "^4.3.0",
|
|
27
|
-
"@routstr/sdk": "^0.3.
|
|
27
|
+
"@routstr/sdk": "^0.3.11",
|
|
28
28
|
"applesauce-core": "^5.1.0",
|
|
29
29
|
"applesauce-relay": "^5.1.0",
|
|
30
30
|
"applesauce-wallet-connect": "^6.0.0",
|