@juspay/shooter 1.4.0 → 1.6.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 (480) hide show
  1. package/.claude/hooks/notifier.cjs +15 -1
  2. package/README.md +132 -36
  3. package/bin/shooter.cjs +98 -29
  4. package/build/client/_app/immutable/assets/2.Dk9NfqnS.css +1 -0
  5. package/build/client/_app/immutable/assets/2.Dk9NfqnS.css.br +0 -0
  6. package/build/client/_app/immutable/assets/2.Dk9NfqnS.css.gz +0 -0
  7. package/build/client/_app/immutable/assets/3.DHxQoulp.css +1 -0
  8. package/build/client/_app/immutable/assets/3.DHxQoulp.css.br +0 -0
  9. package/build/client/_app/immutable/assets/3.DHxQoulp.css.gz +0 -0
  10. package/build/client/_app/immutable/assets/{3.DGDHCVnW.css → 4.D5l1JxgO.css} +1 -1
  11. package/build/client/_app/immutable/assets/4.D5l1JxgO.css.br +0 -0
  12. package/build/client/_app/immutable/assets/4.D5l1JxgO.css.gz +0 -0
  13. package/build/client/_app/immutable/assets/5.C5qz-NeI.css +1 -0
  14. package/build/client/_app/immutable/assets/5.C5qz-NeI.css.br +0 -0
  15. package/build/client/_app/immutable/assets/5.C5qz-NeI.css.gz +0 -0
  16. package/build/client/_app/immutable/chunks/1mEchsPO.js +1 -0
  17. package/build/client/_app/immutable/chunks/1mEchsPO.js.br +0 -0
  18. package/build/client/_app/immutable/chunks/1mEchsPO.js.gz +0 -0
  19. package/build/client/_app/immutable/chunks/B7X-vhXI.js +1 -0
  20. package/build/client/_app/immutable/chunks/B7X-vhXI.js.br +0 -0
  21. package/build/client/_app/immutable/chunks/B7X-vhXI.js.gz +0 -0
  22. package/build/client/_app/immutable/chunks/BRkqKgVG.js +1 -0
  23. package/build/client/_app/immutable/chunks/BRkqKgVG.js.br +0 -0
  24. package/build/client/_app/immutable/chunks/BRkqKgVG.js.gz +0 -0
  25. package/build/client/_app/immutable/chunks/BfJ-f-Tu.js +1 -0
  26. package/build/client/_app/immutable/chunks/BfJ-f-Tu.js.br +2 -0
  27. package/build/client/_app/immutable/chunks/BfJ-f-Tu.js.gz +0 -0
  28. package/build/client/_app/immutable/chunks/CGMJxf7r.js +1 -0
  29. package/build/client/_app/immutable/chunks/CGMJxf7r.js.br +0 -0
  30. package/build/client/_app/immutable/chunks/CGMJxf7r.js.gz +0 -0
  31. package/build/client/_app/immutable/chunks/{CZHsSL_X.js → CJFjKwJ7.js} +1 -1
  32. package/build/client/_app/immutable/chunks/CJFjKwJ7.js.br +0 -0
  33. package/build/client/_app/immutable/chunks/CJFjKwJ7.js.gz +0 -0
  34. package/build/client/_app/immutable/chunks/CNH2HlKj.js +20 -0
  35. package/build/client/_app/immutable/chunks/CNH2HlKj.js.br +0 -0
  36. package/build/client/_app/immutable/chunks/CNH2HlKj.js.gz +0 -0
  37. package/build/client/_app/immutable/chunks/CR6bkGJW.js +6 -0
  38. package/build/client/_app/immutable/chunks/CR6bkGJW.js.br +0 -0
  39. package/build/client/_app/immutable/chunks/CR6bkGJW.js.gz +0 -0
  40. package/build/client/_app/immutable/chunks/CVtJ6yRM.js +1 -0
  41. package/build/client/_app/immutable/chunks/CVtJ6yRM.js.br +0 -0
  42. package/build/client/_app/immutable/chunks/CVtJ6yRM.js.gz +0 -0
  43. package/build/client/_app/immutable/chunks/{CSoRdFvv.js → CaiJSUi3.js} +1 -1
  44. package/build/client/_app/immutable/chunks/CaiJSUi3.js.br +0 -0
  45. package/build/client/_app/immutable/chunks/CaiJSUi3.js.gz +0 -0
  46. package/build/client/_app/immutable/chunks/{DjsDGxCa.js → CmczWE_d.js} +4 -4
  47. package/build/client/_app/immutable/chunks/CmczWE_d.js.br +0 -0
  48. package/build/client/_app/immutable/chunks/CmczWE_d.js.gz +0 -0
  49. package/build/client/_app/immutable/chunks/{CDVSripB.js → CqfYvnci.js} +1 -1
  50. package/build/client/_app/immutable/chunks/CqfYvnci.js.br +0 -0
  51. package/build/client/_app/immutable/chunks/CqfYvnci.js.gz +0 -0
  52. package/build/client/_app/immutable/chunks/CsgHjHGZ.js +1 -0
  53. package/build/client/_app/immutable/chunks/CsgHjHGZ.js.br +0 -0
  54. package/build/client/_app/immutable/chunks/CsgHjHGZ.js.gz +0 -0
  55. package/build/client/_app/immutable/chunks/{UJOiqIYE.js → CtrCjGZT.js} +1 -1
  56. package/build/client/_app/immutable/chunks/CtrCjGZT.js.br +0 -0
  57. package/build/client/_app/immutable/chunks/CtrCjGZT.js.gz +0 -0
  58. package/build/client/_app/immutable/chunks/DDiOVAd8.js +61 -0
  59. package/build/client/_app/immutable/chunks/DDiOVAd8.js.br +0 -0
  60. package/build/client/_app/immutable/chunks/DDiOVAd8.js.gz +0 -0
  61. package/build/client/_app/immutable/chunks/DVl0sebP.js +2 -0
  62. package/build/client/_app/immutable/chunks/DVl0sebP.js.br +0 -0
  63. package/build/client/_app/immutable/chunks/DVl0sebP.js.gz +0 -0
  64. package/build/client/_app/immutable/chunks/EhLZwqfu.js +3 -0
  65. package/build/client/_app/immutable/chunks/EhLZwqfu.js.br +0 -0
  66. package/build/client/_app/immutable/chunks/EhLZwqfu.js.gz +0 -0
  67. package/build/client/_app/immutable/chunks/gQJcRhou.js +1 -0
  68. package/build/client/_app/immutable/chunks/gQJcRhou.js.br +0 -0
  69. package/build/client/_app/immutable/chunks/gQJcRhou.js.gz +0 -0
  70. package/build/client/_app/immutable/chunks/pRcLbE0d.js +1 -0
  71. package/build/client/_app/immutable/chunks/pRcLbE0d.js.br +0 -0
  72. package/build/client/_app/immutable/chunks/pRcLbE0d.js.gz +0 -0
  73. package/build/client/_app/immutable/chunks/{CF4lQ45j.js → y_g-KC7l.js} +1 -1
  74. package/build/client/_app/immutable/chunks/y_g-KC7l.js.br +0 -0
  75. package/build/client/_app/immutable/chunks/y_g-KC7l.js.gz +0 -0
  76. package/build/client/_app/immutable/entry/app.zJvbFXsj.js +2 -0
  77. package/build/client/_app/immutable/entry/app.zJvbFXsj.js.br +0 -0
  78. package/build/client/_app/immutable/entry/app.zJvbFXsj.js.gz +0 -0
  79. package/build/client/_app/immutable/entry/start.B2Jf5iFd.js +1 -0
  80. package/build/client/_app/immutable/entry/start.B2Jf5iFd.js.br +2 -0
  81. package/build/client/_app/immutable/entry/start.B2Jf5iFd.js.gz +0 -0
  82. package/build/client/_app/immutable/nodes/0.B_E4j3MX.js +1 -0
  83. package/build/client/_app/immutable/nodes/0.B_E4j3MX.js.br +0 -0
  84. package/build/client/_app/immutable/nodes/0.B_E4j3MX.js.gz +0 -0
  85. package/build/client/_app/immutable/nodes/1.B0oFqb8X.js +1 -0
  86. package/build/client/_app/immutable/nodes/1.B0oFqb8X.js.br +0 -0
  87. package/build/client/_app/immutable/nodes/1.B0oFqb8X.js.gz +0 -0
  88. package/build/client/_app/immutable/nodes/2.Bqul0XyM.js +13 -0
  89. package/build/client/_app/immutable/nodes/2.Bqul0XyM.js.br +0 -0
  90. package/build/client/_app/immutable/nodes/2.Bqul0XyM.js.gz +0 -0
  91. package/build/client/_app/immutable/nodes/3.CTqUQKSN.js +9 -0
  92. package/build/client/_app/immutable/nodes/3.CTqUQKSN.js.br +0 -0
  93. package/build/client/_app/immutable/nodes/3.CTqUQKSN.js.gz +0 -0
  94. package/build/client/_app/immutable/nodes/4.B_pbOZoD.js +4 -0
  95. package/build/client/_app/immutable/nodes/4.B_pbOZoD.js.br +0 -0
  96. package/build/client/_app/immutable/nodes/4.B_pbOZoD.js.gz +0 -0
  97. package/build/client/_app/immutable/nodes/5.CdLPNo5-.js +1 -0
  98. package/build/client/_app/immutable/nodes/5.CdLPNo5-.js.br +0 -0
  99. package/build/client/_app/immutable/nodes/5.CdLPNo5-.js.gz +0 -0
  100. package/build/client/_app/immutable/nodes/6.BvjUfHnH.js +1 -0
  101. package/build/client/_app/immutable/nodes/6.BvjUfHnH.js.br +0 -0
  102. package/build/client/_app/immutable/nodes/6.BvjUfHnH.js.gz +0 -0
  103. package/build/client/_app/immutable/nodes/{5.g3R-QfIW.js → 7.5K7Od8ba.js} +3 -3
  104. package/build/client/_app/immutable/nodes/7.5K7Od8ba.js.br +0 -0
  105. package/build/client/_app/immutable/nodes/7.5K7Od8ba.js.gz +0 -0
  106. package/build/client/_app/immutable/nodes/8.Bs1DrW0_.js +2 -0
  107. package/build/client/_app/immutable/nodes/8.Bs1DrW0_.js.br +0 -0
  108. package/build/client/_app/immutable/nodes/8.Bs1DrW0_.js.gz +0 -0
  109. package/build/client/_app/immutable/nodes/9.1fMlGdqv.js +2 -0
  110. package/build/client/_app/immutable/nodes/9.1fMlGdqv.js.br +0 -0
  111. package/build/client/_app/immutable/nodes/9.1fMlGdqv.js.gz +0 -0
  112. package/build/client/_app/version.json +1 -1
  113. package/build/client/_app/version.json.br +0 -0
  114. package/build/client/_app/version.json.gz +0 -0
  115. package/build/server/chunks/0-e1fgD9Mi.js +23 -0
  116. package/build/server/chunks/0-e1fgD9Mi.js.map +1 -0
  117. package/build/server/chunks/1-D2_XVthm.js +9 -0
  118. package/build/server/chunks/{1-BV7u1xGo.js.map → 1-D2_XVthm.js.map} +1 -1
  119. package/build/server/chunks/2-DADX86JZ.js +21 -0
  120. package/build/server/chunks/2-DADX86JZ.js.map +1 -0
  121. package/build/server/chunks/3-CHacdiCg.js +21 -0
  122. package/build/server/chunks/3-CHacdiCg.js.map +1 -0
  123. package/build/server/chunks/4-0UE_6Ep-.js +23 -0
  124. package/build/server/chunks/4-0UE_6Ep-.js.map +1 -0
  125. package/build/server/chunks/5-BBIP1PzX.js +24 -0
  126. package/build/server/chunks/5-BBIP1PzX.js.map +1 -0
  127. package/build/server/chunks/6-CWLNQu6F.js +9 -0
  128. package/build/server/chunks/6-CWLNQu6F.js.map +1 -0
  129. package/build/server/chunks/7-DmQ3B8uy.js +9 -0
  130. package/build/server/chunks/7-DmQ3B8uy.js.map +1 -0
  131. package/build/server/chunks/8-CnFVjQtZ.js +9 -0
  132. package/build/server/chunks/8-CnFVjQtZ.js.map +1 -0
  133. package/build/server/chunks/9-Dw7-P6aF.js +9 -0
  134. package/build/server/chunks/9-Dw7-P6aF.js.map +1 -0
  135. package/build/server/chunks/{Button-Cs1aE6ka.js → Button-WKgiLWZI.js} +4 -9
  136. package/build/server/chunks/Button-WKgiLWZI.js.map +1 -0
  137. package/build/server/chunks/{EmptyState-DDFH1K8g.js → EmptyState-BUBqASsp.js} +3 -3
  138. package/build/server/chunks/{EmptyState-DDFH1K8g.js.map → EmptyState-BUBqASsp.js.map} +1 -1
  139. package/build/server/chunks/{Icon-CEUrotA6.js → Icon-BNBAg85a.js} +3 -3
  140. package/build/server/chunks/Icon-BNBAg85a.js.map +1 -0
  141. package/build/server/chunks/{Shimmer-DB8W1zt6.js → Shimmer-C4uBVwxz.js} +2 -2
  142. package/build/server/chunks/{Shimmer-DB8W1zt6.js.map → Shimmer-C4uBVwxz.js.map} +1 -1
  143. package/build/server/chunks/{_error.svelte-uCOJNxvr.js → _error.svelte-DkIwmECt.js} +5 -5
  144. package/build/server/chunks/{_error.svelte-uCOJNxvr.js.map → _error.svelte-DkIwmECt.js.map} +1 -1
  145. package/build/server/chunks/{_layout.svelte-CtWmEJwe.js → _layout.svelte-DllETxmJ.js} +13 -7
  146. package/build/server/chunks/_layout.svelte-DllETxmJ.js.map +1 -0
  147. package/build/server/chunks/_page.svelte-BZSdLKE_.js +118 -0
  148. package/build/server/chunks/_page.svelte-BZSdLKE_.js.map +1 -0
  149. package/build/server/chunks/{_page.svelte-CxWcQ0Am.js → _page.svelte-Cmuco1mC.js} +84 -199
  150. package/build/server/chunks/_page.svelte-Cmuco1mC.js.map +1 -0
  151. package/build/server/chunks/{_page.svelte-BgevQjq1.js → _page.svelte-Co5sF7W-.js} +12 -11
  152. package/build/server/chunks/{_page.svelte-BgevQjq1.js.map → _page.svelte-Co5sF7W-.js.map} +1 -1
  153. package/build/server/chunks/{_page.svelte-DO4oa_LY.js → _page.svelte-CpL3R-VI.js} +8 -8
  154. package/build/server/chunks/{_page.svelte-DO4oa_LY.js.map → _page.svelte-CpL3R-VI.js.map} +1 -1
  155. package/build/server/chunks/_page.svelte-DDSzYLUs.js +137 -0
  156. package/build/server/chunks/_page.svelte-DDSzYLUs.js.map +1 -0
  157. package/build/server/chunks/_page.svelte-JIkgFUFf.js +26 -0
  158. package/build/server/chunks/_page.svelte-JIkgFUFf.js.map +1 -0
  159. package/build/server/chunks/{_page.svelte-CVq6tRb3.js → _page.svelte-Y9-O5a5w.js} +10 -9
  160. package/build/server/chunks/_page.svelte-Y9-O5a5w.js.map +1 -0
  161. package/build/server/chunks/{_page.svelte-BcZaKdX9.js → _page.svelte-fcX09N4d.js} +9 -9
  162. package/build/server/chunks/{_page.svelte-BcZaKdX9.js.map → _page.svelte-fcX09N4d.js.map} +1 -1
  163. package/build/server/chunks/{_server.ts-DYpJImqd.js → _server.ts-0Xr2fWaq.js} +9 -5
  164. package/build/server/chunks/_server.ts-0Xr2fWaq.js.map +1 -0
  165. package/build/server/chunks/{_server.ts-CTpcLUH8.js → _server.ts-2ixC-X3K.js} +20 -5
  166. package/build/server/chunks/_server.ts-2ixC-X3K.js.map +1 -0
  167. package/build/server/chunks/{_server.ts-CAxsWKvS.js → _server.ts-40c_epk8.js} +20 -4
  168. package/build/server/chunks/_server.ts-40c_epk8.js.map +1 -0
  169. package/build/server/chunks/{_server.ts-WhTJBEJy.js → _server.ts-A9_tRR-K.js} +5 -4
  170. package/build/server/chunks/{_server.ts-WhTJBEJy.js.map → _server.ts-A9_tRR-K.js.map} +1 -1
  171. package/build/server/chunks/_server.ts-BRAzC6W1.js +98 -0
  172. package/build/server/chunks/_server.ts-BRAzC6W1.js.map +1 -0
  173. package/build/server/chunks/{_server.ts-DB_Kg97c.js → _server.ts-BScvgttw.js} +24 -4
  174. package/build/server/chunks/_server.ts-BScvgttw.js.map +1 -0
  175. package/build/server/chunks/{_server.ts-Ch-6iOHp.js → _server.ts-B__YN2kX.js} +121 -87
  176. package/build/server/chunks/_server.ts-B__YN2kX.js.map +1 -0
  177. package/build/server/chunks/{_server.ts-tSpgyl1D.js → _server.ts-Bjbr7glm.js} +4 -3
  178. package/build/server/chunks/_server.ts-Bjbr7glm.js.map +1 -0
  179. package/build/server/chunks/{_server.ts-COu0vNpd.js → _server.ts-BrqaMMAa.js} +7 -6
  180. package/build/server/chunks/_server.ts-BrqaMMAa.js.map +1 -0
  181. package/build/server/chunks/{_server.ts-vekTmWAx.js → _server.ts-BuYyCrnF.js} +6 -4
  182. package/build/server/chunks/_server.ts-BuYyCrnF.js.map +1 -0
  183. package/build/server/chunks/{_server.ts-Deok2y88.js → _server.ts-ByIrRtCx.js} +132 -76
  184. package/build/server/chunks/_server.ts-ByIrRtCx.js.map +1 -0
  185. package/build/server/chunks/{_server.ts-DYvb9ijZ.js → _server.ts-ByPExYfO.js} +4 -3
  186. package/build/server/chunks/{_server.ts-DYvb9ijZ.js.map → _server.ts-ByPExYfO.js.map} +1 -1
  187. package/build/server/chunks/_server.ts-CjpQ10xh.js +123 -0
  188. package/build/server/chunks/_server.ts-CjpQ10xh.js.map +1 -0
  189. package/build/server/chunks/_server.ts-CyjDrcZN.js +21 -0
  190. package/build/server/chunks/_server.ts-CyjDrcZN.js.map +1 -0
  191. package/build/server/chunks/{_server.ts-DV8zTCF9.js → _server.ts-DOGUMzPx.js} +4 -3
  192. package/build/server/chunks/{_server.ts-DV8zTCF9.js.map → _server.ts-DOGUMzPx.js.map} +1 -1
  193. package/build/server/chunks/_server.ts-DZvfyuNj.js +15 -0
  194. package/build/server/chunks/_server.ts-DZvfyuNj.js.map +1 -0
  195. package/build/server/chunks/{_server.ts-XzT2UHM1.js → _server.ts-DkPPTUPo.js} +4 -3
  196. package/build/server/chunks/{_server.ts-XzT2UHM1.js.map → _server.ts-DkPPTUPo.js.map} +1 -1
  197. package/build/server/chunks/{auth-DeCdZ83n.js → auth-DuunT7Cg.js} +2 -2
  198. package/build/server/chunks/{auth-DeCdZ83n.js.map → auth-DuunT7Cg.js.map} +1 -1
  199. package/build/server/chunks/{client-BdGHe_hY.js → client-DRtPDkMh.js} +4 -4
  200. package/build/server/chunks/{client-BdGHe_hY.js.map → client-DRtPDkMh.js.map} +1 -1
  201. package/build/server/chunks/client2-bqqmu0b7.js +7 -0
  202. package/build/server/chunks/{client2-CCBGA-2V.js.map → client2-bqqmu0b7.js.map} +1 -1
  203. package/build/server/chunks/close-BGlLztTb.js +192 -0
  204. package/build/server/chunks/close-BGlLztTb.js.map +1 -0
  205. package/build/server/chunks/events-handler-Dm1mNPQP.js +20 -0
  206. package/build/server/chunks/events-handler-Dm1mNPQP.js.map +1 -0
  207. package/build/server/chunks/html-FW6Ia4bL.js +8 -0
  208. package/build/server/chunks/html-FW6Ia4bL.js.map +1 -0
  209. package/build/server/chunks/{shared-server-sSGG17Df.js → index-CoD1IJuy.js} +2 -11
  210. package/build/server/chunks/index-CoD1IJuy.js.map +1 -0
  211. package/build/server/chunks/{index-DwaY1cAm.js → index-DP9bWJrR.js} +2 -2
  212. package/build/server/chunks/{index-DwaY1cAm.js.map → index-DP9bWJrR.js.map} +1 -1
  213. package/build/server/chunks/{index-server-CrDaL06Y.js → index-server-BUmV4MIG.js} +2 -2
  214. package/build/server/chunks/index-server-BUmV4MIG.js.map +1 -0
  215. package/build/server/chunks/index-server2-BJrT0wnA.js +5 -0
  216. package/build/server/chunks/index-server2-BJrT0wnA.js.map +1 -0
  217. package/build/server/chunks/{index2-CgclKpUj.js → index2-D5Y19GKR.js} +2 -2
  218. package/build/server/chunks/index2-D5Y19GKR.js.map +1 -0
  219. package/build/server/chunks/{library-apns-BqJbvSKh.js → library-apns-Cf-E-DhM.js} +5 -2
  220. package/build/server/chunks/library-apns-Cf-E-DhM.js.map +1 -0
  221. package/build/server/chunks/providers-DtstoHQ0.js +17 -0
  222. package/build/server/chunks/providers-DtstoHQ0.js.map +1 -0
  223. package/build/server/chunks/{pty-manager-BQVB7IVj.js → pty-manager-TyMUpDA9.js} +41 -9
  224. package/build/server/chunks/pty-manager-TyMUpDA9.js.map +1 -0
  225. package/build/server/chunks/{root-DDSnEAZv.js → root-CATOR_0t.js} +2 -2
  226. package/build/server/chunks/root-CATOR_0t.js.map +1 -0
  227. package/build/server/chunks/shared-server-DaWdgxVh.js +11 -0
  228. package/build/server/chunks/shared-server-DaWdgxVh.js.map +1 -0
  229. package/build/server/chunks/{state.svelte-hBbXlUak.js → state.svelte-CftllyvC.js} +3 -3
  230. package/build/server/chunks/state.svelte-CftllyvC.js.map +1 -0
  231. package/build/server/chunks/{stores-DHNzYNpX.js → stores-BjL57aOK.js} +4 -4
  232. package/build/server/chunks/{stores-DHNzYNpX.js.map → stores-BjL57aOK.js.map} +1 -1
  233. package/build/server/index.js +173 -6
  234. package/build/server/index.js.map +1 -1
  235. package/build/server/manifest.js +53 -30
  236. package/build/server/manifest.js.map +1 -1
  237. package/package.json +28 -7
  238. package/scripts/dev.mjs +361 -0
  239. package/scripts/install.sh +133 -73
  240. package/scripts/{fix-generated-types.sh → postgen-types.sh} +2 -2
  241. package/scripts/setup.cjs +440 -244
  242. package/scripts/vercel-env-commands.sh +3 -3
  243. package/server.ts +3 -3
  244. package/src/app.html +163 -0
  245. package/src/lib/modules/client/activity/ActivityFeed.svelte +279 -0
  246. package/src/lib/modules/client/activity/index.ts +8 -0
  247. package/src/lib/modules/client/activity/store.svelte.ts +478 -0
  248. package/src/lib/modules/client/activity/summarizer.ts +224 -0
  249. package/src/lib/modules/client/common/Card.svelte +2 -9
  250. package/src/lib/modules/client/common/EmptyState.svelte +2 -20
  251. package/src/lib/modules/client/common/Icon.svelte +3 -7
  252. package/src/lib/modules/client/common/StatusBadge.svelte +2 -4
  253. package/src/lib/modules/client/common/config-guard.ts +22 -2
  254. package/src/lib/modules/client/common/index.ts +1 -1
  255. package/src/lib/modules/client/common/time.ts +27 -9
  256. package/src/lib/modules/client/dashboard/DashboardCard.svelte +374 -0
  257. package/src/lib/modules/client/dashboard/DashboardView.svelte +66 -0
  258. package/src/lib/modules/client/dashboard/index.ts +12 -0
  259. package/src/lib/modules/client/dashboard/store.svelte.ts +663 -0
  260. package/src/lib/modules/client/dashboard/summarizer.ts +205 -0
  261. package/src/lib/modules/client/neurolink/fetch-proxy.ts +70 -0
  262. package/src/lib/modules/client/neurolink/provider-config.ts +111 -0
  263. package/src/lib/modules/client/terminal/ChatView.svelte +46 -43
  264. package/src/lib/modules/client/terminal/CommandPalette.svelte +3 -12
  265. package/src/lib/modules/client/terminal/ConnectionStatus.svelte +3 -6
  266. package/src/lib/modules/client/terminal/LaunchSheet.svelte +10 -21
  267. package/src/lib/modules/client/terminal/QuickKeys.svelte +4 -11
  268. package/src/lib/modules/client/terminal/ShortcutsHelp.svelte +3 -6
  269. package/src/lib/modules/client/terminal/keyboard-shortcuts.ts +5 -7
  270. package/src/lib/modules/client/terminal/xterm-wrapper.ts +27 -47
  271. package/src/lib/modules/server/apn/library-apns.ts +6 -3
  272. package/src/lib/modules/server/apn/notification-history.ts +2 -2
  273. package/src/lib/modules/server/apn/notification-sessions.ts +1 -3
  274. package/src/lib/modules/server/apn/pending-requests.ts +1 -1
  275. package/src/lib/modules/server/apn/types.ts +2 -52
  276. package/src/lib/modules/server/cli/index.ts +1 -30
  277. package/src/lib/modules/server/cli/runner.ts +7 -15
  278. package/src/lib/modules/server/fcm/fcm-service.ts +2 -2
  279. package/src/lib/modules/server/sessions/jsonl-parser.ts +97 -42
  280. package/src/lib/modules/server/sessions/jsonl-reader.ts +69 -60
  281. package/src/lib/modules/server/sessions/opencode-reader.ts +1 -1
  282. package/src/lib/modules/server/sessions/process-detector.ts +72 -31
  283. package/src/lib/modules/server/sessions/types.ts +2 -42
  284. package/src/lib/modules/server/terminal/holder-client.ts +11 -35
  285. package/src/lib/modules/server/terminal/opencode-watcher.ts +16 -24
  286. package/src/lib/modules/server/terminal/pty-manager.ts +40 -45
  287. package/src/lib/modules/server/terminal/session-watcher.ts +15 -17
  288. package/src/lib/modules/server/terminal/terminal-store.ts +1 -1
  289. package/src/lib/modules/server/ws/events-handler.ts +1 -16
  290. package/src/lib/modules/server/ws/keepalive.ts +1 -5
  291. package/src/lib/modules/server/ws/server.ts +1 -1
  292. package/src/lib/modules/server/ws/session-handler.ts +20 -86
  293. package/src/lib/modules/server/ws/terminal-handler.ts +28 -51
  294. package/src/lib/modules/server/ws/ticket-store.ts +1 -1
  295. package/src/lib/modules/shared/providers.ts +21 -0
  296. package/src/lib/types/activity.ts +18 -0
  297. package/src/lib/types/apn.ts +43 -0
  298. package/src/lib/types/cli.ts +39 -0
  299. package/src/lib/types/common.ts +39 -0
  300. package/src/lib/types/dashboard.ts +4 -0
  301. package/src/lib/types/generated/Client.ts +1656 -0
  302. package/src/{generated/types → lib/types/generated}/WsProtocol.ts +344 -2
  303. package/src/lib/types/index.ts +28 -0
  304. package/src/lib/types/neurolink.ts +4 -0
  305. package/src/lib/types/server.ts +93 -0
  306. package/src/lib/types/sessions.ts +59 -0
  307. package/src/lib/types/terminal-client.ts +132 -0
  308. package/src/lib/types/ws.ts +161 -0
  309. package/src/routes/+error.svelte +7 -2
  310. package/src/routes/+layout.server.ts +9 -0
  311. package/src/routes/+layout.svelte +36 -7
  312. package/src/routes/+page.server.ts +7 -0
  313. package/src/routes/+page.svelte +85 -35
  314. package/src/routes/activity/+page.server.ts +7 -0
  315. package/src/routes/activity/+page.svelte +58 -0
  316. package/src/routes/api/health/+server.ts +32 -19
  317. package/src/routes/api/neurolink-proxy/+server.ts +136 -0
  318. package/src/routes/api/notify/+server.ts +159 -84
  319. package/src/routes/api/sessions/+server.ts +1 -1
  320. package/src/routes/api/sessions/connect/+server.ts +10 -6
  321. package/src/routes/api/terminals/+server.ts +20 -1
  322. package/src/routes/api/terminals/[id]/+server.ts +16 -2
  323. package/src/routes/api/webhook/+server.ts +5 -33
  324. package/src/routes/api/ws-ticket/+server.ts +4 -4
  325. package/src/routes/config/+page.server.ts +9 -0
  326. package/src/routes/config/+page.svelte +118 -25
  327. package/src/routes/neurolink/+page.server.ts +10 -0
  328. package/src/routes/neurolink/+page.svelte +331 -0
  329. package/src/routes/project/+page.svelte +17 -12
  330. package/src/routes/session/[id]/+page.svelte +146 -62
  331. package/src/routes/terminals/+page.svelte +2 -2
  332. package/src/routes/terminals/[id]/+page.svelte +99 -88
  333. package/svelte.config.js +1 -3
  334. package/tsconfig.json +1 -0
  335. package/build/client/_app/immutable/assets/2.CAShZ7lQ.css +0 -1
  336. package/build/client/_app/immutable/assets/2.CAShZ7lQ.css.br +0 -1
  337. package/build/client/_app/immutable/assets/2.CAShZ7lQ.css.gz +0 -0
  338. package/build/client/_app/immutable/assets/3.DGDHCVnW.css.br +0 -0
  339. package/build/client/_app/immutable/assets/3.DGDHCVnW.css.gz +0 -0
  340. package/build/client/_app/immutable/chunks/B5NAKyil.js +0 -20
  341. package/build/client/_app/immutable/chunks/B5NAKyil.js.br +0 -0
  342. package/build/client/_app/immutable/chunks/B5NAKyil.js.gz +0 -0
  343. package/build/client/_app/immutable/chunks/B8XegpSE.js +0 -1
  344. package/build/client/_app/immutable/chunks/B8XegpSE.js.br +0 -0
  345. package/build/client/_app/immutable/chunks/B8XegpSE.js.gz +0 -0
  346. package/build/client/_app/immutable/chunks/B8zoBsv3.js +0 -6
  347. package/build/client/_app/immutable/chunks/B8zoBsv3.js.br +0 -0
  348. package/build/client/_app/immutable/chunks/B8zoBsv3.js.gz +0 -0
  349. package/build/client/_app/immutable/chunks/BN1NjBrw.js +0 -1
  350. package/build/client/_app/immutable/chunks/BN1NjBrw.js.br +0 -0
  351. package/build/client/_app/immutable/chunks/BN1NjBrw.js.gz +0 -0
  352. package/build/client/_app/immutable/chunks/BOYo8yTr.js +0 -1
  353. package/build/client/_app/immutable/chunks/BOYo8yTr.js.br +0 -0
  354. package/build/client/_app/immutable/chunks/BOYo8yTr.js.gz +0 -0
  355. package/build/client/_app/immutable/chunks/Bu1aqm5j.js +0 -1
  356. package/build/client/_app/immutable/chunks/Bu1aqm5j.js.br +0 -0
  357. package/build/client/_app/immutable/chunks/Bu1aqm5j.js.gz +0 -0
  358. package/build/client/_app/immutable/chunks/CDVSripB.js.br +0 -0
  359. package/build/client/_app/immutable/chunks/CDVSripB.js.gz +0 -0
  360. package/build/client/_app/immutable/chunks/CF4lQ45j.js.br +0 -0
  361. package/build/client/_app/immutable/chunks/CF4lQ45j.js.gz +0 -0
  362. package/build/client/_app/immutable/chunks/CQjSATpv.js +0 -61
  363. package/build/client/_app/immutable/chunks/CQjSATpv.js.br +0 -0
  364. package/build/client/_app/immutable/chunks/CQjSATpv.js.gz +0 -0
  365. package/build/client/_app/immutable/chunks/CSoRdFvv.js.br +0 -0
  366. package/build/client/_app/immutable/chunks/CSoRdFvv.js.gz +0 -0
  367. package/build/client/_app/immutable/chunks/CZHsSL_X.js.br +0 -0
  368. package/build/client/_app/immutable/chunks/CZHsSL_X.js.gz +0 -0
  369. package/build/client/_app/immutable/chunks/DSU1n5N_.js +0 -1
  370. package/build/client/_app/immutable/chunks/DSU1n5N_.js.br +0 -0
  371. package/build/client/_app/immutable/chunks/DSU1n5N_.js.gz +0 -0
  372. package/build/client/_app/immutable/chunks/DVkn4r72.js +0 -1
  373. package/build/client/_app/immutable/chunks/DVkn4r72.js.br +0 -0
  374. package/build/client/_app/immutable/chunks/DVkn4r72.js.gz +0 -0
  375. package/build/client/_app/immutable/chunks/DjsDGxCa.js.br +0 -0
  376. package/build/client/_app/immutable/chunks/DjsDGxCa.js.gz +0 -0
  377. package/build/client/_app/immutable/chunks/UJOiqIYE.js.br +0 -0
  378. package/build/client/_app/immutable/chunks/UJOiqIYE.js.gz +0 -0
  379. package/build/client/_app/immutable/chunks/r0JawsZc.js +0 -2
  380. package/build/client/_app/immutable/chunks/r0JawsZc.js.br +0 -0
  381. package/build/client/_app/immutable/chunks/r0JawsZc.js.gz +0 -0
  382. package/build/client/_app/immutable/entry/app.DwWiuoEC.js +0 -2
  383. package/build/client/_app/immutable/entry/app.DwWiuoEC.js.br +0 -0
  384. package/build/client/_app/immutable/entry/app.DwWiuoEC.js.gz +0 -0
  385. package/build/client/_app/immutable/entry/start.DG8BMhrh.js +0 -1
  386. package/build/client/_app/immutable/entry/start.DG8BMhrh.js.br +0 -0
  387. package/build/client/_app/immutable/entry/start.DG8BMhrh.js.gz +0 -0
  388. package/build/client/_app/immutable/nodes/0.ejabgzDQ.js +0 -1
  389. package/build/client/_app/immutable/nodes/0.ejabgzDQ.js.br +0 -0
  390. package/build/client/_app/immutable/nodes/0.ejabgzDQ.js.gz +0 -0
  391. package/build/client/_app/immutable/nodes/1.BFK7Ubrr.js +0 -1
  392. package/build/client/_app/immutable/nodes/1.BFK7Ubrr.js.br +0 -0
  393. package/build/client/_app/immutable/nodes/1.BFK7Ubrr.js.gz +0 -0
  394. package/build/client/_app/immutable/nodes/2.DV3saFiY.js +0 -1
  395. package/build/client/_app/immutable/nodes/2.DV3saFiY.js.br +0 -0
  396. package/build/client/_app/immutable/nodes/2.DV3saFiY.js.gz +0 -0
  397. package/build/client/_app/immutable/nodes/3.3yohCM25.js +0 -3
  398. package/build/client/_app/immutable/nodes/3.3yohCM25.js.br +0 -0
  399. package/build/client/_app/immutable/nodes/3.3yohCM25.js.gz +0 -0
  400. package/build/client/_app/immutable/nodes/4.D6NIf10D.js +0 -1
  401. package/build/client/_app/immutable/nodes/4.D6NIf10D.js.br +0 -0
  402. package/build/client/_app/immutable/nodes/4.D6NIf10D.js.gz +0 -0
  403. package/build/client/_app/immutable/nodes/5.g3R-QfIW.js.br +0 -0
  404. package/build/client/_app/immutable/nodes/5.g3R-QfIW.js.gz +0 -0
  405. package/build/client/_app/immutable/nodes/6.DSpd_nYK.js +0 -2
  406. package/build/client/_app/immutable/nodes/6.DSpd_nYK.js.br +0 -0
  407. package/build/client/_app/immutable/nodes/6.DSpd_nYK.js.gz +0 -0
  408. package/build/client/_app/immutable/nodes/7.F9WBFTz2.js +0 -2
  409. package/build/client/_app/immutable/nodes/7.F9WBFTz2.js.br +0 -0
  410. package/build/client/_app/immutable/nodes/7.F9WBFTz2.js.gz +0 -0
  411. package/build/server/chunks/0-ePgrkfG9.js +0 -9
  412. package/build/server/chunks/0-ePgrkfG9.js.map +0 -1
  413. package/build/server/chunks/1-BV7u1xGo.js +0 -9
  414. package/build/server/chunks/2-3p1kyvjQ.js +0 -9
  415. package/build/server/chunks/2-3p1kyvjQ.js.map +0 -1
  416. package/build/server/chunks/3-Ck7ewhOX.js +0 -9
  417. package/build/server/chunks/3-Ck7ewhOX.js.map +0 -1
  418. package/build/server/chunks/4-ChFYfo_S.js +0 -9
  419. package/build/server/chunks/4-ChFYfo_S.js.map +0 -1
  420. package/build/server/chunks/5-q-tQLBBu.js +0 -9
  421. package/build/server/chunks/5-q-tQLBBu.js.map +0 -1
  422. package/build/server/chunks/6-BIaAbm8b.js +0 -9
  423. package/build/server/chunks/6-BIaAbm8b.js.map +0 -1
  424. package/build/server/chunks/7--TmbCgrH.js +0 -9
  425. package/build/server/chunks/7--TmbCgrH.js.map +0 -1
  426. package/build/server/chunks/Button-Cs1aE6ka.js.map +0 -1
  427. package/build/server/chunks/Icon-CEUrotA6.js.map +0 -1
  428. package/build/server/chunks/_layout.svelte-CtWmEJwe.js.map +0 -1
  429. package/build/server/chunks/_page.svelte-BdYynOck.js +0 -85
  430. package/build/server/chunks/_page.svelte-BdYynOck.js.map +0 -1
  431. package/build/server/chunks/_page.svelte-CVq6tRb3.js.map +0 -1
  432. package/build/server/chunks/_page.svelte-CxWcQ0Am.js.map +0 -1
  433. package/build/server/chunks/_server.ts-BStnNIcq.js +0 -34
  434. package/build/server/chunks/_server.ts-BStnNIcq.js.map +0 -1
  435. package/build/server/chunks/_server.ts-CAxsWKvS.js.map +0 -1
  436. package/build/server/chunks/_server.ts-COu0vNpd.js.map +0 -1
  437. package/build/server/chunks/_server.ts-CTpcLUH8.js.map +0 -1
  438. package/build/server/chunks/_server.ts-Cf84YIaW.js +0 -25
  439. package/build/server/chunks/_server.ts-Cf84YIaW.js.map +0 -1
  440. package/build/server/chunks/_server.ts-Ch-6iOHp.js.map +0 -1
  441. package/build/server/chunks/_server.ts-CtH0dhUp.js +0 -71
  442. package/build/server/chunks/_server.ts-CtH0dhUp.js.map +0 -1
  443. package/build/server/chunks/_server.ts-DB_Kg97c.js.map +0 -1
  444. package/build/server/chunks/_server.ts-DYpJImqd.js.map +0 -1
  445. package/build/server/chunks/_server.ts-Deok2y88.js.map +0 -1
  446. package/build/server/chunks/_server.ts-tSpgyl1D.js.map +0 -1
  447. package/build/server/chunks/_server.ts-vekTmWAx.js.map +0 -1
  448. package/build/server/chunks/client2-CCBGA-2V.js +0 -7
  449. package/build/server/chunks/index-server-CrDaL06Y.js.map +0 -1
  450. package/build/server/chunks/index2-CgclKpUj.js.map +0 -1
  451. package/build/server/chunks/library-apns-BqJbvSKh.js.map +0 -1
  452. package/build/server/chunks/pty-manager-BQVB7IVj.js.map +0 -1
  453. package/build/server/chunks/root-DDSnEAZv.js.map +0 -1
  454. package/build/server/chunks/shared-server-sSGG17Df.js.map +0 -1
  455. package/build/server/chunks/state.svelte-hBbXlUak.js.map +0 -1
  456. package/src/generated/types/Client.ts +0 -589
  457. package/src/lib/types/config.ts +0 -1
  458. /package/build/client/_app/immutable/assets/{4.BFUut--w.css → 6.BFUut--w.css} +0 -0
  459. /package/build/client/_app/immutable/assets/{4.BFUut--w.css.br → 6.BFUut--w.css.br} +0 -0
  460. /package/build/client/_app/immutable/assets/{4.BFUut--w.css.gz → 6.BFUut--w.css.gz} +0 -0
  461. /package/build/client/_app/immutable/assets/{5.BTOx7yt7.css → 7.BTOx7yt7.css} +0 -0
  462. /package/build/client/_app/immutable/assets/{5.BTOx7yt7.css.br → 7.BTOx7yt7.css.br} +0 -0
  463. /package/build/client/_app/immutable/assets/{5.BTOx7yt7.css.gz → 7.BTOx7yt7.css.gz} +0 -0
  464. /package/build/client/_app/immutable/assets/{6.eZGZN-BF.css → 8.eZGZN-BF.css} +0 -0
  465. /package/build/client/_app/immutable/assets/{6.eZGZN-BF.css.br → 8.eZGZN-BF.css.br} +0 -0
  466. /package/build/client/_app/immutable/assets/{6.eZGZN-BF.css.gz → 8.eZGZN-BF.css.gz} +0 -0
  467. /package/build/client/_app/immutable/assets/{7.DwS5ZHBh.css → 9.DwS5ZHBh.css} +0 -0
  468. /package/build/client/_app/immutable/assets/{7.DwS5ZHBh.css.br → 9.DwS5ZHBh.css.br} +0 -0
  469. /package/build/client/_app/immutable/assets/{7.DwS5ZHBh.css.gz → 9.DwS5ZHBh.css.gz} +0 -0
  470. /package/src/{generated/types → lib/types/generated}/API.ts +0 -0
  471. /package/src/{generated/types → lib/types/generated}/APN.ts +0 -0
  472. /package/src/{generated/types → lib/types/generated}/CLI.ts +0 -0
  473. /package/src/{generated/types → lib/types/generated}/Config.ts +0 -0
  474. /package/src/{generated/types → lib/types/generated}/Holder.ts +0 -0
  475. /package/src/{generated/types → lib/types/generated}/JWT.ts +0 -0
  476. /package/src/{generated/types → lib/types/generated}/Notification.ts +0 -0
  477. /package/src/{generated/types → lib/types/generated}/OpenCode.ts +0 -0
  478. /package/src/{generated/types → lib/types/generated}/Sessions.ts +0 -0
  479. /package/src/{generated/types → lib/types/generated}/Terminal.ts +0 -0
  480. /package/src/{generated/types → lib/types/generated}/index.ts +0 -0
@@ -0,0 +1,1656 @@
1
+ import { type SessionSource, decodeSessionSource } from './index';
2
+ import {
3
+ isJSON,
4
+ decodeString,
5
+ _decodeString,
6
+ decodeArray,
7
+ _decodeArray,
8
+ decodeNumber,
9
+ _decodeNumber,
10
+ decodeBoolean,
11
+ _decodeBoolean,
12
+ } from 'type-decoder';
13
+
14
+ /**
15
+ * @type { ClientTerminalStatus }
16
+ * @description Whether a client-side terminal view is running or has exited
17
+ */
18
+ export type ClientTerminalStatus = 'running' | 'exited';
19
+
20
+ export function decodeClientTerminalStatus(rawInput: unknown): ClientTerminalStatus | null {
21
+ switch (rawInput) {
22
+ case 'running':
23
+ case 'exited':
24
+ return rawInput;
25
+ }
26
+ return null;
27
+ }
28
+
29
+ export function _decodeClientTerminalStatus(rawInput: unknown): ClientTerminalStatus | undefined {
30
+ switch (rawInput) {
31
+ case 'running':
32
+ case 'exited':
33
+ return rawInput;
34
+ }
35
+ return;
36
+ }
37
+
38
+ /**
39
+ * @type { SessionMessage }
40
+ * @description A conversation message entry
41
+ */
42
+ export type SessionMessage = {
43
+ /**
44
+ * @description Unique message identifier
45
+ * @type { string }
46
+ * @memberof SessionMessage
47
+ */
48
+ id: string;
49
+ /**
50
+ * @description Message type discriminator (e.g. "user", "assistant", "tool-use", "tool-result")
51
+ * @type { string }
52
+ * @memberof SessionMessage
53
+ */
54
+ type: string;
55
+ /**
56
+ * @description Primary text content of the message
57
+ * @type { string }
58
+ * @memberof SessionMessage
59
+ */
60
+ content: string;
61
+ /**
62
+ * @description ISO 8601 timestamp of the message
63
+ * @type { string }
64
+ * @memberof SessionMessage
65
+ */
66
+ timestamp: string;
67
+ [keys: string]: unknown;
68
+ };
69
+
70
+ export function decodeSessionMessage(rawInput: unknown): SessionMessage | null {
71
+ if (isJSON(rawInput)) {
72
+ const decodedId = decodeString(rawInput['id']);
73
+ const decodedType = decodeString(rawInput['type']);
74
+ const decodedContent = decodeString(rawInput['content']);
75
+ const decodedTimestamp = decodeString(rawInput['timestamp']);
76
+
77
+ if (
78
+ decodedId === null ||
79
+ decodedType === null ||
80
+ decodedContent === null ||
81
+ decodedTimestamp === null
82
+ ) {
83
+ return null;
84
+ }
85
+
86
+ return {
87
+ ...rawInput,
88
+ id: decodedId,
89
+ type: decodedType,
90
+ content: decodedContent,
91
+ timestamp: decodedTimestamp,
92
+ };
93
+ }
94
+ return null;
95
+ }
96
+
97
+ /**
98
+ * @type { TerminalListItem }
99
+ * @description Terminal entry displayed in the /terminals list page
100
+ */
101
+ export type TerminalListItem = {
102
+ /**
103
+ * @description Unique terminal identifier
104
+ * @type { string }
105
+ * @memberof TerminalListItem
106
+ */
107
+ id: string;
108
+ /**
109
+ * @description The command that was launched (e.g. "zsh", "claude")
110
+ * @type { string }
111
+ * @memberof TerminalListItem
112
+ */
113
+ command: string;
114
+ /**
115
+ * @description Command arguments passed at launch
116
+ * @type { string[] }
117
+ * @memberof TerminalListItem
118
+ */
119
+ args: string[];
120
+ /**
121
+ * @description Working directory the terminal was launched in
122
+ * @type { string }
123
+ * @memberof TerminalListItem
124
+ */
125
+ cwd: string;
126
+ /**
127
+ * @description Current working directory detected via OSC 7, or null if not yet detected
128
+ * @type { string }
129
+ * @memberof TerminalListItem
130
+ */
131
+ currentCwd: string | null;
132
+ /**
133
+ * @description OS process ID of the terminal process
134
+ * @type { number }
135
+ * @memberof TerminalListItem
136
+ */
137
+ pid: number;
138
+ /**
139
+ * @description ISO 8601 timestamp when the terminal was created
140
+ * @type { string }
141
+ * @memberof TerminalListItem
142
+ */
143
+ createdAt: string;
144
+ /**
145
+ * @description ISO 8601 timestamp when the process exited, or null if still running
146
+ * @type { string }
147
+ * @memberof TerminalListItem
148
+ */
149
+ exitedAt: string | null;
150
+ /**
151
+ * @description Whether the terminal is running or has exited
152
+ * @type { ClientTerminalStatus }
153
+ * @memberof TerminalListItem
154
+ */
155
+ status: ClientTerminalStatus;
156
+ /**
157
+ * @description Process exit code, or null if still running
158
+ * @type { number }
159
+ * @memberof TerminalListItem
160
+ */
161
+ exitCode: number | null;
162
+ /**
163
+ * @description Whether the terminal is currently producing output
164
+ * @type { boolean }
165
+ * @memberof TerminalListItem
166
+ */
167
+ isActive: boolean | null;
168
+ /**
169
+ * @description Most recent output line for preview display, or null if none
170
+ * @type { string }
171
+ * @memberof TerminalListItem
172
+ */
173
+ lastOutput: string | null;
174
+ };
175
+
176
+ export function decodeTerminalListItem(rawInput: unknown): TerminalListItem | null {
177
+ if (isJSON(rawInput)) {
178
+ const decodedId = decodeString(rawInput['id']);
179
+ const decodedCommand = decodeString(rawInput['command']);
180
+ const decodedArgs = decodeArray(rawInput['args'], decodeString);
181
+ const decodedCwd = decodeString(rawInput['cwd']);
182
+ const decodedCurrentCwd = decodeString(rawInput['currentCwd']);
183
+ const decodedPid = decodeNumber(rawInput['pid']);
184
+ const decodedCreatedAt = decodeString(rawInput['createdAt']);
185
+ const decodedExitedAt = decodeString(rawInput['exitedAt']);
186
+ const decodedStatus = decodeClientTerminalStatus(rawInput['status']);
187
+ const decodedExitCode = decodeNumber(rawInput['exitCode']);
188
+ const decodedIsActive = decodeBoolean(rawInput['isActive']);
189
+ const decodedLastOutput = decodeString(rawInput['lastOutput']);
190
+
191
+ if (
192
+ decodedId === null ||
193
+ decodedCommand === null ||
194
+ decodedArgs === null ||
195
+ decodedCwd === null ||
196
+ decodedPid === null ||
197
+ decodedCreatedAt === null ||
198
+ decodedStatus === null
199
+ ) {
200
+ return null;
201
+ }
202
+
203
+ return {
204
+ id: decodedId,
205
+ command: decodedCommand,
206
+ args: decodedArgs,
207
+ cwd: decodedCwd,
208
+ currentCwd: decodedCurrentCwd,
209
+ pid: decodedPid,
210
+ createdAt: decodedCreatedAt,
211
+ exitedAt: decodedExitedAt,
212
+ status: decodedStatus,
213
+ exitCode: decodedExitCode,
214
+ isActive: decodedIsActive,
215
+ lastOutput: decodedLastOutput,
216
+ };
217
+ }
218
+ return null;
219
+ }
220
+
221
+ /**
222
+ * @type { TerminalDetailView }
223
+ * @description Terminal data used by the /terminals/[id] detail page
224
+ */
225
+ export type TerminalDetailView = {
226
+ /**
227
+ * @description Unique terminal identifier
228
+ * @type { string }
229
+ * @memberof TerminalDetailView
230
+ */
231
+ id: string;
232
+ /**
233
+ * @description The command that was launched
234
+ * @type { string }
235
+ * @memberof TerminalDetailView
236
+ */
237
+ command: string;
238
+ /**
239
+ * @description Command arguments passed at launch
240
+ * @type { string[] }
241
+ * @memberof TerminalDetailView
242
+ */
243
+ args: string[];
244
+ /**
245
+ * @description Working directory the terminal was launched in
246
+ * @type { string }
247
+ * @memberof TerminalDetailView
248
+ */
249
+ cwd: string;
250
+ /**
251
+ * @description OS process ID of the terminal process
252
+ * @type { number }
253
+ * @memberof TerminalDetailView
254
+ */
255
+ pid: number;
256
+ /**
257
+ * @description ISO 8601 timestamp when the terminal was created
258
+ * @type { string }
259
+ * @memberof TerminalDetailView
260
+ */
261
+ createdAt: string;
262
+ /**
263
+ * @description ISO 8601 timestamp when the process exited, or null if still running
264
+ * @type { string }
265
+ * @memberof TerminalDetailView
266
+ */
267
+ exitedAt: string | null;
268
+ /**
269
+ * @description Whether the terminal is running or has exited
270
+ * @type { ClientTerminalStatus }
271
+ * @memberof TerminalDetailView
272
+ */
273
+ status: ClientTerminalStatus;
274
+ /**
275
+ * @description Process exit code, or null if still running
276
+ * @type { number }
277
+ * @memberof TerminalDetailView
278
+ */
279
+ exitCode: number | null;
280
+ /**
281
+ * @description Number of WebSocket clients currently attached to this terminal
282
+ * @type { number }
283
+ * @memberof TerminalDetailView
284
+ */
285
+ clientCount: number | null;
286
+ /**
287
+ * @description Most recent scrollback line for preview display, or null if empty
288
+ * @type { string }
289
+ * @memberof TerminalDetailView
290
+ */
291
+ lastOutput: string | null;
292
+ /**
293
+ * @description ISO 8601 timestamp when this response was generated
294
+ * @type { string }
295
+ * @memberof TerminalDetailView
296
+ */
297
+ timestamp: string | null;
298
+ /**
299
+ * @description WebSocket path for raw terminal I/O
300
+ * @type { string }
301
+ * @memberof TerminalDetailView
302
+ */
303
+ ws: string;
304
+ /**
305
+ * @description WebSocket path for structured session stream
306
+ * @type { string }
307
+ * @memberof TerminalDetailView
308
+ */
309
+ sessionWs: string;
310
+ };
311
+
312
+ export function decodeTerminalDetailView(rawInput: unknown): TerminalDetailView | null {
313
+ if (isJSON(rawInput)) {
314
+ const decodedId = decodeString(rawInput['id']);
315
+ const decodedCommand = decodeString(rawInput['command']);
316
+ const decodedArgs = decodeArray(rawInput['args'], decodeString);
317
+ const decodedCwd = decodeString(rawInput['cwd']);
318
+ const decodedPid = decodeNumber(rawInput['pid']);
319
+ const decodedCreatedAt = decodeString(rawInput['createdAt']);
320
+ const decodedExitedAt = decodeString(rawInput['exitedAt']);
321
+ const decodedStatus = decodeClientTerminalStatus(rawInput['status']);
322
+ const decodedExitCode = decodeNumber(rawInput['exitCode']);
323
+ const decodedClientCount = decodeNumber(rawInput['clientCount']);
324
+ const decodedLastOutput = decodeString(rawInput['lastOutput']);
325
+ const decodedTimestamp = decodeString(rawInput['timestamp']);
326
+ const decodedWs = decodeString(rawInput['ws']);
327
+ const decodedSessionWs = decodeString(rawInput['sessionWs']);
328
+
329
+ if (
330
+ decodedId === null ||
331
+ decodedCommand === null ||
332
+ decodedArgs === null ||
333
+ decodedCwd === null ||
334
+ decodedPid === null ||
335
+ decodedCreatedAt === null ||
336
+ decodedStatus === null ||
337
+ decodedWs === null ||
338
+ decodedSessionWs === null
339
+ ) {
340
+ return null;
341
+ }
342
+
343
+ return {
344
+ id: decodedId,
345
+ command: decodedCommand,
346
+ args: decodedArgs,
347
+ cwd: decodedCwd,
348
+ pid: decodedPid,
349
+ createdAt: decodedCreatedAt,
350
+ exitedAt: decodedExitedAt,
351
+ status: decodedStatus,
352
+ exitCode: decodedExitCode,
353
+ clientCount: decodedClientCount,
354
+ lastOutput: decodedLastOutput,
355
+ timestamp: decodedTimestamp,
356
+ ws: decodedWs,
357
+ sessionWs: decodedSessionWs,
358
+ };
359
+ }
360
+ return null;
361
+ }
362
+
363
+ /**
364
+ * @type { SessionViewResponse }
365
+ * @description API response shape for fetching a single session with its conversation messages
366
+ */
367
+ export type SessionViewResponse = {
368
+ /**
369
+ * @description Session metadata
370
+ * @type { SessionViewResponseSession }
371
+ * @memberof SessionViewResponse
372
+ */
373
+ session: SessionViewResponseSession;
374
+ /**
375
+ * @description Conversation messages in the session
376
+ * @type { SessionMessage[] }
377
+ * @memberof SessionViewResponse
378
+ */
379
+ messages: SessionMessage[];
380
+ };
381
+
382
+ export function decodeSessionViewResponse(rawInput: unknown): SessionViewResponse | null {
383
+ if (isJSON(rawInput)) {
384
+ const decodedSession = decodeSessionViewResponseSession(rawInput['session']);
385
+ const decodedMessages = decodeArray(rawInput['messages'], decodeSessionMessage);
386
+
387
+ if (decodedSession === null || decodedMessages === null) {
388
+ return null;
389
+ }
390
+
391
+ return {
392
+ session: decodedSession,
393
+ messages: decodedMessages,
394
+ };
395
+ }
396
+ return null;
397
+ }
398
+
399
+ /**
400
+ * @type { SessionViewResponseSession }
401
+ * @description Session metadata
402
+ */
403
+ export type SessionViewResponseSession = {
404
+ /**
405
+ * @description Unique session identifier
406
+ * @type { string }
407
+ * @memberof SessionViewResponseSession
408
+ */
409
+ id: string;
410
+ /**
411
+ * @description Human-readable session title
412
+ * @type { string }
413
+ * @memberof SessionViewResponseSession
414
+ */
415
+ title: string;
416
+ /**
417
+ * @description Absolute path to the project directory
418
+ * @type { string }
419
+ * @memberof SessionViewResponseSession
420
+ */
421
+ projectPath: string;
422
+ /**
423
+ * @description Git branch active during the session
424
+ * @type { string }
425
+ * @memberof SessionViewResponseSession
426
+ */
427
+ gitBranch: string;
428
+ /**
429
+ * @description Total number of messages in the session
430
+ * @type { number }
431
+ * @memberof SessionViewResponseSession
432
+ */
433
+ messageCount: number;
434
+ /**
435
+ * @description ISO 8601 timestamp when the session started
436
+ * @type { string }
437
+ * @memberof SessionViewResponseSession
438
+ */
439
+ created: string;
440
+ /**
441
+ * @description ISO 8601 timestamp of the last session update
442
+ * @type { string }
443
+ * @memberof SessionViewResponseSession
444
+ */
445
+ modified: string;
446
+ /**
447
+ * @description Brief summary of the session content
448
+ * @type { string }
449
+ * @memberof SessionViewResponseSession
450
+ */
451
+ summary: string;
452
+ /**
453
+ * @description Source tool that created the session
454
+ * @type { SessionSource }
455
+ * @memberof SessionViewResponseSession
456
+ */
457
+ source: SessionSource;
458
+ };
459
+
460
+ export function decodeSessionViewResponseSession(
461
+ rawInput: unknown
462
+ ): SessionViewResponseSession | null {
463
+ if (isJSON(rawInput)) {
464
+ const decodedId = decodeString(rawInput['id']);
465
+ const decodedTitle = decodeString(rawInput['title']);
466
+ const decodedProjectPath = decodeString(rawInput['projectPath']);
467
+ const decodedGitBranch = decodeString(rawInput['gitBranch']);
468
+ const decodedMessageCount = decodeNumber(rawInput['messageCount']);
469
+ const decodedCreated = decodeString(rawInput['created']);
470
+ const decodedModified = decodeString(rawInput['modified']);
471
+ const decodedSummary = decodeString(rawInput['summary']);
472
+ const decodedSource = decodeSessionSource(rawInput['source']);
473
+
474
+ if (
475
+ decodedId === null ||
476
+ decodedTitle === null ||
477
+ decodedProjectPath === null ||
478
+ decodedGitBranch === null ||
479
+ decodedMessageCount === null ||
480
+ decodedCreated === null ||
481
+ decodedModified === null ||
482
+ decodedSummary === null ||
483
+ decodedSource === null
484
+ ) {
485
+ return null;
486
+ }
487
+
488
+ return {
489
+ id: decodedId,
490
+ title: decodedTitle,
491
+ projectPath: decodedProjectPath,
492
+ gitBranch: decodedGitBranch,
493
+ messageCount: decodedMessageCount,
494
+ created: decodedCreated,
495
+ modified: decodedModified,
496
+ summary: decodedSummary,
497
+ source: decodedSource,
498
+ };
499
+ }
500
+ return null;
501
+ }
502
+
503
+ /**
504
+ * @type { ShortcutAction }
505
+ * @description A keyboard shortcut definition for the terminal help overlay
506
+ */
507
+ export type ShortcutAction = {
508
+ /**
509
+ * @description Key character that triggers the shortcut (e.g. "/", "k")
510
+ * @type { string }
511
+ * @memberof ShortcutAction
512
+ */
513
+ key: string;
514
+ /**
515
+ * @description Human-readable key combo label shown in the UI (e.g. "Cmd+/")
516
+ * @type { string }
517
+ * @memberof ShortcutAction
518
+ */
519
+ label: string;
520
+ /**
521
+ * @description What the shortcut does
522
+ * @type { string }
523
+ * @memberof ShortcutAction
524
+ */
525
+ description: string;
526
+ /**
527
+ * @description Whether the shift modifier is also required
528
+ * @type { boolean }
529
+ * @memberof ShortcutAction
530
+ */
531
+ shift: boolean | null;
532
+ };
533
+
534
+ export function decodeShortcutAction(rawInput: unknown): ShortcutAction | null {
535
+ if (isJSON(rawInput)) {
536
+ const decodedKey = decodeString(rawInput['key']);
537
+ const decodedLabel = decodeString(rawInput['label']);
538
+ const decodedDescription = decodeString(rawInput['description']);
539
+ const decodedShift = decodeBoolean(rawInput['shift']);
540
+
541
+ if (decodedKey === null || decodedLabel === null || decodedDescription === null) {
542
+ return null;
543
+ }
544
+
545
+ return {
546
+ key: decodedKey,
547
+ label: decodedLabel,
548
+ description: decodedDescription,
549
+ shift: decodedShift,
550
+ };
551
+ }
552
+ return null;
553
+ }
554
+
555
+ /**
556
+ * @type { SessionStatus }
557
+ * @description Lifecycle status of a dashboard session card
558
+ */
559
+ export type SessionStatus = 'running' | 'idle' | 'exited' | 'error';
560
+
561
+ export function decodeSessionStatus(rawInput: unknown): SessionStatus | null {
562
+ switch (rawInput) {
563
+ case 'running':
564
+ case 'idle':
565
+ case 'exited':
566
+ case 'error':
567
+ return rawInput;
568
+ }
569
+ return null;
570
+ }
571
+
572
+ export function _decodeSessionStatus(rawInput: unknown): SessionStatus | undefined {
573
+ switch (rawInput) {
574
+ case 'running':
575
+ case 'idle':
576
+ case 'exited':
577
+ case 'error':
578
+ return rawInput;
579
+ }
580
+ return;
581
+ }
582
+
583
+ /**
584
+ * @type { SessionEvent }
585
+ * @description A single event recorded from the /ws/events WebSocket channel for a session
586
+ */
587
+ export type SessionEvent = {
588
+ /**
589
+ * @description Raw event payload received from the server
590
+ * @type { SessionEventData }
591
+ * @memberof SessionEvent
592
+ */
593
+ data: SessionEventData;
594
+ /**
595
+ * @description Unique event identifier (e.g. "evt-1", "evt-42")
596
+ * @type { string }
597
+ * @memberof SessionEvent
598
+ */
599
+ id: string;
600
+ /**
601
+ * @description Whether this event has been included in an AI summary
602
+ * @type { boolean }
603
+ * @memberof SessionEvent
604
+ */
605
+ summarized: boolean;
606
+ /**
607
+ * @description Terminal identifier this event belongs to
608
+ * @type { string }
609
+ * @memberof SessionEvent
610
+ */
611
+ terminalId: string;
612
+ /**
613
+ * @description ISO 8601 timestamp of the event
614
+ * @type { string }
615
+ * @memberof SessionEvent
616
+ */
617
+ timestamp: string;
618
+ /**
619
+ * @description Event type discriminator (e.g. "tool-started", "tool-failed", "agent-idle")
620
+ * @type { string }
621
+ * @memberof SessionEvent
622
+ */
623
+ type: string;
624
+ };
625
+
626
+ export function decodeSessionEvent(rawInput: unknown): SessionEvent | null {
627
+ if (isJSON(rawInput)) {
628
+ const decodedData = decodeSessionEventData(rawInput['data']);
629
+ const decodedId = decodeString(rawInput['id']);
630
+ const decodedSummarized = decodeBoolean(rawInput['summarized']);
631
+ const decodedTerminalId = decodeString(rawInput['terminalId']);
632
+ const decodedTimestamp = decodeString(rawInput['timestamp']);
633
+ const decodedType = decodeString(rawInput['type']);
634
+
635
+ if (
636
+ decodedData === null ||
637
+ decodedId === null ||
638
+ decodedSummarized === null ||
639
+ decodedTerminalId === null ||
640
+ decodedTimestamp === null ||
641
+ decodedType === null
642
+ ) {
643
+ return null;
644
+ }
645
+
646
+ return {
647
+ data: decodedData,
648
+ id: decodedId,
649
+ summarized: decodedSummarized,
650
+ terminalId: decodedTerminalId,
651
+ timestamp: decodedTimestamp,
652
+ type: decodedType,
653
+ };
654
+ }
655
+ return null;
656
+ }
657
+
658
+ /**
659
+ * @type { SessionEventData }
660
+ * @description Raw event payload received from the server
661
+ */
662
+ export type SessionEventData = Record<string, unknown>;
663
+
664
+ export function decodeSessionEventData(rawInput: unknown): SessionEventData | null {
665
+ if (isJSON(rawInput)) {
666
+ return {
667
+ ...rawInput,
668
+ };
669
+ }
670
+ return null;
671
+ }
672
+
673
+ /**
674
+ * @type { SessionState }
675
+ * @description Live state of one session card tracked by the dashboard store
676
+ */
677
+ export type SessionState = {
678
+ /**
679
+ * @description The command running in the terminal (e.g. "claude", "opencode")
680
+ * @type { string }
681
+ * @memberof SessionState
682
+ */
683
+ command: string;
684
+ /**
685
+ * @description ISO 8601 timestamp when the terminal was created
686
+ * @type { string }
687
+ * @memberof SessionState
688
+ */
689
+ createdAt: string;
690
+ /**
691
+ * @description Working directory path of the terminal
692
+ * @type { string }
693
+ * @memberof SessionState
694
+ */
695
+ cwd: string;
696
+ /**
697
+ * @description Total number of error events accumulated in this session
698
+ * @type { number }
699
+ * @memberof SessionState
700
+ */
701
+ errorCount: number;
702
+ /**
703
+ * @description Total events received (including truncated ones beyond the ring buffer)
704
+ * @type { number }
705
+ * @memberof SessionState
706
+ */
707
+ eventCount: number;
708
+ /**
709
+ * @description Recent events ring buffer (capped at 100 entries)
710
+ * @type { SessionEvent[] }
711
+ * @memberof SessionState
712
+ */
713
+ events: SessionEvent[];
714
+ /**
715
+ * @description ISO 8601 timestamp when the terminal exited, or null if still running
716
+ * @type { string }
717
+ * @memberof SessionState
718
+ */
719
+ exitedAt: string | null;
720
+ /**
721
+ * @description What Claude is working on (extracted from first user message), or null if not yet known
722
+ * @type { string }
723
+ * @memberof SessionState
724
+ */
725
+ goal: string | null;
726
+ /**
727
+ * @description Whether an AI summary is currently being generated for this session
728
+ * @type { boolean }
729
+ * @memberof SessionState
730
+ */
731
+ isSummarizing: boolean;
732
+ /**
733
+ * @description Display name of the project (basename of cwd)
734
+ * @type { string }
735
+ * @memberof SessionState
736
+ */
737
+ projectName: string;
738
+ /**
739
+ * @description Full cwd path used as the project path
740
+ * @type { string }
741
+ * @memberof SessionState
742
+ */
743
+ projectPath: string;
744
+ /**
745
+ * @description Current lifecycle status of the session
746
+ * @type { SessionStatus }
747
+ * @memberof SessionState
748
+ */
749
+ status: SessionStatus;
750
+ /**
751
+ * @description Current AI-generated progress summary, or null if not yet summarized
752
+ * @type { string }
753
+ * @memberof SessionState
754
+ */
755
+ summary: string | null;
756
+ /**
757
+ * @description ISO 8601 timestamp when the summary was last updated, or null if never
758
+ * @type { string }
759
+ * @memberof SessionState
760
+ */
761
+ summaryUpdatedAt: string | null;
762
+ /**
763
+ * @description Unique terminal identifier (e.g. "term_a1b2c3")
764
+ * @type { string }
765
+ * @memberof SessionState
766
+ */
767
+ terminalId: string;
768
+ /**
769
+ * @description Total number of tool calls made in this session
770
+ * @type { number }
771
+ * @memberof SessionState
772
+ */
773
+ toolCallCount: number;
774
+ };
775
+
776
+ export function decodeSessionState(rawInput: unknown): SessionState | null {
777
+ if (isJSON(rawInput)) {
778
+ const decodedCommand = decodeString(rawInput['command']);
779
+ const decodedCreatedAt = decodeString(rawInput['createdAt']);
780
+ const decodedCwd = decodeString(rawInput['cwd']);
781
+ const decodedErrorCount = decodeNumber(rawInput['errorCount']);
782
+ const decodedEventCount = decodeNumber(rawInput['eventCount']);
783
+ const decodedEvents = decodeArray(rawInput['events'], decodeSessionEvent);
784
+ const decodedExitedAt = decodeString(rawInput['exitedAt']);
785
+ const decodedGoal = decodeString(rawInput['goal']);
786
+ const decodedIsSummarizing = decodeBoolean(rawInput['isSummarizing']);
787
+ const decodedProjectName = decodeString(rawInput['projectName']);
788
+ const decodedProjectPath = decodeString(rawInput['projectPath']);
789
+ const decodedStatus = decodeSessionStatus(rawInput['status']);
790
+ const decodedSummary = decodeString(rawInput['summary']);
791
+ const decodedSummaryUpdatedAt = decodeString(rawInput['summaryUpdatedAt']);
792
+ const decodedTerminalId = decodeString(rawInput['terminalId']);
793
+ const decodedToolCallCount = decodeNumber(rawInput['toolCallCount']);
794
+
795
+ if (
796
+ decodedCommand === null ||
797
+ decodedCreatedAt === null ||
798
+ decodedCwd === null ||
799
+ decodedErrorCount === null ||
800
+ decodedEventCount === null ||
801
+ decodedEvents === null ||
802
+ decodedIsSummarizing === null ||
803
+ decodedProjectName === null ||
804
+ decodedProjectPath === null ||
805
+ decodedStatus === null ||
806
+ decodedTerminalId === null ||
807
+ decodedToolCallCount === null
808
+ ) {
809
+ return null;
810
+ }
811
+
812
+ return {
813
+ command: decodedCommand,
814
+ createdAt: decodedCreatedAt,
815
+ cwd: decodedCwd,
816
+ errorCount: decodedErrorCount,
817
+ eventCount: decodedEventCount,
818
+ events: decodedEvents,
819
+ exitedAt: decodedExitedAt,
820
+ goal: decodedGoal,
821
+ isSummarizing: decodedIsSummarizing,
822
+ projectName: decodedProjectName,
823
+ projectPath: decodedProjectPath,
824
+ status: decodedStatus,
825
+ summary: decodedSummary,
826
+ summaryUpdatedAt: decodedSummaryUpdatedAt,
827
+ terminalId: decodedTerminalId,
828
+ toolCallCount: decodedToolCallCount,
829
+ };
830
+ }
831
+ return null;
832
+ }
833
+
834
+ /**
835
+ * @type { DashboardCard }
836
+ * @description A session card displayed on the home dashboard — extends SessionState with derived display fields
837
+ */
838
+ export type DashboardCard = {
839
+ /**
840
+ * @description The command running in the terminal
841
+ * @type { string }
842
+ * @memberof DashboardCard
843
+ */
844
+ command: string;
845
+ /**
846
+ * @description ISO 8601 timestamp when the terminal was created
847
+ * @type { string }
848
+ * @memberof DashboardCard
849
+ */
850
+ createdAt: string;
851
+ /**
852
+ * @description Working directory path of the terminal
853
+ * @type { string }
854
+ * @memberof DashboardCard
855
+ */
856
+ cwd: string;
857
+ /**
858
+ * @description Milliseconds elapsed since createdAt (derived at render time)
859
+ * @type { number }
860
+ * @memberof DashboardCard
861
+ */
862
+ duration: number;
863
+ /**
864
+ * @description Total number of error events accumulated in this session
865
+ * @type { number }
866
+ * @memberof DashboardCard
867
+ */
868
+ errorCount: number;
869
+ /**
870
+ * @description Total events received (including truncated ones beyond the ring buffer)
871
+ * @type { number }
872
+ * @memberof DashboardCard
873
+ */
874
+ eventCount: number;
875
+ /**
876
+ * @description Recent events ring buffer (capped at 100 entries)
877
+ * @type { SessionEvent[] }
878
+ * @memberof DashboardCard
879
+ */
880
+ events: SessionEvent[];
881
+ /**
882
+ * @description ISO 8601 timestamp when the terminal exited, or null if still running
883
+ * @type { string }
884
+ * @memberof DashboardCard
885
+ */
886
+ exitedAt: string | null;
887
+ /**
888
+ * @description What Claude is working on, or null if not yet known
889
+ * @type { string }
890
+ * @memberof DashboardCard
891
+ */
892
+ goal: string | null;
893
+ /**
894
+ * @description Whether the session has received events within the last 30 seconds
895
+ * @type { boolean }
896
+ * @memberof DashboardCard
897
+ */
898
+ isActive: boolean;
899
+ /**
900
+ * @description Whether an AI summary is currently being generated
901
+ * @type { boolean }
902
+ * @memberof DashboardCard
903
+ */
904
+ isSummarizing: boolean;
905
+ /**
906
+ * @description Display name of the project (basename of cwd)
907
+ * @type { string }
908
+ * @memberof DashboardCard
909
+ */
910
+ projectName: string;
911
+ /**
912
+ * @description Full cwd path used as the project path
913
+ * @type { string }
914
+ * @memberof DashboardCard
915
+ */
916
+ projectPath: string;
917
+ /**
918
+ * @description Current lifecycle status of the session
919
+ * @type { SessionStatus }
920
+ * @memberof DashboardCard
921
+ */
922
+ status: SessionStatus;
923
+ /**
924
+ * @description Current AI-generated progress summary, or null if not yet summarized
925
+ * @type { string }
926
+ * @memberof DashboardCard
927
+ */
928
+ summary: string | null;
929
+ /**
930
+ * @description ISO 8601 timestamp when the summary was last updated, or null if never
931
+ * @type { string }
932
+ * @memberof DashboardCard
933
+ */
934
+ summaryUpdatedAt: string | null;
935
+ /**
936
+ * @description Unique terminal identifier
937
+ * @type { string }
938
+ * @memberof DashboardCard
939
+ */
940
+ terminalId: string;
941
+ /**
942
+ * @description Total number of tool calls made in this session
943
+ * @type { number }
944
+ * @memberof DashboardCard
945
+ */
946
+ toolCallCount: number;
947
+ };
948
+
949
+ export function decodeDashboardCard(rawInput: unknown): DashboardCard | null {
950
+ if (isJSON(rawInput)) {
951
+ const decodedCommand = decodeString(rawInput['command']);
952
+ const decodedCreatedAt = decodeString(rawInput['createdAt']);
953
+ const decodedCwd = decodeString(rawInput['cwd']);
954
+ const decodedDuration = decodeNumber(rawInput['duration']);
955
+ const decodedErrorCount = decodeNumber(rawInput['errorCount']);
956
+ const decodedEventCount = decodeNumber(rawInput['eventCount']);
957
+ const decodedEvents = decodeArray(rawInput['events'], decodeSessionEvent);
958
+ const decodedExitedAt = decodeString(rawInput['exitedAt']);
959
+ const decodedGoal = decodeString(rawInput['goal']);
960
+ const decodedIsActive = decodeBoolean(rawInput['isActive']);
961
+ const decodedIsSummarizing = decodeBoolean(rawInput['isSummarizing']);
962
+ const decodedProjectName = decodeString(rawInput['projectName']);
963
+ const decodedProjectPath = decodeString(rawInput['projectPath']);
964
+ const decodedStatus = decodeSessionStatus(rawInput['status']);
965
+ const decodedSummary = decodeString(rawInput['summary']);
966
+ const decodedSummaryUpdatedAt = decodeString(rawInput['summaryUpdatedAt']);
967
+ const decodedTerminalId = decodeString(rawInput['terminalId']);
968
+ const decodedToolCallCount = decodeNumber(rawInput['toolCallCount']);
969
+
970
+ if (
971
+ decodedCommand === null ||
972
+ decodedCreatedAt === null ||
973
+ decodedCwd === null ||
974
+ decodedDuration === null ||
975
+ decodedErrorCount === null ||
976
+ decodedEventCount === null ||
977
+ decodedEvents === null ||
978
+ decodedIsActive === null ||
979
+ decodedIsSummarizing === null ||
980
+ decodedProjectName === null ||
981
+ decodedProjectPath === null ||
982
+ decodedStatus === null ||
983
+ decodedTerminalId === null ||
984
+ decodedToolCallCount === null
985
+ ) {
986
+ return null;
987
+ }
988
+
989
+ return {
990
+ command: decodedCommand,
991
+ createdAt: decodedCreatedAt,
992
+ cwd: decodedCwd,
993
+ duration: decodedDuration,
994
+ errorCount: decodedErrorCount,
995
+ eventCount: decodedEventCount,
996
+ events: decodedEvents,
997
+ exitedAt: decodedExitedAt,
998
+ goal: decodedGoal,
999
+ isActive: decodedIsActive,
1000
+ isSummarizing: decodedIsSummarizing,
1001
+ projectName: decodedProjectName,
1002
+ projectPath: decodedProjectPath,
1003
+ status: decodedStatus,
1004
+ summary: decodedSummary,
1005
+ summaryUpdatedAt: decodedSummaryUpdatedAt,
1006
+ terminalId: decodedTerminalId,
1007
+ toolCallCount: decodedToolCallCount,
1008
+ };
1009
+ }
1010
+ return null;
1011
+ }
1012
+
1013
+ /**
1014
+ * @type { SummaryRecentEvent }
1015
+ * @description A summarized view of a recent session event used as input to the AI summarizer
1016
+ */
1017
+ export type SummaryRecentEvent = {
1018
+ /**
1019
+ * @description Command string if the event carries one (e.g. bash command), or undefined if not applicable
1020
+ * @type { string }
1021
+ * @memberof SummaryRecentEvent
1022
+ */
1023
+ command: string | null;
1024
+ /**
1025
+ * @description Error message if the event is an error event, or undefined if not applicable
1026
+ * @type { string }
1027
+ * @memberof SummaryRecentEvent
1028
+ */
1029
+ error: string | null;
1030
+ /**
1031
+ * @description Tool name if the event involves a tool call (e.g. "Edit", "Bash"), or undefined if not applicable
1032
+ * @type { string }
1033
+ * @memberof SummaryRecentEvent
1034
+ */
1035
+ tool: string | null;
1036
+ /**
1037
+ * @description Event type discriminator
1038
+ * @type { string }
1039
+ * @memberof SummaryRecentEvent
1040
+ */
1041
+ type: string;
1042
+ };
1043
+
1044
+ export function decodeSummaryRecentEvent(rawInput: unknown): SummaryRecentEvent | null {
1045
+ if (isJSON(rawInput)) {
1046
+ const decodedCommand = decodeString(rawInput['command']);
1047
+ const decodedError = decodeString(rawInput['error']);
1048
+ const decodedTool = decodeString(rawInput['tool']);
1049
+ const decodedType = decodeString(rawInput['type']);
1050
+
1051
+ if (decodedType === null) {
1052
+ return null;
1053
+ }
1054
+
1055
+ return {
1056
+ command: decodedCommand,
1057
+ error: decodedError,
1058
+ tool: decodedTool,
1059
+ type: decodedType,
1060
+ };
1061
+ }
1062
+ return null;
1063
+ }
1064
+
1065
+ /**
1066
+ * @type { SummaryContext }
1067
+ * @description Session context passed to the SessionSummarizer to generate an AI summary
1068
+ */
1069
+ export type SummaryContext = {
1070
+ /**
1071
+ * @description Last 3 user+assistant messages concatenated for context
1072
+ * @type { string }
1073
+ * @memberof SummaryContext
1074
+ */
1075
+ conversationExcerpt: string;
1076
+ /**
1077
+ * @description Total errors in the session
1078
+ * @type { number }
1079
+ * @memberof SummaryContext
1080
+ */
1081
+ errorCount: number;
1082
+ /**
1083
+ * @description Null until first user message is seen; the user's stated task
1084
+ * @type { string }
1085
+ * @memberof SummaryContext
1086
+ */
1087
+ goal: string | null;
1088
+ /**
1089
+ * @description Subset of recent events from the session store (last 10)
1090
+ * @type { SummaryRecentEvent[] }
1091
+ * @memberof SummaryContext
1092
+ */
1093
+ recentEvents: SummaryRecentEvent[];
1094
+ /**
1095
+ * @description Current session status
1096
+ * @type { SessionStatus }
1097
+ * @memberof SummaryContext
1098
+ */
1099
+ status: SessionStatus;
1100
+ /**
1101
+ * @description Total tool calls in the session
1102
+ * @type { number }
1103
+ * @memberof SummaryContext
1104
+ */
1105
+ toolCallCount: number;
1106
+ };
1107
+
1108
+ export function decodeSummaryContext(rawInput: unknown): SummaryContext | null {
1109
+ if (isJSON(rawInput)) {
1110
+ const decodedConversationExcerpt = decodeString(rawInput['conversationExcerpt']);
1111
+ const decodedErrorCount = decodeNumber(rawInput['errorCount']);
1112
+ const decodedGoal = decodeString(rawInput['goal']);
1113
+ const decodedRecentEvents = decodeArray(rawInput['recentEvents'], decodeSummaryRecentEvent);
1114
+ const decodedStatus = decodeSessionStatus(rawInput['status']);
1115
+ const decodedToolCallCount = decodeNumber(rawInput['toolCallCount']);
1116
+
1117
+ if (
1118
+ decodedConversationExcerpt === null ||
1119
+ decodedErrorCount === null ||
1120
+ decodedRecentEvents === null ||
1121
+ decodedStatus === null ||
1122
+ decodedToolCallCount === null
1123
+ ) {
1124
+ return null;
1125
+ }
1126
+
1127
+ return {
1128
+ conversationExcerpt: decodedConversationExcerpt,
1129
+ errorCount: decodedErrorCount,
1130
+ goal: decodedGoal,
1131
+ recentEvents: decodedRecentEvents,
1132
+ status: decodedStatus,
1133
+ toolCallCount: decodedToolCallCount,
1134
+ };
1135
+ }
1136
+ return null;
1137
+ }
1138
+
1139
+ /**
1140
+ * @type { SummaryTone }
1141
+ * @description Which prompt style was used to generate the summary
1142
+ */
1143
+ export type SummaryTone = 'conversational' | 'status-report';
1144
+
1145
+ export function decodeSummaryTone(rawInput: unknown): SummaryTone | null {
1146
+ switch (rawInput) {
1147
+ case 'conversational':
1148
+ case 'status-report':
1149
+ return rawInput;
1150
+ }
1151
+ return null;
1152
+ }
1153
+
1154
+ export function _decodeSummaryTone(rawInput: unknown): SummaryTone | undefined {
1155
+ switch (rawInput) {
1156
+ case 'conversational':
1157
+ case 'status-report':
1158
+ return rawInput;
1159
+ }
1160
+ return;
1161
+ }
1162
+
1163
+ /**
1164
+ * @type { SummaryResult }
1165
+ * @description Result returned by SessionSummarizer.summarize
1166
+ */
1167
+ export type SummaryResult = {
1168
+ /**
1169
+ * @description ISO 8601 timestamp of when this summary was generated
1170
+ * @type { string }
1171
+ * @memberof SummaryResult
1172
+ */
1173
+ generatedAt: string;
1174
+ /**
1175
+ * @description Plain-English summary text
1176
+ * @type { string }
1177
+ * @memberof SummaryResult
1178
+ */
1179
+ text: string;
1180
+ /**
1181
+ * @description Which prompt style produced this result
1182
+ * @type { SummaryTone }
1183
+ * @memberof SummaryResult
1184
+ */
1185
+ tone: SummaryTone;
1186
+ };
1187
+
1188
+ export function decodeSummaryResult(rawInput: unknown): SummaryResult | null {
1189
+ if (isJSON(rawInput)) {
1190
+ const decodedGeneratedAt = decodeString(rawInput['generatedAt']);
1191
+ const decodedText = decodeString(rawInput['text']);
1192
+ const decodedTone = decodeSummaryTone(rawInput['tone']);
1193
+
1194
+ if (decodedGeneratedAt === null || decodedText === null || decodedTone === null) {
1195
+ return null;
1196
+ }
1197
+
1198
+ return {
1199
+ generatedAt: decodedGeneratedAt,
1200
+ text: decodedText,
1201
+ tone: decodedTone,
1202
+ };
1203
+ }
1204
+ return null;
1205
+ }
1206
+
1207
+ /**
1208
+ * @type { DashboardTerminalRecord }
1209
+ * @description Slim terminal record returned by GET /api/terminals used to populate dashboard session cards
1210
+ */
1211
+ export type DashboardTerminalRecord = {
1212
+ /**
1213
+ * @description The command running in the terminal
1214
+ * @type { string }
1215
+ * @memberof DashboardTerminalRecord
1216
+ */
1217
+ command: string;
1218
+ /**
1219
+ * @description ISO 8601 timestamp when the terminal was created
1220
+ * @type { string }
1221
+ * @memberof DashboardTerminalRecord
1222
+ */
1223
+ createdAt: string;
1224
+ /**
1225
+ * @description Working directory of the terminal
1226
+ * @type { string }
1227
+ * @memberof DashboardTerminalRecord
1228
+ */
1229
+ cwd: string;
1230
+ /**
1231
+ * @description ISO 8601 timestamp when the terminal exited, or null if still running
1232
+ * @type { string }
1233
+ * @memberof DashboardTerminalRecord
1234
+ */
1235
+ exitedAt: string | null;
1236
+ /**
1237
+ * @description Unique terminal identifier
1238
+ * @type { string }
1239
+ * @memberof DashboardTerminalRecord
1240
+ */
1241
+ id: string;
1242
+ /**
1243
+ * @description Raw terminal status string from the server
1244
+ * @type { string }
1245
+ * @memberof DashboardTerminalRecord
1246
+ */
1247
+ status: string;
1248
+ };
1249
+
1250
+ export function decodeDashboardTerminalRecord(rawInput: unknown): DashboardTerminalRecord | null {
1251
+ if (isJSON(rawInput)) {
1252
+ const decodedCommand = decodeString(rawInput['command']);
1253
+ const decodedCreatedAt = decodeString(rawInput['createdAt']);
1254
+ const decodedCwd = decodeString(rawInput['cwd']);
1255
+ const decodedExitedAt = decodeString(rawInput['exitedAt']);
1256
+ const decodedId = decodeString(rawInput['id']);
1257
+ const decodedStatus = decodeString(rawInput['status']);
1258
+
1259
+ if (
1260
+ decodedCommand === null ||
1261
+ decodedCreatedAt === null ||
1262
+ decodedCwd === null ||
1263
+ decodedId === null ||
1264
+ decodedStatus === null
1265
+ ) {
1266
+ return null;
1267
+ }
1268
+
1269
+ return {
1270
+ command: decodedCommand,
1271
+ createdAt: decodedCreatedAt,
1272
+ cwd: decodedCwd,
1273
+ exitedAt: decodedExitedAt,
1274
+ id: decodedId,
1275
+ status: decodedStatus,
1276
+ };
1277
+ }
1278
+ return null;
1279
+ }
1280
+
1281
+ /**
1282
+ * @type { DashboardTerminalListResponse }
1283
+ * @description API response shape for GET /api/terminals as consumed by the dashboard store
1284
+ */
1285
+ export type DashboardTerminalListResponse = {
1286
+ /**
1287
+ * @description Array of slim terminal records
1288
+ * @type { DashboardTerminalRecord[] }
1289
+ * @memberof DashboardTerminalListResponse
1290
+ */
1291
+ terminals: DashboardTerminalRecord[];
1292
+ };
1293
+
1294
+ export function decodeDashboardTerminalListResponse(
1295
+ rawInput: unknown
1296
+ ): DashboardTerminalListResponse | null {
1297
+ if (isJSON(rawInput)) {
1298
+ const decodedTerminals = decodeArray(rawInput['terminals'], decodeDashboardTerminalRecord);
1299
+
1300
+ if (decodedTerminals === null) {
1301
+ return null;
1302
+ }
1303
+
1304
+ return {
1305
+ terminals: decodedTerminals,
1306
+ };
1307
+ }
1308
+ return null;
1309
+ }
1310
+
1311
+ /**
1312
+ * @type { ActivityEvent }
1313
+ * @description A single event captured from a Claude Code session WebSocket for the activity feed
1314
+ */
1315
+ export type ActivityEvent = {
1316
+ /**
1317
+ * @description Raw event payload with tool name, file path, command, etc.
1318
+ * @type { ActivityEventData }
1319
+ * @memberof ActivityEvent
1320
+ */
1321
+ data: ActivityEventData;
1322
+ /**
1323
+ * @description Unique event identifier (e.g. "evt-1")
1324
+ * @type { string }
1325
+ * @memberof ActivityEvent
1326
+ */
1327
+ id: string;
1328
+ /**
1329
+ * @description Display name of the project this event belongs to
1330
+ * @type { string }
1331
+ * @memberof ActivityEvent
1332
+ */
1333
+ projectName: string;
1334
+ /**
1335
+ * @description Claude Code session ID this event was received from
1336
+ * @type { string }
1337
+ * @memberof ActivityEvent
1338
+ */
1339
+ sessionId: string;
1340
+ /**
1341
+ * @description Whether this event has been included in an AI-generated summary
1342
+ * @type { boolean }
1343
+ * @memberof ActivityEvent
1344
+ */
1345
+ summarized: boolean;
1346
+ /**
1347
+ * @description ISO 8601 timestamp of the event
1348
+ * @type { string }
1349
+ * @memberof ActivityEvent
1350
+ */
1351
+ timestamp: string;
1352
+ /**
1353
+ * @description Event type discriminator (e.g. "tool_use", "tool_result", "user_message", "error")
1354
+ * @type { string }
1355
+ * @memberof ActivityEvent
1356
+ */
1357
+ type: string;
1358
+ };
1359
+
1360
+ export function decodeActivityEvent(rawInput: unknown): ActivityEvent | null {
1361
+ if (isJSON(rawInput)) {
1362
+ const decodedData = decodeActivityEventData(rawInput['data']);
1363
+ const decodedId = decodeString(rawInput['id']);
1364
+ const decodedProjectName = decodeString(rawInput['projectName']);
1365
+ const decodedSessionId = decodeString(rawInput['sessionId']);
1366
+ const decodedSummarized = decodeBoolean(rawInput['summarized']);
1367
+ const decodedTimestamp = decodeString(rawInput['timestamp']);
1368
+ const decodedType = decodeString(rawInput['type']);
1369
+
1370
+ if (
1371
+ decodedData === null ||
1372
+ decodedId === null ||
1373
+ decodedProjectName === null ||
1374
+ decodedSessionId === null ||
1375
+ decodedSummarized === null ||
1376
+ decodedTimestamp === null ||
1377
+ decodedType === null
1378
+ ) {
1379
+ return null;
1380
+ }
1381
+
1382
+ return {
1383
+ data: decodedData,
1384
+ id: decodedId,
1385
+ projectName: decodedProjectName,
1386
+ sessionId: decodedSessionId,
1387
+ summarized: decodedSummarized,
1388
+ timestamp: decodedTimestamp,
1389
+ type: decodedType,
1390
+ };
1391
+ }
1392
+ return null;
1393
+ }
1394
+
1395
+ /**
1396
+ * @type { ActivityEventData }
1397
+ * @description Raw event payload with tool name, file path, command, etc.
1398
+ */
1399
+ export type ActivityEventData = Record<string, unknown>;
1400
+
1401
+ export function decodeActivityEventData(rawInput: unknown): ActivityEventData | null {
1402
+ if (isJSON(rawInput)) {
1403
+ return {
1404
+ ...rawInput,
1405
+ };
1406
+ }
1407
+ return null;
1408
+ }
1409
+
1410
+ /**
1411
+ * @type { ActivitySummary }
1412
+ * @description An AI-generated rolling summary covering a batch of activity events
1413
+ */
1414
+ export type ActivitySummary = {
1415
+ /**
1416
+ * @description IDs of the events included in this summary
1417
+ * @type { string[] }
1418
+ * @memberof ActivitySummary
1419
+ */
1420
+ eventIds: string[];
1421
+ /**
1422
+ * @description Unique summary identifier (e.g. "sum-1")
1423
+ * @type { string }
1424
+ * @memberof ActivitySummary
1425
+ */
1426
+ id: string;
1427
+ /**
1428
+ * @description Project this summary covers
1429
+ * @type { string }
1430
+ * @memberof ActivitySummary
1431
+ */
1432
+ projectName: string;
1433
+ /**
1434
+ * @description AI-generated summary text
1435
+ * @type { string }
1436
+ * @memberof ActivitySummary
1437
+ */
1438
+ text: string;
1439
+ /**
1440
+ * @description ISO 8601 timestamp when the summary was generated
1441
+ * @type { string }
1442
+ * @memberof ActivitySummary
1443
+ */
1444
+ timestamp: string;
1445
+ };
1446
+
1447
+ export function decodeActivitySummary(rawInput: unknown): ActivitySummary | null {
1448
+ if (isJSON(rawInput)) {
1449
+ const decodedEventIds = decodeArray(rawInput['eventIds'], decodeString);
1450
+ const decodedId = decodeString(rawInput['id']);
1451
+ const decodedProjectName = decodeString(rawInput['projectName']);
1452
+ const decodedText = decodeString(rawInput['text']);
1453
+ const decodedTimestamp = decodeString(rawInput['timestamp']);
1454
+
1455
+ if (
1456
+ decodedEventIds === null ||
1457
+ decodedId === null ||
1458
+ decodedProjectName === null ||
1459
+ decodedText === null ||
1460
+ decodedTimestamp === null
1461
+ ) {
1462
+ return null;
1463
+ }
1464
+
1465
+ return {
1466
+ eventIds: decodedEventIds,
1467
+ id: decodedId,
1468
+ projectName: decodedProjectName,
1469
+ text: decodedText,
1470
+ timestamp: decodedTimestamp,
1471
+ };
1472
+ }
1473
+ return null;
1474
+ }
1475
+
1476
+ /**
1477
+ * @type { ActivitySummaryResult }
1478
+ * @description Result from the activity feed summarizer (NeuroLink generate call)
1479
+ */
1480
+ export type ActivitySummaryResult = {
1481
+ /**
1482
+ * @description Error message if summarization failed, or null on success
1483
+ * @type { string }
1484
+ * @memberof ActivitySummaryResult
1485
+ */
1486
+ error: string | null;
1487
+ /**
1488
+ * @description Summary text or fallback description
1489
+ * @type { string }
1490
+ * @memberof ActivitySummaryResult
1491
+ */
1492
+ text: string;
1493
+ };
1494
+
1495
+ export function decodeActivitySummaryResult(rawInput: unknown): ActivitySummaryResult | null {
1496
+ if (isJSON(rawInput)) {
1497
+ const decodedError = decodeString(rawInput['error']);
1498
+ const decodedText = decodeString(rawInput['text']);
1499
+
1500
+ if (decodedText === null) {
1501
+ return null;
1502
+ }
1503
+
1504
+ return {
1505
+ error: decodedError,
1506
+ text: decodedText,
1507
+ };
1508
+ }
1509
+ return null;
1510
+ }
1511
+
1512
+ /**
1513
+ * @type { ProviderStatus }
1514
+ * @description Configuration status of a single AI provider
1515
+ */
1516
+ export type ProviderStatus = {
1517
+ /**
1518
+ * @description Whether the provider has valid API credentials
1519
+ * @type { boolean }
1520
+ * @memberof ProviderStatus
1521
+ */
1522
+ configured: boolean;
1523
+ /**
1524
+ * @description Provider identifier (e.g. "google-ai", "anthropic")
1525
+ * @type { string }
1526
+ * @memberof ProviderStatus
1527
+ */
1528
+ id: string;
1529
+ /**
1530
+ * @description Human-readable provider name
1531
+ * @type { string }
1532
+ * @memberof ProviderStatus
1533
+ */
1534
+ label: string;
1535
+ };
1536
+
1537
+ export function decodeProviderStatus(rawInput: unknown): ProviderStatus | null {
1538
+ if (isJSON(rawInput)) {
1539
+ const decodedConfigured = decodeBoolean(rawInput['configured']);
1540
+ const decodedId = decodeString(rawInput['id']);
1541
+ const decodedLabel = decodeString(rawInput['label']);
1542
+
1543
+ if (decodedConfigured === null || decodedId === null || decodedLabel === null) {
1544
+ return null;
1545
+ }
1546
+
1547
+ return {
1548
+ configured: decodedConfigured,
1549
+ id: decodedId,
1550
+ label: decodedLabel,
1551
+ };
1552
+ }
1553
+ return null;
1554
+ }
1555
+
1556
+ /**
1557
+ * @type { ActivitySessionRecord }
1558
+ * @description A discovered active session that the activity store connects to via WebSocket
1559
+ */
1560
+ export type ActivitySessionRecord = {
1561
+ /**
1562
+ * @description ISO 8601 timestamp when the session was created
1563
+ * @type { string }
1564
+ * @memberof ActivitySessionRecord
1565
+ */
1566
+ createdAt: string;
1567
+ /**
1568
+ * @description Session identifier
1569
+ * @type { string }
1570
+ * @memberof ActivitySessionRecord
1571
+ */
1572
+ id: string;
1573
+ /**
1574
+ * @description ISO 8601 timestamp of last session modification
1575
+ * @type { string }
1576
+ * @memberof ActivitySessionRecord
1577
+ */
1578
+ modifiedAt: string;
1579
+ /**
1580
+ * @description Display name of the project
1581
+ * @type { string }
1582
+ * @memberof ActivitySessionRecord
1583
+ */
1584
+ projectName: string;
1585
+ /**
1586
+ * @description Full filesystem path of the project
1587
+ * @type { string }
1588
+ * @memberof ActivitySessionRecord
1589
+ */
1590
+ projectPath: string;
1591
+ };
1592
+
1593
+ export function decodeActivitySessionRecord(rawInput: unknown): ActivitySessionRecord | null {
1594
+ if (isJSON(rawInput)) {
1595
+ const decodedCreatedAt = decodeString(rawInput['createdAt']);
1596
+ const decodedId = decodeString(rawInput['id']);
1597
+ const decodedModifiedAt = decodeString(rawInput['modifiedAt']);
1598
+ const decodedProjectName = decodeString(rawInput['projectName']);
1599
+ const decodedProjectPath = decodeString(rawInput['projectPath']);
1600
+
1601
+ if (
1602
+ decodedCreatedAt === null ||
1603
+ decodedId === null ||
1604
+ decodedModifiedAt === null ||
1605
+ decodedProjectName === null ||
1606
+ decodedProjectPath === null
1607
+ ) {
1608
+ return null;
1609
+ }
1610
+
1611
+ return {
1612
+ createdAt: decodedCreatedAt,
1613
+ id: decodedId,
1614
+ modifiedAt: decodedModifiedAt,
1615
+ projectName: decodedProjectName,
1616
+ projectPath: decodedProjectPath,
1617
+ };
1618
+ }
1619
+ return null;
1620
+ }
1621
+
1622
+ /**
1623
+ * @type { NativeBridgeConfig }
1624
+ * @description Configuration returned by ShooterBridge.getConfig() in the native app WebView
1625
+ */
1626
+ export type NativeBridgeConfig = {
1627
+ /**
1628
+ * @description Base URL of the Shooter server
1629
+ * @type { string }
1630
+ * @memberof NativeBridgeConfig
1631
+ */
1632
+ serverUrl: string;
1633
+ /**
1634
+ * @description API key for authenticating with the server
1635
+ * @type { string }
1636
+ * @memberof NativeBridgeConfig
1637
+ */
1638
+ apiKey: string;
1639
+ };
1640
+
1641
+ export function decodeNativeBridgeConfig(rawInput: unknown): NativeBridgeConfig | null {
1642
+ if (isJSON(rawInput)) {
1643
+ const decodedServerUrl = decodeString(rawInput['serverUrl']);
1644
+ const decodedApiKey = decodeString(rawInput['apiKey']);
1645
+
1646
+ if (decodedServerUrl === null || decodedApiKey === null) {
1647
+ return null;
1648
+ }
1649
+
1650
+ return {
1651
+ serverUrl: decodedServerUrl,
1652
+ apiKey: decodedApiKey,
1653
+ };
1654
+ }
1655
+ return null;
1656
+ }