@shepai/cli 1.53.2 → 1.55.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (298) hide show
  1. package/README.md +22 -8
  2. package/apis/json-schema/NotificationEvent.yaml +4 -0
  3. package/dist/packages/core/src/application/ports/output/services/daemon-service.interface.d.ts +49 -0
  4. package/dist/packages/core/src/application/ports/output/services/daemon-service.interface.d.ts.map +1 -0
  5. package/dist/packages/core/src/application/ports/output/services/daemon-service.interface.js +7 -0
  6. package/dist/packages/core/src/application/ports/output/services/index.d.ts +1 -0
  7. package/dist/packages/core/src/application/ports/output/services/index.d.ts.map +1 -1
  8. package/dist/packages/core/src/domain/generated/output.d.ts +4 -0
  9. package/dist/packages/core/src/domain/generated/output.d.ts.map +1 -1
  10. package/dist/packages/core/src/infrastructure/di/container.d.ts.map +1 -1
  11. package/dist/packages/core/src/infrastructure/di/container.js +2 -0
  12. package/dist/packages/core/src/infrastructure/services/browser-opener.service.d.ts +1 -0
  13. package/dist/packages/core/src/infrastructure/services/browser-opener.service.d.ts.map +1 -1
  14. package/dist/packages/core/src/infrastructure/services/browser-opener.service.js +4 -0
  15. package/dist/packages/core/src/infrastructure/services/daemon/daemon-pid.service.d.ts +16 -0
  16. package/dist/packages/core/src/infrastructure/services/daemon/daemon-pid.service.d.ts.map +1 -0
  17. package/dist/packages/core/src/infrastructure/services/daemon/daemon-pid.service.js +72 -0
  18. package/dist/packages/core/src/infrastructure/services/filesystem/shep-directory.service.d.ts +7 -0
  19. package/dist/packages/core/src/infrastructure/services/filesystem/shep-directory.service.d.ts.map +1 -1
  20. package/dist/packages/core/src/infrastructure/services/filesystem/shep-directory.service.js +9 -0
  21. package/dist/packages/core/src/infrastructure/services/notifications/notification-bus.d.ts +4 -0
  22. package/dist/packages/core/src/infrastructure/services/notifications/notification-bus.d.ts.map +1 -1
  23. package/dist/packages/core/src/infrastructure/services/notifications/notification-bus.js +19 -6
  24. package/dist/packages/core/src/infrastructure/services/notifications/notification-watcher.service.d.ts +5 -3
  25. package/dist/packages/core/src/infrastructure/services/notifications/notification-watcher.service.d.ts.map +1 -1
  26. package/dist/packages/core/src/infrastructure/services/notifications/notification-watcher.service.js +21 -13
  27. package/dist/src/presentation/cli/commands/_serve.command.d.ts +32 -0
  28. package/dist/src/presentation/cli/commands/_serve.command.d.ts.map +1 -0
  29. package/dist/src/presentation/cli/commands/_serve.command.js +91 -0
  30. package/dist/src/presentation/cli/commands/daemon/start-daemon.d.ts +27 -0
  31. package/dist/src/presentation/cli/commands/daemon/start-daemon.d.ts.map +1 -0
  32. package/dist/src/presentation/cli/commands/daemon/start-daemon.js +135 -0
  33. package/dist/src/presentation/cli/commands/start.command.d.ts +15 -0
  34. package/dist/src/presentation/cli/commands/start.command.d.ts.map +1 -0
  35. package/dist/src/presentation/cli/commands/start.command.js +33 -0
  36. package/dist/src/presentation/cli/commands/status.command.d.ts +20 -0
  37. package/dist/src/presentation/cli/commands/status.command.d.ts.map +1 -0
  38. package/dist/src/presentation/cli/commands/status.command.js +119 -0
  39. package/dist/src/presentation/cli/commands/stop.command.d.ts +22 -0
  40. package/dist/src/presentation/cli/commands/stop.command.d.ts.map +1 -0
  41. package/dist/src/presentation/cli/commands/stop.command.js +79 -0
  42. package/dist/src/presentation/cli/commands/ui.command.d.ts.map +1 -1
  43. package/dist/src/presentation/cli/commands/ui.command.js +2 -1
  44. package/dist/src/presentation/cli/index.d.ts +5 -2
  45. package/dist/src/presentation/cli/index.d.ts.map +1 -1
  46. package/dist/src/presentation/cli/index.js +23 -4
  47. package/dist/src/presentation/web/app/api/agent-events/health/route.d.ts +13 -0
  48. package/dist/src/presentation/web/app/api/agent-events/health/route.d.ts.map +1 -0
  49. package/dist/src/presentation/web/app/api/agent-events/health/route.js +60 -0
  50. package/dist/src/presentation/web/app/api/agent-events/route.d.ts +8 -4
  51. package/dist/src/presentation/web/app/api/agent-events/route.d.ts.map +1 -1
  52. package/dist/src/presentation/web/app/api/agent-events/route.js +227 -55
  53. package/dist/src/presentation/web/app/layout.d.ts.map +1 -1
  54. package/dist/src/presentation/web/app/layout.js +14 -0
  55. package/dist/src/presentation/web/components/common/feature-node/derive-feature-state.d.ts +5 -1
  56. package/dist/src/presentation/web/components/common/feature-node/derive-feature-state.d.ts.map +1 -1
  57. package/dist/src/presentation/web/components/common/feature-node/derive-feature-state.js +29 -1
  58. package/dist/src/presentation/web/components/common/repository-node/repository-node.d.ts.map +1 -1
  59. package/dist/src/presentation/web/components/common/repository-node/repository-node.js +1 -1
  60. package/dist/src/presentation/web/components/common/sound-toggle/index.d.ts +2 -0
  61. package/dist/src/presentation/web/components/common/sound-toggle/index.d.ts.map +1 -0
  62. package/dist/src/presentation/web/components/common/sound-toggle/index.js +1 -0
  63. package/dist/src/presentation/web/components/common/sound-toggle/sound-toggle.d.ts +2 -0
  64. package/dist/src/presentation/web/components/common/sound-toggle/sound-toggle.d.ts.map +1 -0
  65. package/dist/src/presentation/web/components/common/sound-toggle/sound-toggle.js +9 -0
  66. package/dist/src/presentation/web/components/common/sound-toggle/sound-toggle.stories.d.ts +8 -0
  67. package/dist/src/presentation/web/components/common/sound-toggle/sound-toggle.stories.d.ts.map +1 -0
  68. package/dist/src/presentation/web/components/common/sound-toggle/sound-toggle.stories.js +17 -0
  69. package/dist/src/presentation/web/components/features/control-center/control-center-inner.d.ts.map +1 -1
  70. package/dist/src/presentation/web/components/features/control-center/control-center-inner.js +14 -2
  71. package/dist/src/presentation/web/components/features/control-center/use-control-center-state.d.ts +1 -0
  72. package/dist/src/presentation/web/components/features/control-center/use-control-center-state.d.ts.map +1 -1
  73. package/dist/src/presentation/web/components/features/control-center/use-control-center-state.js +151 -19
  74. package/dist/src/presentation/web/components/layouts/app-shell/app-shell.d.ts.map +1 -1
  75. package/dist/src/presentation/web/components/layouts/app-shell/app-shell.js +2 -1
  76. package/dist/src/presentation/web/dev-server.js +14 -0
  77. package/dist/src/presentation/web/hooks/use-agent-events.d.ts +7 -0
  78. package/dist/src/presentation/web/hooks/use-agent-events.d.ts.map +1 -1
  79. package/dist/src/presentation/web/hooks/use-agent-events.js +135 -47
  80. package/dist/src/presentation/web/hooks/use-notifications.d.ts.map +1 -1
  81. package/dist/src/presentation/web/hooks/use-notifications.js +56 -16
  82. package/dist/src/presentation/web/hooks/use-notifications.stories.d.ts.map +1 -1
  83. package/dist/src/presentation/web/hooks/use-notifications.stories.js +28 -9
  84. package/dist/src/presentation/web/hooks/use-sound-enabled.d.ts +6 -0
  85. package/dist/src/presentation/web/hooks/use-sound-enabled.d.ts.map +1 -0
  86. package/dist/src/presentation/web/hooks/use-sound-enabled.js +27 -0
  87. package/dist/src/presentation/web/hooks/use-sound.d.ts +15 -0
  88. package/dist/src/presentation/web/hooks/use-sound.d.ts.map +1 -0
  89. package/dist/src/presentation/web/hooks/use-sound.js +83 -0
  90. package/dist/src/presentation/web/hooks/use-sound.stories.d.ts +8 -0
  91. package/dist/src/presentation/web/hooks/use-sound.stories.d.ts.map +1 -0
  92. package/dist/src/presentation/web/hooks/use-sound.stories.js +67 -0
  93. package/dist/src/presentation/web/public/agent-events-sw.d.ts +35 -0
  94. package/dist/src/presentation/web/public/agent-events-sw.d.ts.map +1 -0
  95. package/dist/src/presentation/web/public/agent-events-sw.js +140 -0
  96. package/dist/tsconfig.build.tsbuildinfo +1 -1
  97. package/package.json +1 -1
  98. package/web/.next/BUILD_ID +1 -1
  99. package/web/.next/app-path-routes-manifest.json +1 -0
  100. package/web/.next/build-manifest.json +2 -2
  101. package/web/.next/cache/.previewinfo +1 -1
  102. package/web/.next/cache/.rscinfo +1 -1
  103. package/web/.next/cache/.tsbuildinfo +1 -1
  104. package/web/.next/cache/config.json +3 -3
  105. package/web/.next/fallback-build-manifest.json +2 -2
  106. package/web/.next/prerender-manifest.json +3 -3
  107. package/web/.next/required-server-files.js +1 -1
  108. package/web/.next/required-server-files.json +1 -1
  109. package/web/.next/routes-manifest.json +6 -0
  110. package/web/.next/server/app/_global-error.html +2 -2
  111. package/web/.next/server/app/_global-error.rsc +1 -1
  112. package/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  113. package/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  114. package/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  115. package/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  116. package/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  117. package/web/.next/server/app/_not-found/page.js.nft.json +1 -1
  118. package/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  119. package/web/.next/server/app/api/agent-events/health/route/app-paths-manifest.json +3 -0
  120. package/web/.next/server/app/api/agent-events/health/route/build-manifest.json +11 -0
  121. package/web/.next/server/app/api/agent-events/health/route/server-reference-manifest.json +4 -0
  122. package/web/.next/server/app/api/agent-events/health/route.js +6 -0
  123. package/web/.next/server/app/api/agent-events/health/route.js.map +5 -0
  124. package/web/.next/server/app/api/agent-events/health/route.js.nft.json +1 -0
  125. package/web/.next/server/app/api/agent-events/health/route_client-reference-manifest.js +2 -0
  126. package/web/.next/server/app/api/agent-events/route.js +2 -1
  127. package/web/.next/server/app/api/agent-events/route.js.nft.json +1 -1
  128. package/web/.next/server/app/page/server-reference-manifest.json +14 -14
  129. package/web/.next/server/app/page.js.nft.json +1 -1
  130. package/web/.next/server/app/page_client-reference-manifest.js +1 -1
  131. package/web/.next/server/app/version/page.js +1 -1
  132. package/web/.next/server/app/version/page.js.nft.json +1 -1
  133. package/web/.next/server/app/version/page_client-reference-manifest.js +1 -1
  134. package/web/.next/server/app-paths-manifest.json +1 -0
  135. package/web/.next/server/chunks/403f9_next_dist_esm_build_templates_app-route_4d623b8e.js +6 -0
  136. package/web/.next/server/chunks/403f9_next_dist_esm_build_templates_app-route_4d623b8e.js.map +1 -0
  137. package/web/.next/server/chunks/744ca_web__next-internal_server_app_api_agent-events_health_route_actions_4d471160.js +3 -0
  138. package/web/.next/server/chunks/744ca_web__next-internal_server_app_api_agent-events_health_route_actions_4d471160.js.map +1 -0
  139. package/web/.next/server/chunks/[root-of-the-server]__3b22a1eb._.js +3 -0
  140. package/web/.next/server/chunks/[root-of-the-server]__3b22a1eb._.js.map +1 -0
  141. package/web/.next/server/chunks/[root-of-the-server]__d9d410a8._.js +3 -0
  142. package/web/.next/server/chunks/[root-of-the-server]__d9d410a8._.js.map +1 -0
  143. package/web/.next/server/chunks/ssr/[root-of-the-server]__03a0dba7._.js +2 -2
  144. package/web/.next/server/chunks/ssr/[root-of-the-server]__03a0dba7._.js.map +1 -1
  145. package/web/.next/server/chunks/ssr/[root-of-the-server]__2d1a8e37._.js +2 -2
  146. package/web/.next/server/chunks/ssr/[root-of-the-server]__2d1a8e37._.js.map +1 -1
  147. package/web/.next/{standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__249c74f6._.js → server/chunks/ssr/[root-of-the-server]__6e8b5181._.js} +2 -2
  148. package/web/.next/server/chunks/ssr/{_1fafa9db._.js → _1632a21f._.js} +2 -2
  149. package/web/.next/server/chunks/ssr/_1632a21f._.js.map +1 -0
  150. package/web/.next/server/chunks/ssr/src_presentation_web_67178b88._.js +1 -1
  151. package/web/.next/server/chunks/ssr/src_presentation_web_67178b88._.js.map +1 -1
  152. package/web/.next/server/chunks/ssr/src_presentation_web_components_e599bb8c._.js +1 -1
  153. package/web/.next/server/chunks/ssr/src_presentation_web_components_e599bb8c._.js.map +1 -1
  154. package/web/.next/server/chunks/ssr/src_presentation_web_ed0934e5._.js.map +1 -1
  155. package/web/.next/server/pages/500.html +2 -2
  156. package/web/.next/server/server-reference-manifest.js +1 -1
  157. package/web/.next/server/server-reference-manifest.json +15 -15
  158. package/web/.next/standalone/src/presentation/web/.next/BUILD_ID +1 -1
  159. package/web/.next/standalone/src/presentation/web/.next/app-path-routes-manifest.json +1 -0
  160. package/web/.next/standalone/src/presentation/web/.next/build-manifest.json +2 -2
  161. package/web/.next/standalone/src/presentation/web/.next/prerender-manifest.json +3 -3
  162. package/web/.next/standalone/src/presentation/web/.next/required-server-files.json +1 -1
  163. package/web/.next/standalone/src/presentation/web/.next/routes-manifest.json +6 -0
  164. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.html +2 -2
  165. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.rsc +1 -1
  166. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  167. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  168. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  169. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  170. package/web/.next/standalone/src/presentation/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  171. package/web/.next/standalone/src/presentation/web/.next/server/app/_not-found/page.js.nft.json +1 -1
  172. package/web/.next/standalone/src/presentation/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  173. package/web/.next/standalone/src/presentation/web/.next/server/app/api/agent-events/health/route/app-paths-manifest.json +3 -0
  174. package/web/.next/standalone/src/presentation/web/.next/server/app/api/agent-events/health/route/build-manifest.json +11 -0
  175. package/web/.next/standalone/src/presentation/web/.next/server/app/api/agent-events/health/route/server-reference-manifest.json +4 -0
  176. package/web/.next/standalone/src/presentation/web/.next/server/app/api/agent-events/health/route.js +6 -0
  177. package/web/.next/standalone/src/presentation/web/.next/server/app/api/agent-events/health/route.js.map +5 -0
  178. package/web/.next/standalone/src/presentation/web/.next/server/app/api/agent-events/health/route.js.nft.json +1 -0
  179. package/web/.next/standalone/src/presentation/web/.next/server/app/api/agent-events/health/route_client-reference-manifest.js +2 -0
  180. package/web/.next/standalone/src/presentation/web/.next/server/app/api/agent-events/route.js +2 -1
  181. package/web/.next/standalone/src/presentation/web/.next/server/app/api/agent-events/route.js.nft.json +1 -1
  182. package/web/.next/standalone/src/presentation/web/.next/server/app/page/server-reference-manifest.json +14 -14
  183. package/web/.next/standalone/src/presentation/web/.next/server/app/page.js.nft.json +1 -1
  184. package/web/.next/standalone/src/presentation/web/.next/server/app/page_client-reference-manifest.js +1 -1
  185. package/web/.next/standalone/src/presentation/web/.next/server/app/version/page.js +1 -1
  186. package/web/.next/standalone/src/presentation/web/.next/server/app/version/page.js.nft.json +1 -1
  187. package/web/.next/standalone/src/presentation/web/.next/server/app/version/page_client-reference-manifest.js +1 -1
  188. package/web/.next/standalone/src/presentation/web/.next/server/app-paths-manifest.json +1 -0
  189. package/web/.next/standalone/src/presentation/web/.next/server/chunks/403f9_next_dist_esm_build_templates_app-route_4d623b8e.js +6 -0
  190. package/web/.next/standalone/src/presentation/web/.next/server/chunks/744ca_web__next-internal_server_app_api_agent-events_health_route_actions_4d471160.js +3 -0
  191. package/web/.next/standalone/src/presentation/web/.next/server/chunks/[root-of-the-server]__3b22a1eb._.js +3 -0
  192. package/web/.next/standalone/src/presentation/web/.next/server/chunks/[root-of-the-server]__d9d410a8._.js +3 -0
  193. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__03a0dba7._.js +2 -2
  194. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__2d1a8e37._.js +2 -2
  195. package/web/.next/{server/chunks/ssr/[root-of-the-server]__249c74f6._.js → standalone/src/presentation/web/.next/server/chunks/ssr/[root-of-the-server]__6e8b5181._.js} +2 -2
  196. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/{_1fafa9db._.js → _1632a21f._.js} +2 -2
  197. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web_67178b88._.js +1 -1
  198. package/web/.next/standalone/src/presentation/web/.next/server/chunks/ssr/src_presentation_web_components_e599bb8c._.js +1 -1
  199. package/web/.next/standalone/src/presentation/web/.next/server/pages/500.html +2 -2
  200. package/web/.next/standalone/src/presentation/web/.next/server/server-reference-manifest.js +1 -1
  201. package/web/.next/standalone/src/presentation/web/.next/server/server-reference-manifest.json +15 -15
  202. package/web/.next/standalone/src/presentation/web/app/api/agent-events/health/route.ts +69 -0
  203. package/web/.next/standalone/src/presentation/web/app/api/agent-events/route.ts +258 -54
  204. package/web/.next/standalone/src/presentation/web/app/layout.tsx +14 -0
  205. package/web/.next/standalone/src/presentation/web/components/common/feature-node/derive-feature-state.ts +39 -2
  206. package/web/.next/standalone/src/presentation/web/components/common/repository-node/repository-node.tsx +1 -0
  207. package/web/.next/standalone/src/presentation/web/components/common/sound-toggle/index.ts +1 -0
  208. package/web/.next/standalone/src/presentation/web/components/common/sound-toggle/sound-toggle.stories.tsx +27 -0
  209. package/web/.next/standalone/src/presentation/web/components/common/sound-toggle/sound-toggle.tsx +21 -0
  210. package/web/.next/standalone/src/presentation/web/components/features/control-center/control-center-inner.tsx +15 -1
  211. package/web/.next/standalone/src/presentation/web/components/features/control-center/use-control-center-state.ts +194 -31
  212. package/web/.next/standalone/src/presentation/web/components/layouts/app-shell/app-shell.tsx +3 -1
  213. package/web/.next/standalone/src/presentation/web/dev-server.ts +21 -0
  214. package/web/.next/standalone/src/presentation/web/hooks/use-agent-events.ts +161 -52
  215. package/web/.next/standalone/src/presentation/web/hooks/use-notifications.stories.tsx +39 -10
  216. package/web/.next/standalone/src/presentation/web/hooks/use-notifications.ts +68 -15
  217. package/web/.next/standalone/src/presentation/web/hooks/use-sound-enabled.ts +38 -0
  218. package/web/.next/standalone/src/presentation/web/hooks/use-sound.stories.tsx +109 -0
  219. package/web/.next/standalone/src/presentation/web/hooks/use-sound.ts +101 -0
  220. package/web/.next/standalone/src/presentation/web/public/agent-events-sw.js +164 -0
  221. package/web/.next/standalone/src/presentation/web/public/sounds/button.wav +0 -0
  222. package/web/.next/standalone/src/presentation/web/public/sounds/caution.wav +0 -0
  223. package/web/.next/standalone/src/presentation/web/public/sounds/celebration.wav +0 -0
  224. package/web/.next/standalone/src/presentation/web/public/sounds/disabled.wav +0 -0
  225. package/web/.next/standalone/src/presentation/web/public/sounds/notification.wav +0 -0
  226. package/web/.next/standalone/src/presentation/web/public/sounds/progress_loop.wav +0 -0
  227. package/web/.next/standalone/src/presentation/web/public/sounds/ringtone_loop.wav +0 -0
  228. package/web/.next/standalone/src/presentation/web/public/sounds/select.wav +0 -0
  229. package/web/.next/standalone/src/presentation/web/public/sounds/swipe.wav +0 -0
  230. package/web/.next/standalone/src/presentation/web/public/sounds/swipe_01.wav +0 -0
  231. package/web/.next/standalone/src/presentation/web/public/sounds/swipe_02.wav +0 -0
  232. package/web/.next/standalone/src/presentation/web/public/sounds/swipe_03.wav +0 -0
  233. package/web/.next/standalone/src/presentation/web/public/sounds/swipe_04.wav +0 -0
  234. package/web/.next/standalone/src/presentation/web/public/sounds/swipe_05.wav +0 -0
  235. package/web/.next/standalone/src/presentation/web/public/sounds/tap_01.wav +0 -0
  236. package/web/.next/standalone/src/presentation/web/public/sounds/tap_02.wav +0 -0
  237. package/web/.next/standalone/src/presentation/web/public/sounds/tap_03.wav +0 -0
  238. package/web/.next/standalone/src/presentation/web/public/sounds/tap_04.wav +0 -0
  239. package/web/.next/standalone/src/presentation/web/public/sounds/tap_05.wav +0 -0
  240. package/web/.next/standalone/src/presentation/web/public/sounds/toggle_off.wav +0 -0
  241. package/web/.next/standalone/src/presentation/web/public/sounds/toggle_on.wav +0 -0
  242. package/web/.next/standalone/src/presentation/web/public/sounds/transition_down.wav +0 -0
  243. package/web/.next/standalone/src/presentation/web/public/sounds/transition_up.wav +0 -0
  244. package/web/.next/standalone/src/presentation/web/public/sounds/type_01.wav +0 -0
  245. package/web/.next/standalone/src/presentation/web/public/sounds/type_02.wav +0 -0
  246. package/web/.next/standalone/src/presentation/web/public/sounds/type_03.wav +0 -0
  247. package/web/.next/standalone/src/presentation/web/public/sounds/type_04.wav +0 -0
  248. package/web/.next/standalone/src/presentation/web/public/sounds/type_05.wav +0 -0
  249. package/web/.next/standalone/src/presentation/web/server.js +1 -1
  250. package/web/.next/static/chunks/1f13bc20e938335c.css +2 -0
  251. package/web/.next/static/chunks/3eaa6f2966e27b8f.js +10 -0
  252. package/web/.next/static/chunks/{68fb91b6eca14536.js → 8f4461aee9a05741.js} +2 -2
  253. package/web/.next/static/chunks/9a82d8c76f175235.js +1 -0
  254. package/web/.next/trace +1 -1
  255. package/web/.next/trace-build +1 -1
  256. package/web/.next/types/link.d.ts +1 -0
  257. package/web/.next/types/routes.d.ts +2 -1
  258. package/web/.next/types/validator.ts +9 -0
  259. package/web/public/agent-events-sw.js +164 -0
  260. package/web/public/sounds/button.wav +0 -0
  261. package/web/public/sounds/caution.wav +0 -0
  262. package/web/public/sounds/celebration.wav +0 -0
  263. package/web/public/sounds/disabled.wav +0 -0
  264. package/web/public/sounds/notification.wav +0 -0
  265. package/web/public/sounds/progress_loop.wav +0 -0
  266. package/web/public/sounds/ringtone_loop.wav +0 -0
  267. package/web/public/sounds/select.wav +0 -0
  268. package/web/public/sounds/swipe.wav +0 -0
  269. package/web/public/sounds/swipe_01.wav +0 -0
  270. package/web/public/sounds/swipe_02.wav +0 -0
  271. package/web/public/sounds/swipe_03.wav +0 -0
  272. package/web/public/sounds/swipe_04.wav +0 -0
  273. package/web/public/sounds/swipe_05.wav +0 -0
  274. package/web/public/sounds/tap_01.wav +0 -0
  275. package/web/public/sounds/tap_02.wav +0 -0
  276. package/web/public/sounds/tap_03.wav +0 -0
  277. package/web/public/sounds/tap_04.wav +0 -0
  278. package/web/public/sounds/tap_05.wav +0 -0
  279. package/web/public/sounds/toggle_off.wav +0 -0
  280. package/web/public/sounds/toggle_on.wav +0 -0
  281. package/web/public/sounds/transition_down.wav +0 -0
  282. package/web/public/sounds/transition_up.wav +0 -0
  283. package/web/public/sounds/type_01.wav +0 -0
  284. package/web/public/sounds/type_02.wav +0 -0
  285. package/web/public/sounds/type_03.wav +0 -0
  286. package/web/public/sounds/type_04.wav +0 -0
  287. package/web/public/sounds/type_05.wav +0 -0
  288. package/web/.next/server/chunks/[root-of-the-server]__7d5c8b74._.js +0 -6
  289. package/web/.next/server/chunks/[root-of-the-server]__7d5c8b74._.js.map +0 -1
  290. package/web/.next/server/chunks/ssr/_1fafa9db._.js.map +0 -1
  291. package/web/.next/standalone/src/presentation/web/.next/server/chunks/[root-of-the-server]__7d5c8b74._.js +0 -6
  292. package/web/.next/static/chunks/13d5334b17480a16.js +0 -10
  293. package/web/.next/static/chunks/16dec8021e251230.css +0 -2
  294. package/web/.next/static/chunks/22f30ce91a3c79c1.js +0 -1
  295. /package/web/.next/server/chunks/ssr/{[root-of-the-server]__249c74f6._.js.map → [root-of-the-server]__6e8b5181._.js.map} +0 -0
  296. /package/web/.next/static/{nFvWyY2BF3AIS3FpZBKEb → JmDtIt3S6EfLuRKwlzJrW}/_buildManifest.js +0 -0
  297. /package/web/.next/static/{nFvWyY2BF3AIS3FpZBKEb → JmDtIt3S6EfLuRKwlzJrW}/_clientMiddlewareManifest.json +0 -0
  298. /package/web/.next/static/{nFvWyY2BF3AIS3FpZBKEb → JmDtIt3S6EfLuRKwlzJrW}/_ssgManifest.js +0 -0
@@ -52,15 +52,17 @@ const EVENT_MESSAGES = {
52
52
  export class NotificationWatcherService {
53
53
  runRepository;
54
54
  phaseTimingRepository;
55
+ featureRepository;
55
56
  notificationService;
56
57
  pollIntervalMs;
57
58
  trackedRuns = new Map();
58
59
  intervalId = null;
59
60
  // Suppresses notifications on the first poll to avoid replaying historical state
60
61
  isBootstrapped = false;
61
- constructor(runRepository, phaseTimingRepository, notificationService, pollIntervalMs = DEFAULT_POLL_INTERVAL_MS) {
62
+ constructor(runRepository, phaseTimingRepository, featureRepository, notificationService, pollIntervalMs = DEFAULT_POLL_INTERVAL_MS) {
62
63
  this.runRepository = runRepository;
63
64
  this.phaseTimingRepository = phaseTimingRepository;
65
+ this.featureRepository = featureRepository;
64
66
  this.notificationService = notificationService;
65
67
  this.pollIntervalMs = pollIntervalMs;
66
68
  }
@@ -87,9 +89,8 @@ export class NotificationWatcherService {
87
89
  const runs = await this.runRepository.list();
88
90
  await this.processRuns(runs);
89
91
  }
90
- catch (error) {
91
- // eslint-disable-next-line no-console
92
- console.warn('NotificationWatcherService poll failed:', error);
92
+ catch {
93
+ // DB not ready or query failed — skip this poll cycle
93
94
  }
94
95
  }
95
96
  async processRuns(runs) {
@@ -103,10 +104,11 @@ export class NotificationWatcherService {
103
104
  const prevState = this.trackedRuns.get(run.id);
104
105
  if (!prevState) {
105
106
  // New run — track and emit initial status event
106
- const featureName = this.resolveFeatureName(run);
107
+ const { featureId, featureName } = await this.resolveFeatureIdentity(run);
107
108
  const newState = {
108
109
  status: run.status,
109
110
  completedPhases: new Set(),
111
+ featureId,
110
112
  featureName,
111
113
  };
112
114
  this.trackedRuns.set(run.id, newState);
@@ -151,6 +153,7 @@ export class NotificationWatcherService {
151
153
  const event = {
152
154
  eventType: mapping.eventType,
153
155
  agentRunId: run.id,
156
+ featureId: state.featureId,
154
157
  featureName: state.featureName,
155
158
  message: EVENT_MESSAGES[mapping.eventType] ?? `Agent status: ${run.status}`,
156
159
  severity: mapping.severity,
@@ -167,6 +170,7 @@ export class NotificationWatcherService {
167
170
  const event = {
168
171
  eventType: NotificationEventType.PhaseCompleted,
169
172
  agentRunId: runId,
173
+ featureId: state.featureId,
170
174
  featureName: state.featureName,
171
175
  phaseName: timing.phase,
172
176
  message: `Completed ${timing.phase} phase`,
@@ -178,14 +182,18 @@ export class NotificationWatcherService {
178
182
  }
179
183
  }
180
184
  }
181
- resolveFeatureName(run) {
182
- // Feature name resolution from featureId would require IFeatureRepository.
183
- // For now, use the agentName or a fallback. The NotificationService or
184
- // a higher layer can enrich with the actual feature name if needed.
185
+ async resolveFeatureIdentity(run) {
185
186
  if (run.featureId) {
186
- return `Feature ${run.featureId}`;
187
+ try {
188
+ const feature = await this.featureRepository.findById(run.featureId);
189
+ if (feature)
190
+ return { featureId: feature.id, featureName: feature.name };
191
+ }
192
+ catch {
193
+ // Fall through to fallback
194
+ }
187
195
  }
188
- return `Agent ${run.id}`;
196
+ return { featureId: run.featureId ?? run.id, featureName: `Agent ${run.id}` };
189
197
  }
190
198
  }
191
199
  // --- Singleton accessors (follows getNotificationBus() pattern) ---
@@ -196,11 +204,11 @@ let watcherInstance = null;
196
204
  *
197
205
  * @throws Error if the watcher is already initialized
198
206
  */
199
- export function initializeNotificationWatcher(runRepository, phaseTimingRepository, notificationService, pollIntervalMs) {
207
+ export function initializeNotificationWatcher(runRepository, phaseTimingRepository, featureRepository, notificationService, pollIntervalMs) {
200
208
  if (watcherInstance !== null) {
201
209
  throw new Error('Notification watcher already initialized. Cannot re-initialize.');
202
210
  }
203
- watcherInstance = new NotificationWatcherService(runRepository, phaseTimingRepository, notificationService, pollIntervalMs);
211
+ watcherInstance = new NotificationWatcherService(runRepository, phaseTimingRepository, featureRepository, notificationService, pollIntervalMs);
204
212
  }
205
213
  /**
206
214
  * Get the notification watcher singleton.
@@ -0,0 +1,32 @@
1
+ /**
2
+ * _serve Command (hidden daemon entry point)
3
+ *
4
+ * Runs the web server in-process as a detached daemon child.
5
+ * This command is hidden from --help output and is invoked internally by
6
+ * the startDaemon() helper via child_process.spawn({detached: true}).
7
+ *
8
+ * NOTE: The DI container and settings are already initialized by index.ts
9
+ * bootstrap() before Commander dispatches to this action. This command
10
+ * only needs to start the web server and notification watcher.
11
+ *
12
+ * Lifecycle:
13
+ * 1. Start WebServerService on the provided --port
14
+ * 2. Initialize notification watcher
15
+ * 3. Block until SIGTERM or SIGINT triggers graceful shutdown
16
+ *
17
+ * The shutdown sequence mirrors ui.command.ts:
18
+ * - Set isShuttingDown flag (idempotent — prevents double-shutdown)
19
+ * - Start 5s forceExit timer (unref'd so it doesn't block)
20
+ * - Stop notification watcher
21
+ * - Stop WebServerService
22
+ * - process.exit(0)
23
+ *
24
+ * Usage (internal only):
25
+ * process.execPath _serve --port <N>
26
+ */
27
+ import { Command } from 'commander';
28
+ /**
29
+ * Create the hidden _serve command (daemon child entry point).
30
+ */
31
+ export declare function createServeCommand(): Command;
32
+ //# sourceMappingURL=_serve.command.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"_serve.command.d.ts","sourceRoot":"","sources":["../../../../../src/presentation/cli/commands/_serve.command.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,EAAE,OAAO,EAAwB,MAAM,WAAW,CAAC;AAuB1D;;GAEG;AACH,wBAAgB,kBAAkB,IAAI,OAAO,CAwD5C"}
@@ -0,0 +1,91 @@
1
+ /**
2
+ * _serve Command (hidden daemon entry point)
3
+ *
4
+ * Runs the web server in-process as a detached daemon child.
5
+ * This command is hidden from --help output and is invoked internally by
6
+ * the startDaemon() helper via child_process.spawn({detached: true}).
7
+ *
8
+ * NOTE: The DI container and settings are already initialized by index.ts
9
+ * bootstrap() before Commander dispatches to this action. This command
10
+ * only needs to start the web server and notification watcher.
11
+ *
12
+ * Lifecycle:
13
+ * 1. Start WebServerService on the provided --port
14
+ * 2. Initialize notification watcher
15
+ * 3. Block until SIGTERM or SIGINT triggers graceful shutdown
16
+ *
17
+ * The shutdown sequence mirrors ui.command.ts:
18
+ * - Set isShuttingDown flag (idempotent — prevents double-shutdown)
19
+ * - Start 5s forceExit timer (unref'd so it doesn't block)
20
+ * - Stop notification watcher
21
+ * - Stop WebServerService
22
+ * - process.exit(0)
23
+ *
24
+ * Usage (internal only):
25
+ * process.execPath _serve --port <N>
26
+ */
27
+ import { Command, InvalidArgumentError } from 'commander';
28
+ import { container } from '../../../../packages/core/src/infrastructure/di/container.js';
29
+ import { setVersionEnvVars } from '../../../../packages/core/src/infrastructure/services/version.service.js';
30
+ import { resolveWebDir } from '../../../../packages/core/src/infrastructure/services/web-server.service.js';
31
+ import { initializeNotificationWatcher, getNotificationWatcher, } from '../../../../packages/core/src/infrastructure/services/notifications/notification-watcher.service.js';
32
+ function parsePort(value) {
33
+ const port = parseInt(value, 10);
34
+ if (isNaN(port) || port < 1024 || port > 65535) {
35
+ throw new InvalidArgumentError('Port must be an integer between 1024 and 65535');
36
+ }
37
+ return port;
38
+ }
39
+ /**
40
+ * Create the hidden _serve command (daemon child entry point).
41
+ */
42
+ export function createServeCommand() {
43
+ const cmd = new Command('_serve')
44
+ .description('Start the web server daemon (internal use only)')
45
+ .helpOption(false)
46
+ .addHelpCommand(false)
47
+ .option('-p, --port <number>', 'Port to listen on', parsePort)
48
+ .action(async (options) => {
49
+ try {
50
+ const port = options.port ?? 4050;
51
+ const { dir, dev } = resolveWebDir();
52
+ // Set version env vars for the web UI
53
+ const versionService = container.resolve('IVersionService');
54
+ setVersionEnvVars(versionService.getVersion());
55
+ // Start the web server
56
+ const service = container.resolve('IWebServerService');
57
+ await service.start(port, dir, dev);
58
+ // Start notification watcher
59
+ const runRepo = container.resolve('IAgentRunRepository');
60
+ const phaseTimingRepo = container.resolve('IPhaseTimingRepository');
61
+ const featureRepo = container.resolve('IFeatureRepository');
62
+ const notificationService = container.resolve('INotificationService');
63
+ initializeNotificationWatcher(runRepo, phaseTimingRepo, featureRepo, notificationService);
64
+ getNotificationWatcher().start();
65
+ // Graceful shutdown handler — identical pattern to ui.command.ts
66
+ let isShuttingDown = false;
67
+ const shutdown = async () => {
68
+ if (isShuttingDown)
69
+ return;
70
+ isShuttingDown = true;
71
+ // Force exit after 5s if graceful shutdown stalls
72
+ const forceExit = setTimeout(() => process.exit(0), 5000);
73
+ forceExit.unref();
74
+ getNotificationWatcher().stop();
75
+ await service.stop();
76
+ process.exit(0);
77
+ };
78
+ process.on('SIGTERM', shutdown);
79
+ process.on('SIGINT', shutdown);
80
+ }
81
+ catch (error) {
82
+ const err = error instanceof Error ? error : new Error(String(error));
83
+ // Write to stderr so it appears in daemon logs if redirected
84
+ process.stderr.write(`[_serve] Fatal error: ${err.message}\n`);
85
+ process.exit(1);
86
+ }
87
+ });
88
+ // Mark hidden so Commander omits it from --help output
89
+ cmd._hidden = true;
90
+ return cmd;
91
+ }
@@ -0,0 +1,27 @@
1
+ /**
2
+ * startDaemon() — Shared daemon-spawn helper
3
+ *
4
+ * Contains the parent-side logic for starting the Shep web UI as a
5
+ * detached background daemon. Used by both:
6
+ * - The default `shep` action (index.ts)
7
+ * - The `shep start` command (start.command.ts)
8
+ *
9
+ * Flow:
10
+ * 1. Resolve available port (respects --port override)
11
+ * 2. Check if daemon is already running (idempotent — print URL and return)
12
+ * 3. Spawn the daemon with execArgv propagated (supports tsx in dev mode)
13
+ * {detached: true, stdio: ['ignore','ignore','pipe']} + child.unref()
14
+ * 4. Wait briefly to confirm the child is alive; surface stderr on crash
15
+ * 5. Write daemon.json atomically via IDaemonService
16
+ * 6. Print formatted URL to stdout
17
+ * 7. Open browser via BrowserOpenerService
18
+ */
19
+ export interface StartDaemonOptions {
20
+ port?: number;
21
+ }
22
+ /**
23
+ * Start the Shep web UI as a detached background daemon.
24
+ * Idempotent: if a daemon is already running, prints the existing URL and returns.
25
+ */
26
+ export declare function startDaemon(opts?: StartDaemonOptions): Promise<void>;
27
+ //# sourceMappingURL=start-daemon.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"start-daemon.d.ts","sourceRoot":"","sources":["../../../../../../src/presentation/cli/commands/daemon/start-daemon.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAkBH,MAAM,WAAW,kBAAkB;IACjC,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;GAGG;AACH,wBAAsB,WAAW,CAAC,IAAI,GAAE,kBAAuB,GAAG,OAAO,CAAC,IAAI,CAAC,CA0F9E"}
@@ -0,0 +1,135 @@
1
+ /**
2
+ * startDaemon() — Shared daemon-spawn helper
3
+ *
4
+ * Contains the parent-side logic for starting the Shep web UI as a
5
+ * detached background daemon. Used by both:
6
+ * - The default `shep` action (index.ts)
7
+ * - The `shep start` command (start.command.ts)
8
+ *
9
+ * Flow:
10
+ * 1. Resolve available port (respects --port override)
11
+ * 2. Check if daemon is already running (idempotent — print URL and return)
12
+ * 3. Spawn the daemon with execArgv propagated (supports tsx in dev mode)
13
+ * {detached: true, stdio: ['ignore','ignore','pipe']} + child.unref()
14
+ * 4. Wait briefly to confirm the child is alive; surface stderr on crash
15
+ * 5. Write daemon.json atomically via IDaemonService
16
+ * 6. Print formatted URL to stdout
17
+ * 7. Open browser via BrowserOpenerService
18
+ */
19
+ import { spawn } from 'node:child_process';
20
+ import http from 'node:http';
21
+ import { container } from '../../../../../packages/core/src/infrastructure/di/container.js';
22
+ import { findAvailablePort, DEFAULT_PORT } from '../../../../../packages/core/src/infrastructure/services/port.service.js';
23
+ import { BrowserOpenerService } from '../../../../../packages/core/src/infrastructure/services/browser-opener.service.js';
24
+ import { fmt, messages, spinner } from '../../ui/index.js';
25
+ /** How long to wait (ms) after spawn to verify the child is still alive. */
26
+ const SPAWN_SETTLE_MS = 500;
27
+ /** Max time (ms) to wait for the server to become reachable before opening the browser. */
28
+ const READY_TIMEOUT_MS = 30_000;
29
+ /** Interval (ms) between readiness probes. */
30
+ const READY_POLL_MS = 300;
31
+ /**
32
+ * Start the Shep web UI as a detached background daemon.
33
+ * Idempotent: if a daemon is already running, prints the existing URL and returns.
34
+ */
35
+ export async function startDaemon(opts = {}) {
36
+ const daemonService = container.resolve('IDaemonService');
37
+ // Check for an already-running daemon
38
+ const existing = await daemonService.read();
39
+ if (existing && daemonService.isAlive(existing.pid)) {
40
+ const url = `http://localhost:${existing.port}`;
41
+ messages.newline();
42
+ messages.info(`Shep is already running at ${fmt.code(url)}`);
43
+ messages.newline();
44
+ return;
45
+ }
46
+ // Resolve the port
47
+ const startPort = opts.port ?? DEFAULT_PORT;
48
+ const port = await findAvailablePort(startPort);
49
+ // Spawn the daemon as a detached child process.
50
+ // Propagate process.execArgv so tsx loader hooks (--require / --import) are
51
+ // available in dev mode. In production (compiled JS), execArgv is empty.
52
+ const child = spawn(process.execPath, [...process.execArgv, process.argv[1], '_serve', '--port', String(port)], {
53
+ detached: true,
54
+ // Keep stderr piped so we can surface startup errors; stdin/stdout detached.
55
+ stdio: ['ignore', 'ignore', 'pipe'],
56
+ });
57
+ // Collect stderr in case the child crashes during startup.
58
+ let stderrChunks = [];
59
+ child.stderr.on('data', (chunk) => stderrChunks.push(chunk));
60
+ // Wait briefly for the child to either settle or crash.
61
+ const exitCode = await Promise.race([
62
+ new Promise((resolve) => child.on('exit', (code) => resolve(code))),
63
+ new Promise((resolve) => setTimeout(() => resolve(undefined), SPAWN_SETTLE_MS)),
64
+ ]);
65
+ if (exitCode !== undefined) {
66
+ // Child exited during the settle window — startup failed.
67
+ const stderr = Buffer.concat(stderrChunks).toString().trim();
68
+ messages.newline();
69
+ messages.error(`Daemon failed to start (exit code ${exitCode ?? 'unknown'}).`);
70
+ if (stderr) {
71
+ console.error(stderr);
72
+ }
73
+ messages.newline();
74
+ // Clean up stale daemon.json if it exists
75
+ await daemonService.delete();
76
+ return;
77
+ }
78
+ // Child is alive — detach fully.
79
+ // Unref stderr so the parent event loop is not held open, then unref the child.
80
+ child.stderr.destroy();
81
+ stderrChunks = [];
82
+ child.unref();
83
+ // Write daemon.json atomically
84
+ await daemonService.write({
85
+ pid: child.pid,
86
+ port,
87
+ startedAt: new Date().toISOString(),
88
+ });
89
+ const url = `http://localhost:${port}`;
90
+ messages.newline();
91
+ console.log(fmt.heading('Shep Web UI'));
92
+ messages.newline();
93
+ // Poll until the server responds, with a spinner on stderr.
94
+ // Skip readiness check in E2E / CI environments where the daemon child
95
+ // cannot actually start a Next.js server within the test's time window.
96
+ if (process.env.SHEP_SKIP_READINESS_CHECK) {
97
+ messages.success(`Daemon spawned at ${fmt.code(url)}`);
98
+ }
99
+ else {
100
+ const ready = await spinner('Starting server', () => waitForServer(url, READY_TIMEOUT_MS));
101
+ if (ready) {
102
+ messages.success(`Server ready at ${fmt.code(url)}`);
103
+ }
104
+ else {
105
+ messages.warning(`Server may still be starting at ${fmt.code(url)}`);
106
+ }
107
+ }
108
+ messages.newline();
109
+ const opener = new BrowserOpenerService({ warn: messages.warning });
110
+ opener.open(url);
111
+ }
112
+ /**
113
+ * Poll a URL until it returns any HTTP response (even 500).
114
+ * Resolves true when reachable, false on timeout.
115
+ */
116
+ function waitForServer(url, timeoutMs) {
117
+ const deadline = Date.now() + timeoutMs;
118
+ return new Promise((resolve) => {
119
+ const probe = () => {
120
+ if (Date.now() > deadline)
121
+ return resolve(false);
122
+ const req = http.get(url, () => {
123
+ resolve(true);
124
+ });
125
+ req.on('error', () => {
126
+ setTimeout(probe, READY_POLL_MS);
127
+ });
128
+ req.setTimeout(2000, () => {
129
+ req.destroy();
130
+ setTimeout(probe, READY_POLL_MS);
131
+ });
132
+ };
133
+ probe();
134
+ });
135
+ }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * start Command
3
+ *
4
+ * Starts the Shep web UI as a detached background daemon.
5
+ * All spawn logic lives in the shared startDaemon() helper to avoid
6
+ * duplication between this command and the default `shep` action.
7
+ *
8
+ * Usage: shep start [--port <number>]
9
+ */
10
+ import { Command } from 'commander';
11
+ /**
12
+ * Create the start command.
13
+ */
14
+ export declare function createStartCommand(): Command;
15
+ //# sourceMappingURL=start.command.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"start.command.d.ts","sourceRoot":"","sources":["../../../../../src/presentation/cli/commands/start.command.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,OAAO,EAAwB,MAAM,WAAW,CAAC;AAW1D;;GAEG;AACH,wBAAgB,kBAAkB,IAAI,OAAO,CAc5C"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * start Command
3
+ *
4
+ * Starts the Shep web UI as a detached background daemon.
5
+ * All spawn logic lives in the shared startDaemon() helper to avoid
6
+ * duplication between this command and the default `shep` action.
7
+ *
8
+ * Usage: shep start [--port <number>]
9
+ */
10
+ import { Command, InvalidArgumentError } from 'commander';
11
+ import { startDaemon } from './daemon/start-daemon.js';
12
+ function parsePort(value) {
13
+ const port = parseInt(value, 10);
14
+ if (isNaN(port) || port < 1024 || port > 65535) {
15
+ throw new InvalidArgumentError('Port must be an integer between 1024 and 65535');
16
+ }
17
+ return port;
18
+ }
19
+ /**
20
+ * Create the start command.
21
+ */
22
+ export function createStartCommand() {
23
+ return new Command('start')
24
+ .description('Start the Shep web UI as a background daemon')
25
+ .option('-p, --port <number>', 'Port number (1024-65535)', parsePort)
26
+ .addHelpText('after', `
27
+ Examples:
28
+ $ shep start Start on default port (4050)
29
+ $ shep start --port 8080 Start on custom port`)
30
+ .action(async (options) => {
31
+ await startDaemon({ port: options.port });
32
+ });
33
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * status Command
3
+ *
4
+ * Displays live status and metrics for the running Shep web UI daemon.
5
+ *
6
+ * Metrics collected:
7
+ * - PID, port, URL (from daemon.json)
8
+ * - Uptime (computed from startedAt)
9
+ * - CPU%, RSS memory (from ps shell-out via execFile — injection-safe)
10
+ *
11
+ * Gracefully degrades if ps is unavailable (timeout or error).
12
+ *
13
+ * Usage: shep status
14
+ */
15
+ import { Command } from 'commander';
16
+ /**
17
+ * Create the status command.
18
+ */
19
+ export declare function createStatusCommand(): Command;
20
+ //# sourceMappingURL=status.command.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status.command.d.ts","sourceRoot":"","sources":["../../../../../src/presentation/cli/commands/status.command.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA+EpC;;GAEG;AACH,wBAAgB,mBAAmB,IAAI,OAAO,CA2C7C"}
@@ -0,0 +1,119 @@
1
+ /**
2
+ * status Command
3
+ *
4
+ * Displays live status and metrics for the running Shep web UI daemon.
5
+ *
6
+ * Metrics collected:
7
+ * - PID, port, URL (from daemon.json)
8
+ * - Uptime (computed from startedAt)
9
+ * - CPU%, RSS memory (from ps shell-out via execFile — injection-safe)
10
+ *
11
+ * Gracefully degrades if ps is unavailable (timeout or error).
12
+ *
13
+ * Usage: shep status
14
+ */
15
+ import { Command } from 'commander';
16
+ import { execFile } from 'node:child_process';
17
+ import { container } from '../../../../packages/core/src/infrastructure/di/container.js';
18
+ import { renderDetailView, messages } from '../ui/index.js';
19
+ const PS_TIMEOUT_MS = 2000;
20
+ /**
21
+ * Parse ps output: "pid cpu% rss_kb\n"
22
+ * Returns formatted strings for display.
23
+ */
24
+ function parsePs(output) {
25
+ const parts = output.trim().split(/\s+/);
26
+ const cpu = parts[1] ?? '?';
27
+ const rssKb = parseFloat(parts[2] ?? '0');
28
+ const rssMb = (rssKb / 1024).toFixed(1);
29
+ return { cpu, rssMb };
30
+ }
31
+ /**
32
+ * Human-readable uptime string, e.g. "2h 14m 3s".
33
+ */
34
+ function humanizeUptime(ms) {
35
+ const totalSeconds = Math.max(0, Math.floor(ms / 1000));
36
+ const hours = Math.floor(totalSeconds / 3600);
37
+ const minutes = Math.floor((totalSeconds % 3600) / 60);
38
+ const seconds = totalSeconds % 60;
39
+ const parts = [];
40
+ if (hours > 0)
41
+ parts.push(`${hours}h`);
42
+ if (minutes > 0)
43
+ parts.push(`${minutes}m`);
44
+ parts.push(`${seconds}s`);
45
+ return parts.join(' ');
46
+ }
47
+ /**
48
+ * Shell out to ps for CPU% and RSS, with a timeout guard.
49
+ * Uses execFile (not exec) — the PID is a separate array argument,
50
+ * preventing any possibility of shell injection.
51
+ */
52
+ function fetchPsMetrics(pid) {
53
+ return new Promise((resolve) => {
54
+ const pidStr = String(pid);
55
+ let settled = false;
56
+ const fallback = () => ({ cpu: 'unavailable', rssMb: 'unavailable' });
57
+ const timer = setTimeout(() => {
58
+ if (!settled) {
59
+ settled = true;
60
+ child.kill();
61
+ resolve(fallback());
62
+ }
63
+ }, PS_TIMEOUT_MS);
64
+ const child = execFile('ps', ['-o', 'pid=,pcpu=,rss=', '-p', pidStr], { timeout: PS_TIMEOUT_MS }, (err, stdout) => {
65
+ if (settled)
66
+ return;
67
+ settled = true;
68
+ clearTimeout(timer);
69
+ if (err || !stdout.trim()) {
70
+ resolve(fallback());
71
+ }
72
+ else {
73
+ resolve(parsePs(stdout));
74
+ }
75
+ });
76
+ });
77
+ }
78
+ /**
79
+ * Create the status command.
80
+ */
81
+ export function createStatusCommand() {
82
+ return new Command('status')
83
+ .description('Show the status of the Shep web UI daemon')
84
+ .action(async () => {
85
+ const daemonService = container.resolve('IDaemonService');
86
+ const state = await daemonService.read();
87
+ if (!state || !daemonService.isAlive(state.pid)) {
88
+ messages.newline();
89
+ messages.info('Shep daemon is not running.');
90
+ messages.info('Run `shep start` to launch it.');
91
+ messages.newline();
92
+ return;
93
+ }
94
+ const { pid, port, startedAt } = state;
95
+ const url = `http://localhost:${port}`;
96
+ const uptimeMs = Date.now() - Date.parse(startedAt);
97
+ const uptime = humanizeUptime(uptimeMs);
98
+ const { cpu, rssMb } = await fetchPsMetrics(pid);
99
+ renderDetailView({
100
+ title: 'Shep Daemon Status',
101
+ sections: [
102
+ {
103
+ fields: [
104
+ { label: 'PID', value: String(pid) },
105
+ { label: 'Port', value: String(port) },
106
+ { label: 'URL', value: url },
107
+ { label: 'Started', value: new Date(startedAt).toLocaleString() },
108
+ { label: 'Uptime', value: uptime },
109
+ { label: 'CPU %', value: cpu === 'unavailable' ? 'unavailable' : `${cpu}%` },
110
+ {
111
+ label: 'Memory (RSS)',
112
+ value: rssMb === 'unavailable' ? 'unavailable' : `${rssMb} MB`,
113
+ },
114
+ ],
115
+ },
116
+ ],
117
+ });
118
+ });
119
+ }
@@ -0,0 +1,22 @@
1
+ /**
2
+ * stop Command
3
+ *
4
+ * Stops the running Shep web UI daemon.
5
+ *
6
+ * Stop sequence:
7
+ * 1. Read daemon.json via IDaemonService
8
+ * 2. If no daemon / PID not alive: print clear message and exit 0
9
+ * 3. Validate PID is a positive finite integer
10
+ * 4. Send SIGTERM
11
+ * 5. Poll process liveness every 200ms for up to 5000ms
12
+ * 6. If still alive after 5s, send SIGKILL
13
+ * 7. Always delete daemon.json (in finally block)
14
+ *
15
+ * Usage: shep stop
16
+ */
17
+ import { Command } from 'commander';
18
+ /**
19
+ * Create the stop command.
20
+ */
21
+ export declare function createStopCommand(): Command;
22
+ //# sourceMappingURL=stop.command.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stop.command.d.ts","sourceRoot":"","sources":["../../../../../src/presentation/cli/commands/stop.command.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA4BpC;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,OAAO,CA+C3C"}