termdock 1.4.263 → 1.4.265

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 (329) hide show
  1. package/dist/client/assets/App-u-k6rrzb.js +156 -0
  2. package/dist/client/assets/App-u-k6rrzb.js.br +0 -0
  3. package/dist/client/assets/App-u-k6rrzb.js.gz +0 -0
  4. package/dist/client/assets/{ChangeWalkthroughPanel-CzrJ6gJP.js → ChangeWalkthroughPanel-BfPRxDiF.js} +2 -2
  5. package/dist/client/assets/ChangeWalkthroughPanel-BfPRxDiF.js.br +0 -0
  6. package/dist/client/assets/ChangeWalkthroughPanel-BfPRxDiF.js.gz +0 -0
  7. package/dist/client/assets/{DagPlayground-C7RaD0hD.js → DagPlayground-DIFzVISA.js} +1 -1
  8. package/dist/client/assets/DagPlayground-DIFzVISA.js.br +0 -0
  9. package/dist/client/assets/DagPlayground-DIFzVISA.js.gz +0 -0
  10. package/dist/client/assets/{DiffLab-Cl8rs4sk.js → DiffLab-gjCZJZHD.js} +1 -1
  11. package/dist/client/assets/DiffLab-gjCZJZHD.js.br +0 -0
  12. package/dist/client/assets/DiffLab-gjCZJZHD.js.gz +0 -0
  13. package/dist/client/assets/{DiffReview-D4715gv-.js → DiffReview-_Ehf5r7B.js} +1 -1
  14. package/dist/client/assets/DiffReview-_Ehf5r7B.js.br +0 -0
  15. package/dist/client/assets/DiffReview-_Ehf5r7B.js.gz +0 -0
  16. package/dist/client/assets/{DiffReviewLab-DelgnRri.js → DiffReviewLab-D5ZZmL0c.js} +1 -1
  17. package/dist/client/assets/DiffReviewLab-D5ZZmL0c.js.br +0 -0
  18. package/dist/client/assets/DiffReviewLab-D5ZZmL0c.js.gz +0 -0
  19. package/dist/client/assets/DiffViewer-CYn0xF79.js +37 -0
  20. package/dist/client/assets/DiffViewer-CYn0xF79.js.br +0 -0
  21. package/dist/client/assets/DiffViewer-CYn0xF79.js.gz +0 -0
  22. package/dist/client/assets/{ModelPreview-COGEMw6A.js → ModelPreview-CD59YJ_a.js} +1 -1
  23. package/dist/client/assets/ModelPreview-CD59YJ_a.js.br +0 -0
  24. package/dist/client/assets/ModelPreview-CD59YJ_a.js.gz +0 -0
  25. package/dist/client/assets/{RightSidebar-B4lwUjHl.js → RightSidebar-CeHCLhLy.js} +47 -47
  26. package/dist/client/assets/RightSidebar-CeHCLhLy.js.br +0 -0
  27. package/dist/client/assets/RightSidebar-CeHCLhLy.js.gz +0 -0
  28. package/dist/client/assets/{abnfDiagram-VCTEODGH-COWpaiS-.js → abnfDiagram-VCTEODGH-DSa1Ysa8.js} +1 -1
  29. package/dist/client/assets/abnfDiagram-VCTEODGH-DSa1Ysa8.js.br +0 -0
  30. package/dist/client/assets/abnfDiagram-VCTEODGH-DSa1Ysa8.js.gz +0 -0
  31. package/dist/client/assets/{app-DHnc6XU3.js → app-L3gaEB1i.js} +11 -11
  32. package/dist/client/assets/app-L3gaEB1i.js.br +0 -0
  33. package/dist/client/assets/app-L3gaEB1i.js.gz +0 -0
  34. package/dist/client/assets/{arc-B5ZIGNkR.js → arc-BheyZ1mz.js} +1 -1
  35. package/dist/client/assets/arc-BheyZ1mz.js.br +0 -0
  36. package/dist/client/assets/arc-BheyZ1mz.js.gz +0 -0
  37. package/dist/client/assets/{architectureDiagram-5GKGNRK7-D9KCt4gq.js → architectureDiagram-5GKGNRK7-BXlasfrQ.js} +1 -1
  38. package/dist/client/assets/architectureDiagram-5GKGNRK7-BXlasfrQ.js.br +0 -0
  39. package/dist/client/assets/architectureDiagram-5GKGNRK7-BXlasfrQ.js.gz +0 -0
  40. package/dist/client/assets/{blockDiagram-I7D4REHJ-wwBKtSqS.js → blockDiagram-I7D4REHJ-DwCl8uZj.js} +1 -1
  41. package/dist/client/assets/blockDiagram-I7D4REHJ-DwCl8uZj.js.br +0 -0
  42. package/dist/client/assets/blockDiagram-I7D4REHJ-DwCl8uZj.js.gz +0 -0
  43. package/dist/client/assets/{c4Diagram-7LVT6UL2-BSWUY2m8.js → c4Diagram-7LVT6UL2-Coq6KCH6.js} +1 -1
  44. package/dist/client/assets/c4Diagram-7LVT6UL2-Coq6KCH6.js.br +0 -0
  45. package/dist/client/assets/c4Diagram-7LVT6UL2-Coq6KCH6.js.gz +0 -0
  46. package/dist/client/assets/channel-Cm5edA5B.js +1 -0
  47. package/dist/client/assets/{chunk-2Q5K7J3B-C6tBCDpG.js → chunk-2Q5K7J3B-CUFYwTko.js} +1 -1
  48. package/dist/client/assets/{chunk-5VM5RSS4-BboWsUGn.js → chunk-5VM5RSS4-COcgIAqN.js} +1 -1
  49. package/dist/client/assets/{chunk-F27PBJKO-t8LC2p6f.js → chunk-F27PBJKO-BWhCCTPq.js} +1 -1
  50. package/dist/client/assets/chunk-F27PBJKO-BWhCCTPq.js.br +0 -0
  51. package/dist/client/assets/chunk-F27PBJKO-BWhCCTPq.js.gz +0 -0
  52. package/dist/client/assets/{chunk-IMKFNOWR-BLpilR76.js → chunk-IMKFNOWR-C7TuNlR1.js} +1 -1
  53. package/dist/client/assets/chunk-IMKFNOWR-C7TuNlR1.js.br +0 -0
  54. package/dist/client/assets/chunk-IMKFNOWR-C7TuNlR1.js.gz +0 -0
  55. package/dist/client/assets/{chunk-JWPE2WC7-UAJgqWvI.js → chunk-JWPE2WC7-DjNz0bcc.js} +1 -1
  56. package/dist/client/assets/{chunk-POPQ4Y6H-CpVzK8jo.js → chunk-POPQ4Y6H-TT5BNkJr.js} +1 -1
  57. package/dist/client/assets/{chunk-SVP7TREG-irhVxs6O.js → chunk-SVP7TREG-DK9JcY1h.js} +1 -1
  58. package/dist/client/assets/chunk-SVP7TREG-DK9JcY1h.js.br +0 -0
  59. package/dist/client/assets/chunk-SVP7TREG-DK9JcY1h.js.gz +0 -0
  60. package/dist/client/assets/{chunk-TICWLB2K-Au4c5Tnt.js → chunk-TICWLB2K-B19MlAfZ.js} +1 -1
  61. package/dist/client/assets/chunk-TICWLB2K-B19MlAfZ.js.br +0 -0
  62. package/dist/client/assets/chunk-TICWLB2K-B19MlAfZ.js.gz +0 -0
  63. package/dist/client/assets/{chunk-XXDRQBXY-NlJK-TD4.js → chunk-XXDRQBXY-BI9irqHN.js} +1 -1
  64. package/dist/client/assets/classDiagram-ZZMXUADV-o0rDEd1E.js +1 -0
  65. package/dist/client/assets/classDiagram-v2-VYDZK3BY-o0rDEd1E.js +1 -0
  66. package/dist/client/assets/{cose-bilkent-JH36ORCC-CBucN9Xa.js → cose-bilkent-JH36ORCC-DSg7_PN1.js} +1 -1
  67. package/dist/client/assets/cose-bilkent-JH36ORCC-DSg7_PN1.js.br +0 -0
  68. package/dist/client/assets/cose-bilkent-JH36ORCC-DSg7_PN1.js.gz +0 -0
  69. package/dist/client/assets/{cynefin-OW5HDTMX-DBkqdsPg.js → cynefin-OW5HDTMX-uC7egYG5.js} +1 -1
  70. package/dist/client/assets/cynefin-OW5HDTMX-uC7egYG5.js.br +0 -0
  71. package/dist/client/assets/cynefin-OW5HDTMX-uC7egYG5.js.gz +0 -0
  72. package/dist/client/assets/{cynefinDiagram-5FMLGOSQ-g-mNhqWP.js → cynefinDiagram-5FMLGOSQ-C-XGZrRz.js} +1 -1
  73. package/dist/client/assets/cynefinDiagram-5FMLGOSQ-C-XGZrRz.js.br +0 -0
  74. package/dist/client/assets/cynefinDiagram-5FMLGOSQ-C-XGZrRz.js.gz +0 -0
  75. package/dist/client/assets/{dagre-GXQ25YYZ-aMhTipKP.js → dagre-GXQ25YYZ-B5YJUKWx.js} +1 -1
  76. package/dist/client/assets/dagre-GXQ25YYZ-B5YJUKWx.js.br +0 -0
  77. package/dist/client/assets/dagre-GXQ25YYZ-B5YJUKWx.js.gz +0 -0
  78. package/dist/client/assets/{diagram-S7CK7UJ4-FTXlqV5C.js → diagram-S7CK7UJ4-Bnyrx-jR.js} +1 -1
  79. package/dist/client/assets/diagram-S7CK7UJ4-Bnyrx-jR.js.br +0 -0
  80. package/dist/client/assets/diagram-S7CK7UJ4-Bnyrx-jR.js.gz +0 -0
  81. package/dist/client/assets/{diagram-UQ7AKVKN-CWKlCxDh.js → diagram-UQ7AKVKN-CUp603fW.js} +1 -1
  82. package/dist/client/assets/diagram-UQ7AKVKN-CUp603fW.js.br +0 -0
  83. package/dist/client/assets/diagram-UQ7AKVKN-CUp603fW.js.gz +0 -0
  84. package/dist/client/assets/{diagram-VSXAHHWV-DuCN1zfS.js → diagram-VSXAHHWV-uIXayijw.js} +1 -1
  85. package/dist/client/assets/diagram-VSXAHHWV-uIXayijw.js.br +0 -0
  86. package/dist/client/assets/diagram-VSXAHHWV-uIXayijw.js.gz +0 -0
  87. package/dist/client/assets/{diagram-VX7I27RA-BYYvrnH1.js → diagram-VX7I27RA-6J1oIMOb.js} +1 -1
  88. package/dist/client/assets/diagram-VX7I27RA-6J1oIMOb.js.br +0 -0
  89. package/dist/client/assets/diagram-VX7I27RA-6J1oIMOb.js.gz +0 -0
  90. package/dist/client/assets/{diagram-Z3DM3KII-BJ_UXuWt.js → diagram-Z3DM3KII-C0WgBWqD.js} +1 -1
  91. package/dist/client/assets/diagram-Z3DM3KII-C0WgBWqD.js.br +0 -0
  92. package/dist/client/assets/diagram-Z3DM3KII-C0WgBWqD.js.gz +0 -0
  93. package/dist/client/assets/{ebnfDiagram-PWID7BFC-BLb31-CG.js → ebnfDiagram-PWID7BFC-B5DEM-bI.js} +1 -1
  94. package/dist/client/assets/ebnfDiagram-PWID7BFC-B5DEM-bI.js.br +0 -0
  95. package/dist/client/assets/ebnfDiagram-PWID7BFC-B5DEM-bI.js.gz +0 -0
  96. package/dist/client/assets/{erDiagram-RLTQ6QDP-Dt5T_zZr.js → erDiagram-RLTQ6QDP-BzxCr-aA.js} +1 -1
  97. package/dist/client/assets/erDiagram-RLTQ6QDP-BzxCr-aA.js.br +0 -0
  98. package/dist/client/assets/erDiagram-RLTQ6QDP-BzxCr-aA.js.gz +0 -0
  99. package/dist/client/assets/{flowDiagram-HODETNUW-CC8Rn9fH.js → flowDiagram-HODETNUW-DHjjUn1K.js} +1 -1
  100. package/dist/client/assets/flowDiagram-HODETNUW-DHjjUn1K.js.br +0 -0
  101. package/dist/client/assets/flowDiagram-HODETNUW-DHjjUn1K.js.gz +0 -0
  102. package/dist/client/assets/{ganttDiagram-EL5Y4UJY-Bki2eBkC.js → ganttDiagram-EL5Y4UJY-wH7qPWQb.js} +1 -1
  103. package/dist/client/assets/ganttDiagram-EL5Y4UJY-wH7qPWQb.js.br +0 -0
  104. package/dist/client/assets/ganttDiagram-EL5Y4UJY-wH7qPWQb.js.gz +0 -0
  105. package/dist/client/assets/{gestureArbiter-BX-zjr4n.js → gestureArbiter-D6RWPMQo.js} +1 -1
  106. package/dist/client/assets/gestureArbiter-D6RWPMQo.js.br +0 -0
  107. package/dist/client/assets/gestureArbiter-D6RWPMQo.js.gz +0 -0
  108. package/dist/client/assets/{gitGraphDiagram-WWUBYQGX-DlR3hAyj.js → gitGraphDiagram-WWUBYQGX-CcY9a3m3.js} +1 -1
  109. package/dist/client/assets/gitGraphDiagram-WWUBYQGX-CcY9a3m3.js.br +0 -0
  110. package/dist/client/assets/gitGraphDiagram-WWUBYQGX-CcY9a3m3.js.gz +0 -0
  111. package/dist/client/assets/{index-ddQQwU7M.css → index-BOv8uqyA.css} +1 -1
  112. package/dist/client/assets/index-BOv8uqyA.css.br +0 -0
  113. package/dist/client/assets/{index-ddQQwU7M.css.gz → index-BOv8uqyA.css.gz} +0 -0
  114. package/dist/client/assets/{infoDiagram-27XIBGKW-C-5zXbqy.js → infoDiagram-27XIBGKW-0ylnBr_8.js} +1 -1
  115. package/dist/client/assets/{ishikawaDiagram-5VMMS53U-D_KAjhux.js → ishikawaDiagram-5VMMS53U-CjrSMq_a.js} +1 -1
  116. package/dist/client/assets/ishikawaDiagram-5VMMS53U-CjrSMq_a.js.br +0 -0
  117. package/dist/client/assets/{ishikawaDiagram-5VMMS53U-D_KAjhux.js.gz → ishikawaDiagram-5VMMS53U-CjrSMq_a.js.gz} +0 -0
  118. package/dist/client/assets/{journeyDiagram-3NMN7TZE-DasVMhbH.js → journeyDiagram-3NMN7TZE-DrzTWkK-.js} +1 -1
  119. package/dist/client/assets/journeyDiagram-3NMN7TZE-DrzTWkK-.js.br +0 -0
  120. package/dist/client/assets/journeyDiagram-3NMN7TZE-DrzTWkK-.js.gz +0 -0
  121. package/dist/client/assets/{kanban-definition-UXKFOSKX-BeDZ9x71.js → kanban-definition-UXKFOSKX-D9CCE6r8.js} +1 -1
  122. package/dist/client/assets/kanban-definition-UXKFOSKX-D9CCE6r8.js.br +0 -0
  123. package/dist/client/assets/kanban-definition-UXKFOSKX-D9CCE6r8.js.gz +0 -0
  124. package/dist/client/assets/{layout-BonFkvNe.js → layout-BjxwetPy.js} +1 -1
  125. package/dist/client/assets/layout-BjxwetPy.js.br +0 -0
  126. package/dist/client/assets/layout-BjxwetPy.js.gz +0 -0
  127. package/dist/client/assets/{linear-amKhUz-E.js → linear-C0mh26-a.js} +1 -1
  128. package/dist/client/assets/linear-C0mh26-a.js.br +0 -0
  129. package/dist/client/assets/linear-C0mh26-a.js.gz +0 -0
  130. package/dist/client/assets/{mermaid.core-C743W_cw.js → mermaid.core-CWIVZHBb.js} +5 -5
  131. package/dist/client/assets/mermaid.core-CWIVZHBb.js.br +0 -0
  132. package/dist/client/assets/mermaid.core-CWIVZHBb.js.gz +0 -0
  133. package/dist/client/assets/{mindmap-definition-YA3MSWOX-dnSMIb-2.js → mindmap-definition-YA3MSWOX-BGo9fKR8.js} +1 -1
  134. package/dist/client/assets/mindmap-definition-YA3MSWOX-BGo9fKR8.js.br +0 -0
  135. package/dist/client/assets/mindmap-definition-YA3MSWOX-BGo9fKR8.js.gz +0 -0
  136. package/dist/client/assets/{passwordBootstrap-Z-zMfeRE.js → passwordBootstrap-CHyPbAR9.js} +2 -2
  137. package/dist/client/assets/passwordBootstrap-CHyPbAR9.js.br +0 -0
  138. package/dist/client/assets/passwordBootstrap-CHyPbAR9.js.gz +0 -0
  139. package/dist/client/assets/{pegDiagram-XKGWAZYB-BMMrDjH3.js → pegDiagram-XKGWAZYB-0Nb1LL5B.js} +1 -1
  140. package/dist/client/assets/pegDiagram-XKGWAZYB-0Nb1LL5B.js.br +0 -0
  141. package/dist/client/assets/pegDiagram-XKGWAZYB-0Nb1LL5B.js.gz +0 -0
  142. package/dist/client/assets/{pieDiagram-E7YTZNPT-CGZ4yhWL.js → pieDiagram-E7YTZNPT-84u6YN0w.js} +1 -1
  143. package/dist/client/assets/pieDiagram-E7YTZNPT-84u6YN0w.js.br +0 -0
  144. package/dist/client/assets/pieDiagram-E7YTZNPT-84u6YN0w.js.gz +0 -0
  145. package/dist/client/assets/{quadrantDiagram-AXDQQJYC-BfUxBRZx.js → quadrantDiagram-AXDQQJYC-BUHd3sQq.js} +1 -1
  146. package/dist/client/assets/quadrantDiagram-AXDQQJYC-BUHd3sQq.js.br +0 -0
  147. package/dist/client/assets/quadrantDiagram-AXDQQJYC-BUHd3sQq.js.gz +0 -0
  148. package/dist/client/assets/{railroadDiagram-O6MQD6OU-DRtstXqY.js → railroadDiagram-O6MQD6OU-CENx-rxh.js} +1 -1
  149. package/dist/client/assets/railroadDiagram-O6MQD6OU-CENx-rxh.js.br +0 -0
  150. package/dist/client/assets/railroadDiagram-O6MQD6OU-CENx-rxh.js.gz +0 -0
  151. package/dist/client/assets/{referenceLongPress-OgQ-br0J.js → referenceLongPress-D9JDfvlW.js} +1 -1
  152. package/dist/client/assets/referenceLongPress-D9JDfvlW.js.br +0 -0
  153. package/dist/client/assets/referenceLongPress-D9JDfvlW.js.gz +0 -0
  154. package/dist/client/assets/{requirementDiagram-BXWQKSXE-jIWvwxYZ.js → requirementDiagram-BXWQKSXE-CBQ0Ny7q.js} +1 -1
  155. package/dist/client/assets/requirementDiagram-BXWQKSXE-CBQ0Ny7q.js.br +0 -0
  156. package/dist/client/assets/requirementDiagram-BXWQKSXE-CBQ0Ny7q.js.gz +0 -0
  157. package/dist/client/assets/{sankeyDiagram-P5KCCOFB-CmV9o38-.js → sankeyDiagram-P5KCCOFB-3bBhyXPE.js} +1 -1
  158. package/dist/client/assets/sankeyDiagram-P5KCCOFB-3bBhyXPE.js.br +0 -0
  159. package/dist/client/assets/sankeyDiagram-P5KCCOFB-3bBhyXPE.js.gz +0 -0
  160. package/dist/client/assets/{sequenceDiagram-WJ2MYXX4-C37VhGXR.js → sequenceDiagram-WJ2MYXX4-BK6pizap.js} +1 -1
  161. package/dist/client/assets/sequenceDiagram-WJ2MYXX4-BK6pizap.js.br +0 -0
  162. package/dist/client/assets/sequenceDiagram-WJ2MYXX4-BK6pizap.js.gz +0 -0
  163. package/dist/client/assets/{sizeCapture-INFHLROL-D3aZUvMq.js → sizeCapture-INFHLROL-3qlKfimL.js} +1 -1
  164. package/dist/client/assets/sizeCapture-INFHLROL-3qlKfimL.js.br +0 -0
  165. package/dist/client/assets/sizeCapture-INFHLROL-3qlKfimL.js.gz +0 -0
  166. package/dist/client/assets/{sparkles-BwJHncXI.js → sparkles-CT3DLxs9.js} +1 -1
  167. package/dist/client/assets/{stateDiagram-D77RDMKH-B3W9KWRf.js → stateDiagram-D77RDMKH-DJ5Eljth.js} +1 -1
  168. package/dist/client/assets/stateDiagram-D77RDMKH-DJ5Eljth.js.br +0 -0
  169. package/dist/client/assets/stateDiagram-D77RDMKH-DJ5Eljth.js.gz +0 -0
  170. package/dist/client/assets/stateDiagram-v2-MP3YSRHH-IzUrIY-v.js +1 -0
  171. package/dist/client/assets/{swimlanes-42K2YHIH-CgFI-TZF.js → swimlanes-42K2YHIH-D2LOfXqZ.js} +1 -1
  172. package/dist/client/assets/swimlanes-42K2YHIH-D2LOfXqZ.js.br +0 -0
  173. package/dist/client/assets/swimlanes-42K2YHIH-D2LOfXqZ.js.gz +0 -0
  174. package/dist/client/assets/swimlanesDiagram-VR7AAH4N-fxwyakfK.js +8 -0
  175. package/dist/client/assets/{timeline-definition-24CTP7MA-Ad00a495.js → timeline-definition-24CTP7MA-CXi-wBS0.js} +1 -1
  176. package/dist/client/assets/timeline-definition-24CTP7MA-CXi-wBS0.js.br +0 -0
  177. package/dist/client/assets/timeline-definition-24CTP7MA-CXi-wBS0.js.gz +0 -0
  178. package/dist/client/assets/{triangle-alert-DDQ67k7N.js → triangle-alert-C6No3Q8u.js} +1 -1
  179. package/dist/client/assets/{vennDiagram-4TSXK5OY-CSawzFTi.js → vennDiagram-4TSXK5OY-CIz0V4J6.js} +1 -1
  180. package/dist/client/assets/vennDiagram-4TSXK5OY-CIz0V4J6.js.br +0 -0
  181. package/dist/client/assets/vennDiagram-4TSXK5OY-CIz0V4J6.js.gz +0 -0
  182. package/dist/client/assets/{wardleyDiagram-VM6X3IG4-Emoh-n_o.js → wardleyDiagram-VM6X3IG4-B0UrTqd3.js} +1 -1
  183. package/dist/client/assets/wardleyDiagram-VM6X3IG4-B0UrTqd3.js.br +0 -0
  184. package/dist/client/assets/wardleyDiagram-VM6X3IG4-B0UrTqd3.js.gz +0 -0
  185. package/dist/client/assets/{xychartDiagram-S5SC5T6Z-BPgImmcN.js → xychartDiagram-S5SC5T6Z-CGda0vcy.js} +1 -1
  186. package/dist/client/assets/xychartDiagram-S5SC5T6Z-CGda0vcy.js.br +0 -0
  187. package/dist/client/assets/xychartDiagram-S5SC5T6Z-CGda0vcy.js.gz +0 -0
  188. package/dist/client/index.html +2 -2
  189. package/dist/client/index.html.br +0 -0
  190. package/dist/client/index.html.gz +0 -0
  191. package/dist/client/sw.js +1 -1
  192. package/dist/client/sw.js.br +0 -0
  193. package/dist/client/sw.js.gz +0 -0
  194. package/dist/server/android/recording.js +178 -0
  195. package/dist/server/cli.js +20 -0
  196. package/dist/server/entry.js +5 -2
  197. package/dist/server/routes/android.js +55 -0
  198. package/dist/server/routes/filesystem.js +13 -3
  199. package/dist/server/routes/terminal.js +30 -2
  200. package/dist/server/supervisionHandoff.js +33 -0
  201. package/dist/server/supervisor.js +6 -3
  202. package/dist/server/utils/enableSupervision.js +80 -0
  203. package/dist/server/utils/serverHealth.js +28 -3
  204. package/dist/server/utils/settings.js +1 -1
  205. package/dist/server/utils/startupFailure.js +16 -0
  206. package/dist/server/utils/supervisorProtocol.js +5 -0
  207. package/package.json +1 -1
  208. package/runtime-manifest.json +3 -3
  209. package/dist/client/assets/App-Be0tsVAS.js +0 -156
  210. package/dist/client/assets/App-Be0tsVAS.js.br +0 -0
  211. package/dist/client/assets/App-Be0tsVAS.js.gz +0 -0
  212. package/dist/client/assets/ChangeWalkthroughPanel-CzrJ6gJP.js.br +0 -0
  213. package/dist/client/assets/ChangeWalkthroughPanel-CzrJ6gJP.js.gz +0 -0
  214. package/dist/client/assets/DagPlayground-C7RaD0hD.js.br +0 -0
  215. package/dist/client/assets/DagPlayground-C7RaD0hD.js.gz +0 -0
  216. package/dist/client/assets/DiffLab-Cl8rs4sk.js.br +0 -0
  217. package/dist/client/assets/DiffLab-Cl8rs4sk.js.gz +0 -0
  218. package/dist/client/assets/DiffReview-D4715gv-.js.br +0 -0
  219. package/dist/client/assets/DiffReview-D4715gv-.js.gz +0 -0
  220. package/dist/client/assets/DiffReviewLab-DelgnRri.js.br +0 -0
  221. package/dist/client/assets/DiffReviewLab-DelgnRri.js.gz +0 -0
  222. package/dist/client/assets/DiffViewer-DxGkdCpG.js +0 -36
  223. package/dist/client/assets/DiffViewer-DxGkdCpG.js.br +0 -0
  224. package/dist/client/assets/DiffViewer-DxGkdCpG.js.gz +0 -0
  225. package/dist/client/assets/ModelPreview-COGEMw6A.js.br +0 -0
  226. package/dist/client/assets/ModelPreview-COGEMw6A.js.gz +0 -0
  227. package/dist/client/assets/RightSidebar-B4lwUjHl.js.br +0 -0
  228. package/dist/client/assets/RightSidebar-B4lwUjHl.js.gz +0 -0
  229. package/dist/client/assets/abnfDiagram-VCTEODGH-COWpaiS-.js.br +0 -0
  230. package/dist/client/assets/abnfDiagram-VCTEODGH-COWpaiS-.js.gz +0 -0
  231. package/dist/client/assets/app-DHnc6XU3.js.br +0 -0
  232. package/dist/client/assets/app-DHnc6XU3.js.gz +0 -0
  233. package/dist/client/assets/arc-B5ZIGNkR.js.br +0 -0
  234. package/dist/client/assets/arc-B5ZIGNkR.js.gz +0 -0
  235. package/dist/client/assets/architectureDiagram-5GKGNRK7-D9KCt4gq.js.br +0 -0
  236. package/dist/client/assets/architectureDiagram-5GKGNRK7-D9KCt4gq.js.gz +0 -0
  237. package/dist/client/assets/blockDiagram-I7D4REHJ-wwBKtSqS.js.br +0 -0
  238. package/dist/client/assets/blockDiagram-I7D4REHJ-wwBKtSqS.js.gz +0 -0
  239. package/dist/client/assets/c4Diagram-7LVT6UL2-BSWUY2m8.js.br +0 -0
  240. package/dist/client/assets/c4Diagram-7LVT6UL2-BSWUY2m8.js.gz +0 -0
  241. package/dist/client/assets/channel-TGPTnJaC.js +0 -1
  242. package/dist/client/assets/chunk-F27PBJKO-t8LC2p6f.js.br +0 -0
  243. package/dist/client/assets/chunk-F27PBJKO-t8LC2p6f.js.gz +0 -0
  244. package/dist/client/assets/chunk-IMKFNOWR-BLpilR76.js.br +0 -0
  245. package/dist/client/assets/chunk-IMKFNOWR-BLpilR76.js.gz +0 -0
  246. package/dist/client/assets/chunk-SVP7TREG-irhVxs6O.js.br +0 -0
  247. package/dist/client/assets/chunk-SVP7TREG-irhVxs6O.js.gz +0 -0
  248. package/dist/client/assets/chunk-TICWLB2K-Au4c5Tnt.js.br +0 -0
  249. package/dist/client/assets/chunk-TICWLB2K-Au4c5Tnt.js.gz +0 -0
  250. package/dist/client/assets/classDiagram-ZZMXUADV-ECZW8RQZ.js +0 -1
  251. package/dist/client/assets/classDiagram-v2-VYDZK3BY-ECZW8RQZ.js +0 -1
  252. package/dist/client/assets/cose-bilkent-JH36ORCC-CBucN9Xa.js.br +0 -0
  253. package/dist/client/assets/cose-bilkent-JH36ORCC-CBucN9Xa.js.gz +0 -0
  254. package/dist/client/assets/cynefin-OW5HDTMX-DBkqdsPg.js.br +0 -0
  255. package/dist/client/assets/cynefin-OW5HDTMX-DBkqdsPg.js.gz +0 -0
  256. package/dist/client/assets/cynefinDiagram-5FMLGOSQ-g-mNhqWP.js.br +0 -0
  257. package/dist/client/assets/cynefinDiagram-5FMLGOSQ-g-mNhqWP.js.gz +0 -0
  258. package/dist/client/assets/dagre-GXQ25YYZ-aMhTipKP.js.br +0 -0
  259. package/dist/client/assets/dagre-GXQ25YYZ-aMhTipKP.js.gz +0 -0
  260. package/dist/client/assets/diagram-S7CK7UJ4-FTXlqV5C.js.br +0 -0
  261. package/dist/client/assets/diagram-S7CK7UJ4-FTXlqV5C.js.gz +0 -0
  262. package/dist/client/assets/diagram-UQ7AKVKN-CWKlCxDh.js.br +0 -0
  263. package/dist/client/assets/diagram-UQ7AKVKN-CWKlCxDh.js.gz +0 -0
  264. package/dist/client/assets/diagram-VSXAHHWV-DuCN1zfS.js.br +0 -0
  265. package/dist/client/assets/diagram-VSXAHHWV-DuCN1zfS.js.gz +0 -0
  266. package/dist/client/assets/diagram-VX7I27RA-BYYvrnH1.js.br +0 -0
  267. package/dist/client/assets/diagram-VX7I27RA-BYYvrnH1.js.gz +0 -0
  268. package/dist/client/assets/diagram-Z3DM3KII-BJ_UXuWt.js.br +0 -0
  269. package/dist/client/assets/diagram-Z3DM3KII-BJ_UXuWt.js.gz +0 -0
  270. package/dist/client/assets/ebnfDiagram-PWID7BFC-BLb31-CG.js.br +0 -0
  271. package/dist/client/assets/ebnfDiagram-PWID7BFC-BLb31-CG.js.gz +0 -0
  272. package/dist/client/assets/erDiagram-RLTQ6QDP-Dt5T_zZr.js.br +0 -0
  273. package/dist/client/assets/erDiagram-RLTQ6QDP-Dt5T_zZr.js.gz +0 -0
  274. package/dist/client/assets/flowDiagram-HODETNUW-CC8Rn9fH.js.br +0 -0
  275. package/dist/client/assets/flowDiagram-HODETNUW-CC8Rn9fH.js.gz +0 -0
  276. package/dist/client/assets/ganttDiagram-EL5Y4UJY-Bki2eBkC.js.br +0 -0
  277. package/dist/client/assets/ganttDiagram-EL5Y4UJY-Bki2eBkC.js.gz +0 -0
  278. package/dist/client/assets/gestureArbiter-BX-zjr4n.js.br +0 -0
  279. package/dist/client/assets/gestureArbiter-BX-zjr4n.js.gz +0 -0
  280. package/dist/client/assets/gitGraphDiagram-WWUBYQGX-DlR3hAyj.js.br +0 -0
  281. package/dist/client/assets/gitGraphDiagram-WWUBYQGX-DlR3hAyj.js.gz +0 -0
  282. package/dist/client/assets/index-ddQQwU7M.css.br +0 -0
  283. package/dist/client/assets/ishikawaDiagram-5VMMS53U-D_KAjhux.js.br +0 -0
  284. package/dist/client/assets/journeyDiagram-3NMN7TZE-DasVMhbH.js.br +0 -0
  285. package/dist/client/assets/journeyDiagram-3NMN7TZE-DasVMhbH.js.gz +0 -0
  286. package/dist/client/assets/kanban-definition-UXKFOSKX-BeDZ9x71.js.br +0 -0
  287. package/dist/client/assets/kanban-definition-UXKFOSKX-BeDZ9x71.js.gz +0 -0
  288. package/dist/client/assets/layout-BonFkvNe.js.br +0 -0
  289. package/dist/client/assets/layout-BonFkvNe.js.gz +0 -0
  290. package/dist/client/assets/linear-amKhUz-E.js.br +0 -0
  291. package/dist/client/assets/linear-amKhUz-E.js.gz +0 -0
  292. package/dist/client/assets/mermaid.core-C743W_cw.js.br +0 -0
  293. package/dist/client/assets/mermaid.core-C743W_cw.js.gz +0 -0
  294. package/dist/client/assets/mindmap-definition-YA3MSWOX-dnSMIb-2.js.br +0 -0
  295. package/dist/client/assets/mindmap-definition-YA3MSWOX-dnSMIb-2.js.gz +0 -0
  296. package/dist/client/assets/passwordBootstrap-Z-zMfeRE.js.br +0 -0
  297. package/dist/client/assets/passwordBootstrap-Z-zMfeRE.js.gz +0 -0
  298. package/dist/client/assets/pegDiagram-XKGWAZYB-BMMrDjH3.js.br +0 -0
  299. package/dist/client/assets/pegDiagram-XKGWAZYB-BMMrDjH3.js.gz +0 -0
  300. package/dist/client/assets/pieDiagram-E7YTZNPT-CGZ4yhWL.js.br +0 -0
  301. package/dist/client/assets/pieDiagram-E7YTZNPT-CGZ4yhWL.js.gz +0 -0
  302. package/dist/client/assets/quadrantDiagram-AXDQQJYC-BfUxBRZx.js.br +0 -0
  303. package/dist/client/assets/quadrantDiagram-AXDQQJYC-BfUxBRZx.js.gz +0 -0
  304. package/dist/client/assets/railroadDiagram-O6MQD6OU-DRtstXqY.js.br +0 -0
  305. package/dist/client/assets/railroadDiagram-O6MQD6OU-DRtstXqY.js.gz +0 -0
  306. package/dist/client/assets/referenceLongPress-OgQ-br0J.js.br +0 -0
  307. package/dist/client/assets/referenceLongPress-OgQ-br0J.js.gz +0 -0
  308. package/dist/client/assets/requirementDiagram-BXWQKSXE-jIWvwxYZ.js.br +0 -0
  309. package/dist/client/assets/requirementDiagram-BXWQKSXE-jIWvwxYZ.js.gz +0 -0
  310. package/dist/client/assets/sankeyDiagram-P5KCCOFB-CmV9o38-.js.br +0 -0
  311. package/dist/client/assets/sankeyDiagram-P5KCCOFB-CmV9o38-.js.gz +0 -0
  312. package/dist/client/assets/sequenceDiagram-WJ2MYXX4-C37VhGXR.js.br +0 -0
  313. package/dist/client/assets/sequenceDiagram-WJ2MYXX4-C37VhGXR.js.gz +0 -0
  314. package/dist/client/assets/sizeCapture-INFHLROL-D3aZUvMq.js.br +0 -0
  315. package/dist/client/assets/sizeCapture-INFHLROL-D3aZUvMq.js.gz +0 -0
  316. package/dist/client/assets/stateDiagram-D77RDMKH-B3W9KWRf.js.br +0 -0
  317. package/dist/client/assets/stateDiagram-D77RDMKH-B3W9KWRf.js.gz +0 -0
  318. package/dist/client/assets/stateDiagram-v2-MP3YSRHH-D9yMXUEN.js +0 -1
  319. package/dist/client/assets/swimlanes-42K2YHIH-CgFI-TZF.js.br +0 -0
  320. package/dist/client/assets/swimlanes-42K2YHIH-CgFI-TZF.js.gz +0 -0
  321. package/dist/client/assets/swimlanesDiagram-VR7AAH4N-DfgFmAr4.js +0 -8
  322. package/dist/client/assets/timeline-definition-24CTP7MA-Ad00a495.js.br +0 -0
  323. package/dist/client/assets/timeline-definition-24CTP7MA-Ad00a495.js.gz +0 -0
  324. package/dist/client/assets/vennDiagram-4TSXK5OY-CSawzFTi.js.br +0 -0
  325. package/dist/client/assets/vennDiagram-4TSXK5OY-CSawzFTi.js.gz +0 -0
  326. package/dist/client/assets/wardleyDiagram-VM6X3IG4-Emoh-n_o.js.br +0 -0
  327. package/dist/client/assets/wardleyDiagram-VM6X3IG4-Emoh-n_o.js.gz +0 -0
  328. package/dist/client/assets/xychartDiagram-S5SC5T6Z-BPgImmcN.js.br +0 -0
  329. package/dist/client/assets/xychartDiagram-S5SC5T6Z-BPgImmcN.js.gz +0 -0
@@ -31,6 +31,8 @@ import { fileURLToPath } from 'url';
31
31
  import { WebSocketServer } from 'ws';
32
32
  import terminalRoutes, { handleTerminalWebSocket, handleControlWebSocket, resolveStableFederationSessionId } from './routes/terminal.js';
33
33
  import androidRoutes, { handleAndroidWebSocket } from './routes/android.js';
34
+ import { exitWithStartupFailure } from './utils/startupFailure.js';
35
+ import { androidRecordings } from './android/recording.js';
34
36
  import { stopAllScrcpySessions } from './android/scrcpy.js';
35
37
  import filesystemRoutes from './routes/filesystem.js';
36
38
  import authRoutes from './routes/auth.js';
@@ -463,7 +465,7 @@ export function startServer(options = {}) {
463
465
  server.once('close', () => { clearInterval(desktopRoutesTimer); for (const target of dynamicDirectTargets.values())
464
466
  target.close(); });
465
467
  server.once('close', () => relayRouter.close());
466
- server.once('close', () => { void stopAllScrcpySessions(); });
468
+ server.once('close', () => { void stopAllScrcpySessions(); void androidRecordings.stopAll(); });
467
469
  const federation = createFederationRuntime(app, path.join(homedir(), '.termdock', 'federation'), {
468
470
  terminal: handleTerminalWebSocket, control: handleControlWebSocket, android: handleAndroidWebSocket,
469
471
  }, {
@@ -773,7 +775,8 @@ export function startServer(options = {}) {
773
775
  crashForensics.recordIncident('port-conflict', { extra: { port, host, code: error.code } });
774
776
  console.error(`Port ${port} is already in use.`);
775
777
  console.error(`To free it, find and stop the process: lsof -tiTCP:${port} -sTCP:LISTEN | xargs kill`);
776
- process.exit(1);
778
+ exitWithStartupFailure('port-conflict', `Port ${port} is already in use (EADDRINUSE).`);
779
+ return;
777
780
  }
778
781
  console.error('Server error:', error);
779
782
  process.exit(1);
@@ -1,6 +1,8 @@
1
1
  import { Router } from 'express';
2
2
  import { connectDevice, disconnectDevice, listDevices, resolveAdbBinary } from '../android/adb.js';
3
3
  import { createScrcpySession, resolveScrcpyBinaries } from '../android/scrcpy.js';
4
+ import { androidRecordings } from '../android/recording.js';
5
+ import { pathValidator } from '../utils/pathValidator.js';
4
6
  const router = Router();
5
7
  const SERIAL_PATTERN = /^[0-9a-zA-Z_.:\-]{1,128}$/;
6
8
  /** 逐帧/逐控件的诊断日志默认关闭,需要时 TERMDOCK_ANDROID_DEBUG=1 打开。 */
@@ -46,6 +48,59 @@ router.post('/disconnect', async (req, res) => {
46
48
  res.status(400).json({ error: message, code: message });
47
49
  }
48
50
  });
51
+ router.get('/recordings', async (req, res) => {
52
+ const serial = typeof req.query.serial === 'string' ? req.query.serial : '';
53
+ if (!isValidAndroidSerial(serial)) {
54
+ res.status(400).json({ error: 'INVALID_SERIAL' });
55
+ return;
56
+ }
57
+ try {
58
+ res.json({ recordings: await androidRecordings.list(serial) });
59
+ }
60
+ catch (error) {
61
+ res.status(500).json({ error: String(error) });
62
+ }
63
+ });
64
+ router.post('/recordings', async (req, res) => {
65
+ const serial = typeof req.body?.serial === 'string' ? req.body.serial : '';
66
+ if (!isValidAndroidSerial(serial)) {
67
+ res.status(400).json({ error: 'INVALID_SERIAL' });
68
+ return;
69
+ }
70
+ try {
71
+ res.json(await androidRecordings.start(serial));
72
+ }
73
+ catch (error) {
74
+ res.status(500).json({ error: error instanceof Error ? error.message : String(error) });
75
+ }
76
+ });
77
+ router.post('/recordings/:id/stop', async (req, res) => {
78
+ try {
79
+ res.json(await androidRecordings.stop(req.params.id));
80
+ }
81
+ catch (error) {
82
+ res.status(400).json({ error: String(error) });
83
+ }
84
+ });
85
+ router.post('/recordings/:id/save', async (req, res) => {
86
+ try {
87
+ const directory = req.body?.directory === undefined ? undefined
88
+ : await (req.pathValidator ?? pathValidator).validateAsync(req.body.directory);
89
+ res.json({ path: await androidRecordings.save(req.params.id, directory) });
90
+ }
91
+ catch (error) {
92
+ res.status(400).json({ error: error instanceof Error ? error.message : String(error) });
93
+ }
94
+ });
95
+ router.delete('/recordings/:id', async (req, res) => {
96
+ try {
97
+ await androidRecordings.discard(req.params.id);
98
+ res.json({ ok: true });
99
+ }
100
+ catch (error) {
101
+ res.status(400).json({ error: String(error) });
102
+ }
103
+ });
49
104
  export default router;
50
105
  function clampInt(value, min, max, fallback) {
51
106
  if (typeof value !== 'number' || !Number.isFinite(value))
@@ -1349,7 +1349,7 @@ async function getCachedGitBundle(resolvedCwd, gitRoot, includeNested, refresh,
1349
1349
  const now = Date.now();
1350
1350
  if (signal)
1351
1351
  throwIfAborted(signal, 'git.bundle');
1352
- const cached = gitBundleCache.get(cacheKey);
1352
+ const cached = getCompatibleGitBundleCache(gitRoot, includeNested, discoverOnly);
1353
1353
  if (!refresh && cached && (cached.expiresAt > now || allowStale)) {
1354
1354
  const cacheAgeMs = Math.max(0, now - (cached.expiresAt - GIT_BUNDLE_CACHE_TTL_MS));
1355
1355
  return {
@@ -1399,8 +1399,7 @@ async function refreshGitBundleCacheDetached(resolvedCwd, gitRoot, includeNested
1399
1399
  return promise;
1400
1400
  }
1401
1401
  function getGitBundleCache(gitRoot, includeNested, allowStale = false, discoverOnly = false) {
1402
- const cacheKey = getGitBundleCacheKey(gitRoot, includeNested, discoverOnly);
1403
- const cached = gitBundleCache.get(cacheKey);
1402
+ const cached = getCompatibleGitBundleCache(gitRoot, includeNested, discoverOnly);
1404
1403
  if (!cached)
1405
1404
  return null;
1406
1405
  const now = Date.now();
@@ -2438,6 +2437,17 @@ const gitBundleBuildPromises = new Map();
2438
2437
  function getGitBundleCacheKey(gitRoot, includeNested, discoverOnly = false) {
2439
2438
  return `${gitRoot}\u0000${includeNested ? 'nested' : 'single'}${discoverOnly ? ' discover' : ''}`;
2440
2439
  }
2440
+ function getCompatibleGitBundleCache(gitRoot, includeNested, discoverOnly) {
2441
+ const cached = gitBundleCache.get(getGitBundleCacheKey(gitRoot, includeNested, discoverOnly));
2442
+ if (!includeNested || !discoverOnly)
2443
+ return cached;
2444
+ // Reopening the sidebar requests discovery, while manual refresh builds the
2445
+ // full snapshot. Full data satisfies discovery too: do not return an older
2446
+ // discovery snapshot and roll back both the file list and its timestamp.
2447
+ // The reverse is unsafe because discovery contains deferred placeholders.
2448
+ const full = gitBundleCache.get(getGitBundleCacheKey(gitRoot, true));
2449
+ return full && (!cached || full.updatedAt >= cached.updatedAt) ? full : cached;
2450
+ }
2441
2451
  function clearGitBundleCacheForRoot(root) {
2442
2452
  for (const key of gitBundleCache.keys()) {
2443
2453
  if (key.startsWith(`${root}\u0000`))
@@ -1,3 +1,4 @@
1
+ import { androidRecordings } from '../android/recording.js';
1
2
  import { assertPeerRegistrationAuthority } from '../agent/collaborationPeerTransport.js';
2
3
  import { progressRoutes } from '../notifications/progressRoutes.js';
3
4
  import { ensureNodePty } from '../utils/ensureNodePty.js';
@@ -38,6 +39,7 @@ import { AgentResumeHistoryStore } from '../agent/resumeHistory.js';
38
39
  import { isTmuxRecoveryCandidate } from '../utils/tmuxRecoveryCandidate.js';
39
40
  import { AutomationStore, normalizeAutomationSchedule } from '../agent/automationStore.js';
40
41
  import { buildBracketedSubmitBytes, canDeliverPromptToAgent } from '../agent/promptDelivery.js';
42
+ import { prepareSupervisionEnable } from '../utils/enableSupervision.js';
41
43
  import { buildServerHealthState, dismissServerHealth } from '../utils/serverHealth.js';
42
44
  import { collaborationRoutes } from '../agent/collaborationRoutes.js';
43
45
  import { extrasFromBody } from '../agent/collaborationProtocol.js';
@@ -625,8 +627,24 @@ function flushPersistAndExit() {
625
627
  }
626
628
  catch { /* best effort */ }
627
629
  }
628
- process.on('SIGTERM', () => { flushPersistAndExit(); void persistToolbarPresetsNow(); caffeinateManager.shutdown(); process.exit(0); });
629
- process.on('SIGINT', () => { flushPersistAndExit(); void persistToolbarPresetsNow(); caffeinateManager.shutdown(); process.exit(0); });
630
+ let shuttingDown = false;
631
+ const shutdownWithRecordings = async () => {
632
+ if (shuttingDown)
633
+ return;
634
+ shuttingDown = true;
635
+ flushPersistAndExit();
636
+ void persistToolbarPresetsNow();
637
+ caffeinateManager.shutdown();
638
+ // Give the MP4 muxer a chance to write its trailer before the service exits.
639
+ try {
640
+ await androidRecordings.stopAll();
641
+ }
642
+ finally {
643
+ process.exit(0);
644
+ }
645
+ };
646
+ process.on('SIGTERM', () => { void shutdownWithRecordings(); });
647
+ process.on('SIGINT', () => { void shutdownWithRecordings(); });
630
648
  // 服务启动时从磁盘加载(带去重,防止历史累积的重复条目复活)
631
649
  void (async () => {
632
650
  await Promise.all([loadGlobalSessionStateFromDisk(), loadTmuxRecoveryStateFromDisk()]);
@@ -6258,6 +6276,16 @@ router.post('/update/restart', async (_req, res) => {
6258
6276
  router.get('/server-health', (_req, res) => {
6259
6277
  res.json(buildServerHealthState());
6260
6278
  });
6279
+ router.post('/server-health/enable-supervision', async (_req, res) => {
6280
+ try {
6281
+ const commit = await prepareSupervisionEnable();
6282
+ res.once('finish', commit);
6283
+ res.status(202).json({ restarting: true });
6284
+ }
6285
+ catch (error) {
6286
+ res.status(409).json({ error: error instanceof Error ? error.message : String(error) });
6287
+ }
6288
+ });
6261
6289
  router.post('/server-health/dismiss', (_req, res) => {
6262
6290
  res.status(202).json(dismissServerHealth());
6263
6291
  });
@@ -0,0 +1,33 @@
1
+ /** The prepared process becomes the supervisor once the old service has exited. */
2
+ import fs from 'node:fs';
3
+ import { runSupervisor } from './supervisor.js';
4
+ import { isProcessRunning } from './utils/termdockState.js';
5
+ const { parentPid, config } = JSON.parse(process.argv[2]);
6
+ if (!Number.isSafeInteger(parentPid) || parentPid <= 1 || !process.connected)
7
+ process.exit(1);
8
+ fs.accessSync(config.childEntry, fs.constants.R_OK);
9
+ if (config.healthCaPath)
10
+ fs.accessSync(config.healthCaPath, fs.constants.R_OK);
11
+ let committed = false;
12
+ const expiry = setTimeout(() => process.exit(1), 10_000);
13
+ process.on('disconnect', () => { if (!committed)
14
+ process.exit(1); });
15
+ process.once('message', (message) => {
16
+ if (message?.type !== 'handoff-commit')
17
+ process.exit(1);
18
+ committed = true;
19
+ clearTimeout(expiry);
20
+ const deadline = Date.now() + 30_000;
21
+ const timer = setInterval(() => {
22
+ if (isProcessRunning(parentPid)) {
23
+ if (Date.now() >= deadline) {
24
+ console.error('[supervisor] old service did not exit; handoff cancelled');
25
+ process.exit(1);
26
+ }
27
+ return;
28
+ }
29
+ clearInterval(timer);
30
+ runSupervisor(config);
31
+ }, 100);
32
+ });
33
+ process.send?.({ type: 'handoff-ready' });
@@ -29,7 +29,7 @@ import { backoffDelayMs } from './utils/backoff.js';
29
29
  import { installCrashForensics, isOomSuspect, readRuntimeTail, readServerLogTail } from './utils/crashForensics.js';
30
30
  import { probeHealthOnce, HEALTH_PROBE_TIMEOUT_MS } from './utils/healthProbe.js';
31
31
  import { writeJsonLogSync } from './utils/serverLogger.js';
32
- import { SUPERVISOR_STATE_VERSION, classifyChildExit, isReadyMessage, isServerIntentMessage, shouldGiveUp, } from './utils/supervisorProtocol.js';
32
+ import { SUPERVISOR_STATE_VERSION, classifyChildExit, isReadyMessage, isStartupFailedMessage, isServerIntentMessage, shouldGiveUp, } from './utils/supervisorProtocol.js';
33
33
  import { SUPERVISOR_STATE_PATH, STOP_GRACE_MS, isProcessRunning, removeJsonState, writeJsonStateAtomic, } from './utils/termdockState.js';
34
34
  export const DEFAULT_SUPERVISOR_TIMING = {
35
35
  readyTimeoutMs: 30_000,
@@ -189,6 +189,10 @@ export function createSupervisor(config, deps) {
189
189
  deps.notifyLauncher({ type: 'termdock-ready' });
190
190
  return;
191
191
  }
192
+ if (isStartupFailedMessage(message) && !becameReady) {
193
+ portConflict = message.reason === 'port-conflict';
194
+ return;
195
+ }
192
196
  if (isServerIntentMessage(message)) {
193
197
  intent = message.intent;
194
198
  deps.log(`[supervisor] server declared intent: ${message.intent}`);
@@ -356,7 +360,6 @@ export function createSupervisor(config, deps) {
356
360
  return;
357
361
  }
358
362
  if (shouldGiveUp(consecutiveCrashes, timing.maxConsecutiveCrashes)) {
359
- lastIncident.detail = `${lastIncident.detail} — gave up after ${consecutiveCrashes} consecutive crashes`;
360
363
  deps.log(`[supervisor] ${consecutiveCrashes} consecutive crashes; giving up. Service is NOT running.`);
361
364
  recordApplicationGaveUp();
362
365
  setPhase('gave-up');
@@ -380,7 +383,7 @@ export function createSupervisor(config, deps) {
380
383
  deps.recordIncident({
381
384
  role: 'supervisor',
382
385
  event: 'gave-up',
383
- detail: `gave up after ${consecutiveCrashes} consecutive crashes`,
386
+ detail: `gave up after ${consecutiveCrashes} consecutive failures: ${lastIncident?.detail ?? 'unknown cause'}`,
384
387
  version: config.version ?? null,
385
388
  restarts,
386
389
  lastIncident,
@@ -0,0 +1,80 @@
1
+ import { spawn } from 'node:child_process';
2
+ import fs from 'node:fs';
3
+ import { fileURLToPath } from 'node:url';
4
+ import { getSupervisorStatus } from './supervisorClient.js';
5
+ import { SERVER_LOG_PATH } from './termdockState.js';
6
+ let config = null;
7
+ let pending = false;
8
+ // Only the production CLI registers a launch recipe. Desktop and development
9
+ // runtimes must remain owned by their original launcher.
10
+ export function configureSupervisionEnable(value) {
11
+ config = value;
12
+ }
13
+ export function canEnableSupervision() {
14
+ return config !== null && !(process.env.TERMDOCK_SUPERVISED === '1' && process.connected)
15
+ && !process.env.TERMDOCK_DESKTOP && !process.env.TERMDOCK_DESKTOP_OWNER_SOCKET
16
+ && !process.env.INVOCATION_ID && !process.env.NOTIFY_SOCKET && !process.env.PM2_HOME
17
+ && !getSupervisorStatus()?.alive;
18
+ }
19
+ /** Prepare the replacement before stopping anything; commit after HTTP flush. */
20
+ export async function prepareSupervisionEnable() {
21
+ if (pending)
22
+ throw new Error('Automatic recovery is already being enabled.');
23
+ if (!canEnableSupervision() || !config)
24
+ throw new Error('Enable automatic recovery through the service launcher.');
25
+ pending = true;
26
+ try {
27
+ const script = fileURLToPath(new URL('../supervisionHandoff.js', import.meta.url));
28
+ fs.accessSync(script, fs.constants.R_OK);
29
+ fs.accessSync(config.childEntry, fs.constants.R_OK);
30
+ const fd = fs.openSync(SERVER_LOG_PATH, 'a', 0o600);
31
+ let child;
32
+ try {
33
+ child = spawn(process.execPath, [script, JSON.stringify({ parentPid: process.pid, config })], {
34
+ detached: true,
35
+ stdio: ['ignore', fd, fd, 'ipc'],
36
+ });
37
+ }
38
+ finally {
39
+ fs.closeSync(fd);
40
+ }
41
+ await new Promise((resolve, reject) => {
42
+ const timer = setTimeout(() => {
43
+ child.kill();
44
+ reject(new Error('Automatic recovery did not become ready.'));
45
+ }, 5000);
46
+ child.once('error', (error) => { clearTimeout(timer); reject(error); });
47
+ child.once('exit', () => {
48
+ clearTimeout(timer);
49
+ pending = false;
50
+ reject(new Error('Automatic recovery could not start.'));
51
+ });
52
+ child.once('message', (message) => {
53
+ if (message?.type !== 'handoff-ready')
54
+ return;
55
+ clearTimeout(timer);
56
+ resolve();
57
+ });
58
+ });
59
+ let committed = false;
60
+ return () => {
61
+ if (committed)
62
+ return;
63
+ committed = true;
64
+ // Never terminate the working service if the prepared process vanished.
65
+ child.send({ type: 'handoff-commit' }, (error) => {
66
+ if (error) {
67
+ pending = false;
68
+ return;
69
+ }
70
+ child.disconnect();
71
+ child.unref();
72
+ setTimeout(() => process.kill(process.pid, 'SIGTERM'), 750).unref();
73
+ });
74
+ };
75
+ }
76
+ catch (error) {
77
+ pending = false;
78
+ throw error;
79
+ }
80
+ }
@@ -12,6 +12,7 @@
12
12
  import { INCIDENT_DISMISSED_PATH, readJsonFile, writeJsonStateAtomic, } from './termdockState.js';
13
13
  import { readCrashLogTail } from './crashForensics.js';
14
14
  import { getSupervisorStatus } from './supervisorClient.js';
15
+ import { canEnableSupervision } from './enableSupervision.js';
15
16
  /**
16
17
  * 值得用户知道的事件。刻意排除 `stop`(是用户自己停的)、
17
18
  * `update-restart` / `manual-restart`(我们自己发起的,成功了就没什么可说)。
@@ -60,8 +61,31 @@ export function toIncident(entry) {
60
61
  export function findLatestNotableIncident(entries) {
61
62
  for (let index = entries.length - 1; index >= 0; index -= 1) {
62
63
  const incident = toIncident(entries[index]);
63
- if (incident && NOTABLE_EVENTS.has(incident.event))
64
- return incident;
64
+ if (!incident || !NOTABLE_EVENTS.has(incident.event))
65
+ continue;
66
+ if (incident.event === 'gave-up') {
67
+ // Old logs store the final failure separately. Match the supervisor, then
68
+ // its exact child PID; never borrow an unrelated process's port conflict.
69
+ const summary = entries[index];
70
+ const failure = entries.slice(0, index).reverse().find((entry) => entry.role === 'supervisor' && entry.pid === summary.pid
71
+ && entry.event !== 'gave-up' && NOTABLE_EVENTS.has(String(entry.event)));
72
+ const nested = summary.lastIncident;
73
+ const cause = nested && typeof nested === 'object' ? nested : failure;
74
+ const portConflict = failure && typeof failure.serverPid === 'number'
75
+ ? entries.slice(0, index).reverse().find((entry) => entry.role === 'server'
76
+ && entry.pid === failure.serverPid && entry.event === 'port-conflict'
77
+ && typeof entry.ts === 'string' && Date.parse(entry.ts) <= incident.at
78
+ && incident.at - Date.parse(entry.ts) < 60_000)
79
+ : undefined;
80
+ const causeEvent = portConflict ? 'port-conflict' : pickString(cause?.event);
81
+ if (causeEvent)
82
+ incident.causeEvent = causeEvent;
83
+ if (portConflict) {
84
+ const port = pickNumber(portConflict.port);
85
+ incident.detail = port === null ? 'EADDRINUSE' : `Port ${port} is already in use (EADDRINUSE).`;
86
+ }
87
+ }
88
+ return incident;
65
89
  }
66
90
  return null;
67
91
  }
@@ -101,7 +125,8 @@ export function buildServerHealthState(options = {}) {
101
125
  }
102
126
  const dismissedAt = readDismissal(dismissedPath);
103
127
  return {
104
- supervised: process.env.TERMDOCK_SUPERVISED === '1',
128
+ supervised: process.env.TERMDOCK_SUPERVISED === '1' && process.connected === true,
129
+ canEnableSupervision: canEnableSupervision(),
105
130
  supervisor,
106
131
  incident,
107
132
  dismissedAt,
@@ -108,7 +108,7 @@ function normalizeAndroidQuality(value) {
108
108
  if (!value || typeof value !== 'object' || Array.isArray(value))
109
109
  return null;
110
110
  const item = value;
111
- if (item.id !== 'low' && item.id !== 'medium' && item.id !== 'high' && item.id !== 'custom')
111
+ if (item.id !== 'auto' && item.id !== 'low' && item.id !== 'medium' && item.id !== 'high' && item.id !== 'custom')
112
112
  return null;
113
113
  const maxSize = typeof item.maxSize === 'number' ? Math.max(360, Math.min(2160, Math.round(item.maxSize))) : 1080;
114
114
  const bitRate = typeof item.bitRate === 'number' ? Math.max(300_000, Math.min(30_000_000, Math.round(item.bitRate))) : 4_000_000;
@@ -0,0 +1,16 @@
1
+ /** Flush the failure to the supervisor before exiting; a log entry alone is not IPC. */
2
+ export function exitWithStartupFailure(reason, detail) {
3
+ if (!process.connected || !process.send)
4
+ process.exit(1);
5
+ const timeout = setTimeout(() => process.exit(1), 1_000);
6
+ const finish = () => {
7
+ clearTimeout(timeout);
8
+ process.exit(1);
9
+ };
10
+ try {
11
+ process.send({ type: 'termdock-startup-failed', reason, detail }, finish);
12
+ }
13
+ catch {
14
+ finish();
15
+ }
16
+ }
@@ -5,6 +5,11 @@
5
5
  * 否则唯一验证手段就只剩"手动 kill 一下看看"。
6
6
  */
7
7
  export const SUPERVISOR_STATE_VERSION = 1;
8
+ export function isStartupFailedMessage(value) {
9
+ return isRecord(value) && value.type === 'termdock-startup-failed'
10
+ && typeof value.reason === 'string'
11
+ && (value.detail === undefined || typeof value.detail === 'string');
12
+ }
8
13
  function isRecord(value) {
9
14
  return typeof value === 'object' && value !== null;
10
15
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "termdock",
3
- "version": "1.4.263",
3
+ "version": "1.4.265",
4
4
  "description": "A complete web-based terminal application with modern UI",
5
5
  "type": "module",
6
6
  "main": ".desktop-dist/main.js",
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
3
  "packageName": "termdock",
4
- "version": "1.4.263",
4
+ "version": "1.4.265",
5
5
  "runtimeProtocolVersion": 1,
6
6
  "minimumDesktopVersion": "1.4.46",
7
7
  "nodeMajor": 22,
8
8
  "dependencyHash": "sha256-K4KyYJuVoQDpZsBM8ZX1Rw3MddOxAzVdp/cPm0iGZz0=",
9
- "serverBundleHash": "sha256-P298/VVMPtBwuB5OMydf/x/ktRiauZC5IvJvwL2N5/8=",
10
- "clientBundleHash": "sha256-UmhjK1bsiYqkurkrSz5AHkF84uC3gST7Z0Op2nhnJzw=",
9
+ "serverBundleHash": "sha256-J2E5faoECqRyYnXPzSDHcV9tXUuUih+LDtG464f/dHI=",
10
+ "clientBundleHash": "sha256-FJtbhUlN3+7dITPffcvTDidMmywkNOsNa+E+7ow3+SY=",
11
11
  "entrypoint": "dist/server/cli.js",
12
12
  "clientEntrypoint": "dist/client/index.html"
13
13
  }