quotacap 0.0.15 → 0.0.17

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.
@@ -8,12 +8,14 @@ export function parseClaudeUsage(result, now = new Date()) {
8
8
  const usedPct = weeklyMatch ? parseInt(weeklyMatch[1], 10) : 0;
9
9
  const sessionPct = sessionMatch ? parseInt(sessionMatch[1], 10) : undefined;
10
10
  let resetsAt = parseResetText(result, now);
11
+ const parsedReset = !!resetsAt;
11
12
  if (!resetsAt)
12
13
  resetsAt = new Date(now.getTime() + 7 * 86400000).toISOString();
14
+ const resets = new Date(resetsAt).getTime();
15
+ const periodStart = (parsedReset ? new Date(resets - 7 * 86400000) : new Date(now.getTime() - 7 * 86400000)).toISOString();
13
16
  return {
14
17
  provider: "claude", plan: "max", usedPct, sessionPct,
15
- resetsAt, periodStart: new Date(now.getTime() - 7 * 86400000).toISOString(),
16
- raw: result, source: "cli", fetchedAt: now.toISOString()
18
+ resetsAt, periodStart, raw: result, source: "cli", fetchedAt: now.toISOString()
17
19
  };
18
20
  }
19
21
  export const claudeAdapter = {
@@ -2,13 +2,15 @@ import { parseResetText } from "./parse.js";
2
2
  export function parseManualUsage(provider, text, now = new Date()) {
3
3
  const m = text.match(/(\d+)% used/);
4
4
  const usedPct = m ? parseInt(m[1], 10) : 0;
5
- const resetsAt = parseResetText(text, now) ?? new Date(now.getTime() + 3 * 86400000).toISOString();
5
+ const parsedReset = parseResetText(text, now);
6
+ const resetsAt = parsedReset ?? new Date(now.getTime() + 3 * 86400000).toISOString();
7
+ const periodStart = (parsedReset ? new Date(new Date(resetsAt).getTime() - 7 * 86400000) : new Date(now.getTime() - 7 * 86400000)).toISOString();
6
8
  return {
7
9
  provider,
8
10
  plan: "unknown",
9
11
  usedPct,
10
12
  resetsAt,
11
- periodStart: new Date(now.getTime() - 7 * 86400000).toISOString(),
13
+ periodStart,
12
14
  raw: text,
13
15
  source: "manual",
14
16
  fetchedAt: now.toISOString(),
@@ -17,17 +17,40 @@ export function renderQuotasTable(quotas, advisories = []) {
17
17
  const resets = q.resetsAt ? formatResetDate(q.resetsAt) : "—";
18
18
  const daysLeft = a?.daysLeft != null ? a.daysLeft.toFixed(1) : "—";
19
19
  const ideal = a?.idealRate != null ? `${Math.round(a.idealRate)}%/day` : "—";
20
- const burn = a?.burnMeasured ? `${a.burnRate.toFixed(1)}%/day` : "—";
20
+ const burn = (() => {
21
+ const rate = a?.burnMeasured ? `${a.burnRate.toFixed(1)}%/day`
22
+ : q.periodStart ? (() => {
23
+ const elapsed = (Date.now() - new Date(q.periodStart).getTime()) / 86400000;
24
+ if (elapsed > 0.1)
25
+ return `${((q.usedPct ?? 0) / elapsed).toFixed(1)}%/day avg`;
26
+ return null;
27
+ })()
28
+ : null;
29
+ if (!rate)
30
+ return "—";
31
+ // Pace glyphs are single-width text-presentation (no VS16), so they
32
+ // cannot shift pipes in renderers that miscount emoji width.
33
+ const value = a?.burnMeasured ? a.burnRate : q.periodStart ? (q.usedPct ?? 0) / Math.max(0.1, (Date.now() - new Date(q.periodStart).getTime()) / 86400000) : null;
34
+ const ideal = a?.idealRate;
35
+ if (ideal != null && value != null) {
36
+ if (value > ideal)
37
+ return `${rate} ⚠`;
38
+ if (value < ideal * 0.8)
39
+ return `${rate} ↓`;
40
+ return `${rate} ✔`;
41
+ }
42
+ return rate;
43
+ })();
21
44
  // Renderers that count emoji as one cell but paint two (Kimi, Claude
22
45
  // Code) shift the pipes after a wide glyph by +1; the status glyphs
23
46
  // live in the last column so the shift touches only the right border.
24
47
  const icon = a?.status === "at risk" ? "⚠️" : a != null ? "✅" : "—";
25
48
  const waste = a?.wastePct != null ? `${Math.round(a.wastePct)}%` : "—";
26
- return `| ${q.provider} | ${used}% | ${100 - used}% | ${resets} | ${daysLeft} | ${ideal} | ${burn} | ${waste} | ${icon} |`;
49
+ return `| ${q.provider} | ${used}% | ${100 - used}% | ${resets} | ${daysLeft} | ${ideal} | ${burn} | ${waste} |`;
27
50
  });
28
51
  return [
29
- "| Provider | Used | Remaining | Resets | Days left | Ideal daily burn | Burn rate | Waste if unused | Status |",
30
- "|---|---|---|---|---|---|---|---|---|",
52
+ "| Provider | Used | Left | Resets | Days left | Ideal daily burn | Burn rate | Waste if unused |",
53
+ "|---|---|---|---|---|---|---|---|",
31
54
  ...rows,
32
55
  ].join("\n");
33
56
  }
@@ -8,12 +8,14 @@ export function parseClaudeUsage(result, now = new Date()) {
8
8
  const usedPct = weeklyMatch ? parseInt(weeklyMatch[1], 10) : 0;
9
9
  const sessionPct = sessionMatch ? parseInt(sessionMatch[1], 10) : undefined;
10
10
  let resetsAt = parseResetText(result, now);
11
+ const parsedReset = !!resetsAt;
11
12
  if (!resetsAt)
12
13
  resetsAt = new Date(now.getTime() + 7 * 86400000).toISOString();
14
+ const resets = new Date(resetsAt).getTime();
15
+ const periodStart = (parsedReset ? new Date(resets - 7 * 86400000) : new Date(now.getTime() - 7 * 86400000)).toISOString();
13
16
  return {
14
17
  provider: "claude", plan: "max", usedPct, sessionPct,
15
- resetsAt, periodStart: new Date(now.getTime() - 7 * 86400000).toISOString(),
16
- raw: result, source: "cli", fetchedAt: now.toISOString()
18
+ resetsAt, periodStart, raw: result, source: "cli", fetchedAt: now.toISOString()
17
19
  };
18
20
  }
19
21
  export const claudeAdapter = {
@@ -2,13 +2,15 @@ import { parseResetText } from "./parse.js";
2
2
  export function parseManualUsage(provider, text, now = new Date()) {
3
3
  const m = text.match(/(\d+)% used/);
4
4
  const usedPct = m ? parseInt(m[1], 10) : 0;
5
- const resetsAt = parseResetText(text, now) ?? new Date(now.getTime() + 3 * 86400000).toISOString();
5
+ const parsedReset = parseResetText(text, now);
6
+ const resetsAt = parsedReset ?? new Date(now.getTime() + 3 * 86400000).toISOString();
7
+ const periodStart = (parsedReset ? new Date(new Date(resetsAt).getTime() - 7 * 86400000) : new Date(now.getTime() - 7 * 86400000)).toISOString();
6
8
  return {
7
9
  provider,
8
10
  plan: "unknown",
9
11
  usedPct,
10
12
  resetsAt,
11
- periodStart: new Date(now.getTime() - 7 * 86400000).toISOString(),
13
+ periodStart,
12
14
  raw: text,
13
15
  source: "manual",
14
16
  fetchedAt: now.toISOString(),
@@ -17,17 +17,40 @@ export function renderQuotasTable(quotas, advisories = []) {
17
17
  const resets = q.resetsAt ? formatResetDate(q.resetsAt) : "—";
18
18
  const daysLeft = a?.daysLeft != null ? a.daysLeft.toFixed(1) : "—";
19
19
  const ideal = a?.idealRate != null ? `${Math.round(a.idealRate)}%/day` : "—";
20
- const burn = a?.burnMeasured ? `${a.burnRate.toFixed(1)}%/day` : "—";
20
+ const burn = (() => {
21
+ const rate = a?.burnMeasured ? `${a.burnRate.toFixed(1)}%/day`
22
+ : q.periodStart ? (() => {
23
+ const elapsed = (Date.now() - new Date(q.periodStart).getTime()) / 86400000;
24
+ if (elapsed > 0.1)
25
+ return `${((q.usedPct ?? 0) / elapsed).toFixed(1)}%/day avg`;
26
+ return null;
27
+ })()
28
+ : null;
29
+ if (!rate)
30
+ return "—";
31
+ // Pace glyphs are single-width text-presentation (no VS16), so they
32
+ // cannot shift pipes in renderers that miscount emoji width.
33
+ const value = a?.burnMeasured ? a.burnRate : q.periodStart ? (q.usedPct ?? 0) / Math.max(0.1, (Date.now() - new Date(q.periodStart).getTime()) / 86400000) : null;
34
+ const ideal = a?.idealRate;
35
+ if (ideal != null && value != null) {
36
+ if (value > ideal)
37
+ return `${rate} ⚠`;
38
+ if (value < ideal * 0.8)
39
+ return `${rate} ↓`;
40
+ return `${rate} ✔`;
41
+ }
42
+ return rate;
43
+ })();
21
44
  // Renderers that count emoji as one cell but paint two (Kimi, Claude
22
45
  // Code) shift the pipes after a wide glyph by +1; the status glyphs
23
46
  // live in the last column so the shift touches only the right border.
24
47
  const icon = a?.status === "at risk" ? "⚠️" : a != null ? "✅" : "—";
25
48
  const waste = a?.wastePct != null ? `${Math.round(a.wastePct)}%` : "—";
26
- return `| ${q.provider} | ${used}% | ${100 - used}% | ${resets} | ${daysLeft} | ${ideal} | ${burn} | ${waste} | ${icon} |`;
49
+ return `| ${q.provider} | ${used}% | ${100 - used}% | ${resets} | ${daysLeft} | ${ideal} | ${burn} | ${waste} |`;
27
50
  });
28
51
  return [
29
- "| Provider | Used | Remaining | Resets | Days left | Ideal daily burn | Burn rate | Waste if unused | Status |",
30
- "|---|---|---|---|---|---|---|---|---|",
52
+ "| Provider | Used | Left | Resets | Days left | Ideal daily burn | Burn rate | Waste if unused |",
53
+ "|---|---|---|---|---|---|---|---|",
31
54
  ...rows,
32
55
  ].join("\n");
33
56
  }
@@ -1,2 +1,2 @@
1
1
  // generated by scripts/build-embed.mjs — do not edit
2
- export const VERSION = "0.0.15";
2
+ export const VERSION = "0.0.17";