@shepai/cli 1.148.0 → 1.149.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/dist/src/presentation/web/app/api/agent-events/route.js +1 -1
- package/dist/src/presentation/web/app/api/sessions/route.d.ts.map +1 -1
- package/dist/src/presentation/web/app/api/sessions/route.js +2 -268
- package/dist/src/presentation/web/app/api/sessions-batch/route.d.ts +17 -0
- package/dist/src/presentation/web/app/api/sessions-batch/route.d.ts.map +1 -0
- package/dist/src/presentation/web/app/api/sessions-batch/route.js +61 -0
- package/dist/src/presentation/web/components/common/feature-node/feature-sessions-dropdown.d.ts +1 -1
- package/dist/src/presentation/web/components/common/feature-node/feature-sessions-dropdown.d.ts.map +1 -1
- package/dist/src/presentation/web/components/common/feature-node/feature-sessions-dropdown.js +15 -73
- package/dist/src/presentation/web/components/common/feature-node/feature-sessions-dropdown.stories.d.ts.map +1 -1
- package/dist/src/presentation/web/components/common/feature-node/feature-sessions-dropdown.stories.js +18 -17
- package/dist/src/presentation/web/components/features/control-center/control-center.d.ts.map +1 -1
- package/dist/src/presentation/web/components/features/control-center/control-center.js +2 -1
- package/dist/src/presentation/web/components/features/control-center/use-control-center-state.d.ts.map +1 -1
- package/dist/src/presentation/web/components/features/control-center/use-control-center-state.js +4 -1
- package/dist/src/presentation/web/components/layouts/app-sidebar/app-sidebar.d.ts.map +1 -1
- package/dist/src/presentation/web/components/layouts/app-sidebar/app-sidebar.js +32 -33
- package/dist/src/presentation/web/hooks/sessions-provider.d.ts +12 -0
- package/dist/src/presentation/web/hooks/sessions-provider.d.ts.map +1 -0
- package/dist/src/presentation/web/hooks/sessions-provider.js +57 -0
- package/dist/src/presentation/web/hooks/use-deploy-action.d.ts.map +1 -1
- package/dist/src/presentation/web/hooks/use-deploy-action.js +8 -54
- package/dist/src/presentation/web/lib/session-scanner.d.ts +27 -0
- package/dist/src/presentation/web/lib/session-scanner.d.ts.map +1 -0
- package/dist/src/presentation/web/lib/session-scanner.js +255 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/web/.next/BUILD_ID +1 -1
- package/web/.next/app-path-routes-manifest.json +1 -0
- package/web/.next/build-manifest.json +2 -2
- package/web/.next/fallback-build-manifest.json +2 -2
- package/web/.next/prerender-manifest.json +3 -3
- package/web/.next/required-server-files.js +2 -2
- package/web/.next/required-server-files.json +2 -2
- package/web/.next/routes-manifest.json +6 -0
- package/web/.next/server/app/(dashboard)/@drawer/adopt/page/server-reference-manifest.json +28 -28
- package/web/.next/server/app/(dashboard)/@drawer/adopt/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/@drawer/adopt/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/@drawer/create/page/server-reference-manifest.json +28 -28
- package/web/.next/server/app/(dashboard)/@drawer/create/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/@drawer/create/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/[tab]/page/server-reference-manifest.json +36 -36
- package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/[tab]/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/[tab]/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page/server-reference-manifest.json +36 -36
- package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page/server-reference-manifest.json +26 -26
- package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/create/page/server-reference-manifest.json +28 -28
- package/web/.next/server/app/(dashboard)/create/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/create/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/feature/[featureId]/[tab]/page/server-reference-manifest.json +36 -36
- package/web/.next/server/app/(dashboard)/feature/[featureId]/[tab]/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/feature/[featureId]/[tab]/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/feature/[featureId]/page/server-reference-manifest.json +36 -36
- package/web/.next/server/app/(dashboard)/feature/[featureId]/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/feature/[featureId]/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/page/server-reference-manifest.json +26 -26
- package/web/.next/server/app/(dashboard)/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page/server-reference-manifest.json +26 -26
- package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page.js.nft.json +1 -1
- package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/_global-error.html +2 -2
- package/web/.next/server/app/_global-error.rsc +1 -1
- package/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/web/.next/server/app/_not-found/page/server-reference-manifest.json +3 -3
- package/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/api/attachments/preview/route.js.nft.json +1 -1
- package/web/.next/server/app/api/evidence/route.js.nft.json +1 -1
- package/web/.next/server/app/api/graph-data/route.js.nft.json +1 -1
- package/web/.next/server/app/api/sessions/route.js +2 -3
- package/web/.next/server/app/api/sessions/route.js.nft.json +1 -1
- package/web/.next/server/app/api/sessions-batch/route/app-paths-manifest.json +3 -0
- package/web/.next/server/app/api/sessions-batch/route/build-manifest.json +11 -0
- package/web/.next/server/app/api/sessions-batch/route/server-reference-manifest.json +4 -0
- package/web/.next/server/app/api/sessions-batch/route.js +7 -0
- package/web/.next/server/app/api/sessions-batch/route.js.map +5 -0
- package/web/.next/server/app/api/sessions-batch/route.js.nft.json +1 -0
- package/web/.next/server/app/api/sessions-batch/route_client-reference-manifest.js +2 -0
- package/web/.next/server/app/settings/page/server-reference-manifest.json +8 -8
- package/web/.next/server/app/settings/page.js.nft.json +1 -1
- package/web/.next/server/app/settings/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/skills/page/server-reference-manifest.json +8 -8
- package/web/.next/server/app/skills/page.js.nft.json +1 -1
- package/web/.next/server/app/skills/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/tools/page/server-reference-manifest.json +8 -8
- package/web/.next/server/app/tools/page.js.nft.json +1 -1
- package/web/.next/server/app/tools/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/version/page/server-reference-manifest.json +3 -3
- package/web/.next/server/app/version/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app-paths-manifest.json +1 -0
- package/web/.next/server/chunks/403f9_next_dist_esm_build_templates_app-route_4d623b8e.js +1 -1
- package/web/.next/server/chunks/403f9_next_dist_esm_build_templates_app-route_4d623b8e.js.map +1 -1
- package/web/.next/server/chunks/744ca_web__next-internal_server_app_api_sessions-batch_route_actions_4859f283.js +3 -0
- package/web/.next/server/chunks/[root-of-the-server]__0d33c29e._.js +3 -0
- package/web/.next/server/chunks/[root-of-the-server]__0d33c29e._.js.map +1 -0
- package/web/.next/server/chunks/[root-of-the-server]__2f61738a._.js +3 -0
- package/web/.next/server/chunks/[root-of-the-server]__2f61738a._.js.map +1 -0
- package/web/.next/server/chunks/[root-of-the-server]__a402b567._.js +1 -1
- package/web/.next/server/chunks/ssr/744ca_web_components_common_control-center-drawer_create-drawer-client_tsx_5e26fc0a._.js +1 -1
- package/web/.next/server/chunks/ssr/744ca_web_components_common_control-center-drawer_create-drawer-client_tsx_5e26fc0a._.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__2138fa7e._.js +2 -2
- package/web/.next/server/chunks/ssr/[root-of-the-server]__29580090._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__29580090._.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__357d99f9._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__3ef34e4c._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__43f51aa6._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__43f51aa6._.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__815546bd._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__815546bd._.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__aad040c0._.js +2 -2
- package/web/.next/server/chunks/ssr/[root-of-the-server]__aad040c0._.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__c094882b._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__c094882b._.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__d48c5b11._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__d48c5b11._.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__dac5dbf1._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__dac5dbf1._.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__fae8b355._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__fae8b355._.js.map +1 -1
- package/web/.next/server/chunks/ssr/_05c23ad9._.js +1 -1
- package/web/.next/server/chunks/ssr/_05c23ad9._.js.map +1 -1
- package/web/.next/server/chunks/ssr/_0c5f56e3._.js +2 -2
- package/web/.next/server/chunks/ssr/_0c5f56e3._.js.map +1 -1
- package/web/.next/server/chunks/ssr/_16eb4fec._.js +1 -1
- package/web/.next/server/chunks/ssr/_16eb4fec._.js.map +1 -1
- package/web/.next/server/chunks/ssr/_1b719e7f._.js +1 -1
- package/web/.next/server/chunks/ssr/_1b719e7f._.js.map +1 -1
- package/web/.next/server/chunks/ssr/_37e8548b._.js +1 -1
- package/web/.next/server/chunks/ssr/_37e8548b._.js.map +1 -1
- package/web/.next/server/chunks/ssr/{_fe63a7f9._.js → _458e9a64._.js} +2 -2
- package/web/.next/server/chunks/ssr/{_fe63a7f9._.js.map → _458e9a64._.js.map} +1 -1
- package/web/.next/server/chunks/ssr/_5022e2b1._.js +4 -0
- package/web/.next/server/chunks/ssr/_5022e2b1._.js.map +1 -0
- package/web/.next/server/chunks/ssr/_55d763e2._.js +1 -1
- package/web/.next/server/chunks/ssr/_55d763e2._.js.map +1 -1
- package/web/.next/server/chunks/ssr/_6256a985._.js +1 -1
- package/web/.next/server/chunks/ssr/_6256a985._.js.map +1 -1
- package/web/.next/server/chunks/ssr/_64bdfc6f._.js +2 -2
- package/web/.next/server/chunks/ssr/_64bdfc6f._.js.map +1 -1
- package/web/.next/server/chunks/ssr/_8fcc39d4._.js +1 -1
- package/web/.next/server/chunks/ssr/_b71645b4._.js +1 -1
- package/web/.next/server/chunks/ssr/_b71645b4._.js.map +1 -1
- package/web/.next/server/chunks/ssr/_d8575088._.js +1 -1
- package/web/.next/server/chunks/ssr/_d8575088._.js.map +1 -1
- package/web/.next/server/chunks/ssr/b1a17_presentation_web_components_features_settings_settings-page-client_tsx_6ed9d5f8._.js +1 -1
- package/web/.next/server/chunks/ssr/b1a17_presentation_web_components_features_settings_settings-page-client_tsx_6ed9d5f8._.js.map +1 -1
- package/web/.next/server/chunks/ssr/{src_presentation_web_7b2fda40._.js → src_presentation_web_35159458._.js} +2 -2
- package/web/.next/server/chunks/ssr/{src_presentation_web_7b2fda40._.js.map → src_presentation_web_35159458._.js.map} +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_skills_page_actions_1b176e3c.js +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_skills_page_actions_1b176e3c.js.map +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_tools_page_actions_bd9f0dda.js +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_tools_page_actions_bd9f0dda.js.map +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_app_actions_open-ide_ts_baaca5d5._.js +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_components_e599bb8c._.js +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_components_e599bb8c._.js.map +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_components_features_control-center_7ac3562e._.js +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_components_features_control-center_7ac3562e._.js.map +1 -1
- package/web/.next/server/pages/500.html +2 -2
- package/web/.next/server/server-reference-manifest.js +1 -1
- package/web/.next/server/server-reference-manifest.json +44 -44
- package/web/.next/static/chunks/{0137d4850cab3c45.js → 24b1c1e60fd3b7b5.js} +2 -2
- package/web/.next/static/chunks/{c731682077fbac4f.js → 3e7a130816229439.js} +1 -1
- package/web/.next/static/chunks/{7c5131e33516a325.js → 3f1b33498b472b00.js} +1 -1
- package/web/.next/static/chunks/{04869f1d3f5d9071.js → 4ef564fb1174e497.js} +1 -1
- package/web/.next/static/chunks/75834e430247b325.js +1 -0
- package/web/.next/static/chunks/79dc2e2f1c2ff519.js +1 -0
- package/web/.next/static/chunks/{063a24b49d9818a0.js → a086f8dfef2c3325.js} +1 -1
- package/web/.next/static/chunks/{48850e202dd814ac.js → a6363f73e05ccf47.js} +1 -1
- package/web/.next/static/chunks/{6f76e63ead3fac2e.js → b7126c0b3a97e77e.js} +1 -1
- package/web/.next/static/chunks/d3df6e6434e16519.js +1 -0
- package/web/.next/static/chunks/eaca60cc3ab0bf9f.js +2 -0
- package/web/.next/static/chunks/{9dad6769d10a32df.js → fe5d48f8ca483935.js} +1 -1
- package/web/.next/server/chunks/403f9_next_dist_esm_build_templates_app-route_ff60e4a5.js +0 -3
- package/web/.next/server/chunks/403f9_next_dist_esm_build_templates_app-route_ff60e4a5.js.map +0 -1
- package/web/.next/server/chunks/[externals]__448264a3._.js +0 -3
- package/web/.next/server/chunks/ssr/_4533d6f8._.js +0 -4
- package/web/.next/server/chunks/ssr/_4533d6f8._.js.map +0 -1
- package/web/.next/static/chunks/21e82fee1a7e1668.js +0 -1
- package/web/.next/static/chunks/682563e4503cbd58.js +0 -1
- package/web/.next/static/chunks/683b1d85e789c2eb.js +0 -2
- package/web/.next/static/chunks/d62ae5e449d87057.js +0 -1
- /package/web/.next/server/chunks/{[externals]__448264a3._.js.map → 744ca_web__next-internal_server_app_api_sessions-batch_route_actions_4859f283.js.map} +0 -0
- /package/web/.next/static/{zYKuE1zbe1UWwAJv5EVwg → 1CQHYZVn3VajyhdvnsCaw}/_buildManifest.js +0 -0
- /package/web/.next/static/{zYKuE1zbe1UWwAJv5EVwg → 1CQHYZVn3VajyhdvnsCaw}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/static/{zYKuE1zbe1UWwAJv5EVwg → 1CQHYZVn3VajyhdvnsCaw}/_ssgManifest.js +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-control-center-state.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/features/control-center/use-control-center-state.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAClE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AACxE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,qCAAqC,CAAC;AAC9E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAC;AAC5E,OAAO,EAGL,KAAK,eAAe,EACrB,MAAM,yBAAyB,CAAC;AAkBjC,OAAO,EAAiB,KAAK,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAM7E,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,KAAK,EAAE,IAAI,EAAE,CAAC;IACd,aAAa,EAAE,CAAC,OAAO,EAAE,UAAU,CAAC,cAAc,CAAC,EAAE,KAAK,IAAI,CAAC;IAC/D,aAAa,EAAE,CAAC,UAAU,EAAE,UAAU,KAAK,IAAI,CAAC;IAChD,mBAAmB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK;QAAE,QAAQ,EAAE,OAAO,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;IAC/E,YAAY,EAAE,CAAC,SAAS,EAAE,eAAe,KAAK,IAAI,CAAC;IACnD,oBAAoB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAClD,mBAAmB,EAAE,CACnB,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE,OAAO,EACjB,aAAa,CAAC,EAAE,OAAO,EACvB,OAAO,CAAC,EAAE,OAAO,KACd,IAAI,CAAC;IACV,kBAAkB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAChD,kBAAkB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAChD,iBAAiB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/C,sBAAsB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IACpD,sBAAsB,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAChE,iBAAiB,EAAE,CACjB,YAAY,EAAE,MAAM,GAAG,IAAI,EAC3B,YAAY,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC,EACvC,QAAQ,CAAC,EAAE,MAAM,KACd,MAAM,CAAC;IACZ,yDAAyD;IACzD,YAAY,EAAE,OAAO,CAAC;IACtB,0CAA0C;IAC1C,eAAe,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACzC,wDAAwD;IACxD,wBAAwB,EAAE,CAAC,aAAa,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;IACxE,gDAAgD;IAChD,iBAAiB,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,kBAAkB,GAAG,SAAS,CAAC;IACtE,0EAA0E;IAC1E,YAAY,EAAE,CAAC,SAAS,EAAE,cAAc,KAAK,IAAI,CAAC;CACnD;AAQD,wBAAgB,qBAAqB,CACnC,YAAY,EAAE,cAAc,EAAE,EAC9B,YAAY,EAAE,IAAI,EAAE,GACnB,kBAAkB,
|
|
1
|
+
{"version":3,"file":"use-control-center-state.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/features/control-center/use-control-center-state.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAClE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AACxE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,qCAAqC,CAAC;AAC9E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAC;AAC5E,OAAO,EAGL,KAAK,eAAe,EACrB,MAAM,yBAAyB,CAAC;AAkBjC,OAAO,EAAiB,KAAK,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAM7E,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,KAAK,EAAE,IAAI,EAAE,CAAC;IACd,aAAa,EAAE,CAAC,OAAO,EAAE,UAAU,CAAC,cAAc,CAAC,EAAE,KAAK,IAAI,CAAC;IAC/D,aAAa,EAAE,CAAC,UAAU,EAAE,UAAU,KAAK,IAAI,CAAC;IAChD,mBAAmB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK;QAAE,QAAQ,EAAE,OAAO,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;IAC/E,YAAY,EAAE,CAAC,SAAS,EAAE,eAAe,KAAK,IAAI,CAAC;IACnD,oBAAoB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAClD,mBAAmB,EAAE,CACnB,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE,OAAO,EACjB,aAAa,CAAC,EAAE,OAAO,EACvB,OAAO,CAAC,EAAE,OAAO,KACd,IAAI,CAAC;IACV,kBAAkB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAChD,kBAAkB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAChD,iBAAiB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/C,sBAAsB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IACpD,sBAAsB,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAChE,iBAAiB,EAAE,CACjB,YAAY,EAAE,MAAM,GAAG,IAAI,EAC3B,YAAY,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC,EACvC,QAAQ,CAAC,EAAE,MAAM,KACd,MAAM,CAAC;IACZ,yDAAyD;IACzD,YAAY,EAAE,OAAO,CAAC;IACtB,0CAA0C;IAC1C,eAAe,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACzC,wDAAwD;IACxD,wBAAwB,EAAE,CAAC,aAAa,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;IACxE,gDAAgD;IAChD,iBAAiB,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,kBAAkB,GAAG,SAAS,CAAC;IACtE,0EAA0E;IAC1E,YAAY,EAAE,CAAC,SAAS,EAAE,cAAc,KAAK,IAAI,CAAC;CACnD;AAQD,wBAAgB,qBAAqB,CACnC,YAAY,EAAE,cAAc,EAAE,EAC9B,YAAY,EAAE,IAAI,EAAE,GACnB,kBAAkB,CAilBpB"}
|
package/dist/src/presentation/web/components/features/control-center/use-control-center-state.js
CHANGED
|
@@ -18,7 +18,7 @@ import { createLogger } from '../../../lib/logger.js';
|
|
|
18
18
|
import { mapEventTypeToState, resolveSseEventUpdates, } from '../../common/feature-node/derive-feature-state.js';
|
|
19
19
|
import { useGraphState } from '../../../hooks/use-graph-state.js';
|
|
20
20
|
const log = createLogger('[Polling]');
|
|
21
|
-
const POLL_INTERVAL_MS =
|
|
21
|
+
const POLL_INTERVAL_MS = 15_000;
|
|
22
22
|
/** Must match the message string emitted by the SSE route in agent-events/route.ts */
|
|
23
23
|
const METADATA_UPDATED_MESSAGE = 'Feature metadata updated';
|
|
24
24
|
let nextFeatureId = 0;
|
|
@@ -175,6 +175,9 @@ export function useControlCenterState(initialNodes, initialEdges) {
|
|
|
175
175
|
useEffect(() => {
|
|
176
176
|
log.debug(`polling enabled (${POLL_INTERVAL_MS}ms interval)`);
|
|
177
177
|
const timer = setInterval(async () => {
|
|
178
|
+
// Skip when tab is hidden — no point polling for a user who isn't looking.
|
|
179
|
+
if (document.hidden)
|
|
180
|
+
return;
|
|
178
181
|
// Skip fetch entirely while a mutation is in-flight — the response
|
|
179
182
|
// would contain pre-mutation data that reconcile would discard anyway.
|
|
180
183
|
if (isMutating())
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app-sidebar.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/layouts/app-sidebar/app-sidebar.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"app-sidebar.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/layouts/app-sidebar/app-sidebar.tsx"],"names":[],"mappings":"AA4BA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2CAA2C,CAAC;AAI/E,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAE7D,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,aAAa,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,YAAY,EAAE,iBAAiB,CAAC;IAEhC,cAAc,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;CAC9C;AAED,wBAAgB,UAAU,CAAC,EACzB,QAAQ,EACR,YAAY,EAEZ,cAAc,GACf,EAAE,eAAe,2CAoMjB"}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { usePathname } from 'next/navigation';
|
|
4
|
-
import Link from 'next/link';
|
|
5
4
|
import { Home, Moon, Sun, Volume2, VolumeOff, Wrench, Puzzle, Settings } from 'lucide-react';
|
|
6
5
|
import { Sidebar, SidebarHeader, SidebarContent, SidebarFooter, SidebarMenu, SidebarMenuItem, SidebarMenuButton, SidebarRail, useSidebar, } from '../../ui/sidebar.js';
|
|
7
6
|
import { ScrollArea } from '../../ui/scroll-area.js';
|
|
@@ -39,39 +38,39 @@ export function AppSidebar({ features, featureFlags, onFeatureClick, }) {
|
|
|
39
38
|
'flex min-w-0 flex-1 items-center gap-2 overflow-hidden px-2',
|
|
40
39
|
'transition-opacity duration-200 ease-out',
|
|
41
40
|
expandedVisible ? 'opacity-100' : 'opacity-0',
|
|
42
|
-
].join(' '), "aria-hidden": !expandedVisible, children: [_jsx(ShepLogo, { className: "shrink-0", size: 20, variant: versionData.isDev ? 'dev' : 'default' }), _jsx("span", { className: "truncate text-sm font-semibold tracking-tight", children: "Shep" }), _jsx(VersionBadge, { version: versionData.version, branch: versionData.branch || undefined, commitHash: versionData.commitHash || undefined, isDev: versionData.isDev, packageName: versionData.packageName, description: versionData.description, instancePath: versionData.instancePath || undefined })] })) : null, _jsx(SidebarCollapseToggle, { className: "shrink-0 transition-all duration-200" })] }) }), _jsx(SidebarNavItem, { icon: Home, label: "Control Center", href: "/", active: pathname === '/' }), _jsx(SidebarNavItem, { icon: Wrench, label: "Tools", href: "/tools", active: pathname === '/tools' }), featureFlags.skills ? (_jsx(SidebarNavItem, { icon: Puzzle, label: "Skills", href: "/skills", active: pathname === '/skills' })) : null] }) }), _jsx(SidebarContent, { children: showExpanded ? (_jsxs("div", { className: [
|
|
41
|
+
].join(' '), "aria-hidden": !expandedVisible, children: [_jsx(ShepLogo, { className: "shrink-0", size: 20, variant: versionData.isDev ? 'dev' : 'default' }), _jsx("span", { className: "truncate text-sm font-semibold tracking-tight", children: "Shep" }), _jsx(VersionBadge, { version: versionData.version, branch: versionData.branch || undefined, commitHash: versionData.commitHash || undefined, isDev: versionData.isDev, packageName: versionData.packageName, description: versionData.description, instancePath: versionData.instancePath || undefined })] })) : null, _jsx(SidebarCollapseToggle, { className: "shrink-0 transition-all duration-200" })] }) }), _jsx(SidebarNavItem, { icon: Home, label: "Control Center", href: "/", active: pathname === '/' }), _jsx(SidebarNavItem, { icon: Wrench, label: "Tools", href: "/tools", active: pathname === '/tools' }), featureFlags.skills ? (_jsx(SidebarNavItem, { icon: Puzzle, label: "Skills", href: "/skills", active: pathname === '/skills' })) : null, _jsx(SidebarNavItem, { icon: Settings, label: "Settings", href: "/settings", active: pathname === '/settings' })] }) }), _jsx(SidebarContent, { children: showExpanded ? (_jsxs("div", { className: [
|
|
43
42
|
'flex min-h-0 flex-1 flex-col overflow-hidden transition-opacity duration-200 ease-out',
|
|
44
43
|
'[&_[data-sidebar=group-label]]:!mt-0 [&_[data-sidebar=group-label]]:!opacity-100 [&_[data-sidebar=group-label]]:!transition-none',
|
|
45
44
|
expandedVisible ? 'opacity-100' : 'opacity-0',
|
|
46
|
-
].join(' '), children: [_jsx(SidebarSectionHeader, { label: "Features" }), _jsx(ScrollArea, { className: "min-h-0 flex-1", children: grouped.map(({ key, label, items }) => items.length > 0 ? (_jsx(FeatureStatusGroup, { label: label, count: items.length, children: items.map((feature) => (_jsx(FeatureListItem, { name: feature.name, status: feature.status, startedAt: feature.startedAt, duration: feature.duration, agentType: feature.agentType, modelId: feature.modelId, onClick: onFeatureClick ? () => onFeatureClick(feature.featureId) : undefined }, feature.featureId))) }, key)) : null) })] })) : null }), _jsx(SidebarFooter, { className: "border-t p-2", children: _jsx(SidebarMenu, { children: _jsx(SidebarMenuItem, { children:
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
45
|
+
].join(' '), children: [_jsx(SidebarSectionHeader, { label: "Features" }), _jsx(ScrollArea, { className: "min-h-0 flex-1", children: grouped.map(({ key, label, items }) => items.length > 0 ? (_jsx(FeatureStatusGroup, { label: label, count: items.length, children: items.map((feature) => (_jsx(FeatureListItem, { name: feature.name, status: feature.status, startedAt: feature.startedAt, duration: feature.duration, agentType: feature.agentType, modelId: feature.modelId, onClick: onFeatureClick ? () => onFeatureClick(feature.featureId) : undefined }, feature.featureId))) }, key)) : null) })] })) : null }), _jsx(SidebarFooter, { className: "border-t p-2", children: _jsx(SidebarMenu, { children: _jsx(SidebarMenuItem, { children: _jsx("div", { className: "flex items-center gap-1", children: _jsxs(TooltipProvider, { children: [_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsxs(SidebarMenuButton, { className: "w-auto flex-none", onClick: (e) => {
|
|
46
|
+
const currentResolved = theme === 'system' ? resolvedTheme : theme;
|
|
47
|
+
const goingToDark = currentResolved !== 'dark';
|
|
48
|
+
const newTheme = theme === 'system'
|
|
49
|
+
? resolvedTheme === 'dark'
|
|
50
|
+
? 'light'
|
|
51
|
+
: 'dark'
|
|
52
|
+
: theme === 'dark'
|
|
53
|
+
? 'light'
|
|
54
|
+
: 'dark';
|
|
55
|
+
if (goingToDark) {
|
|
56
|
+
toggleOnSound.play();
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
toggleOffSound.play();
|
|
60
|
+
}
|
|
61
|
+
const prefersReducedMotion = typeof window !== 'undefined' &&
|
|
62
|
+
window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
|
63
|
+
if (!('startViewTransition' in document) || prefersReducedMotion) {
|
|
64
|
+
setTheme(newTheme);
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
document.documentElement.style.setProperty('--x', `${e.clientX}px`);
|
|
68
|
+
document.documentElement.style.setProperty('--y', `${e.clientY}px`);
|
|
69
|
+
document.startViewTransition(() => {
|
|
70
|
+
setTheme(newTheme);
|
|
71
|
+
});
|
|
72
|
+
}, "aria-label": `Switch to ${resolvedTheme === 'dark' ? 'light' : 'dark'} mode`, children: [_jsx(Sun, { className: "h-4 w-4 scale-100 rotate-0 transition-all dark:scale-0 dark:-rotate-90" }), _jsx(Moon, { className: "absolute h-4 w-4 scale-0 rotate-90 transition-all dark:scale-100 dark:rotate-0" })] }) }), _jsx(TooltipContent, { side: "top", children: resolvedTheme === 'dark' ? 'Light mode' : 'Dark mode' })] }), !collapsed && (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(SidebarMenuButton, { className: "w-auto flex-none", onClick: () => {
|
|
73
|
+
clickSound.play();
|
|
74
|
+
toggleSound();
|
|
75
|
+
}, "aria-label": soundEnabled ? 'Mute sounds' : 'Unmute sounds', children: soundEnabled ? (_jsx(Volume2, { className: "h-4 w-4" })) : (_jsx(VolumeOff, { className: "h-4 w-4" })) }) }), _jsx(TooltipContent, { side: "top", children: soundEnabled ? 'Mute sounds' : 'Unmute sounds' })] }))] }) }) }) }) }), _jsx(SidebarRail, {})] }));
|
|
77
76
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
import type { SessionSummary } from '../components/common/feature-node/feature-sessions-dropdown.js';
|
|
3
|
+
interface SessionsContextValue {
|
|
4
|
+
getSessionsForPath: (path: string) => SessionSummary[];
|
|
5
|
+
hasActiveSessions: (path: string) => boolean;
|
|
6
|
+
}
|
|
7
|
+
export declare function SessionsProvider({ children }: {
|
|
8
|
+
children: ReactNode;
|
|
9
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
10
|
+
export declare function useSessionsContext(): SessionsContextValue;
|
|
11
|
+
export {};
|
|
12
|
+
//# sourceMappingURL=sessions-provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sessions-provider.d.ts","sourceRoot":"","sources":["../../../../../src/presentation/web/hooks/sessions-provider.tsx"],"names":[],"mappings":"AAEA,OAAO,EAQL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AACf,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,4DAA4D,CAAC;AAOjG,UAAU,oBAAoB;IAC5B,kBAAkB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,cAAc,EAAE,CAAC;IACvD,iBAAiB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;CAC9C;AAMD,wBAAgB,gBAAgB,CAAC,EAAE,QAAQ,EAAE,EAAE;IAAE,QAAQ,EAAE,SAAS,CAAA;CAAE,2CAoDrE;AAID,wBAAgB,kBAAkB,IAAI,oBAAoB,CAMzD"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState, } from 'react';
|
|
4
|
+
const POLL_INTERVAL_MS = 30_000;
|
|
5
|
+
const ACTIVE_THRESHOLD_MS = 5 * 60 * 1000; // 5 minutes
|
|
6
|
+
const SessionsContext = createContext(null);
|
|
7
|
+
// ── Provider component ────────────────────────────────────────────────
|
|
8
|
+
export function SessionsProvider({ children }) {
|
|
9
|
+
const [sessionsByPath, setSessionsByPath] = useState(new Map());
|
|
10
|
+
const sessionsByPathRef = useRef(sessionsByPath);
|
|
11
|
+
sessionsByPathRef.current = sessionsByPath;
|
|
12
|
+
const fetchSessions = useCallback(async () => {
|
|
13
|
+
if (document.hidden)
|
|
14
|
+
return;
|
|
15
|
+
try {
|
|
16
|
+
const res = await fetch('/api/sessions-batch');
|
|
17
|
+
if (!res.ok)
|
|
18
|
+
return;
|
|
19
|
+
const data = (await res.json());
|
|
20
|
+
const next = new Map();
|
|
21
|
+
for (const [path, sessions] of Object.entries(data.sessionsByPath)) {
|
|
22
|
+
next.set(path, sessions);
|
|
23
|
+
}
|
|
24
|
+
setSessionsByPath(next);
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
// Silent — stale data is better than no data
|
|
28
|
+
}
|
|
29
|
+
}, []);
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
void fetchSessions();
|
|
32
|
+
const timer = setInterval(() => void fetchSessions(), POLL_INTERVAL_MS);
|
|
33
|
+
return () => clearInterval(timer);
|
|
34
|
+
}, [fetchSessions]);
|
|
35
|
+
const getSessionsForPath = useCallback((path) => sessionsByPathRef.current.get(path) ?? [],
|
|
36
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
37
|
+
[sessionsByPath]);
|
|
38
|
+
const hasActiveSessions = useCallback((path) => {
|
|
39
|
+
const sessions = sessionsByPathRef.current.get(path);
|
|
40
|
+
if (!sessions)
|
|
41
|
+
return false;
|
|
42
|
+
const now = Date.now();
|
|
43
|
+
return sessions.some((s) => s.lastMessageAt && now - new Date(s.lastMessageAt).getTime() < ACTIVE_THRESHOLD_MS);
|
|
44
|
+
},
|
|
45
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
46
|
+
[sessionsByPath]);
|
|
47
|
+
const value = useMemo(() => ({ getSessionsForPath, hasActiveSessions }), [getSessionsForPath, hasActiveSessions]);
|
|
48
|
+
return _jsx(SessionsContext.Provider, { value: value, children: children });
|
|
49
|
+
}
|
|
50
|
+
// ── Consumer hook ─────────────────────────────────────────────────────
|
|
51
|
+
export function useSessionsContext() {
|
|
52
|
+
const ctx = useContext(SessionsContext);
|
|
53
|
+
if (!ctx) {
|
|
54
|
+
return { getSessionsForPath: () => [], hasActiveSessions: () => false };
|
|
55
|
+
}
|
|
56
|
+
return ctx;
|
|
57
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-deploy-action.d.ts","sourceRoot":"","sources":["../../../../../src/presentation/web/hooks/use-deploy-action.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sCAAsC,CAAC;AAM5E,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,SAAS,GAAG,YAAY,CAAC;IACrC,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,aAAa,EAAE,OAAO,CAAC;IACvB,WAAW,EAAE,OAAO,CAAC;IACrB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,MAAM,EAAE,eAAe,GAAG,IAAI,CAAC;IAC/B,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;CACpB;AAMD,wBAAgB,eAAe,CAAC,KAAK,EAAE,iBAAiB,GAAG,IAAI,GAAG,iBAAiB,
|
|
1
|
+
{"version":3,"file":"use-deploy-action.d.ts","sourceRoot":"","sources":["../../../../../src/presentation/web/hooks/use-deploy-action.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sCAAsC,CAAC;AAM5E,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,SAAS,GAAG,YAAY,CAAC;IACrC,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,aAAa,EAAE,OAAO,CAAC;IACvB,WAAW,EAAE,OAAO,CAAC;IACrB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,MAAM,EAAE,eAAe,GAAG,IAAI,CAAC;IAC/B,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;CACpB;AAMD,wBAAgB,eAAe,CAAC,KAAK,EAAE,iBAAiB,GAAG,IAAI,GAAG,iBAAiB,CAwLlF"}
|
|
@@ -27,34 +27,9 @@ export function useDeployAction(input) {
|
|
|
27
27
|
mountedRef.current = false;
|
|
28
28
|
};
|
|
29
29
|
}, []);
|
|
30
|
-
//
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
return;
|
|
34
|
-
let cancelled = false;
|
|
35
|
-
(async () => {
|
|
36
|
-
try {
|
|
37
|
-
const result = await getDeploymentStatus(input.targetId);
|
|
38
|
-
if (cancelled || !mountedRef.current)
|
|
39
|
-
return;
|
|
40
|
-
if (result && result.state !== 'Stopped') {
|
|
41
|
-
log.info(`mount recovery: "${input.targetId}" state=${result.state}, url=${result.url}`);
|
|
42
|
-
setStatus(result.state);
|
|
43
|
-
setUrl(result.url);
|
|
44
|
-
startPolling(input.targetId);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
catch {
|
|
48
|
-
// Server container may not be available (e.g., in tests)
|
|
49
|
-
log.debug(`mount recovery failed for "${input.targetId}" — ignoring`);
|
|
50
|
-
}
|
|
51
|
-
})();
|
|
52
|
-
return () => {
|
|
53
|
-
cancelled = true;
|
|
54
|
-
};
|
|
55
|
-
// Only run on mount (input identity is stable from the caller)
|
|
56
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
57
|
-
}, [input?.targetId]);
|
|
30
|
+
// Mount recovery removed — getGraphData() already enriches nodes with
|
|
31
|
+
// deployment status (get-graph-data.ts lines 180-200). No need to call
|
|
32
|
+
// a server action per node on every mount.
|
|
58
33
|
// Cleanup on unmount
|
|
59
34
|
useEffect(() => {
|
|
60
35
|
return () => {
|
|
@@ -108,32 +83,11 @@ export function useDeployAction(input) {
|
|
|
108
83
|
}
|
|
109
84
|
}, POLL_INTERVAL);
|
|
110
85
|
}, [stopPolling]);
|
|
111
|
-
// Idle poll —
|
|
112
|
-
//
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
return;
|
|
117
|
-
const timer = setInterval(async () => {
|
|
118
|
-
if (!mountedRef.current)
|
|
119
|
-
return;
|
|
120
|
-
try {
|
|
121
|
-
const result = await getDeploymentStatus(input.targetId);
|
|
122
|
-
if (!mountedRef.current)
|
|
123
|
-
return;
|
|
124
|
-
if (result && result.state !== 'Stopped') {
|
|
125
|
-
log.info(`idle poll: "${input.targetId}" state=${result.state}, url=${result.url}`);
|
|
126
|
-
setStatus(result.state);
|
|
127
|
-
setUrl(result.url);
|
|
128
|
-
startPolling(input.targetId);
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
catch {
|
|
132
|
-
// ignore
|
|
133
|
-
}
|
|
134
|
-
}, IDLE_POLL_INTERVAL);
|
|
135
|
-
return () => clearInterval(timer);
|
|
136
|
-
}, [input, status, startPolling]);
|
|
86
|
+
// Idle poll removed — it was calling getDeploymentStatus every 5s for EVERY
|
|
87
|
+
// node on the canvas (~25 nodes = ~5 server action calls/sec). The mount
|
|
88
|
+
// recovery effect above (line 57) already checks on mount. Deployments
|
|
89
|
+
// started externally (e.g., from the drawer) should update the node via
|
|
90
|
+
// reconcile from the graph-data poll instead.
|
|
137
91
|
const handleDeploy = useCallback(async () => {
|
|
138
92
|
if (!input) {
|
|
139
93
|
log.warn('deploy() called but input is null — no-op');
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared session scanning logic used by both /api/sessions and /api/sessions-batch.
|
|
3
|
+
*
|
|
4
|
+
* Scans Claude Code and Cursor session directories for JSONL session files,
|
|
5
|
+
* parsing headers to extract preview, message count, and timestamps.
|
|
6
|
+
*/
|
|
7
|
+
export interface SessionResult {
|
|
8
|
+
id: string;
|
|
9
|
+
agentType: string;
|
|
10
|
+
preview: string | null;
|
|
11
|
+
messageCount: number;
|
|
12
|
+
firstMessageAt: string | null;
|
|
13
|
+
lastMessageAt: string | null;
|
|
14
|
+
createdAt: string | null;
|
|
15
|
+
projectPath: string;
|
|
16
|
+
filePath: string;
|
|
17
|
+
/** mtime for sorting — not sent to client */
|
|
18
|
+
_mtime: number;
|
|
19
|
+
}
|
|
20
|
+
export declare function scanClaudeSessions(repositoryPath: string, limit: number, includeWorktrees?: boolean): Promise<SessionResult[]>;
|
|
21
|
+
export declare function scanCursorSessions(repositoryPath: string, limit: number): Promise<SessionResult[]>;
|
|
22
|
+
/**
|
|
23
|
+
* Scan sessions for a single repository path from all providers.
|
|
24
|
+
* Merges and sorts by recency.
|
|
25
|
+
*/
|
|
26
|
+
export declare function scanSessionsForPath(repositoryPath: string, limit: number, includeWorktrees?: boolean): Promise<SessionResult[]>;
|
|
27
|
+
//# sourceMappingURL=session-scanner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-scanner.d.ts","sourceRoot":"","sources":["../../../../../src/presentation/web/lib/session-scanner.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAOH,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,6CAA6C;IAC7C,MAAM,EAAE,MAAM,CAAC;CAChB;AAsID,wBAAsB,kBAAkB,CACtC,cAAc,EAAE,MAAM,EACtB,KAAK,EAAE,MAAM,EACb,gBAAgB,UAAQ,GACvB,OAAO,CAAC,aAAa,EAAE,CAAC,CA2C1B;AAiED,wBAAsB,kBAAkB,CACtC,cAAc,EAAE,MAAM,EACtB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,aAAa,EAAE,CAAC,CA6D1B;AAED;;;GAGG;AACH,wBAAsB,mBAAmB,CACvC,cAAc,EAAE,MAAM,EACtB,KAAK,EAAE,MAAM,EACb,gBAAgB,UAAQ,GACvB,OAAO,CAAC,aAAa,EAAE,CAAC,CAS1B"}
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared session scanning logic used by both /api/sessions and /api/sessions-batch.
|
|
3
|
+
*
|
|
4
|
+
* Scans Claude Code and Cursor session directories for JSONL session files,
|
|
5
|
+
* parsing headers to extract preview, message count, and timestamps.
|
|
6
|
+
*/
|
|
7
|
+
import { createHash } from 'node:crypto';
|
|
8
|
+
import { homedir } from 'node:os';
|
|
9
|
+
import { join } from 'node:path';
|
|
10
|
+
import { readdir, stat } from 'node:fs/promises';
|
|
11
|
+
// ── Path encoding helpers ─────────────────────────────────────────────
|
|
12
|
+
/**
|
|
13
|
+
* Claude Code encodes paths by replacing '/', '\', '.' with '-'.
|
|
14
|
+
* e.g. /home/user/.shep/repos/abc → -home-user--shep-repos-abc
|
|
15
|
+
*/
|
|
16
|
+
function claudeEncodePath(p) {
|
|
17
|
+
return p.replace(/[/\\.]/g, '-');
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Cursor encodes paths by stripping the leading '/', removing dots,
|
|
21
|
+
* and replacing '/' and '\' with '-'.
|
|
22
|
+
* e.g. /home/user/.shep/repos/abc → home-user-shep-repos-abc
|
|
23
|
+
*/
|
|
24
|
+
function cursorEncodePath(p) {
|
|
25
|
+
return p.replace(/^\//, '').replace(/\./g, '').replace(/[/\\]/g, '-');
|
|
26
|
+
}
|
|
27
|
+
// ── Shared helpers ────────────────────────────────────────────────────
|
|
28
|
+
function extractText(content) {
|
|
29
|
+
if (typeof content === 'string')
|
|
30
|
+
return content;
|
|
31
|
+
if (Array.isArray(content)) {
|
|
32
|
+
for (const block of content) {
|
|
33
|
+
if (typeof block === 'object' && block !== null) {
|
|
34
|
+
const b = block;
|
|
35
|
+
if (b.type === 'text' && typeof b.text === 'string')
|
|
36
|
+
return b.text;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
const PREVIEW_READ_BYTES = 8_192; // 8KB is enough for first few messages
|
|
43
|
+
// ── Claude Code session scanner ───────────────────────────────────────
|
|
44
|
+
async function collectJsonlFiles(projectDir) {
|
|
45
|
+
let entries;
|
|
46
|
+
try {
|
|
47
|
+
entries = await readdir(projectDir);
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return [];
|
|
51
|
+
}
|
|
52
|
+
const jsonlFiles = entries.filter((e) => e.endsWith('.jsonl'));
|
|
53
|
+
const fileInfos = await Promise.allSettled(jsonlFiles.map(async (name) => {
|
|
54
|
+
const filePath = join(projectDir, name);
|
|
55
|
+
const s = await stat(filePath);
|
|
56
|
+
return { name, filePath, mtime: s.mtime.getTime() };
|
|
57
|
+
}));
|
|
58
|
+
return fileInfos
|
|
59
|
+
.filter((r) => r.status === 'fulfilled')
|
|
60
|
+
.map((r) => r.value);
|
|
61
|
+
}
|
|
62
|
+
async function parseClaudeSession(filePath, fileName, mtime, repositoryPath) {
|
|
63
|
+
const { createReadStream } = await import('node:fs');
|
|
64
|
+
const id = fileName.replace('.jsonl', '');
|
|
65
|
+
let preview = null;
|
|
66
|
+
let firstTimestamp = null;
|
|
67
|
+
let messageCount = 0;
|
|
68
|
+
const head = await new Promise((resolve) => {
|
|
69
|
+
const chunks = [];
|
|
70
|
+
let size = 0;
|
|
71
|
+
const stream = createReadStream(filePath, { end: PREVIEW_READ_BYTES - 1 });
|
|
72
|
+
stream.on('data', (chunk) => {
|
|
73
|
+
chunks.push(chunk);
|
|
74
|
+
size += chunk.length;
|
|
75
|
+
});
|
|
76
|
+
stream.on('end', () => resolve(Buffer.concat(chunks, size).toString('utf-8')));
|
|
77
|
+
stream.on('error', () => resolve(''));
|
|
78
|
+
});
|
|
79
|
+
if (!head)
|
|
80
|
+
return null;
|
|
81
|
+
const lines = head.split('\n').filter((l) => l.trim());
|
|
82
|
+
for (const line of lines) {
|
|
83
|
+
try {
|
|
84
|
+
const entry = JSON.parse(line);
|
|
85
|
+
if (entry.type === 'user' || entry.type === 'assistant') {
|
|
86
|
+
const role = entry.message?.role;
|
|
87
|
+
if (role === 'user' || role === 'assistant') {
|
|
88
|
+
messageCount++;
|
|
89
|
+
if (entry.timestamp) {
|
|
90
|
+
firstTimestamp ??= entry.timestamp;
|
|
91
|
+
}
|
|
92
|
+
if (role === 'user' && preview === null) {
|
|
93
|
+
preview = extractText(entry.message?.content);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
break;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
if (messageCount === 0)
|
|
103
|
+
return null;
|
|
104
|
+
const mtimeIso = new Date(mtime).toISOString();
|
|
105
|
+
return {
|
|
106
|
+
id,
|
|
107
|
+
agentType: 'claude-code',
|
|
108
|
+
preview,
|
|
109
|
+
messageCount,
|
|
110
|
+
firstMessageAt: firstTimestamp,
|
|
111
|
+
lastMessageAt: mtimeIso,
|
|
112
|
+
createdAt: firstTimestamp ?? mtimeIso,
|
|
113
|
+
projectPath: repositoryPath,
|
|
114
|
+
filePath,
|
|
115
|
+
_mtime: mtime,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
export async function scanClaudeSessions(repositoryPath, limit, includeWorktrees = false) {
|
|
119
|
+
const dirName = claudeEncodePath(repositoryPath);
|
|
120
|
+
const projectsRoot = join(homedir(), '.claude', 'projects');
|
|
121
|
+
const primaryDir = join(projectsRoot, dirName);
|
|
122
|
+
let allFiles = await collectJsonlFiles(primaryDir);
|
|
123
|
+
if (includeWorktrees) {
|
|
124
|
+
try {
|
|
125
|
+
const allDirs = await readdir(projectsRoot);
|
|
126
|
+
const prefixMatches = allDirs.filter((d) => d !== dirName && d.startsWith(dirName));
|
|
127
|
+
const normalizedRepoPath = repositoryPath.replace(/\\/g, '/');
|
|
128
|
+
const repoHash = createHash('sha256').update(normalizedRepoPath).digest('hex').slice(0, 16);
|
|
129
|
+
const shepHome = join(homedir(), '.shep').replace(/\\/g, '/');
|
|
130
|
+
const shepWorktreePrefix = claudeEncodePath(join(shepHome, 'repos', repoHash));
|
|
131
|
+
const shepMatches = allDirs.filter((d) => d.startsWith(shepWorktreePrefix) && !prefixMatches.includes(d) && d !== dirName);
|
|
132
|
+
const worktreeDirs = [...prefixMatches, ...shepMatches];
|
|
133
|
+
const worktreeResults = await Promise.all(worktreeDirs.map((d) => collectJsonlFiles(join(projectsRoot, d))));
|
|
134
|
+
for (const files of worktreeResults) {
|
|
135
|
+
allFiles = allFiles.concat(files);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
catch {
|
|
139
|
+
// projectsRoot doesn't exist — no sessions at all
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
const valid = allFiles.sort((a, b) => b.mtime - a.mtime).slice(0, limit);
|
|
143
|
+
const results = await Promise.allSettled(valid.map(async (fi) => parseClaudeSession(fi.filePath, fi.name, fi.mtime, repositoryPath)));
|
|
144
|
+
return results
|
|
145
|
+
.filter((r) => r.status === 'fulfilled')
|
|
146
|
+
.map((r) => r.value)
|
|
147
|
+
.filter((s) => s !== null);
|
|
148
|
+
}
|
|
149
|
+
// ── Cursor session scanner ────────────────────────────────────────────
|
|
150
|
+
async function parseCursorSession(filePath, fileName, mtime, repositoryPath) {
|
|
151
|
+
const { createReadStream } = await import('node:fs');
|
|
152
|
+
const id = fileName.replace('.jsonl', '');
|
|
153
|
+
const head = await new Promise((resolve) => {
|
|
154
|
+
const chunks = [];
|
|
155
|
+
let size = 0;
|
|
156
|
+
const stream = createReadStream(filePath, { end: PREVIEW_READ_BYTES - 1 });
|
|
157
|
+
stream.on('data', (chunk) => {
|
|
158
|
+
chunks.push(chunk);
|
|
159
|
+
size += chunk.length;
|
|
160
|
+
});
|
|
161
|
+
stream.on('end', () => resolve(Buffer.concat(chunks, size).toString('utf-8')));
|
|
162
|
+
stream.on('error', () => resolve(''));
|
|
163
|
+
});
|
|
164
|
+
if (!head)
|
|
165
|
+
return null;
|
|
166
|
+
let preview = null;
|
|
167
|
+
let messageCount = 0;
|
|
168
|
+
const lines = head.split('\n').filter((l) => l.trim());
|
|
169
|
+
for (const line of lines) {
|
|
170
|
+
try {
|
|
171
|
+
const entry = JSON.parse(line);
|
|
172
|
+
if (entry.role === 'user' || entry.role === 'assistant') {
|
|
173
|
+
messageCount++;
|
|
174
|
+
if (entry.role === 'user' && preview === null) {
|
|
175
|
+
preview = extractText(entry.message?.content);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
catch {
|
|
180
|
+
break;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
if (messageCount === 0)
|
|
184
|
+
return null;
|
|
185
|
+
const mtimeIso = new Date(mtime).toISOString();
|
|
186
|
+
return {
|
|
187
|
+
id,
|
|
188
|
+
agentType: 'cursor',
|
|
189
|
+
preview,
|
|
190
|
+
messageCount,
|
|
191
|
+
firstMessageAt: mtimeIso,
|
|
192
|
+
lastMessageAt: mtimeIso,
|
|
193
|
+
createdAt: mtimeIso,
|
|
194
|
+
projectPath: repositoryPath,
|
|
195
|
+
filePath,
|
|
196
|
+
_mtime: mtime,
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
export async function scanCursorSessions(repositoryPath, limit) {
|
|
200
|
+
const dirName = cursorEncodePath(repositoryPath);
|
|
201
|
+
const transcriptsDir = join(homedir(), '.cursor', 'projects', dirName, 'agent-transcripts');
|
|
202
|
+
let entries;
|
|
203
|
+
try {
|
|
204
|
+
entries = await readdir(transcriptsDir);
|
|
205
|
+
}
|
|
206
|
+
catch {
|
|
207
|
+
return [];
|
|
208
|
+
}
|
|
209
|
+
const fileInfos = await Promise.allSettled(entries.map(async (entry) => {
|
|
210
|
+
const entryPath = join(transcriptsDir, entry);
|
|
211
|
+
const s = await stat(entryPath);
|
|
212
|
+
if (s.isFile() && entry.endsWith('.jsonl')) {
|
|
213
|
+
return { name: entry, filePath: entryPath, mtime: s.mtime.getTime() };
|
|
214
|
+
}
|
|
215
|
+
if (s.isDirectory()) {
|
|
216
|
+
const jsonlPath = join(entryPath, `${entry}.jsonl`);
|
|
217
|
+
try {
|
|
218
|
+
const jsonlStat = await stat(jsonlPath);
|
|
219
|
+
return {
|
|
220
|
+
name: `${entry}.jsonl`,
|
|
221
|
+
filePath: jsonlPath,
|
|
222
|
+
mtime: jsonlStat.mtime.getTime(),
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
catch {
|
|
226
|
+
return null;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
return null;
|
|
230
|
+
}));
|
|
231
|
+
const valid = fileInfos
|
|
232
|
+
.filter((r) => r.status === 'fulfilled')
|
|
233
|
+
.map((r) => r.value)
|
|
234
|
+
.filter((v) => v !== null)
|
|
235
|
+
.sort((a, b) => b.mtime - a.mtime)
|
|
236
|
+
.slice(0, limit);
|
|
237
|
+
const results = await Promise.allSettled(valid.map(async (fi) => parseCursorSession(fi.filePath, fi.name, fi.mtime, repositoryPath)));
|
|
238
|
+
return results
|
|
239
|
+
.filter((r) => r.status === 'fulfilled')
|
|
240
|
+
.map((r) => r.value)
|
|
241
|
+
.filter((s) => s !== null);
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Scan sessions for a single repository path from all providers.
|
|
245
|
+
* Merges and sorts by recency.
|
|
246
|
+
*/
|
|
247
|
+
export async function scanSessionsForPath(repositoryPath, limit, includeWorktrees = false) {
|
|
248
|
+
const [claudeSessions, cursorSessions] = await Promise.all([
|
|
249
|
+
scanClaudeSessions(repositoryPath, limit, includeWorktrees),
|
|
250
|
+
scanCursorSessions(repositoryPath, limit),
|
|
251
|
+
]);
|
|
252
|
+
return [...claudeSessions, ...cursorSessions]
|
|
253
|
+
.sort((a, b) => b._mtime - a._mtime)
|
|
254
|
+
.slice(0, Math.min(limit, 50));
|
|
255
|
+
}
|