fleetlens 0.16.6 → 0.16.8
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/app/apps/web/.next/BUILD_ID +1 -1
- package/app/apps/web/.next/build-manifest.json +3 -3
- package/app/apps/web/.next/prerender-manifest.json +3 -3
- package/app/apps/web/.next/server/app/_global-error.html +1 -1
- package/app/apps/web/.next/server/app/_global-error.rsc +1 -1
- package/app/apps/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/app/apps/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/app/apps/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/app/apps/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/app/apps/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/app/apps/web/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/agent/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/agent/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/api/agent/chats/[id]/messages/route.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/api/agent/index/route.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/api/agent/mcp/route.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/api/agent/suggestions/route.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/api/ask/route.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/api/digest/day/[date]/route.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/api/digest/month/[yearMonth]/route.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/api/digest/month-index/route.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/api/digest/week/[startDate]/route.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/api/digest/week-index/route.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/api/pdf/[key]/route.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/api/projects/open-folder/route.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/api/runs/[runId]/route.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/api/runs/[runId]/stream/route.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/api/settings/credentials/route.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/api/settings/route.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/changelog/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/changelog/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/day/[date]/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/day/[date]/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/day/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/day/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/digest/[date]/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/digest/[date]/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/digest/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/digest/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/insights/[key]/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/insights/[key]/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/insights/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/insights/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/parallelism/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/parallelism/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/print/[key]/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/print/[key]/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/projects/[slug]/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/projects/[slug]/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/projects/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/projects/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/runs/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/runs/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/sessions/[id]/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/sessions/[id]/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/sessions/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/sessions/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/settings/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/settings/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/team/onboarding/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/team/onboarding/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/team/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/team/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/usage/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/usage/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_0sr4wr_.js +1 -1
- package/app/apps/web/.next/server/chunks/packages_parser_dist_06d9se1._.js +1 -1
- package/app/apps/web/.next/server/chunks/packages_parser_dist_10ihc3~._.js +1 -1
- package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0-zxvif._.js +1 -1
- package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__01wj6p0._.js +1 -1
- package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0f3q8gt._.js +1 -1
- package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0ygwqfa._.js +1 -1
- package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0z0wa91._.js +1 -1
- package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__12s3gv9._.js +1 -1
- package/app/apps/web/.next/server/chunks/ssr/_0k8yioi._.js +1 -1
- package/app/apps/web/.next/server/chunks/ssr/apps_web_0epqfet._.js +1 -1
- package/app/apps/web/.next/server/chunks/ssr/apps_web_0~aj0~u._.js +1 -1
- package/app/apps/web/.next/server/chunks/ssr/apps_web_components_0x014hn._.js +1 -1
- package/app/apps/web/.next/server/chunks/ssr/apps_web_components_usage-charts-dashboard_tsx_0bgr_uv._.js +1 -1
- package/app/apps/web/.next/server/chunks/ssr/apps_web_lib_changelog_ts_0n7_p1d._.js +1 -1
- package/app/apps/web/.next/server/chunks/ssr/apps_web_lib_changelog_ts_0nb5_-s._.js +1 -1
- package/app/apps/web/.next/server/chunks/ssr/packages_parser_dist_0mx_509._.js +1 -1
- package/app/apps/web/.next/server/middleware-build-manifest.js +3 -3
- package/app/apps/web/.next/server/middleware-manifest.json +5 -5
- package/app/apps/web/.next/server/pages/500.html +1 -1
- package/app/apps/web/.next/server/server-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/server-reference-manifest.json +1 -1
- package/app/apps/web/.next/static/chunks/01ky0t_.38u4u.js +14 -0
- package/app/apps/web/.next/static/chunks/0d~6j~aj2vr1t.js +1 -0
- package/app/apps/web/.next/static/chunks/{0-z49mtd-zwgv.js → 0~opjw4gew2-w.js} +1 -1
- package/app/apps/web/app/settings/page.tsx +1 -1
- package/app/apps/web/app/usage/page.tsx +178 -13
- package/app/apps/web/components/menubar-install-banner.tsx +1 -1
- package/app/apps/web/components/menubar-install.tsx +1 -1
- package/app/apps/web/components/usage-chart-range.tsx +9 -3
- package/app/apps/web/components/usage-chart.tsx +9 -3
- package/app/apps/web/components/usage-charts-dashboard.tsx +10 -3
- package/app/apps/web/lib/changelog-data.generated.ts +1 -1
- package/app/apps/web/lib/format.test.ts +10 -0
- package/app/apps/web/lib/format.ts +6 -4
- package/app/apps/web/lib/usage-data.ts +11 -0
- package/app/apps/web/lib/usage-display.test.ts +79 -0
- package/app/apps/web/lib/usage-display.ts +59 -0
- package/app/apps/web/package.json +1 -1
- package/app/apps/web/tsconfig.tsbuildinfo +1 -1
- package/dist/daemon-worker.js +994 -328
- package/dist/index.js +1085 -413
- package/menubar/FleetlensMenubar.app/Contents/Info.plist +2 -2
- package/menubar/FleetlensMenubar.app/Contents/MacOS/FleetlensMenubar +0 -0
- package/package.json +1 -1
- package/app/apps/web/.next/static/chunks/0rrcj195v1nd..js +0 -1
- package/app/apps/web/.next/static/chunks/1350ox0.qybjo.js +0 -14
- /package/app/apps/web/.next/static/{8slBxbEObd72azKwDvtmy → yxbgwGc3dXx7HY0GSHJeG}/_buildManifest.js +0 -0
- /package/app/apps/web/.next/static/{8slBxbEObd72azKwDvtmy → yxbgwGc3dXx7HY0GSHJeG}/_clientMiddlewareManifest.js +0 -0
- /package/app/apps/web/.next/static/{8slBxbEObd72azKwDvtmy → yxbgwGc3dXx7HY0GSHJeG}/_ssgManifest.js +0 -0
|
@@ -27,10 +27,15 @@ import { UsageChartsDashboard } from "@/components/usage-charts-dashboard";
|
|
|
27
27
|
import { PreviousCyclesTrend } from "@/components/previous-cycles-trend";
|
|
28
28
|
import {
|
|
29
29
|
type AgentKind,
|
|
30
|
-
agentMetadata,
|
|
31
30
|
getAgentMetadata,
|
|
32
31
|
isAgentKind,
|
|
33
32
|
} from "@claude-lens/parser";
|
|
33
|
+
import {
|
|
34
|
+
copilotQuotaPresentation,
|
|
35
|
+
copilotUnitLabel,
|
|
36
|
+
type CopilotMonthlyQuota,
|
|
37
|
+
visibleUsageAgents,
|
|
38
|
+
} from "@/lib/usage-display";
|
|
34
39
|
|
|
35
40
|
export const dynamic = "force-dynamic";
|
|
36
41
|
|
|
@@ -49,17 +54,9 @@ export default async function UsagePage({
|
|
|
49
54
|
}) {
|
|
50
55
|
const params = await searchParams;
|
|
51
56
|
const allSnapshots = readUsageSnapshots();
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
agentsWithData.add(k);
|
|
56
|
-
}
|
|
57
|
-
// Always show Claude in the tab strip even if empty — it's the canonical
|
|
58
|
-
// source. Codex appears only once it has at least one snapshot.
|
|
59
|
-
agentsWithData.add("claude-code");
|
|
60
|
-
// Always list Grok once registered — weekly pool snapshots appear after
|
|
61
|
-
// the daemon's first poll; the tab still works empty.
|
|
62
|
-
if (getAgentMetadata("grok")) agentsWithData.add("grok");
|
|
57
|
+
// Tabs represent usage sources found on this machine. Claude stays as the
|
|
58
|
+
// canonical empty state; every other provider needs a real saved sample.
|
|
59
|
+
const agentsWithData = new Set(visibleUsageAgents(allSnapshots));
|
|
63
60
|
|
|
64
61
|
const requested: AgentKind | undefined = isAgentKind(params.agent) ? params.agent : undefined;
|
|
65
62
|
const selected: AgentKind =
|
|
@@ -78,6 +75,7 @@ export default async function UsagePage({
|
|
|
78
75
|
// longer history just narrows them rather than overflowing.
|
|
79
76
|
const cycles7d = calibration ? previousCyclesTrend(calibration, "7d", 12) : [];
|
|
80
77
|
const isGrok = selected === "grok";
|
|
78
|
+
const isCopilot = selected === "copilot";
|
|
81
79
|
const codexWeeklyOnly =
|
|
82
80
|
selected === "codex" && latest?.five_hour?.utilization == null;
|
|
83
81
|
|
|
@@ -157,6 +155,26 @@ export default async function UsagePage({
|
|
|
157
155
|
</span>
|
|
158
156
|
</span>
|
|
159
157
|
)}
|
|
158
|
+
{isCopilot && latest?.plan_type && (
|
|
159
|
+
<span
|
|
160
|
+
style={{
|
|
161
|
+
marginLeft: "auto",
|
|
162
|
+
display: "inline-flex",
|
|
163
|
+
alignItems: "center",
|
|
164
|
+
gap: 8,
|
|
165
|
+
fontSize: 12,
|
|
166
|
+
padding: "4px 10px",
|
|
167
|
+
border: "1px solid var(--af-border-subtle)",
|
|
168
|
+
borderRadius: 999,
|
|
169
|
+
fontFamily: "var(--font-mono)",
|
|
170
|
+
}}
|
|
171
|
+
>
|
|
172
|
+
<span style={{ color: "var(--af-text-tertiary)" }}>allowance</span>
|
|
173
|
+
<span style={{ color: "var(--af-text)", fontWeight: 600 }}>
|
|
174
|
+
GitHub Copilot · {latest.plan_type}
|
|
175
|
+
</span>
|
|
176
|
+
</span>
|
|
177
|
+
)}
|
|
160
178
|
{selected === "zai" && latest?.plan_type && (
|
|
161
179
|
<span
|
|
162
180
|
style={{
|
|
@@ -207,6 +225,12 @@ export default async function UsagePage({
|
|
|
207
225
|
planType={latest.plan_type}
|
|
208
226
|
/>
|
|
209
227
|
)}
|
|
228
|
+
{isCopilot && latest.monthly && (
|
|
229
|
+
<CopilotMonthlyCard
|
|
230
|
+
window={latest.monthly}
|
|
231
|
+
quota={latest.monthly_quota}
|
|
232
|
+
/>
|
|
233
|
+
)}
|
|
210
234
|
{cycles7d.length > 0 && (
|
|
211
235
|
<PreviousCyclesTrend windowLabel="7d" cycles={cycles7d} />
|
|
212
236
|
)}
|
|
@@ -224,6 +248,17 @@ export default async function UsagePage({
|
|
|
224
248
|
colorVar: "var(--af-accent)",
|
|
225
249
|
},
|
|
226
250
|
]
|
|
251
|
+
: isCopilot
|
|
252
|
+
? [
|
|
253
|
+
{
|
|
254
|
+
key: "monthly",
|
|
255
|
+
label: "Monthly AI-credit utilization",
|
|
256
|
+
// Monthly charts derive their exact start from the
|
|
257
|
+
// reset date; this value is only a nominal fallback.
|
|
258
|
+
windowMs: 30 * 24 * 60 * 60 * 1000,
|
|
259
|
+
colorVar: agentAccent("copilot"),
|
|
260
|
+
},
|
|
261
|
+
]
|
|
227
262
|
: codexWeeklyOnly
|
|
228
263
|
? [
|
|
229
264
|
{
|
|
@@ -235,7 +270,7 @@ export default async function UsagePage({
|
|
|
235
270
|
]
|
|
236
271
|
: undefined
|
|
237
272
|
}
|
|
238
|
-
hideSonnet={isGrok || selected === "codex"}
|
|
273
|
+
hideSonnet={isGrok || isCopilot || selected === "codex"}
|
|
239
274
|
/>
|
|
240
275
|
{selected === "zai" && latest?.web_search_quota && (
|
|
241
276
|
<WebSearchQuotaCard quota={latest.web_search_quota} />
|
|
@@ -251,6 +286,7 @@ export default async function UsagePage({
|
|
|
251
286
|
Last {agentLabel(selected)} poll: {new Date(latest.captured_at).toLocaleString()} ·{" "}
|
|
252
287
|
{snapshots.length} snapshot{snapshots.length === 1 ? "" : "s"} on disk
|
|
253
288
|
{isGrok ? " · weekly shared-pool % (no 5h window)" : ""}
|
|
289
|
+
{isCopilot ? " · monthly AI-credit allowance (no 5h/7d windows)" : ""}
|
|
254
290
|
{codexWeeklyOnly ? " · weekly-only Codex limit" : ""}
|
|
255
291
|
</div>
|
|
256
292
|
</>
|
|
@@ -259,6 +295,99 @@ export default async function UsagePage({
|
|
|
259
295
|
);
|
|
260
296
|
}
|
|
261
297
|
|
|
298
|
+
function CopilotMonthlyCard({
|
|
299
|
+
window,
|
|
300
|
+
quota,
|
|
301
|
+
}: {
|
|
302
|
+
window: { utilization: number | null; resets_at: string | null };
|
|
303
|
+
quota?: CopilotMonthlyQuota | null;
|
|
304
|
+
}) {
|
|
305
|
+
const pct = window.utilization;
|
|
306
|
+
const unit = copilotUnitLabel(quota?.unit);
|
|
307
|
+
const presentation = copilotQuotaPresentation(pct, quota);
|
|
308
|
+
return (
|
|
309
|
+
<div className="af-card" style={{ padding: "16px 18px" }}>
|
|
310
|
+
<div
|
|
311
|
+
style={{
|
|
312
|
+
fontSize: 11,
|
|
313
|
+
color: "var(--af-text-tertiary)",
|
|
314
|
+
textTransform: "uppercase",
|
|
315
|
+
letterSpacing: "0.05em",
|
|
316
|
+
fontWeight: 600,
|
|
317
|
+
}}
|
|
318
|
+
>
|
|
319
|
+
Monthly {unit}
|
|
320
|
+
</div>
|
|
321
|
+
<div
|
|
322
|
+
style={{
|
|
323
|
+
fontSize: 28,
|
|
324
|
+
fontWeight: 700,
|
|
325
|
+
marginTop: 8,
|
|
326
|
+
letterSpacing: "-0.02em",
|
|
327
|
+
lineHeight: 1.1,
|
|
328
|
+
fontVariantNumeric: "tabular-nums",
|
|
329
|
+
}}
|
|
330
|
+
>
|
|
331
|
+
{presentation.headline}
|
|
332
|
+
</div>
|
|
333
|
+
{presentation.detail && (
|
|
334
|
+
<div style={{ marginTop: 6, fontSize: 12, color: "var(--af-text-secondary)" }}>
|
|
335
|
+
{presentation.detail}
|
|
336
|
+
</div>
|
|
337
|
+
)}
|
|
338
|
+
{pct !== null && (
|
|
339
|
+
<div
|
|
340
|
+
style={{
|
|
341
|
+
marginTop: 10,
|
|
342
|
+
height: 8,
|
|
343
|
+
borderRadius: 999,
|
|
344
|
+
background: "var(--af-border-subtle)",
|
|
345
|
+
overflow: "hidden",
|
|
346
|
+
}}
|
|
347
|
+
>
|
|
348
|
+
<div
|
|
349
|
+
style={{
|
|
350
|
+
width: `${Math.min(100, Math.max(0, pct))}%`,
|
|
351
|
+
height: "100%",
|
|
352
|
+
borderRadius: 999,
|
|
353
|
+
background: agentAccent("copilot"),
|
|
354
|
+
}}
|
|
355
|
+
/>
|
|
356
|
+
</div>
|
|
357
|
+
)}
|
|
358
|
+
{presentation.limitNotReported ? (
|
|
359
|
+
<p style={{ fontSize: 12, color: "var(--af-text-tertiary)", marginTop: 10, marginBottom: 0, lineHeight: 1.5 }}>
|
|
360
|
+
Copilot did not expose a personal credit ceiling for this account. Organization-managed plans can
|
|
361
|
+
draw from a shared pool, so this does not mean individual use is unlimited.{" "}
|
|
362
|
+
<a
|
|
363
|
+
href="https://docs.github.com/en/copilot/concepts/billing/usage-based-billing-for-organizations-and-enterprises"
|
|
364
|
+
target="_blank"
|
|
365
|
+
rel="noreferrer"
|
|
366
|
+
style={{ color: "var(--af-accent)" }}
|
|
367
|
+
>
|
|
368
|
+
How GitHub pools AI credits ↗
|
|
369
|
+
</a>
|
|
370
|
+
{" · "}
|
|
371
|
+
<a
|
|
372
|
+
href="https://github.com/settings/billing"
|
|
373
|
+
target="_blank"
|
|
374
|
+
rel="noreferrer"
|
|
375
|
+
style={{ color: "var(--af-accent)" }}
|
|
376
|
+
>
|
|
377
|
+
Verify in GitHub billing ↗
|
|
378
|
+
</a>
|
|
379
|
+
{window.resets_at ? ` · Reported reset ${new Date(window.resets_at).toLocaleString()}.` : ""}
|
|
380
|
+
</p>
|
|
381
|
+
) : (
|
|
382
|
+
<p style={{ fontSize: 12, color: "var(--af-text-tertiary)", marginTop: 10, marginBottom: 0 }}>
|
|
383
|
+
Copilot reports a monthly allowance; it does not expose Codex-style 5-hour or 7-day windows.
|
|
384
|
+
{window.resets_at ? ` Resets ${new Date(window.resets_at).toLocaleString()}.` : ""}
|
|
385
|
+
</p>
|
|
386
|
+
)}
|
|
387
|
+
</div>
|
|
388
|
+
);
|
|
389
|
+
}
|
|
390
|
+
|
|
262
391
|
function WebSearchQuotaCard({
|
|
263
392
|
quota,
|
|
264
393
|
}: {
|
|
@@ -450,6 +579,42 @@ function GrokWeeklyCard({
|
|
|
450
579
|
}
|
|
451
580
|
|
|
452
581
|
function EmptyState({ agent }: { agent: AgentKind }) {
|
|
582
|
+
if (agent === "copilot") {
|
|
583
|
+
return (
|
|
584
|
+
<div className="af-card" style={{ padding: "48px 32px", textAlign: "center" }}>
|
|
585
|
+
<div style={{ fontSize: 15, fontWeight: 600, color: "var(--af-text)" }}>
|
|
586
|
+
No GitHub Copilot usage samples yet
|
|
587
|
+
</div>
|
|
588
|
+
<p
|
|
589
|
+
style={{
|
|
590
|
+
fontSize: 12,
|
|
591
|
+
color: "var(--af-text-tertiary)",
|
|
592
|
+
margin: "8px auto 0",
|
|
593
|
+
maxWidth: 520,
|
|
594
|
+
lineHeight: 1.5,
|
|
595
|
+
}}
|
|
596
|
+
>
|
|
597
|
+
Run <code style={{ fontSize: 11 }}>copilot login</code>, then start the Fleetlens daemon.
|
|
598
|
+
Fleetlens reads the monthly AI-credit quota through Copilot CLI's authenticated local SDK server.
|
|
599
|
+
</p>
|
|
600
|
+
<pre
|
|
601
|
+
style={{
|
|
602
|
+
display: "inline-block",
|
|
603
|
+
marginTop: 14,
|
|
604
|
+
background: "var(--background)",
|
|
605
|
+
border: "1px solid var(--af-border-subtle)",
|
|
606
|
+
padding: "8px 16px",
|
|
607
|
+
borderRadius: 6,
|
|
608
|
+
fontSize: 13,
|
|
609
|
+
fontFamily: "var(--font-mono)",
|
|
610
|
+
color: "var(--af-text)",
|
|
611
|
+
}}
|
|
612
|
+
>
|
|
613
|
+
fleetlens daemon start
|
|
614
|
+
</pre>
|
|
615
|
+
</div>
|
|
616
|
+
);
|
|
617
|
+
}
|
|
453
618
|
if (agent === "grok") {
|
|
454
619
|
return (
|
|
455
620
|
<div
|
|
@@ -83,7 +83,7 @@ export function MenubarInstallBanner() {
|
|
|
83
83
|
}}
|
|
84
84
|
>
|
|
85
85
|
<span style={{ flex: 1 }}>
|
|
86
|
-
Get live Claude Code / Codex / Z.ai plan usage in your menu bar (Grok
|
|
86
|
+
Get live Claude Code / Codex / Copilot / Z.ai plan usage in your menu bar (Grok
|
|
87
87
|
sessions live on the dashboard) —{" "}
|
|
88
88
|
<strong>install the Fleetlens widget</strong>.
|
|
89
89
|
</span>
|
|
@@ -57,7 +57,7 @@ export function MenubarInstall({ initial }: { initial: MenubarState }) {
|
|
|
57
57
|
? state.running
|
|
58
58
|
? "Installed and running — check your menu bar."
|
|
59
59
|
: "Installed but not running."
|
|
60
|
-
: "Adds
|
|
60
|
+
: "Adds live Claude Code, Codex, Copilot, Z.ai, and Grok quota gauges to your menu bar, updated as the daemon polls."}
|
|
61
61
|
</p>
|
|
62
62
|
|
|
63
63
|
<div className="flex flex-wrap gap-2">
|
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
import { useMemo, useState } from "react";
|
|
4
|
-
import type { UsageSnapshot
|
|
4
|
+
import type { UsageSnapshot } from "@/lib/usage-data";
|
|
5
5
|
|
|
6
|
-
type SeriesKey = "five_hour" | "seven_day" | "seven_day_sonnet";
|
|
6
|
+
type SeriesKey = "five_hour" | "seven_day" | "seven_day_sonnet" | "monthly";
|
|
7
|
+
|
|
8
|
+
function cycleStart(seriesKey: SeriesKey, resetsAt: number, windowMs: number): number {
|
|
9
|
+
if (seriesKey !== "monthly") return resetsAt - windowMs;
|
|
10
|
+
const reset = new Date(resetsAt);
|
|
11
|
+
return Date.UTC(reset.getUTCFullYear(), reset.getUTCMonth() - 1, 1);
|
|
12
|
+
}
|
|
7
13
|
|
|
8
14
|
/**
|
|
9
15
|
* Gap threshold: consecutive snapshots separated by more than this are
|
|
@@ -126,7 +132,7 @@ export function UsageChartRange({
|
|
|
126
132
|
|
|
127
133
|
const cycles: CycleData[] = [];
|
|
128
134
|
for (const [resetsAtMs, points] of byReset) {
|
|
129
|
-
const cycleStartMs = resetsAtMs
|
|
135
|
+
const cycleStartMs = cycleStart(seriesKey, resetsAtMs, windowMs);
|
|
130
136
|
// Sort points by time
|
|
131
137
|
points.sort((a, b) => a.t - b.t);
|
|
132
138
|
// Convert utilization → remaining budget
|
|
@@ -5,7 +5,13 @@ import { Maximize2 } from "lucide-react";
|
|
|
5
5
|
import type { UsageSnapshot, UsageWindow } from "@/lib/usage-data";
|
|
6
6
|
import { paceLabel, toneVar } from "@/lib/utilization-tone";
|
|
7
7
|
|
|
8
|
-
type SeriesKey = "five_hour" | "seven_day" | "seven_day_sonnet";
|
|
8
|
+
type SeriesKey = "five_hour" | "seven_day" | "seven_day_sonnet" | "monthly";
|
|
9
|
+
|
|
10
|
+
function cycleStart(seriesKey: SeriesKey, resetsAt: number, windowMs: number): number {
|
|
11
|
+
if (seriesKey !== "monthly") return resetsAt - windowMs;
|
|
12
|
+
const reset = new Date(resetsAt);
|
|
13
|
+
return Date.UTC(reset.getUTCFullYear(), reset.getUTCMonth() - 1, 1);
|
|
14
|
+
}
|
|
9
15
|
|
|
10
16
|
/**
|
|
11
17
|
* Burndown-style chart for a single usage window.
|
|
@@ -52,7 +58,7 @@ export function UsageChart({
|
|
|
52
58
|
.map((s) => ({ capturedAt: new Date(s.captured_at).getTime(), window: s[seriesKey] }))
|
|
53
59
|
.filter(
|
|
54
60
|
(x): x is { capturedAt: number; window: UsageWindow } =>
|
|
55
|
-
x.window
|
|
61
|
+
x.window != null && x.window.utilization !== null,
|
|
56
62
|
);
|
|
57
63
|
|
|
58
64
|
if (valid.length === 0) return null;
|
|
@@ -61,7 +67,7 @@ export function UsageChart({
|
|
|
61
67
|
const resetsAt = latest.window.resets_at ? new Date(latest.window.resets_at).getTime() : null;
|
|
62
68
|
if (!resetsAt) return null;
|
|
63
69
|
|
|
64
|
-
const windowStart = resetsAt
|
|
70
|
+
const windowStart = cycleStart(seriesKey, resetsAt, windowMs);
|
|
65
71
|
const now = Date.now();
|
|
66
72
|
const currentRemaining = 100 - (latest.window.utilization ?? 0);
|
|
67
73
|
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
type DateRange,
|
|
14
14
|
} from "@/components/date-range-picker";
|
|
15
15
|
|
|
16
|
-
type SeriesKey = "five_hour" | "seven_day" | "seven_day_sonnet";
|
|
16
|
+
type SeriesKey = "five_hour" | "seven_day" | "seven_day_sonnet" | "monthly";
|
|
17
17
|
|
|
18
18
|
const HOUR = 60 * 60 * 1000;
|
|
19
19
|
const DAY = 24 * HOUR;
|
|
@@ -99,7 +99,7 @@ export function UsageChartsDashboard({
|
|
|
99
99
|
seriesKey={w.key}
|
|
100
100
|
windowMs={w.windowMs}
|
|
101
101
|
colorVar={w.colorVar}
|
|
102
|
-
predictedSeries={pred[w.key]}
|
|
102
|
+
predictedSeries={w.key === "monthly" ? undefined : pred[w.key]}
|
|
103
103
|
onExpand={() => setExpanded(w)}
|
|
104
104
|
/>
|
|
105
105
|
</section>
|
|
@@ -125,7 +125,7 @@ export function UsageChartsDashboard({
|
|
|
125
125
|
<ExpandedModal
|
|
126
126
|
config={expanded}
|
|
127
127
|
snapshots={snapshots}
|
|
128
|
-
predictedSeries={pred[expanded.key]}
|
|
128
|
+
predictedSeries={expanded.key === "monthly" ? undefined : pred[expanded.key]}
|
|
129
129
|
onClose={() => setExpanded(null)}
|
|
130
130
|
/>
|
|
131
131
|
)}
|
|
@@ -154,6 +154,13 @@ function ExpandedModal({
|
|
|
154
154
|
const w = snapshots[i]![config.key];
|
|
155
155
|
if (w && w.resets_at) {
|
|
156
156
|
const endMs = new Date(w.resets_at).getTime();
|
|
157
|
+
if (config.key === "monthly") {
|
|
158
|
+
const end = new Date(endMs);
|
|
159
|
+
return {
|
|
160
|
+
startMs: Date.UTC(end.getUTCFullYear(), end.getUTCMonth() - 1, 1),
|
|
161
|
+
endMs,
|
|
162
|
+
};
|
|
163
|
+
}
|
|
157
164
|
return { startMs: endMs - config.windowMs, endMs };
|
|
158
165
|
}
|
|
159
166
|
}
|