@shepai/cli 1.122.1 → 1.122.2-pr367.fdba557
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/packages/core/src/application/ports/output/services/index.d.ts +2 -0
- package/dist/packages/core/src/application/ports/output/services/index.d.ts.map +1 -1
- package/dist/packages/core/src/application/ports/output/services/tunnel-service.interface.d.ts +41 -0
- package/dist/packages/core/src/application/ports/output/services/tunnel-service.interface.d.ts.map +1 -0
- package/dist/packages/core/src/application/ports/output/services/tunnel-service.interface.js +14 -0
- package/dist/packages/core/src/application/ports/output/services/webhook-service.interface.d.ts +65 -0
- package/dist/packages/core/src/application/ports/output/services/webhook-service.interface.d.ts.map +1 -0
- package/dist/packages/core/src/application/ports/output/services/webhook-service.interface.js +10 -0
- package/dist/packages/core/src/infrastructure/di/container.d.ts.map +1 -1
- package/dist/packages/core/src/infrastructure/di/container.js +14 -0
- package/dist/packages/core/src/infrastructure/services/agents/feature-agent/feature-agent-graph.d.ts.map +1 -1
- package/dist/packages/core/src/infrastructure/services/agents/feature-agent/feature-agent-graph.js +21 -4
- package/dist/packages/core/src/infrastructure/services/agents/feature-agent/nodes/validate.node.d.ts +7 -1
- package/dist/packages/core/src/infrastructure/services/agents/feature-agent/nodes/validate.node.d.ts.map +1 -1
- package/dist/packages/core/src/infrastructure/services/agents/feature-agent/nodes/validate.node.js +24 -2
- package/dist/packages/core/src/infrastructure/services/tunnel/cloudflare-tunnel.service.d.ts +55 -0
- package/dist/packages/core/src/infrastructure/services/tunnel/cloudflare-tunnel.service.d.ts.map +1 -0
- package/dist/packages/core/src/infrastructure/services/tunnel/cloudflare-tunnel.service.js +203 -0
- package/dist/packages/core/src/infrastructure/services/webhook/github-webhook.service.d.ts +75 -0
- package/dist/packages/core/src/infrastructure/services/webhook/github-webhook.service.d.ts.map +1 -0
- package/dist/packages/core/src/infrastructure/services/webhook/github-webhook.service.js +372 -0
- package/dist/packages/core/src/infrastructure/services/webhook/webhook-manager.service.d.ts +82 -0
- package/dist/packages/core/src/infrastructure/services/webhook/webhook-manager.service.d.ts.map +1 -0
- package/dist/packages/core/src/infrastructure/services/webhook/webhook-manager.service.js +206 -0
- package/dist/src/presentation/cli/commands/ui.command.d.ts.map +1 -1
- package/dist/src/presentation/cli/commands/ui.command.js +15 -0
- package/dist/src/presentation/web/app/actions/load-webhook-status.d.ts +3 -0
- package/dist/src/presentation/web/app/actions/load-webhook-status.d.ts.map +1 -0
- package/dist/src/presentation/web/app/actions/load-webhook-status.js +19 -0
- package/dist/src/presentation/web/app/api/webhooks/deliveries/route.d.ts +9 -0
- package/dist/src/presentation/web/app/api/webhooks/deliveries/route.d.ts.map +1 -0
- package/dist/src/presentation/web/app/api/webhooks/deliveries/route.js +27 -0
- package/dist/src/presentation/web/app/api/webhooks/github/route.d.ts +17 -0
- package/dist/src/presentation/web/app/api/webhooks/github/route.d.ts.map +1 -0
- package/dist/src/presentation/web/app/api/webhooks/github/route.js +70 -0
- package/dist/src/presentation/web/app/api/webhooks/status/route.d.ts +9 -0
- package/dist/src/presentation/web/app/api/webhooks/status/route.d.ts.map +1 -0
- package/dist/src/presentation/web/app/api/webhooks/status/route.js +26 -0
- package/dist/src/presentation/web/app/webhooks/page.d.ts +4 -0
- package/dist/src/presentation/web/app/webhooks/page.d.ts.map +1 -0
- package/dist/src/presentation/web/app/webhooks/page.js +9 -0
- package/dist/src/presentation/web/components/features/webhooks/types.d.ts +32 -0
- package/dist/src/presentation/web/components/features/webhooks/types.d.ts.map +1 -0
- package/dist/src/presentation/web/components/features/webhooks/types.js +1 -0
- package/dist/src/presentation/web/components/features/webhooks/webhook-delivery-table.d.ts +6 -0
- package/dist/src/presentation/web/components/features/webhooks/webhook-delivery-table.d.ts.map +1 -0
- package/dist/src/presentation/web/components/features/webhooks/webhook-delivery-table.js +38 -0
- package/dist/src/presentation/web/components/features/webhooks/webhook-delivery-table.stories.d.ts +9 -0
- package/dist/src/presentation/web/components/features/webhooks/webhook-delivery-table.stories.d.ts.map +1 -0
- package/dist/src/presentation/web/components/features/webhooks/webhook-delivery-table.stories.js +69 -0
- package/dist/src/presentation/web/components/features/webhooks/webhook-repo-list.d.ts +7 -0
- package/dist/src/presentation/web/components/features/webhooks/webhook-repo-list.d.ts.map +1 -0
- package/dist/src/presentation/web/components/features/webhooks/webhook-repo-list.js +11 -0
- package/dist/src/presentation/web/components/features/webhooks/webhook-repo-list.stories.d.ts +9 -0
- package/dist/src/presentation/web/components/features/webhooks/webhook-repo-list.stories.d.ts.map +1 -0
- package/dist/src/presentation/web/components/features/webhooks/webhook-repo-list.stories.js +31 -0
- package/dist/src/presentation/web/components/features/webhooks/webhook-status-cards.d.ts +6 -0
- package/dist/src/presentation/web/components/features/webhooks/webhook-status-cards.d.ts.map +1 -0
- package/dist/src/presentation/web/components/features/webhooks/webhook-status-cards.js +21 -0
- package/dist/src/presentation/web/components/features/webhooks/webhook-status-cards.stories.d.ts +9 -0
- package/dist/src/presentation/web/components/features/webhooks/webhook-status-cards.stories.d.ts.map +1 -0
- package/dist/src/presentation/web/components/features/webhooks/webhook-status-cards.stories.js +56 -0
- package/dist/src/presentation/web/components/features/webhooks/webhooks-page-client.d.ts +6 -0
- package/dist/src/presentation/web/components/features/webhooks/webhooks-page-client.d.ts.map +1 -0
- package/dist/src/presentation/web/components/features/webhooks/webhooks-page-client.js +39 -0
- package/dist/src/presentation/web/components/features/webhooks/webhooks-page-client.stories.d.ts +8 -0
- package/dist/src/presentation/web/components/features/webhooks/webhooks-page-client.stories.d.ts.map +1 -0
- package/dist/src/presentation/web/components/features/webhooks/webhooks-page-client.stories.js +59 -0
- 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 +2 -2
- package/dist/src/presentation/web/dev-server.js +20 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +3 -1
- package/web/.next/BUILD_ID +1 -1
- package/web/.next/app-path-routes-manifest.json +5 -1
- package/web/.next/build-manifest.json +6 -6
- package/web/.next/fallback-build-manifest.json +2 -2
- package/web/.next/prerender-manifest.json +3 -3
- package/web/.next/required-server-files.js +3 -3
- package/web/.next/required-server-files.json +3 -3
- package/web/.next/routes-manifest.json +24 -0
- package/web/.next/server/app/(dashboard)/@drawer/create/page/build-manifest.json +4 -4
- package/web/.next/server/app/(dashboard)/@drawer/create/page/server-reference-manifest.json +22 -22
- package/web/.next/server/app/(dashboard)/@drawer/create/page.js +1 -1
- 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/build-manifest.json +4 -4
- package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/[tab]/page/server-reference-manifest.json +28 -28
- package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/[tab]/page.js +1 -1
- 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/build-manifest.json +4 -4
- package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page/server-reference-manifest.json +28 -28
- package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page.js +1 -1
- 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/build-manifest.json +4 -4
- package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page/server-reference-manifest.json +20 -20
- package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page.js +1 -1
- 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/build-manifest.json +4 -4
- package/web/.next/server/app/(dashboard)/create/page/server-reference-manifest.json +22 -22
- package/web/.next/server/app/(dashboard)/create/page.js +1 -1
- 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/build-manifest.json +4 -4
- package/web/.next/server/app/(dashboard)/feature/[featureId]/[tab]/page/server-reference-manifest.json +28 -28
- package/web/.next/server/app/(dashboard)/feature/[featureId]/[tab]/page.js +1 -1
- 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/build-manifest.json +4 -4
- package/web/.next/server/app/(dashboard)/feature/[featureId]/page/server-reference-manifest.json +28 -28
- package/web/.next/server/app/(dashboard)/feature/[featureId]/page.js +1 -1
- 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/build-manifest.json +4 -4
- package/web/.next/server/app/(dashboard)/page/server-reference-manifest.json +20 -20
- package/web/.next/server/app/(dashboard)/page.js +1 -1
- 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/build-manifest.json +4 -4
- package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page/server-reference-manifest.json +20 -20
- package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page.js +1 -1
- 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/page/build-manifest.json +4 -4
- 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/build-manifest.json +4 -4
- package/web/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
- package/web/.next/server/app/_not-found/page.js +1 -1
- package/web/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/api/attachments/preview/route.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 +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 +1 -1
- package/web/.next/server/app/api/graph-data/route.js.nft.json +1 -1
- package/web/.next/server/app/api/webhooks/deliveries/route/app-paths-manifest.json +3 -0
- package/web/.next/server/app/api/webhooks/deliveries/route/build-manifest.json +11 -0
- package/web/.next/server/app/api/webhooks/deliveries/route/server-reference-manifest.json +4 -0
- package/web/.next/server/app/api/webhooks/deliveries/route.js +6 -0
- package/web/.next/server/app/api/webhooks/deliveries/route.js.map +5 -0
- package/web/.next/server/app/api/webhooks/deliveries/route.js.nft.json +1 -0
- package/web/.next/server/app/api/webhooks/deliveries/route_client-reference-manifest.js +2 -0
- package/web/.next/server/app/api/webhooks/github/route/app-paths-manifest.json +3 -0
- package/web/.next/server/app/api/webhooks/github/route/build-manifest.json +11 -0
- package/web/.next/server/app/api/webhooks/github/route/server-reference-manifest.json +4 -0
- package/web/.next/server/app/api/webhooks/github/route.js +6 -0
- package/web/.next/server/app/api/webhooks/github/route.js.map +5 -0
- package/web/.next/server/app/api/webhooks/github/route.js.nft.json +1 -0
- package/web/.next/server/app/api/webhooks/github/route_client-reference-manifest.js +2 -0
- package/web/.next/server/app/api/webhooks/status/route/app-paths-manifest.json +3 -0
- package/web/.next/server/app/api/webhooks/status/route/build-manifest.json +11 -0
- package/web/.next/server/app/api/webhooks/status/route/server-reference-manifest.json +4 -0
- package/web/.next/server/app/api/webhooks/status/route.js +6 -0
- package/web/.next/server/app/api/webhooks/status/route.js.map +5 -0
- package/web/.next/server/app/api/webhooks/status/route.js.nft.json +1 -0
- package/web/.next/server/app/api/webhooks/status/route_client-reference-manifest.js +2 -0
- package/web/.next/server/app/settings/page/build-manifest.json +4 -4
- package/web/.next/server/app/settings/page/server-reference-manifest.json +6 -6
- package/web/.next/server/app/settings/page.js +1 -1
- 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/build-manifest.json +4 -4
- package/web/.next/server/app/skills/page/server-reference-manifest.json +6 -6
- package/web/.next/server/app/skills/page.js +1 -1
- 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/build-manifest.json +4 -4
- package/web/.next/server/app/tools/page/server-reference-manifest.json +6 -6
- package/web/.next/server/app/tools/page.js +1 -1
- 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/build-manifest.json +4 -4
- package/web/.next/server/app/version/page/server-reference-manifest.json +1 -1
- package/web/.next/server/app/version/page.js +2 -2
- package/web/.next/server/app/version/page.js.nft.json +1 -1
- package/web/.next/server/app/version/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/webhooks/page/app-paths-manifest.json +3 -0
- package/web/.next/server/app/webhooks/page/build-manifest.json +18 -0
- package/web/.next/server/app/webhooks/page/next-font-manifest.json +6 -0
- package/web/.next/server/app/webhooks/page/react-loadable-manifest.json +1 -0
- package/web/.next/server/app/webhooks/page/server-reference-manifest.json +35 -0
- package/web/.next/server/app/webhooks/page.js +16 -0
- package/web/.next/server/app/webhooks/page.js.map +5 -0
- package/web/.next/server/app/webhooks/page.js.nft.json +1 -0
- package/web/.next/server/app/webhooks/page_client-reference-manifest.js +2 -0
- package/web/.next/server/app-paths-manifest.json +5 -1
- package/web/.next/server/chunks/744ca_web__next-internal_server_app_api_webhooks_deliveries_route_actions_d9e34c8d.js +3 -0
- package/web/.next/server/chunks/744ca_web__next-internal_server_app_api_webhooks_deliveries_route_actions_d9e34c8d.js.map +1 -0
- package/web/.next/server/chunks/744ca_web__next-internal_server_app_api_webhooks_github_route_actions_1b0d0c38.js +3 -0
- package/web/.next/server/chunks/744ca_web__next-internal_server_app_api_webhooks_github_route_actions_1b0d0c38.js.map +1 -0
- package/web/.next/server/chunks/744ca_web__next-internal_server_app_api_webhooks_status_route_actions_1a0d86a1.js +3 -0
- package/web/.next/server/chunks/744ca_web__next-internal_server_app_api_webhooks_status_route_actions_1a0d86a1.js.map +1 -0
- package/web/.next/server/chunks/[root-of-the-server]__15e5d45c._.js +3 -0
- package/web/.next/server/chunks/[root-of-the-server]__15e5d45c._.js.map +1 -0
- package/web/.next/server/chunks/[root-of-the-server]__23be381e._.js +3 -0
- package/web/.next/server/chunks/[root-of-the-server]__23be381e._.js.map +1 -0
- package/web/.next/server/chunks/{[root-of-the-server]__42b7ab55._.js → [root-of-the-server]__3baec744._.js} +2 -2
- package/web/.next/server/chunks/{[root-of-the-server]__42b7ab55._.js.map → [root-of-the-server]__3baec744._.js.map} +1 -1
- package/web/.next/server/chunks/{[root-of-the-server]__53013576._.js → [root-of-the-server]__7d63f9a4._.js} +2 -2
- package/web/.next/server/chunks/[root-of-the-server]__9fc760c0._.js +3 -0
- package/web/.next/server/chunks/[root-of-the-server]__9fc760c0._.js.map +1 -0
- package/web/.next/server/chunks/{[root-of-the-server]__92078db6._.js → [root-of-the-server]__a1c7c02a._.js} +2 -2
- 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/7f428_lucide-react_dist_esm_icons_activity_22f9667f.js +3 -0
- package/web/.next/server/chunks/ssr/7f428_lucide-react_dist_esm_icons_activity_22f9667f.js.map +1 -0
- package/web/.next/server/chunks/ssr/7f428_lucide-react_dist_esm_icons_rocket_1575cd4d.js +3 -0
- package/web/.next/server/chunks/ssr/7f428_lucide-react_dist_esm_icons_rocket_1575cd4d.js.map +1 -0
- package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_@drawer_repository_[repositoryId]_page_actions_f30d0343.js +1 -1
- package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_@drawer_repository_[repositoryId]_page_actions_f30d0343.js.map +1 -1
- package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_repository_[repositoryId]_page_actions_9f1775bb.js +1 -1
- package/web/.next/server/chunks/ssr/8ba4b_server_app_(dashboard)_repository_[repositoryId]_page_actions_9f1775bb.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__11033878._.js +4 -0
- package/web/.next/server/chunks/ssr/{[root-of-the-server]__6b17a22d._.js.map → [root-of-the-server]__11033878._.js.map} +1 -1
- package/web/.next/server/chunks/ssr/{[root-of-the-server]__06fef644._.js → [root-of-the-server]__12c2ab7d._.js} +2 -2
- package/web/.next/server/chunks/ssr/{[root-of-the-server]__06fef644._.js.map → [root-of-the-server]__12c2ab7d._.js.map} +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__2698e9f4._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__2698e9f4._.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__3c16bf85._.js +3 -0
- package/web/.next/server/chunks/ssr/[root-of-the-server]__3c16bf85._.js.map +1 -0
- package/web/.next/server/chunks/ssr/[root-of-the-server]__610f06df._.js +3 -0
- package/web/.next/server/chunks/ssr/[root-of-the-server]__610f06df._.js.map +1 -0
- package/web/.next/server/chunks/ssr/[root-of-the-server]__6bb00d90._.js +2 -2
- package/web/.next/server/chunks/ssr/[root-of-the-server]__6bb00d90._.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__76915230._.js +4 -0
- package/web/.next/server/chunks/ssr/{[root-of-the-server]__8c553503._.js.map → [root-of-the-server]__76915230._.js.map} +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__7a939e4e._.js +4 -0
- package/web/.next/server/chunks/ssr/{[root-of-the-server]__a022445f._.js.map → [root-of-the-server]__7a939e4e._.js.map} +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__87a7ae30._.js +3 -0
- package/web/.next/server/chunks/ssr/[root-of-the-server]__87a7ae30._.js.map +1 -0
- package/web/.next/server/chunks/ssr/[root-of-the-server]__8e3bf5d7._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__8e3bf5d7._.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__920dccd5._.js +4 -0
- package/web/.next/server/chunks/ssr/[root-of-the-server]__920dccd5._.js.map +1 -0
- package/web/.next/server/chunks/ssr/[root-of-the-server]__c07f9f79._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__c07f9f79._.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__c4dc1b5a._.js +4 -0
- package/web/.next/server/chunks/ssr/{[root-of-the-server]__a7de2711._.js.map → [root-of-the-server]__c4dc1b5a._.js.map} +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__c605a375._.js +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__c605a375._.js.map +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__fdc75809._.js +4 -0
- package/web/.next/server/chunks/ssr/{[root-of-the-server]__804c006d._.js.map → [root-of-the-server]__fdc75809._.js.map} +1 -1
- package/web/.next/server/chunks/ssr/_07f57bd0._.js +3 -0
- package/web/.next/server/chunks/ssr/_07f57bd0._.js.map +1 -0
- package/web/.next/server/chunks/ssr/_135edb9c._.js +1 -1
- package/web/.next/server/chunks/ssr/_135edb9c._.js.map +1 -1
- package/web/.next/server/chunks/ssr/_15a756dd._.js +3 -0
- package/web/.next/server/chunks/ssr/_15a756dd._.js.map +1 -0
- package/web/.next/server/chunks/ssr/_26b2c311._.js +3 -0
- package/web/.next/server/chunks/ssr/_26b2c311._.js.map +1 -0
- package/web/.next/server/chunks/ssr/_2f6f48b8._.js +1 -1
- package/web/.next/server/chunks/ssr/_2f6f48b8._.js.map +1 -1
- package/web/.next/server/chunks/ssr/{_92d851b4._.js → _3d1ed2a7._.js} +2 -2
- package/web/.next/server/chunks/ssr/_3d1ed2a7._.js.map +1 -0
- package/web/.next/server/chunks/ssr/_3eca2297._.js +3 -0
- package/web/.next/server/chunks/ssr/{_b294a667._.js.map → _3eca2297._.js.map} +1 -1
- package/web/.next/server/chunks/ssr/_4470c218._.js +3 -0
- package/web/.next/server/chunks/ssr/_4470c218._.js.map +1 -0
- package/web/.next/server/chunks/ssr/_462cc3a6._.js +3 -0
- package/web/.next/server/chunks/ssr/_462cc3a6._.js.map +1 -0
- package/web/.next/server/chunks/ssr/_961cb4b2._.js +3 -0
- package/web/.next/server/chunks/ssr/_961cb4b2._.js.map +1 -0
- package/web/.next/server/chunks/ssr/_a4899a76._.js +3 -0
- package/web/.next/server/chunks/ssr/_a4899a76._.js.map +1 -0
- package/web/.next/server/chunks/ssr/_b5babdfc._.js +9 -0
- package/web/.next/server/chunks/ssr/_b5babdfc._.js.map +1 -0
- package/web/.next/server/chunks/ssr/_b7eb93fc._.js +4 -0
- package/web/.next/server/chunks/ssr/_b7eb93fc._.js.map +1 -0
- package/web/.next/server/chunks/ssr/_cc77a143._.js +1 -1
- package/web/.next/server/chunks/ssr/_cc77a143._.js.map +1 -1
- package/web/.next/server/chunks/ssr/_f8b36e2c._.js +3 -0
- package/web/.next/server/chunks/ssr/_f8b36e2c._.js.map +1 -0
- 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/b1a17_presentation_web_components_features_webhooks_webhooks-page-client_tsx_b2662ea3._.js +3 -0
- package/web/.next/server/chunks/ssr/b1a17_presentation_web_components_features_webhooks_webhooks-page-client_tsx_b2662ea3._.js.map +1 -0
- package/web/.next/server/chunks/ssr/{_1bb131c4._.js → node_modules__pnpm_747b43ac._.js} +2 -2
- package/web/.next/server/chunks/ssr/node_modules__pnpm_747b43ac._.js.map +1 -0
- package/web/.next/server/chunks/ssr/src_presentation_web_5f039db1._.js +2 -2
- package/web/.next/server/chunks/ssr/src_presentation_web_5f039db1._.js.map +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_(dashboard)_page_actions_2a52b697.js +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_(dashboard)_page_actions_2a52b697.js.map +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_skills_page_actions_1fa8042e.js +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_skills_page_actions_1fa8042e.js.map +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_tools_page_actions_24684200.js +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_tools_page_actions_24684200.js.map +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_c4337169._.js +2 -2
- package/web/.next/server/chunks/ssr/src_presentation_web_c4337169._.js.map +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_components_bf50c527._.js +1 -1
- package/web/.next/server/chunks/ssr/src_presentation_web_components_de814675._.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/middleware-build-manifest.js +4 -4
- 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 +57 -35
- package/web/.next/static/chunks/0c76de69325ef923.js +1 -0
- package/web/.next/static/chunks/{658fc2f5f65cf3b5.js → 1b9d34af4064dafe.js} +2 -2
- package/web/.next/static/chunks/{50dd80c8aafa3b20.js → 293ccfbe777d995a.js} +1 -1
- package/web/.next/static/chunks/3835a408dbf8d0bb.js +1 -0
- package/web/.next/static/chunks/{941c13dcdb1978a4.js → 3f0773d9b56e04a2.js} +2 -2
- package/web/.next/static/chunks/40b6bcf1a2de4a0f.js +1 -0
- package/web/.next/static/chunks/4c5b31a2c65d4aa5.js +11 -0
- package/web/.next/static/chunks/59a0709f1b080353.js +1 -0
- package/web/.next/static/chunks/5f6161ceb4263319.js +1 -0
- package/web/.next/static/chunks/77ff1b7b725b7780.js +1 -0
- package/web/.next/static/chunks/{672e3da3ad26005b.js → 98e3a7cf58fc912a.js} +1 -1
- package/web/.next/static/chunks/9af45e885bd4ccef.js +1 -0
- package/web/.next/static/chunks/a57dc2afcf67304a.js +1 -0
- package/web/.next/static/chunks/b4f502a76ba10e4b.js +1 -0
- package/web/.next/static/chunks/b5d8e8100e310d9f.css +1 -0
- package/web/.next/static/chunks/{df85e52b1afb7340.js → b747e59ebdcbd917.js} +1 -1
- package/web/.next/static/chunks/c04ce6760f349f3e.js +1 -0
- package/web/.next/static/chunks/{78ab7b134f0649ac.js → c3c68de2392a9071.js} +1 -1
- package/web/.next/static/chunks/c7e793951b20a67f.js +1 -0
- package/web/.next/static/chunks/cf30e300568abf0f.js +1 -0
- package/web/.next/static/chunks/d43dff89384fb60a.js +1 -0
- package/web/.next/static/chunks/{37ac7fe1cd493293.js → e087f3e5c46c0756.js} +1 -1
- package/web/.next/static/chunks/{turbopack-426d9bc2450816ba.js → turbopack-b1685b5f87ad1d60.js} +1 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__545792ab._.js +0 -3
- package/web/.next/server/chunks/ssr/[root-of-the-server]__545792ab._.js.map +0 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__6b17a22d._.js +0 -4
- package/web/.next/server/chunks/ssr/[root-of-the-server]__804c006d._.js +0 -4
- package/web/.next/server/chunks/ssr/[root-of-the-server]__8c553503._.js +0 -4
- package/web/.next/server/chunks/ssr/[root-of-the-server]__9f155c0e._.js +0 -3
- package/web/.next/server/chunks/ssr/[root-of-the-server]__9f155c0e._.js.map +0 -1
- package/web/.next/server/chunks/ssr/[root-of-the-server]__a022445f._.js +0 -4
- package/web/.next/server/chunks/ssr/[root-of-the-server]__a7de2711._.js +0 -4
- package/web/.next/server/chunks/ssr/_0dc2cfd3._.js +0 -3
- package/web/.next/server/chunks/ssr/_0dc2cfd3._.js.map +0 -1
- package/web/.next/server/chunks/ssr/_1bb131c4._.js.map +0 -1
- package/web/.next/server/chunks/ssr/_1bb79fe8._.js +0 -3
- package/web/.next/server/chunks/ssr/_1bb79fe8._.js.map +0 -1
- package/web/.next/server/chunks/ssr/_469c5451._.js +0 -3
- package/web/.next/server/chunks/ssr/_469c5451._.js.map +0 -1
- package/web/.next/server/chunks/ssr/_6d26a08f._.js +0 -3
- package/web/.next/server/chunks/ssr/_6d26a08f._.js.map +0 -1
- package/web/.next/server/chunks/ssr/_75f4ccb3._.js +0 -3
- package/web/.next/server/chunks/ssr/_75f4ccb3._.js.map +0 -1
- package/web/.next/server/chunks/ssr/_92d851b4._.js.map +0 -1
- package/web/.next/server/chunks/ssr/_b294a667._.js +0 -3
- package/web/.next/server/chunks/ssr/_b5fc318a._.js +0 -3
- package/web/.next/server/chunks/ssr/_b5fc318a._.js.map +0 -1
- package/web/.next/server/chunks/ssr/_f1a649e3._.js +0 -9
- package/web/.next/server/chunks/ssr/_f1a649e3._.js.map +0 -1
- package/web/.next/server/chunks/ssr/node_modules__pnpm_b59e4b16._.js +0 -3
- package/web/.next/server/chunks/ssr/node_modules__pnpm_b59e4b16._.js.map +0 -1
- package/web/.next/static/chunks/0d2af96e64960ef5.css +0 -1
- package/web/.next/static/chunks/11e4439096f8d652.js +0 -1
- package/web/.next/static/chunks/303a1217006489b9.js +0 -1
- package/web/.next/static/chunks/4562ee849ea13c3a.js +0 -1
- package/web/.next/static/chunks/845d8e58a91014e6.js +0 -1
- package/web/.next/static/chunks/84a181ff3270fd9f.js +0 -1
- package/web/.next/static/chunks/96eeeb28e06de78c.js +0 -1
- package/web/.next/static/chunks/9df4681de92ebfc3.js +0 -11
- package/web/.next/static/chunks/cca0f5112a9260d8.js +0 -1
- package/web/.next/static/chunks/e2ef95d904c360f3.js +0 -1
- package/web/.next/static/chunks/e6a2fb8707e079ff.js +0 -1
- package/web/.next/static/chunks/e9c3fbbeb418cbfa.js +0 -1
- package/web/.next/static/chunks/ed3a602003bcb31b.js +0 -1
- /package/web/.next/server/chunks/{[root-of-the-server]__53013576._.js.map → [root-of-the-server]__7d63f9a4._.js.map} +0 -0
- /package/web/.next/server/chunks/{[root-of-the-server]__92078db6._.js.map → [root-of-the-server]__a1c7c02a._.js.map} +0 -0
- /package/web/.next/static/{hsK1x0unrjI_pUsvQhn6P → svtG6xX1MdaGWkml8eX53}/_buildManifest.js +0 -0
- /package/web/.next/static/{hsK1x0unrjI_pUsvQhn6P → svtG6xX1MdaGWkml8eX53}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/static/{hsK1x0unrjI_pUsvQhn6P → svtG6xX1MdaGWkml8eX53}/_ssgManifest.js +0 -0
package/dist/packages/core/src/infrastructure/services/webhook/github-webhook.service.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"github-webhook.service.d.ts","sourceRoot":"","sources":["../../../../../../../packages/core/src/infrastructure/services/webhook/github-webhook.service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,KAAK,EACV,eAAe,EACf,YAAY,EACZ,uBAAuB,EACxB,MAAM,yEAAyE,CAAC;AACjF,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gFAAgF,CAAC;AACzH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wEAAwE,CAAC;AAC5G,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,8EAA8E,CAAC;AASzH,MAAM,MAAM,YAAY,GAAG,CACzB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EAAE,EACd,OAAO,CAAC,EAAE,MAAM,KACb,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAEjD,MAAM,WAAW,iBAAiB;IAChC,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,MAAM,qBAAqB,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,CAAC;AAEpE,MAAM,WAAW,qBAAqB;IACpC,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,qBAAqB,CAAC;IAC9B,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAED,qBAAa,oBAAqB,YAAW,eAAe;IAC1D,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAqB;IACjD,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAgB;IAC7C,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAuB;IAC3D,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAe;IACtC,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAA2B;IAC9D,OAAO,CAAC,QAAQ,CAAC,eAAe,CAA+B;IAC/D,OAAO,CAAC,aAAa,CAAS;gBAG5B,WAAW,EAAE,kBAAkB,EAC/B,YAAY,EAAE,aAAa,EAC3B,mBAAmB,EAAE,oBAAoB,EACzC,MAAM,EAAE,YAAY;IAStB;;;OAGG;IACH,SAAS,IAAI,MAAM;IAInB;;OAEG;IACH,qBAAqB,IAAI,SAAS,iBAAiB,EAAE;IAIrD;;OAEG;IACH,kBAAkB,IAAI,SAAS,qBAAqB,EAAE;IAIhD,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAoBlD,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAkC/C,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC;IA4BrC,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,uBAAuB;IAgCxF,WAAW,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IA4CrD,OAAO,CAAC,cAAc;YAOR,sBAAsB;YAsBtB,qBAAqB;YAiBrB,mBAAmB;YAoBnB,uBAAuB;YAWvB,mBAAmB;YAKnB,qBAAqB;YA6CrB,qBAAqB;YA4BrB,sBAAsB;YAqDtB,eAAe;CAe9B"}
|
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GitHub Webhook Service
|
|
3
|
+
*
|
|
4
|
+
* Manages GitHub webhook registration and incoming event processing.
|
|
5
|
+
* Uses the GitHub CLI (`gh api`) for webhook CRUD operations.
|
|
6
|
+
*
|
|
7
|
+
* Webhook events supplement the existing PR sync polling:
|
|
8
|
+
* - Webhooks provide near-instant event delivery
|
|
9
|
+
* - Polling remains as a fallback for missed events
|
|
10
|
+
*
|
|
11
|
+
* Registered events: pull_request, check_suite, check_run
|
|
12
|
+
*/
|
|
13
|
+
import { createHmac, randomBytes, timingSafeEqual } from 'node:crypto';
|
|
14
|
+
import { SdlcLifecycle, PrStatus, CiStatus } from '../../../domain/generated/output.js';
|
|
15
|
+
import { NotificationEventType, NotificationSeverity } from '../../../domain/generated/output.js';
|
|
16
|
+
const TAG = '[GitHubWebhook]';
|
|
17
|
+
const WEBHOOK_EVENTS = ['pull_request', 'check_suite', 'check_run'];
|
|
18
|
+
const MAX_EVENT_HISTORY = 200;
|
|
19
|
+
export class GitHubWebhookService {
|
|
20
|
+
featureRepo;
|
|
21
|
+
gitPrService;
|
|
22
|
+
notificationService;
|
|
23
|
+
execFn;
|
|
24
|
+
registeredWebhooks = [];
|
|
25
|
+
deliveryHistory = [];
|
|
26
|
+
webhookSecret;
|
|
27
|
+
constructor(featureRepo, gitPrService, notificationService, execFn) {
|
|
28
|
+
this.featureRepo = featureRepo;
|
|
29
|
+
this.gitPrService = gitPrService;
|
|
30
|
+
this.notificationService = notificationService;
|
|
31
|
+
this.execFn = execFn;
|
|
32
|
+
this.webhookSecret = randomBytes(32).toString('hex');
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Get the webhook secret for signature validation.
|
|
36
|
+
* Exposed so the API route can validate incoming requests.
|
|
37
|
+
*/
|
|
38
|
+
getSecret() {
|
|
39
|
+
return this.webhookSecret;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Get the list of registered webhooks (for dashboard display).
|
|
43
|
+
*/
|
|
44
|
+
getRegisteredWebhooks() {
|
|
45
|
+
return this.registeredWebhooks;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Get recent delivery history (newest first).
|
|
49
|
+
*/
|
|
50
|
+
getDeliveryHistory() {
|
|
51
|
+
return this.deliveryHistory;
|
|
52
|
+
}
|
|
53
|
+
async registerWebhooks(publicUrl) {
|
|
54
|
+
const webhookUrl = `${publicUrl}/api/webhooks/github`;
|
|
55
|
+
// Find all repos with features in Review lifecycle
|
|
56
|
+
const features = await this.featureRepo.list({ lifecycle: SdlcLifecycle.Review });
|
|
57
|
+
const repoPaths = new Set();
|
|
58
|
+
for (const feature of features) {
|
|
59
|
+
if (feature.repositoryPath) {
|
|
60
|
+
repoPaths.add(feature.repositoryPath);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
for (const repoPath of repoPaths) {
|
|
64
|
+
await this.registerWebhookForRepo(repoPath, webhookUrl);
|
|
65
|
+
}
|
|
66
|
+
// eslint-disable-next-line no-console
|
|
67
|
+
console.log(`${TAG} Registered webhooks for ${this.registeredWebhooks.length} repositories`);
|
|
68
|
+
}
|
|
69
|
+
async updateWebhookUrl(newUrl) {
|
|
70
|
+
const webhookUrl = `${newUrl}/api/webhooks/github`;
|
|
71
|
+
for (const webhook of this.registeredWebhooks) {
|
|
72
|
+
try {
|
|
73
|
+
await this.execFn('gh', [
|
|
74
|
+
'api',
|
|
75
|
+
'--method',
|
|
76
|
+
'PATCH',
|
|
77
|
+
`-H`,
|
|
78
|
+
'Accept: application/vnd.github+json',
|
|
79
|
+
`/repos/${webhook.repoFullName}/hooks/${webhook.webhookId}`,
|
|
80
|
+
'-f',
|
|
81
|
+
`config[url]=${webhookUrl}`,
|
|
82
|
+
'-f',
|
|
83
|
+
`config[content_type]=json`,
|
|
84
|
+
'-f',
|
|
85
|
+
`config[secret]=${this.webhookSecret}`,
|
|
86
|
+
], { cwd: webhook.repositoryPath });
|
|
87
|
+
// eslint-disable-next-line no-console
|
|
88
|
+
console.log(`${TAG} Updated webhook URL for ${webhook.repoFullName}`);
|
|
89
|
+
}
|
|
90
|
+
catch (error) {
|
|
91
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
92
|
+
// eslint-disable-next-line no-console
|
|
93
|
+
console.warn(`${TAG} Failed to update webhook for ${webhook.repoFullName}: ${msg}`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
async removeWebhooks() {
|
|
98
|
+
for (const webhook of this.registeredWebhooks) {
|
|
99
|
+
try {
|
|
100
|
+
await this.execFn('gh', [
|
|
101
|
+
'api',
|
|
102
|
+
'--method',
|
|
103
|
+
'DELETE',
|
|
104
|
+
`-H`,
|
|
105
|
+
'Accept: application/vnd.github+json',
|
|
106
|
+
`/repos/${webhook.repoFullName}/hooks/${webhook.webhookId}`,
|
|
107
|
+
], { cwd: webhook.repositoryPath });
|
|
108
|
+
// eslint-disable-next-line no-console
|
|
109
|
+
console.log(`${TAG} Removed webhook for ${webhook.repoFullName}`);
|
|
110
|
+
}
|
|
111
|
+
catch (error) {
|
|
112
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
113
|
+
// eslint-disable-next-line no-console
|
|
114
|
+
console.warn(`${TAG} Failed to remove webhook for ${webhook.repoFullName}: ${msg}`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
this.registeredWebhooks.length = 0;
|
|
118
|
+
}
|
|
119
|
+
validateSignature(payload, signature, secret) {
|
|
120
|
+
if (!signature) {
|
|
121
|
+
return { valid: false, error: 'Missing signature header' };
|
|
122
|
+
}
|
|
123
|
+
const parts = signature.split('=');
|
|
124
|
+
if (parts.length !== 2 || parts[0] !== 'sha256') {
|
|
125
|
+
return { valid: false, error: 'Invalid signature format (expected sha256=...)' };
|
|
126
|
+
}
|
|
127
|
+
const expected = createHmac('sha256', secret).update(payload).digest('hex');
|
|
128
|
+
const actual = parts[1];
|
|
129
|
+
// Use timing-safe comparison to prevent timing attacks
|
|
130
|
+
try {
|
|
131
|
+
const expectedBuf = Buffer.from(expected, 'hex');
|
|
132
|
+
const actualBuf = Buffer.from(actual, 'hex');
|
|
133
|
+
if (expectedBuf.length !== actualBuf.length) {
|
|
134
|
+
return { valid: false, error: 'Signature length mismatch' };
|
|
135
|
+
}
|
|
136
|
+
if (!timingSafeEqual(expectedBuf, actualBuf)) {
|
|
137
|
+
return { valid: false, error: 'Signature mismatch' };
|
|
138
|
+
}
|
|
139
|
+
return { valid: true };
|
|
140
|
+
}
|
|
141
|
+
catch {
|
|
142
|
+
return { valid: false, error: 'Invalid signature encoding' };
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
async handleEvent(event) {
|
|
146
|
+
const startTime = Date.now();
|
|
147
|
+
// eslint-disable-next-line no-console
|
|
148
|
+
console.log(`${TAG} Received ${event.source}/${event.eventType} (delivery: ${event.deliveryId})`);
|
|
149
|
+
let status = 'success';
|
|
150
|
+
let statusMessage = 'Processed successfully';
|
|
151
|
+
try {
|
|
152
|
+
switch (event.eventType) {
|
|
153
|
+
case 'pull_request':
|
|
154
|
+
await this.handlePullRequestEvent(event.payload);
|
|
155
|
+
break;
|
|
156
|
+
case 'check_suite':
|
|
157
|
+
await this.handleCheckSuiteEvent(event.payload);
|
|
158
|
+
break;
|
|
159
|
+
case 'check_run':
|
|
160
|
+
await this.handleCheckRunEvent(event.payload);
|
|
161
|
+
break;
|
|
162
|
+
default:
|
|
163
|
+
status = 'ignored';
|
|
164
|
+
statusMessage = `Unhandled event type: ${event.eventType}`;
|
|
165
|
+
// eslint-disable-next-line no-console
|
|
166
|
+
console.log(`${TAG} Ignoring unhandled event type: ${event.eventType}`);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
catch (error) {
|
|
170
|
+
status = 'error';
|
|
171
|
+
statusMessage = error instanceof Error ? error.message : String(error);
|
|
172
|
+
}
|
|
173
|
+
this.recordDelivery({
|
|
174
|
+
deliveryId: event.deliveryId,
|
|
175
|
+
eventType: event.eventType,
|
|
176
|
+
source: event.source,
|
|
177
|
+
receivedAt: new Date().toISOString(),
|
|
178
|
+
status,
|
|
179
|
+
statusMessage,
|
|
180
|
+
durationMs: Date.now() - startTime,
|
|
181
|
+
payload: event.payload,
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
recordDelivery(record) {
|
|
185
|
+
this.deliveryHistory.unshift(record);
|
|
186
|
+
if (this.deliveryHistory.length > MAX_EVENT_HISTORY) {
|
|
187
|
+
this.deliveryHistory.length = MAX_EVENT_HISTORY;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
async handlePullRequestEvent(payload) {
|
|
191
|
+
const action = payload.action;
|
|
192
|
+
const pr = payload.pull_request;
|
|
193
|
+
if (!pr)
|
|
194
|
+
return;
|
|
195
|
+
const prNumber = pr.number;
|
|
196
|
+
const prUrl = pr.html_url ?? '';
|
|
197
|
+
const headBranch = pr.head?.ref;
|
|
198
|
+
// Find feature by PR number or branch
|
|
199
|
+
const feature = await this.findFeatureByPrOrBranch(prNumber, headBranch);
|
|
200
|
+
if (!feature)
|
|
201
|
+
return;
|
|
202
|
+
if (action === 'closed' && pr.merged === true) {
|
|
203
|
+
await this.updateFeaturePrStatus(feature, PrStatus.Merged, prNumber, prUrl);
|
|
204
|
+
}
|
|
205
|
+
else if (action === 'closed') {
|
|
206
|
+
await this.updateFeaturePrStatus(feature, PrStatus.Closed, prNumber, prUrl);
|
|
207
|
+
}
|
|
208
|
+
else if (action === 'reopened') {
|
|
209
|
+
await this.updateFeaturePrStatus(feature, PrStatus.Open, prNumber, prUrl);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
async handleCheckSuiteEvent(payload) {
|
|
213
|
+
const action = payload.action;
|
|
214
|
+
if (action !== 'completed')
|
|
215
|
+
return;
|
|
216
|
+
const checkSuite = payload.check_suite;
|
|
217
|
+
if (!checkSuite)
|
|
218
|
+
return;
|
|
219
|
+
const conclusion = checkSuite.conclusion;
|
|
220
|
+
const headBranch = checkSuite.head_branch;
|
|
221
|
+
const feature = await this.findFeatureByBranch(headBranch);
|
|
222
|
+
if (!feature)
|
|
223
|
+
return;
|
|
224
|
+
const ciStatus = conclusion === 'success' ? CiStatus.Success : CiStatus.Failure;
|
|
225
|
+
await this.updateFeatureCiStatus(feature, ciStatus);
|
|
226
|
+
}
|
|
227
|
+
async handleCheckRunEvent(payload) {
|
|
228
|
+
const action = payload.action;
|
|
229
|
+
if (action !== 'completed')
|
|
230
|
+
return;
|
|
231
|
+
const checkRun = payload.check_run;
|
|
232
|
+
if (!checkRun)
|
|
233
|
+
return;
|
|
234
|
+
const conclusion = checkRun.conclusion;
|
|
235
|
+
const checkSuite = checkRun.check_suite;
|
|
236
|
+
const headBranch = checkSuite?.head_branch;
|
|
237
|
+
if (!headBranch)
|
|
238
|
+
return;
|
|
239
|
+
const feature = await this.findFeatureByBranch(headBranch);
|
|
240
|
+
if (!feature)
|
|
241
|
+
return;
|
|
242
|
+
const ciStatus = conclusion === 'success' ? CiStatus.Success : CiStatus.Failure;
|
|
243
|
+
await this.updateFeatureCiStatus(feature, ciStatus);
|
|
244
|
+
}
|
|
245
|
+
async findFeatureByPrOrBranch(prNumber, branch) {
|
|
246
|
+
const features = await this.featureRepo.list({ lifecycle: SdlcLifecycle.Review });
|
|
247
|
+
// Try matching by PR number first
|
|
248
|
+
const byPr = features.find((f) => f.pr?.number === prNumber);
|
|
249
|
+
if (byPr)
|
|
250
|
+
return byPr;
|
|
251
|
+
// Fall back to branch matching
|
|
252
|
+
return features.find((f) => f.branch === branch) ?? null;
|
|
253
|
+
}
|
|
254
|
+
async findFeatureByBranch(branch) {
|
|
255
|
+
const features = await this.featureRepo.list({ lifecycle: SdlcLifecycle.Review });
|
|
256
|
+
return features.find((f) => f.branch === branch) ?? null;
|
|
257
|
+
}
|
|
258
|
+
async updateFeaturePrStatus(feature, status, prNumber, prUrl) {
|
|
259
|
+
const oldStatus = feature.pr?.status;
|
|
260
|
+
if (oldStatus === status)
|
|
261
|
+
return;
|
|
262
|
+
feature.pr = { ...feature.pr, status, url: prUrl, number: prNumber };
|
|
263
|
+
if (status === PrStatus.Merged) {
|
|
264
|
+
feature.lifecycle = SdlcLifecycle.Maintain;
|
|
265
|
+
}
|
|
266
|
+
await this.featureRepo.update(feature);
|
|
267
|
+
// Emit notification
|
|
268
|
+
const eventType = status === PrStatus.Merged
|
|
269
|
+
? NotificationEventType.PrMerged
|
|
270
|
+
: status === PrStatus.Closed
|
|
271
|
+
? NotificationEventType.PrClosed
|
|
272
|
+
: NotificationEventType.PrChecksPassed; // reopened
|
|
273
|
+
const severity = status === PrStatus.Merged
|
|
274
|
+
? NotificationSeverity.Success
|
|
275
|
+
: status === PrStatus.Closed
|
|
276
|
+
? NotificationSeverity.Warning
|
|
277
|
+
: NotificationSeverity.Info;
|
|
278
|
+
const event = {
|
|
279
|
+
eventType,
|
|
280
|
+
featureId: feature.id,
|
|
281
|
+
agentRunId: feature.agentRunId ?? '',
|
|
282
|
+
featureName: feature.name,
|
|
283
|
+
message: `PR #${prNumber} ${status.toLowerCase()} for ${feature.name} (via webhook)`,
|
|
284
|
+
severity,
|
|
285
|
+
timestamp: new Date().toISOString(),
|
|
286
|
+
};
|
|
287
|
+
this.notificationService.notify(event);
|
|
288
|
+
}
|
|
289
|
+
async updateFeatureCiStatus(feature, ciStatus) {
|
|
290
|
+
if (!feature.pr)
|
|
291
|
+
return;
|
|
292
|
+
if (feature.pr.ciStatus === ciStatus)
|
|
293
|
+
return;
|
|
294
|
+
feature.pr = { ...feature.pr, ciStatus };
|
|
295
|
+
await this.featureRepo.update(feature);
|
|
296
|
+
const eventType = ciStatus === CiStatus.Success
|
|
297
|
+
? NotificationEventType.PrChecksPassed
|
|
298
|
+
: NotificationEventType.PrChecksFailed;
|
|
299
|
+
const severity = ciStatus === CiStatus.Success ? NotificationSeverity.Success : NotificationSeverity.Error;
|
|
300
|
+
const event = {
|
|
301
|
+
eventType,
|
|
302
|
+
featureId: feature.id,
|
|
303
|
+
agentRunId: feature.agentRunId ?? '',
|
|
304
|
+
featureName: feature.name,
|
|
305
|
+
message: `CI ${ciStatus === CiStatus.Success ? 'passed' : 'failed'} for ${feature.name} (via webhook)`,
|
|
306
|
+
severity,
|
|
307
|
+
timestamp: new Date().toISOString(),
|
|
308
|
+
};
|
|
309
|
+
this.notificationService.notify(event);
|
|
310
|
+
}
|
|
311
|
+
async registerWebhookForRepo(repoPath, webhookUrl) {
|
|
312
|
+
try {
|
|
313
|
+
// Get the repo full name (owner/repo) from the remote URL
|
|
314
|
+
const repoFullName = await this.getRepoFullName(repoPath);
|
|
315
|
+
if (!repoFullName) {
|
|
316
|
+
// eslint-disable-next-line no-console
|
|
317
|
+
console.warn(`${TAG} Could not determine repo name for ${repoPath}`);
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
// Create webhook via GitHub API
|
|
321
|
+
const { stdout } = await this.execFn('gh', [
|
|
322
|
+
'api',
|
|
323
|
+
'--method',
|
|
324
|
+
'POST',
|
|
325
|
+
`-H`,
|
|
326
|
+
'Accept: application/vnd.github+json',
|
|
327
|
+
`/repos/${repoFullName}/hooks`,
|
|
328
|
+
'-f',
|
|
329
|
+
'name=web',
|
|
330
|
+
'-f',
|
|
331
|
+
`config[url]=${webhookUrl}`,
|
|
332
|
+
'-f',
|
|
333
|
+
'config[content_type]=json',
|
|
334
|
+
'-f',
|
|
335
|
+
`config[secret]=${this.webhookSecret}`,
|
|
336
|
+
...WEBHOOK_EVENTS.flatMap((e) => ['-f', `events[]=${e}`]),
|
|
337
|
+
'-F',
|
|
338
|
+
'active=true',
|
|
339
|
+
], { cwd: repoPath });
|
|
340
|
+
const response = JSON.parse(stdout);
|
|
341
|
+
const webhookId = response.id;
|
|
342
|
+
this.registeredWebhooks.push({
|
|
343
|
+
repoFullName,
|
|
344
|
+
webhookId,
|
|
345
|
+
repositoryPath: repoPath,
|
|
346
|
+
});
|
|
347
|
+
// eslint-disable-next-line no-console
|
|
348
|
+
console.log(`${TAG} Registered webhook #${webhookId} for ${repoFullName}`);
|
|
349
|
+
}
|
|
350
|
+
catch (error) {
|
|
351
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
352
|
+
// eslint-disable-next-line no-console
|
|
353
|
+
console.warn(`${TAG} Failed to register webhook for ${repoPath}: ${msg}`);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
async getRepoFullName(repoPath) {
|
|
357
|
+
try {
|
|
358
|
+
const remoteUrl = await this.gitPrService.getRemoteUrl(repoPath);
|
|
359
|
+
if (!remoteUrl)
|
|
360
|
+
return null;
|
|
361
|
+
// Parse owner/repo from URL
|
|
362
|
+
// Handles both https://github.com/owner/repo and git@github.com:owner/repo.git
|
|
363
|
+
const match = remoteUrl.match(/github\.com[/:]([\w.-]+)\/([\w.-]+?)(?:\.git)?$/);
|
|
364
|
+
if (!match)
|
|
365
|
+
return null;
|
|
366
|
+
return `${match[1]}/${match[2]}`;
|
|
367
|
+
}
|
|
368
|
+
catch {
|
|
369
|
+
return null;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Webhook Manager Service
|
|
3
|
+
*
|
|
4
|
+
* Orchestrates the Cloudflare Tunnel and GitHub Webhook lifecycle.
|
|
5
|
+
* Follows the singleton pattern used by NotificationWatcherService
|
|
6
|
+
* and PrSyncWatcherService.
|
|
7
|
+
*
|
|
8
|
+
* Lifecycle:
|
|
9
|
+
* 1. Start Cloudflare Tunnel to get a public URL
|
|
10
|
+
* 2. Register GitHub webhooks pointing to the tunnel URL
|
|
11
|
+
* 3. Monitor for tunnel URL changes → update webhooks
|
|
12
|
+
* 4. On shutdown → remove webhooks → stop tunnel
|
|
13
|
+
*
|
|
14
|
+
* The manager is optional — if cloudflared is not installed or webhook
|
|
15
|
+
* registration fails, the system falls back to polling gracefully.
|
|
16
|
+
*/
|
|
17
|
+
import type { ITunnelService } from '../../../application/ports/output/services/tunnel-service.interface.js';
|
|
18
|
+
import type { IWebhookService } from '../../../application/ports/output/services/webhook-service.interface.js';
|
|
19
|
+
import type { RegisteredWebhook, WebhookDeliveryRecord } from './github-webhook.service.js';
|
|
20
|
+
export interface WebhookSystemStatus {
|
|
21
|
+
running: boolean;
|
|
22
|
+
tunnel: {
|
|
23
|
+
connected: boolean;
|
|
24
|
+
publicUrl: string | null;
|
|
25
|
+
};
|
|
26
|
+
webhooks: {
|
|
27
|
+
registered: readonly RegisteredWebhook[];
|
|
28
|
+
totalDeliveries: number;
|
|
29
|
+
successCount: number;
|
|
30
|
+
errorCount: number;
|
|
31
|
+
ignoredCount: number;
|
|
32
|
+
};
|
|
33
|
+
startedAt: string | null;
|
|
34
|
+
}
|
|
35
|
+
export declare class WebhookManagerService {
|
|
36
|
+
private readonly tunnelService;
|
|
37
|
+
private readonly webhookService;
|
|
38
|
+
private running;
|
|
39
|
+
private startedAt;
|
|
40
|
+
constructor(tunnelService: ITunnelService, webhookService: IWebhookService);
|
|
41
|
+
/**
|
|
42
|
+
* Start the tunnel and register webhooks.
|
|
43
|
+
* Fails gracefully — logs warnings but does not throw.
|
|
44
|
+
*
|
|
45
|
+
* @param localPort - The local web server port to tunnel
|
|
46
|
+
*/
|
|
47
|
+
start(localPort: number): Promise<void>;
|
|
48
|
+
/**
|
|
49
|
+
* Stop the webhook system — remove webhooks, then stop the tunnel.
|
|
50
|
+
*/
|
|
51
|
+
stop(): Promise<void>;
|
|
52
|
+
isRunning(): boolean;
|
|
53
|
+
getTunnelUrl(): string | null;
|
|
54
|
+
getStatus(): WebhookSystemStatus;
|
|
55
|
+
getDeliveryHistory(): readonly WebhookDeliveryRecord[];
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Initialize the webhook manager singleton.
|
|
59
|
+
* Must be called once during web server startup.
|
|
60
|
+
*
|
|
61
|
+
* @throws Error if the manager is already initialized
|
|
62
|
+
*/
|
|
63
|
+
export declare function initializeWebhookManager(tunnelService: ITunnelService, webhookService: IWebhookService): void;
|
|
64
|
+
/**
|
|
65
|
+
* Get the webhook manager singleton.
|
|
66
|
+
*
|
|
67
|
+
* @returns The webhook manager service
|
|
68
|
+
* @throws Error if the manager hasn't been initialized yet
|
|
69
|
+
*/
|
|
70
|
+
export declare function getWebhookManager(): WebhookManagerService;
|
|
71
|
+
/**
|
|
72
|
+
* Check if the webhook manager has been initialized.
|
|
73
|
+
*/
|
|
74
|
+
export declare function hasWebhookManager(): boolean;
|
|
75
|
+
/**
|
|
76
|
+
* Reset the webhook manager singleton (for testing purposes only).
|
|
77
|
+
* Stops the manager if running before resetting.
|
|
78
|
+
*
|
|
79
|
+
* @internal
|
|
80
|
+
*/
|
|
81
|
+
export declare function resetWebhookManager(): void;
|
|
82
|
+
//# sourceMappingURL=webhook-manager.service.d.ts.map
|
package/dist/packages/core/src/infrastructure/services/webhook/webhook-manager.service.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webhook-manager.service.d.ts","sourceRoot":"","sources":["../../../../../../../packages/core/src/infrastructure/services/webhook/webhook-manager.service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wEAAwE,CAAC;AAC7G,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yEAAyE,CAAC;AAC/G,OAAO,KAAK,EACV,iBAAiB,EACjB,qBAAqB,EAEtB,MAAM,6BAA6B,CAAC;AAErC,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE;QACN,SAAS,EAAE,OAAO,CAAC;QACnB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;KAC1B,CAAC;IACF,QAAQ,EAAE;QACR,UAAU,EAAE,SAAS,iBAAiB,EAAE,CAAC;QACzC,eAAe,EAAE,MAAM,CAAC;QACxB,YAAY,EAAE,MAAM,CAAC;QACrB,UAAU,EAAE,MAAM,CAAC;QACnB,YAAY,EAAE,MAAM,CAAC;KACtB,CAAC;IACF,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAID,qBAAa,qBAAqB;IAChC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAiB;IAC/C,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAkB;IACjD,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,SAAS,CAAuB;gBAE5B,aAAa,EAAE,cAAc,EAAE,cAAc,EAAE,eAAe;IAK1E;;;;;OAKG;IACG,KAAK,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA2C7C;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IA2B3B,SAAS,IAAI,OAAO;IAIpB,YAAY,IAAI,MAAM,GAAG,IAAI;IAI7B,SAAS,IAAI,mBAAmB;IAoChC,kBAAkB,IAAI,SAAS,qBAAqB,EAAE;CAOvD;AAoBD;;;;;GAKG;AACH,wBAAgB,wBAAwB,CACtC,aAAa,EAAE,cAAc,EAC7B,cAAc,EAAE,eAAe,GAC9B,IAAI,CAMN;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,IAAI,qBAAqB,CASzD;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,OAAO,CAE3C;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,IAAI,IAAI,CAM1C"}
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Webhook Manager Service
|
|
3
|
+
*
|
|
4
|
+
* Orchestrates the Cloudflare Tunnel and GitHub Webhook lifecycle.
|
|
5
|
+
* Follows the singleton pattern used by NotificationWatcherService
|
|
6
|
+
* and PrSyncWatcherService.
|
|
7
|
+
*
|
|
8
|
+
* Lifecycle:
|
|
9
|
+
* 1. Start Cloudflare Tunnel to get a public URL
|
|
10
|
+
* 2. Register GitHub webhooks pointing to the tunnel URL
|
|
11
|
+
* 3. Monitor for tunnel URL changes → update webhooks
|
|
12
|
+
* 4. On shutdown → remove webhooks → stop tunnel
|
|
13
|
+
*
|
|
14
|
+
* The manager is optional — if cloudflared is not installed or webhook
|
|
15
|
+
* registration fails, the system falls back to polling gracefully.
|
|
16
|
+
*/
|
|
17
|
+
const TAG = '[WebhookManager]';
|
|
18
|
+
export class WebhookManagerService {
|
|
19
|
+
tunnelService;
|
|
20
|
+
webhookService;
|
|
21
|
+
running = false;
|
|
22
|
+
startedAt = null;
|
|
23
|
+
constructor(tunnelService, webhookService) {
|
|
24
|
+
this.tunnelService = tunnelService;
|
|
25
|
+
this.webhookService = webhookService;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Start the tunnel and register webhooks.
|
|
29
|
+
* Fails gracefully — logs warnings but does not throw.
|
|
30
|
+
*
|
|
31
|
+
* @param localPort - The local web server port to tunnel
|
|
32
|
+
*/
|
|
33
|
+
async start(localPort) {
|
|
34
|
+
if (this.running)
|
|
35
|
+
return;
|
|
36
|
+
try {
|
|
37
|
+
// Step 1: Start the tunnel
|
|
38
|
+
// eslint-disable-next-line no-console
|
|
39
|
+
console.log(`${TAG} Starting Cloudflare Tunnel for port ${localPort}...`);
|
|
40
|
+
const publicUrl = await this.tunnelService.start(localPort);
|
|
41
|
+
// Step 2: Listen for URL changes
|
|
42
|
+
this.tunnelService.onUrlChange(async (newUrl) => {
|
|
43
|
+
// eslint-disable-next-line no-console
|
|
44
|
+
console.log(`${TAG} Tunnel URL changed, updating webhooks...`);
|
|
45
|
+
try {
|
|
46
|
+
await this.webhookService.updateWebhookUrl(newUrl);
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
50
|
+
// eslint-disable-next-line no-console
|
|
51
|
+
console.warn(`${TAG} Failed to update webhooks after URL change: ${msg}`);
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
// Step 3: Register webhooks with external services
|
|
55
|
+
await this.webhookService.registerWebhooks(publicUrl);
|
|
56
|
+
this.running = true;
|
|
57
|
+
this.startedAt = new Date().toISOString();
|
|
58
|
+
// eslint-disable-next-line no-console
|
|
59
|
+
console.log(`${TAG} Webhook system ready (tunnel: ${publicUrl})`);
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
63
|
+
// eslint-disable-next-line no-console
|
|
64
|
+
console.warn(`${TAG} Failed to start webhook system (falling back to polling): ${msg}`);
|
|
65
|
+
// Clean up partial state
|
|
66
|
+
try {
|
|
67
|
+
await this.tunnelService.stop();
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
// Already stopped or never started
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Stop the webhook system — remove webhooks, then stop the tunnel.
|
|
76
|
+
*/
|
|
77
|
+
async stop() {
|
|
78
|
+
if (!this.running)
|
|
79
|
+
return;
|
|
80
|
+
// eslint-disable-next-line no-console
|
|
81
|
+
console.log(`${TAG} Shutting down webhook system...`);
|
|
82
|
+
try {
|
|
83
|
+
await this.webhookService.removeWebhooks();
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
87
|
+
// eslint-disable-next-line no-console
|
|
88
|
+
console.warn(`${TAG} Failed to remove webhooks during shutdown: ${msg}`);
|
|
89
|
+
}
|
|
90
|
+
try {
|
|
91
|
+
await this.tunnelService.stop();
|
|
92
|
+
}
|
|
93
|
+
catch (error) {
|
|
94
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
95
|
+
// eslint-disable-next-line no-console
|
|
96
|
+
console.warn(`${TAG} Failed to stop tunnel during shutdown: ${msg}`);
|
|
97
|
+
}
|
|
98
|
+
this.running = false;
|
|
99
|
+
// eslint-disable-next-line no-console
|
|
100
|
+
console.log(`${TAG} Webhook system stopped`);
|
|
101
|
+
}
|
|
102
|
+
isRunning() {
|
|
103
|
+
return this.running;
|
|
104
|
+
}
|
|
105
|
+
getTunnelUrl() {
|
|
106
|
+
return this.tunnelService.getPublicUrl();
|
|
107
|
+
}
|
|
108
|
+
getStatus() {
|
|
109
|
+
const ghService = this.webhookService;
|
|
110
|
+
const deliveries = typeof ghService.getDeliveryHistory === 'function' ? ghService.getDeliveryHistory() : [];
|
|
111
|
+
const registered = typeof ghService.getRegisteredWebhooks === 'function'
|
|
112
|
+
? ghService.getRegisteredWebhooks()
|
|
113
|
+
: [];
|
|
114
|
+
let successCount = 0;
|
|
115
|
+
let errorCount = 0;
|
|
116
|
+
let ignoredCount = 0;
|
|
117
|
+
for (const d of deliveries) {
|
|
118
|
+
if (d.status === 'success')
|
|
119
|
+
successCount++;
|
|
120
|
+
else if (d.status === 'error')
|
|
121
|
+
errorCount++;
|
|
122
|
+
else
|
|
123
|
+
ignoredCount++;
|
|
124
|
+
}
|
|
125
|
+
return {
|
|
126
|
+
running: this.running,
|
|
127
|
+
tunnel: {
|
|
128
|
+
connected: this.tunnelService.isRunning(),
|
|
129
|
+
publicUrl: this.tunnelService.getPublicUrl(),
|
|
130
|
+
},
|
|
131
|
+
webhooks: {
|
|
132
|
+
registered,
|
|
133
|
+
totalDeliveries: deliveries.length,
|
|
134
|
+
successCount,
|
|
135
|
+
errorCount,
|
|
136
|
+
ignoredCount,
|
|
137
|
+
},
|
|
138
|
+
startedAt: this.startedAt,
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
getDeliveryHistory() {
|
|
142
|
+
const ghService = this.webhookService;
|
|
143
|
+
if (typeof ghService.getDeliveryHistory === 'function') {
|
|
144
|
+
return ghService.getDeliveryHistory();
|
|
145
|
+
}
|
|
146
|
+
return [];
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
// --- Singleton accessors ---
|
|
150
|
+
//
|
|
151
|
+
// The singleton is stored on globalThis so that it survives module duplication
|
|
152
|
+
// across bundler contexts (e.g. Next.js Turbopack bundles API routes separately
|
|
153
|
+
// from the dev-server entry point, giving each its own copy of module-level
|
|
154
|
+
// variables). By using globalThis, the same manager instance is visible to both
|
|
155
|
+
// the initializer (dev-server.ts / CLI) and the API route handlers.
|
|
156
|
+
const GLOBAL_KEY = '__shepWebhookManager';
|
|
157
|
+
function getInstance() {
|
|
158
|
+
return globalThis[GLOBAL_KEY] ?? null;
|
|
159
|
+
}
|
|
160
|
+
function setInstance(instance) {
|
|
161
|
+
globalThis[GLOBAL_KEY] = instance;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Initialize the webhook manager singleton.
|
|
165
|
+
* Must be called once during web server startup.
|
|
166
|
+
*
|
|
167
|
+
* @throws Error if the manager is already initialized
|
|
168
|
+
*/
|
|
169
|
+
export function initializeWebhookManager(tunnelService, webhookService) {
|
|
170
|
+
if (getInstance() !== null) {
|
|
171
|
+
throw new Error('Webhook manager already initialized. Cannot re-initialize.');
|
|
172
|
+
}
|
|
173
|
+
setInstance(new WebhookManagerService(tunnelService, webhookService));
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Get the webhook manager singleton.
|
|
177
|
+
*
|
|
178
|
+
* @returns The webhook manager service
|
|
179
|
+
* @throws Error if the manager hasn't been initialized yet
|
|
180
|
+
*/
|
|
181
|
+
export function getWebhookManager() {
|
|
182
|
+
const instance = getInstance();
|
|
183
|
+
if (instance === null) {
|
|
184
|
+
throw new Error('Webhook manager not initialized. Call initializeWebhookManager() during web server startup.');
|
|
185
|
+
}
|
|
186
|
+
return instance;
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Check if the webhook manager has been initialized.
|
|
190
|
+
*/
|
|
191
|
+
export function hasWebhookManager() {
|
|
192
|
+
return getInstance() !== null;
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Reset the webhook manager singleton (for testing purposes only).
|
|
196
|
+
* Stops the manager if running before resetting.
|
|
197
|
+
*
|
|
198
|
+
* @internal
|
|
199
|
+
*/
|
|
200
|
+
export function resetWebhookManager() {
|
|
201
|
+
const instance = getInstance();
|
|
202
|
+
if (instance !== null) {
|
|
203
|
+
void instance.stop();
|
|
204
|
+
}
|
|
205
|
+
setInstance(null);
|
|
206
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ui.command.d.ts","sourceRoot":"","sources":["../../../../../src/presentation/cli/commands/ui.command.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,OAAO,EAAwB,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"ui.command.d.ts","sourceRoot":"","sources":["../../../../../src/presentation/cli/commands/ui.command.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,OAAO,EAAwB,MAAM,WAAW,CAAC;AAuC1D;;GAEG;AACH,wBAAgB,eAAe,IAAI,OAAO,CAwGzC"}
|