@ekkos/cli 1.3.0 → 1.3.1
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/dist/commands/dashboard.js +64 -17
- package/package.json +1 -1
|
@@ -1089,14 +1089,15 @@ async function launchDashboard(initialSessionName, initialJsonlPath, refreshMs,
|
|
|
1089
1089
|
let header = '';
|
|
1090
1090
|
let separator = '';
|
|
1091
1091
|
let rows = [];
|
|
1092
|
-
// Always show all
|
|
1092
|
+
// Always show all 8 columns: Turn, Time, Model, Context, Cache Rd, Cache Wr, Output, Cost
|
|
1093
1093
|
// Shrink flex columns to fit narrow panes instead of dropping them.
|
|
1094
1094
|
const colNum = 4;
|
|
1095
|
+
const colTime = 8; // "HH:MM:SS" or "H:MM AM"
|
|
1095
1096
|
const colM = 7;
|
|
1096
1097
|
const colCtx = 7;
|
|
1097
1098
|
const colCost = 8;
|
|
1098
|
-
const nDividers =
|
|
1099
|
-
const fixedW = colNum + colM + colCtx + colCost;
|
|
1099
|
+
const nDividers = 7;
|
|
1100
|
+
const fixedW = colNum + colTime + colM + colCtx + colCost;
|
|
1100
1101
|
const flexTotal = Math.max(0, w - fixedW - nDividers);
|
|
1101
1102
|
let rdW = Math.max(5, Math.floor(flexTotal * 0.35));
|
|
1102
1103
|
let wrW = Math.max(5, Math.floor(flexTotal * 0.30));
|
|
@@ -1118,14 +1119,30 @@ async function launchDashboard(initialSessionName, initialJsonlPath, refreshMs,
|
|
|
1118
1119
|
rdW -= trimRd;
|
|
1119
1120
|
}
|
|
1120
1121
|
}
|
|
1121
|
-
|
|
1122
|
-
|
|
1122
|
+
// Format turn timestamp to short time string
|
|
1123
|
+
const fmtTime = (iso) => {
|
|
1124
|
+
if (!iso)
|
|
1125
|
+
return '--:--';
|
|
1126
|
+
try {
|
|
1127
|
+
const d = new Date(iso);
|
|
1128
|
+
const h = d.getHours();
|
|
1129
|
+
const m = d.getMinutes().toString().padStart(2, '0');
|
|
1130
|
+
const s = d.getSeconds().toString().padStart(2, '0');
|
|
1131
|
+
return `${h}:${m}:${s}`;
|
|
1132
|
+
}
|
|
1133
|
+
catch {
|
|
1134
|
+
return '--:--';
|
|
1135
|
+
}
|
|
1136
|
+
};
|
|
1137
|
+
header = `{bold}${pad('Turn', colNum)}${div}${pad('Time', colTime)}${div}${pad('Model', colM)}${div}${pad('Context', colCtx)}${div}${rpad('Cache Rd', rdW)}${div}${rpad('Cache Wr', wrW)}${div}${rpad('Output', outW)}${div}${rpad('Cost', colCost)}{/bold}`;
|
|
1138
|
+
separator = `{gray-fg}${'─'.repeat(colNum)}┼${'─'.repeat(colTime)}┼${'─'.repeat(colM)}┼${'─'.repeat(colCtx)}┼${'─'.repeat(rdW)}┼${'─'.repeat(wrW)}┼${'─'.repeat(outW)}┼${'─'.repeat(colCost)}{/gray-fg}`;
|
|
1123
1139
|
rows = turns.map(t => {
|
|
1124
1140
|
const mTag = modelTag(t.routedModel);
|
|
1125
1141
|
const mColor = t.routedModel.includes('haiku') ? 'green' : t.routedModel.includes('sonnet') ? 'blue' : 'magenta';
|
|
1126
1142
|
const costFlag = t.cost > 1.0 ? '{red-fg}' : t.cost > 0.5 ? '{yellow-fg}' : '{white-fg}';
|
|
1127
1143
|
const costEnd = t.cost > 1.0 ? '{/red-fg}' : t.cost > 0.5 ? '{/yellow-fg}' : '{/white-fg}';
|
|
1128
1144
|
return (pad(String(t.turn), colNum) + div +
|
|
1145
|
+
`{gray-fg}${pad(fmtTime(t.timestamp), colTime)}{/gray-fg}` + div +
|
|
1129
1146
|
`{${mColor}-fg}${pad(mTag, colM)}{/${mColor}-fg}` + div +
|
|
1130
1147
|
pad(`${t.contextPct.toFixed(0)}%`, colCtx) + div +
|
|
1131
1148
|
`{green-fg}${rpad(fmtK(t.cacheRead), rdW)}{/green-fg}` + div +
|
|
@@ -1220,9 +1237,22 @@ async function launchDashboard(initialSessionName, initialJsonlPath, refreshMs,
|
|
|
1220
1237
|
return null;
|
|
1221
1238
|
}
|
|
1222
1239
|
}
|
|
1223
|
-
|
|
1240
|
+
// Cache last fetched usage data so the countdown can tick every second
|
|
1241
|
+
let cachedUsage = null;
|
|
1242
|
+
// Fetch fresh usage data from API (called every 15s)
|
|
1243
|
+
async function fetchAndCacheUsage() {
|
|
1244
|
+
try {
|
|
1245
|
+
cachedUsage = await fetchAnthropicUsage();
|
|
1246
|
+
}
|
|
1247
|
+
catch (err) {
|
|
1248
|
+
dlog(`Window fetch: ${err.message}`);
|
|
1249
|
+
}
|
|
1250
|
+
renderWindowBox();
|
|
1251
|
+
}
|
|
1252
|
+
// Render countdown from cached data (called every 1s)
|
|
1253
|
+
function renderWindowBox() {
|
|
1224
1254
|
try {
|
|
1225
|
-
const usage =
|
|
1255
|
+
const usage = cachedUsage;
|
|
1226
1256
|
let line1 = ' {gray-fg}No usage data{/gray-fg}';
|
|
1227
1257
|
let line2 = '';
|
|
1228
1258
|
// ── 5h Window (from Anthropic OAuth API) ──
|
|
@@ -1230,26 +1260,38 @@ async function launchDashboard(initialSessionName, initialJsonlPath, refreshMs,
|
|
|
1230
1260
|
const pct = usage.five_hour.utilization;
|
|
1231
1261
|
const resetAt = new Date(usage.five_hour.resets_at).getTime();
|
|
1232
1262
|
const remainMs = Math.max(0, resetAt - Date.now());
|
|
1233
|
-
const
|
|
1234
|
-
const rH = Math.floor(
|
|
1235
|
-
const rM =
|
|
1263
|
+
const remainSec = Math.round(remainMs / 1000);
|
|
1264
|
+
const rH = Math.floor(remainSec / 3600);
|
|
1265
|
+
const rM = Math.floor((remainSec % 3600) / 60);
|
|
1266
|
+
const rS = remainSec % 60;
|
|
1236
1267
|
const pctColor = pct < 50 ? 'green' : pct < 80 ? 'yellow' : 'red';
|
|
1237
|
-
const
|
|
1268
|
+
const countdown = `${rH}h${rM.toString().padStart(2, '0')}m${rS.toString().padStart(2, '0')}s`;
|
|
1269
|
+
const resetDate = new Date(resetAt);
|
|
1270
|
+
const resetTime = resetDate.toLocaleString('en-US', { month: 'short', day: 'numeric', year: 'numeric', hour: 'numeric', minute: '2-digit' });
|
|
1238
1271
|
line1 = ` {bold}5h:{/bold}` +
|
|
1239
1272
|
` {${pctColor}-fg}${pct.toFixed(0)}% used{/${pctColor}-fg}` +
|
|
1240
|
-
` {${
|
|
1273
|
+
` {${pctColor}-fg}⏱ ${countdown}{/${pctColor}-fg}` +
|
|
1274
|
+
` resets ${resetTime}`;
|
|
1241
1275
|
}
|
|
1242
1276
|
// ── Weekly (from Anthropic OAuth API) ──
|
|
1243
1277
|
if (usage?.seven_day) {
|
|
1244
1278
|
const pct = usage.seven_day.utilization;
|
|
1245
1279
|
const resetAt = new Date(usage.seven_day.resets_at);
|
|
1246
|
-
const
|
|
1247
|
-
const
|
|
1248
|
-
const
|
|
1280
|
+
const remainMs = Math.max(0, resetAt.getTime() - Date.now());
|
|
1281
|
+
const remainSec = Math.round(remainMs / 1000);
|
|
1282
|
+
const rD = Math.floor(remainSec / 86400);
|
|
1283
|
+
const rH = Math.floor((remainSec % 86400) / 3600);
|
|
1284
|
+
const rM = Math.floor((remainSec % 3600) / 60);
|
|
1285
|
+
const rS = remainSec % 60;
|
|
1249
1286
|
const pctColor = pct < 50 ? 'green' : pct < 80 ? 'yellow' : 'red';
|
|
1287
|
+
const countdown = rD > 0
|
|
1288
|
+
? `${rD}d${rH}h${rM.toString().padStart(2, '0')}m`
|
|
1289
|
+
: `${rH}h${rM.toString().padStart(2, '0')}m${rS.toString().padStart(2, '0')}s`;
|
|
1290
|
+
const resetTime = resetAt.toLocaleString('en-US', { weekday: 'short', month: 'short', day: 'numeric', year: 'numeric', hour: 'numeric', minute: '2-digit' });
|
|
1250
1291
|
line2 = ` {bold}Week:{/bold}` +
|
|
1251
1292
|
` {${pctColor}-fg}${pct.toFixed(0)}% used{/${pctColor}-fg}` +
|
|
1252
|
-
`
|
|
1293
|
+
` {${pctColor}-fg}⏱ ${countdown}{/${pctColor}-fg}` +
|
|
1294
|
+
` resets ${resetTime}`;
|
|
1253
1295
|
}
|
|
1254
1296
|
windowBox.setContent(line1 + (line2 ? '\n' + line2 : ''));
|
|
1255
1297
|
}
|
|
@@ -1262,6 +1304,8 @@ async function launchDashboard(initialSessionName, initialJsonlPath, refreshMs,
|
|
|
1262
1304
|
}
|
|
1263
1305
|
catch { }
|
|
1264
1306
|
}
|
|
1307
|
+
// Legacy wrapper for backward compat
|
|
1308
|
+
async function updateWindowBox() { await fetchAndCacheUsage(); }
|
|
1265
1309
|
// ── Handle terminal resize ──
|
|
1266
1310
|
// Recalculate all widget positions from new screen.height
|
|
1267
1311
|
screen.on('resize', () => {
|
|
@@ -1292,6 +1336,7 @@ async function launchDashboard(initialSessionName, initialJsonlPath, refreshMs,
|
|
|
1292
1336
|
screen.key(['C-c'], () => {
|
|
1293
1337
|
clearInterval(pollInterval);
|
|
1294
1338
|
clearInterval(windowPollInterval);
|
|
1339
|
+
clearInterval(countdownInterval);
|
|
1295
1340
|
clearInterval(headerAnimInterval);
|
|
1296
1341
|
clearInterval(fortuneInterval);
|
|
1297
1342
|
screen.destroy();
|
|
@@ -1301,6 +1346,7 @@ async function launchDashboard(initialSessionName, initialJsonlPath, refreshMs,
|
|
|
1301
1346
|
screen.key(['q'], () => {
|
|
1302
1347
|
clearInterval(pollInterval);
|
|
1303
1348
|
clearInterval(windowPollInterval);
|
|
1349
|
+
clearInterval(countdownInterval);
|
|
1304
1350
|
clearInterval(headerAnimInterval);
|
|
1305
1351
|
clearInterval(fortuneInterval);
|
|
1306
1352
|
screen.destroy();
|
|
@@ -1408,7 +1454,8 @@ async function launchDashboard(initialSessionName, initialJsonlPath, refreshMs,
|
|
|
1408
1454
|
fortuneText = activeFortunes[fortuneIdx];
|
|
1409
1455
|
renderHeader();
|
|
1410
1456
|
}, 30000);
|
|
1411
|
-
const windowPollInterval = setInterval(
|
|
1457
|
+
const windowPollInterval = setInterval(fetchAndCacheUsage, 15000); // fetch fresh data every 15s
|
|
1458
|
+
const countdownInterval = setInterval(renderWindowBox, 1000); // tick countdown every 1s
|
|
1412
1459
|
}
|
|
1413
1460
|
// ── Helpers ──
|
|
1414
1461
|
function fmtK(n) {
|