@a5c-ai/babysitter-observer-dashboard 5.0.1-staging.7a8768ec
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/.next/BUILD_ID +1 -0
- package/.next/app-path-routes-manifest.json +16 -0
- package/.next/build-manifest.json +23 -0
- package/.next/export-marker.json +6 -0
- package/.next/fallback-build-manifest.json +13 -0
- package/.next/images-manifest.json +68 -0
- package/.next/next-minimal-server.js.nft.json +1 -0
- package/.next/next-server.js.nft.json +1 -0
- package/.next/package.json +1 -0
- package/.next/prerender-manifest.json +114 -0
- package/.next/required-server-files.json +334 -0
- package/.next/routes-manifest.json +139 -0
- package/.next/server/app/_global-error/page/app-paths-manifest.json +3 -0
- package/.next/server/app/_global-error/page/build-manifest.json +19 -0
- package/.next/server/app/_global-error/page/next-font-manifest.json +6 -0
- package/.next/server/app/_global-error/page/react-loadable-manifest.json +1 -0
- package/.next/server/app/_global-error/page/server-reference-manifest.json +4 -0
- package/.next/server/app/_global-error/page.js +9 -0
- package/.next/server/app/_global-error/page.js.map +5 -0
- package/.next/server/app/_global-error/page.js.nft.json +1 -0
- package/.next/server/app/_global-error/page_client-reference-manifest.js +3 -0
- package/.next/server/app/_global-error.html +1 -0
- package/.next/server/app/_global-error.meta +15 -0
- package/.next/server/app/_global-error.rsc +14 -0
- package/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +5 -0
- package/.next/server/app/_global-error.segments/_full.segment.rsc +14 -0
- package/.next/server/app/_global-error.segments/_head.segment.rsc +5 -0
- package/.next/server/app/_global-error.segments/_index.segment.rsc +5 -0
- package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -0
- package/.next/server/app/_not-found/page/app-paths-manifest.json +3 -0
- package/.next/server/app/_not-found/page/build-manifest.json +19 -0
- package/.next/server/app/_not-found/page/next-font-manifest.json +6 -0
- package/.next/server/app/_not-found/page/react-loadable-manifest.json +1 -0
- package/.next/server/app/_not-found/page/server-reference-manifest.json +4 -0
- package/.next/server/app/_not-found/page.js +13 -0
- package/.next/server/app/_not-found/page.js.map +5 -0
- package/.next/server/app/_not-found/page.js.nft.json +1 -0
- package/.next/server/app/_not-found/page_client-reference-manifest.js +3 -0
- package/.next/server/app/_not-found.html +1 -0
- package/.next/server/app/_not-found.meta +16 -0
- package/.next/server/app/_not-found.rsc +19 -0
- package/.next/server/app/_not-found.segments/_full.segment.rsc +19 -0
- package/.next/server/app/_not-found.segments/_head.segment.rsc +6 -0
- package/.next/server/app/_not-found.segments/_index.segment.rsc +8 -0
- package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +6 -0
- package/.next/server/app/_not-found.segments/_not-found.segment.rsc +5 -0
- package/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -0
- package/.next/server/app/api/config/route/app-paths-manifest.json +3 -0
- package/.next/server/app/api/config/route/build-manifest.json +9 -0
- package/.next/server/app/api/config/route/server-reference-manifest.json +4 -0
- package/.next/server/app/api/config/route.js +8 -0
- package/.next/server/app/api/config/route.js.map +5 -0
- package/.next/server/app/api/config/route.js.nft.json +1 -0
- package/.next/server/app/api/config/route_client-reference-manifest.js +3 -0
- package/.next/server/app/api/digest/route/app-paths-manifest.json +3 -0
- package/.next/server/app/api/digest/route/build-manifest.json +9 -0
- package/.next/server/app/api/digest/route/server-reference-manifest.json +4 -0
- package/.next/server/app/api/digest/route.js +9 -0
- package/.next/server/app/api/digest/route.js.map +5 -0
- package/.next/server/app/api/digest/route.js.nft.json +1 -0
- package/.next/server/app/api/digest/route_client-reference-manifest.js +3 -0
- package/.next/server/app/api/runs/[runId]/events/route/app-paths-manifest.json +3 -0
- package/.next/server/app/api/runs/[runId]/events/route/build-manifest.json +9 -0
- package/.next/server/app/api/runs/[runId]/events/route/server-reference-manifest.json +4 -0
- package/.next/server/app/api/runs/[runId]/events/route.js +8 -0
- package/.next/server/app/api/runs/[runId]/events/route.js.map +5 -0
- package/.next/server/app/api/runs/[runId]/events/route.js.nft.json +1 -0
- package/.next/server/app/api/runs/[runId]/events/route_client-reference-manifest.js +3 -0
- package/.next/server/app/api/runs/[runId]/route/app-paths-manifest.json +3 -0
- package/.next/server/app/api/runs/[runId]/route/build-manifest.json +9 -0
- package/.next/server/app/api/runs/[runId]/route/server-reference-manifest.json +4 -0
- package/.next/server/app/api/runs/[runId]/route.js +9 -0
- package/.next/server/app/api/runs/[runId]/route.js.map +5 -0
- package/.next/server/app/api/runs/[runId]/route.js.nft.json +1 -0
- package/.next/server/app/api/runs/[runId]/route_client-reference-manifest.js +3 -0
- package/.next/server/app/api/runs/[runId]/tasks/[effectId]/route/app-paths-manifest.json +3 -0
- package/.next/server/app/api/runs/[runId]/tasks/[effectId]/route/build-manifest.json +9 -0
- package/.next/server/app/api/runs/[runId]/tasks/[effectId]/route/server-reference-manifest.json +4 -0
- package/.next/server/app/api/runs/[runId]/tasks/[effectId]/route.js +8 -0
- package/.next/server/app/api/runs/[runId]/tasks/[effectId]/route.js.map +5 -0
- package/.next/server/app/api/runs/[runId]/tasks/[effectId]/route.js.nft.json +1 -0
- package/.next/server/app/api/runs/[runId]/tasks/[effectId]/route_client-reference-manifest.js +3 -0
- package/.next/server/app/api/runs/route/app-paths-manifest.json +3 -0
- package/.next/server/app/api/runs/route/build-manifest.json +9 -0
- package/.next/server/app/api/runs/route/server-reference-manifest.json +4 -0
- package/.next/server/app/api/runs/route.js +9 -0
- package/.next/server/app/api/runs/route.js.map +5 -0
- package/.next/server/app/api/runs/route.js.nft.json +1 -0
- package/.next/server/app/api/runs/route_client-reference-manifest.js +3 -0
- package/.next/server/app/api/stream/route/app-paths-manifest.json +3 -0
- package/.next/server/app/api/stream/route/build-manifest.json +9 -0
- package/.next/server/app/api/stream/route/server-reference-manifest.json +4 -0
- package/.next/server/app/api/stream/route.js +8 -0
- package/.next/server/app/api/stream/route.js.map +5 -0
- package/.next/server/app/api/stream/route.js.nft.json +1 -0
- package/.next/server/app/api/stream/route_client-reference-manifest.js +3 -0
- package/.next/server/app/api/test/route/app-paths-manifest.json +3 -0
- package/.next/server/app/api/test/route/build-manifest.json +9 -0
- package/.next/server/app/api/test/route/server-reference-manifest.json +4 -0
- package/.next/server/app/api/test/route.js +6 -0
- package/.next/server/app/api/test/route.js.map +5 -0
- package/.next/server/app/api/test/route.js.nft.json +1 -0
- package/.next/server/app/api/test/route_client-reference-manifest.js +3 -0
- package/.next/server/app/api/version/route/app-paths-manifest.json +3 -0
- package/.next/server/app/api/version/route/build-manifest.json +9 -0
- package/.next/server/app/api/version/route/server-reference-manifest.json +4 -0
- package/.next/server/app/api/version/route.js +7 -0
- package/.next/server/app/api/version/route.js.map +5 -0
- package/.next/server/app/api/version/route.js.nft.json +1 -0
- package/.next/server/app/api/version/route_client-reference-manifest.js +3 -0
- package/.next/server/app/icon.svg/route/app-paths-manifest.json +3 -0
- package/.next/server/app/icon.svg/route/build-manifest.json +9 -0
- package/.next/server/app/icon.svg/route.js +7 -0
- package/.next/server/app/icon.svg/route.js.map +5 -0
- package/.next/server/app/icon.svg/route.js.nft.json +1 -0
- package/.next/server/app/icon.svg.body +20 -0
- package/.next/server/app/icon.svg.meta +1 -0
- package/.next/server/app/index.html +1 -0
- package/.next/server/app/index.meta +14 -0
- package/.next/server/app/index.rsc +21 -0
- package/.next/server/app/index.segments/__PAGE__.segment.rsc +9 -0
- package/.next/server/app/index.segments/_full.segment.rsc +21 -0
- package/.next/server/app/index.segments/_head.segment.rsc +6 -0
- package/.next/server/app/index.segments/_index.segment.rsc +8 -0
- package/.next/server/app/index.segments/_tree.segment.rsc +2 -0
- package/.next/server/app/page/app-paths-manifest.json +3 -0
- package/.next/server/app/page/build-manifest.json +19 -0
- package/.next/server/app/page/next-font-manifest.json +6 -0
- package/.next/server/app/page/react-loadable-manifest.json +1 -0
- package/.next/server/app/page/server-reference-manifest.json +17 -0
- package/.next/server/app/page.js +15 -0
- package/.next/server/app/page.js.map +5 -0
- package/.next/server/app/page.js.nft.json +1 -0
- package/.next/server/app/page_client-reference-manifest.js +3 -0
- package/.next/server/app/runs/[runId]/page/app-paths-manifest.json +3 -0
- package/.next/server/app/runs/[runId]/page/build-manifest.json +19 -0
- package/.next/server/app/runs/[runId]/page/next-font-manifest.json +6 -0
- package/.next/server/app/runs/[runId]/page/react-loadable-manifest.json +22 -0
- package/.next/server/app/runs/[runId]/page/server-reference-manifest.json +17 -0
- package/.next/server/app/runs/[runId]/page.js +15 -0
- package/.next/server/app/runs/[runId]/page.js.map +5 -0
- package/.next/server/app/runs/[runId]/page.js.nft.json +1 -0
- package/.next/server/app/runs/[runId]/page_client-reference-manifest.js +3 -0
- package/.next/server/app-paths-manifest.json +16 -0
- package/.next/server/chunks/01oi_server_app_api_runs_[runId]_tasks_[effectId]_route_actions_0r72yai.js +3 -0
- package/.next/server/chunks/01oi_server_app_api_runs_[runId]_tasks_[effectId]_route_actions_0r72yai.js.map +1 -0
- package/.next/server/chunks/0h.v__next-internal_server_app_api_runs_[runId]_events_route_actions_0~msldk.js +3 -0
- package/.next/server/chunks/0h.v__next-internal_server_app_api_runs_[runId]_events_route_actions_0~msldk.js.map +1 -0
- package/.next/server/chunks/0h.v__next-internal_server_app_api_runs_[runId]_route_actions_09iz0n6.js +3 -0
- package/.next/server/chunks/0h.v__next-internal_server_app_api_runs_[runId]_route_actions_09iz0n6.js.map +1 -0
- package/.next/server/chunks/0juq_observer-dashboard__next-internal_server_app_api_config_route_actions_0~eypoa.js +3 -0
- package/.next/server/chunks/0juq_observer-dashboard__next-internal_server_app_api_config_route_actions_0~eypoa.js.map +1 -0
- package/.next/server/chunks/0juq_observer-dashboard__next-internal_server_app_api_digest_route_actions_04jj5zs.js +3 -0
- package/.next/server/chunks/0juq_observer-dashboard__next-internal_server_app_api_digest_route_actions_04jj5zs.js.map +1 -0
- package/.next/server/chunks/0juq_observer-dashboard__next-internal_server_app_api_runs_route_actions_0~-t-o4.js +3 -0
- package/.next/server/chunks/0juq_observer-dashboard__next-internal_server_app_api_runs_route_actions_0~-t-o4.js.map +1 -0
- package/.next/server/chunks/0juq_observer-dashboard__next-internal_server_app_api_stream_route_actions_0fkmv2_.js +3 -0
- package/.next/server/chunks/0juq_observer-dashboard__next-internal_server_app_api_stream_route_actions_0fkmv2_.js.map +1 -0
- package/.next/server/chunks/0juq_observer-dashboard__next-internal_server_app_api_test_route_actions_00ugava.js +3 -0
- package/.next/server/chunks/0juq_observer-dashboard__next-internal_server_app_api_test_route_actions_00ugava.js.map +1 -0
- package/.next/server/chunks/0juq_observer-dashboard__next-internal_server_app_api_version_route_actions_0~v3ojm.js +3 -0
- package/.next/server/chunks/0juq_observer-dashboard__next-internal_server_app_api_version_route_actions_0~v3ojm.js.map +1 -0
- package/.next/server/chunks/0juq_observer-dashboard__next-internal_server_app_icon_svg_route_actions_0yypxkm.js +3 -0
- package/.next/server/chunks/0juq_observer-dashboard__next-internal_server_app_icon_svg_route_actions_0yypxkm.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__0.6bt.6._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__0.6bt.6._.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__08kwev1._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__08kwev1._.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__096el.d._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__096el.d._.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__0_bmt4z._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__0_bmt4z._.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__0_ln2d2._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__0_ln2d2._.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__0al3v65._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__0al3v65._.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__0gf516b._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__0gf516b._.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__0kdfw4x._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__0kdfw4x._.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__0pxt00h._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__0pxt00h._.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__0rubnza._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__0rubnza._.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__0tn9iud._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__0tn9iud._.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__0ws5o6i._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__0ws5o6i._.js.map +1 -0
- package/.next/server/chunks/[turbopack]_runtime.js +903 -0
- package/.next/server/chunks/[turbopack]_runtime.js.map +11 -0
- package/.next/server/chunks/node_modules_next_04~_e52._.js +13 -0
- package/.next/server/chunks/node_modules_next_04~_e52._.js.map +1 -0
- package/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_0nyyph-.js +9 -0
- package/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_0nyyph-.js.map +1 -0
- package/.next/server/chunks/packages_observer-dashboard_src_lib_02.vvb.._.js +3 -0
- package/.next/server/chunks/packages_observer-dashboard_src_lib_02.vvb.._.js.map +1 -0
- package/.next/server/chunks/packages_observer-dashboard_src_lib_0rqgpk0._.js +3 -0
- package/.next/server/chunks/packages_observer-dashboard_src_lib_0rqgpk0._.js.map +1 -0
- package/.next/server/chunks/ssr/0juq_observer-dashboard__next-internal_server_app__global-error_page_actions_0ekoxmy.js +3 -0
- package/.next/server/chunks/ssr/0juq_observer-dashboard__next-internal_server_app__global-error_page_actions_0ekoxmy.js.map +1 -0
- package/.next/server/chunks/ssr/0juq_observer-dashboard__next-internal_server_app__not-found_page_actions_09b3ti3.js +3 -0
- package/.next/server/chunks/ssr/0juq_observer-dashboard__next-internal_server_app__not-found_page_actions_09b3ti3.js.map +1 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__006f7~t._.js +4 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__006f7~t._.js.map +1 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__02jgvbi._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__02jgvbi._.js.map +1 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__04j8t~1._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__04j8t~1._.js.map +1 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__09c~s.0._.js +33 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__09c~s.0._.js.map +1 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0cpy61n._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0cpy61n._.js.map +1 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0cwa32j._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0cwa32j._.js.map +1 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0fk_g0j._.js +19 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0fk_g0j._.js.map +1 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0pddpic._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0pddpic._.js.map +1 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0pvtneq._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0pvtneq._.js.map +1 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0xc-2vm._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0xc-2vm._.js.map +1 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__10xgshr._.js +33 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__10xgshr._.js.map +1 -0
- package/.next/server/chunks/ssr/[turbopack]_runtime.js +903 -0
- package/.next/server/chunks/ssr/[turbopack]_runtime.js.map +11 -0
- package/.next/server/chunks/ssr/_00yo1im._.js +3 -0
- package/.next/server/chunks/ssr/_00yo1im._.js.map +1 -0
- package/.next/server/chunks/ssr/_03sbc.o._.js +3 -0
- package/.next/server/chunks/ssr/_03sbc.o._.js.map +1 -0
- package/.next/server/chunks/ssr/_04z5ea0._.js +3 -0
- package/.next/server/chunks/ssr/_04z5ea0._.js.map +1 -0
- package/.next/server/chunks/ssr/_0gmb3g_._.js +3 -0
- package/.next/server/chunks/ssr/_0gmb3g_._.js.map +1 -0
- package/.next/server/chunks/ssr/_0okz9j8._.js +6 -0
- package/.next/server/chunks/ssr/_0okz9j8._.js.map +1 -0
- package/.next/server/chunks/ssr/_0wrbwro._.js +6 -0
- package/.next/server/chunks/ssr/_0wrbwro._.js.map +1 -0
- package/.next/server/chunks/ssr/node_modules_09w7yel._.js +33 -0
- package/.next/server/chunks/ssr/node_modules_09w7yel._.js.map +1 -0
- package/.next/server/chunks/ssr/node_modules_next_dist_0avqw4q._.js +3 -0
- package/.next/server/chunks/ssr/node_modules_next_dist_0avqw4q._.js.map +1 -0
- package/.next/server/chunks/ssr/node_modules_next_dist_0h9llsw._.js +6 -0
- package/.next/server/chunks/ssr/node_modules_next_dist_0h9llsw._.js.map +1 -0
- package/.next/server/chunks/ssr/node_modules_next_dist_0i_._k3._.js +3 -0
- package/.next/server/chunks/ssr/node_modules_next_dist_0i_._k3._.js.map +1 -0
- package/.next/server/chunks/ssr/node_modules_next_dist_client_components_0ee1czk._.js +3 -0
- package/.next/server/chunks/ssr/node_modules_next_dist_client_components_0ee1czk._.js.map +1 -0
- package/.next/server/chunks/ssr/node_modules_next_dist_client_components_builtin_global-error_0lgvd_..js +3 -0
- package/.next/server/chunks/ssr/node_modules_next_dist_client_components_builtin_global-error_0lgvd_..js.map +1 -0
- package/.next/server/chunks/ssr/node_modules_next_dist_client_components_builtin_unauthorized_0cjv-23.js +3 -0
- package/.next/server/chunks/ssr/node_modules_next_dist_client_components_builtin_unauthorized_0cjv-23.js.map +1 -0
- package/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_0amzg6z.js +4 -0
- package/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_0amzg6z.js.map +1 -0
- package/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_0dw1x0d.js +4 -0
- package/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_0dw1x0d.js.map +1 -0
- package/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_0rc3ul_.js +4 -0
- package/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_0rc3ul_.js.map +1 -0
- package/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_10iomok.js +4 -0
- package/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_10iomok.js.map +1 -0
- package/.next/server/chunks/ssr/packages_observer-dashboard_0~a2tmu._.js +3 -0
- package/.next/server/chunks/ssr/packages_observer-dashboard_0~a2tmu._.js.map +1 -0
- package/.next/server/chunks/ssr/packages_observer-dashboard_src_0f.ozc-._.js +3 -0
- package/.next/server/chunks/ssr/packages_observer-dashboard_src_0f.ozc-._.js.map +1 -0
- package/.next/server/chunks/ssr/packages_observer-dashboard_src_components_providers_tsx_03_vrrn._.js +7 -0
- package/.next/server/chunks/ssr/packages_observer-dashboard_src_components_providers_tsx_03_vrrn._.js.map +1 -0
- package/.next/server/functions-config-manifest.json +4 -0
- package/.next/server/interception-route-rewrite-manifest.js +1 -0
- package/.next/server/middleware-build-manifest.js +23 -0
- package/.next/server/middleware-manifest.json +6 -0
- package/.next/server/next-font-manifest.js +1 -0
- package/.next/server/next-font-manifest.json +6 -0
- package/.next/server/pages/404.html +1 -0
- package/.next/server/pages/500.html +1 -0
- package/.next/server/pages-manifest.json +4 -0
- package/.next/server/prefetch-hints.json +1 -0
- package/.next/server/server-reference-manifest.js +1 -0
- package/.next/server/server-reference-manifest.json +24 -0
- package/.next/static/MvfPly4ZXHqaveDVOG1qq/_buildManifest.js +11 -0
- package/.next/static/MvfPly4ZXHqaveDVOG1qq/_clientMiddlewareManifest.js +1 -0
- package/.next/static/MvfPly4ZXHqaveDVOG1qq/_ssgManifest.js +1 -0
- package/.next/static/chunks/00c87uqn~kk2z.js +1 -0
- package/.next/static/chunks/01xlw8hd842-c.js +1 -0
- package/.next/static/chunks/02p9dpa.0oxiz.js +1 -0
- package/.next/static/chunks/034i63v_muq~d.js +1 -0
- package/.next/static/chunks/03mo-6~4pxdio.js +1 -0
- package/.next/static/chunks/03~yq9q893hmn.js +1 -0
- package/.next/static/chunks/04dm5qn77.fc2.js +1 -0
- package/.next/static/chunks/07uz2g0_38qia.js +4 -0
- package/.next/static/chunks/08nqnwvixoxnk.js +2 -0
- package/.next/static/chunks/0d3shmwh5_nmn.js +1 -0
- package/.next/static/chunks/0e_wyjw3nx.a..js +1 -0
- package/.next/static/chunks/0mstyq17cbf8-.js +1 -0
- package/.next/static/chunks/0ntuxw9.how-q.js +1 -0
- package/.next/static/chunks/0t3uzajv1qzmo.js +1 -0
- package/.next/static/chunks/0u~_nwr5-v.xp.js +1 -0
- package/.next/static/chunks/0wxcxh6eyzams.js +1 -0
- package/.next/static/chunks/0x6y7yt4kiddp.css +1 -0
- package/.next/static/chunks/0z6-vonyxr0dn.js +4 -0
- package/.next/static/chunks/0ze4gu236oq96.js +31 -0
- package/.next/static/chunks/0zwozael9msy1.js +1 -0
- package/.next/static/chunks/10qd__0r7a~.l.js +1 -0
- package/.next/static/chunks/11jh_u1ynmatg.js +1 -0
- package/.next/static/chunks/13xer3cb9shu-.js +5 -0
- package/.next/static/chunks/142zlnch_xmgd.js +1 -0
- package/.next/static/chunks/144kcri75qczu.js +1 -0
- package/.next/static/chunks/turbopack-0-ww6fe7b37qt.js +1 -0
- package/.next/static/media/icon.08ljfy7xai2x_.svg +20 -0
- package/LICENSE +21 -0
- package/README.md +490 -0
- package/next.config.mjs +25 -0
- package/package.json +104 -0
- package/postcss.config.mjs +8 -0
- package/src/app/actions/__tests__/approve-breakpoint.test.ts +246 -0
- package/src/app/actions/approve-breakpoint.ts +145 -0
- package/src/app/api/config/route.ts +137 -0
- package/src/app/api/digest/route.ts +45 -0
- package/src/app/api/runs/[runId]/events/route.ts +56 -0
- package/src/app/api/runs/[runId]/route.ts +84 -0
- package/src/app/api/runs/[runId]/tasks/[effectId]/route.ts +44 -0
- package/src/app/api/runs/route.ts +48 -0
- package/src/app/api/stream/route.ts +136 -0
- package/src/app/api/test/route.ts +1 -0
- package/src/app/api/version/route.ts +57 -0
- package/src/app/globals.css +555 -0
- package/src/app/icon.svg +20 -0
- package/src/app/layout.tsx +39 -0
- package/src/app/not-found.tsx +16 -0
- package/src/app/page.tsx +120 -0
- package/src/app/runs/[runId]/page.tsx +279 -0
- package/src/cli.ts +271 -0
- package/src/components/breakpoint/__tests__/breakpoint-approval.test.tsx +212 -0
- package/src/components/breakpoint/__tests__/breakpoint-panel.test.tsx +130 -0
- package/src/components/breakpoint/__tests__/file-preview.test.tsx +313 -0
- package/src/components/breakpoint/breakpoint-approval.tsx +138 -0
- package/src/components/breakpoint/breakpoint-panel.tsx +95 -0
- package/src/components/breakpoint/file-preview.tsx +215 -0
- package/src/components/dashboard/.gitkeep +0 -0
- package/src/components/dashboard/__tests__/breakpoint-banner.test.tsx +177 -0
- package/src/components/dashboard/__tests__/catch-up-banner.test.tsx +141 -0
- package/src/components/dashboard/__tests__/executive-summary-banner.test.tsx +164 -0
- package/src/components/dashboard/__tests__/kpi-grid.test.tsx +101 -0
- package/src/components/dashboard/__tests__/pagination-controls.test.tsx +125 -0
- package/src/components/dashboard/__tests__/project-accordion.test.tsx +97 -0
- package/src/components/dashboard/__tests__/project-list-view.test.tsx +174 -0
- package/src/components/dashboard/__tests__/project-search-input.test.tsx +110 -0
- package/src/components/dashboard/__tests__/project-section-header.test.tsx +91 -0
- package/src/components/dashboard/__tests__/project-section.test.tsx +151 -0
- package/src/components/dashboard/__tests__/run-card.test.tsx +164 -0
- package/src/components/dashboard/__tests__/run-filter-bar.test.tsx +109 -0
- package/src/components/dashboard/__tests__/run-list.test.tsx +123 -0
- package/src/components/dashboard/__tests__/search-filter.test.tsx +150 -0
- package/src/components/dashboard/__tests__/virtualized-run-list.test.tsx +179 -0
- package/src/components/dashboard/breakpoint-banner.tsx +301 -0
- package/src/components/dashboard/catch-up-banner.tsx +88 -0
- package/src/components/dashboard/executive-summary-banner.tsx +174 -0
- package/src/components/dashboard/global-search.tsx +323 -0
- package/src/components/dashboard/kpi-grid.tsx +140 -0
- package/src/components/dashboard/pagination-controls.tsx +100 -0
- package/src/components/dashboard/project-accordion.tsx +72 -0
- package/src/components/dashboard/project-health-card.tsx +536 -0
- package/src/components/dashboard/project-list-view.tsx +246 -0
- package/src/components/dashboard/project-search-input.tsx +41 -0
- package/src/components/dashboard/project-section-header.tsx +73 -0
- package/src/components/dashboard/project-section.tsx +89 -0
- package/src/components/dashboard/run-card.tsx +218 -0
- package/src/components/dashboard/run-filter-bar.tsx +100 -0
- package/src/components/dashboard/run-list.tsx +77 -0
- package/src/components/dashboard/search-filter.tsx +69 -0
- package/src/components/dashboard/virtualized-run-list.tsx +130 -0
- package/src/components/details/.gitkeep +0 -0
- package/src/components/details/__tests__/agent-panel.test.tsx +236 -0
- package/src/components/details/__tests__/json-tree.test.tsx +347 -0
- package/src/components/details/__tests__/log-viewer.test.tsx +168 -0
- package/src/components/details/__tests__/task-detail.test.tsx +212 -0
- package/src/components/details/__tests__/timing-panel.test.tsx +271 -0
- package/src/components/details/agent-panel.tsx +234 -0
- package/src/components/details/json-tree/categorize.ts +131 -0
- package/src/components/details/json-tree/index.tsx +120 -0
- package/src/components/details/json-tree/json-node.tsx +223 -0
- package/src/components/details/json-tree/smart-summary.tsx +596 -0
- package/src/components/details/json-tree/tree-controls.tsx +47 -0
- package/src/components/details/json-tree.tsx +9 -0
- package/src/components/details/log-viewer.tsx +140 -0
- package/src/components/details/task-detail.tsx +114 -0
- package/src/components/details/timing-panel.tsx +247 -0
- package/src/components/events/.gitkeep +0 -0
- package/src/components/events/__tests__/event-item.test.tsx +211 -0
- package/src/components/events/__tests__/event-stream.test.tsx +225 -0
- package/src/components/events/event-item.tsx +121 -0
- package/src/components/events/event-stream.tsx +260 -0
- package/src/components/notifications/.gitkeep +0 -0
- package/src/components/notifications/__tests__/notification-panel.test.tsx +287 -0
- package/src/components/notifications/__tests__/notification-provider.test.tsx +585 -0
- package/src/components/notifications/__tests__/toast-stack.test.tsx +217 -0
- package/src/components/notifications/notification-panel.tsx +124 -0
- package/src/components/notifications/notification-provider.tsx +175 -0
- package/src/components/notifications/toast-stack.tsx +75 -0
- package/src/components/pipeline/.gitkeep +0 -0
- package/src/components/pipeline/__tests__/parallel-group.test.tsx +88 -0
- package/src/components/pipeline/__tests__/pipeline-view.test.tsx +345 -0
- package/src/components/pipeline/__tests__/step-card.test.tsx +330 -0
- package/src/components/pipeline/parallel-group.tsx +39 -0
- package/src/components/pipeline/pipeline-view.tsx +197 -0
- package/src/components/pipeline/step-card.tsx +166 -0
- package/src/components/providers/event-stream-provider.tsx +29 -0
- package/src/components/providers.tsx +24 -0
- package/src/components/shared/.gitkeep +0 -0
- package/src/components/shared/__tests__/empty-state.test.tsx +49 -0
- package/src/components/shared/__tests__/friendly-id.test.tsx +47 -0
- package/src/components/shared/__tests__/kbd.test.tsx +45 -0
- package/src/components/shared/__tests__/kind-badge.test.tsx +71 -0
- package/src/components/shared/__tests__/metrics-row.test.tsx +74 -0
- package/src/components/shared/__tests__/outcome-banner.test.tsx +71 -0
- package/src/components/shared/__tests__/progress-bar.test.tsx +89 -0
- package/src/components/shared/__tests__/session-pill.test.tsx +62 -0
- package/src/components/shared/__tests__/settings-modal.test.tsx +201 -0
- package/src/components/shared/__tests__/shortcuts-help.test.tsx +103 -0
- package/src/components/shared/__tests__/status-badge.test.tsx +98 -0
- package/src/components/shared/__tests__/theme-provider.test.tsx +100 -0
- package/src/components/shared/__tests__/truncated-id.test.tsx +53 -0
- package/src/components/shared/app-footer.tsx +80 -0
- package/src/components/shared/app-header.tsx +160 -0
- package/src/components/shared/empty-state.tsx +18 -0
- package/src/components/shared/error-boundary.tsx +81 -0
- package/src/components/shared/friendly-id.tsx +48 -0
- package/src/components/shared/kbd.tsx +15 -0
- package/src/components/shared/kind-badge.tsx +51 -0
- package/src/components/shared/metrics-row.tsx +106 -0
- package/src/components/shared/outcome-banner.tsx +56 -0
- package/src/components/shared/progress-bar.tsx +42 -0
- package/src/components/shared/session-pill.tsx +69 -0
- package/src/components/shared/settings-modal.tsx +509 -0
- package/src/components/shared/shortcuts-help.tsx +113 -0
- package/src/components/shared/status-badge.tsx +110 -0
- package/src/components/shared/theme-provider.tsx +46 -0
- package/src/components/shared/truncated-id.tsx +51 -0
- package/src/components/ui/.gitkeep +0 -0
- package/src/components/ui/__tests__/accordion.test.tsx +96 -0
- package/src/components/ui/__tests__/badge.test.tsx +69 -0
- package/src/components/ui/__tests__/button.test.tsx +113 -0
- package/src/components/ui/__tests__/tabs.test.tsx +75 -0
- package/src/components/ui/__tests__/tooltip.test.tsx +90 -0
- package/src/components/ui/accordion.tsx +61 -0
- package/src/components/ui/badge.tsx +25 -0
- package/src/components/ui/button.tsx +40 -0
- package/src/components/ui/card.tsx +21 -0
- package/src/components/ui/scroll-area.tsx +35 -0
- package/src/components/ui/separator.tsx +24 -0
- package/src/components/ui/tabs.tsx +64 -0
- package/src/components/ui/tooltip.tsx +37 -0
- package/src/hooks/.gitkeep +0 -0
- package/src/hooks/__tests__/use-animated-number.test.ts +184 -0
- package/src/hooks/__tests__/use-batched-updates.test.ts +315 -0
- package/src/hooks/__tests__/use-event-stream.test.ts +243 -0
- package/src/hooks/__tests__/use-keyboard.test.ts +217 -0
- package/src/hooks/__tests__/use-notifications.test.ts +230 -0
- package/src/hooks/__tests__/use-polling.test.ts +274 -0
- package/src/hooks/__tests__/use-project-runs.test.ts +163 -0
- package/src/hooks/__tests__/use-projects.test.ts +248 -0
- package/src/hooks/__tests__/use-run-dashboard.test.ts +168 -0
- package/src/hooks/__tests__/use-run-detail.test.ts +273 -0
- package/src/hooks/__tests__/use-smart-polling.test.ts +305 -0
- package/src/hooks/use-animated-number.ts +87 -0
- package/src/hooks/use-batched-updates.ts +150 -0
- package/src/hooks/use-event-stream.ts +150 -0
- package/src/hooks/use-keyboard.ts +45 -0
- package/src/hooks/use-notifications.ts +82 -0
- package/src/hooks/use-persisted-state.ts +60 -0
- package/src/hooks/use-polling.ts +60 -0
- package/src/hooks/use-project-runs.ts +51 -0
- package/src/hooks/use-projects.ts +26 -0
- package/src/hooks/use-run-dashboard.ts +207 -0
- package/src/hooks/use-run-detail.ts +77 -0
- package/src/hooks/use-smart-polling.ts +144 -0
- package/src/lib/.gitkeep +0 -0
- package/src/lib/__tests__/cn.test.ts +69 -0
- package/src/lib/__tests__/config-loader.test.ts +210 -0
- package/src/lib/__tests__/config.test.ts +561 -0
- package/src/lib/__tests__/error-handler.test.ts +143 -0
- package/src/lib/__tests__/fetcher.test.ts +517 -0
- package/src/lib/__tests__/global-registry.test.ts +214 -0
- package/src/lib/__tests__/parser.test.ts +1532 -0
- package/src/lib/__tests__/path-resolver.test.ts +112 -0
- package/src/lib/__tests__/run-cache.test.ts +591 -0
- package/src/lib/__tests__/server-init.test.ts +512 -0
- package/src/lib/__tests__/source-discovery.test.ts +246 -0
- package/src/lib/__tests__/utils.test.ts +160 -0
- package/src/lib/__tests__/watcher.test.ts +227 -0
- package/src/lib/cn.ts +6 -0
- package/src/lib/config-loader.ts +195 -0
- package/src/lib/config.ts +20 -0
- package/src/lib/error-handler.ts +76 -0
- package/src/lib/fetcher.ts +394 -0
- package/src/lib/global-registry.ts +117 -0
- package/src/lib/parser.ts +794 -0
- package/src/lib/path-resolver.ts +16 -0
- package/src/lib/run-cache.ts +404 -0
- package/src/lib/server-init.ts +226 -0
- package/src/lib/services/__tests__/run-query-service.test.ts +819 -0
- package/src/lib/services/run-query-service.ts +286 -0
- package/src/lib/source-discovery.ts +216 -0
- package/src/lib/utils.ts +103 -0
- package/src/lib/watcher.ts +265 -0
- package/src/test/fixtures.ts +269 -0
- package/src/test/mocks/handlers.ts +110 -0
- package/src/test/mocks/server.ts +17 -0
- package/src/test/setup.ts +200 -0
- package/src/test/test-utils.tsx +36 -0
- package/src/types/.gitkeep +0 -0
- package/src/types/breakpoint.ts +17 -0
- package/src/types/index.ts +214 -0
- package/tsconfig.json +50 -0
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
2
|
+
|
|
3
|
+
// Create hoisted mock functions
|
|
4
|
+
const { mockAccess, mockWriteFile, mockMkdir, mockReaddir, mockFindRunDir } = vi.hoisted(() => ({
|
|
5
|
+
mockAccess: vi.fn(),
|
|
6
|
+
mockWriteFile: vi.fn(),
|
|
7
|
+
mockMkdir: vi.fn(),
|
|
8
|
+
mockReaddir: vi.fn(),
|
|
9
|
+
mockFindRunDir: vi.fn(),
|
|
10
|
+
}));
|
|
11
|
+
|
|
12
|
+
// Mock path-resolver
|
|
13
|
+
vi.mock("@/lib/path-resolver", () => ({
|
|
14
|
+
findRunDir: mockFindRunDir,
|
|
15
|
+
}));
|
|
16
|
+
|
|
17
|
+
// Mock fs with a complete replacement that includes default export
|
|
18
|
+
vi.mock("fs", () => {
|
|
19
|
+
return {
|
|
20
|
+
default: {
|
|
21
|
+
promises: {
|
|
22
|
+
access: mockAccess,
|
|
23
|
+
writeFile: mockWriteFile,
|
|
24
|
+
mkdir: mockMkdir,
|
|
25
|
+
readdir: mockReaddir,
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
promises: {
|
|
29
|
+
access: mockAccess,
|
|
30
|
+
writeFile: mockWriteFile,
|
|
31
|
+
mkdir: mockMkdir,
|
|
32
|
+
readdir: mockReaddir,
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
import { approveBreakpoint } from "../approve-breakpoint";
|
|
38
|
+
|
|
39
|
+
const defaultSource = { path: "/projects", depth: 2, label: "test" };
|
|
40
|
+
|
|
41
|
+
describe("approveBreakpoint", () => {
|
|
42
|
+
beforeEach(() => {
|
|
43
|
+
vi.clearAllMocks();
|
|
44
|
+
// Default: journal dir has some existing entries
|
|
45
|
+
mockMkdir.mockResolvedValue(undefined);
|
|
46
|
+
mockReaddir.mockResolvedValue(["000001.01ABC.json", "000002.01DEF.json"]);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
// -------------------------------------------------------------------------
|
|
50
|
+
// Input validation
|
|
51
|
+
// -------------------------------------------------------------------------
|
|
52
|
+
|
|
53
|
+
it("returns error when runId is empty", async () => {
|
|
54
|
+
const result = await approveBreakpoint("", "eff-001", "yes");
|
|
55
|
+
expect(result.success).toBe(false);
|
|
56
|
+
expect(result.error).toContain("Missing or invalid runId");
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it("returns error when effectId is empty", async () => {
|
|
60
|
+
const result = await approveBreakpoint("run-001", "", "yes");
|
|
61
|
+
expect(result.success).toBe(false);
|
|
62
|
+
expect(result.error).toContain("Missing or invalid effectId");
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it("returns error when answer is empty", async () => {
|
|
66
|
+
const result = await approveBreakpoint("run-001", "eff-001", "");
|
|
67
|
+
expect(result.success).toBe(false);
|
|
68
|
+
expect(result.error).toContain("Answer cannot be empty");
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it("returns error when answer is only whitespace", async () => {
|
|
72
|
+
const result = await approveBreakpoint("run-001", "eff-001", " ");
|
|
73
|
+
expect(result.success).toBe(false);
|
|
74
|
+
expect(result.error).toContain("Answer cannot be empty");
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it("returns error when runId contains path traversal characters", async () => {
|
|
78
|
+
const result = await approveBreakpoint("../etc", "eff-001", "yes");
|
|
79
|
+
expect(result.success).toBe(false);
|
|
80
|
+
expect(result.error).toContain("Invalid characters");
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it("returns error when effectId contains path traversal characters", async () => {
|
|
84
|
+
const result = await approveBreakpoint("run-001", "../../etc", "yes");
|
|
85
|
+
expect(result.success).toBe(false);
|
|
86
|
+
expect(result.error).toContain("Invalid characters");
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
// -------------------------------------------------------------------------
|
|
90
|
+
// Run/task resolution
|
|
91
|
+
// -------------------------------------------------------------------------
|
|
92
|
+
|
|
93
|
+
it("returns error when run is not found", async () => {
|
|
94
|
+
mockFindRunDir.mockResolvedValue(null);
|
|
95
|
+
|
|
96
|
+
const result = await approveBreakpoint("run-999", "eff-001", "yes");
|
|
97
|
+
expect(result.success).toBe(false);
|
|
98
|
+
expect(result.error).toContain("Run not found");
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it("returns error when task directory does not exist", async () => {
|
|
102
|
+
mockFindRunDir.mockResolvedValue({
|
|
103
|
+
runDir: "/projects/app/.a5c/runs/run-001",
|
|
104
|
+
source: defaultSource,
|
|
105
|
+
projectName: "app",
|
|
106
|
+
projectPath: "/projects/app",
|
|
107
|
+
});
|
|
108
|
+
mockAccess.mockRejectedValueOnce(new Error("ENOENT"));
|
|
109
|
+
|
|
110
|
+
const result = await approveBreakpoint("run-001", "eff-001", "yes");
|
|
111
|
+
expect(result.success).toBe(false);
|
|
112
|
+
expect(result.error).toContain("Task directory not found");
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
// -------------------------------------------------------------------------
|
|
116
|
+
// Success path
|
|
117
|
+
// -------------------------------------------------------------------------
|
|
118
|
+
|
|
119
|
+
it("writes result.json and journal entry on success", async () => {
|
|
120
|
+
const runDir = "/projects/app/.a5c/runs/run-001";
|
|
121
|
+
mockFindRunDir.mockResolvedValue({
|
|
122
|
+
runDir,
|
|
123
|
+
source: defaultSource,
|
|
124
|
+
projectName: "app",
|
|
125
|
+
projectPath: "/projects/app",
|
|
126
|
+
});
|
|
127
|
+
mockAccess.mockResolvedValue(undefined);
|
|
128
|
+
mockWriteFile.mockResolvedValue(undefined);
|
|
129
|
+
|
|
130
|
+
const result = await approveBreakpoint("run-001", "eff-001", "Deploy approved");
|
|
131
|
+
expect(result.success).toBe(true);
|
|
132
|
+
|
|
133
|
+
// Should write 2 files: result.json + journal entry
|
|
134
|
+
expect(mockWriteFile).toHaveBeenCalledTimes(2);
|
|
135
|
+
|
|
136
|
+
// First write: result.json
|
|
137
|
+
const [resultPath, resultContent] = mockWriteFile.mock.calls[0];
|
|
138
|
+
expect(resultPath).toContain("eff-001");
|
|
139
|
+
expect(resultPath).toContain("result.json");
|
|
140
|
+
|
|
141
|
+
const parsed = JSON.parse(resultContent as string);
|
|
142
|
+
expect(parsed.status).toBe("ok");
|
|
143
|
+
expect(parsed.value.answer).toBe("Deploy approved");
|
|
144
|
+
expect(parsed.value.approvedBy).toBe("observer-dashboard");
|
|
145
|
+
expect(parsed.value.approvedAt).toBeDefined();
|
|
146
|
+
expect(parsed.startedAt).toBeDefined();
|
|
147
|
+
expect(parsed.finishedAt).toBeDefined();
|
|
148
|
+
|
|
149
|
+
// Second write: journal entry
|
|
150
|
+
const [journalPath, journalContent] = mockWriteFile.mock.calls[1];
|
|
151
|
+
expect(journalPath).toContain("journal");
|
|
152
|
+
expect(journalPath).toMatch(/000003\./); // next seq after 000001, 000002
|
|
153
|
+
|
|
154
|
+
const journalParsed = JSON.parse(journalContent as string);
|
|
155
|
+
expect(journalParsed.type).toBe("EFFECT_RESOLVED");
|
|
156
|
+
expect(journalParsed.data.effectId).toBe("eff-001");
|
|
157
|
+
expect(journalParsed.data.status).toBe("ok");
|
|
158
|
+
expect(journalParsed.data.resultRef).toBe("tasks/eff-001/result.json");
|
|
159
|
+
expect(journalParsed.checksum).toBeDefined();
|
|
160
|
+
expect(typeof journalParsed.checksum).toBe("string");
|
|
161
|
+
expect(journalParsed.checksum.length).toBe(64); // SHA-256 hex
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it("journal checksum is valid SHA-256 of payload without checksum", async () => {
|
|
165
|
+
const runDir = "/projects/app/.a5c/runs/run-001";
|
|
166
|
+
mockFindRunDir.mockResolvedValue({
|
|
167
|
+
runDir,
|
|
168
|
+
source: defaultSource,
|
|
169
|
+
projectName: "app",
|
|
170
|
+
projectPath: "/projects/app",
|
|
171
|
+
});
|
|
172
|
+
mockAccess.mockResolvedValue(undefined);
|
|
173
|
+
mockWriteFile.mockResolvedValue(undefined);
|
|
174
|
+
|
|
175
|
+
await approveBreakpoint("run-001", "eff-001", "yes");
|
|
176
|
+
|
|
177
|
+
const [, journalContent] = mockWriteFile.mock.calls[1];
|
|
178
|
+
const journalParsed = JSON.parse(journalContent as string);
|
|
179
|
+
|
|
180
|
+
// Recompute checksum: SHA-256 of JSON.stringify(payloadWithoutChecksum, null, 2) + "\n"
|
|
181
|
+
const { checksum: _checksum, ...payloadWithoutChecksum } = journalParsed;
|
|
182
|
+
const crypto = await import("crypto");
|
|
183
|
+
const expected = crypto.default
|
|
184
|
+
.createHash("sha256")
|
|
185
|
+
.update(JSON.stringify(payloadWithoutChecksum, null, 2) + "\n")
|
|
186
|
+
.digest("hex");
|
|
187
|
+
expect(journalParsed.checksum).toBe(expected);
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
it("uses seq 1 when journal dir is empty", async () => {
|
|
191
|
+
const runDir = "/projects/app/.a5c/runs/run-001";
|
|
192
|
+
mockFindRunDir.mockResolvedValue({
|
|
193
|
+
runDir,
|
|
194
|
+
source: defaultSource,
|
|
195
|
+
projectName: "app",
|
|
196
|
+
projectPath: "/projects/app",
|
|
197
|
+
});
|
|
198
|
+
mockAccess.mockResolvedValue(undefined);
|
|
199
|
+
mockWriteFile.mockResolvedValue(undefined);
|
|
200
|
+
mockReaddir.mockResolvedValue([]); // empty journal
|
|
201
|
+
|
|
202
|
+
await approveBreakpoint("run-001", "eff-001", "yes");
|
|
203
|
+
|
|
204
|
+
const [journalPath] = mockWriteFile.mock.calls[1];
|
|
205
|
+
expect(journalPath).toMatch(/000001\./);
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
it("trims whitespace from the answer", async () => {
|
|
209
|
+
const runDir = "/projects/app/.a5c/runs/run-001";
|
|
210
|
+
mockFindRunDir.mockResolvedValue({
|
|
211
|
+
runDir,
|
|
212
|
+
source: defaultSource,
|
|
213
|
+
projectName: "app",
|
|
214
|
+
projectPath: "/projects/app",
|
|
215
|
+
});
|
|
216
|
+
mockAccess.mockResolvedValue(undefined);
|
|
217
|
+
mockWriteFile.mockResolvedValue(undefined);
|
|
218
|
+
|
|
219
|
+
const result = await approveBreakpoint("run-001", "eff-001", " yes ");
|
|
220
|
+
expect(result.success).toBe(true);
|
|
221
|
+
|
|
222
|
+
const [, content] = mockWriteFile.mock.calls[0];
|
|
223
|
+
const parsed = JSON.parse(content as string);
|
|
224
|
+
expect(parsed.value.answer).toBe("yes");
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
// -------------------------------------------------------------------------
|
|
228
|
+
// Write failure
|
|
229
|
+
// -------------------------------------------------------------------------
|
|
230
|
+
|
|
231
|
+
it("returns error when file write fails", async () => {
|
|
232
|
+
const runDir = "/projects/app/.a5c/runs/run-001";
|
|
233
|
+
mockFindRunDir.mockResolvedValue({
|
|
234
|
+
runDir,
|
|
235
|
+
source: defaultSource,
|
|
236
|
+
projectName: "app",
|
|
237
|
+
projectPath: "/projects/app",
|
|
238
|
+
});
|
|
239
|
+
mockAccess.mockResolvedValue(undefined);
|
|
240
|
+
mockWriteFile.mockRejectedValue(new Error("EACCES: permission denied"));
|
|
241
|
+
|
|
242
|
+
const result = await approveBreakpoint("run-001", "eff-001", "yes");
|
|
243
|
+
expect(result.success).toBe(false);
|
|
244
|
+
expect(result.error).toContain("EACCES");
|
|
245
|
+
});
|
|
246
|
+
});
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
"use server";
|
|
2
|
+
|
|
3
|
+
import { promises as fs } from "fs";
|
|
4
|
+
import path from "path";
|
|
5
|
+
import crypto from "crypto";
|
|
6
|
+
import { monotonicFactory } from "ulid";
|
|
7
|
+
import { findRunDir } from "@/lib/path-resolver";
|
|
8
|
+
|
|
9
|
+
export interface ApproveBreakpointResult {
|
|
10
|
+
success: boolean;
|
|
11
|
+
error?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const nextUlid = monotonicFactory();
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Determine the next journal sequence number by scanning existing files.
|
|
18
|
+
*/
|
|
19
|
+
async function getNextJournalSeq(journalDir: string): Promise<number> {
|
|
20
|
+
try {
|
|
21
|
+
const files = await fs.readdir(journalDir);
|
|
22
|
+
let max = 0;
|
|
23
|
+
for (const f of files) {
|
|
24
|
+
const seqStr = f.split(".")[0];
|
|
25
|
+
const seq = Number(seqStr);
|
|
26
|
+
if (Number.isFinite(seq) && seq > max) max = seq;
|
|
27
|
+
}
|
|
28
|
+
return max + 1;
|
|
29
|
+
} catch {
|
|
30
|
+
// Journal dir may not exist yet — seq 1
|
|
31
|
+
return 1;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Write an EFFECT_RESOLVED journal entry using the same format as the
|
|
37
|
+
* babysitter SDK: SHA-256 checksum of the JSON payload (without checksum)
|
|
38
|
+
* serialized as `JSON.stringify(payload, null, 2) + "\n"`.
|
|
39
|
+
*/
|
|
40
|
+
async function appendJournalEntry(
|
|
41
|
+
runDir: string,
|
|
42
|
+
effectId: string,
|
|
43
|
+
now: string,
|
|
44
|
+
): Promise<void> {
|
|
45
|
+
const journalDir = path.join(runDir, "journal");
|
|
46
|
+
await fs.mkdir(journalDir, { recursive: true });
|
|
47
|
+
|
|
48
|
+
const seq = await getNextJournalSeq(journalDir);
|
|
49
|
+
const ulid = nextUlid();
|
|
50
|
+
const filename = `${seq.toString().padStart(6, "0")}.${ulid}.json`;
|
|
51
|
+
|
|
52
|
+
const eventPayload = {
|
|
53
|
+
type: "EFFECT_RESOLVED",
|
|
54
|
+
recordedAt: now,
|
|
55
|
+
data: {
|
|
56
|
+
effectId,
|
|
57
|
+
status: "ok",
|
|
58
|
+
resultRef: `tasks/${effectId}/result.json`,
|
|
59
|
+
startedAt: now,
|
|
60
|
+
finishedAt: now,
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const contents = JSON.stringify(eventPayload, null, 2) + "\n";
|
|
65
|
+
const checksum = crypto.createHash("sha256").update(contents).digest("hex");
|
|
66
|
+
const payloadWithChecksum = JSON.stringify({ ...eventPayload, checksum }, null, 2) + "\n";
|
|
67
|
+
|
|
68
|
+
await fs.writeFile(path.join(journalDir, filename), payloadWithChecksum, "utf-8");
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Server Action: approve a breakpoint by writing both the result.json AND
|
|
73
|
+
* an EFFECT_RESOLVED journal entry. This ensures the SDK's state machine
|
|
74
|
+
* recognizes the resolution on the next `run:iterate` (the SDK auto-rebuilds
|
|
75
|
+
* its state cache when it detects journal head mismatch).
|
|
76
|
+
*
|
|
77
|
+
* Previous implementation only wrote result.json, leaving the journal
|
|
78
|
+
* untouched — the SDK never knew the breakpoint was resolved, causing runs
|
|
79
|
+
* to stay stuck in "Waiting" state permanently.
|
|
80
|
+
*/
|
|
81
|
+
export async function approveBreakpoint(
|
|
82
|
+
runId: string,
|
|
83
|
+
effectId: string,
|
|
84
|
+
answer: string,
|
|
85
|
+
): Promise<ApproveBreakpointResult> {
|
|
86
|
+
// --- Validate inputs ---
|
|
87
|
+
if (!runId || typeof runId !== "string") {
|
|
88
|
+
return { success: false, error: "Missing or invalid runId" };
|
|
89
|
+
}
|
|
90
|
+
if (!effectId || typeof effectId !== "string") {
|
|
91
|
+
return { success: false, error: "Missing or invalid effectId" };
|
|
92
|
+
}
|
|
93
|
+
if (!answer || typeof answer !== "string" || answer.trim().length === 0) {
|
|
94
|
+
return { success: false, error: "Answer cannot be empty" };
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Sanitize IDs to prevent path traversal
|
|
98
|
+
const idPattern = /^[a-zA-Z0-9_\-]+$/;
|
|
99
|
+
if (!idPattern.test(runId) || !idPattern.test(effectId)) {
|
|
100
|
+
return { success: false, error: "Invalid characters in runId or effectId" };
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
try {
|
|
104
|
+
// --- Resolve the run directory ---
|
|
105
|
+
const found = await findRunDir(runId);
|
|
106
|
+
if (!found) {
|
|
107
|
+
return { success: false, error: `Run not found: ${runId}` };
|
|
108
|
+
}
|
|
109
|
+
const runDir = found.runDir;
|
|
110
|
+
|
|
111
|
+
// --- Verify the task directory exists ---
|
|
112
|
+
const taskDir = path.join(runDir, "tasks", effectId);
|
|
113
|
+
try {
|
|
114
|
+
await fs.access(taskDir);
|
|
115
|
+
} catch {
|
|
116
|
+
return { success: false, error: `Task directory not found: ${effectId}` };
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// --- Write result.json (SDK-compatible format) ---
|
|
120
|
+
const now = new Date().toISOString();
|
|
121
|
+
const resultPayload = {
|
|
122
|
+
status: "ok",
|
|
123
|
+
value: {
|
|
124
|
+
answer: answer.trim(),
|
|
125
|
+
approvedAt: now,
|
|
126
|
+
approvedBy: "observer-dashboard",
|
|
127
|
+
},
|
|
128
|
+
startedAt: now,
|
|
129
|
+
finishedAt: now,
|
|
130
|
+
};
|
|
131
|
+
const resultPath = path.join(taskDir, "result.json");
|
|
132
|
+
await fs.writeFile(resultPath, JSON.stringify(resultPayload, null, 2), "utf-8");
|
|
133
|
+
|
|
134
|
+
// --- Append EFFECT_RESOLVED journal entry ---
|
|
135
|
+
// This is the critical piece that was missing: without a journal entry,
|
|
136
|
+
// the SDK's state machine never knows the breakpoint was resolved and
|
|
137
|
+
// the run stays stuck in "Waiting" forever.
|
|
138
|
+
await appendJournalEntry(runDir, effectId, now);
|
|
139
|
+
|
|
140
|
+
return { success: true };
|
|
141
|
+
} catch (err: unknown) {
|
|
142
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
143
|
+
return { success: false, error: msg };
|
|
144
|
+
}
|
|
145
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { NextResponse } from "next/server";
|
|
2
|
+
import { getConfig, invalidateConfigCache, writeConfig } from "@/lib/config-loader";
|
|
3
|
+
import { invalidateAll, discoverAndCacheAll } from "@/lib/run-cache";
|
|
4
|
+
import { normalizeError } from "@/lib/error-handler";
|
|
5
|
+
|
|
6
|
+
export const dynamic = "force-dynamic";
|
|
7
|
+
|
|
8
|
+
export async function GET() {
|
|
9
|
+
try {
|
|
10
|
+
const config = await getConfig();
|
|
11
|
+
return NextResponse.json(config);
|
|
12
|
+
} catch (err) {
|
|
13
|
+
const normalized = normalizeError(err);
|
|
14
|
+
return NextResponse.json(
|
|
15
|
+
{ error: normalized.message, code: normalized.code },
|
|
16
|
+
{ status: normalized.status }
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export async function POST(request: Request) {
|
|
22
|
+
try {
|
|
23
|
+
const body = await request.json();
|
|
24
|
+
|
|
25
|
+
// Validate sources
|
|
26
|
+
if (!Array.isArray(body.sources)) {
|
|
27
|
+
return NextResponse.json(
|
|
28
|
+
{ error: "sources must be an array" },
|
|
29
|
+
{ status: 400 }
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
for (let i = 0; i < body.sources.length; i++) {
|
|
34
|
+
const s = body.sources[i];
|
|
35
|
+
if (!s || typeof s.path !== "string" || !s.path.trim()) {
|
|
36
|
+
return NextResponse.json(
|
|
37
|
+
{ error: `sources[${i}].path must be a non-empty string` },
|
|
38
|
+
{ status: 400 }
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
if (typeof s.depth !== "number" || s.depth < 0 || s.depth > 10) {
|
|
42
|
+
return NextResponse.json(
|
|
43
|
+
{ error: `sources[${i}].depth must be a number between 0 and 10` },
|
|
44
|
+
{ status: 400 }
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Validate pollInterval
|
|
50
|
+
if (body.pollInterval !== undefined) {
|
|
51
|
+
if (typeof body.pollInterval !== "number" || body.pollInterval < 500) {
|
|
52
|
+
return NextResponse.json(
|
|
53
|
+
{ error: "pollInterval must be a number >= 500" },
|
|
54
|
+
{ status: 400 }
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Validate theme
|
|
60
|
+
if (body.theme !== undefined) {
|
|
61
|
+
if (body.theme !== "dark" && body.theme !== "light") {
|
|
62
|
+
return NextResponse.json(
|
|
63
|
+
{ error: "theme must be 'dark' or 'light'" },
|
|
64
|
+
{ status: 400 }
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Validate staleThresholdMs
|
|
70
|
+
if (body.staleThresholdMs !== undefined) {
|
|
71
|
+
if (typeof body.staleThresholdMs !== "number" || body.staleThresholdMs < 0) {
|
|
72
|
+
return NextResponse.json(
|
|
73
|
+
{ error: "staleThresholdMs must be a non-negative number" },
|
|
74
|
+
{ status: 400 }
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Validate retentionDays
|
|
80
|
+
if (body.retentionDays !== undefined) {
|
|
81
|
+
if (typeof body.retentionDays !== "number" || body.retentionDays < 1 || body.retentionDays > 365) {
|
|
82
|
+
return NextResponse.json(
|
|
83
|
+
{ error: "retentionDays must be a number between 1 and 365" },
|
|
84
|
+
{ status: 400 }
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Validate hiddenProjects
|
|
90
|
+
if (body.hiddenProjects !== undefined) {
|
|
91
|
+
if (!Array.isArray(body.hiddenProjects) || body.hiddenProjects.some((p: unknown) => typeof p !== "string")) {
|
|
92
|
+
return NextResponse.json(
|
|
93
|
+
{ error: "hiddenProjects must be an array of strings" },
|
|
94
|
+
{ status: 400 }
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Build config to save
|
|
100
|
+
const configToSave = {
|
|
101
|
+
sources: body.sources.map((s: { path: string; depth: number; label?: string }) => ({
|
|
102
|
+
path: s.path.trim(),
|
|
103
|
+
depth: s.depth,
|
|
104
|
+
...(s.label ? { label: s.label.trim() } : {}),
|
|
105
|
+
})),
|
|
106
|
+
...(body.pollInterval !== undefined ? { pollInterval: body.pollInterval } : {}),
|
|
107
|
+
...(body.theme !== undefined ? { theme: body.theme } : {}),
|
|
108
|
+
...(body.staleThresholdMs !== undefined ? { staleThresholdMs: body.staleThresholdMs } : {}),
|
|
109
|
+
...(body.retentionDays !== undefined ? { retentionDays: body.retentionDays } : {}),
|
|
110
|
+
...(body.hiddenProjects !== undefined ? { hiddenProjects: body.hiddenProjects } : {}),
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
// Write to disk
|
|
114
|
+
await writeConfig(configToSave);
|
|
115
|
+
|
|
116
|
+
// Invalidate caches so next request reads fresh
|
|
117
|
+
invalidateConfigCache();
|
|
118
|
+
invalidateAll(); // Clear run cache so re-discovery uses new sources
|
|
119
|
+
|
|
120
|
+
// Read back the full merged config
|
|
121
|
+
const savedConfig = await getConfig();
|
|
122
|
+
|
|
123
|
+
// Re-discover all runs with new sources (fire and forget)
|
|
124
|
+
discoverAndCacheAll().catch((err) =>
|
|
125
|
+
console.error("Failed to re-discover runs after config change:", err)
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
return NextResponse.json(savedConfig);
|
|
129
|
+
} catch (err) {
|
|
130
|
+
console.error("Failed to save config:", err);
|
|
131
|
+
const normalized = normalizeError(err);
|
|
132
|
+
return NextResponse.json(
|
|
133
|
+
{ error: normalized.message, code: normalized.code },
|
|
134
|
+
{ status: normalized.status }
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { NextResponse } from "next/server";
|
|
2
|
+
import { ensureInitialized } from "@/lib/server-init";
|
|
3
|
+
import { getAllCachedDigests, discoverAndCacheAll } from "@/lib/run-cache";
|
|
4
|
+
import { normalizeError } from "@/lib/error-handler";
|
|
5
|
+
|
|
6
|
+
export const dynamic = "force-dynamic";
|
|
7
|
+
|
|
8
|
+
export async function GET() {
|
|
9
|
+
try {
|
|
10
|
+
try {
|
|
11
|
+
await ensureInitialized();
|
|
12
|
+
} catch (initError) {
|
|
13
|
+
console.error("Server initialization failed:", initError);
|
|
14
|
+
return NextResponse.json(
|
|
15
|
+
{ error: "Server initialization failed. Check observer configuration and watch sources.", code: "INIT_FAILED" },
|
|
16
|
+
{ status: 500 }
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Ensure cache is populated — discoverAndCacheAll() is debounced internally
|
|
21
|
+
// (10s) so repeated calls are cheap. This guarantees runs invalidated by the
|
|
22
|
+
// watcher are re-populated before we read the cache.
|
|
23
|
+
await discoverAndCacheAll();
|
|
24
|
+
const runs = getAllCachedDigests();
|
|
25
|
+
|
|
26
|
+
// Sort by updatedAt descending (most recent first), with runId tiebreaker
|
|
27
|
+
// to ensure stable ordering when multiple runs share the same timestamp.
|
|
28
|
+
runs.sort((a, b) => {
|
|
29
|
+
const cmp = (b.updatedAt || "").localeCompare(a.updatedAt || "");
|
|
30
|
+
if (cmp !== 0) return cmp;
|
|
31
|
+
return a.runId.localeCompare(b.runId);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
return NextResponse.json({ runs }, {
|
|
35
|
+
headers: { "Cache-Control": "no-cache, no-store" },
|
|
36
|
+
});
|
|
37
|
+
} catch (error) {
|
|
38
|
+
console.error("Failed to read digest:", error);
|
|
39
|
+
const normalized = normalizeError(error);
|
|
40
|
+
return NextResponse.json(
|
|
41
|
+
{ error: normalized.message, code: normalized.code },
|
|
42
|
+
{ status: normalized.status }
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { NextResponse } from "next/server";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { findRunDir } from "@/lib/path-resolver";
|
|
4
|
+
import { parseJournalDir } from "@/lib/parser";
|
|
5
|
+
import { normalizeError } from "@/lib/error-handler";
|
|
6
|
+
|
|
7
|
+
export const dynamic = "force-dynamic";
|
|
8
|
+
|
|
9
|
+
function isValidId(id: string): boolean {
|
|
10
|
+
return /^[a-zA-Z0-9_\-]+$/.test(id);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export async function GET(
|
|
14
|
+
request: Request,
|
|
15
|
+
{ params }: { params: { runId: string } }
|
|
16
|
+
) {
|
|
17
|
+
try {
|
|
18
|
+
const { runId } = params;
|
|
19
|
+
if (!isValidId(runId)) {
|
|
20
|
+
return NextResponse.json({ error: "Invalid run ID" }, { status: 400 });
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const found = await findRunDir(runId);
|
|
24
|
+
if (!found) {
|
|
25
|
+
return NextResponse.json({ error: "Run not found" }, { status: 404 });
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const journalPath = path.join(found.runDir, "journal");
|
|
29
|
+
|
|
30
|
+
const url = new URL(request.url);
|
|
31
|
+
const limit = parseInt(url.searchParams.get("limit") || "50", 10);
|
|
32
|
+
const offset = parseInt(url.searchParams.get("offset") || "0", 10);
|
|
33
|
+
|
|
34
|
+
if (isNaN(limit) || isNaN(offset) || limit < 0 || offset < 0) {
|
|
35
|
+
return NextResponse.json(
|
|
36
|
+
{ error: "Invalid pagination parameters", code: "INVALID_INPUT" },
|
|
37
|
+
{ status: 400 }
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const allEvents = await parseJournalDir(journalPath);
|
|
42
|
+
const total = allEvents.length;
|
|
43
|
+
const events = allEvents.slice(offset, offset + limit);
|
|
44
|
+
|
|
45
|
+
return NextResponse.json({ events, total }, {
|
|
46
|
+
headers: { "Cache-Control": "no-cache, no-store" },
|
|
47
|
+
});
|
|
48
|
+
} catch (error) {
|
|
49
|
+
console.error("Failed to read events:", error);
|
|
50
|
+
const normalized = normalizeError(error);
|
|
51
|
+
return NextResponse.json(
|
|
52
|
+
{ error: normalized.message, code: normalized.code },
|
|
53
|
+
{ status: normalized.status }
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
}
|