fleetlens 0.13.0 → 0.13.2
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 +8 -8
- package/app/apps/web/.next/prerender-manifest.json +3 -3
- package/app/apps/web/.next/server/app/_global-error/page/build-manifest.json +5 -5
- package/app/apps/web/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/_global-error.html +1 -1
- package/app/apps/web/.next/server/app/_global-error.rsc +7 -7
- package/app/apps/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +2 -2
- package/app/apps/web/.next/server/app/_global-error.segments/_full.segment.rsc +7 -7
- package/app/apps/web/.next/server/app/_global-error.segments/_head.segment.rsc +3 -3
- package/app/apps/web/.next/server/app/_global-error.segments/_index.segment.rsc +3 -3
- 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/build-manifest.json +5 -5
- package/app/apps/web/.next/server/app/_not-found/page.js +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/api/ask/route.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/api/digest/day/[date]/route.js +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 +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 +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/events/route.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/api/jobs/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/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/runs/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/api/team-config/route.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/api/workflow-agent/route.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/changelog/page/build-manifest.json +5 -5
- package/app/apps/web/.next/server/app/changelog/page.js +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/build-manifest.json +5 -5
- package/app/apps/web/.next/server/app/day/[date]/page.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/build-manifest.json +5 -5
- package/app/apps/web/.next/server/app/day/page.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/build-manifest.json +5 -5
- package/app/apps/web/.next/server/app/digest/[date]/page.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/build-manifest.json +5 -5
- package/app/apps/web/.next/server/app/digest/page.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/build-manifest.json +5 -5
- package/app/apps/web/.next/server/app/insights/[key]/page.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/build-manifest.json +5 -5
- package/app/apps/web/.next/server/app/insights/page.js +3 -2
- 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/build-manifest.json +5 -5
- package/app/apps/web/.next/server/app/page.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/build-manifest.json +5 -5
- package/app/apps/web/.next/server/app/parallelism/page.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/build-manifest.json +5 -5
- package/app/apps/web/.next/server/app/print/[key]/page.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/build-manifest.json +5 -5
- package/app/apps/web/.next/server/app/projects/[slug]/page.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/build-manifest.json +5 -5
- package/app/apps/web/.next/server/app/projects/page.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/build-manifest.json +5 -5
- package/app/apps/web/.next/server/app/runs/page.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/build-manifest.json +5 -5
- package/app/apps/web/.next/server/app/sessions/[id]/page.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/build-manifest.json +5 -5
- package/app/apps/web/.next/server/app/sessions/page.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/build-manifest.json +5 -5
- package/app/apps/web/.next/server/app/settings/page.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/page/build-manifest.json +5 -5
- package/app/apps/web/.next/server/app/team/page.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/build-manifest.json +5 -5
- package/app/apps/web/.next/server/app/usage/page.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/[root-of-the-server]__0gril0u._.js +9 -0
- package/app/apps/web/.next/server/chunks/[root-of-the-server]__0n79hpm._.js +9 -0
- package/app/apps/web/.next/server/chunks/[root-of-the-server]__0~utv~v._.js +9 -0
- 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]__0exckd4._.js +1 -1
- package/app/apps/web/.next/server/chunks/ssr/{[root-of-the-server]__0-1_njm._.js → [root-of-the-server]__0k1x-c9._.js} +2 -2
- package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0lhwp-k._.js +8 -0
- package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0n-ax-5._.js +1 -1
- package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0~.f_3z._.js +3 -3
- package/app/apps/web/.next/server/chunks/ssr/_0kajk31._.js +1 -1
- package/app/apps/web/.next/server/chunks/ssr/_0s3n5mz._.js +8 -0
- package/app/apps/web/.next/server/chunks/ssr/_0xu2pvc._.js +1 -1
- package/app/apps/web/.next/server/chunks/ssr/{_0jw2sm7._.js → _12mbgs5._.js} +2 -2
- package/app/apps/web/.next/server/chunks/ssr/apps_web_app_day_[date]_day-view_tsx_0wjoe1u._.js +1 -1
- package/app/apps/web/.next/server/chunks/ssr/apps_web_app_insights_page_tsx_071kwyw._.js +3 -0
- package/app/apps/web/.next/server/chunks/ssr/apps_web_app_sessions_sessions-grid_tsx_0bri5x.._.js +3 -0
- package/app/apps/web/.next/server/chunks/ssr/apps_web_components_0l6hsch._.js +6 -0
- 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/node_modules_lucide-react_dist_esm_icons_0cepfi8._.js +3 -0
- package/app/apps/web/.next/server/chunks/ssr/packages_entries_dist_node_0_271yr.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 +8 -8
- 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/02xxpexzk3rf-.js +1 -0
- package/app/apps/web/.next/static/chunks/{07lbm-sdh9c2j.js → 0389wb_f0j78j.js} +1 -1
- package/app/apps/web/.next/static/chunks/03gy1evbn94qu.js +6 -0
- package/app/apps/web/.next/static/chunks/03rd.3cc_t~yf.js +4 -0
- package/app/apps/web/.next/static/chunks/{0l4e3as18eexy.js → 0g223u9amib0q.js} +1 -1
- package/app/apps/web/.next/static/chunks/{0h3.-_s-k0mj3.js → 0i.hc~2bj0_3d.js} +1 -1
- package/app/apps/web/.next/static/chunks/0m7dd-2zd9vl2.js +4 -0
- package/app/apps/web/.next/static/chunks/0nqklmbs5s3ax.js +6 -0
- package/app/apps/web/.next/static/chunks/{073081qv4uyn8.js → 0p--hzrway8~3.js} +1 -1
- package/app/apps/web/.next/static/chunks/{0gblp_v.kiu-..js → 0qv-gw888_5nu.js} +1 -1
- package/app/apps/web/.next/static/chunks/{14bjditmmrn7l.js → 0tirv_vo6q8uc.js} +1 -1
- package/app/apps/web/.next/static/chunks/0uyctl-m8iydb.js +1 -0
- package/app/apps/web/.next/static/chunks/0~qdw1b2uz._5.js +10 -0
- package/app/apps/web/.next/static/chunks/{0n0n.0279ot6c.js → 14vx702c32kn6.js} +1 -1
- package/app/apps/web/.next/static/chunks/{turbopack-0lb9w090w5ezx.js → turbopack-0mxu3~9vk.brh.js} +1 -1
- package/app/apps/web/app/api/digest/_shared/digest-route-helpers.ts +178 -0
- package/app/apps/web/app/api/digest/day/[date]/route.ts +34 -128
- package/app/apps/web/app/api/digest/month/[yearMonth]/route.ts +36 -121
- package/app/apps/web/app/api/digest/week/[startDate]/route.ts +36 -123
- package/app/apps/web/app/insights/page.tsx +20 -5
- package/app/apps/web/app/parallelism/burst-detail-modal.tsx +637 -0
- package/app/apps/web/app/parallelism/concurrency-info-modal.tsx +228 -0
- package/app/apps/web/app/parallelism/gantt-chart-utils.ts +49 -0
- package/app/apps/web/app/parallelism/gantt-chart.tsx +24 -903
- package/app/apps/web/app/sessions/[id]/session-view/colors.ts +31 -0
- package/app/apps/web/app/sessions/[id]/session-view/debug-list.tsx +78 -0
- package/app/apps/web/app/sessions/[id]/session-view/drawer.tsx +414 -0
- package/app/apps/web/app/sessions/[id]/session-view/entry-day-strip.tsx +124 -0
- package/app/apps/web/app/sessions/[id]/session-view/header-stats.tsx +217 -0
- package/app/apps/web/app/sessions/[id]/session-view/helpers.ts +135 -0
- package/app/apps/web/app/sessions/[id]/session-view/minimap.tsx +1587 -0
- package/app/apps/web/app/sessions/[id]/session-view/subagent-drawer.tsx +339 -0
- package/app/apps/web/app/sessions/[id]/session-view/token-stats.tsx +111 -0
- package/app/apps/web/app/sessions/[id]/session-view/tooltip.tsx +63 -0
- package/app/apps/web/app/sessions/[id]/session-view/types.ts +46 -0
- package/app/apps/web/app/sessions/[id]/session-view/workflows-panel.tsx +944 -0
- package/app/apps/web/app/sessions/[id]/session-view/workflows-shared.tsx +104 -0
- package/app/apps/web/app/sessions/[id]/session-view.tsx +1513 -5018
- package/app/apps/web/app/sessions/[id]/team-tab/minimap-hover-card.tsx +201 -0
- package/app/apps/web/app/sessions/[id]/team-tab/minimap-idle-band.tsx +53 -0
- package/app/apps/web/app/sessions/[id]/team-tab/minimap-shared.ts +20 -0
- package/app/apps/web/app/sessions/[id]/team-tab/team-minimap.tsx +10 -265
- package/app/apps/web/app/sessions/sessions-grid.tsx +10 -10
- package/app/apps/web/components/digest-actions.tsx +103 -0
- package/app/apps/web/components/job-queue-widget.tsx +1 -1
- package/app/apps/web/components/month-digest-view.tsx +19 -64
- package/app/apps/web/components/searchable-select.tsx +215 -0
- package/app/apps/web/components/tail-mode.tsx +46 -9
- package/app/apps/web/components/week-digest-view.tsx +16 -71
- package/app/apps/web/lib/changelog-data.generated.ts +1 -1
- package/app/apps/web/lib/entries-index.ts +7 -8
- package/app/apps/web/lib/entries.ts +0 -9
- package/app/apps/web/lib/format.test.ts +221 -0
- package/app/apps/web/lib/format.ts +17 -0
- package/app/apps/web/lib/utilization-tone.test.ts +56 -0
- package/app/apps/web/package.json +1 -1
- package/app/apps/web/tsconfig.tsbuildinfo +1 -1
- package/dist/daemon-worker.js +35 -43
- package/dist/index.js +37 -49
- package/package.json +1 -1
- package/app/apps/web/.next/server/chunks/[root-of-the-server]__04j3tng._.js +0 -9
- package/app/apps/web/.next/server/chunks/[root-of-the-server]__0cnh3k4._.js +0 -9
- package/app/apps/web/.next/server/chunks/[root-of-the-server]__132eosx._.js +0 -9
- package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__136-0me._.js +0 -8
- package/app/apps/web/.next/server/chunks/ssr/_0w.cv64._.js +0 -8
- package/app/apps/web/.next/server/chunks/ssr/apps_web_components_01d-iwx._.js +0 -6
- package/app/apps/web/.next/server/chunks/ssr/node_modules_lucide-react_dist_esm_icons_0-lmm4p._.js +0 -3
- package/app/apps/web/.next/static/chunks/05k84gif3ucxm.js +0 -6
- package/app/apps/web/.next/static/chunks/0nnjldasalwdu.js +0 -4
- package/app/apps/web/.next/static/chunks/0okg13phslr1..js +0 -4
- package/app/apps/web/.next/static/chunks/0pzt9vemtzct8.js +0 -10
- package/app/apps/web/.next/static/chunks/0qmh5da6gfm56.js +0 -1
- package/app/apps/web/.next/static/chunks/0rnay-a.-jr8-.js +0 -1
- package/app/apps/web/.next/static/chunks/0snv3-2ux2xvv.js +0 -6
- /package/app/apps/web/.next/static/{rbp9x3iPbzrz-wxS-DAU5 → bZs5UyzsYBqC6MFY5n-M_}/_buildManifest.js +0 -0
- /package/app/apps/web/.next/static/{rbp9x3iPbzrz-wxS-DAU5 → bZs5UyzsYBqC6MFY5n-M_}/_clientMiddlewareManifest.js +0 -0
- /package/app/apps/web/.next/static/{rbp9x3iPbzrz-wxS-DAU5 → bZs5UyzsYBqC6MFY5n-M_}/_ssgManifest.js +0 -0
|
@@ -2,146 +2,61 @@ import {
|
|
|
2
2
|
runMonthDigestPipeline, readSettings,
|
|
3
3
|
readMonthDigest, getCurrentMonthDigestFromCache,
|
|
4
4
|
} from "@claude-lens/entries/node";
|
|
5
|
-
import type { PipelineEvent } from "@claude-lens/entries/node";
|
|
6
|
-
import { InflightCoalescer } from "@/lib/inflight-coalesce";
|
|
7
5
|
import {
|
|
8
6
|
isValidYearMonth, currentYearMonth, currentWeekMonday, todayLocal,
|
|
9
7
|
} from "@/lib/entries";
|
|
10
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
handleDigestGet, handleDigestPost, jsonResponse, jsonResponseBare,
|
|
10
|
+
} from "../../_shared/digest-route-helpers";
|
|
11
11
|
|
|
12
12
|
export const runtime = "nodejs";
|
|
13
13
|
export const dynamic = "force-dynamic";
|
|
14
14
|
|
|
15
15
|
type Params = { params: Promise<{ yearMonth: string }> };
|
|
16
16
|
|
|
17
|
-
const coalescer = new InflightCoalescer<string, void>();
|
|
18
|
-
|
|
19
|
-
function updateJobFromEvent(jobId: string, ev: PipelineEvent): void {
|
|
20
|
-
if (ev.type === "status") {
|
|
21
|
-
updateJob(jobId, { progress: { phase: ev.phase, text: ev.text } });
|
|
22
|
-
} else if (ev.type === "entry") {
|
|
23
|
-
updateJob(jobId, { progress: { phase: "enrich", index: ev.index, total: ev.total } });
|
|
24
|
-
} else if (ev.type === "progress") {
|
|
25
|
-
updateJob(jobId, { progress: { phase: ev.phase, bytes: ev.bytes } });
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
17
|
export async function GET(_req: Request, ctx: Params) {
|
|
30
18
|
const { yearMonth } = await ctx.params;
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
if (cached) {
|
|
44
|
-
return new Response(JSON.stringify(cached), {
|
|
45
|
-
status: 200, headers: { "content-type": "application/json" },
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
|
-
return new Response(JSON.stringify({ pending: true, current: true }), {
|
|
49
|
-
status: 200, headers: { "content-type": "application/json" },
|
|
50
|
-
});
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const cached = readMonthDigest(yearMonth);
|
|
54
|
-
if (cached) {
|
|
55
|
-
return new Response(JSON.stringify(cached), {
|
|
56
|
-
status: 200, headers: { "content-type": "application/json" },
|
|
57
|
-
});
|
|
58
|
-
}
|
|
59
|
-
return new Response(JSON.stringify({ pending: true }), {
|
|
60
|
-
status: 200, headers: { "content-type": "application/json" },
|
|
19
|
+
const valid = isValidYearMonth(yearMonth);
|
|
20
|
+
const isFuture = valid && yearMonth > currentYearMonth();
|
|
21
|
+
return handleDigestGet({
|
|
22
|
+
key: valid ? yearMonth : null,
|
|
23
|
+
invalidResponse: jsonResponse({ error: "invalid year-month" }, 400),
|
|
24
|
+
isFuture,
|
|
25
|
+
futureResponse: jsonResponseBare({ error: "future month" }, 400),
|
|
26
|
+
isCurrent: valid && yearMonth === currentYearMonth(),
|
|
27
|
+
readCurrentCache: () => getCurrentMonthDigestFromCache(yearMonth, Date.now()),
|
|
28
|
+
readPersisted: () => readMonthDigest(yearMonth),
|
|
29
|
+
pendingCurrentPayload: { pending: true, current: true },
|
|
30
|
+
currentFallsBackToPersisted: false,
|
|
61
31
|
});
|
|
62
32
|
}
|
|
63
33
|
|
|
64
34
|
export async function POST(req: Request, ctx: Params) {
|
|
65
35
|
const { yearMonth } = await ctx.params;
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
}
|
|
69
|
-
if (yearMonth > currentYearMonth()) {
|
|
70
|
-
return new Response(JSON.stringify({ error: "future month" }), { status: 400 });
|
|
71
|
-
}
|
|
72
|
-
|
|
36
|
+
const valid = isValidYearMonth(yearMonth);
|
|
37
|
+
const isFuture = valid && yearMonth > currentYearMonth();
|
|
73
38
|
const url = new URL(req.url);
|
|
74
39
|
const force = url.searchParams.get("force") === "1";
|
|
75
|
-
const key = `month|${yearMonth}|${force ? 1 : 0}`;
|
|
76
|
-
|
|
77
|
-
const encoder = new TextEncoder();
|
|
78
40
|
const settings = readSettings();
|
|
79
41
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
let jobId: string | null = null;
|
|
100
|
-
|
|
101
|
-
try {
|
|
102
|
-
await coalescer.run(key, async () => {
|
|
103
|
-
if (alreadyInflight) return;
|
|
104
|
-
jobId = registerJob({
|
|
105
|
-
kind: "monthly.synth",
|
|
106
|
-
label: `Month digest · ${yearMonth}`,
|
|
107
|
-
target: yearMonth,
|
|
108
|
-
caller: "user",
|
|
109
|
-
});
|
|
110
|
-
updateJob(jobId, { status: "running" });
|
|
111
|
-
for await (const ev of runMonthDigestPipeline(yearMonth, {
|
|
112
|
-
settings: settings.ai_features,
|
|
113
|
-
force,
|
|
114
|
-
currentYearMonth: currentYearMonth(),
|
|
115
|
-
currentWeekMonday: currentWeekMonday(),
|
|
116
|
-
todayLocalDay: todayLocal(),
|
|
117
|
-
})) {
|
|
118
|
-
send(ev);
|
|
119
|
-
if (jobId) updateJobFromEvent(jobId, ev);
|
|
120
|
-
}
|
|
121
|
-
if (jobId) completeJob(jobId, `/insights/month-${yearMonth}`);
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
if (alreadyInflight) {
|
|
125
|
-
const d = readMonthDigest(yearMonth) ?? getCurrentMonthDigestFromCache(yearMonth, Date.now());
|
|
126
|
-
if (d) send({ type: "digest", digest: d });
|
|
127
|
-
send({ type: "status", phase: "persist", text: "coalesced with in-flight request" });
|
|
128
|
-
}
|
|
129
|
-
} catch (err) {
|
|
130
|
-
send({ type: "error", message: (err as Error).message });
|
|
131
|
-
if (jobId) failJob(jobId, (err as Error).message);
|
|
132
|
-
} finally {
|
|
133
|
-
try { controller.enqueue(encoder.encode(`event: done\ndata: {}\n\n`)); } catch { /* ignore */ }
|
|
134
|
-
finish();
|
|
135
|
-
}
|
|
136
|
-
},
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
return new Response(stream, {
|
|
140
|
-
headers: {
|
|
141
|
-
"Content-Type": "text/event-stream",
|
|
142
|
-
"Cache-Control": "no-cache, no-transform",
|
|
143
|
-
Connection: "keep-alive",
|
|
144
|
-
"X-Accel-Buffering": "no",
|
|
145
|
-
},
|
|
42
|
+
return handleDigestPost({
|
|
43
|
+
key: valid ? yearMonth : null,
|
|
44
|
+
invalidResponse: jsonResponseBare({ error: "invalid year-month" }, 400),
|
|
45
|
+
isFuture,
|
|
46
|
+
futureResponse: jsonResponseBare({ error: "future month" }, 400),
|
|
47
|
+
coalesceScope: "month",
|
|
48
|
+
force,
|
|
49
|
+
jobKind: "monthly.synth",
|
|
50
|
+
jobLabel: `Month digest · ${yearMonth}`,
|
|
51
|
+
resultUrl: `/insights/month-${yearMonth}`,
|
|
52
|
+
runPipeline: () => runMonthDigestPipeline(yearMonth, {
|
|
53
|
+
settings: settings.ai_features,
|
|
54
|
+
force,
|
|
55
|
+
currentYearMonth: currentYearMonth(),
|
|
56
|
+
currentWeekMonday: currentWeekMonday(),
|
|
57
|
+
todayLocalDay: todayLocal(),
|
|
58
|
+
}),
|
|
59
|
+
readDigestForCoalescedRequest: () =>
|
|
60
|
+
readMonthDigest(yearMonth) ?? getCurrentMonthDigestFromCache(yearMonth, Date.now()),
|
|
146
61
|
});
|
|
147
62
|
}
|
|
@@ -2,149 +2,62 @@ import {
|
|
|
2
2
|
runWeekDigestPipeline, readSettings,
|
|
3
3
|
readWeekDigest, getCurrentWeekDigestFromCache,
|
|
4
4
|
} from "@claude-lens/entries/node";
|
|
5
|
-
import type { PipelineEvent } from "@claude-lens/entries/node";
|
|
6
|
-
import { InflightCoalescer } from "@/lib/inflight-coalesce";
|
|
7
5
|
import { asMonday, currentWeekMonday, todayLocal } from "@/lib/entries";
|
|
8
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
handleDigestGet, handleDigestPost, jsonResponse, jsonResponseBare,
|
|
8
|
+
} from "../../_shared/digest-route-helpers";
|
|
9
9
|
|
|
10
10
|
export const runtime = "nodejs";
|
|
11
11
|
export const dynamic = "force-dynamic";
|
|
12
12
|
|
|
13
13
|
type Params = { params: Promise<{ startDate: string }> };
|
|
14
14
|
|
|
15
|
-
const coalescer = new InflightCoalescer<string, void>();
|
|
16
|
-
|
|
17
|
-
function updateJobFromEvent(jobId: string, ev: PipelineEvent): void {
|
|
18
|
-
if (ev.type === "status") {
|
|
19
|
-
updateJob(jobId, { progress: { phase: ev.phase, text: ev.text } });
|
|
20
|
-
} else if (ev.type === "entry") {
|
|
21
|
-
updateJob(jobId, { progress: { phase: "enrich", index: ev.index, total: ev.total } });
|
|
22
|
-
} else if (ev.type === "progress") {
|
|
23
|
-
updateJob(jobId, { progress: { phase: ev.phase, bytes: ev.bytes } });
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
15
|
export async function GET(_req: Request, ctx: Params) {
|
|
28
16
|
const { startDate } = await ctx.params;
|
|
29
17
|
const monday = asMonday(startDate);
|
|
30
|
-
if (!monday) {
|
|
31
|
-
return new Response(JSON.stringify({ error: "invalid Monday date" }), {
|
|
32
|
-
status: 400, headers: { "content-type": "application/json" },
|
|
33
|
-
});
|
|
34
|
-
}
|
|
35
18
|
const today = todayLocal();
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
}
|
|
49
|
-
return new Response(JSON.stringify({ pending: true, current: true }), {
|
|
50
|
-
status: 200, headers: { "content-type": "application/json" },
|
|
51
|
-
});
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
const cached = readWeekDigest(monday);
|
|
55
|
-
if (cached) {
|
|
56
|
-
return new Response(JSON.stringify(cached), {
|
|
57
|
-
status: 200, headers: { "content-type": "application/json" },
|
|
58
|
-
});
|
|
59
|
-
}
|
|
60
|
-
return new Response(JSON.stringify({ pending: true }), {
|
|
61
|
-
status: 200, headers: { "content-type": "application/json" },
|
|
19
|
+
const isFuture = monday !== null && monday > today;
|
|
20
|
+
return handleDigestGet({
|
|
21
|
+
key: monday,
|
|
22
|
+
invalidResponse: jsonResponse({ error: "invalid Monday date" }, 400),
|
|
23
|
+
isFuture,
|
|
24
|
+
futureResponse: jsonResponseBare({ error: "future date" }, 400),
|
|
25
|
+
isCurrent: monday !== null && monday === currentWeekMonday(),
|
|
26
|
+
readCurrentCache: () =>
|
|
27
|
+
monday !== null ? getCurrentWeekDigestFromCache(monday, Date.now()) : null,
|
|
28
|
+
readPersisted: () => (monday !== null ? readWeekDigest(monday) : null),
|
|
29
|
+
pendingCurrentPayload: { pending: true, current: true },
|
|
30
|
+
currentFallsBackToPersisted: true,
|
|
62
31
|
});
|
|
63
32
|
}
|
|
64
33
|
|
|
65
34
|
export async function POST(req: Request, ctx: Params) {
|
|
66
35
|
const { startDate } = await ctx.params;
|
|
67
36
|
const monday = asMonday(startDate);
|
|
68
|
-
|
|
69
|
-
return new Response(JSON.stringify({ error: "invalid Monday date" }), { status: 400 });
|
|
70
|
-
}
|
|
71
|
-
if (monday > todayLocal()) {
|
|
72
|
-
return new Response(JSON.stringify({ error: "future date" }), { status: 400 });
|
|
73
|
-
}
|
|
74
|
-
|
|
37
|
+
const isFuture = monday !== null && monday > todayLocal();
|
|
75
38
|
const url = new URL(req.url);
|
|
76
39
|
const force = url.searchParams.get("force") === "1";
|
|
77
|
-
const key = `week|${monday}|${force ? 1 : 0}`;
|
|
78
|
-
|
|
79
|
-
const encoder = new TextEncoder();
|
|
80
40
|
const settings = readSettings();
|
|
81
41
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
const alreadyInflight = coalescer.inflight(key);
|
|
103
|
-
let jobId: string | null = null;
|
|
104
|
-
|
|
105
|
-
try {
|
|
106
|
-
await coalescer.run(key, async () => {
|
|
107
|
-
if (alreadyInflight) return;
|
|
108
|
-
jobId = registerJob({
|
|
109
|
-
kind: "weekly.synth",
|
|
110
|
-
label: `Week digest · ${monday}`,
|
|
111
|
-
target: monday,
|
|
112
|
-
caller: "user",
|
|
113
|
-
});
|
|
114
|
-
updateJob(jobId, { status: "running" });
|
|
115
|
-
for await (const ev of runWeekDigestPipeline(monday, {
|
|
116
|
-
settings: settings.ai_features,
|
|
117
|
-
force,
|
|
118
|
-
currentWeekMonday: currentWeekMonday(),
|
|
119
|
-
todayLocalDay: todayLocal(),
|
|
120
|
-
})) {
|
|
121
|
-
send(ev);
|
|
122
|
-
if (jobId) updateJobFromEvent(jobId, ev);
|
|
123
|
-
}
|
|
124
|
-
if (jobId) completeJob(jobId, `/insights/week-${monday}`);
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
if (alreadyInflight) {
|
|
128
|
-
const d = readWeekDigest(monday) ?? getCurrentWeekDigestFromCache(monday, Date.now());
|
|
129
|
-
if (d) send({ type: "digest", digest: d });
|
|
130
|
-
send({ type: "status", phase: "persist", text: "coalesced with in-flight request" });
|
|
131
|
-
}
|
|
132
|
-
} catch (err) {
|
|
133
|
-
send({ type: "error", message: (err as Error).message });
|
|
134
|
-
if (jobId) failJob(jobId, (err as Error).message);
|
|
135
|
-
} finally {
|
|
136
|
-
try { controller.enqueue(encoder.encode(`event: done\ndata: {}\n\n`)); } catch { /* ignore */ }
|
|
137
|
-
finish();
|
|
138
|
-
}
|
|
139
|
-
},
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
return new Response(stream, {
|
|
143
|
-
headers: {
|
|
144
|
-
"Content-Type": "text/event-stream",
|
|
145
|
-
"Cache-Control": "no-cache, no-transform",
|
|
146
|
-
Connection: "keep-alive",
|
|
147
|
-
"X-Accel-Buffering": "no",
|
|
148
|
-
},
|
|
42
|
+
return handleDigestPost({
|
|
43
|
+
key: monday,
|
|
44
|
+
invalidResponse: jsonResponseBare({ error: "invalid Monday date" }, 400),
|
|
45
|
+
isFuture,
|
|
46
|
+
futureResponse: jsonResponseBare({ error: "future date" }, 400),
|
|
47
|
+
coalesceScope: "week",
|
|
48
|
+
force,
|
|
49
|
+
jobKind: "weekly.synth",
|
|
50
|
+
jobLabel: `Week digest · ${monday}`,
|
|
51
|
+
resultUrl: `/insights/week-${monday}`,
|
|
52
|
+
runPipeline: () => runWeekDigestPipeline(monday as string, {
|
|
53
|
+
settings: settings.ai_features,
|
|
54
|
+
force,
|
|
55
|
+
currentWeekMonday: currentWeekMonday(),
|
|
56
|
+
todayLocalDay: todayLocal(),
|
|
57
|
+
}),
|
|
58
|
+
readDigestForCoalescedRequest: () =>
|
|
59
|
+
monday !== null
|
|
60
|
+
? (readWeekDigest(monday) ?? getCurrentWeekDigestFromCache(monday, Date.now()))
|
|
61
|
+
: null,
|
|
149
62
|
});
|
|
150
63
|
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import Link from "next/link";
|
|
2
2
|
import { readSettings, readWeekDigest, interactiveLockFresh } from "@claude-lens/entries/node";
|
|
3
|
-
import {
|
|
3
|
+
import { listEntryKeys } from "@claude-lens/entries/fs";
|
|
4
|
+
import { parseEntryKey } from "@claude-lens/entries";
|
|
5
|
+
import { lastCompletedWeekMonday, currentWeekMonday } from "@/lib/entries";
|
|
4
6
|
import { WeekDigestView } from "@/components/week-digest-view";
|
|
5
7
|
import { InsightsTopBar } from "@/components/insights-top-bar";
|
|
6
8
|
|
|
@@ -24,9 +26,21 @@ function weekDates(monday: string): string[] {
|
|
|
24
26
|
return out;
|
|
25
27
|
}
|
|
26
28
|
|
|
27
|
-
|
|
29
|
+
// Build once from the entries dir, then test the 7 weekdays against it.
|
|
30
|
+
// Earlier this called listEntriesForDay per day per render, which opened
|
|
31
|
+
// and parsed every matching entry file 14× before deciding the week is empty.
|
|
32
|
+
function presentDays(): Set<string> {
|
|
33
|
+
const out = new Set<string>();
|
|
34
|
+
for (const key of listEntryKeys()) {
|
|
35
|
+
const parsed = parseEntryKey(key);
|
|
36
|
+
if (parsed) out.add(parsed.local_day);
|
|
37
|
+
}
|
|
38
|
+
return out;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function hasAnyEntries(monday: string, present: Set<string>): boolean {
|
|
28
42
|
for (const d of weekDates(monday)) {
|
|
29
|
-
if (
|
|
43
|
+
if (present.has(d)) return true;
|
|
30
44
|
}
|
|
31
45
|
return false;
|
|
32
46
|
}
|
|
@@ -45,7 +59,8 @@ export default async function InsightsPage() {
|
|
|
45
59
|
const lastWeek = lastCompletedWeekMonday();
|
|
46
60
|
const cached = readWeekDigest(lastWeek);
|
|
47
61
|
const prior = readWeekDigest(priorMonday(lastWeek));
|
|
48
|
-
const
|
|
62
|
+
const present = presentDays();
|
|
63
|
+
const hasData = !!cached || hasAnyEntries(lastWeek, present);
|
|
49
64
|
|
|
50
65
|
// Auto-fire when AI is on and the digest isn't already cached. The interactive
|
|
51
66
|
// pipeline lock (heartbeat-refreshed) suppresses concurrent fires from the
|
|
@@ -58,7 +73,7 @@ export default async function InsightsPage() {
|
|
|
58
73
|
const prevMonday = priorMonday(lastWeek);
|
|
59
74
|
const currentMonday = currentWeekMonday();
|
|
60
75
|
const nextMonday = currentMonday > lastWeek ? currentMonday : null;
|
|
61
|
-
const nextHasData = nextMonday ? hasAnyEntries(nextMonday) : false;
|
|
76
|
+
const nextHasData = nextMonday ? hasAnyEntries(nextMonday, present) : false;
|
|
62
77
|
|
|
63
78
|
return (
|
|
64
79
|
<div style={{ paddingBottom: 64 }}>
|