letmecode 0.1.12 → 0.1.15

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.
@@ -696,7 +696,7 @@ async function buildLiveLimitWindows(options) {
696
696
  if (snapshots.length === 0) {
697
697
  traceClaude(options.traceLogger, "No live usage snapshots matched the expected /usage format.");
698
698
  }
699
- const resolvedPlanType = subscriptionType || "live";
699
+ const resolvedPlanType = resolveClaudeLivePlanType(subscriptionType, snapshots);
700
700
  traceClaude(options.traceLogger, `Resolved live plan type ${resolvedPlanType}.`);
701
701
  const primaryLimitWindows = snapshots
702
702
  .filter((snapshot) => snapshot.scope === "primary")
@@ -707,14 +707,15 @@ async function buildLiveLimitWindows(options) {
707
707
  for (let index = 0; index < snapshots.length; index += 1) {
708
708
  const snapshot = snapshots[index];
709
709
  const row = snapshot.scope === "primary"
710
- ? primaryLimitWindows.filter((window) => window.limitId === `current-${snapshot.label}`)[0]
711
- : secondaryLimitWindows.filter((window) => window.limitId === `current-${snapshot.label}`)[0];
710
+ ? primaryLimitWindows.find((window) => window.limitId === snapshot.limitId)
711
+ : secondaryLimitWindows.find((window) => window.limitId === snapshot.limitId);
712
712
  if (!row) {
713
713
  continue;
714
714
  }
715
715
  traceClaude(options.traceLogger, [
716
716
  `Live window ${snapshot.scope}/${snapshot.label}:`,
717
717
  `used=${snapshot.usedPercent}%`,
718
+ `limit=${snapshot.limitId}`,
718
719
  `range=${row.startTimeUtcIso}->${row.endTimeUtcIso}`,
719
720
  `matchedEvents=${row.eventCount}`,
720
721
  `input=${row.totals.inputTokens}`,
@@ -728,6 +729,12 @@ async function buildLiveLimitWindows(options) {
728
729
  secondaryLimitWindows
729
730
  };
730
731
  }
732
+ function resolveClaudeLivePlanType(subscriptionType, snapshots) {
733
+ if (snapshots.some((snapshot) => snapshot.modelScope === "sonnet-only")) {
734
+ return "team_premium";
735
+ }
736
+ return subscriptionType || "live";
737
+ }
731
738
  async function readClaudeSubscriptionType(root, override, traceLogger) {
732
739
  const output = await readClaudeAuthStatusOutput(root, override, traceLogger);
733
740
  const subscriptionType = parseClaudeSubscriptionType(output);
@@ -992,26 +999,32 @@ function parseLiveUsageWindowSnapshots(usageOutput, now) {
992
999
  for (const line of normalizedOutput.split(/\r?\n/)) {
993
1000
  const match = line
994
1001
  .trim()
995
- .match(/^Current\s+(session|week)(?:\s+\([^)]+\))?:\s+(\d+)%\s+used\b.*?\bresets\s+(.+)$/i);
1002
+ .match(/^Current\s+(session|week)(?:\s+\(([^)]+)\))?:\s+(\d+)%\s+used\b.*?\bresets\s+(.+)$/i);
996
1003
  if (!match) {
997
1004
  continue;
998
1005
  }
999
1006
  const label = match[1].toLowerCase() === "session" ? "session" : "week";
1000
- const usedPercent = Number(match[2]);
1007
+ const windowQualifier = (match[2] ?? "").trim().toLowerCase();
1008
+ const usedPercent = Number(match[3]);
1001
1009
  const windowMinutes = label === "session" ? CLAUDE_SESSION_WINDOW_MINUTES : CLAUDE_WEEK_WINDOW_MINUTES;
1002
- const resetsAtMs = parseResetTimestampUtc(match[3], now.getTime(), windowMinutes);
1010
+ const resetsAtMs = parseResetTimestampUtc(match[4], now.getTime(), windowMinutes);
1003
1011
  if (!Number.isFinite(usedPercent) || !resetsAtMs) {
1004
1012
  continue;
1005
1013
  }
1006
- snapshots.set(label, {
1014
+ const isSonnetOnlyWeek = label === "week" && windowQualifier === "sonnet only";
1015
+ const limitId = isSonnetOnlyWeek ? "current-week-sonnet-only" : `current-${label}`;
1016
+ snapshots.set(limitId, {
1007
1017
  scope: label === "session" ? "primary" : "secondary",
1008
1018
  label,
1019
+ limitId,
1020
+ modelScope: isSonnetOnlyWeek ? "sonnet-only" : "all-models",
1021
+ modelType: isSonnetOnlyWeek ? "sonnet only" : undefined,
1009
1022
  usedPercent,
1010
1023
  resetsAtMs,
1011
1024
  windowMinutes
1012
1025
  });
1013
1026
  }
1014
- return [...snapshots.values()].sort((left, right) => left.windowMinutes - right.windowMinutes);
1027
+ return [...snapshots.values()].sort((left, right) => left.windowMinutes - right.windowMinutes || left.limitId.localeCompare(right.limitId));
1015
1028
  }
1016
1029
  function parseResetTimestampUtc(value, nowMs, windowMinutes) {
1017
1030
  const match = value
@@ -1062,7 +1075,8 @@ function buildLiveLimitWindowRow(snapshot, planType, selectedEvents, now) {
1062
1075
  const startTimeMs = snapshot.resetsAtMs - snapshot.windowMinutes * 60000;
1063
1076
  const inWindowEvents = selectedEvents.filter((event) => Number.isFinite(event.timestampMs) &&
1064
1077
  event.timestampMs >= startTimeMs &&
1065
- event.timestampMs < snapshot.resetsAtMs);
1078
+ event.timestampMs < snapshot.resetsAtMs &&
1079
+ matchesClaudeLiveSnapshotModelScope(snapshot, event.modelId));
1066
1080
  const totals = sumUsageTotals(inWindowEvents.map((event) => event.totals));
1067
1081
  const fallbackLastSeenMs = Math.min(now.getTime(), snapshot.resetsAtMs);
1068
1082
  const firstSeenMs = inWindowEvents.reduce((minimum, event) => Math.min(minimum, event.timestampMs), Number.POSITIVE_INFINITY);
@@ -1070,7 +1084,8 @@ function buildLiveLimitWindowRow(snapshot, planType, selectedEvents, now) {
1070
1084
  return {
1071
1085
  scope: snapshot.scope,
1072
1086
  planType,
1073
- limitId: `current-${snapshot.label}`,
1087
+ limitId: snapshot.limitId,
1088
+ modelType: snapshot.modelType,
1074
1089
  windowMinutes: snapshot.windowMinutes,
1075
1090
  startTimeUtcIso: toUtcIso(startTimeMs),
1076
1091
  endTimeUtcIso: toUtcIso(snapshot.resetsAtMs),
@@ -1083,6 +1098,12 @@ function buildLiveLimitWindowRow(snapshot, planType, selectedEvents, now) {
1083
1098
  eventCount: totals.eventCount
1084
1099
  };
1085
1100
  }
1101
+ function matchesClaudeLiveSnapshotModelScope(snapshot, modelId) {
1102
+ if (snapshot.modelScope !== "sonnet-only") {
1103
+ return true;
1104
+ }
1105
+ return modelId.toLowerCase().includes("sonnet");
1106
+ }
1086
1107
  function buildModelUsageRowsForEvents(events) {
1087
1108
  const byModel = new Map();
1088
1109
  for (const event of events) {
@@ -10,7 +10,7 @@ export function createProviders() {
10
10
  new AntigravityUsageProvider()
11
11
  ];
12
12
  }
13
- export { AntigravityUsageProvider, collectAntigravityQuota, collectAntigravityUsage } from "./antigravity.js";
13
+ export { AntigravityUsageProvider } from "./antigravity.js";
14
14
  export { ClaudeUsageProvider } from "./claude.js";
15
15
  export { CodexUsageProvider } from "./codex.js";
16
16
  export { CopilotUsageProvider, configureCopilotVsCodeLogging } from "./copilot.js";
@@ -4,6 +4,7 @@ import path from "node:path";
4
4
  import { fileURLToPath } from "node:url";
5
5
  const REPORTING_ENDPOINT = "https://devforth.io/admin/api/report_ussage_anonymous";
6
6
  const CREDIT_TO_DOLLARS = 0.01;
7
+ const MIN_REPORTED_USED_PERCENTS = 1;
7
8
  let versionCache = null;
8
9
  export async function reportAnonymousUsage(statsList) {
9
10
  const payload = await buildAnonymousUsagePayload(statsList);
@@ -15,10 +16,12 @@ export async function reportAnonymousUsage(statsList) {
15
16
  export async function buildAnonymousUsageReports(statsList) {
16
17
  const letmecodeVersion = await readLetmecodeVersion();
17
18
  return statsList.flatMap((stats) => {
18
- if (!stats.analytics?.userIdHash) {
19
+ if (!stats.analytics?.userIdHash || stats.providerId === "antigravity") {
19
20
  return [];
20
21
  }
21
- return [...stats.primaryLimitWindows, ...stats.secondaryLimitWindows].map((window) => buildAnonymousUsageReport(stats, window, letmecodeVersion));
22
+ return [...stats.primaryLimitWindows, ...stats.secondaryLimitWindows]
23
+ .filter((window) => shouldReportUsageWindow(window))
24
+ .map((window) => buildAnonymousUsageReport(stats, window, letmecodeVersion));
22
25
  });
23
26
  }
24
27
  export async function buildAnonymousUsagePayload(statsList) {
@@ -46,6 +49,9 @@ function resolveReportModelType(stats, window) {
46
49
  if (stats.providerId === "antigravity") {
47
50
  return resolveAntigravityReportModelType(stats, window);
48
51
  }
52
+ if (window.modelType) {
53
+ return truncateSchemaString(window.modelType, 128);
54
+ }
49
55
  if (window.limitId && window.limitId !== "unknown") {
50
56
  return truncateSchemaString(window.limitId, 128);
51
57
  }
@@ -86,6 +92,9 @@ function resolveReportedUsedPercents(window) {
86
92
  }
87
93
  return clampPercent(window.maxUsedPercent - window.minUsedPercent);
88
94
  }
95
+ function shouldReportUsageWindow(window) {
96
+ return resolveReportedUsedPercents(window) >= MIN_REPORTED_USED_PERCENTS;
97
+ }
89
98
  function clampPercent(value) {
90
99
  if (!Number.isFinite(value)) {
91
100
  return 0;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "letmecode",
3
- "version": "0.1.12",
3
+ "version": "0.1.15",
4
4
  "description": "Provider-based terminal usage dashboard for LetMeCode.",
5
5
  "author": "devforth.io",
6
6
  "license": "MIT",
@@ -38,7 +38,6 @@
38
38
  "typescript"
39
39
  ],
40
40
  "dependencies": {
41
- "@tokscale/cli": "4.0.2",
42
41
  "ink": "4.4.1",
43
42
  "jsonc-parser": "^3.3.1",
44
43
  "react": "18.3.1"