fleetlens 0.13.2 → 0.15.0
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/app-path-routes-manifest.json +4 -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/routes-manifest.json +21 -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 +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 +3 -3
- 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 +4 -4
- 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 +4 -4
- 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 +2 -2
- 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 +4 -4
- 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 +2 -2
- 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 +4 -4
- 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/settings/route.js +4 -3
- package/app/apps/web/.next/server/app/api/settings/route.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/api/team/autostart/route/app-paths-manifest.json +3 -0
- package/app/apps/web/.next/server/app/api/team/autostart/route.js +7 -0
- package/app/apps/web/.next/server/app/api/team/autostart/route.js.nft.json +1 -0
- package/app/apps/web/.next/server/app/api/team/autostart/route_client-reference-manifest.js +3 -0
- package/app/apps/web/.next/server/app/api/team/onboarding/start/route/app-paths-manifest.json +3 -0
- package/app/apps/web/.next/server/app/api/team/onboarding/start/route/build-manifest.json +9 -0
- package/app/apps/web/.next/server/app/api/team/onboarding/start/route/server-reference-manifest.json +4 -0
- package/app/apps/web/.next/server/app/api/team/onboarding/start/route.js +8 -0
- package/app/apps/web/.next/server/app/api/team/onboarding/start/route.js.map +5 -0
- package/app/apps/web/.next/server/app/api/team/onboarding/start/route.js.nft.json +1 -0
- package/app/apps/web/.next/server/app/api/team/onboarding/start/route_client-reference-manifest.js +3 -0
- package/app/apps/web/.next/server/app/api/team/sync-projects/route/app-paths-manifest.json +3 -0
- package/app/apps/web/.next/server/app/api/team/sync-projects/route/build-manifest.json +9 -0
- package/app/apps/web/.next/server/app/api/team/sync-projects/route/server-reference-manifest.json +4 -0
- package/app/apps/web/.next/server/app/api/team/sync-projects/route.js +8 -0
- package/app/apps/web/.next/server/app/api/team/sync-projects/route.js.map +5 -0
- package/app/apps/web/.next/server/app/api/team/sync-projects/route.js.nft.json +1 -0
- package/app/apps/web/.next/server/app/api/team/sync-projects/route_client-reference-manifest.js +3 -0
- 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.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 +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 +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 +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 +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 +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 +2 -2
- 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 +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 +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 +2 -2
- 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 +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 +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 +3 -3
- 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 +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 +2 -2
- 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/app-paths-manifest.json +3 -0
- package/app/apps/web/.next/server/app/team/onboarding/page/build-manifest.json +17 -0
- package/app/apps/web/.next/server/app/team/onboarding/page/next-font-manifest.json +6 -0
- package/app/apps/web/.next/server/app/team/onboarding/page/react-loadable-manifest.json +1 -0
- package/app/apps/web/.next/server/app/team/onboarding/page/server-reference-manifest.json +4 -0
- package/app/apps/web/.next/server/app/team/onboarding/page.js +19 -0
- package/app/apps/web/.next/server/app/team/onboarding/page.js.map +5 -0
- package/app/apps/web/.next/server/app/team/onboarding/page.js.nft.json +1 -0
- package/app/apps/web/.next/server/app/team/onboarding/page_client-reference-manifest.js +3 -0
- package/app/apps/web/.next/server/app/team/page.js +3 -2
- 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 +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/app-paths-manifest.json +4 -1
- package/app/apps/web/.next/server/chunks/02dz_web__next-internal_server_app_api_team_onboarding_start_route_actions_0zeptl_.js +3 -0
- package/app/apps/web/.next/server/chunks/0fuv_zod_v4_classic_external_0.z.vap.js +39 -0
- package/app/apps/web/.next/server/chunks/[root-of-the-server]__038-.1r._.js +6 -0
- package/app/apps/web/.next/server/chunks/{[root-of-the-server]__0~utv~v._.js → [root-of-the-server]__03ewxxu._.js} +2 -2
- package/app/apps/web/.next/server/chunks/[root-of-the-server]__03rmbz6._.js +3 -0
- package/app/apps/web/.next/server/chunks/[root-of-the-server]__065lib8._.js +3 -0
- package/app/apps/web/.next/server/chunks/{[root-of-the-server]__0e.f-t.._.js → [root-of-the-server]__0a0~7d6._.js} +2 -2
- package/app/apps/web/.next/server/chunks/{[root-of-the-server]__0gril0u._.js → [root-of-the-server]__0acikq.._.js} +2 -2
- package/app/apps/web/.next/server/chunks/[root-of-the-server]__0et2sqb._.js +3 -0
- package/app/apps/web/.next/server/chunks/{[externals]__09v47rg._.js → [root-of-the-server]__0jbc7tz._.js} +2 -2
- package/app/apps/web/.next/server/chunks/[root-of-the-server]__0mq0or2._.js +3 -0
- package/app/apps/web/.next/server/chunks/[root-of-the-server]__0mvl0qn._.js +3 -0
- package/app/apps/web/.next/server/chunks/{[root-of-the-server]__0n79hpm._.js → [root-of-the-server]__0ow1kn1._.js} +4 -4
- package/app/apps/web/.next/server/chunks/[root-of-the-server]__0~xt99s._.js +3 -0
- package/app/apps/web/.next/server/chunks/apps_web__next-internal_server_app_api_team_autostart_route_actions_10-4s71.js +3 -0
- package/app/apps/web/.next/server/chunks/apps_web__next-internal_server_app_api_team_sync-projects_route_actions_0r_2f5n.js +3 -0
- package/app/apps/web/.next/server/chunks/node_modules_next_036my~r._.js +13 -0
- package/app/apps/web/.next/server/chunks/packages_09wmnv.._.js +3 -0
- package/app/apps/web/.next/server/chunks/packages_0o6.gpk._.js +3 -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]__0z7mg0b._.js → [root-of-the-server]__08l86e-._.js} +2 -2
- package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0a7n44-._.js +3 -0
- package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0c00i76._.js +3 -0
- package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0i.q-1h._.js +3 -0
- package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0r4tks8._.js +1 -1
- package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0stu~~r._.js +3 -0
- package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0ufi3.w._.js +3 -0
- package/app/apps/web/.next/server/chunks/ssr/{[root-of-the-server]__0o03ymy._.js → [root-of-the-server]__10~b4bf._.js} +2 -2
- package/app/apps/web/.next/server/chunks/ssr/_0xu2pvc._.js +1 -1
- package/app/apps/web/.next/server/chunks/ssr/apps_web_0.97l5.._.js +3 -0
- package/app/apps/web/.next/server/chunks/ssr/{apps_web_app_settings_0u6z6rj._.js → apps_web_0q2x4a~._.js} +2 -2
- package/app/apps/web/.next/server/chunks/ssr/apps_web_114yo0v._.js +3 -0
- package/app/apps/web/.next/server/chunks/ssr/apps_web__next-internal_server_app_team_onboarding_page_actions_0pss~.k.js +3 -0
- package/app/apps/web/.next/server/chunks/ssr/apps_web_components_0c-tpft._.js +1 -1
- package/app/apps/web/.next/server/chunks/ssr/apps_web_components_0txtsv5._.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/apps_web_lib_team-config_ts_10egg08._.js +39 -0
- package/app/apps/web/.next/server/chunks/ssr/{node_modules_next_dist_0t-hj0x._.js → node_modules_0yrkyef._.js} +2 -2
- package/app/apps/web/.next/server/chunks/ssr/node_modules_next_0w2y7fz._.js +2 -2
- package/app/apps/web/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_0jxvcfi.js +2 -2
- package/app/apps/web/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_0s5yhs4.js +4 -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/functions-config-manifest.json +3 -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/{0wwcx42ng8i2e.js → 0-3_u.90d79wi.js} +1 -1
- package/app/apps/web/.next/static/chunks/00a9oyw~h5i1p.js +1 -0
- package/app/apps/web/.next/static/chunks/040s42ypidk7s.css +1 -0
- package/app/apps/web/.next/static/chunks/06eiwghjkkbnh.js +1 -0
- package/app/apps/web/.next/static/chunks/06qeg4pt5jdqs.js +1 -0
- package/app/apps/web/.next/static/chunks/{0e_fzy0n8ij-o.js → 0u6gxpqhl0gc7.js} +1 -1
- package/app/apps/web/.next/static/chunks/18cj08-la37o-.js +1 -0
- package/app/apps/web/app/api/team/autostart/route.ts +41 -0
- package/app/apps/web/app/api/team/onboarding/start/route.ts +96 -0
- package/app/apps/web/app/api/team/sync-projects/route.ts +39 -0
- package/app/apps/web/app/globals.css +82 -0
- package/app/apps/web/app/settings/ai-features-form.tsx +1 -1
- package/app/apps/web/app/settings/page.tsx +21 -7
- package/app/apps/web/app/{settings → team}/force-sync-button.tsx +2 -4
- package/app/apps/web/app/team/onboarding/onboarding-wizard.tsx +503 -0
- package/app/apps/web/app/team/onboarding/page.tsx +28 -0
- package/app/apps/web/app/team/page.tsx +60 -81
- package/app/apps/web/app/{settings/team-connection-section.tsx → team/sync-activity-section.tsx} +6 -23
- package/app/apps/web/app/team/synced-projects-form.tsx +201 -0
- package/app/apps/web/app/team/synced-projects-section.tsx +37 -0
- package/app/apps/web/components/autostart-toggle.tsx +59 -0
- package/app/apps/web/components/previous-cycles-trend.tsx +4 -1
- package/app/apps/web/components/project-sync-picker.tsx +109 -0
- package/app/apps/web/components/team-chip.tsx +1 -1
- package/app/apps/web/components/team-welcome-banner.tsx +1 -1
- package/app/apps/web/lib/autostart-data.ts +31 -0
- package/app/apps/web/lib/changelog-data.generated.ts +1 -1
- package/app/apps/web/lib/sync-projects-data.ts +36 -0
- package/app/apps/web/lib/team-config.ts +21 -4
- package/app/apps/web/package.json +1 -1
- package/app/apps/web/tsconfig.tsbuildinfo +1 -1
- package/dist/daemon-worker.js +2089 -1622
- package/dist/index.js +1462 -230
- package/package.json +1 -1
- package/app/apps/web/.next/server/app/api/team-config/route/app-paths-manifest.json +0 -3
- package/app/apps/web/.next/server/app/api/team-config/route.js +0 -8
- package/app/apps/web/.next/server/app/api/team-config/route.js.nft.json +0 -1
- package/app/apps/web/.next/server/app/api/team-config/route_client-reference-manifest.js +0 -3
- package/app/apps/web/.next/server/chunks/[root-of-the-server]__08agijy._.js +0 -3
- package/app/apps/web/.next/server/chunks/[root-of-the-server]__0atn.dt._.js +0 -3
- package/app/apps/web/.next/server/chunks/[root-of-the-server]__0b.b7~1._.js +0 -3
- package/app/apps/web/.next/server/chunks/[root-of-the-server]__0i06ito._.js +0 -3
- package/app/apps/web/.next/server/chunks/[root-of-the-server]__0y5p73y._.js +0 -3
- package/app/apps/web/.next/server/chunks/_0euqdko._.js +0 -49
- package/app/apps/web/.next/server/chunks/apps_web__next-internal_server_app_api_team-config_route_actions_0ad2gak.js +0 -3
- package/app/apps/web/.next/server/chunks/packages_0c-sz9n._.js +0 -3
- package/app/apps/web/.next/server/chunks/packages_0wywuu.._.js +0 -3
- package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__09yxy53._.js +0 -3
- package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0exckd4._.js +0 -3
- package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0n-ax-5._.js +0 -3
- package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0q9qy0z._.js +0 -3
- package/app/apps/web/.next/server/chunks/ssr/apps_web_app_team_team-sync-form_tsx_0choz0h._.js +0 -3
- package/app/apps/web/.next/static/chunks/083~hzkv577.5.css +0 -1
- package/app/apps/web/.next/static/chunks/0h.ua.-myj.6z.js +0 -1
- package/app/apps/web/.next/static/chunks/0uyctl-m8iydb.js +0 -1
- package/app/apps/web/.next/static/chunks/14joqbsr9ihhu.js +0 -1
- package/app/apps/web/app/api/team-config/route.ts +0 -39
- package/app/apps/web/app/team/team-sync-form.tsx +0 -144
- /package/app/apps/web/.next/server/app/api/{team-config → team/autostart}/route/build-manifest.json +0 -0
- /package/app/apps/web/.next/server/app/api/{team-config → team/autostart}/route/server-reference-manifest.json +0 -0
- /package/app/apps/web/.next/server/app/api/{team-config → team/autostart}/route.js.map +0 -0
- /package/app/apps/web/.next/static/{bZs5UyzsYBqC6MFY5n-M_ → C4y6rare_ZCcXXQLc8ysA}/_buildManifest.js +0 -0
- /package/app/apps/web/.next/static/{bZs5UyzsYBqC6MFY5n-M_ → C4y6rare_ZCcXXQLc8ysA}/_clientMiddlewareManifest.js +0 -0
- /package/app/apps/web/.next/static/{bZs5UyzsYBqC6MFY5n-M_ → C4y6rare_ZCcXXQLc8ysA}/_ssgManifest.js +0 -0
package/dist/index.js
CHANGED
|
@@ -4002,13 +4002,138 @@ async function readJsonl2(filePath) {
|
|
|
4002
4002
|
}
|
|
4003
4003
|
return out;
|
|
4004
4004
|
}
|
|
4005
|
+
function dedupeReplayedLines(lines) {
|
|
4006
|
+
const keepAt = /* @__PURE__ */ new Map();
|
|
4007
|
+
lines.forEach((line, i) => {
|
|
4008
|
+
const rec = line;
|
|
4009
|
+
if (typeof rec.uuid !== "string")
|
|
4010
|
+
return;
|
|
4011
|
+
const prev = keepAt.get(rec.uuid);
|
|
4012
|
+
if (prev === void 0) {
|
|
4013
|
+
keepAt.set(rec.uuid, i);
|
|
4014
|
+
return;
|
|
4015
|
+
}
|
|
4016
|
+
const prevHasTs = lines[prev].timestamp !== void 0;
|
|
4017
|
+
if (!prevHasTs && rec.timestamp !== void 0)
|
|
4018
|
+
keepAt.set(rec.uuid, i);
|
|
4019
|
+
});
|
|
4020
|
+
return lines.filter((line, i) => {
|
|
4021
|
+
const rec = line;
|
|
4022
|
+
return typeof rec.uuid !== "string" || keepAt.get(rec.uuid) === i;
|
|
4023
|
+
});
|
|
4024
|
+
}
|
|
4025
|
+
function textFromContent(content) {
|
|
4026
|
+
if (typeof content === "string")
|
|
4027
|
+
return content;
|
|
4028
|
+
if (Array.isArray(content)) {
|
|
4029
|
+
const parts = [];
|
|
4030
|
+
for (const b of content) {
|
|
4031
|
+
if (b && typeof b === "object" && b.type === "text") {
|
|
4032
|
+
const t = b.text;
|
|
4033
|
+
if (typeof t === "string")
|
|
4034
|
+
parts.push(t);
|
|
4035
|
+
}
|
|
4036
|
+
}
|
|
4037
|
+
if (parts.length)
|
|
4038
|
+
return parts.join("\n");
|
|
4039
|
+
}
|
|
4040
|
+
return void 0;
|
|
4041
|
+
}
|
|
4042
|
+
function partitionCoworkLines(lines) {
|
|
4043
|
+
const mainLines = [];
|
|
4044
|
+
const subagentGroups = /* @__PURE__ */ new Map();
|
|
4045
|
+
for (const line of lines) {
|
|
4046
|
+
const ptid = line.parent_tool_use_id;
|
|
4047
|
+
if (typeof ptid === "string") {
|
|
4048
|
+
const g = subagentGroups.get(ptid);
|
|
4049
|
+
if (g)
|
|
4050
|
+
g.push(line);
|
|
4051
|
+
else
|
|
4052
|
+
subagentGroups.set(ptid, [line]);
|
|
4053
|
+
} else {
|
|
4054
|
+
mainLines.push(line);
|
|
4055
|
+
}
|
|
4056
|
+
}
|
|
4057
|
+
return { mainLines, subagentGroups };
|
|
4058
|
+
}
|
|
4059
|
+
function buildCoworkSubagents(subagentGroups, allLines, sessionStartMs) {
|
|
4060
|
+
if (subagentGroups.size === 0)
|
|
4061
|
+
return [];
|
|
4062
|
+
const refs = /* @__PURE__ */ new Map();
|
|
4063
|
+
for (const line of allLines) {
|
|
4064
|
+
const rec = line;
|
|
4065
|
+
const content = rec.message?.content;
|
|
4066
|
+
if (!Array.isArray(content))
|
|
4067
|
+
continue;
|
|
4068
|
+
for (const b of content) {
|
|
4069
|
+
if (!b || typeof b !== "object")
|
|
4070
|
+
continue;
|
|
4071
|
+
const block = b;
|
|
4072
|
+
if (block.type === "tool_use" && block.name === "Agent" && typeof block.id === "string") {
|
|
4073
|
+
const input = block.input ?? {};
|
|
4074
|
+
const ref = refs.get(block.id) ?? {};
|
|
4075
|
+
if (typeof rec.uuid === "string")
|
|
4076
|
+
ref.parentUuid = rec.uuid;
|
|
4077
|
+
if (typeof input.description === "string")
|
|
4078
|
+
ref.description = input.description;
|
|
4079
|
+
if (typeof input.subagent_type === "string")
|
|
4080
|
+
ref.agentType = input.subagent_type;
|
|
4081
|
+
if (typeof input.prompt === "string")
|
|
4082
|
+
ref.prompt = input.prompt;
|
|
4083
|
+
refs.set(block.id, ref);
|
|
4084
|
+
} else if (block.type === "tool_result" && typeof block.tool_use_id === "string" && subagentGroups.has(block.tool_use_id)) {
|
|
4085
|
+
const ref = refs.get(block.tool_use_id) ?? {};
|
|
4086
|
+
ref.finalText = textFromContent(block.content) ?? ref.finalText;
|
|
4087
|
+
refs.set(block.tool_use_id, ref);
|
|
4088
|
+
}
|
|
4089
|
+
}
|
|
4090
|
+
}
|
|
4091
|
+
const runs2 = [];
|
|
4092
|
+
for (const [toolUseId, groupLines] of subagentGroups) {
|
|
4093
|
+
const summary = summarizeSubagentLines(groupLines);
|
|
4094
|
+
const ref = refs.get(toolUseId) ?? {};
|
|
4095
|
+
const finalText = ref.finalText ?? summary.finalText;
|
|
4096
|
+
const finalPreview = finalText ? finalText.replace(/\s+/g, " ").trim().slice(0, 240) : summary.finalPreview;
|
|
4097
|
+
const startTOffsetMs = sessionStartMs !== void 0 && summary.startMs !== void 0 ? Math.max(0, summary.startMs - sessionStartMs) : void 0;
|
|
4098
|
+
const endTOffsetMs = sessionStartMs !== void 0 && summary.endMs !== void 0 ? Math.max(0, summary.endMs - sessionStartMs) : void 0;
|
|
4099
|
+
const durationMs = summary.startMs !== void 0 && summary.endMs !== void 0 ? summary.endMs - summary.startMs : void 0;
|
|
4100
|
+
runs2.push({
|
|
4101
|
+
agentId: toolUseId,
|
|
4102
|
+
agentType: ref.agentType ?? "unknown",
|
|
4103
|
+
description: ref.description ?? (ref.prompt ? ref.prompt.replace(/\s+/g, " ").trim().slice(0, 80) : "(no description)"),
|
|
4104
|
+
startMs: summary.startMs,
|
|
4105
|
+
endMs: summary.endMs,
|
|
4106
|
+
durationMs,
|
|
4107
|
+
startTOffsetMs,
|
|
4108
|
+
endTOffsetMs,
|
|
4109
|
+
eventCount: summary.eventCount,
|
|
4110
|
+
totalUsage: summary.totalUsage,
|
|
4111
|
+
parentUuid: ref.parentUuid,
|
|
4112
|
+
parentToolUseId: toolUseId,
|
|
4113
|
+
runInBackground: false,
|
|
4114
|
+
finalPreview,
|
|
4115
|
+
finalText,
|
|
4116
|
+
prompt: ref.prompt,
|
|
4117
|
+
model: summary.model,
|
|
4118
|
+
toolCalls: summary.toolCalls,
|
|
4119
|
+
toolCallCount: summary.toolCallCount,
|
|
4120
|
+
assistantMessageCount: summary.assistantMessageCount
|
|
4121
|
+
});
|
|
4122
|
+
}
|
|
4123
|
+
runs2.sort((a, b) => (a.startMs ?? 0) - (b.startMs ?? 0));
|
|
4124
|
+
return runs2;
|
|
4125
|
+
}
|
|
4005
4126
|
async function parseCoworkSession(file) {
|
|
4006
4127
|
const [lines, coworkMeta, spaceIdToPath] = await Promise.all([
|
|
4007
4128
|
readJsonl2(file.auditPath),
|
|
4008
4129
|
readCoworkMeta(file.metaPath),
|
|
4009
4130
|
loadSpacesForWorkspace(file.workspaceDir)
|
|
4010
4131
|
]);
|
|
4011
|
-
const
|
|
4132
|
+
const deduped = dedupeReplayedLines(lines);
|
|
4133
|
+
const { mainLines, subagentGroups } = partitionCoworkLines(deduped);
|
|
4134
|
+
const { meta: baseMeta, events } = parseTranscript(mainLines);
|
|
4135
|
+
const sessionStartMs = baseMeta.firstTimestamp ? Date.parse(baseMeta.firstTimestamp) : void 0;
|
|
4136
|
+
const subagents = buildCoworkSubagents(subagentGroups, deduped, sessionStartMs);
|
|
4012
4137
|
const { projectName, projectDir, cwd } = resolveProject(coworkMeta, spaceIdToPath);
|
|
4013
4138
|
const meta = {
|
|
4014
4139
|
...baseMeta,
|
|
@@ -4021,7 +4146,7 @@ async function parseCoworkSession(file) {
|
|
|
4021
4146
|
cwd: cwd ?? baseMeta.cwd,
|
|
4022
4147
|
model: baseMeta.model ?? coworkMeta.model
|
|
4023
4148
|
};
|
|
4024
|
-
return { meta, events };
|
|
4149
|
+
return { meta, events, subagents };
|
|
4025
4150
|
}
|
|
4026
4151
|
function cacheKeyFor(file) {
|
|
4027
4152
|
return {
|
|
@@ -4072,8 +4197,12 @@ async function getCoworkSession(id, opts = {}) {
|
|
|
4072
4197
|
const cached = detailCache5.get(file.auditPath);
|
|
4073
4198
|
if (cached && cacheKeyMatches(cached.key, key))
|
|
4074
4199
|
return cached.detail;
|
|
4075
|
-
const { meta, events } = await parseCoworkSession(file);
|
|
4076
|
-
const detail = {
|
|
4200
|
+
const { meta, events, subagents } = await parseCoworkSession(file);
|
|
4201
|
+
const detail = {
|
|
4202
|
+
...meta,
|
|
4203
|
+
events,
|
|
4204
|
+
...subagents.length ? { subagents } : {}
|
|
4205
|
+
};
|
|
4077
4206
|
detailCache5.set(file.auditPath, { detail, key });
|
|
4078
4207
|
return detail;
|
|
4079
4208
|
}
|
|
@@ -4082,6 +4211,7 @@ var init_cowork = __esm({
|
|
|
4082
4211
|
"../parser/dist/cowork.js"() {
|
|
4083
4212
|
"use strict";
|
|
4084
4213
|
init_analytics();
|
|
4214
|
+
init_claude_code();
|
|
4085
4215
|
init_parser();
|
|
4086
4216
|
DEFAULT_COWORK_ROOT = defaultCoworkRoot();
|
|
4087
4217
|
SESSION_ID_RE = /^local_[0-9a-f-]+$/;
|
|
@@ -4092,8 +4222,17 @@ var init_cowork = __esm({
|
|
|
4092
4222
|
});
|
|
4093
4223
|
|
|
4094
4224
|
// ../parser/dist/team-config.js
|
|
4095
|
-
import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, unlinkSync as unlinkSync2, mkdirSync as mkdirSync2 } from "node:fs";
|
|
4225
|
+
import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, unlinkSync as unlinkSync2, mkdirSync as mkdirSync2, renameSync } from "node:fs";
|
|
4096
4226
|
import { join } from "node:path";
|
|
4227
|
+
function shouldSyncProject(repoName, sp) {
|
|
4228
|
+
if (!sp)
|
|
4229
|
+
return true;
|
|
4230
|
+
if (sp.excluded.includes(repoName))
|
|
4231
|
+
return false;
|
|
4232
|
+
if (sp.included.includes(repoName))
|
|
4233
|
+
return true;
|
|
4234
|
+
return sp.autoIncludeNew;
|
|
4235
|
+
}
|
|
4097
4236
|
function readTeamConfig(dir) {
|
|
4098
4237
|
const d = dir ?? cclensHome();
|
|
4099
4238
|
try {
|
|
@@ -4105,7 +4244,9 @@ function readTeamConfig(dir) {
|
|
|
4105
4244
|
function writeTeamConfig(config, dir) {
|
|
4106
4245
|
const d = dir ?? cclensHome();
|
|
4107
4246
|
mkdirSync2(d, { recursive: true });
|
|
4108
|
-
|
|
4247
|
+
const tmp = join(d, `${CONFIG_FILE}.${process.pid}.tmp`);
|
|
4248
|
+
writeFileSync2(tmp, JSON.stringify(config, null, 2), { mode: 384 });
|
|
4249
|
+
renameSync(tmp, join(d, CONFIG_FILE));
|
|
4109
4250
|
}
|
|
4110
4251
|
function clearTeamConfig(dir) {
|
|
4111
4252
|
const d = dir ?? cclensHome();
|
|
@@ -4179,6 +4320,7 @@ __export(fs_exports, {
|
|
|
4179
4320
|
readJsonlFile: () => readJsonlFile,
|
|
4180
4321
|
readTeamConfig: () => readTeamConfig,
|
|
4181
4322
|
sessionIdFromFileName: () => sessionIdFromFileName,
|
|
4323
|
+
shouldSyncProject: () => shouldSyncProject,
|
|
4182
4324
|
walkJsonlFiles: () => walkJsonlFiles,
|
|
4183
4325
|
writeTeamConfig: () => writeTeamConfig
|
|
4184
4326
|
});
|
|
@@ -4400,9 +4542,14 @@ function buildCalibrationCurve(events, snapshots, tier = "pro-max-20x", granular
|
|
|
4400
4542
|
curve.push({
|
|
4401
4543
|
ts: new Date(cur).toISOString(),
|
|
4402
4544
|
real_5h: real5,
|
|
4403
|
-
|
|
4545
|
+
// Predicted utilization LEGITIMATELY exceeds 200 on overage (the
|
|
4546
|
+
// extrapolated cycle close). The old min(200) clamp silently capped it,
|
|
4547
|
+
// so cyclePeaks.peakPct could never carry a *predicted* overage to the
|
|
4548
|
+
// team server (which now accepts up to 10000). Keep only a generous
|
|
4549
|
+
// finite ceiling as a corruption guard, matching the wire cap.
|
|
4550
|
+
pred_5h: Math.max(0, Math.min(1e4, p5)),
|
|
4404
4551
|
real_7d: real7,
|
|
4405
|
-
pred_7d: Math.max(0, Math.min(
|
|
4552
|
+
pred_7d: Math.max(0, Math.min(1e4, p7)),
|
|
4406
4553
|
cycle_end_5h: r5k != null ? new Date(r5k).toISOString() : null,
|
|
4407
4554
|
cycle_end_7d: r7k != null ? new Date(r7k).toISOString() : null
|
|
4408
4555
|
});
|
|
@@ -4542,7 +4689,7 @@ function getServerStatus() {
|
|
|
4542
4689
|
return { running: false };
|
|
4543
4690
|
}
|
|
4544
4691
|
function isServerStale(status2) {
|
|
4545
|
-
return status2.running === true && status2.version !== "0.
|
|
4692
|
+
return status2.running === true && status2.version !== "0.15.0";
|
|
4546
4693
|
}
|
|
4547
4694
|
async function ensureCurrentServer(opts = {}) {
|
|
4548
4695
|
const status2 = getServerStatus();
|
|
@@ -4598,8 +4745,8 @@ async function startServer(opts = {}) {
|
|
|
4598
4745
|
});
|
|
4599
4746
|
child.unref();
|
|
4600
4747
|
const pid = child.pid;
|
|
4601
|
-
writePid(PID_FILE, pid, port, "0.
|
|
4602
|
-
await waitForHealth(`http://localhost:${port}`,
|
|
4748
|
+
writePid(PID_FILE, pid, port, "0.15.0");
|
|
4749
|
+
await waitForHealth(`http://localhost:${port}`, 3e4);
|
|
4603
4750
|
return { pid, port };
|
|
4604
4751
|
}
|
|
4605
4752
|
function stopServer() {
|
|
@@ -4661,6 +4808,13 @@ var init_server = __esm({
|
|
|
4661
4808
|
});
|
|
4662
4809
|
|
|
4663
4810
|
// src/usage/storage.ts
|
|
4811
|
+
var storage_exports = {};
|
|
4812
|
+
__export(storage_exports, {
|
|
4813
|
+
appendSnapshot: () => appendSnapshot,
|
|
4814
|
+
latestClaudeCodeSnapshot: () => latestClaudeCodeSnapshot,
|
|
4815
|
+
latestSnapshot: () => latestSnapshot,
|
|
4816
|
+
readSnapshots: () => readSnapshots
|
|
4817
|
+
});
|
|
4664
4818
|
import { appendFileSync, mkdirSync as mkdirSync3, readFileSync as readFileSync3, existsSync as existsSync2 } from "node:fs";
|
|
4665
4819
|
import { dirname as dirname3 } from "node:path";
|
|
4666
4820
|
function appendSnapshot(filePath, snapshot) {
|
|
@@ -4958,9 +5112,9 @@ Installed: ${PACKAGE_NAME}@${verify.installedVersion} at ${verify.installedPath
|
|
|
4958
5112
|
console.warn(
|
|
4959
5113
|
` \u2022 reinstall in the correct Node env: 'npm install -g ${PACKAGE_NAME}@latest'`
|
|
4960
5114
|
);
|
|
4961
|
-
} else if (verify.installedVersion !== "0.
|
|
5115
|
+
} else if (verify.installedVersion !== "0.15.0") {
|
|
4962
5116
|
console.log(
|
|
4963
|
-
` \u2192 This process is still running ${"0.
|
|
5117
|
+
` \u2192 This process is still running ${"0.15.0"}. Next invocation will use ${verify.installedVersion}.`
|
|
4964
5118
|
);
|
|
4965
5119
|
}
|
|
4966
5120
|
}
|
|
@@ -4995,14 +5149,14 @@ async function getUpdateAvailability() {
|
|
|
4995
5149
|
if (isDevMode()) return null;
|
|
4996
5150
|
const latest = await fetchLatestVersion();
|
|
4997
5151
|
if (latest === null) return {
|
|
4998
|
-
current: "0.
|
|
5152
|
+
current: "0.15.0",
|
|
4999
5153
|
latest: null,
|
|
5000
5154
|
updateAvailable: false
|
|
5001
5155
|
};
|
|
5002
5156
|
return {
|
|
5003
|
-
current: "0.
|
|
5157
|
+
current: "0.15.0",
|
|
5004
5158
|
latest,
|
|
5005
|
-
updateAvailable: shouldUpdate("0.
|
|
5159
|
+
updateAvailable: shouldUpdate("0.15.0", latest)
|
|
5006
5160
|
};
|
|
5007
5161
|
}
|
|
5008
5162
|
async function checkForUpdate() {
|
|
@@ -5092,7 +5246,7 @@ async function restartServices(state) {
|
|
|
5092
5246
|
}
|
|
5093
5247
|
async function forceUpdate() {
|
|
5094
5248
|
const latest = await fetchLatestVersion();
|
|
5095
|
-
const current = "0.
|
|
5249
|
+
const current = "0.15.0";
|
|
5096
5250
|
if (latest === null) {
|
|
5097
5251
|
console.error("Could not reach npm registry. Check your network.");
|
|
5098
5252
|
process.exit(1);
|
|
@@ -5135,10 +5289,13 @@ __export(autostart_exports, {
|
|
|
5135
5289
|
autostart: () => autostart,
|
|
5136
5290
|
buildPlist: () => buildPlist,
|
|
5137
5291
|
dismissPrompt: () => dismissPrompt,
|
|
5292
|
+
ensureTeamAutostart: () => ensureTeamAutostart,
|
|
5293
|
+
hasOptedOut: () => hasOptedOut,
|
|
5138
5294
|
installAutostart: () => installAutostart,
|
|
5139
5295
|
isAutostartInstalled: () => isAutostartInstalled,
|
|
5140
5296
|
isPromptDismissed: () => isPromptDismissed,
|
|
5141
|
-
maybePromptAutostart: () => maybePromptAutostart
|
|
5297
|
+
maybePromptAutostart: () => maybePromptAutostart,
|
|
5298
|
+
recordOptOut: () => recordOptOut
|
|
5142
5299
|
});
|
|
5143
5300
|
import { existsSync as existsSync5, mkdirSync as mkdirSync4, writeFileSync as writeFileSync3, rmSync, readFileSync as readFileSync6, realpathSync as realpathSync2 } from "node:fs";
|
|
5144
5301
|
import { homedir as homedir2 } from "node:os";
|
|
@@ -5175,7 +5332,6 @@ function buildPlist(opts) {
|
|
|
5175
5332
|
<array>
|
|
5176
5333
|
<string>${nodePath}</string>
|
|
5177
5334
|
<string>${scriptPath}</string>
|
|
5178
|
-
<string>daemon</string>
|
|
5179
5335
|
<string>start</string>
|
|
5180
5336
|
</array>
|
|
5181
5337
|
<key>RunAtLoad</key>
|
|
@@ -5207,9 +5363,10 @@ async function installAutostart() {
|
|
|
5207
5363
|
await runLaunchctl(["unload", p]).catch(() => {
|
|
5208
5364
|
});
|
|
5209
5365
|
await runLaunchctl(["load", "-w", p]);
|
|
5210
|
-
|
|
5366
|
+
writeFlags({ optedOut: false });
|
|
5367
|
+
console.log("Autostart installed \u2014 Fleetlens (dashboard + usage daemon) will start at login.");
|
|
5211
5368
|
console.log(` LaunchAgent: ${p}`);
|
|
5212
|
-
console.log(` Runs: ${nodePath} ${scriptPath}
|
|
5369
|
+
console.log(` Runs: ${nodePath} ${scriptPath} start`);
|
|
5213
5370
|
console.log(
|
|
5214
5371
|
" Note: the Node path above is baked in. If you upgrade or switch Node (e.g. via nvm), re-run `fleetlens autostart install`."
|
|
5215
5372
|
);
|
|
@@ -5221,14 +5378,27 @@ async function uninstallAutostart() {
|
|
|
5221
5378
|
return;
|
|
5222
5379
|
}
|
|
5223
5380
|
const p = plistPath();
|
|
5381
|
+
recordOptOut();
|
|
5224
5382
|
if (!existsSync5(p)) {
|
|
5225
|
-
console.log("Autostart is not installed \u2014 nothing to remove.");
|
|
5383
|
+
console.log("Autostart is not installed \u2014 nothing to remove. Noted your opt-out; team join won't enable it.");
|
|
5226
5384
|
return;
|
|
5227
5385
|
}
|
|
5228
5386
|
await runLaunchctl(["unload", "-w", p]).catch(() => {
|
|
5229
5387
|
});
|
|
5230
5388
|
rmSync(p, { force: true });
|
|
5231
5389
|
console.log(`Autostart removed (${p}). The daemon will no longer start at login.`);
|
|
5390
|
+
console.log(" Opt-out recorded \u2014 team join/start won't re-enable it.");
|
|
5391
|
+
}
|
|
5392
|
+
async function ensureTeamAutostart() {
|
|
5393
|
+
try {
|
|
5394
|
+
if (!isMac()) return;
|
|
5395
|
+
if (isAutostartInstalled()) return;
|
|
5396
|
+
if (hasOptedOut()) return;
|
|
5397
|
+
console.log(" Team reporting stays on across reboots: enabling Fleetlens auto-start at login (dashboard + daemon).");
|
|
5398
|
+
console.log(" (Opt out anytime: fleetlens autostart uninstall \u2014 join won't re-enable it.)");
|
|
5399
|
+
await installAutostart();
|
|
5400
|
+
} catch {
|
|
5401
|
+
}
|
|
5232
5402
|
}
|
|
5233
5403
|
async function statusAutostart() {
|
|
5234
5404
|
if (!isMac()) {
|
|
@@ -5249,22 +5419,33 @@ async function statusAutostart() {
|
|
|
5249
5419
|
function promptFlagPath() {
|
|
5250
5420
|
return cclensPath("autostart.json");
|
|
5251
5421
|
}
|
|
5252
|
-
function
|
|
5422
|
+
function readFlags() {
|
|
5253
5423
|
try {
|
|
5254
|
-
|
|
5255
|
-
return j.promptDismissed === true;
|
|
5424
|
+
return JSON.parse(readFileSync6(promptFlagPath(), "utf8"));
|
|
5256
5425
|
} catch {
|
|
5257
|
-
return
|
|
5426
|
+
return {};
|
|
5258
5427
|
}
|
|
5259
5428
|
}
|
|
5260
|
-
function
|
|
5429
|
+
function writeFlags(patch) {
|
|
5261
5430
|
const p = promptFlagPath();
|
|
5262
5431
|
try {
|
|
5263
5432
|
mkdirSync4(dirname6(p), { recursive: true });
|
|
5264
|
-
writeFileSync3(p, JSON.stringify({
|
|
5433
|
+
writeFileSync3(p, JSON.stringify({ ...readFlags(), ...patch }, null, 2), "utf8");
|
|
5265
5434
|
} catch {
|
|
5266
5435
|
}
|
|
5267
5436
|
}
|
|
5437
|
+
function isPromptDismissed() {
|
|
5438
|
+
return readFlags().promptDismissed === true;
|
|
5439
|
+
}
|
|
5440
|
+
function dismissPrompt() {
|
|
5441
|
+
writeFlags({ promptDismissed: true });
|
|
5442
|
+
}
|
|
5443
|
+
function hasOptedOut() {
|
|
5444
|
+
return readFlags().optedOut === true;
|
|
5445
|
+
}
|
|
5446
|
+
function recordOptOut() {
|
|
5447
|
+
writeFlags({ optedOut: true });
|
|
5448
|
+
}
|
|
5268
5449
|
function promptYesNo(question, defaultYes) {
|
|
5269
5450
|
return new Promise((resolve3) => {
|
|
5270
5451
|
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
@@ -5279,15 +5460,20 @@ function promptYesNo(question, defaultYes) {
|
|
|
5279
5460
|
async function maybePromptAutostart(opts) {
|
|
5280
5461
|
try {
|
|
5281
5462
|
if (!isMac()) return;
|
|
5282
|
-
if (!opts.daemonStarted) return;
|
|
5283
5463
|
if (process.env.__FLEETLENS_UPDATED) return;
|
|
5464
|
+
const { readTeamConfig: readTeamConfig2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
|
|
5465
|
+
if (readTeamConfig2()) {
|
|
5466
|
+
await ensureTeamAutostart();
|
|
5467
|
+
return;
|
|
5468
|
+
}
|
|
5469
|
+
if (!opts.daemonStarted) return;
|
|
5284
5470
|
if (!process.stdin.isTTY) return;
|
|
5285
5471
|
if (isAutostartInstalled()) return;
|
|
5286
5472
|
if (isPromptDismissed()) return;
|
|
5287
5473
|
console.log("");
|
|
5288
|
-
console.log("Tip: keep
|
|
5289
|
-
console.log(" This installs a launchd LaunchAgent that runs `fleetlens
|
|
5290
|
-
console.log(" at login (
|
|
5474
|
+
console.log("Tip: keep Fleetlens running after you restart your Mac?");
|
|
5475
|
+
console.log(" This installs a launchd LaunchAgent that runs `fleetlens start`");
|
|
5476
|
+
console.log(" at login (dashboard server + usage daemon).");
|
|
5291
5477
|
const yes = await promptYesNo("Set that up now? [Y/n] ", true);
|
|
5292
5478
|
if (yes) {
|
|
5293
5479
|
await installAutostart();
|
|
@@ -5450,7 +5636,7 @@ async function status() {
|
|
|
5450
5636
|
console.log(`Server: running on ${url} (PID ${server.pid}, v${ver})`);
|
|
5451
5637
|
if (isServerStale(server)) {
|
|
5452
5638
|
console.log(
|
|
5453
|
-
` \u26A0 serving stale ${ver} (CLI is ${"0.
|
|
5639
|
+
` \u26A0 serving stale ${ver} (CLI is ${"0.15.0"}) \u2014 run 'fleetlens start' to restart on the current version`
|
|
5454
5640
|
);
|
|
5455
5641
|
}
|
|
5456
5642
|
} else {
|
|
@@ -5588,6 +5774,11 @@ var init_token = __esm({
|
|
|
5588
5774
|
});
|
|
5589
5775
|
|
|
5590
5776
|
// src/usage/api.ts
|
|
5777
|
+
var api_exports = {};
|
|
5778
|
+
__export(api_exports, {
|
|
5779
|
+
UsageApiError: () => UsageApiError,
|
|
5780
|
+
fetchUsage: () => fetchUsage
|
|
5781
|
+
});
|
|
5591
5782
|
async function fetchUsage() {
|
|
5592
5783
|
const creds = readOAuthCredentials();
|
|
5593
5784
|
if (!creds) {
|
|
@@ -6058,8 +6249,42 @@ var init_usage = __esm({
|
|
|
6058
6249
|
});
|
|
6059
6250
|
|
|
6060
6251
|
// ../entries/dist/types.js
|
|
6061
|
-
function
|
|
6062
|
-
return
|
|
6252
|
+
function pendingEnrichment() {
|
|
6253
|
+
return {
|
|
6254
|
+
status: "pending",
|
|
6255
|
+
generated_at: null,
|
|
6256
|
+
model: null,
|
|
6257
|
+
cost_usd: null,
|
|
6258
|
+
error: null,
|
|
6259
|
+
brief_summary: null,
|
|
6260
|
+
underlying_goal: null,
|
|
6261
|
+
friction_detail: null,
|
|
6262
|
+
user_instructions: [],
|
|
6263
|
+
outcome: null,
|
|
6264
|
+
claude_helpfulness: null,
|
|
6265
|
+
goal_categories: {},
|
|
6266
|
+
retry_count: 0
|
|
6267
|
+
};
|
|
6268
|
+
}
|
|
6269
|
+
function skippedTrivialEnrichment(generatedAt) {
|
|
6270
|
+
return {
|
|
6271
|
+
status: "skipped_trivial",
|
|
6272
|
+
generated_at: generatedAt,
|
|
6273
|
+
model: null,
|
|
6274
|
+
cost_usd: null,
|
|
6275
|
+
error: null,
|
|
6276
|
+
brief_summary: null,
|
|
6277
|
+
underlying_goal: null,
|
|
6278
|
+
friction_detail: null,
|
|
6279
|
+
user_instructions: [],
|
|
6280
|
+
outcome: "trivial",
|
|
6281
|
+
claude_helpfulness: null,
|
|
6282
|
+
goal_categories: {},
|
|
6283
|
+
retry_count: 0
|
|
6284
|
+
};
|
|
6285
|
+
}
|
|
6286
|
+
function entryKey(sessionId, localDay2) {
|
|
6287
|
+
return `${sessionId}__${localDay2}`;
|
|
6063
6288
|
}
|
|
6064
6289
|
function parseEntryKey(key) {
|
|
6065
6290
|
const m = /^([^_]+(?:_[^_]+)*)__(\d{4}-\d{2}-\d{2})$/.exec(key);
|
|
@@ -6067,10 +6292,11 @@ function parseEntryKey(key) {
|
|
|
6067
6292
|
return null;
|
|
6068
6293
|
return { session_id: m[1], local_day: m[2] };
|
|
6069
6294
|
}
|
|
6070
|
-
var GOAL_CATEGORIES, CURRENT_DAY_DIGEST_SCHEMA_VERSION, CURRENT_WEEK_DIGEST_SCHEMA_VERSION, CURRENT_MONTH_DIGEST_SCHEMA_VERSION, PROMPT_FRAME_ORIGIN;
|
|
6295
|
+
var CURRENT_ENTRY_SCHEMA_VERSION, GOAL_CATEGORIES, CURRENT_DAY_DIGEST_SCHEMA_VERSION, CURRENT_WEEK_DIGEST_SCHEMA_VERSION, CURRENT_MONTH_DIGEST_SCHEMA_VERSION, PROMPT_FRAME_ORIGIN;
|
|
6071
6296
|
var init_types = __esm({
|
|
6072
6297
|
"../entries/dist/types.js"() {
|
|
6073
6298
|
"use strict";
|
|
6299
|
+
CURRENT_ENTRY_SCHEMA_VERSION = 2;
|
|
6074
6300
|
GOAL_CATEGORIES = [
|
|
6075
6301
|
"build",
|
|
6076
6302
|
"plan",
|
|
@@ -6588,6 +6814,35 @@ function classifyUserInputSource(text) {
|
|
|
6588
6814
|
return "slash_command";
|
|
6589
6815
|
return "human";
|
|
6590
6816
|
}
|
|
6817
|
+
function countSatisfactionSignals(text) {
|
|
6818
|
+
if (!text)
|
|
6819
|
+
return { happy: 0, satisfied: 0, dissatisfied: 0, frustrated: 0 };
|
|
6820
|
+
return {
|
|
6821
|
+
happy: (text.match(HAPPY_RE) ?? []).length,
|
|
6822
|
+
satisfied: (text.match(SATISFIED_RE) ?? []).length,
|
|
6823
|
+
dissatisfied: (text.match(DISSATISFIED_RE) ?? []).length,
|
|
6824
|
+
frustrated: (text.match(FRUSTRATED_RE) ?? []).length
|
|
6825
|
+
};
|
|
6826
|
+
}
|
|
6827
|
+
function extractUserInstructions(text) {
|
|
6828
|
+
if (!text)
|
|
6829
|
+
return [];
|
|
6830
|
+
INSTRUCTION_RE.lastIndex = 0;
|
|
6831
|
+
const out = [];
|
|
6832
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6833
|
+
let m;
|
|
6834
|
+
while ((m = INSTRUCTION_RE.exec(text)) !== null) {
|
|
6835
|
+
const phrase = m[1].trim().replace(/\s+/g, " ");
|
|
6836
|
+
const lc = phrase.toLowerCase();
|
|
6837
|
+
if (seen.has(lc))
|
|
6838
|
+
continue;
|
|
6839
|
+
seen.add(lc);
|
|
6840
|
+
out.push(phrase);
|
|
6841
|
+
if (out.length >= 5)
|
|
6842
|
+
break;
|
|
6843
|
+
}
|
|
6844
|
+
return out;
|
|
6845
|
+
}
|
|
6591
6846
|
function classifySubagentRole(sa) {
|
|
6592
6847
|
const text = `${sa.description} ${sa.prompt_preview}`.toLowerCase();
|
|
6593
6848
|
if (/\b(re-?review|review[s]?|verify|audit|spec[- ]review|code[- ]quality|code[- ]reuse|efficiency review|sanity check|quick test)\b/.test(text)) {
|
|
@@ -6725,7 +6980,7 @@ function computeEntrySignals(entry) {
|
|
|
6725
6980
|
continuation_kind
|
|
6726
6981
|
};
|
|
6727
6982
|
}
|
|
6728
|
-
var TEAMMATE_RE, SKILL_LOAD_RE, SLASH_COMMAND_RE2, STOCK_SUBAGENT_PREFIXES, STOCK_SKILL_PREFIXES, EXTERNAL_REF_PATTERNS;
|
|
6983
|
+
var TEAMMATE_RE, SKILL_LOAD_RE, SLASH_COMMAND_RE2, HAPPY_RE, SATISFIED_RE, DISSATISFIED_RE, FRUSTRATED_RE, INSTRUCTION_RE, STOCK_SUBAGENT_PREFIXES, STOCK_SKILL_PREFIXES, EXTERNAL_REF_PATTERNS;
|
|
6729
6984
|
var init_signals = __esm({
|
|
6730
6985
|
"../entries/dist/signals.js"() {
|
|
6731
6986
|
"use strict";
|
|
@@ -6733,6 +6988,11 @@ var init_signals = __esm({
|
|
|
6733
6988
|
TEAMMATE_RE = /^<teammate-message\b/;
|
|
6734
6989
|
SKILL_LOAD_RE = /^Base directory for this skill:/;
|
|
6735
6990
|
SLASH_COMMAND_RE2 = /^<command-name>|^<local-command-stdout>/;
|
|
6991
|
+
HAPPY_RE = /\b(?:yay|yaay|yass|woohoo|nice|great|love(?:ly)?|perfect|amazing|awesome)\s*!|!!!/gi;
|
|
6992
|
+
SATISFIED_RE = /\b(?:thanks|thank you|looks good|lgtm|works|that works|all good|sounds good)\b/gi;
|
|
6993
|
+
DISSATISFIED_RE = /\b(?:that'?s (?:not|wrong)|try again|no, (?:that|this)|not quite|incorrect)\b/gi;
|
|
6994
|
+
FRUSTRATED_RE = /\b(?:broken|stop|why (?:did|are|would) you|give up|ugh|argh|wtf)\b/gi;
|
|
6995
|
+
INSTRUCTION_RE = /\b(?:can you|please|let's|let us|i need|could you|would you)\s+([^.?!\n]{5,200})/gi;
|
|
6736
6996
|
STOCK_SUBAGENT_PREFIXES = /^(general-purpose|Explore|Plan|claude-code-guide|playwright-qa-verifier|statusline-setup|frontend-design:|code-review:|code-simplifier:|codex:|superpowers:)/;
|
|
6737
6997
|
STOCK_SKILL_PREFIXES = /^(superpowers|mcp__|frontend-design|code-review|codex|claude-code-guide|claude-api):/i;
|
|
6738
6998
|
EXTERNAL_REF_PATTERNS = [
|
|
@@ -6745,6 +7005,9 @@ var init_signals = __esm({
|
|
|
6745
7005
|
});
|
|
6746
7006
|
|
|
6747
7007
|
// ../entries/dist/trivial.js
|
|
7008
|
+
function isTrivial(n) {
|
|
7009
|
+
return n.active_min < 1 && n.turn_count < 3 && n.tools_total === 0;
|
|
7010
|
+
}
|
|
6748
7011
|
var init_trivial = __esm({
|
|
6749
7012
|
"../entries/dist/trivial.js"() {
|
|
6750
7013
|
"use strict";
|
|
@@ -6752,7 +7015,521 @@ var init_trivial = __esm({
|
|
|
6752
7015
|
});
|
|
6753
7016
|
|
|
6754
7017
|
// ../entries/dist/build.js
|
|
6755
|
-
|
|
7018
|
+
function viewEvent(ev) {
|
|
7019
|
+
let rawType;
|
|
7020
|
+
if (ev.role === "user" || ev.role === "tool-result")
|
|
7021
|
+
rawType = "user";
|
|
7022
|
+
else if (ev.role === "agent" || ev.role === "agent-thinking" || ev.role === "tool-call")
|
|
7023
|
+
rawType = "assistant";
|
|
7024
|
+
else if (ev.rawType === "summary")
|
|
7025
|
+
rawType = "summary";
|
|
7026
|
+
else
|
|
7027
|
+
rawType = "system";
|
|
7028
|
+
const rawCwd = ev.raw?.cwd;
|
|
7029
|
+
return {
|
|
7030
|
+
rawType,
|
|
7031
|
+
content: ev.blocks ?? [],
|
|
7032
|
+
model: ev.model,
|
|
7033
|
+
msgId: ev.messageId,
|
|
7034
|
+
usage: ev.usage ? {
|
|
7035
|
+
input_tokens: ev.usage.input,
|
|
7036
|
+
output_tokens: ev.usage.output,
|
|
7037
|
+
cache_read_input_tokens: ev.usage.cacheRead,
|
|
7038
|
+
cache_creation_input_tokens: ev.usage.cacheWrite
|
|
7039
|
+
} : void 0,
|
|
7040
|
+
cwd: rawCwd
|
|
7041
|
+
};
|
|
7042
|
+
}
|
|
7043
|
+
function parseMs(iso) {
|
|
7044
|
+
if (!iso)
|
|
7045
|
+
return void 0;
|
|
7046
|
+
const ms = Date.parse(iso);
|
|
7047
|
+
return Number.isNaN(ms) ? void 0 : ms;
|
|
7048
|
+
}
|
|
7049
|
+
function trunc(s, n) {
|
|
7050
|
+
if (!s)
|
|
7051
|
+
return "";
|
|
7052
|
+
const one = s.replace(/\s+/g, " ").trim();
|
|
7053
|
+
return one.length <= n ? one : one.slice(0, n - 1) + "\u2026";
|
|
7054
|
+
}
|
|
7055
|
+
function minimizeImages(s) {
|
|
7056
|
+
if (!s)
|
|
7057
|
+
return s;
|
|
7058
|
+
let counter = 0;
|
|
7059
|
+
return s.replace(IMAGE_BLOCK_RE, () => `[Image #${++counter}]`);
|
|
7060
|
+
}
|
|
7061
|
+
function bashVerb(cmd) {
|
|
7062
|
+
if (!cmd)
|
|
7063
|
+
return "?";
|
|
7064
|
+
let firstSeg = cmd.split(BASH_CHAIN_SPLIT_RE, 1)[0].trim();
|
|
7065
|
+
if (firstSeg.startsWith("cd ") || firstSeg === "cd") {
|
|
7066
|
+
const parts = cmd.split(/&&|;/, 3);
|
|
7067
|
+
if (parts.length >= 2)
|
|
7068
|
+
firstSeg = parts[1].trim();
|
|
7069
|
+
}
|
|
7070
|
+
const tokens = firstSeg.split(/\s+/).filter(Boolean);
|
|
7071
|
+
while (tokens.length && SHELL_NOISE.has(tokens[0]))
|
|
7072
|
+
tokens.shift();
|
|
7073
|
+
if (!tokens.length)
|
|
7074
|
+
return "?";
|
|
7075
|
+
let first = tokens[0];
|
|
7076
|
+
if (first.includes("=") && !first.startsWith("/")) {
|
|
7077
|
+
tokens.shift();
|
|
7078
|
+
if (!tokens.length)
|
|
7079
|
+
return "?";
|
|
7080
|
+
first = tokens[0];
|
|
7081
|
+
}
|
|
7082
|
+
if (first.includes("/") && !first.startsWith("./")) {
|
|
7083
|
+
first = first.replace(/\/+$/, "").split("/").pop();
|
|
7084
|
+
}
|
|
7085
|
+
return first.replace(/^["']|["']$/g, "") || "?";
|
|
7086
|
+
}
|
|
7087
|
+
function firstTextBlock(blocks) {
|
|
7088
|
+
if (!Array.isArray(blocks))
|
|
7089
|
+
return "";
|
|
7090
|
+
for (const b of blocks) {
|
|
7091
|
+
if (b && typeof b === "object" && b.type === "text") {
|
|
7092
|
+
return String(b.text ?? "");
|
|
7093
|
+
}
|
|
7094
|
+
}
|
|
7095
|
+
return "";
|
|
7096
|
+
}
|
|
7097
|
+
function blockText(blocks) {
|
|
7098
|
+
if (!blocks)
|
|
7099
|
+
return "";
|
|
7100
|
+
return blocks.filter((b) => !!b && typeof b === "object" && b.type === "text" && typeof b.text === "string").map((b) => b.text).join(" ");
|
|
7101
|
+
}
|
|
7102
|
+
function groupEventsByLocalDay(events) {
|
|
7103
|
+
const byDay = /* @__PURE__ */ new Map();
|
|
7104
|
+
for (const ev of events) {
|
|
7105
|
+
if (!ev.timestamp)
|
|
7106
|
+
continue;
|
|
7107
|
+
const ms = Date.parse(ev.timestamp);
|
|
7108
|
+
if (Number.isNaN(ms))
|
|
7109
|
+
continue;
|
|
7110
|
+
const day2 = toLocalDay(ms);
|
|
7111
|
+
let bucket = byDay.get(day2);
|
|
7112
|
+
if (!bucket) {
|
|
7113
|
+
bucket = [];
|
|
7114
|
+
byDay.set(day2, bucket);
|
|
7115
|
+
}
|
|
7116
|
+
bucket.push(ev);
|
|
7117
|
+
}
|
|
7118
|
+
return byDay;
|
|
7119
|
+
}
|
|
7120
|
+
function newTurn(startMs, userInput = "") {
|
|
7121
|
+
return {
|
|
7122
|
+
startMs,
|
|
7123
|
+
lastMs: startMs,
|
|
7124
|
+
eventTs: startMs !== void 0 ? [startMs] : [],
|
|
7125
|
+
userInput,
|
|
7126
|
+
toolsTotal: 0,
|
|
7127
|
+
toolMix: /* @__PURE__ */ new Map(),
|
|
7128
|
+
bashVerbs: /* @__PURE__ */ new Map(),
|
|
7129
|
+
skills: /* @__PURE__ */ new Map(),
|
|
7130
|
+
subagents: [],
|
|
7131
|
+
taskOps: 0,
|
|
7132
|
+
toolErrors: 0,
|
|
7133
|
+
consecSameToolMax: 0,
|
|
7134
|
+
consec: 0,
|
|
7135
|
+
endedInInterrupt: false,
|
|
7136
|
+
models: /* @__PURE__ */ new Map(),
|
|
7137
|
+
tokensIn: 0,
|
|
7138
|
+
tokensOut: 0,
|
|
7139
|
+
tokensCacheR: 0,
|
|
7140
|
+
tokensCacheW: 0,
|
|
7141
|
+
seenMsgIds: /* @__PURE__ */ new Set()
|
|
7142
|
+
};
|
|
7143
|
+
}
|
|
7144
|
+
function closeTurn(t, closed, cwd) {
|
|
7145
|
+
const ts = [...t.eventTs].sort((a, b) => a - b);
|
|
7146
|
+
let active = 0;
|
|
7147
|
+
for (let i = 1; i < ts.length; i++) {
|
|
7148
|
+
const dt = ts[i] - ts[i - 1];
|
|
7149
|
+
if (dt < IDLE_GAP_MS4)
|
|
7150
|
+
active += dt;
|
|
7151
|
+
}
|
|
7152
|
+
closed.push({
|
|
7153
|
+
startMs: t.startMs ?? 0,
|
|
7154
|
+
activeMs: active,
|
|
7155
|
+
userInput: t.userInput,
|
|
7156
|
+
firstAgentText: t.firstAgentText,
|
|
7157
|
+
lastAgentText: t.lastAgentText,
|
|
7158
|
+
toolsTotal: t.toolsTotal,
|
|
7159
|
+
toolMix: t.toolMix,
|
|
7160
|
+
bashVerbs: t.bashVerbs,
|
|
7161
|
+
skills: t.skills,
|
|
7162
|
+
subagents: t.subagents,
|
|
7163
|
+
taskOps: t.taskOps,
|
|
7164
|
+
toolErrors: t.toolErrors,
|
|
7165
|
+
consecSameToolMax: t.consecSameToolMax,
|
|
7166
|
+
endedInInterrupt: t.endedInInterrupt,
|
|
7167
|
+
models: t.models,
|
|
7168
|
+
totalTokens: t.tokensIn + t.tokensOut + t.tokensCacheR + t.tokensCacheW,
|
|
7169
|
+
cwd
|
|
7170
|
+
});
|
|
7171
|
+
}
|
|
7172
|
+
function aggregateDay(dayEvents, _sessionFallbackProject) {
|
|
7173
|
+
const prTitles = [];
|
|
7174
|
+
let commits = 0;
|
|
7175
|
+
let pushes = 0;
|
|
7176
|
+
const gitToolUseIds = /* @__PURE__ */ new Set();
|
|
7177
|
+
const githubRepos = /* @__PURE__ */ new Set();
|
|
7178
|
+
const editedDirCounts = /* @__PURE__ */ new Map();
|
|
7179
|
+
const modelTurns = /* @__PURE__ */ new Map();
|
|
7180
|
+
let totalToolErrors = 0;
|
|
7181
|
+
let totalInterrupts = 0;
|
|
7182
|
+
let exitPlanCalls = 0;
|
|
7183
|
+
let taskOpsTotal = 0;
|
|
7184
|
+
let firstUser;
|
|
7185
|
+
let finalAgent;
|
|
7186
|
+
const cwdCounts = /* @__PURE__ */ new Map();
|
|
7187
|
+
const closed = [];
|
|
7188
|
+
let cur;
|
|
7189
|
+
const allTs = [];
|
|
7190
|
+
let lastEventCwd;
|
|
7191
|
+
for (const ev of dayEvents) {
|
|
7192
|
+
const tsMs = parseMs(ev.timestamp);
|
|
7193
|
+
if (tsMs !== void 0)
|
|
7194
|
+
allTs.push(tsMs);
|
|
7195
|
+
const view = viewEvent(ev);
|
|
7196
|
+
const rawType = view.rawType;
|
|
7197
|
+
const msg = { content: view.content, id: view.msgId, model: view.model, usage: view.usage };
|
|
7198
|
+
const content = view.content;
|
|
7199
|
+
const eventCwd = view.cwd;
|
|
7200
|
+
if (eventCwd)
|
|
7201
|
+
lastEventCwd = eventCwd;
|
|
7202
|
+
if (rawType === "user") {
|
|
7203
|
+
const isToolResultOnly = Array.isArray(content) && content.length > 0 && content.every((c) => c && typeof c === "object" && c.type === "tool_result");
|
|
7204
|
+
if (!isToolResultOnly) {
|
|
7205
|
+
const text = firstTextBlock(content) || (typeof content === "string" ? content : "");
|
|
7206
|
+
if (cur) {
|
|
7207
|
+
if (text && INTERRUPT_RE2.test(text)) {
|
|
7208
|
+
cur.endedInInterrupt = true;
|
|
7209
|
+
totalInterrupts++;
|
|
7210
|
+
}
|
|
7211
|
+
closeTurn(cur, closed, eventCwd);
|
|
7212
|
+
}
|
|
7213
|
+
cur = newTurn(tsMs, text);
|
|
7214
|
+
if (!firstUser && text && classifyUserInputSource(text) === "human") {
|
|
7215
|
+
firstUser = text;
|
|
7216
|
+
}
|
|
7217
|
+
} else {
|
|
7218
|
+
if (Array.isArray(content)) {
|
|
7219
|
+
for (const c of content) {
|
|
7220
|
+
if (!c || typeof c !== "object" || c.type !== "tool_result")
|
|
7221
|
+
continue;
|
|
7222
|
+
if (c.is_error) {
|
|
7223
|
+
totalToolErrors++;
|
|
7224
|
+
if (cur)
|
|
7225
|
+
cur.toolErrors++;
|
|
7226
|
+
}
|
|
7227
|
+
const tuId = c.tool_use_id;
|
|
7228
|
+
if (tuId && gitToolUseIds.has(tuId)) {
|
|
7229
|
+
const rc = c.content;
|
|
7230
|
+
const text = typeof rc === "string" ? rc : Array.isArray(rc) ? rc.map((b) => b && typeof b === "object" && typeof b.text === "string" ? b.text : "").join("\n") : "";
|
|
7231
|
+
for (const m of text.matchAll(/(?<![.\w])github\.com[:/]([A-Za-z0-9_.-]+)\/([A-Za-z0-9_.-]+?)(?:\.git\b|[/\s"'),]|$)/g)) {
|
|
7232
|
+
if (m[1].toLowerCase() === "user-attachments")
|
|
7233
|
+
continue;
|
|
7234
|
+
githubRepos.add(`${m[1]}/${m[2]}`.toLowerCase());
|
|
7235
|
+
}
|
|
7236
|
+
}
|
|
7237
|
+
}
|
|
7238
|
+
}
|
|
7239
|
+
if (cur && tsMs !== void 0) {
|
|
7240
|
+
cur.eventTs.push(tsMs);
|
|
7241
|
+
cur.lastMs = tsMs;
|
|
7242
|
+
}
|
|
7243
|
+
}
|
|
7244
|
+
} else if (rawType === "assistant") {
|
|
7245
|
+
if (cur && tsMs !== void 0) {
|
|
7246
|
+
cur.eventTs.push(tsMs);
|
|
7247
|
+
cur.lastMs = tsMs;
|
|
7248
|
+
}
|
|
7249
|
+
const model = msg.model;
|
|
7250
|
+
if (model) {
|
|
7251
|
+
modelTurns.set(model, (modelTurns.get(model) ?? 0) + 1);
|
|
7252
|
+
if (cur)
|
|
7253
|
+
cur.models.set(model, (cur.models.get(model) ?? 0) + 1);
|
|
7254
|
+
}
|
|
7255
|
+
const msgId = msg.id;
|
|
7256
|
+
const usage2 = msg.usage;
|
|
7257
|
+
if (cur && msgId && !cur.seenMsgIds.has(msgId) && usage2) {
|
|
7258
|
+
cur.seenMsgIds.add(msgId);
|
|
7259
|
+
cur.tokensIn += usage2.input_tokens ?? 0;
|
|
7260
|
+
cur.tokensOut += usage2.output_tokens ?? 0;
|
|
7261
|
+
cur.tokensCacheR += usage2.cache_read_input_tokens ?? 0;
|
|
7262
|
+
cur.tokensCacheW += usage2.cache_creation_input_tokens ?? 0;
|
|
7263
|
+
}
|
|
7264
|
+
if (Array.isArray(content)) {
|
|
7265
|
+
for (const c of content) {
|
|
7266
|
+
if (!c || typeof c !== "object")
|
|
7267
|
+
continue;
|
|
7268
|
+
const ct = c;
|
|
7269
|
+
if (ct.type === "text" && ct.text) {
|
|
7270
|
+
finalAgent = ct.text;
|
|
7271
|
+
if (cur) {
|
|
7272
|
+
if (cur.firstAgentText === void 0)
|
|
7273
|
+
cur.firstAgentText = ct.text;
|
|
7274
|
+
cur.lastAgentText = ct.text;
|
|
7275
|
+
}
|
|
7276
|
+
} else if (ct.type === "tool_use") {
|
|
7277
|
+
const name = ct.name ?? "unknown";
|
|
7278
|
+
if (cur) {
|
|
7279
|
+
cur.toolsTotal++;
|
|
7280
|
+
cur.toolMix.set(name, (cur.toolMix.get(name) ?? 0) + 1);
|
|
7281
|
+
if (name === cur.lastTool)
|
|
7282
|
+
cur.consec++;
|
|
7283
|
+
else {
|
|
7284
|
+
cur.lastTool = name;
|
|
7285
|
+
cur.consec = 1;
|
|
7286
|
+
}
|
|
7287
|
+
if (cur.consec > cur.consecSameToolMax)
|
|
7288
|
+
cur.consecSameToolMax = cur.consec;
|
|
7289
|
+
if (name === "Skill") {
|
|
7290
|
+
const sk = String(ct.input?.skill ?? "unknown");
|
|
7291
|
+
cur.skills.set(sk, (cur.skills.get(sk) ?? 0) + 1);
|
|
7292
|
+
}
|
|
7293
|
+
if (name === "Agent") {
|
|
7294
|
+
const inp = ct.input ?? {};
|
|
7295
|
+
cur.subagents.push({
|
|
7296
|
+
type: String(inp.subagent_type ?? "general-purpose"),
|
|
7297
|
+
description: trunc(String(inp.description ?? ""), 80),
|
|
7298
|
+
prompt_preview: trunc(String(inp.prompt ?? ""), 240),
|
|
7299
|
+
background: Boolean(inp.run_in_background)
|
|
7300
|
+
});
|
|
7301
|
+
}
|
|
7302
|
+
if (name === "ExitPlanMode")
|
|
7303
|
+
exitPlanCalls++;
|
|
7304
|
+
if (name === "TodoWrite" || name === "TaskCreate" || name === "TaskUpdate") {
|
|
7305
|
+
cur.taskOps++;
|
|
7306
|
+
taskOpsTotal++;
|
|
7307
|
+
}
|
|
7308
|
+
}
|
|
7309
|
+
if (name === "Bash") {
|
|
7310
|
+
const cmd = String(ct.input?.command ?? "");
|
|
7311
|
+
if (cur)
|
|
7312
|
+
cur.bashVerbs.set(bashVerb(cmd), (cur.bashVerbs.get(bashVerb(cmd)) ?? 0) + 1);
|
|
7313
|
+
if (/\bgh\s+pr\s+create\b/.test(cmd)) {
|
|
7314
|
+
const m = cmd.match(PR_TITLE_RE);
|
|
7315
|
+
prTitles.push(m?.[1] ?? cmd.slice(0, 120));
|
|
7316
|
+
}
|
|
7317
|
+
if (/\bgit\s+commit\b/.test(cmd))
|
|
7318
|
+
commits++;
|
|
7319
|
+
if (/\bgit\s+push\b/.test(cmd))
|
|
7320
|
+
pushes++;
|
|
7321
|
+
if (ct.id && /\bgit\s+push\b|\bgh\s+pr\s+/.test(cmd))
|
|
7322
|
+
gitToolUseIds.add(ct.id);
|
|
7323
|
+
}
|
|
7324
|
+
if (eventCwd && (name === "Bash" || name === "Edit" || name === "Write" || name === "Read")) {
|
|
7325
|
+
cwdCounts.set(eventCwd, (cwdCounts.get(eventCwd) ?? 0) + 1);
|
|
7326
|
+
}
|
|
7327
|
+
if (name === "Edit" || name === "Write" || name === "MultiEdit" || name === "NotebookEdit") {
|
|
7328
|
+
const fp = String(ct.input?.file_path ?? ct.input?.notebook_path ?? "");
|
|
7329
|
+
const slash = fp.lastIndexOf("/");
|
|
7330
|
+
if (slash > 0) {
|
|
7331
|
+
const dir = fp.slice(0, slash);
|
|
7332
|
+
editedDirCounts.set(dir, (editedDirCounts.get(dir) ?? 0) + 1);
|
|
7333
|
+
}
|
|
7334
|
+
}
|
|
7335
|
+
}
|
|
7336
|
+
}
|
|
7337
|
+
}
|
|
7338
|
+
}
|
|
7339
|
+
}
|
|
7340
|
+
if (cur)
|
|
7341
|
+
closeTurn(cur, closed, lastEventCwd);
|
|
7342
|
+
const tsSorted = [...allTs].sort((a, b) => a - b);
|
|
7343
|
+
let activeMs2 = 0;
|
|
7344
|
+
for (let i = 1; i < tsSorted.length; i++) {
|
|
7345
|
+
const dt = tsSorted[i] - tsSorted[i - 1];
|
|
7346
|
+
if (dt < IDLE_GAP_MS4)
|
|
7347
|
+
activeMs2 += dt;
|
|
7348
|
+
}
|
|
7349
|
+
const tsSortedStrings = dayEvents.map((e) => e.timestamp).filter((t) => !!t).sort();
|
|
7350
|
+
const start_iso = tsSortedStrings[0] ?? "";
|
|
7351
|
+
const end_iso = tsSortedStrings[tsSortedStrings.length - 1] ?? "";
|
|
7352
|
+
return {
|
|
7353
|
+
closed,
|
|
7354
|
+
firstUser,
|
|
7355
|
+
finalAgent,
|
|
7356
|
+
prTitles,
|
|
7357
|
+
commits,
|
|
7358
|
+
pushes,
|
|
7359
|
+
githubRepos: [...githubRepos].slice(0, 5),
|
|
7360
|
+
editedDirs: [...editedDirCounts.entries()].sort((a, b) => b[1] - a[1]).slice(0, 8).map(([dir]) => dir),
|
|
7361
|
+
modelTurns,
|
|
7362
|
+
totalToolErrors,
|
|
7363
|
+
totalInterrupts,
|
|
7364
|
+
exitPlanCalls,
|
|
7365
|
+
taskOpsTotal,
|
|
7366
|
+
cwdCounts,
|
|
7367
|
+
start_iso,
|
|
7368
|
+
end_iso,
|
|
7369
|
+
activeMs: activeMs2
|
|
7370
|
+
};
|
|
7371
|
+
}
|
|
7372
|
+
function buildTopTools(closed) {
|
|
7373
|
+
const toolMix = /* @__PURE__ */ new Map();
|
|
7374
|
+
const bashVerbsAll = /* @__PURE__ */ new Map();
|
|
7375
|
+
for (const t of closed) {
|
|
7376
|
+
for (const [k, v] of t.toolMix)
|
|
7377
|
+
toolMix.set(k, (toolMix.get(k) ?? 0) + v);
|
|
7378
|
+
for (const [k, v] of t.bashVerbs)
|
|
7379
|
+
bashVerbsAll.set(k, (bashVerbsAll.get(k) ?? 0) + v);
|
|
7380
|
+
}
|
|
7381
|
+
const top = [...toolMix.entries()].sort((a, b) => b[1] - a[1]).slice(0, 3);
|
|
7382
|
+
return top.map(([name, count]) => {
|
|
7383
|
+
if (name === "Bash" && bashVerbsAll.size > 0) {
|
|
7384
|
+
const sub = [...bashVerbsAll.entries()].sort((a, b) => b[1] - a[1]).slice(0, 3).map(([v, c]) => c > 1 ? `${v}\xD7${c}` : v).join(", ");
|
|
7385
|
+
return `Bash\xD7${count} (${sub})`;
|
|
7386
|
+
}
|
|
7387
|
+
return count > 1 ? `${name}\xD7${count}` : name;
|
|
7388
|
+
});
|
|
7389
|
+
}
|
|
7390
|
+
function buildEntries(sessionDetail) {
|
|
7391
|
+
const byDay = groupEventsByLocalDay(sessionDetail.events);
|
|
7392
|
+
const entries2 = [];
|
|
7393
|
+
const generatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
7394
|
+
const sessionFallbackProject = canonicalProjectName(sessionDetail.projectName ?? "");
|
|
7395
|
+
for (const [local_day, dayEvents] of byDay) {
|
|
7396
|
+
if (dayEvents.length === 0)
|
|
7397
|
+
continue;
|
|
7398
|
+
const agg = aggregateDay(dayEvents, sessionFallbackProject);
|
|
7399
|
+
const { closed } = agg;
|
|
7400
|
+
const turnCount = closed.length;
|
|
7401
|
+
const toolsTotal = closed.reduce((s, t) => s + t.toolsTotal, 0);
|
|
7402
|
+
const tokensTotal = closed.reduce((s, t) => s + t.totalTokens, 0);
|
|
7403
|
+
const subagentCalls = closed.reduce((s, t) => s + t.subagents.length, 0);
|
|
7404
|
+
const skillsMap = /* @__PURE__ */ new Map();
|
|
7405
|
+
for (const t of closed) {
|
|
7406
|
+
for (const [k, v] of t.skills)
|
|
7407
|
+
skillsMap.set(k, (skillsMap.get(k) ?? 0) + v);
|
|
7408
|
+
}
|
|
7409
|
+
const skillCalls = [...skillsMap.values()].reduce((a, b) => a + b, 0);
|
|
7410
|
+
const taskOps = agg.taskOpsTotal;
|
|
7411
|
+
const interrupts = agg.totalInterrupts;
|
|
7412
|
+
const toolErrors = agg.totalToolErrors;
|
|
7413
|
+
const consecMax = Math.max(0, ...closed.map((t) => t.consecSameToolMax));
|
|
7414
|
+
const exitPlanCalls = agg.exitPlanCalls;
|
|
7415
|
+
const prs = agg.prTitles.length;
|
|
7416
|
+
const commits = agg.commits;
|
|
7417
|
+
const pushes = agg.pushes;
|
|
7418
|
+
const activeMin = Math.round(agg.activeMs / 6e3) / 10;
|
|
7419
|
+
const numbers = {
|
|
7420
|
+
active_min: activeMin,
|
|
7421
|
+
turn_count: turnCount,
|
|
7422
|
+
tools_total: toolsTotal,
|
|
7423
|
+
subagent_calls: subagentCalls,
|
|
7424
|
+
skill_calls: skillCalls,
|
|
7425
|
+
task_ops: taskOps,
|
|
7426
|
+
interrupts,
|
|
7427
|
+
tool_errors: toolErrors,
|
|
7428
|
+
consec_same_tool_max: consecMax,
|
|
7429
|
+
exit_plan_calls: exitPlanCalls,
|
|
7430
|
+
prs,
|
|
7431
|
+
commits,
|
|
7432
|
+
pushes,
|
|
7433
|
+
tokens_total: tokensTotal
|
|
7434
|
+
};
|
|
7435
|
+
const longestTurnActiveMs = Math.max(0, ...closed.map((t) => t.activeMs));
|
|
7436
|
+
const flags = [];
|
|
7437
|
+
if (interrupts >= 3)
|
|
7438
|
+
flags.push("interrupt_heavy");
|
|
7439
|
+
if (toolErrors >= 20)
|
|
7440
|
+
flags.push("high_errors");
|
|
7441
|
+
if (consecMax >= 8)
|
|
7442
|
+
flags.push("loop_suspected");
|
|
7443
|
+
if (agg.activeMs < 5 * 6e4 && prs >= 1)
|
|
7444
|
+
flags.push("fast_ship");
|
|
7445
|
+
if (exitPlanCalls > 0)
|
|
7446
|
+
flags.push("plan_used");
|
|
7447
|
+
const subagentTurns = closed.filter((t) => t.subagents.length >= 1).length;
|
|
7448
|
+
if (subagentTurns >= 3)
|
|
7449
|
+
flags.push("orchestrated");
|
|
7450
|
+
if (longestTurnActiveMs >= 20 * 6e4 && interrupts === 0)
|
|
7451
|
+
flags.push("long_autonomous");
|
|
7452
|
+
const primaryModel = [...agg.modelTurns.entries()].sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]))[0]?.[0] ?? null;
|
|
7453
|
+
const modelMix = Object.fromEntries(agg.modelTurns);
|
|
7454
|
+
const firstUserRaw = agg.firstUser ?? "";
|
|
7455
|
+
const first_user = trunc(minimizeImages(firstUserRaw), 400);
|
|
7456
|
+
const finalAgentRaw = agg.finalAgent ?? "";
|
|
7457
|
+
const final_agent = trunc(minimizeImages(finalAgentRaw), 400);
|
|
7458
|
+
let project = sessionFallbackProject;
|
|
7459
|
+
if (agg.cwdCounts.size > 0) {
|
|
7460
|
+
const best = [...agg.cwdCounts.entries()].sort((a, b) => b[1] - a[1])[0];
|
|
7461
|
+
project = canonicalProjectName(best[0]);
|
|
7462
|
+
}
|
|
7463
|
+
const top_tools = buildTopTools(closed);
|
|
7464
|
+
const skills = Object.fromEntries(skillsMap);
|
|
7465
|
+
const subagents = closed.flatMap((t) => t.subagents);
|
|
7466
|
+
const humanText = dayEvents.filter((ev) => {
|
|
7467
|
+
const v = viewEvent(ev);
|
|
7468
|
+
if (v.rawType !== "user")
|
|
7469
|
+
return false;
|
|
7470
|
+
const content = v.content;
|
|
7471
|
+
const isToolResultOnly = content.length > 0 && content.every((c) => c.type === "tool_result");
|
|
7472
|
+
if (isToolResultOnly)
|
|
7473
|
+
return false;
|
|
7474
|
+
const text = blockText(ev.blocks) || ev.preview || "";
|
|
7475
|
+
return classifyUserInputSource(text) === "human";
|
|
7476
|
+
}).map((ev) => blockText(ev.blocks) || ev.preview || "").join("\n");
|
|
7477
|
+
const satisfaction_signals = countSatisfactionSignals(humanText);
|
|
7478
|
+
const user_input_sources = {
|
|
7479
|
+
human: 0,
|
|
7480
|
+
teammate: 0,
|
|
7481
|
+
skill_load: 0,
|
|
7482
|
+
slash_command: 0,
|
|
7483
|
+
system_instruction: 0
|
|
7484
|
+
};
|
|
7485
|
+
for (const ev of dayEvents) {
|
|
7486
|
+
const v = viewEvent(ev);
|
|
7487
|
+
if (v.rawType !== "user")
|
|
7488
|
+
continue;
|
|
7489
|
+
const content = v.content;
|
|
7490
|
+
const isToolResultOnly = content.length > 0 && content.every((c) => c.type === "tool_result");
|
|
7491
|
+
if (isToolResultOnly)
|
|
7492
|
+
continue;
|
|
7493
|
+
const text = blockText(ev.blocks) || ev.preview || "";
|
|
7494
|
+
const src = classifyUserInputSource(text);
|
|
7495
|
+
user_input_sources[src]++;
|
|
7496
|
+
}
|
|
7497
|
+
const pendingUserInstructions = extractUserInstructions(humanText).slice(0, 5);
|
|
7498
|
+
const trivial = isTrivial(numbers);
|
|
7499
|
+
const entry = {
|
|
7500
|
+
version: CURRENT_ENTRY_SCHEMA_VERSION,
|
|
7501
|
+
agent: sessionDetail.agent,
|
|
7502
|
+
session_id: sessionDetail.id,
|
|
7503
|
+
local_day,
|
|
7504
|
+
project,
|
|
7505
|
+
start_iso: agg.start_iso,
|
|
7506
|
+
end_iso: agg.end_iso,
|
|
7507
|
+
numbers,
|
|
7508
|
+
flags,
|
|
7509
|
+
primary_model: primaryModel,
|
|
7510
|
+
model_mix: modelMix,
|
|
7511
|
+
first_user,
|
|
7512
|
+
final_agent,
|
|
7513
|
+
pr_titles: agg.prTitles,
|
|
7514
|
+
...agg.githubRepos.length ? { github_repos: agg.githubRepos } : {},
|
|
7515
|
+
...agg.editedDirs.length ? { edited_dirs: agg.editedDirs } : {},
|
|
7516
|
+
top_tools,
|
|
7517
|
+
skills,
|
|
7518
|
+
subagents,
|
|
7519
|
+
satisfaction_signals,
|
|
7520
|
+
user_input_sources,
|
|
7521
|
+
enrichment: trivial ? skippedTrivialEnrichment(generatedAt) : { ...pendingEnrichment(), user_instructions: pendingUserInstructions },
|
|
7522
|
+
generated_at: generatedAt,
|
|
7523
|
+
source_jsonl: sessionDetail.filePath ?? "",
|
|
7524
|
+
source_checkpoint: { byte_offset: 0, last_event_ts: agg.end_iso || null }
|
|
7525
|
+
};
|
|
7526
|
+
entry.signals = computeEntrySignals(entry);
|
|
7527
|
+
entries2.push(entry);
|
|
7528
|
+
}
|
|
7529
|
+
entries2.sort((a, b) => a.local_day.localeCompare(b.local_day));
|
|
7530
|
+
return entries2;
|
|
7531
|
+
}
|
|
7532
|
+
var IDLE_GAP_MS4, INTERRUPT_RE2, PR_TITLE_RE, BASH_CHAIN_SPLIT_RE, SHELL_NOISE, IMAGE_BLOCK_RE;
|
|
6756
7533
|
var init_build = __esm({
|
|
6757
7534
|
"../entries/dist/build.js"() {
|
|
6758
7535
|
"use strict";
|
|
@@ -6761,6 +7538,11 @@ var init_build = __esm({
|
|
|
6761
7538
|
init_signals();
|
|
6762
7539
|
init_trivial();
|
|
6763
7540
|
IDLE_GAP_MS4 = 3 * 60 * 1e3;
|
|
7541
|
+
INTERRUPT_RE2 = /\[request interrupted|interrupted by user/i;
|
|
7542
|
+
PR_TITLE_RE = /--title\s+["']([^"']+)["']/;
|
|
7543
|
+
BASH_CHAIN_SPLIT_RE = /\|\||&&|;|\|/;
|
|
7544
|
+
SHELL_NOISE = /* @__PURE__ */ new Set(["sudo", "time", "nohup", "nice", "env", "exec"]);
|
|
7545
|
+
IMAGE_BLOCK_RE = /\[Image:\s*source:\s*[^\]]+\]/g;
|
|
6764
7546
|
}
|
|
6765
7547
|
});
|
|
6766
7548
|
|
|
@@ -6784,7 +7566,7 @@ var init_dist2 = __esm({
|
|
|
6784
7566
|
});
|
|
6785
7567
|
|
|
6786
7568
|
// ../entries/dist/digest-fs.js
|
|
6787
|
-
import { readFileSync as readFileSync8, readdirSync, writeFileSync as writeFileSync4, renameSync, chmodSync, mkdirSync as mkdirSync5, existsSync as existsSync6 } from "node:fs";
|
|
7569
|
+
import { readFileSync as readFileSync8, readdirSync, writeFileSync as writeFileSync4, renameSync as renameSync2, chmodSync, mkdirSync as mkdirSync5, existsSync as existsSync6 } from "node:fs";
|
|
6788
7570
|
import { dirname as dirname7, join as join7 } from "node:path";
|
|
6789
7571
|
function parseDigestFile(path7, expectedVersion) {
|
|
6790
7572
|
try {
|
|
@@ -6833,7 +7615,7 @@ function writeDayDigest(digest2) {
|
|
|
6833
7615
|
} catch {
|
|
6834
7616
|
}
|
|
6835
7617
|
}
|
|
6836
|
-
|
|
7618
|
+
renameSync2(tmp, final);
|
|
6837
7619
|
}
|
|
6838
7620
|
function readDayDigest(date) {
|
|
6839
7621
|
const p = dayDigestPath(date);
|
|
@@ -6869,7 +7651,7 @@ function writeWeekDigest(digest2) {
|
|
|
6869
7651
|
} catch {
|
|
6870
7652
|
}
|
|
6871
7653
|
}
|
|
6872
|
-
|
|
7654
|
+
renameSync2(tmp, final);
|
|
6873
7655
|
}
|
|
6874
7656
|
function readWeekDigest(monday) {
|
|
6875
7657
|
const p = weekDigestPath(monday);
|
|
@@ -6906,7 +7688,7 @@ function writeMonthDigest(digest2) {
|
|
|
6906
7688
|
} catch {
|
|
6907
7689
|
}
|
|
6908
7690
|
}
|
|
6909
|
-
|
|
7691
|
+
renameSync2(tmp, final);
|
|
6910
7692
|
}
|
|
6911
7693
|
function readMonthDigest(yearMonth) {
|
|
6912
7694
|
const p = monthDigestPath(yearMonth);
|
|
@@ -6955,6 +7737,7 @@ __export(fs_exports2, {
|
|
|
6955
7737
|
__setDigestsDirForTest: () => __setDigestsDirForTest,
|
|
6956
7738
|
__setEntriesDirForTest: () => __setEntriesDirForTest,
|
|
6957
7739
|
entriesDir: () => entriesDir,
|
|
7740
|
+
entryExists: () => entryExists,
|
|
6958
7741
|
getCurrentMonthDigestFromCache: () => getCurrentMonthDigestFromCache,
|
|
6959
7742
|
getCurrentWeekDigestFromCache: () => getCurrentWeekDigestFromCache,
|
|
6960
7743
|
getTodayDigestFromCache: () => getTodayDigestFromCache,
|
|
@@ -6977,7 +7760,7 @@ __export(fs_exports2, {
|
|
|
6977
7760
|
writeMonthDigest: () => writeMonthDigest,
|
|
6978
7761
|
writeWeekDigest: () => writeWeekDigest
|
|
6979
7762
|
});
|
|
6980
|
-
import { readFileSync as readFileSync9, readdirSync as readdirSync2, writeFileSync as writeFileSync5, mkdirSync as mkdirSync6, renameSync as
|
|
7763
|
+
import { readFileSync as readFileSync9, readdirSync as readdirSync2, writeFileSync as writeFileSync5, mkdirSync as mkdirSync6, renameSync as renameSync3, existsSync as existsSync7 } from "node:fs";
|
|
6981
7764
|
import { randomBytes } from "node:crypto";
|
|
6982
7765
|
import { join as join8 } from "node:path";
|
|
6983
7766
|
function entriesDir() {
|
|
@@ -6991,8 +7774,8 @@ function __setEntriesDirForTest(path7) {
|
|
|
6991
7774
|
entriesDirCached = path7;
|
|
6992
7775
|
mkdirSync6(path7, { recursive: true });
|
|
6993
7776
|
}
|
|
6994
|
-
function pathFor(sessionId,
|
|
6995
|
-
return join8(entriesDir(), `${entryKey(sessionId,
|
|
7777
|
+
function pathFor(sessionId, localDay2) {
|
|
7778
|
+
return join8(entriesDir(), `${entryKey(sessionId, localDay2)}.json`);
|
|
6996
7779
|
}
|
|
6997
7780
|
function tmpPathFor(final) {
|
|
6998
7781
|
return `${final}.tmp.${process.pid}.${randomBytes(4).toString("hex")}`;
|
|
@@ -7004,7 +7787,7 @@ function writeEntry(entry) {
|
|
|
7004
7787
|
const tmp = tmpPathFor(final);
|
|
7005
7788
|
const json = JSON.stringify(entry, null, 2);
|
|
7006
7789
|
writeFileSync5(tmp, json, { encoding: "utf8" });
|
|
7007
|
-
|
|
7790
|
+
renameSync3(tmp, final);
|
|
7008
7791
|
}
|
|
7009
7792
|
function enrichmentInputHash(e) {
|
|
7010
7793
|
return [
|
|
@@ -7032,26 +7815,29 @@ function writeEntryPreservingEnrichment(fresh) {
|
|
|
7032
7815
|
}
|
|
7033
7816
|
const tmp = tmpPathFor(final);
|
|
7034
7817
|
writeFileSync5(tmp, JSON.stringify(toWrite, null, 2), { encoding: "utf8" });
|
|
7035
|
-
|
|
7818
|
+
renameSync3(tmp, final);
|
|
7036
7819
|
}
|
|
7037
|
-
function readEntry(sessionId,
|
|
7038
|
-
const p = pathFor(sessionId,
|
|
7820
|
+
function readEntry(sessionId, localDay2) {
|
|
7821
|
+
const p = pathFor(sessionId, localDay2);
|
|
7039
7822
|
if (!existsSync7(p))
|
|
7040
7823
|
return null;
|
|
7041
7824
|
const raw = readFileSync9(p, "utf8");
|
|
7042
7825
|
return JSON.parse(raw);
|
|
7043
7826
|
}
|
|
7827
|
+
function entryExists(sessionId, localDay2) {
|
|
7828
|
+
return existsSync7(pathFor(sessionId, localDay2));
|
|
7829
|
+
}
|
|
7044
7830
|
function listEntryKeys() {
|
|
7045
7831
|
const dir = entriesDir();
|
|
7046
7832
|
if (!existsSync7(dir))
|
|
7047
7833
|
return [];
|
|
7048
7834
|
return readdirSync2(dir).filter((f) => f.endsWith(".json")).map((f) => f.slice(0, -".json".length));
|
|
7049
7835
|
}
|
|
7050
|
-
function listEntriesForDay(
|
|
7836
|
+
function listEntriesForDay(localDay2) {
|
|
7051
7837
|
const out = [];
|
|
7052
7838
|
for (const key of listEntryKeys()) {
|
|
7053
7839
|
const parsed = parseEntryKey(key);
|
|
7054
|
-
if (!parsed || parsed.local_day !==
|
|
7840
|
+
if (!parsed || parsed.local_day !== localDay2)
|
|
7055
7841
|
continue;
|
|
7056
7842
|
const e = readEntry(parsed.session_id, parsed.local_day);
|
|
7057
7843
|
if (e)
|
|
@@ -11220,7 +12006,7 @@ var init_zod = __esm({
|
|
|
11220
12006
|
});
|
|
11221
12007
|
|
|
11222
12008
|
// ../entries/dist/prompts/enrich.js
|
|
11223
|
-
function
|
|
12009
|
+
function trunc2(s, n) {
|
|
11224
12010
|
if (s.length <= n)
|
|
11225
12011
|
return s;
|
|
11226
12012
|
return s.slice(0, n - 1) + "\u2026";
|
|
@@ -11241,7 +12027,7 @@ function buildEnrichmentUserPrompt(entry, humanTurns) {
|
|
|
11241
12027
|
satisfaction_signals: entry.satisfaction_signals,
|
|
11242
12028
|
user_input_sources: entry.user_input_sources
|
|
11243
12029
|
};
|
|
11244
|
-
const turns = humanTurns.slice(0, 8).map((t, i) => `${i + 1}. ${
|
|
12030
|
+
const turns = humanTurns.slice(0, 8).map((t, i) => `${i + 1}. ${trunc2(t, 300)}`).join("\n");
|
|
11245
12031
|
return `SLICE FACTS:
|
|
11246
12032
|
${JSON.stringify(facts, null, 2)}
|
|
11247
12033
|
|
|
@@ -11322,7 +12108,7 @@ RESPOND WITH ONLY VALID JSON (no prose, no code fence):
|
|
|
11322
12108
|
});
|
|
11323
12109
|
|
|
11324
12110
|
// ../entries/dist/tmux-runner.js
|
|
11325
|
-
import { mkdirSync as mkdirSync7, writeFileSync as writeFileSync6, existsSync as existsSync8, readdirSync as readdirSync3, realpathSync as realpathSync3, readFileSync as readFileSync10, unlinkSync as unlinkSync3, renameSync as
|
|
12111
|
+
import { mkdirSync as mkdirSync7, writeFileSync as writeFileSync6, existsSync as existsSync8, readdirSync as readdirSync3, realpathSync as realpathSync3, readFileSync as readFileSync10, unlinkSync as unlinkSync3, renameSync as renameSync4 } from "node:fs";
|
|
11326
12112
|
import { join as join9 } from "node:path";
|
|
11327
12113
|
import { homedir as homedir4 } from "node:os";
|
|
11328
12114
|
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
@@ -11365,7 +12151,7 @@ function ensureRuntimeCwdTrusted(cwd) {
|
|
|
11365
12151
|
const tmp = `${cfgPath}.cclens-tmp-${process.pid}`;
|
|
11366
12152
|
try {
|
|
11367
12153
|
writeFileSync6(tmp, JSON.stringify(cfg, null, 2));
|
|
11368
|
-
|
|
12154
|
+
renameSync4(tmp, cfgPath);
|
|
11369
12155
|
} catch {
|
|
11370
12156
|
try {
|
|
11371
12157
|
unlinkSync3(tmp);
|
|
@@ -12308,7 +13094,7 @@ var init_queue = __esm({
|
|
|
12308
13094
|
});
|
|
12309
13095
|
|
|
12310
13096
|
// ../entries/dist/settings.js
|
|
12311
|
-
import { readFileSync as readFileSync13, writeFileSync as writeFileSync9, renameSync as
|
|
13097
|
+
import { readFileSync as readFileSync13, writeFileSync as writeFileSync9, renameSync as renameSync5, chmodSync as chmodSync2, mkdirSync as mkdirSync10, existsSync as existsSync12 } from "node:fs";
|
|
12312
13098
|
import { dirname as dirname9 } from "node:path";
|
|
12313
13099
|
function settingsPath() {
|
|
12314
13100
|
if (settingsPathCached)
|
|
@@ -12361,7 +13147,7 @@ function writeSettings(s) {
|
|
|
12361
13147
|
if (process.platform !== "win32") {
|
|
12362
13148
|
chmodSync2(tmp, 384);
|
|
12363
13149
|
}
|
|
12364
|
-
|
|
13150
|
+
renameSync5(tmp, p);
|
|
12365
13151
|
}
|
|
12366
13152
|
var DEFAULT_SETTINGS, settingsPathCached;
|
|
12367
13153
|
var init_settings = __esm({
|
|
@@ -12382,7 +13168,7 @@ var init_settings = __esm({
|
|
|
12382
13168
|
});
|
|
12383
13169
|
|
|
12384
13170
|
// ../entries/dist/prompts/digest-day.js
|
|
12385
|
-
function
|
|
13171
|
+
function trunc3(s, n) {
|
|
12386
13172
|
if (s.length <= n)
|
|
12387
13173
|
return s;
|
|
12388
13174
|
return s.slice(0, n - 1) + "\u2026";
|
|
@@ -12393,9 +13179,9 @@ function prettyProject(p) {
|
|
|
12393
13179
|
}
|
|
12394
13180
|
function buildDigestUserPrompt(base, entries2) {
|
|
12395
13181
|
const enriched = entries2.filter((e) => e.enrichment.status === "done");
|
|
12396
|
-
const summaries = enriched.slice(0, 12).map((e) => `- (${prettyProject(e.project)}, ${Math.round(e.numbers.active_min)}m) ${
|
|
12397
|
-
const frictions = enriched.map((e) => e.enrichment.friction_detail).filter((x) => !!x).slice(0, 6).map((f, i) => `${i + 1}. ${
|
|
12398
|
-
const instructions = enriched.flatMap((e) => e.enrichment.user_instructions).slice(0, 10).map((s, i) => `${i + 1}. ${
|
|
13182
|
+
const summaries = enriched.slice(0, 12).map((e) => `- (${prettyProject(e.project)}, ${Math.round(e.numbers.active_min)}m) ${trunc3(e.enrichment.brief_summary ?? "", 240)}`).join("\n");
|
|
13183
|
+
const frictions = enriched.map((e) => e.enrichment.friction_detail).filter((x) => !!x).slice(0, 6).map((f, i) => `${i + 1}. ${trunc3(f, 240)}`).join("\n");
|
|
13184
|
+
const instructions = enriched.flatMap((e) => e.enrichment.user_instructions).slice(0, 10).map((s, i) => `${i + 1}. ${trunc3(s, 240)}`).join("\n");
|
|
12399
13185
|
const per_entry = entries2.map((e) => ({
|
|
12400
13186
|
agent: e.agent ?? "claude-code",
|
|
12401
13187
|
project: prettyProject(e.project),
|
|
@@ -14425,7 +15211,7 @@ function activeMs(timestamps) {
|
|
|
14425
15211
|
}
|
|
14426
15212
|
return active;
|
|
14427
15213
|
}
|
|
14428
|
-
function
|
|
15214
|
+
function bashVerb2(cmd) {
|
|
14429
15215
|
if (!cmd)
|
|
14430
15216
|
return "?";
|
|
14431
15217
|
let firstSeg = cmd.split(/\|\||&&|;|\|/, 1)[0].trim();
|
|
@@ -14435,7 +15221,7 @@ function bashVerb(cmd) {
|
|
|
14435
15221
|
firstSeg = parts[1].trim();
|
|
14436
15222
|
}
|
|
14437
15223
|
const tokens = firstSeg.split(/\s+/).filter(Boolean);
|
|
14438
|
-
while (tokens.length &&
|
|
15224
|
+
while (tokens.length && SHELL_NOISE2.has(tokens[0]))
|
|
14439
15225
|
tokens.shift();
|
|
14440
15226
|
if (!tokens.length)
|
|
14441
15227
|
return "?";
|
|
@@ -14508,7 +15294,7 @@ function eventsToTurns(events, windowStartMs, windowEndMs) {
|
|
|
14508
15294
|
const isToolOnly = blocks.length > 0 && blocks.every((c) => c.type === "tool_result");
|
|
14509
15295
|
if (!isToolOnly) {
|
|
14510
15296
|
const text = blocks.filter((b) => b.type === "text").map((b) => b.text).join(" ");
|
|
14511
|
-
const interrupted =
|
|
15297
|
+
const interrupted = INTERRUPT_RE3.test(text);
|
|
14512
15298
|
if (cur && interrupted)
|
|
14513
15299
|
cur.interrupts += 1;
|
|
14514
15300
|
if (cur)
|
|
@@ -14562,10 +15348,10 @@ function eventsToTurns(events, windowStartMs, windowEndMs) {
|
|
|
14562
15348
|
cur.todo_ops += 1;
|
|
14563
15349
|
} else if (name === "Bash") {
|
|
14564
15350
|
const cmd = String(ct.input?.command ?? "");
|
|
14565
|
-
const v =
|
|
15351
|
+
const v = bashVerb2(cmd);
|
|
14566
15352
|
cur.bash_verbs.set(v, (cur.bash_verbs.get(v) ?? 0) + 1);
|
|
14567
15353
|
if (/\bgh\s+pr\s+create\b/.test(cmd)) {
|
|
14568
|
-
const m = cmd.match(
|
|
15354
|
+
const m = cmd.match(PR_TITLE_RE2);
|
|
14569
15355
|
cur.pr_created = m?.[1] ?? cmd.slice(0, 120);
|
|
14570
15356
|
}
|
|
14571
15357
|
}
|
|
@@ -14988,7 +15774,7 @@ async function generateTopSession(slice, opts = {}) {
|
|
|
14988
15774
|
void lastModel;
|
|
14989
15775
|
}
|
|
14990
15776
|
}
|
|
14991
|
-
var IDLE_GAP_MS5, LONG_AUTONOMOUS_MIN, N_USER, N_FIRST_AGENT, N_LAST_AGENT, N_USER_TURN1, N_SUBAGENT,
|
|
15777
|
+
var IDLE_GAP_MS5, LONG_AUTONOMOUS_MIN, N_USER, N_FIRST_AGENT, N_LAST_AGENT, N_USER_TURN1, N_SUBAGENT, SHELL_NOISE2, INTERRUPT_RE3, PR_TITLE_RE2, PinResponseSchema, TopSessionResponseSchema, SYSTEM_PROMPT5, TOP_SESSION_SYSTEM_PROMPT, DEFAULT_MODEL5, defaultCallLLMTopSession, validateTopSession;
|
|
14992
15778
|
var init_top_sessions = __esm({
|
|
14993
15779
|
"../entries/dist/top-sessions.js"() {
|
|
14994
15780
|
"use strict";
|
|
@@ -15002,9 +15788,9 @@ var init_top_sessions = __esm({
|
|
|
15002
15788
|
N_LAST_AGENT = 200;
|
|
15003
15789
|
N_USER_TURN1 = 400;
|
|
15004
15790
|
N_SUBAGENT = 240;
|
|
15005
|
-
|
|
15006
|
-
|
|
15007
|
-
|
|
15791
|
+
SHELL_NOISE2 = /* @__PURE__ */ new Set(["sudo", "time", "nohup", "nice", "env", "exec"]);
|
|
15792
|
+
INTERRUPT_RE3 = /\[request interrupted|interrupted by user/i;
|
|
15793
|
+
PR_TITLE_RE2 = /--title\s+["']([^"']+)["']/;
|
|
15008
15794
|
PinResponseSchema = external_exports.object({
|
|
15009
15795
|
start_min: external_exports.number(),
|
|
15010
15796
|
end_min: external_exports.number().optional(),
|
|
@@ -15119,8 +15905,8 @@ VOCABULARY:
|
|
|
15119
15905
|
});
|
|
15120
15906
|
|
|
15121
15907
|
// ../entries/dist/digest-week-pipeline.js
|
|
15122
|
-
function mondayFor(
|
|
15123
|
-
const d = /* @__PURE__ */ new Date(`${
|
|
15908
|
+
function mondayFor(localDay2) {
|
|
15909
|
+
const d = /* @__PURE__ */ new Date(`${localDay2}T00:00:00`);
|
|
15124
15910
|
const dow = d.getDay();
|
|
15125
15911
|
const offset = dow === 0 ? -6 : 1 - dow;
|
|
15126
15912
|
d.setDate(d.getDate() + offset);
|
|
@@ -15796,8 +16582,8 @@ async function digest(args2) {
|
|
|
15796
16582
|
printHelp2();
|
|
15797
16583
|
process.exit(1);
|
|
15798
16584
|
}
|
|
15799
|
-
function mondayOf(
|
|
15800
|
-
const d = /* @__PURE__ */ new Date(`${
|
|
16585
|
+
function mondayOf(localDay2) {
|
|
16586
|
+
const d = /* @__PURE__ */ new Date(`${localDay2}T00:00:00`);
|
|
15801
16587
|
const dow = d.getDay();
|
|
15802
16588
|
const offset = dow === 0 ? -6 : 1 - dow;
|
|
15803
16589
|
d.setDate(d.getDate() + offset);
|
|
@@ -16097,6 +16883,52 @@ var init_config = __esm({
|
|
|
16097
16883
|
}
|
|
16098
16884
|
});
|
|
16099
16885
|
|
|
16886
|
+
// src/perception/build-entries.ts
|
|
16887
|
+
import { statSync as statSync3, readFileSync as readFileSync14 } from "node:fs";
|
|
16888
|
+
import { basename, dirname as dirname10 } from "node:path";
|
|
16889
|
+
function decodeProjectDirName(projectDir) {
|
|
16890
|
+
if (projectDir.startsWith("-")) {
|
|
16891
|
+
return "/" + projectDir.slice(1).replace(/-/g, "/");
|
|
16892
|
+
}
|
|
16893
|
+
return projectDir.replace(/-/g, "/");
|
|
16894
|
+
}
|
|
16895
|
+
function buildEntriesForFile(filePath) {
|
|
16896
|
+
const stat = statSync3(filePath);
|
|
16897
|
+
const raw = readFileSync14(filePath, "utf8");
|
|
16898
|
+
const rawLines = raw.split("\n").filter(Boolean).map((l) => {
|
|
16899
|
+
try {
|
|
16900
|
+
return JSON.parse(l);
|
|
16901
|
+
} catch {
|
|
16902
|
+
return null;
|
|
16903
|
+
}
|
|
16904
|
+
}).filter((x) => x !== null);
|
|
16905
|
+
if (rawLines.length === 0) return null;
|
|
16906
|
+
const { meta, events } = parseTranscript(rawLines);
|
|
16907
|
+
const fileName = basename(filePath);
|
|
16908
|
+
const projectDir = basename(dirname10(filePath));
|
|
16909
|
+
const sessionId = fileName.replace(/\.jsonl$/, "");
|
|
16910
|
+
const sd = {
|
|
16911
|
+
...meta,
|
|
16912
|
+
id: sessionId,
|
|
16913
|
+
filePath,
|
|
16914
|
+
projectDir,
|
|
16915
|
+
projectName: meta.cwd ?? decodeProjectDirName(projectDir),
|
|
16916
|
+
events
|
|
16917
|
+
};
|
|
16918
|
+
const built = buildEntries(sd);
|
|
16919
|
+
for (const e of built) {
|
|
16920
|
+
e.source_checkpoint.byte_offset = stat.size;
|
|
16921
|
+
}
|
|
16922
|
+
return built;
|
|
16923
|
+
}
|
|
16924
|
+
var init_build_entries = __esm({
|
|
16925
|
+
"src/perception/build-entries.ts"() {
|
|
16926
|
+
"use strict";
|
|
16927
|
+
init_dist();
|
|
16928
|
+
init_dist2();
|
|
16929
|
+
}
|
|
16930
|
+
});
|
|
16931
|
+
|
|
16100
16932
|
// src/team/push.ts
|
|
16101
16933
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
16102
16934
|
function readLatestUsageSnapshotForWire(filePath, nowMs = Date.now()) {
|
|
@@ -16157,6 +16989,10 @@ function clipSegmentsToDay(segments, bounds) {
|
|
|
16157
16989
|
}
|
|
16158
16990
|
return out;
|
|
16159
16991
|
}
|
|
16992
|
+
function filterSyncedSessions(sessions, syncProjects) {
|
|
16993
|
+
if (!syncProjects) return sessions;
|
|
16994
|
+
return sessions.filter((s) => shouldSyncProject(projectRepoName(s.projectName), syncProjects));
|
|
16995
|
+
}
|
|
16160
16996
|
function sessionTouchesDay(s, day2) {
|
|
16161
16997
|
const bounds = dayBoundsMs(day2);
|
|
16162
16998
|
if (s.activeSegments && s.activeSegments.length > 0) {
|
|
@@ -16164,7 +17000,7 @@ function sessionTouchesDay(s, day2) {
|
|
|
16164
17000
|
}
|
|
16165
17001
|
return sessionDay(s) === day2;
|
|
16166
17002
|
}
|
|
16167
|
-
function buildRichRollupBlocks(day2, daySessions, entries2,
|
|
17003
|
+
function buildRichRollupBlocks(day2, daySessions, entries2, resolveRepo) {
|
|
16168
17004
|
const bounds = dayBoundsMs(day2);
|
|
16169
17005
|
const clippedSessions = [];
|
|
16170
17006
|
for (const s of daySessions) {
|
|
@@ -16190,7 +17026,6 @@ function buildRichRollupBlocks(day2, daySessions, entries2, privateProjects, res
|
|
|
16190
17026
|
};
|
|
16191
17027
|
const sessionRepo = /* @__PURE__ */ new Map();
|
|
16192
17028
|
for (const e of entries2) {
|
|
16193
|
-
if (privateProjects.has(projectRepoName(e.project)) || privateProjects.has(e.project)) continue;
|
|
16194
17029
|
const r = repoForEntry(e);
|
|
16195
17030
|
if (r) sessionRepo.set(e.session_id, r);
|
|
16196
17031
|
}
|
|
@@ -16198,7 +17033,6 @@ function buildRichRollupBlocks(day2, daySessions, entries2, privateProjects, res
|
|
|
16198
17033
|
for (const s of clippedSessions) {
|
|
16199
17034
|
const canonical = canonicalProjectName(s.projectName);
|
|
16200
17035
|
const name = projectRepoName(s.projectName);
|
|
16201
|
-
if (privateProjects.has(name) || privateProjects.has(canonical)) continue;
|
|
16202
17036
|
const repo = resolveRepo?.(canonical) ?? sessionRepo.get(s.id);
|
|
16203
17037
|
const key = repo ?? name;
|
|
16204
17038
|
const ms = s.activeSegments.reduce((sum, seg) => sum + (seg.endMs - seg.startMs), 0);
|
|
@@ -16221,7 +17055,6 @@ function buildRichRollupBlocks(day2, daySessions, entries2, privateProjects, res
|
|
|
16221
17055
|
let longTotalMin = 0;
|
|
16222
17056
|
let longMaxSingleMin = 0;
|
|
16223
17057
|
for (const e of entries2) {
|
|
16224
|
-
if (privateProjects.has(projectRepoName(e.project)) || privateProjects.has(e.project)) continue;
|
|
16225
17058
|
const shape = e.signals?.working_shape ?? null;
|
|
16226
17059
|
if (shape) {
|
|
16227
17060
|
const cur = workingShapes.get(shape) ?? { sessions: 0, agentTimeMs: 0 };
|
|
@@ -16297,19 +17130,36 @@ function buildEnrichedExtras(entries2) {
|
|
|
16297
17130
|
}
|
|
16298
17131
|
return { outcomeMix, helpfulnessMix, goalMix };
|
|
16299
17132
|
}
|
|
16300
|
-
function
|
|
16301
|
-
|
|
17133
|
+
function resetEnsuredSessions() {
|
|
17134
|
+
ensuredSessions.clear();
|
|
17135
|
+
}
|
|
17136
|
+
function ensureEntriesForDay(day2, daySessions) {
|
|
17137
|
+
for (const s of daySessions) {
|
|
17138
|
+
if (ensuredSessions.has(s.id)) continue;
|
|
17139
|
+
if ((s.agent ?? "claude-code") !== "claude-code") continue;
|
|
17140
|
+
if (entryExists(s.id, day2)) continue;
|
|
17141
|
+
ensuredSessions.add(s.id);
|
|
17142
|
+
try {
|
|
17143
|
+
for (const e of buildEntriesForFile(s.filePath) ?? []) writeEntryPreservingEnrichment(e);
|
|
17144
|
+
} catch {
|
|
17145
|
+
}
|
|
17146
|
+
}
|
|
17147
|
+
}
|
|
17148
|
+
function buildRichBlocksForDay(day2, daySessions, resolveRepo) {
|
|
17149
|
+
ensureEntriesForDay(day2, daySessions);
|
|
17150
|
+
const sessionIds = new Set(daySessions.map((s) => s.id));
|
|
17151
|
+
const entries2 = listEntriesForDay(day2).filter((e) => sessionIds.has(e.session_id));
|
|
16302
17152
|
if (entries2.length === 0) return void 0;
|
|
16303
|
-
const rich = buildRichRollupBlocks(day2, daySessions, entries2,
|
|
16304
|
-
const enriched =
|
|
16305
|
-
return
|
|
17153
|
+
const rich = buildRichRollupBlocks(day2, daySessions, entries2, resolveRepo);
|
|
17154
|
+
const enriched = buildEnrichedExtras(entries2);
|
|
17155
|
+
return { rich, enriched };
|
|
16306
17156
|
}
|
|
16307
17157
|
function buildIngestPayload(inputs) {
|
|
16308
17158
|
const richRollup = inputs.rollup && inputs.richExtras ? { ...inputs.rollup, ...inputs.richExtras } : void 0;
|
|
16309
17159
|
return {
|
|
16310
17160
|
ingestId: randomUUID2(),
|
|
16311
17161
|
observedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
16312
|
-
cliVersion: "0.
|
|
17162
|
+
cliVersion: "0.15.0",
|
|
16313
17163
|
...richRollup || inputs.enrichedExtras || inputs.artifactSignals ? { schemaVersion: RICH_ROLLUP_SCHEMA_VERSION } : {},
|
|
16314
17164
|
...inputs.rollup ? { dailyRollup: inputs.rollup } : {},
|
|
16315
17165
|
...richRollup ? { richRollup } : {},
|
|
@@ -16317,7 +17167,8 @@ function buildIngestPayload(inputs) {
|
|
|
16317
17167
|
...inputs.artifactSignals ? { artifactSignals: inputs.artifactSignals } : {},
|
|
16318
17168
|
...inputs.usageSnapshot ? { usageSnapshot: inputs.usageSnapshot } : {},
|
|
16319
17169
|
...inputs.planTier ? { planTier: inputs.planTier } : {},
|
|
16320
|
-
...inputs.cyclePeaks ? { cyclePeaks: inputs.cyclePeaks } : {}
|
|
17170
|
+
...inputs.cyclePeaks ? { cyclePeaks: inputs.cyclePeaks } : {},
|
|
17171
|
+
...inputs.syncLog && inputs.syncLog.length ? { syncLog: inputs.syncLog } : {}
|
|
16321
17172
|
};
|
|
16322
17173
|
}
|
|
16323
17174
|
async function pushToTeamServer(config, payload) {
|
|
@@ -16333,22 +17184,24 @@ async function pushToTeamServer(config, payload) {
|
|
|
16333
17184
|
const body = await res.json().catch(() => null);
|
|
16334
17185
|
return { ok: res.ok, status: res.status, body };
|
|
16335
17186
|
}
|
|
16336
|
-
var SNAPSHOT_FRESHNESS_MS, POST_TIMEOUT_MS;
|
|
17187
|
+
var SNAPSHOT_FRESHNESS_MS, POST_TIMEOUT_MS, ensuredSessions;
|
|
16337
17188
|
var init_push = __esm({
|
|
16338
17189
|
"src/team/push.ts"() {
|
|
16339
17190
|
"use strict";
|
|
16340
17191
|
init_dist();
|
|
16341
17192
|
init_fs();
|
|
16342
17193
|
init_fs2();
|
|
17194
|
+
init_build_entries();
|
|
16343
17195
|
init_storage();
|
|
16344
17196
|
SNAPSHOT_FRESHNESS_MS = 10 * 60 * 1e3;
|
|
16345
17197
|
POST_TIMEOUT_MS = 15e3;
|
|
17198
|
+
ensuredSessions = /* @__PURE__ */ new Set();
|
|
16346
17199
|
}
|
|
16347
17200
|
});
|
|
16348
17201
|
|
|
16349
17202
|
// src/team/git-remote.ts
|
|
16350
|
-
import { existsSync as existsSync13, readFileSync as
|
|
16351
|
-
import { dirname as
|
|
17203
|
+
import { existsSync as existsSync13, readFileSync as readFileSync15, statSync as statSync4 } from "node:fs";
|
|
17204
|
+
import { dirname as dirname11, isAbsolute, join as join11, resolve } from "node:path";
|
|
16352
17205
|
function parseGithubRemote(url) {
|
|
16353
17206
|
const m = url.trim().match(GITHUB_URL_RE);
|
|
16354
17207
|
return m ? `${m[1]}/${m[2]}`.toLowerCase() : null;
|
|
@@ -16369,15 +17222,15 @@ function originUrl(configText) {
|
|
|
16369
17222
|
}
|
|
16370
17223
|
function gitConfigPathFor(dir) {
|
|
16371
17224
|
const dotGit = join11(dir, ".git");
|
|
16372
|
-
const st =
|
|
17225
|
+
const st = statSync4(dotGit, { throwIfNoEntry: false });
|
|
16373
17226
|
if (!st) return null;
|
|
16374
17227
|
if (st.isDirectory()) return join11(dotGit, "config");
|
|
16375
|
-
const m =
|
|
17228
|
+
const m = readFileSync15(dotGit, "utf8").match(/^gitdir:\s*(.+)\s*$/m);
|
|
16376
17229
|
if (!m) return null;
|
|
16377
17230
|
const gd = isAbsolute(m[1]) ? m[1] : resolve(dir, m[1]);
|
|
16378
17231
|
const commondirFile = join11(gd, "commondir");
|
|
16379
17232
|
if (existsSync13(commondirFile)) {
|
|
16380
|
-
const common =
|
|
17233
|
+
const common = readFileSync15(commondirFile, "utf8").trim();
|
|
16381
17234
|
return join11(isAbsolute(common) ? common : resolve(gd, common), "config");
|
|
16382
17235
|
}
|
|
16383
17236
|
return join11(gd, "config");
|
|
@@ -16390,14 +17243,14 @@ function createRepoResolver() {
|
|
|
16390
17243
|
if (hit !== void 0) return hit;
|
|
16391
17244
|
let repo = null;
|
|
16392
17245
|
try {
|
|
16393
|
-
for (let d = dir; ; d =
|
|
17246
|
+
for (let d = dir; ; d = dirname11(d)) {
|
|
16394
17247
|
const config = gitConfigPathFor(d);
|
|
16395
17248
|
if (config && existsSync13(config)) {
|
|
16396
|
-
const url = originUrl(
|
|
17249
|
+
const url = originUrl(readFileSync15(config, "utf8"));
|
|
16397
17250
|
repo = url ? parseGithubRemote(url) : null;
|
|
16398
17251
|
break;
|
|
16399
17252
|
}
|
|
16400
|
-
if (d ===
|
|
17253
|
+
if (d === dirname11(d)) break;
|
|
16401
17254
|
}
|
|
16402
17255
|
} catch {
|
|
16403
17256
|
repo = null;
|
|
@@ -16415,14 +17268,14 @@ var init_git_remote = __esm({
|
|
|
16415
17268
|
});
|
|
16416
17269
|
|
|
16417
17270
|
// src/team/queue.ts
|
|
16418
|
-
import { readFileSync as
|
|
16419
|
-
import { dirname as
|
|
17271
|
+
import { readFileSync as readFileSync16, writeFileSync as writeFileSync10, appendFileSync as appendFileSync4, statSync as statSync5, mkdirSync as mkdirSync11 } from "node:fs";
|
|
17272
|
+
import { dirname as dirname12 } from "node:path";
|
|
16420
17273
|
function enqueuePayload(payload, queuePath = QUEUE_PATH) {
|
|
16421
|
-
mkdirSync11(
|
|
17274
|
+
mkdirSync11(dirname12(queuePath), { recursive: true });
|
|
16422
17275
|
const entry = { payload, enqueuedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
16423
17276
|
appendFileSync4(queuePath, JSON.stringify(entry) + "\n", "utf8");
|
|
16424
17277
|
try {
|
|
16425
|
-
const { size } =
|
|
17278
|
+
const { size } = statSync5(queuePath);
|
|
16426
17279
|
if (size > MAX_SIZE_BYTES) {
|
|
16427
17280
|
pruneOldest(queuePath);
|
|
16428
17281
|
}
|
|
@@ -16432,7 +17285,7 @@ function enqueuePayload(payload, queuePath = QUEUE_PATH) {
|
|
|
16432
17285
|
function dequeuePayloads(queuePath = QUEUE_PATH) {
|
|
16433
17286
|
let lines;
|
|
16434
17287
|
try {
|
|
16435
|
-
lines =
|
|
17288
|
+
lines = readFileSync16(queuePath, "utf8").trim().split("\n").filter(Boolean);
|
|
16436
17289
|
} catch {
|
|
16437
17290
|
return [];
|
|
16438
17291
|
}
|
|
@@ -16453,7 +17306,7 @@ function dequeuePayloads(queuePath = QUEUE_PATH) {
|
|
|
16453
17306
|
function pruneOldest(queuePath) {
|
|
16454
17307
|
let lines;
|
|
16455
17308
|
try {
|
|
16456
|
-
lines =
|
|
17309
|
+
lines = readFileSync16(queuePath, "utf8").trim().split("\n").filter(Boolean);
|
|
16457
17310
|
} catch {
|
|
16458
17311
|
return;
|
|
16459
17312
|
}
|
|
@@ -16472,8 +17325,8 @@ var init_queue2 = __esm({
|
|
|
16472
17325
|
});
|
|
16473
17326
|
|
|
16474
17327
|
// src/usage/profile.ts
|
|
16475
|
-
import { existsSync as existsSync14, mkdirSync as mkdirSync12, readFileSync as
|
|
16476
|
-
import { dirname as
|
|
17328
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync12, readFileSync as readFileSync17, writeFileSync as writeFileSync11 } from "node:fs";
|
|
17329
|
+
import { dirname as dirname13 } from "node:path";
|
|
16477
17330
|
function mapRateLimitTier(rateLimitTier) {
|
|
16478
17331
|
switch (rateLimitTier) {
|
|
16479
17332
|
case "default_claude_pro":
|
|
@@ -16515,7 +17368,7 @@ async function fetchProfile(nowMs = Date.now()) {
|
|
|
16515
17368
|
function readCachedProfile(cachePath) {
|
|
16516
17369
|
if (!existsSync14(cachePath)) return null;
|
|
16517
17370
|
try {
|
|
16518
|
-
const raw = JSON.parse(
|
|
17371
|
+
const raw = JSON.parse(readFileSync17(cachePath, "utf8"));
|
|
16519
17372
|
if (typeof raw.fetchedAtMs !== "number" || !raw.profile) return null;
|
|
16520
17373
|
return raw;
|
|
16521
17374
|
} catch {
|
|
@@ -16523,7 +17376,7 @@ function readCachedProfile(cachePath) {
|
|
|
16523
17376
|
}
|
|
16524
17377
|
}
|
|
16525
17378
|
function writeCachedProfile(cachePath, profile, nowMs = Date.now()) {
|
|
16526
|
-
mkdirSync12(
|
|
17379
|
+
mkdirSync12(dirname13(cachePath), { recursive: true });
|
|
16527
17380
|
writeFileSync11(
|
|
16528
17381
|
cachePath,
|
|
16529
17382
|
JSON.stringify({ fetchedAtMs: nowMs, profile }, null, 2),
|
|
@@ -16703,6 +17556,38 @@ var init_backfill = __esm({
|
|
|
16703
17556
|
}
|
|
16704
17557
|
});
|
|
16705
17558
|
|
|
17559
|
+
// src/team/sync-log.ts
|
|
17560
|
+
import { readFileSync as readFileSync18 } from "node:fs";
|
|
17561
|
+
function readPendingSyncLog(watermark) {
|
|
17562
|
+
let raw;
|
|
17563
|
+
try {
|
|
17564
|
+
raw = readFileSync18(cclensPath("daemon.log"), "utf8");
|
|
17565
|
+
} catch {
|
|
17566
|
+
return { lines: [] };
|
|
17567
|
+
}
|
|
17568
|
+
const lines = [];
|
|
17569
|
+
for (const line of raw.split("\n")) {
|
|
17570
|
+
const m = line.match(/^(\S+) (INFO|WARN|ERROR) (.*)$/);
|
|
17571
|
+
if (!m) continue;
|
|
17572
|
+
const [, ts, levelRaw, msg] = m;
|
|
17573
|
+
if (!msg.startsWith("[sync] ")) continue;
|
|
17574
|
+
if (watermark && ts <= watermark) continue;
|
|
17575
|
+
const clipped = msg.length > MAX_MSG_CHARS ? msg.slice(0, MAX_MSG_CHARS) + "\u2026" : msg;
|
|
17576
|
+
lines.push({ ts, level: levelRaw.toLowerCase(), msg: clipped });
|
|
17577
|
+
}
|
|
17578
|
+
const capped = lines.slice(0, MAX_LINES);
|
|
17579
|
+
return { lines: capped, watermark: capped.length ? capped[capped.length - 1].ts : void 0 };
|
|
17580
|
+
}
|
|
17581
|
+
var MAX_LINES, MAX_MSG_CHARS;
|
|
17582
|
+
var init_sync_log = __esm({
|
|
17583
|
+
"src/team/sync-log.ts"() {
|
|
17584
|
+
"use strict";
|
|
17585
|
+
init_fs();
|
|
17586
|
+
MAX_LINES = 300;
|
|
17587
|
+
MAX_MSG_CHARS = 1900;
|
|
17588
|
+
}
|
|
17589
|
+
});
|
|
17590
|
+
|
|
16706
17591
|
// src/team/last-push.ts
|
|
16707
17592
|
import { writeFileSync as writeFileSync12, unlinkSync as unlinkSync5, mkdirSync as mkdirSync13 } from "node:fs";
|
|
16708
17593
|
import { join as join12 } from "node:path";
|
|
@@ -16740,7 +17625,7 @@ __export(file_probe_exports, {
|
|
|
16740
17625
|
probeArtifactSignals: () => probeArtifactSignals
|
|
16741
17626
|
});
|
|
16742
17627
|
import { createHash } from "node:crypto";
|
|
16743
|
-
import { existsSync as existsSync15, readdirSync as readdirSync4, statSync as
|
|
17628
|
+
import { existsSync as existsSync15, readdirSync as readdirSync4, statSync as statSync6 } from "node:fs";
|
|
16744
17629
|
import { homedir as homedir5 } from "node:os";
|
|
16745
17630
|
import { join as join13, relative, resolve as resolve2, sep } from "node:path";
|
|
16746
17631
|
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
@@ -16771,7 +17656,7 @@ function listMarkdownFiles(dir) {
|
|
|
16771
17656
|
const full = join13(dir, name);
|
|
16772
17657
|
let stat;
|
|
16773
17658
|
try {
|
|
16774
|
-
stat =
|
|
17659
|
+
stat = statSync6(full);
|
|
16775
17660
|
} catch {
|
|
16776
17661
|
continue;
|
|
16777
17662
|
}
|
|
@@ -16788,19 +17673,19 @@ function firstSeenDateOf(file) {
|
|
|
16788
17673
|
const out = execFileSync2(
|
|
16789
17674
|
"git",
|
|
16790
17675
|
["log", "--diff-filter=A", "--follow", "--format=%cs", "--", file],
|
|
16791
|
-
{ encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], cwd:
|
|
17676
|
+
{ encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], cwd: dirname14(file) }
|
|
16792
17677
|
).trim().split("\n").filter(Boolean);
|
|
16793
17678
|
if (out.length > 0) return out[out.length - 1];
|
|
16794
17679
|
} catch {
|
|
16795
17680
|
}
|
|
16796
17681
|
try {
|
|
16797
|
-
const s =
|
|
16798
|
-
return
|
|
17682
|
+
const s = statSync6(file);
|
|
17683
|
+
return localDay(s.birthtime || s.mtime);
|
|
16799
17684
|
} catch {
|
|
16800
|
-
return (
|
|
17685
|
+
return todayLocal();
|
|
16801
17686
|
}
|
|
16802
17687
|
}
|
|
16803
|
-
function
|
|
17688
|
+
function dirname14(p) {
|
|
16804
17689
|
const i = p.lastIndexOf(sep);
|
|
16805
17690
|
return i < 0 ? "." : p.slice(0, i);
|
|
16806
17691
|
}
|
|
@@ -16815,13 +17700,15 @@ function detectGitEmail(cwd) {
|
|
|
16815
17700
|
return null;
|
|
16816
17701
|
}
|
|
16817
17702
|
}
|
|
16818
|
-
function
|
|
16819
|
-
const d = /* @__PURE__ */ new Date();
|
|
17703
|
+
function localDay(d) {
|
|
16820
17704
|
const y = d.getFullYear();
|
|
16821
17705
|
const m = String(d.getMonth() + 1).padStart(2, "0");
|
|
16822
17706
|
const day2 = String(d.getDate()).padStart(2, "0");
|
|
16823
17707
|
return `${y}-${m}-${day2}`;
|
|
16824
17708
|
}
|
|
17709
|
+
function todayLocal() {
|
|
17710
|
+
return localDay(/* @__PURE__ */ new Date());
|
|
17711
|
+
}
|
|
16825
17712
|
function scanAuthored(roots, subdir, day2) {
|
|
16826
17713
|
const out = [];
|
|
16827
17714
|
for (const r of roots) {
|
|
@@ -16846,11 +17733,11 @@ function scanEdited(roots, day2) {
|
|
|
16846
17733
|
if (firstSeen === day2) continue;
|
|
16847
17734
|
let stat;
|
|
16848
17735
|
try {
|
|
16849
|
-
stat =
|
|
17736
|
+
stat = statSync6(f);
|
|
16850
17737
|
} catch {
|
|
16851
17738
|
continue;
|
|
16852
17739
|
}
|
|
16853
|
-
const mtimeDay =
|
|
17740
|
+
const mtimeDay = localDay(stat.mtime);
|
|
16854
17741
|
if (mtimeDay !== day2) continue;
|
|
16855
17742
|
const rel = relative(dir, f);
|
|
16856
17743
|
out.push({ pathHash: pathHash(`${r.label}:skills:${rel}`), lineDelta: 0 });
|
|
@@ -16872,7 +17759,7 @@ function claudemdLineDelta(extraRoots, authorEmail, day2) {
|
|
|
16872
17759
|
const out = execFileSync2(
|
|
16873
17760
|
"git",
|
|
16874
17761
|
["log", "--numstat", "--format=COMMIT %cs %ae", "--", file],
|
|
16875
|
-
{ encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], cwd:
|
|
17762
|
+
{ encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], cwd: dirname14(file) }
|
|
16876
17763
|
);
|
|
16877
17764
|
let currentDate = "";
|
|
16878
17765
|
let currentEmail = "";
|
|
@@ -16936,8 +17823,8 @@ async function dispatchCommand(command2, config, log) {
|
|
|
16936
17823
|
}
|
|
16937
17824
|
}
|
|
16938
17825
|
async function runActivityBackfill(command2, config, log) {
|
|
16939
|
-
const { listSessions: listSessions2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
|
|
16940
|
-
const { toLocalDay: toLocalDay4 } = await Promise.resolve().then(() => (init_dist(), dist_exports));
|
|
17826
|
+
const { listSessions: listSessions2, shouldSyncProject: shouldSyncProject2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
|
|
17827
|
+
const { toLocalDay: toLocalDay4, projectRepoName: projectRepoName2 } = await Promise.resolve().then(() => (init_dist(), dist_exports));
|
|
16941
17828
|
const { probeArtifactSignals: probeArtifactSignals2 } = await Promise.resolve().then(() => (init_file_probe(), file_probe_exports));
|
|
16942
17829
|
const days = command2.params.days;
|
|
16943
17830
|
const todayMs = Date.now();
|
|
@@ -16946,7 +17833,7 @@ async function runActivityBackfill(command2, config, log) {
|
|
|
16946
17833
|
log("info", `command ${command2.id}: backfill-activity from ${targetDay} (${days} days)`);
|
|
16947
17834
|
let sessions;
|
|
16948
17835
|
try {
|
|
16949
|
-
sessions = await listSessions2({ limit: 1e4 });
|
|
17836
|
+
sessions = filterSyncedSessions(await listSessions2({ limit: 1e4 }), config.syncProjects);
|
|
16950
17837
|
} catch (err) {
|
|
16951
17838
|
return {
|
|
16952
17839
|
id: command2.id,
|
|
@@ -16964,16 +17851,15 @@ async function runActivityBackfill(command2, config, log) {
|
|
|
16964
17851
|
summary: { pushed: 0, fromDay: targetDay }
|
|
16965
17852
|
};
|
|
16966
17853
|
}
|
|
16967
|
-
const privateProjects = new Set(config.privateProjects ?? []);
|
|
16968
|
-
const enrichmentOptIn = !!config.enrichmentOptIn;
|
|
16969
17854
|
const resolveRepo = createRepoResolver();
|
|
16970
17855
|
let pushed = 0;
|
|
16971
17856
|
for (const rollup of rollups) {
|
|
16972
17857
|
const daySessions = sessions.filter((s) => sessionTouchesDay(s, rollup.day));
|
|
16973
|
-
const richBlocks = buildRichBlocksForDay(rollup.day, daySessions,
|
|
17858
|
+
const richBlocks = buildRichBlocksForDay(rollup.day, daySessions, resolveRepo);
|
|
16974
17859
|
let artifactSignals = null;
|
|
16975
17860
|
try {
|
|
16976
|
-
|
|
17861
|
+
const cwdAllowed = shouldSyncProject2(projectRepoName2(process.cwd()), config.syncProjects);
|
|
17862
|
+
artifactSignals = probeArtifactSignals2({ day: rollup.day, extraRoots: cwdAllowed ? [process.cwd()] : [] });
|
|
16977
17863
|
} catch {
|
|
16978
17864
|
}
|
|
16979
17865
|
const payload = buildIngestPayload({
|
|
@@ -17012,21 +17898,128 @@ var init_commands = __esm({
|
|
|
17012
17898
|
// src/team/sync.ts
|
|
17013
17899
|
var sync_exports = {};
|
|
17014
17900
|
__export(sync_exports, {
|
|
17901
|
+
buildSyncLine: () => buildSyncLine,
|
|
17015
17902
|
runTeamSync: () => runTeamSync
|
|
17016
17903
|
});
|
|
17017
17904
|
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
17905
|
+
function syncLogDelivered(body) {
|
|
17906
|
+
const blocks = body?.blocks;
|
|
17907
|
+
return !blocks || blocks.accepted.includes("syncLog");
|
|
17908
|
+
}
|
|
17909
|
+
function levelForStatus(s) {
|
|
17910
|
+
if (s === "ok" || s === "idle") return "info";
|
|
17911
|
+
if (s === "degraded" || s === "failed") return "warn";
|
|
17912
|
+
return "error";
|
|
17913
|
+
}
|
|
17914
|
+
function fmtDuration(ms) {
|
|
17915
|
+
return ms >= 1e3 ? `${(ms / 1e3).toFixed(1)}s` : `${ms}ms`;
|
|
17916
|
+
}
|
|
17917
|
+
function liveSnapshotStr(snap) {
|
|
17918
|
+
if (!snap) return void 0;
|
|
17919
|
+
const f = snap.fiveHour?.utilization;
|
|
17920
|
+
const s = snap.sevenDay?.utilization;
|
|
17921
|
+
const parts = [
|
|
17922
|
+
typeof f === "number" ? `5h ${Math.round(f)}%` : null,
|
|
17923
|
+
typeof s === "number" ? `7d ${Math.round(s)}%` : null
|
|
17924
|
+
].filter(Boolean);
|
|
17925
|
+
return parts.length ? parts.join(" / ") : void 0;
|
|
17926
|
+
}
|
|
17927
|
+
function buildSyncLine(status2, trigger, s, elapsedMs, nextSyncMs) {
|
|
17928
|
+
const parts = [`[sync] ${status2}`, trigger];
|
|
17929
|
+
if (s.pushedDays.length) {
|
|
17930
|
+
parts.push(
|
|
17931
|
+
s.pushedDays.length === 1 ? `pushed 1 day (${s.pushedDays[0]})` : `pushed ${s.pushedDays.length} days (${s.pushedDays[0]}\u2192${s.pushedDays[s.pushedDays.length - 1]})`
|
|
17932
|
+
);
|
|
17933
|
+
}
|
|
17934
|
+
if (s.queued) {
|
|
17935
|
+
parts.push(
|
|
17936
|
+
s.queuedDay ? `queued ${s.queued} for retry (${s.queuedDay}, HTTP ${s.queuedStatus})` : `queued ${s.queued} for retry`
|
|
17937
|
+
);
|
|
17938
|
+
}
|
|
17939
|
+
if (s.droppedDays.length) {
|
|
17940
|
+
parts.push(`dropped ${s.droppedDays.length} unrecoverable (${s.droppedDays.join(",")})`);
|
|
17941
|
+
}
|
|
17942
|
+
if (s.recoveredDays?.length) {
|
|
17943
|
+
parts.push(
|
|
17944
|
+
`recovered ${s.recoveredDays.length} dropped day${s.recoveredDays.length === 1 ? "" : "s"} (${s.recoveredDays.join(",")})`
|
|
17945
|
+
);
|
|
17946
|
+
}
|
|
17947
|
+
if (s.queuedDrained) parts.push(`drained ${s.queuedDrained} from backlog`);
|
|
17948
|
+
if (s.idleReason) parts.push(s.idleReason);
|
|
17949
|
+
if (s.live) parts.push(`live ${s.live}`);
|
|
17950
|
+
if (s.usageSnapshots > 0) {
|
|
17951
|
+
parts.push(`usage +${s.usageSnapshots} snapshot${s.usageSnapshots === 1 ? "" : "s"}`);
|
|
17952
|
+
}
|
|
17953
|
+
if (s.accepted?.length) parts.push(`server accepted ${s.accepted.join(",")}`);
|
|
17954
|
+
if (s.skipped && Object.keys(s.skipped).length) {
|
|
17955
|
+
parts.push(
|
|
17956
|
+
`server SKIPPED ${Object.entries(s.skipped).map(([k, v]) => `${k} (${v})`).join("; ")}`
|
|
17957
|
+
);
|
|
17958
|
+
}
|
|
17959
|
+
if (s.errorMsg) parts.push(s.errorMsg);
|
|
17960
|
+
parts.push(fmtDuration(elapsedMs));
|
|
17961
|
+
if (nextSyncMs) parts.push(`next ~${Math.round(nextSyncMs / 6e4)}m`);
|
|
17962
|
+
return parts.join(" \xB7 ");
|
|
17963
|
+
}
|
|
17018
17964
|
async function runTeamSync(log = noopLog2, configOverride, options = {}) {
|
|
17019
17965
|
const config = configOverride === void 0 ? readTeamConfig() : configOverride;
|
|
17020
17966
|
if (!config) return { paired: false, pushed: 0, queued: 0, queuedDrained: 0 };
|
|
17967
|
+
if (config.setupPending) {
|
|
17968
|
+
log(
|
|
17969
|
+
"info",
|
|
17970
|
+
buildSyncLine(
|
|
17971
|
+
"idle",
|
|
17972
|
+
options.trigger ?? "auto",
|
|
17973
|
+
{
|
|
17974
|
+
pushedDays: [],
|
|
17975
|
+
droppedDays: [],
|
|
17976
|
+
queued: 0,
|
|
17977
|
+
queuedDrained: 0,
|
|
17978
|
+
usageSnapshots: 0,
|
|
17979
|
+
idleReason: "setup pending \u2014 finish onboarding in the dashboard (/team/onboarding)"
|
|
17980
|
+
},
|
|
17981
|
+
0,
|
|
17982
|
+
options.nextSyncMs
|
|
17983
|
+
)
|
|
17984
|
+
);
|
|
17985
|
+
return { paired: true, pushed: 0, queued: 0, queuedDrained: 0, setupPending: true };
|
|
17986
|
+
}
|
|
17987
|
+
const startedAt = Date.now();
|
|
17988
|
+
const trigger = options.trigger ?? "auto";
|
|
17989
|
+
const summary = {
|
|
17990
|
+
pushedDays: [],
|
|
17991
|
+
droppedDays: [],
|
|
17992
|
+
queued: 0,
|
|
17993
|
+
queuedDrained: 0,
|
|
17994
|
+
usageSnapshots: 0
|
|
17995
|
+
};
|
|
17996
|
+
const finish = (status2) => {
|
|
17997
|
+
log(
|
|
17998
|
+
levelForStatus(status2),
|
|
17999
|
+
buildSyncLine(status2, trigger, summary, Date.now() - startedAt, options.nextSyncMs)
|
|
18000
|
+
);
|
|
18001
|
+
};
|
|
18002
|
+
const recordVerdict = (body) => {
|
|
18003
|
+
if (body?.blocks) {
|
|
18004
|
+
summary.accepted = [.../* @__PURE__ */ new Set([...summary.accepted ?? [], ...body.blocks.accepted])];
|
|
18005
|
+
summary.skipped = { ...summary.skipped, ...body.blocks.skipped };
|
|
18006
|
+
}
|
|
18007
|
+
};
|
|
18008
|
+
const emit = options.onProgress ?? (() => {
|
|
18009
|
+
});
|
|
17021
18010
|
try {
|
|
18011
|
+
resetEnsuredSessions();
|
|
17022
18012
|
const { listSessions: listSessions2, loadCalibrationCurve: loadCalibrationCurve2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
|
|
17023
|
-
const { toLocalDay: toLocalDay4 } = await Promise.resolve().then(() => (init_dist(), dist_exports));
|
|
18013
|
+
const { toLocalDay: toLocalDay4, projectRepoName: projectRepoName2 } = await Promise.resolve().then(() => (init_dist(), dist_exports));
|
|
17024
18014
|
const today = toLocalDay4(Date.now());
|
|
17025
|
-
let
|
|
18015
|
+
let patches = {};
|
|
17026
18016
|
const persistConfig = (patch) => {
|
|
17027
|
-
|
|
17028
|
-
|
|
18017
|
+
patches = { ...patches, ...patch };
|
|
18018
|
+
const onDisk = readTeamConfig();
|
|
18019
|
+
if (!onDisk) return;
|
|
18020
|
+
writeTeamConfig({ ...onDisk, ...patches });
|
|
17029
18021
|
};
|
|
18022
|
+
const pendingLog = readPendingSyncLog(config.lastSyncedLogAt);
|
|
17030
18023
|
const collectedCommands = /* @__PURE__ */ new Map();
|
|
17031
18024
|
const collectCommands = (commands) => {
|
|
17032
18025
|
if (!commands) return;
|
|
@@ -17064,128 +18057,259 @@ async function runTeamSync(log = noopLog2, configOverride, options = {}) {
|
|
|
17064
18057
|
log("warn", `team command dispatch error: ${err.message}`);
|
|
17065
18058
|
}
|
|
17066
18059
|
};
|
|
18060
|
+
const isValidationPoison = (status3) => status3 === 400 || status3 === 422;
|
|
18061
|
+
const drainBacklog = async () => {
|
|
18062
|
+
let drained = 0;
|
|
18063
|
+
const backlog = dequeuePayloads();
|
|
18064
|
+
for (let i = 0; i < backlog.length; i++) {
|
|
18065
|
+
const queuedPayload = backlog[i];
|
|
18066
|
+
if (queuedPayload.richRollup?.projects?.some((p) => !shouldSyncProject(p.project, config.syncProjects))) {
|
|
18067
|
+
log("info", "team push: dropped queued payload containing now-excluded project(s)");
|
|
18068
|
+
continue;
|
|
18069
|
+
}
|
|
18070
|
+
const qResult = await pushToTeamServer(config, queuedPayload);
|
|
18071
|
+
if (!qResult.ok) {
|
|
18072
|
+
if (isValidationPoison(qResult.status)) {
|
|
18073
|
+
log("warn", `team push: queued payload rejected (HTTP ${qResult.status}); dropping unrecoverable item`);
|
|
18074
|
+
continue;
|
|
18075
|
+
}
|
|
18076
|
+
for (const remaining of backlog.slice(i)) enqueuePayload(remaining);
|
|
18077
|
+
break;
|
|
18078
|
+
}
|
|
18079
|
+
collectCommands(qResult.body?.commands);
|
|
18080
|
+
drained++;
|
|
18081
|
+
}
|
|
18082
|
+
return drained;
|
|
18083
|
+
};
|
|
18084
|
+
emit({ type: "phase", phase: "usage-backfill" });
|
|
17067
18085
|
const usageBackfill = await runTeamBackfill(log, USAGE_LOG4, config, {
|
|
17068
18086
|
sinceCapturedAt: options.forceUsageBackfill ? void 0 : config.lastSyncedUsageSnapshotAt
|
|
17069
18087
|
});
|
|
18088
|
+
emit({
|
|
18089
|
+
type: "usage",
|
|
18090
|
+
inserted: usageBackfill.insertedSnapshots ?? 0,
|
|
18091
|
+
alreadyKnown: usageBackfill.skippedSnapshots ?? 0
|
|
18092
|
+
});
|
|
17070
18093
|
if (usageBackfill.lastSnapshotAt) {
|
|
17071
18094
|
persistConfig({ lastSyncedUsageSnapshotAt: usageBackfill.lastSnapshotAt });
|
|
17072
18095
|
}
|
|
17073
|
-
const
|
|
17074
|
-
const
|
|
18096
|
+
const unfilteredSessions = await listSessions2({ limit: 1e4 });
|
|
18097
|
+
const sessions = filterSyncedSessions(unfilteredSessions, config.syncProjects);
|
|
18098
|
+
const allRollups = buildRollupsForRange(sessions);
|
|
18099
|
+
const inWindow = (day2) => !config.lastSyncedDay || day2 >= config.lastSyncedDay;
|
|
18100
|
+
const rollups = allRollups.filter((r) => inWindow(r.day));
|
|
18101
|
+
if (config.syncProjects) {
|
|
18102
|
+
const filteredDays = new Set(allRollups.map((r) => r.day));
|
|
18103
|
+
for (const r of buildRollupsForRange(unfilteredSessions)) {
|
|
18104
|
+
if (!filteredDays.has(r.day) && inWindow(r.day)) {
|
|
18105
|
+
rollups.push({
|
|
18106
|
+
day: r.day,
|
|
18107
|
+
agentTimeMs: 0,
|
|
18108
|
+
sessions: 0,
|
|
18109
|
+
uniqueSessions: 0,
|
|
18110
|
+
toolCalls: 0,
|
|
18111
|
+
turns: 0,
|
|
18112
|
+
tokens: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }
|
|
18113
|
+
});
|
|
18114
|
+
}
|
|
18115
|
+
}
|
|
18116
|
+
rollups.sort((a, b) => a.day < b.day ? -1 : 1);
|
|
18117
|
+
}
|
|
18118
|
+
emit({ type: "phase", phase: "activity", totalDays: rollups.length });
|
|
17075
18119
|
const usageSnapshot = readLatestUsageSnapshotForWire(USAGE_LOG4) ?? void 0;
|
|
17076
18120
|
const planTier = await getPlanTier(PROFILE_CACHE2).catch(() => null) ?? void 0;
|
|
17077
18121
|
const cyclePeaks = await buildCyclePeaksForPush(planTier, loadCalibrationCurve2);
|
|
17078
18122
|
if (rollups.length === 0) {
|
|
17079
18123
|
const hasLiveData = Boolean(usageSnapshot || planTier || cyclePeaks);
|
|
17080
18124
|
if (!hasLiveData) {
|
|
17081
|
-
|
|
18125
|
+
summary.usageSnapshots = usageBackfill.sentSnapshots;
|
|
18126
|
+
if (usageBackfill.sentSnapshots > 0) {
|
|
18127
|
+
finish("ok");
|
|
18128
|
+
} else {
|
|
18129
|
+
summary.idleReason = "nothing to sync";
|
|
18130
|
+
finish("idle");
|
|
18131
|
+
}
|
|
18132
|
+
emit({ type: "done", pushed: 0, queued: 0, pushedDays: summary.pushedDays });
|
|
17082
18133
|
return { paired: true, pushed: 0, queued: 0, queuedDrained: 0, usageBackfill };
|
|
17083
18134
|
}
|
|
17084
|
-
|
|
18135
|
+
summary.usageSnapshots = usageBackfill.sentSnapshots;
|
|
18136
|
+
summary.live = liveSnapshotStr(usageSnapshot);
|
|
18137
|
+
const payload = buildIngestPayload({ usageSnapshot, planTier, cyclePeaks, syncLog: pendingLog.lines });
|
|
17085
18138
|
const result = await pushToTeamServer(config, payload);
|
|
17086
18139
|
if (!result.ok) {
|
|
18140
|
+
if (isValidationPoison(result.status)) {
|
|
18141
|
+
const errLine2 = `team push: live-only payload rejected (HTTP ${result.status}); dropping unrecoverable payload`;
|
|
18142
|
+
writeLastPushFailure(payload, errLine2);
|
|
18143
|
+
summary.errorMsg = `live-snapshot push HTTP ${result.status} (validation) \u2014 dropped unrecoverable`;
|
|
18144
|
+
finish("error");
|
|
18145
|
+
emit({ type: "done", pushed: 0, queued: 0, pushedDays: summary.pushedDays });
|
|
18146
|
+
return { paired: true, pushed: 0, queued: 0, queuedDrained: 0, usageBackfill };
|
|
18147
|
+
}
|
|
17087
18148
|
const errLine = `team push failed (${result.status})`;
|
|
17088
|
-
log("warn", `${errLine}; queueing`);
|
|
17089
18149
|
writeLastPushFailure(payload, errLine);
|
|
17090
18150
|
enqueuePayload(payload);
|
|
18151
|
+
summary.queued = 1;
|
|
18152
|
+
summary.errorMsg = `live-snapshot push HTTP ${result.status} \u2014 queued for retry`;
|
|
18153
|
+
finish("failed");
|
|
18154
|
+
emit({ type: "done", pushed: 0, queued: 1, pushedDays: summary.pushedDays });
|
|
17091
18155
|
return { paired: true, pushed: 0, queued: 1, queuedDrained: 0, usageBackfill };
|
|
17092
18156
|
}
|
|
17093
18157
|
writeLastPushSuccess(payload);
|
|
17094
|
-
|
|
17095
|
-
|
|
17096
|
-
const backlog = dequeuePayloads();
|
|
17097
|
-
for (let i = 0; i < backlog.length; i++) {
|
|
17098
|
-
const qResult = await pushToTeamServer(config, backlog[i]);
|
|
17099
|
-
if (!qResult.ok) {
|
|
17100
|
-
for (const remaining of backlog.slice(i)) enqueuePayload(remaining);
|
|
17101
|
-
break;
|
|
17102
|
-
}
|
|
17103
|
-
collectCommands(qResult.body?.commands);
|
|
17104
|
-
queuedDrained2++;
|
|
18158
|
+
if (pendingLog.watermark && syncLogDelivered(result.body)) {
|
|
18159
|
+
persistConfig({ lastSyncedLogAt: pendingLog.watermark });
|
|
17105
18160
|
}
|
|
17106
|
-
|
|
18161
|
+
collectCommands(result.body?.commands);
|
|
18162
|
+
recordVerdict(result.body);
|
|
18163
|
+
const queuedDrained2 = await drainBacklog();
|
|
18164
|
+
summary.queuedDrained = queuedDrained2;
|
|
18165
|
+
summary.idleReason = "no new daily activity";
|
|
18166
|
+
finish(summary.skipped && Object.keys(summary.skipped).length ? "degraded" : "idle");
|
|
17107
18167
|
await dispatchAndReport();
|
|
18168
|
+
emit({ type: "done", pushed: 1, queued: 0, pushedDays: summary.pushedDays });
|
|
17108
18169
|
return { paired: true, pushed: 1, queued: 0, queuedDrained: queuedDrained2, usageBackfill };
|
|
17109
18170
|
}
|
|
17110
18171
|
let pushed = 0;
|
|
17111
18172
|
let queued = 0;
|
|
17112
18173
|
let failedDay;
|
|
17113
|
-
|
|
17114
|
-
const
|
|
17115
|
-
|
|
18174
|
+
const droppedDaysBefore = config.droppedDays ?? [];
|
|
18175
|
+
const workingDropped = [...droppedDaysBefore];
|
|
18176
|
+
let syncLogWasDelivered = false;
|
|
17116
18177
|
const resolveRepo = createRepoResolver();
|
|
17117
18178
|
const { probeArtifactSignals: probeArtifactSignals2 } = await Promise.resolve().then(() => (init_file_probe(), file_probe_exports));
|
|
17118
|
-
|
|
17119
|
-
const rollup = rollups[i];
|
|
17120
|
-
const isLatest = i === rollups.length - 1;
|
|
18179
|
+
const buildDayPayload = (rollup, opts) => {
|
|
17121
18180
|
const daySessions = sessions.filter((s) => sessionTouchesDay(s, rollup.day));
|
|
17122
|
-
const richBlocks = buildRichBlocksForDay(
|
|
17123
|
-
|
|
17124
|
-
|
|
17125
|
-
|
|
17126
|
-
|
|
17127
|
-
|
|
17128
|
-
|
|
18181
|
+
const richBlocks = buildRichBlocksForDay(rollup.day, daySessions, resolveRepo);
|
|
18182
|
+
const tombstone = daySessions.length === 0 && !!config.syncProjects;
|
|
18183
|
+
const emptyRich = tombstone ? {
|
|
18184
|
+
projects: [],
|
|
18185
|
+
workingShapes: [],
|
|
18186
|
+
concurrencyPeak: 0,
|
|
18187
|
+
parallelMinutes: 0,
|
|
18188
|
+
longAutonomous: { count: 0, totalMin: 0, maxSingleMin: 0 },
|
|
18189
|
+
toolErrors: 0,
|
|
18190
|
+
skillsLoaded: [],
|
|
18191
|
+
subagentsDispatched: [],
|
|
18192
|
+
brainstormWarmupSessions: 0,
|
|
18193
|
+
planModeUsed: 0,
|
|
18194
|
+
prs: 0,
|
|
18195
|
+
commits: 0,
|
|
18196
|
+
pushes: 0
|
|
18197
|
+
} : void 0;
|
|
17129
18198
|
let artifactSignals = null;
|
|
17130
18199
|
try {
|
|
17131
|
-
|
|
17132
|
-
|
|
17133
|
-
extraRoots: [process.cwd()]
|
|
17134
|
-
});
|
|
18200
|
+
const cwdAllowed = shouldSyncProject(projectRepoName2(process.cwd()), config.syncProjects);
|
|
18201
|
+
artifactSignals = probeArtifactSignals2({ day: rollup.day, extraRoots: cwdAllowed ? [process.cwd()] : [] });
|
|
17135
18202
|
} catch {
|
|
17136
18203
|
}
|
|
17137
|
-
|
|
18204
|
+
return buildIngestPayload({
|
|
17138
18205
|
rollup,
|
|
17139
|
-
richExtras: richBlocks?.rich,
|
|
17140
|
-
enrichedExtras: richBlocks?.enriched,
|
|
18206
|
+
richExtras: richBlocks?.rich ?? emptyRich,
|
|
18207
|
+
enrichedExtras: richBlocks?.enriched ?? (tombstone ? { outcomeMix: {}, helpfulnessMix: {}, goalMix: {} } : void 0),
|
|
17141
18208
|
artifactSignals: artifactSignals ?? void 0,
|
|
17142
|
-
|
|
18209
|
+
// Current-cycle data only on the latest day so older days aren't tagged
|
|
18210
|
+
// with today's snapshot / peaks.
|
|
18211
|
+
usageSnapshot: opts.latest ? usageSnapshot : void 0,
|
|
17143
18212
|
planTier,
|
|
17144
|
-
|
|
17145
|
-
|
|
17146
|
-
|
|
18213
|
+
cyclePeaks: opts.latest ? cyclePeaks : void 0,
|
|
18214
|
+
syncLog: opts.syncLog
|
|
18215
|
+
});
|
|
18216
|
+
};
|
|
18217
|
+
const rollupsDesc = [...rollups].reverse();
|
|
18218
|
+
for (let i = 0; i < rollupsDesc.length; i++) {
|
|
18219
|
+
const rollup = rollupsDesc[i];
|
|
18220
|
+
const isLatest = i === 0;
|
|
18221
|
+
const payload = buildDayPayload(rollup, {
|
|
18222
|
+
latest: isLatest,
|
|
18223
|
+
syncLog: i === 0 ? pendingLog.lines : void 0
|
|
17147
18224
|
});
|
|
17148
18225
|
const result = await pushToTeamServer(config, payload);
|
|
17149
18226
|
if (!result.ok) {
|
|
18227
|
+
if (isValidationPoison(result.status)) {
|
|
18228
|
+
const errLine2 = `team push: ${rollup.day} rejected (HTTP ${result.status}); skipping past unrecoverable day`;
|
|
18229
|
+
writeLastPushFailure(payload, errLine2);
|
|
18230
|
+
summary.droppedDays.push(rollup.day);
|
|
18231
|
+
if (!workingDropped.includes(rollup.day)) workingDropped.push(rollup.day);
|
|
18232
|
+
emit({ type: "day", day: rollup.day, index: i + 1, total: rollupsDesc.length, outcome: "dropped" });
|
|
18233
|
+
continue;
|
|
18234
|
+
}
|
|
17150
18235
|
const errLine = `team push failed on ${rollup.day} (${result.status})`;
|
|
17151
|
-
log("warn", `${errLine}; queueing`);
|
|
17152
18236
|
writeLastPushFailure(payload, errLine);
|
|
17153
18237
|
enqueuePayload(payload);
|
|
17154
18238
|
queued++;
|
|
18239
|
+
summary.queuedDay = rollup.day;
|
|
18240
|
+
summary.queuedStatus = result.status;
|
|
17155
18241
|
failedDay = rollup.day;
|
|
18242
|
+
emit({ type: "day", day: rollup.day, index: i + 1, total: rollupsDesc.length, outcome: "queued" });
|
|
17156
18243
|
break;
|
|
17157
18244
|
}
|
|
17158
18245
|
writeLastPushSuccess(payload);
|
|
17159
18246
|
collectCommands(result.body?.commands);
|
|
18247
|
+
recordVerdict(result.body);
|
|
18248
|
+
if (i === 0) syncLogWasDelivered = syncLogDelivered(result.body);
|
|
17160
18249
|
pushed++;
|
|
17161
|
-
|
|
17162
|
-
|
|
17163
|
-
if (failedDay) {
|
|
17164
|
-
if (lastPushedDay) persistConfig({ lastSyncedDay: lastPushedDay });
|
|
17165
|
-
} else {
|
|
17166
|
-
persistConfig({ lastSyncedDay: today });
|
|
18250
|
+
summary.pushedDays.push(rollup.day);
|
|
18251
|
+
emit({ type: "day", day: rollup.day, index: i + 1, total: rollupsDesc.length, outcome: "pushed" });
|
|
17167
18252
|
}
|
|
17168
|
-
let queuedDrained = 0;
|
|
17169
18253
|
if (!failedDay) {
|
|
17170
|
-
const
|
|
17171
|
-
|
|
17172
|
-
const
|
|
17173
|
-
if (
|
|
17174
|
-
|
|
17175
|
-
|
|
18254
|
+
const retryDay = [...droppedDaysBefore].sort().find((d) => !summary.droppedDays.includes(d));
|
|
18255
|
+
if (retryDay && summary.pushedDays.includes(retryDay)) {
|
|
18256
|
+
const idx = workingDropped.indexOf(retryDay);
|
|
18257
|
+
if (idx >= 0) workingDropped.splice(idx, 1);
|
|
18258
|
+
summary.recoveredDays = [retryDay];
|
|
18259
|
+
}
|
|
18260
|
+
const retryRollup = retryDay && !summary.pushedDays.includes(retryDay) ? allRollups.find((r) => r.day === retryDay) ?? (config.syncProjects ? {
|
|
18261
|
+
day: retryDay,
|
|
18262
|
+
agentTimeMs: 0,
|
|
18263
|
+
sessions: 0,
|
|
18264
|
+
uniqueSessions: 0,
|
|
18265
|
+
toolCalls: 0,
|
|
18266
|
+
turns: 0,
|
|
18267
|
+
tokens: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }
|
|
18268
|
+
} : void 0) : void 0;
|
|
18269
|
+
if (retryDay && retryRollup) {
|
|
18270
|
+
const payload = buildDayPayload(retryRollup, { latest: false });
|
|
18271
|
+
const result = await pushToTeamServer(config, payload);
|
|
18272
|
+
if (result.ok) {
|
|
18273
|
+
writeLastPushSuccess(payload);
|
|
18274
|
+
collectCommands(result.body?.commands);
|
|
18275
|
+
recordVerdict(result.body);
|
|
18276
|
+
const idx = workingDropped.indexOf(retryDay);
|
|
18277
|
+
if (idx >= 0) workingDropped.splice(idx, 1);
|
|
18278
|
+
summary.recoveredDays = [retryDay];
|
|
18279
|
+
} else {
|
|
18280
|
+
writeLastPushFailure(payload, `team push: dropped-day retry ${retryDay} failed (HTTP ${result.status})`);
|
|
17176
18281
|
}
|
|
17177
|
-
collectCommands(qResult.body?.commands);
|
|
17178
|
-
queuedDrained++;
|
|
17179
18282
|
}
|
|
17180
18283
|
}
|
|
17181
|
-
|
|
17182
|
-
|
|
17183
|
-
|
|
18284
|
+
const nextDropped = [...new Set(workingDropped)].sort().slice(-90);
|
|
18285
|
+
const prevDropped = [...new Set(droppedDaysBefore)].sort();
|
|
18286
|
+
const droppedChanged = nextDropped.length !== prevDropped.length || nextDropped.some((d, i) => d !== prevDropped[i]);
|
|
18287
|
+
const droppedPatch = droppedChanged ? { droppedDays: nextDropped } : {};
|
|
18288
|
+
if (failedDay) {
|
|
18289
|
+
if (droppedChanged) persistConfig(droppedPatch);
|
|
18290
|
+
} else {
|
|
18291
|
+
persistConfig({ lastSyncedDay: today, ...droppedPatch });
|
|
18292
|
+
}
|
|
18293
|
+
if (syncLogWasDelivered && pendingLog.watermark) {
|
|
18294
|
+
persistConfig({ lastSyncedLogAt: pendingLog.watermark });
|
|
18295
|
+
}
|
|
18296
|
+
const queuedDrained = !failedDay ? await drainBacklog() : 0;
|
|
18297
|
+
summary.queued = queued;
|
|
18298
|
+
summary.queuedDrained = queuedDrained;
|
|
18299
|
+
summary.usageSnapshots = usageBackfill.sentSnapshots;
|
|
18300
|
+
let status2;
|
|
18301
|
+
if (failedDay) status2 = "failed";
|
|
18302
|
+
else if (summary.droppedDays.length || summary.skipped && Object.keys(summary.skipped).length) status2 = "degraded";
|
|
18303
|
+
else status2 = "ok";
|
|
18304
|
+
finish(status2);
|
|
17184
18305
|
await dispatchAndReport();
|
|
18306
|
+
emit({ type: "done", pushed, queued, pushedDays: summary.pushedDays });
|
|
17185
18307
|
return { paired: true, pushed, queued, queuedDrained, usageBackfill, failedDay };
|
|
17186
18308
|
} catch (err) {
|
|
17187
18309
|
const message = err.message;
|
|
17188
|
-
|
|
18310
|
+
summary.errorMsg = `sync aborted: ${message}`;
|
|
18311
|
+
emit({ type: "error", message });
|
|
18312
|
+
finish("error");
|
|
17189
18313
|
writeLastPushFailure(
|
|
17190
18314
|
{ ingestId: "n/a", observedAt: (/* @__PURE__ */ new Date()).toISOString() },
|
|
17191
18315
|
`team push error: ${message}`
|
|
@@ -17249,8 +18373,13 @@ async function buildCyclePeaksForPush(planTier, loadCurve) {
|
|
|
17249
18373
|
return out.slice(-maxCycles);
|
|
17250
18374
|
};
|
|
17251
18375
|
return {
|
|
18376
|
+
// 7d cycle history cap (26 ≈ six months) MUST stay in lock-step with two
|
|
18377
|
+
// downstream sites or the strip silently truncates: the server read
|
|
18378
|
+
// (loadMembership7dCyclePeaks maxCyclesPerMember in plan-queries.ts) and
|
|
18379
|
+
// the renderer (CyclePeaksStrip maxBars in member-plan-block.tsx). The 5h
|
|
18380
|
+
// cap (24) is independent — leave it.
|
|
17252
18381
|
fiveHour: peaksFor("cycle_end_5h", "real_5h", "pred_5h", 24),
|
|
17253
|
-
sevenDay: peaksFor("cycle_end_7d", "real_7d", "pred_7d",
|
|
18382
|
+
sevenDay: peaksFor("cycle_end_7d", "real_7d", "pred_7d", 26)
|
|
17254
18383
|
};
|
|
17255
18384
|
}
|
|
17256
18385
|
var inFlightCommands, USAGE_LOG4, PROFILE_CACHE2, noopLog2;
|
|
@@ -17262,6 +18391,7 @@ var init_sync = __esm({
|
|
|
17262
18391
|
init_git_remote();
|
|
17263
18392
|
init_queue2();
|
|
17264
18393
|
init_backfill();
|
|
18394
|
+
init_sync_log();
|
|
17265
18395
|
init_last_push();
|
|
17266
18396
|
init_commands();
|
|
17267
18397
|
init_profile();
|
|
@@ -17274,15 +18404,47 @@ var init_sync = __esm({
|
|
|
17274
18404
|
}
|
|
17275
18405
|
});
|
|
17276
18406
|
|
|
18407
|
+
// src/daemon-log.ts
|
|
18408
|
+
var daemon_log_exports = {};
|
|
18409
|
+
__export(daemon_log_exports, {
|
|
18410
|
+
appendDaemonLogLine: () => appendDaemonLogLine
|
|
18411
|
+
});
|
|
18412
|
+
import { appendFileSync as appendFileSync5 } from "node:fs";
|
|
18413
|
+
function appendDaemonLogLine(level, msg) {
|
|
18414
|
+
try {
|
|
18415
|
+
appendFileSync5(
|
|
18416
|
+
cclensPath("daemon.log"),
|
|
18417
|
+
`${(/* @__PURE__ */ new Date()).toISOString()} ${level.toUpperCase()} ${msg}
|
|
18418
|
+
`,
|
|
18419
|
+
"utf8"
|
|
18420
|
+
);
|
|
18421
|
+
} catch {
|
|
18422
|
+
}
|
|
18423
|
+
}
|
|
18424
|
+
var init_daemon_log = __esm({
|
|
18425
|
+
"src/daemon-log.ts"() {
|
|
18426
|
+
"use strict";
|
|
18427
|
+
init_fs();
|
|
18428
|
+
}
|
|
18429
|
+
});
|
|
18430
|
+
|
|
17277
18431
|
// src/team/join.ts
|
|
17278
18432
|
var join_exports = {};
|
|
17279
18433
|
__export(join_exports, {
|
|
18434
|
+
carryOverSyncProjects: () => carryOverSyncProjects,
|
|
17280
18435
|
joinTeam: () => joinTeam
|
|
17281
18436
|
});
|
|
18437
|
+
function carryOverSyncProjects(existing, serverUrl, teamSlug) {
|
|
18438
|
+
if (existing && existing.serverUrl === serverUrl && existing.teamSlug === teamSlug) {
|
|
18439
|
+
return existing.syncProjects;
|
|
18440
|
+
}
|
|
18441
|
+
return void 0;
|
|
18442
|
+
}
|
|
17282
18443
|
async function joinTeam(args2) {
|
|
17283
|
-
const
|
|
18444
|
+
const noBrowser = args2.includes("--no-browser");
|
|
18445
|
+
const [serverUrl, bearerToken] = args2.filter((a) => !a.startsWith("--"));
|
|
17284
18446
|
if (!serverUrl || !bearerToken) {
|
|
17285
|
-
console.error("Usage: fleetlens team join <server-url> <device-token>");
|
|
18447
|
+
console.error("Usage: fleetlens team join <server-url> <device-token> [--no-browser]");
|
|
17286
18448
|
console.error("");
|
|
17287
18449
|
console.error("Get the device token from the dashboard after signup,");
|
|
17288
18450
|
console.error("or from Settings \u2192 My device token.");
|
|
@@ -17298,45 +18460,93 @@ async function joinTeam(args2) {
|
|
|
17298
18460
|
process.exit(1);
|
|
17299
18461
|
}
|
|
17300
18462
|
const data = await res.json();
|
|
18463
|
+
const existing = readTeamConfig();
|
|
17301
18464
|
const config = {
|
|
17302
18465
|
serverUrl,
|
|
17303
18466
|
memberId: data.membership.id,
|
|
17304
18467
|
bearerToken,
|
|
17305
18468
|
teamSlug: data.team.slug,
|
|
17306
18469
|
teamName: data.team.name,
|
|
17307
|
-
pairedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
18470
|
+
pairedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
18471
|
+
// Fence off any daemon.log history from a PREVIOUS team so the first push
|
|
18472
|
+
// doesn't sweep the prior team's [sync] lines onto this team's log. The
|
|
18473
|
+
// pair run's own [sync] line is written after this instant, so it still
|
|
18474
|
+
// uploads on the next sync.
|
|
18475
|
+
lastSyncedLogAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
18476
|
+
syncProjects: carryOverSyncProjects(existing, serverUrl, data.team.slug),
|
|
18477
|
+
// Browser path: gate the daemon until the wizard's "Start syncing".
|
|
18478
|
+
...noBrowser ? {} : { setupPending: true }
|
|
17308
18479
|
};
|
|
17309
18480
|
writeTeamConfig(config);
|
|
17310
18481
|
console.log(`Paired with "${data.team.name}" as ${data.user.displayName || data.user.email}`);
|
|
17311
18482
|
console.log(` role: ${data.membership.role}`);
|
|
17312
|
-
|
|
17313
|
-
|
|
17314
|
-
|
|
17315
|
-
|
|
17316
|
-
|
|
17317
|
-
|
|
17318
|
-
|
|
17319
|
-
|
|
17320
|
-
|
|
17321
|
-
|
|
17322
|
-
|
|
17323
|
-
}
|
|
17324
|
-
|
|
18483
|
+
if (noBrowser) {
|
|
18484
|
+
console.log(" Syncing local history\u2026");
|
|
18485
|
+
const sync = await runTeamSync(logToDaemonLog, config, {
|
|
18486
|
+
forceUsageBackfill: true,
|
|
18487
|
+
trigger: "pair"
|
|
18488
|
+
});
|
|
18489
|
+
const backfill = sync.usageBackfill;
|
|
18490
|
+
if (backfill?.error) {
|
|
18491
|
+
console.log(` \u26A0 Usage history sync failed: ${backfill.error} \u2014 daemon will retry automatically.`);
|
|
18492
|
+
} else if ((backfill?.insertedSnapshots ?? 0) > 0) {
|
|
18493
|
+
console.log(
|
|
18494
|
+
` \u2713 Synced ${backfill.insertedSnapshots} usage snapshot${backfill.insertedSnapshots === 1 ? "" : "s"} from local history.`
|
|
18495
|
+
);
|
|
18496
|
+
} else if ((backfill?.sentSnapshots ?? 0) > 0) {
|
|
18497
|
+
console.log(` \u2713 ${backfill.sentSnapshots} usage snapshots already on server (deduped on capture time).`);
|
|
18498
|
+
} else {
|
|
18499
|
+
console.log(" \xB7 No local usage snapshots yet \u2014 daemon will start collecting.");
|
|
18500
|
+
}
|
|
18501
|
+
if (sync.error) {
|
|
18502
|
+
console.log(` \u26A0 Team sync failed: ${sync.error} \u2014 will retry on next daemon cycle.`);
|
|
18503
|
+
} else if (sync.pushed > 0) {
|
|
18504
|
+
console.log(` \u2713 Synced ${sync.pushed} activity payload${sync.pushed === 1 ? "" : "s"}.`);
|
|
18505
|
+
} else {
|
|
18506
|
+
console.log(" \xB7 No new session activity to push.");
|
|
18507
|
+
}
|
|
18508
|
+
console.log(" Daemon polls every 5 minutes \u2014 next push happens automatically.");
|
|
18509
|
+
const { ensureTeamAutostart: ensureNoBrowser } = await Promise.resolve().then(() => (init_autostart(), autostart_exports));
|
|
18510
|
+
await ensureNoBrowser();
|
|
18511
|
+
return;
|
|
17325
18512
|
}
|
|
17326
|
-
|
|
17327
|
-
|
|
17328
|
-
|
|
17329
|
-
|
|
17330
|
-
|
|
17331
|
-
|
|
18513
|
+
try {
|
|
18514
|
+
const { fetchUsage: fetchUsage2 } = await Promise.resolve().then(() => (init_api(), api_exports));
|
|
18515
|
+
const { appendSnapshot: appendSnapshot2 } = await Promise.resolve().then(() => (init_storage(), storage_exports));
|
|
18516
|
+
const { cclensPath: cclensPath2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
|
|
18517
|
+
appendSnapshot2(cclensPath2("usage.jsonl"), await fetchUsage2());
|
|
18518
|
+
} catch {
|
|
17332
18519
|
}
|
|
17333
|
-
|
|
18520
|
+
const { ensureTeamAutostart: ensureTeamAutostart2 } = await Promise.resolve().then(() => (init_autostart(), autostart_exports));
|
|
18521
|
+
await ensureTeamAutostart2();
|
|
18522
|
+
console.log(" Opening your browser to finish setup\u2026");
|
|
18523
|
+
let url;
|
|
18524
|
+
try {
|
|
18525
|
+
const server = await ensureCurrentServer({});
|
|
18526
|
+
url = `http://localhost:${server.port}/team/onboarding`;
|
|
18527
|
+
const daemon2 = startDaemonSilent();
|
|
18528
|
+
if (!daemon2.started && !daemon2.alreadyRunning) {
|
|
18529
|
+
console.warn(` \u26A0 Daemon failed to start \u2014 ${daemon2.error}`);
|
|
18530
|
+
}
|
|
18531
|
+
} catch (err) {
|
|
18532
|
+
console.error(` \u26A0 Could not start the dashboard: ${err.message}`);
|
|
18533
|
+
console.error(" Re-run 'fleetlens team join' after fixing it, or use --no-browser to sync everything from the terminal.");
|
|
18534
|
+
process.exit(1);
|
|
18535
|
+
}
|
|
18536
|
+
openBrowser(url);
|
|
18537
|
+
console.log(` Continue in your browser: ${url}`);
|
|
18538
|
+
console.log(" Nothing syncs until you finish setup there.");
|
|
17334
18539
|
}
|
|
18540
|
+
var logToDaemonLog;
|
|
17335
18541
|
var init_join = __esm({
|
|
17336
18542
|
"src/team/join.ts"() {
|
|
17337
18543
|
"use strict";
|
|
17338
18544
|
init_config();
|
|
17339
18545
|
init_sync();
|
|
18546
|
+
init_daemon_log();
|
|
18547
|
+
init_server();
|
|
18548
|
+
init_daemon();
|
|
18549
|
+
logToDaemonLog = appendDaemonLogLine;
|
|
17340
18550
|
}
|
|
17341
18551
|
});
|
|
17342
18552
|
|
|
@@ -17415,12 +18625,12 @@ var logs_exports = {};
|
|
|
17415
18625
|
__export(logs_exports, {
|
|
17416
18626
|
teamLogs: () => teamLogs
|
|
17417
18627
|
});
|
|
17418
|
-
import { readFileSync as
|
|
18628
|
+
import { readFileSync as readFileSync19 } from "node:fs";
|
|
17419
18629
|
async function teamLogs() {
|
|
17420
18630
|
const logPath = cclensPath("daemon.log");
|
|
17421
18631
|
try {
|
|
17422
|
-
const lines =
|
|
17423
|
-
const teamLines = lines.filter((l) => l.includes("team "));
|
|
18632
|
+
const lines = readFileSync19(logPath, "utf8").trim().split("\n");
|
|
18633
|
+
const teamLines = lines.filter((l) => l.includes("[sync] ") || l.includes("team "));
|
|
17424
18634
|
if (teamLines.length === 0) {
|
|
17425
18635
|
console.log("No team-related log entries found.");
|
|
17426
18636
|
return;
|
|
@@ -17468,20 +18678,42 @@ async function team(args2) {
|
|
|
17468
18678
|
break;
|
|
17469
18679
|
}
|
|
17470
18680
|
case "sync": {
|
|
18681
|
+
const progressJson = args2.includes("--progress-json");
|
|
17471
18682
|
const { runTeamSync: runTeamSync2 } = await Promise.resolve().then(() => (init_sync(), sync_exports));
|
|
17472
|
-
const
|
|
18683
|
+
const { appendDaemonLogLine: appendDaemonLogLine2 } = await Promise.resolve().then(() => (init_daemon_log(), daemon_log_exports));
|
|
18684
|
+
const outcome = await runTeamSync2(
|
|
18685
|
+
(level, msg) => {
|
|
18686
|
+
if (!progressJson) console.log(`[${level}] ${msg}`);
|
|
18687
|
+
appendDaemonLogLine2(level, msg);
|
|
18688
|
+
},
|
|
18689
|
+
void 0,
|
|
18690
|
+
{
|
|
18691
|
+
trigger: "manual",
|
|
18692
|
+
onProgress: progressJson ? (ev) => console.log(JSON.stringify(ev)) : void 0
|
|
18693
|
+
}
|
|
18694
|
+
);
|
|
17473
18695
|
if (!outcome.paired) {
|
|
17474
18696
|
console.error("Not paired. Run 'fleetlens team join <url> <device-token>' first.");
|
|
17475
18697
|
process.exit(1);
|
|
17476
18698
|
}
|
|
18699
|
+
if (outcome.setupPending) {
|
|
18700
|
+
if (progressJson) {
|
|
18701
|
+
console.log(JSON.stringify({ type: "error", message: "setup pending \u2014 selection not saved yet" }));
|
|
18702
|
+
} else {
|
|
18703
|
+
console.error("Setup pending \u2014 nothing synced. Finish onboarding in the dashboard (/team/onboarding) or re-run 'fleetlens team join'.");
|
|
18704
|
+
}
|
|
18705
|
+
process.exit(1);
|
|
18706
|
+
}
|
|
17477
18707
|
const inserted = outcome.usageBackfill?.insertedSnapshots ?? 0;
|
|
17478
18708
|
const madeProgress = outcome.pushed > 0 || outcome.queuedDrained > 0 || inserted > 0;
|
|
17479
18709
|
if (outcome.error || outcome.usageBackfill?.error && !madeProgress) process.exit(1);
|
|
17480
|
-
|
|
17481
|
-
|
|
17482
|
-
|
|
17483
|
-
|
|
17484
|
-
|
|
18710
|
+
if (!progressJson) {
|
|
18711
|
+
const sent = outcome.usageBackfill?.sentSnapshots ?? 0;
|
|
18712
|
+
const usageChecked = sent > 0 ? `, ${sent} usage snapshot${sent === 1 ? "" : "s"} checked` : "";
|
|
18713
|
+
console.log(
|
|
18714
|
+
`\u2713 ${outcome.pushed} activity payload${outcome.pushed === 1 ? "" : "s"} pushed` + usageChecked + (outcome.queuedDrained ? `, ${outcome.queuedDrained} queued retried` : "") + (outcome.queued ? `, ${outcome.queued} queued for retry (will fire next cycle)` : "")
|
|
18715
|
+
);
|
|
18716
|
+
}
|
|
17485
18717
|
break;
|
|
17486
18718
|
}
|
|
17487
18719
|
case "backfill": {
|
|
@@ -17505,7 +18737,7 @@ async function team(args2) {
|
|
|
17505
18737
|
default:
|
|
17506
18738
|
console.log(`Usage: fleetlens team <join|status|leave|logs|sync|backfill>
|
|
17507
18739
|
|
|
17508
|
-
join <url> <device-token>
|
|
18740
|
+
join <url> <device-token> [--no-browser] Pair with a team server (opens browser setup; --no-browser syncs everything immediately)
|
|
17509
18741
|
status Show team pairing state and sync info
|
|
17510
18742
|
leave Unpair from the team server
|
|
17511
18743
|
logs Show recent team-related daemon log entries
|
|
@@ -17525,7 +18757,7 @@ __export(runs_exports, {
|
|
|
17525
18757
|
runs: () => runs
|
|
17526
18758
|
});
|
|
17527
18759
|
import { execSync as execSync2 } from "node:child_process";
|
|
17528
|
-
import { existsSync as existsSync16, readFileSync as
|
|
18760
|
+
import { existsSync as existsSync16, readFileSync as readFileSync20, readdirSync as readdirSync5, statSync as statSync7 } from "node:fs";
|
|
17529
18761
|
import { join as join14 } from "node:path";
|
|
17530
18762
|
async function runs(args2) {
|
|
17531
18763
|
const json = args2.includes("--json");
|
|
@@ -17611,7 +18843,7 @@ function listActiveRuns() {
|
|
|
17611
18843
|
}
|
|
17612
18844
|
function readSpend(sinceMs) {
|
|
17613
18845
|
if (!existsSync16(SPEND_PATH)) return [];
|
|
17614
|
-
const lines =
|
|
18846
|
+
const lines = readFileSync20(SPEND_PATH, "utf-8").split("\n").filter((l) => l.trim());
|
|
17615
18847
|
const out = [];
|
|
17616
18848
|
for (const line of lines) {
|
|
17617
18849
|
try {
|
|
@@ -17736,7 +18968,7 @@ function listTraceFiles() {
|
|
|
17736
18968
|
const entries2 = readdirSync5(RUNS_DIR2).filter((f) => f.endsWith(".jsonl"));
|
|
17737
18969
|
return entries2.map((f) => {
|
|
17738
18970
|
const path7 = join14(RUNS_DIR2, f);
|
|
17739
|
-
const st =
|
|
18971
|
+
const st = statSync7(path7);
|
|
17740
18972
|
return { runId: f.replace(/\.jsonl$/, ""), path: path7, mtimeMs: st.mtimeMs };
|
|
17741
18973
|
}).sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
17742
18974
|
}
|
|
@@ -17760,7 +18992,7 @@ function resolveRunId(arg) {
|
|
|
17760
18992
|
return null;
|
|
17761
18993
|
}
|
|
17762
18994
|
function readTrace(path7) {
|
|
17763
|
-
return
|
|
18995
|
+
return readFileSync20(path7, "utf-8").split("\n").filter((l) => l.trim()).map((l) => {
|
|
17764
18996
|
try {
|
|
17765
18997
|
return JSON.parse(l);
|
|
17766
18998
|
} catch {
|
|
@@ -17792,13 +19024,13 @@ recent runs:`);
|
|
|
17792
19024
|
const events = readTrace(r.path);
|
|
17793
19025
|
printTrace(r.runId, events);
|
|
17794
19026
|
if (!follow) return;
|
|
17795
|
-
let lastSize =
|
|
19027
|
+
let lastSize = statSync7(r.path).size;
|
|
17796
19028
|
let lastIdx = events.length;
|
|
17797
19029
|
console.log("\n \u27F6 following \xB7 ^C to quit\n");
|
|
17798
19030
|
process.on("SIGINT", () => process.exit(0));
|
|
17799
19031
|
while (true) {
|
|
17800
19032
|
await new Promise((res) => setTimeout(res, 500));
|
|
17801
|
-
const st =
|
|
19033
|
+
const st = statSync7(r.path);
|
|
17802
19034
|
if (st.size === lastSize) continue;
|
|
17803
19035
|
lastSize = st.size;
|
|
17804
19036
|
const all = readTrace(r.path);
|
|
@@ -17947,7 +19179,7 @@ async function main() {
|
|
|
17947
19179
|
case "version":
|
|
17948
19180
|
case "--version":
|
|
17949
19181
|
case "-v":
|
|
17950
|
-
console.log(`fleetlens ${"0.
|
|
19182
|
+
console.log(`fleetlens ${"0.15.0"}`);
|
|
17951
19183
|
break;
|
|
17952
19184
|
case "help":
|
|
17953
19185
|
case "--help":
|
|
@@ -17976,7 +19208,7 @@ Advanced:
|
|
|
17976
19208
|
autostart <install|uninstall|status> Run the usage daemon at login (macOS launchd)
|
|
17977
19209
|
|
|
17978
19210
|
Team:
|
|
17979
|
-
team join <url> <token> Pair with a team server
|
|
19211
|
+
team join <url> <token> Pair with a team server (browser setup)
|
|
17980
19212
|
team status Show team pairing state
|
|
17981
19213
|
team leave Unpair from team server
|
|
17982
19214
|
team logs Show team-related daemon logs
|