@deepseek-harness-tui/dsh-tui 0.9.0 → 0.9.2

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 (307) hide show
  1. package/README.md +69 -256
  2. package/bin/dsh-tui.js +172 -5
  3. package/cordis.patch.yml +16 -4
  4. package/dsh-ecosystem-spec/protocols/profile-definitions.d.ts +13 -13
  5. package/dsh-ecosystem-spec/protocols/profile-definitions.js +59 -59
  6. package/dsh-ecosystem-spec/protocols/tui-channel-http.d.ts +15 -15
  7. package/dsh-ecosystem-spec/protocols/tui-channel-http.js +32 -32
  8. package/dsh-ecosystem-spec/protocols/tui-channel.d.ts +59 -59
  9. package/dsh-ecosystem-spec/protocols/tui-channel.js +166 -166
  10. package/dsh-ecosystem-spec/protocols/tui-contributions.d.ts +42 -42
  11. package/dsh-ecosystem-spec/protocols/tui-contributions.js +143 -143
  12. package/dsh-ecosystem-spec/registry/README.md +14 -14
  13. package/dsh-ecosystem-spec/registry/host-descriptor.tui.example.json +56 -56
  14. package/dsh-ecosystem-spec/registry/permissions-0.1.json +13 -13
  15. package/dsh-ecosystem-spec/registry/registry-0.15.json +149 -149
  16. package/lib/types/cc/hyperlink.d.ts +7 -1
  17. package/lib/types/cc/hyperlink.d.ts.map +1 -1
  18. package/lib/types/cc/hyperlink.js +3 -2
  19. package/lib/types/cc/markdown.d.ts.map +1 -1
  20. package/lib/types/cc/markdown.js +33 -3
  21. package/lib/types/cc/sessionColors.d.ts +27 -0
  22. package/lib/types/cc/sessionColors.d.ts.map +1 -0
  23. package/lib/types/cc/sessionColors.js +39 -0
  24. package/lib/types/commands.d.ts.map +1 -1
  25. package/lib/types/commands.js +6 -0
  26. package/lib/types/components/AutoRecapRow.d.ts +23 -0
  27. package/lib/types/components/AutoRecapRow.d.ts.map +1 -0
  28. package/lib/types/components/AutoRecapRow.js +23 -0
  29. package/lib/types/components/ColorPicker.d.ts +17 -0
  30. package/lib/types/components/ColorPicker.d.ts.map +1 -0
  31. package/lib/types/components/ColorPicker.js +20 -0
  32. package/lib/types/components/CommandSuggestions.d.ts +3 -1
  33. package/lib/types/components/CommandSuggestions.d.ts.map +1 -1
  34. package/lib/types/components/ContextBarView.d.ts +33 -0
  35. package/lib/types/components/ContextBarView.d.ts.map +1 -0
  36. package/lib/types/components/ContextBarView.js +46 -0
  37. package/lib/types/components/EffortInputBorder.d.ts +12 -1
  38. package/lib/types/components/EffortInputBorder.d.ts.map +1 -1
  39. package/lib/types/components/EffortInputBorder.js +22 -5
  40. package/lib/types/components/FileActionsPanel.d.ts +22 -0
  41. package/lib/types/components/FileActionsPanel.d.ts.map +1 -0
  42. package/lib/types/components/FileActionsPanel.js +28 -0
  43. package/lib/types/components/FileSuggestions.d.ts +3 -1
  44. package/lib/types/components/FileSuggestions.d.ts.map +1 -1
  45. package/lib/types/components/MessageList.d.ts +3 -1
  46. package/lib/types/components/MessageList.d.ts.map +1 -1
  47. package/lib/types/components/MessageList.js +13 -6
  48. package/lib/types/components/ModelPicker.d.ts +26 -4
  49. package/lib/types/components/ModelPicker.d.ts.map +1 -1
  50. package/lib/types/components/ModelPicker.js +26 -6
  51. package/lib/types/components/PromptInput.d.ts.map +1 -1
  52. package/lib/types/components/PromptInput.js +365 -57
  53. package/lib/types/components/RecapPanel.d.ts +25 -0
  54. package/lib/types/components/RecapPanel.d.ts.map +1 -0
  55. package/lib/types/components/RecapPanel.js +55 -0
  56. package/lib/types/components/ScrollbarGutter.d.ts.map +1 -1
  57. package/lib/types/components/ScrollbarGutter.js +55 -4
  58. package/lib/types/components/SubagentCard.d.ts.map +1 -1
  59. package/lib/types/components/SubagentCard.js +6 -4
  60. package/lib/types/components/SuggestionCard.d.ts +4 -2
  61. package/lib/types/components/SuggestionCard.d.ts.map +1 -1
  62. package/lib/types/components/TimelineRail.d.ts.map +1 -1
  63. package/lib/types/components/TimelineRail.js +9 -2
  64. package/lib/types/components/design-system/ThemeProvider.d.ts +7 -0
  65. package/lib/types/components/design-system/ThemeProvider.d.ts.map +1 -1
  66. package/lib/types/components/design-system/ThemeProvider.js +2 -1
  67. package/lib/types/components/messages/AssistantToolUseMessage.d.ts +8 -1
  68. package/lib/types/components/messages/AssistantToolUseMessage.d.ts.map +1 -1
  69. package/lib/types/components/messages/AssistantToolUseMessage.js +36 -8
  70. package/lib/types/components/questions/AskUserQuestionPanel.d.ts +7 -3
  71. package/lib/types/components/questions/AskUserQuestionPanel.d.ts.map +1 -1
  72. package/lib/types/components/questions/AskUserQuestionPanel.js +41 -9
  73. package/lib/types/dsh-adapter/channel.d.ts +101 -2
  74. package/lib/types/dsh-adapter/channel.d.ts.map +1 -1
  75. package/lib/types/dsh-adapter/channel.js +969 -93
  76. package/lib/types/dsh-adapter/compat/index.d.ts +8 -6
  77. package/lib/types/dsh-adapter/compat/index.d.ts.map +1 -1
  78. package/lib/types/dsh-adapter/compat/index.js +8 -6
  79. package/lib/types/dsh-adapter/compat/sessionLog.d.ts +150 -0
  80. package/lib/types/dsh-adapter/compat/sessionLog.d.ts.map +1 -1
  81. package/lib/types/dsh-adapter/compat/sessionLog.js +518 -41
  82. package/lib/types/dsh-adapter/contract.d.ts +1 -1
  83. package/lib/types/dsh-adapter/contract.d.ts.map +1 -1
  84. package/lib/types/dsh-adapter/contract.js +1 -0
  85. package/lib/types/dsh-adapter/extension-events.d.ts +1 -1
  86. package/lib/types/dsh-adapter/extension-events.d.ts.map +1 -1
  87. package/lib/types/dsh-adapter/index.d.ts +10 -0
  88. package/lib/types/dsh-adapter/index.d.ts.map +1 -1
  89. package/lib/types/dsh-adapter/index.js +5 -0
  90. package/lib/types/dsh-adapter/plugin.d.ts.map +1 -1
  91. package/lib/types/dsh-adapter/plugin.js +317 -8
  92. package/lib/types/dsh-adapter/presets.d.ts +18 -0
  93. package/lib/types/dsh-adapter/presets.d.ts.map +1 -1
  94. package/lib/types/dsh-adapter/presets.js +31 -0
  95. package/lib/types/dsh-adapter/providerWizard.d.ts +29 -1
  96. package/lib/types/dsh-adapter/providerWizard.d.ts.map +1 -1
  97. package/lib/types/dsh-adapter/providerWizard.js +89 -5
  98. package/lib/types/dsh-adapter/questions.d.ts +19 -2
  99. package/lib/types/dsh-adapter/questions.d.ts.map +1 -1
  100. package/lib/types/dsh-adapter/questions.js +53 -10
  101. package/lib/types/dsh-adapter/recap.d.ts +49 -0
  102. package/lib/types/dsh-adapter/recap.d.ts.map +1 -0
  103. package/lib/types/dsh-adapter/recap.js +121 -0
  104. package/lib/types/dsh-adapter/sessionTree.d.ts +314 -0
  105. package/lib/types/dsh-adapter/sessionTree.d.ts.map +1 -0
  106. package/lib/types/dsh-adapter/sessionTree.js +954 -0
  107. package/lib/types/dsh-adapter/shortcuts.d.ts +15 -44
  108. package/lib/types/dsh-adapter/shortcuts.d.ts.map +1 -1
  109. package/lib/types/dsh-adapter/shortcuts.js +28 -165
  110. package/lib/types/dsh-adapter/types.d.ts +1 -1
  111. package/lib/types/dsh-adapter/types.d.ts.map +1 -1
  112. package/lib/types/herdr.js +1 -1
  113. package/lib/types/i18n.d.ts +604 -176
  114. package/lib/types/i18n.d.ts.map +1 -1
  115. package/lib/types/i18n.js +206 -72
  116. package/lib/types/ink/log-update.d.ts.map +1 -1
  117. package/lib/types/ink/log-update.js +21 -0
  118. package/lib/types/migrationPrefs.d.ts +60 -0
  119. package/lib/types/migrationPrefs.d.ts.map +1 -0
  120. package/lib/types/migrationPrefs.js +97 -0
  121. package/lib/types/modelGroups.d.ts +72 -0
  122. package/lib/types/modelGroups.d.ts.map +1 -0
  123. package/lib/types/modelGroups.js +97 -0
  124. package/lib/types/modelRecents.d.ts +39 -0
  125. package/lib/types/modelRecents.d.ts.map +1 -0
  126. package/lib/types/modelRecents.js +97 -0
  127. package/lib/types/native-ts/yoga-layout/index.d.ts +11 -0
  128. package/lib/types/native-ts/yoga-layout/index.d.ts.map +1 -1
  129. package/lib/types/native-ts/yoga-layout/index.js +31 -1
  130. package/lib/types/oauth.d.ts +15 -0
  131. package/lib/types/oauth.d.ts.map +1 -0
  132. package/lib/types/oauth.js +14 -0
  133. package/lib/types/plugin-spec/registry.d.ts +1 -1
  134. package/lib/types/plugin-spec/registry.js +1 -1
  135. package/lib/types/reload.d.ts +109 -0
  136. package/lib/types/reload.d.ts.map +1 -0
  137. package/lib/types/reload.js +118 -0
  138. package/lib/types/screens/Chat.d.ts +3 -1
  139. package/lib/types/screens/Chat.d.ts.map +1 -1
  140. package/lib/types/screens/Chat.js +588 -64
  141. package/lib/types/screens/SessionTree.d.ts +27 -0
  142. package/lib/types/screens/SessionTree.d.ts.map +1 -0
  143. package/lib/types/screens/SessionTree.js +555 -0
  144. package/lib/types/screens/StatusLine.d.ts +0 -9
  145. package/lib/types/screens/StatusLine.d.ts.map +1 -1
  146. package/lib/types/screens/StatusLine.js +215 -52
  147. package/lib/types/screens/StatusMetrics.d.ts +35 -3
  148. package/lib/types/screens/StatusMetrics.d.ts.map +1 -1
  149. package/lib/types/screens/StatusMetrics.js +63 -8
  150. package/lib/types/screens/chatOverlay.d.ts +17 -1
  151. package/lib/types/screens/chatOverlay.d.ts.map +1 -1
  152. package/lib/types/screens/chatOverlay.js +3 -1
  153. package/lib/types/tips.d.ts.map +1 -1
  154. package/lib/types/tips.js +22 -4
  155. package/lib/types/update.d.ts +159 -0
  156. package/lib/types/update.d.ts.map +1 -1
  157. package/lib/types/update.js +496 -30
  158. package/lib/types/utils/debug.d.ts +1 -1
  159. package/lib/types/utils/debug.js +3 -3
  160. package/lib/types/utils/fileTarget.d.ts +46 -0
  161. package/lib/types/utils/fileTarget.d.ts.map +1 -0
  162. package/lib/types/utils/fileTarget.js +178 -0
  163. package/lib/types/utils/keymap.d.ts +134 -0
  164. package/lib/types/utils/keymap.d.ts.map +1 -0
  165. package/lib/types/utils/keymap.js +311 -0
  166. package/lib/types/utils/mentions.d.ts +25 -2
  167. package/lib/types/utils/mentions.d.ts.map +1 -1
  168. package/lib/types/utils/mentions.js +69 -10
  169. package/lib/types/utils/openExternal.d.ts +50 -0
  170. package/lib/types/utils/openExternal.d.ts.map +1 -0
  171. package/lib/types/utils/openExternal.js +170 -0
  172. package/node_modules/@deepseek-harness-tui/dsh-auth/LICENSE +21 -0
  173. package/node_modules/@deepseek-harness-tui/dsh-auth/README.md +140 -0
  174. package/node_modules/@deepseek-harness-tui/dsh-auth/cordis.patch.yml +11 -0
  175. package/node_modules/@deepseek-harness-tui/dsh-auth/dsh-plugin.json +44 -0
  176. package/node_modules/@deepseek-harness-tui/dsh-auth/lib/command.d.ts +14 -0
  177. package/node_modules/@deepseek-harness-tui/dsh-auth/lib/command.js +67 -0
  178. package/node_modules/@deepseek-harness-tui/dsh-auth/lib/credentials.d.ts +64 -0
  179. package/node_modules/@deepseek-harness-tui/dsh-auth/lib/credentials.js +170 -0
  180. package/node_modules/@deepseek-harness-tui/dsh-auth/lib/index.d.ts +79 -0
  181. package/node_modules/@deepseek-harness-tui/dsh-auth/lib/index.js +196 -0
  182. package/node_modules/@deepseek-harness-tui/dsh-auth/lib/interaction.d.ts +68 -0
  183. package/node_modules/@deepseek-harness-tui/dsh-auth/lib/interaction.js +193 -0
  184. package/node_modules/@deepseek-harness-tui/dsh-auth/lib/opener.d.ts +32 -0
  185. package/node_modules/@deepseek-harness-tui/dsh-auth/lib/opener.js +101 -0
  186. package/node_modules/@deepseek-harness-tui/dsh-auth/lib/profiles.d.ts +31 -0
  187. package/node_modules/@deepseek-harness-tui/dsh-auth/lib/profiles.js +68 -0
  188. package/node_modules/@deepseek-harness-tui/dsh-auth/lib/service.d.ts +69 -0
  189. package/node_modules/@deepseek-harness-tui/dsh-auth/lib/service.js +129 -0
  190. package/node_modules/@deepseek-harness-tui/dsh-auth/package.json +70 -0
  191. package/node_modules/@dsh-std/command/CHANGELOG.md +8 -8
  192. package/node_modules/@dsh-std/command/LICENSE +21 -21
  193. package/node_modules/@dsh-std/command/README.md +26 -26
  194. package/node_modules/@dsh-std/command/README.zh.md +28 -28
  195. package/node_modules/@dsh-std/command/lib/index.js.map +1 -1
  196. package/node_modules/@dsh-std/command/package.json +22 -22
  197. package/node_modules/@dsh-std/connection/CHANGELOG.md +8 -8
  198. package/node_modules/@dsh-std/connection/LICENSE +21 -21
  199. package/node_modules/@dsh-std/connection/README.md +42 -42
  200. package/node_modules/@dsh-std/connection/README.zh.md +42 -42
  201. package/node_modules/@dsh-std/connection/lib/index.js.map +1 -1
  202. package/node_modules/@dsh-std/connection/lib/memory.js.map +1 -1
  203. package/node_modules/@dsh-std/connection/lib/resolve-3X6ausgz.js.map +1 -1
  204. package/node_modules/@dsh-std/connection/package.json +20 -20
  205. package/node_modules/@dsh-std/core/CHANGELOG.md +8 -8
  206. package/node_modules/@dsh-std/core/LICENSE +21 -21
  207. package/node_modules/@dsh-std/core/README.md +13 -13
  208. package/node_modules/@dsh-std/core/README.zh.md +13 -13
  209. package/node_modules/@dsh-std/core/lib/index.js.map +1 -1
  210. package/node_modules/@dsh-std/core/package.json +21 -21
  211. package/node_modules/@dsh-std/manifest/CHANGELOG.md +9 -9
  212. package/node_modules/@dsh-std/manifest/LICENSE +21 -21
  213. package/node_modules/@dsh-std/manifest/README.md +5 -5
  214. package/node_modules/@dsh-std/manifest/README.zh.md +5 -5
  215. package/node_modules/@dsh-std/manifest/lib/index.js.map +1 -1
  216. package/node_modules/@dsh-std/manifest/package.json +20 -20
  217. package/node_modules/@dsh-std/manifest/schema/dsh-plugin-0.15.schema.json +144 -144
  218. package/node_modules/@dsh-std/messages/CHANGELOG.md +8 -8
  219. package/node_modules/@dsh-std/messages/LICENSE +21 -21
  220. package/node_modules/@dsh-std/messages/README.md +5 -5
  221. package/node_modules/@dsh-std/messages/README.zh.md +7 -7
  222. package/node_modules/@dsh-std/messages/lib/index.js.map +1 -1
  223. package/node_modules/@dsh-std/messages/package.json +20 -20
  224. package/node_modules/@dsh-std/messages/schema/message-observer-envelope-0.15.schema.json +54 -54
  225. package/node_modules/@dsh-std/presentation/CHANGELOG.md +9 -9
  226. package/node_modules/@dsh-std/presentation/LICENSE +21 -21
  227. package/node_modules/@dsh-std/presentation/README.md +27 -27
  228. package/node_modules/@dsh-std/presentation/README.zh.md +29 -29
  229. package/node_modules/@dsh-std/presentation/lib/callback.js.map +1 -1
  230. package/node_modules/@dsh-std/presentation/lib/index.js.map +1 -1
  231. package/node_modules/@dsh-std/presentation/package.json +25 -25
  232. package/node_modules/@dsh-std/storage/CHANGELOG.md +8 -8
  233. package/node_modules/@dsh-std/storage/LICENSE +21 -21
  234. package/node_modules/@dsh-std/storage/README.md +5 -5
  235. package/node_modules/@dsh-std/storage/README.zh.md +7 -7
  236. package/node_modules/@dsh-std/storage/lib/index.js.map +1 -1
  237. package/node_modules/@dsh-std/storage/package.json +20 -20
  238. package/node_modules/@dsh-std/storage/schema/local-storage-0.15.schema.json +56 -56
  239. package/package.json +17 -5
  240. package/presets/liangshen/.dsh-tui-managed.json +1 -1
  241. package/presets/liangshen/instruction-hint.mjs +8 -0
  242. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/CHANGELOG.md +8 -8
  243. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/LICENSE +21 -21
  244. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/README.md +42 -42
  245. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/README.zh.md +42 -42
  246. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/lib/index.js.map +1 -1
  247. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/lib/memory.js.map +1 -1
  248. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/lib/resolve-3X6ausgz.js.map +1 -1
  249. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/package.json +20 -20
  250. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/core/CHANGELOG.md +8 -8
  251. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/core/LICENSE +21 -21
  252. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/core/README.md +13 -13
  253. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/core/README.zh.md +13 -13
  254. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/core/lib/index.js.map +1 -1
  255. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/core/package.json +21 -21
  256. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/manifest/CHANGELOG.md +9 -9
  257. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/manifest/LICENSE +21 -21
  258. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/manifest/README.md +5 -5
  259. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/manifest/README.zh.md +5 -5
  260. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/manifest/lib/index.js.map +1 -1
  261. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/manifest/package.json +20 -20
  262. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/manifest/schema/dsh-plugin-0.15.schema.json +144 -144
  263. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/CHANGELOG.md +9 -9
  264. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/LICENSE +21 -21
  265. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/README.md +27 -27
  266. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/README.zh.md +29 -29
  267. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/lib/callback.js.map +1 -1
  268. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/lib/index.js.map +1 -1
  269. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/package.json +25 -25
  270. package/vendor/dsh-std/packages/connection/node_modules/@dsh-std/core/CHANGELOG.md +8 -8
  271. package/vendor/dsh-std/packages/connection/node_modules/@dsh-std/core/LICENSE +21 -21
  272. package/vendor/dsh-std/packages/connection/node_modules/@dsh-std/core/README.md +13 -13
  273. package/vendor/dsh-std/packages/connection/node_modules/@dsh-std/core/README.zh.md +13 -13
  274. package/vendor/dsh-std/packages/connection/node_modules/@dsh-std/core/lib/index.js.map +1 -1
  275. package/vendor/dsh-std/packages/connection/node_modules/@dsh-std/core/package.json +21 -21
  276. package/vendor/dsh-std/packages/manifest/node_modules/@dsh-std/core/CHANGELOG.md +8 -8
  277. package/vendor/dsh-std/packages/manifest/node_modules/@dsh-std/core/LICENSE +21 -21
  278. package/vendor/dsh-std/packages/manifest/node_modules/@dsh-std/core/README.md +13 -13
  279. package/vendor/dsh-std/packages/manifest/node_modules/@dsh-std/core/README.zh.md +13 -13
  280. package/vendor/dsh-std/packages/manifest/node_modules/@dsh-std/core/lib/index.js.map +1 -1
  281. package/vendor/dsh-std/packages/manifest/node_modules/@dsh-std/core/package.json +21 -21
  282. package/vendor/dsh-std/packages/messages/node_modules/@dsh-std/core/CHANGELOG.md +8 -8
  283. package/vendor/dsh-std/packages/messages/node_modules/@dsh-std/core/LICENSE +21 -21
  284. package/vendor/dsh-std/packages/messages/node_modules/@dsh-std/core/README.md +13 -13
  285. package/vendor/dsh-std/packages/messages/node_modules/@dsh-std/core/README.zh.md +13 -13
  286. package/vendor/dsh-std/packages/messages/node_modules/@dsh-std/core/lib/index.js.map +1 -1
  287. package/vendor/dsh-std/packages/messages/node_modules/@dsh-std/core/package.json +21 -21
  288. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/CHANGELOG.md +8 -8
  289. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/LICENSE +21 -21
  290. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/README.md +42 -42
  291. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/README.zh.md +42 -42
  292. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/lib/index.js.map +1 -1
  293. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/lib/memory.js.map +1 -1
  294. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/lib/resolve-3X6ausgz.js.map +1 -1
  295. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/package.json +20 -20
  296. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/core/CHANGELOG.md +8 -8
  297. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/core/LICENSE +21 -21
  298. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/core/README.md +13 -13
  299. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/core/README.zh.md +13 -13
  300. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/core/lib/index.js.map +1 -1
  301. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/core/package.json +21 -21
  302. package/vendor/dsh-std/packages/storage/node_modules/@dsh-std/core/CHANGELOG.md +8 -8
  303. package/vendor/dsh-std/packages/storage/node_modules/@dsh-std/core/LICENSE +21 -21
  304. package/vendor/dsh-std/packages/storage/node_modules/@dsh-std/core/README.md +13 -13
  305. package/vendor/dsh-std/packages/storage/node_modules/@dsh-std/core/README.zh.md +13 -13
  306. package/vendor/dsh-std/packages/storage/node_modules/@dsh-std/core/lib/index.js.map +1 -1
  307. package/vendor/dsh-std/packages/storage/node_modules/@dsh-std/core/package.json +21 -21
@@ -1,10 +1,11 @@
1
1
  import { spawn } from 'node:child_process';
2
- import { readFileSync, realpathSync, renameSync, writeFileSync } from 'node:fs';
2
+ import { appendFileSync, lstatSync, mkdirSync, readdirSync, readFileSync, realpathSync, renameSync, rmSync, statSync, writeFileSync } from 'node:fs';
3
3
  import { homedir } from 'node:os';
4
4
  import { dirname, join, resolve } from 'node:path';
5
5
  import { fileURLToPath } from 'node:url';
6
6
  import { gte, gt, lt, valid } from 'semver';
7
7
  import { shellQuote } from './utils/shellQuote.js';
8
+ import { DATA_DIR } from './utils/paths.js';
8
9
  // Re-exported for scripts/verify-update.mjs and the bin launcher, which reads
9
10
  // the compiled copy at lib/types/utils/shellQuote.js.
10
11
  export { shellQuote };
@@ -13,6 +14,72 @@ const DEFAULT_REGISTRY = 'https://registry.npmjs.org';
13
14
  const UPDATE_CHECK_TIMEOUT_MS = 4000;
14
15
  /** env marker set on the /update restart; the new process verifies it at boot. */
15
16
  const UPDATED_FROM_ENV = 'DSH_TUI_UPDATED_FROM';
17
+ /**
18
+ * env marker set on the /restart replacement: its boot logs to restart.log
19
+ * (ordinary launches stay silent, so the file is restart-only evidence).
20
+ */
21
+ const RESTART_CHILD_ENV = 'DSH_TUI_RESTART_CHILD';
22
+ /**
23
+ * Field-diagnosis log for the /restart terminal handoff, appended by BOTH
24
+ * processes: the exiting TUI logs the funnel path, the spawned replacement
25
+ * logs its boot progress, and every line carries pid + timestamp — so a
26
+ * handoff that dies at any stage (loader entry, TTY gate, session resume,
27
+ * even after the parent's survival window) leaves breadcrumbs on disk even
28
+ * when nothing reaches the already-confused terminal. Diagnosis only: every
29
+ * write is best effort and must never break the handoff itself.
30
+ */
31
+ const RESTART_LOG = join(DATA_DIR, 'restart.log');
32
+ /** Cap so a long debugging streak cannot grow the log unbounded. */
33
+ const RESTART_LOG_MAX_BYTES = 256 * 1024;
34
+ function writeRestartLine(line) {
35
+ try {
36
+ mkdirSync(DATA_DIR, { recursive: true });
37
+ appendFileSync(RESTART_LOG, `${new Date().toISOString()} pid=${process.pid} ${line}\n`);
38
+ }
39
+ catch {
40
+ // Diagnosis only.
41
+ }
42
+ }
43
+ /**
44
+ * Append a /restart handoff event to ~/.dsh-tui/restart.log.
45
+ * @param event - Short stable event name.
46
+ * @param data - Small JSON-safe detail (never credentials or session text).
47
+ */
48
+ export function logRestartEvent(event, data) {
49
+ writeRestartLine(`${event}${data === undefined ? '' : ` ${JSON.stringify(data)}`}`);
50
+ }
51
+ /** Start a fresh, clearly delimited /restart attempt block in the log. */
52
+ export function beginRestartAttempt(sessionId) {
53
+ try {
54
+ mkdirSync(DATA_DIR, { recursive: true });
55
+ let size = 0;
56
+ try {
57
+ size = statSync(RESTART_LOG).size;
58
+ }
59
+ catch {
60
+ size = 0;
61
+ }
62
+ if (size > RESTART_LOG_MAX_BYTES)
63
+ writeFileSync(RESTART_LOG, '');
64
+ }
65
+ catch {
66
+ // Diagnosis only.
67
+ }
68
+ writeRestartLine(`--- /restart attempt session=${sessionId} ---`);
69
+ }
70
+ /**
71
+ * Write a handoff notice to stderr synchronously: process.exit() right after
72
+ * an async stream write skips the flush, and a vanished diagnosis is
73
+ * indistinguishable from a silent failure.
74
+ */
75
+ export function writeHandoffNotice(text) {
76
+ try {
77
+ writeFileSync(2, text);
78
+ }
79
+ catch {
80
+ process.stderr.write(text);
81
+ }
82
+ }
16
83
  /** Read the version from either the compiled package or the source checkout. */
17
84
  export function installedTuiVersion() {
18
85
  const here = dirname(fileURLToPath(import.meta.url));
@@ -212,6 +279,111 @@ export function tuiUpdatePluginArgs(profile, targetVersion) {
212
279
  ? ['plugin', '--profile', profile, 'update', '--latest', PACKAGE_NAME]
213
280
  : ['plugin', '--profile', profile, 'update', `${PACKAGE_NAME}@${targetVersion}`];
214
281
  }
282
+ /**
283
+ * Postinstall-only transitive dependencies of the dsh-tui chain
284
+ * (dsh-auth → @earendil-works/pi-ai → @google/genai + protobufjs, plus the
285
+ * esbuild/koffi peers of that chain): pnpm ≥11 refuses to run their build
286
+ * scripts unless the workspace allowlists them, and a profile that never
287
+ * opted in fails the WHOLE install with ERR_PNPM_IGNORED_BUILDS. None of
288
+ * these scripts is needed at runtime (the repo root allowBuilds them all to
289
+ * `false`), so the update flow pre-seeds the profile's pnpm-workspace.yaml
290
+ * with explicit `false` entries — an explicit decision pnpm honors silently.
291
+ */
292
+ const PROFILE_ALLOW_BUILDS = {
293
+ '@google/genai': false,
294
+ esbuild: false,
295
+ koffi: false,
296
+ protobufjs: false,
297
+ };
298
+ /** The profile's pnpm-workspace.yaml (`$DSH_HOME ?? ~/.dsh` layout). */
299
+ export function profileWorkspaceYamlPath(profile) {
300
+ const dshHome = process.env.DSH_HOME ?? join(homedir(), '.dsh');
301
+ return join(dshHome, 'profiles', profile, 'pnpm-workspace.yaml');
302
+ }
303
+ /** YAML key spelling for an allowBuilds entry (scoped names need quotes). */
304
+ function allowBuildsKeyLine(key) {
305
+ const needsQuotes = key.includes('@') || key.includes('/');
306
+ return ` ${needsQuotes ? `'${key}'` : key}: false`;
307
+ }
308
+ /**
309
+ * Make sure the profile's pnpm-workspace.yaml carries explicit `false`
310
+ * allowBuilds entries for the dsh-tui chain's postinstall-only deps, so a
311
+ * pnpm ≥11 update is not killed by ERR_PNPM_IGNORED_BUILDS. Best effort and
312
+ * idempotent: existing entries are never overwritten (an explicit user
313
+ * decision wins), a missing `allowBuilds:` block is appended, and a missing
314
+ * file is created. Returns undefined when the profile directory is absent or
315
+ * the file could not be updated — the caller still runs pnpm, whose own
316
+ * ERR_PNPM_IGNORED_BUILDS diagnostic stays the visible fallback.
317
+ */
318
+ export function ensureProfileAllowBuilds(profile) {
319
+ const yamlPath = profileWorkspaceYamlPath(profile);
320
+ try {
321
+ if (!existsSafe(dirname(yamlPath)))
322
+ return undefined;
323
+ let text = '';
324
+ try {
325
+ text = readFileSync(yamlPath, 'utf8');
326
+ }
327
+ catch {
328
+ // Missing file — start from an empty document; writeFileSync creates it.
329
+ }
330
+ const lines = text.split(/\r?\n/u);
331
+ let blockStart = -1;
332
+ for (let i = 0; i < lines.length; i += 1) {
333
+ const line = lines[i];
334
+ if (line !== '' && line === line.trimStart() && /^allowBuilds:/u.test(line)) {
335
+ blockStart = i;
336
+ break;
337
+ }
338
+ }
339
+ const present = new Set();
340
+ if (blockStart !== -1) {
341
+ for (let i = blockStart + 1; i < lines.length; i += 1) {
342
+ const line = lines[i];
343
+ if (line === '' || line === line.trimStart())
344
+ break; // dedent = block ends
345
+ const keyMatch = /^\s+('?)(.+?)\1:\s/u.exec(line);
346
+ if (keyMatch !== null)
347
+ present.add(keyMatch[2]);
348
+ }
349
+ }
350
+ const added = Object.keys(PROFILE_ALLOW_BUILDS).filter(key => !present.has(key));
351
+ if (added.length === 0)
352
+ return { existing: [...present], added };
353
+ const insert = added.map(allowBuildsKeyLine);
354
+ if (blockStart !== -1) {
355
+ // Append after the block's last existing entry (before the next
356
+ // top-level key), so existing entries keep their position.
357
+ let blockEnd = blockStart + 1;
358
+ for (let i = blockStart + 1; i < lines.length; i += 1) {
359
+ const line = lines[i];
360
+ if (line === '' || line === line.trimStart())
361
+ break;
362
+ blockEnd = i + 1;
363
+ }
364
+ lines.splice(blockEnd, 0, ...insert);
365
+ }
366
+ else {
367
+ if (lines.length > 0 && lines[lines.length - 1] !== '')
368
+ lines.push('');
369
+ lines.push('allowBuilds:', ...insert);
370
+ }
371
+ writeFileSync(yamlPath, `${lines.join('\n')}\n`);
372
+ return { existing: [...present], added };
373
+ }
374
+ catch {
375
+ return undefined;
376
+ }
377
+ }
378
+ function existsSafe(path) {
379
+ try {
380
+ statSync(path);
381
+ return true;
382
+ }
383
+ catch {
384
+ return false;
385
+ }
386
+ }
215
387
  /**
216
388
  * The pnpm Windows tmp-rename race signature (issue #225): pnpm swaps a
217
389
  * package directory via a `<name>_tmp_<pid>` staging dir, and a file lock or
@@ -225,6 +397,79 @@ export function tuiUpdatePluginArgs(profile, targetVersion) {
225
397
  export function isTransientUpdateFailure(stderr) {
226
398
  return /ENOENT|EPERM|EBUSY/i.test(stderr) && /_tmp_\d+/i.test(stderr);
227
399
  }
400
+ /**
401
+ * The pnpm Linux flavor of the tmp-rename race (issue #479): the very same
402
+ * `importPackage` staging path, but overlayfs surfaces the second swap of an
403
+ * already-replaced package dir as EEXIST — deterministically, on every run,
404
+ * never transiently. A plain retry of the identical command ALWAYS fails the
405
+ * same way (the leftover `_tmp_<pid>_<threadId>` staging dir keeps colliding),
406
+ * so this signature must route to the recovery path
407
+ * (removeStalePackageInstall + rerun), not to the plain retry.
408
+ */
409
+ export function isEexistTmpRenameFailure(stderr) {
410
+ return /EEXIST/i.test(stderr) && /_tmp_\d+/i.test(stderr);
411
+ }
412
+ /**
413
+ * The dsh profile layout places plugin packages under
414
+ * `$DSH_HOME ?? ~/.dsh` — the same root dsh itself resolves
415
+ * (`dshHomePath is $DSH_HOME ?? ~/.dsh`, and packaged presets resolve
416
+ * identically). /update's recovery path needs this location to clear a
417
+ * half-updated install pnpm can no longer swap in place (issue #479).
418
+ */
419
+ export function profilePackageDir(profile) {
420
+ const dshHome = process.env.DSH_HOME ?? join(homedir(), '.dsh');
421
+ return join(dshHome, 'profiles', profile, 'node_modules', PACKAGE_NAME);
422
+ }
423
+ /**
424
+ * Recovery for the pnpm tmp-rename races (issues #225/#479): remove the
425
+ * profile's installed copy of this package plus the sibling `_tmp_`
426
+ * staging dirs a crashed swap leaves behind, so the next installer run
427
+ * takes the fresh-install path instead of renaming over the collision.
428
+ * Field-verified workaround (issue #479): `rm -rf <pkg dir>` + rerun
429
+ * succeeds where ten plain retries fail.
430
+ *
431
+ * A junction/symlink at the package dir is UNLINKED only — the link is
432
+ * removed, the target tree (a dev checkout it may point at) is never
433
+ * traversed or deleted. Best effort throughout: this runs inside a
434
+ * failure-recovery branch, so an unusable result simply falls through to
435
+ * the rerun and the manual repair hint.
436
+ */
437
+ export function removeStalePackageInstall(profile) {
438
+ const pkgDir = profilePackageDir(profile);
439
+ const scopeDir = dirname(pkgDir);
440
+ const removal = { packageDir: 'absent', tmpDirs: [] };
441
+ try {
442
+ const stats = lstatSync(pkgDir);
443
+ // lstat reports junctions AND symlinks as isSymbolicLink — remove the
444
+ // link itself, never what it points at.
445
+ if (stats.isSymbolicLink() || stats.isDirectory()) {
446
+ rmSync(pkgDir, { recursive: true, force: true });
447
+ removal.packageDir = 'removed';
448
+ }
449
+ }
450
+ catch (error) {
451
+ removal.packageDir = error.code === 'ENOENT' ? 'absent' : 'failed';
452
+ }
453
+ try {
454
+ for (const name of readdirSync(scopeDir)) {
455
+ if (!/^dsh-tui_tmp_\d+_\d+$/.test(name))
456
+ continue;
457
+ const staging = join(scopeDir, name);
458
+ try {
459
+ rmSync(staging, { recursive: true, force: true });
460
+ removal.tmpDirs.push(name);
461
+ }
462
+ catch {
463
+ // Staging leftovers are best effort; the package dir removal is
464
+ // the load-bearing half of the recovery.
465
+ }
466
+ }
467
+ }
468
+ catch {
469
+ // Unreadable scope dir — nothing more to clear.
470
+ }
471
+ return removal;
472
+ }
228
473
  /**
229
474
  * Best-effort migrate the GLOBAL launcher to the delegating shim (0.8.7):
230
475
  * after a successful profile update, copy this package's `bin/dsh-tui.js`
@@ -290,24 +535,17 @@ export function migrateGlobalLauncher() {
290
535
  return false;
291
536
  }
292
537
  /**
293
- * Update the installed dsh-tui package and restart the same launcher while
294
- * preserving the active session. The TUI must already be unmounted before
295
- * this is called so pnpm output cannot corrupt the rendered terminal frame.
538
+ * Install-only half of `/update`: run `dsh plugin add`, refuse boot-deadlock
539
+ * targets, verify the profile actually advanced, and migrate the global
540
+ * launcher. No restart — `updateTuiAndRestart` layers the session-preserving
541
+ * restart on top, and the `dsh-tui update` CLI stops here.
296
542
  *
297
- * When the preflight registry check resolved an exact target, pass that
298
- * version to pnpm instead of resolving `latest` a second time. This avoids a
299
- * stale mirror/dist-tag response between the check and install. If preflight
300
- * failed, retain the `--latest` fallback: a plain `pnpm update` stays inside
301
- * the manifest range and can restart unchanged across minor releases.
302
- *
303
- * @param sessionId - Session to resume in the replacement process.
304
- * @param profile - The dsh profile this TUI was launched with; updating any
305
- * other profile would leave the running install untouched.
306
- * @param targetVersion - Exact version returned by the preflight registry
307
- * check, or undefined when that check failed and pnpm should resolve latest.
308
- * @returns Exit codes for the update run and the replacement process.
543
+ * @param profile - The dsh profile to update.
544
+ * @param targetVersion - Exact version from the preflight registry check, or
545
+ * undefined when that check failed and pnpm should resolve latest.
546
+ * @returns The update exit code plus the before/after versions.
309
547
  */
310
- export async function updateTuiAndRestart(sessionId, profile, targetVersion) {
548
+ export async function updateTui(profile, targetVersion) {
311
549
  // Stamp the pre-update version BEFORE pnpm runs: it reads this package's
312
550
  // manifest from disk, which the update replaces on the fly — a
313
551
  // post-update read already sees the NEW version, and the restarted
@@ -316,6 +554,15 @@ export async function updateTuiAndRestart(sessionId, profile, targetVersion) {
316
554
  const updatedFrom = installedTuiVersion() ?? '';
317
555
  const dsh = process.platform === 'win32' ? 'dsh.cmd' : 'dsh';
318
556
  const updateArgs = tuiUpdatePluginArgs(profile, targetVersion);
557
+ // pnpm ≥11 hard-fails installs whose dependency tree carries un-allowlisted
558
+ // build scripts (ERR_PNPM_IGNORED_BUILDS). The dsh-auth chain pulls in
559
+ // postinstall-only deps (@google/genai/protobufjs via pi-ai), so pre-seed
560
+ // the profile workspace with explicit `false` entries before pnpm runs.
561
+ const allowBuilds = ensureProfileAllowBuilds(profile);
562
+ if (allowBuilds !== undefined && allowBuilds.added.length > 0) {
563
+ process.stderr.write(`dsh-tui: pre-seeded profile pnpm allowBuilds (${allowBuilds.added.join(', ')}) — ` +
564
+ 'postinstall-only deps are explicitly ignored\n');
565
+ }
319
566
  let updateStderr = '';
320
567
  const capture = (chunk) => { updateStderr += chunk; };
321
568
  let updateCode = await runProcess(dsh, updateArgs, { shell: true, onStderr: capture });
@@ -327,8 +574,23 @@ export async function updateTuiAndRestart(sessionId, profile, targetVersion) {
327
574
  updateStderr = '';
328
575
  updateCode = await runProcess(dsh, updateArgs, { shell: true, onStderr: capture });
329
576
  }
577
+ // Deterministic Linux tmp-rename race (issue #479): EEXIST never clears on
578
+ // a plain retry (the staging-dir collision reproduces every run), and a
579
+ // transient retry that failed again means the same leftover state. Both
580
+ // recover by removing the stale install + staging dirs, then rerunning —
581
+ // the field-verified workaround that takes pnpm down its fresh-install
582
+ // path instead of renaming over the collision.
583
+ if (updateCode !== 0 && (isEexistTmpRenameFailure(updateStderr) || isTransientUpdateFailure(updateStderr))) {
584
+ const flavor = isEexistTmpRenameFailure(updateStderr) ? 'EEXIST, issue #479' : 'retry failed, issue #225';
585
+ const removal = removeStalePackageInstall(profile);
586
+ const stagingNote = removal.tmpDirs.length > 0 ? ` + ${removal.tmpDirs.length} staging dir(s)` : '';
587
+ process.stderr.write(`dsh-tui: pnpm tmp-rename race (${flavor}) — cleared the stale install ` +
588
+ `(package dir: ${removal.packageDir}${stagingNote}) and rerunning the update…\n`);
589
+ updateStderr = '';
590
+ updateCode = await runProcess(dsh, updateArgs, { shell: true, onStderr: capture });
591
+ }
330
592
  if (updateCode !== 0)
331
- return { updateCode, restartCode: updateCode };
593
+ return { code: updateCode, updatedFrom };
332
594
  // A --latest fallback (preflight failed) on a stale mirror can still land
333
595
  // on the 0.7.0–0.7.1 hard-inject range — restarting into it under an older
334
596
  // global-launcher patch is the permanent boot deadlock of issues
@@ -341,7 +603,7 @@ export async function updateTuiAndRestart(sessionId, profile, targetVersion) {
341
603
  `(#183/#307) — NOT restarting into it. Repair with:\n` +
342
604
  ` dsh plugin --profile ${profile} add ${PACKAGE_NAME}@latest\n` +
343
605
  `(if the mirror has not synced the latest release yet, retry later)\n`);
344
- return { updateCode: 1, restartCode: 1 };
606
+ return { code: 1, updatedFrom, installed: installedNow };
345
607
  }
346
608
  // Post-update verification (issue #225): pnpm can report success yet leave
347
609
  // the profile half-updated (manifest old / lockfile new). Verify against
@@ -358,7 +620,7 @@ export async function updateTuiAndRestart(sessionId, profile, targetVersion) {
358
620
  process.stderr.write(`dsh-tui: update completed but the profile still runs ${installed ?? 'an unreadable version'} ` +
359
621
  `(expected ${targetVersion}) — the profile is half-updated. Repair manually with:\n` +
360
622
  ` dsh plugin --profile ${profile} add ${PACKAGE_NAME}@${targetVersion}\n`);
361
- return { updateCode: 1, restartCode: 1 };
623
+ return { code: 1, updatedFrom, installed };
362
624
  }
363
625
  }
364
626
  // Launcher migration (0.8.7): the freshly installed profile carries the
@@ -368,15 +630,219 @@ export async function updateTuiAndRestart(sessionId, profile, targetVersion) {
368
630
  if (migrateGlobalLauncher()) {
369
631
  process.stderr.write('dsh-tui: global launcher aligned to the delegating shim (no manual npm i -g needed anymore).\n');
370
632
  }
371
- const restartCode = await runProcess(process.execPath, [...process.execArgv, ...process.argv.slice(1)], {
372
- env: {
373
- ...process.env,
374
- // Dual-write the resume contract (issue #120): the cordis layer of a
375
- // still-old TUI build reads only DSH_CC_RESUME_SESSION.
376
- DSH_TUI_RESUME_SESSION: sessionId,
377
- DSH_CC_RESUME_SESSION: sessionId,
378
- [UPDATED_FROM_ENV]: updatedFrom,
379
- },
633
+ return { code: 0, updatedFrom, installed: installedTuiVersion() };
634
+ }
635
+ /**
636
+ * Update the installed dsh-tui package and restart the same launcher while
637
+ * preserving the active session. The TUI must already be unmounted before
638
+ * this is called so pnpm output cannot corrupt the rendered terminal frame.
639
+ *
640
+ * When the preflight registry check resolved an exact target, pass that
641
+ * version to pnpm instead of resolving `latest` a second time. This avoids a
642
+ * stale mirror/dist-tag response between the check and install. If preflight
643
+ * failed, retain the `--latest` fallback: a plain `pnpm update` stays inside
644
+ * the manifest range and can restart unchanged across minor releases.
645
+ *
646
+ * @param sessionId - Session to resume in the replacement process.
647
+ * @param profile - The dsh profile this TUI was launched with; updating any
648
+ * other profile would leave the running install untouched.
649
+ * @param targetVersion - Exact version returned by the preflight registry
650
+ * check, or undefined when that check failed and pnpm should resolve latest.
651
+ * @returns Exit codes for the update run and the replacement process.
652
+ */
653
+ export async function updateTuiAndRestart(sessionId, profile, targetVersion) {
654
+ const outcome = await updateTui(profile, targetVersion);
655
+ const { updatedFrom } = outcome;
656
+ if (outcome.code !== 0)
657
+ return { updateCode: outcome.code, restartCode: outcome.code };
658
+ // Restart through the same hardened handoff as /restart (issues
659
+ // #284/#307/#483): wait for the replacement's natural exit (the outer
660
+ // interpreter must not reclaim the console under it), re-assert the
661
+ // stdin detach from a watchdog so a revived pump cannot swallow the
662
+ // child's keypresses, and capture a fast-death stderr report. The old
663
+ // plain inherit spawn raced the interpreter for input on Windows
664
+ // Terminal (issue #483: "cannot type anything after /update").
665
+ const restartCode = await restartTui(sessionId, {
666
+ env: { [UPDATED_FROM_ENV]: updatedFrom },
667
+ kind: 'update',
668
+ });
669
+ return { updateCode: 0, restartCode };
670
+ }
671
+ /**
672
+ * Headless `dsh-tui update`: the `/update` decision flow (preflight, deadlock
673
+ * refusal, mirror-lag note, `--latest` fallback) followed by the install-only
674
+ * half — no TUI, no restart. The bin launcher dynamic-imports this from the
675
+ * profile copy's compiled lib.
676
+ *
677
+ * @param profile - The dsh profile to update.
678
+ * @returns Process exit code: 0 on success or already-latest, 1 otherwise.
679
+ */
680
+ export async function cliUpdate(profile) {
681
+ const target = await resolveTuiUpdateTarget();
682
+ if (target.kind === 'latest') {
683
+ process.stdout.write(`dsh-tui: already the latest version (${target.current}).\n`);
684
+ return 0;
685
+ }
686
+ let targetVersion;
687
+ if (target.kind === 'update') {
688
+ // Mirror the /update flow exactly: refuse the 0.7.0–0.7.1 hard-inject
689
+ // range (permanent boot deadlock under older launcher patches, #183/#307)
690
+ // instead of installing it.
691
+ if (isBootDeadlockTarget(target.latest)) {
692
+ process.stderr.write(`dsh-tui: refusing to update onto ${target.latest} — that range can permanently deadlock boot ` +
693
+ `(#183/#307). Latest on the official registry: ${target.authoritative ?? target.latest}. ` +
694
+ `If you use a mirror, retry after it syncs.\n`);
695
+ return 1;
696
+ }
697
+ if (target.authoritative !== undefined) {
698
+ process.stdout.write(`dsh-tui: note — your registry serves ${target.latest} while npmjs.org has ${target.authoritative} (mirror lag).\n`);
699
+ }
700
+ targetVersion = target.latest;
701
+ process.stdout.write(`dsh-tui: updating ${target.current} → ${target.latest}…\n`);
702
+ }
703
+ else {
704
+ process.stdout.write('dsh-tui: version check failed (offline or unreadable install) — falling back to `--latest`.\n');
705
+ }
706
+ const outcome = await updateTui(profile, targetVersion);
707
+ if (outcome.code === 0) {
708
+ // A preflight-less run (`--latest` fallback) can "succeed" as a pnpm
709
+ // no-op: same manifest before and after. Say so instead of printing a
710
+ // vacuous `updated X → X` — the /update path surfaces the same state via
711
+ // the DSH_TUI_UPDATED_FROM stamp on restart.
712
+ if (targetVersion === undefined && outcome.installed !== undefined && outcome.installed === outcome.updatedFrom) {
713
+ process.stdout.write(`dsh-tui: version did not advance (still ${outcome.installed}) — already the latest, or the registry has no newer release yet.\n`);
714
+ }
715
+ else {
716
+ process.stdout.write(`dsh-tui: updated ${outcome.updatedFrom || '(unknown)'} → ${outcome.installed ?? '(unreadable)'}.\n`);
717
+ }
718
+ }
719
+ return outcome.code;
720
+ }
721
+ export async function restartTui(sessionId, options = {}) {
722
+ const kind = options.kind ?? 'restart';
723
+ const tag = kind === 'update' ? 'update-restart' : 'restart';
724
+ const argv = [...process.execArgv, ...process.argv.slice(1)];
725
+ logRestartEvent(`${tag}: spawning replacement`, {
726
+ node: process.execPath,
727
+ argv,
728
+ cwd: process.cwd(),
729
+ stdinTty: process.stdin.isTTY === true,
730
+ stdoutTty: process.stdout.isTTY === true,
731
+ stderrTty: process.stderr.isTTY === true,
732
+ nodeOptions: process.env.NODE_OPTIONS ?? null,
733
+ dshHome: process.env.DSH_HOME ?? null,
734
+ });
735
+ const startedAt = Date.now();
736
+ return new Promise(resolve => {
737
+ const child = spawn(process.execPath, argv, {
738
+ env: {
739
+ ...process.env,
740
+ // Dual-write the resume contract (issue #120): the cordis layer of a
741
+ // still-old TUI build reads only DSH_CC_RESUME_SESSION.
742
+ DSH_TUI_RESUME_SESSION: sessionId,
743
+ DSH_CC_RESUME_SESSION: sessionId,
744
+ // Marks the replacement so its own boot logs to restart.log without
745
+ // noisy logging on every ordinary launch (/restart only).
746
+ ...(kind === 'restart' ? { [RESTART_CHILD_ENV]: '1' } : {}),
747
+ ...options.env,
748
+ },
749
+ // stdin/stdout stay inherited so the replacement owns the console the
750
+ // moment it boots; stderr is captured so a boot failure is reportable
751
+ // through THIS process (the terminal may already be mid-handoff when
752
+ // the child dies, and a raw inherit write can vanish).
753
+ stdio: ['inherit', 'inherit', 'pipe'],
754
+ });
755
+ logRestartEvent(`${tag}: replacement spawned`, { childPid: child.pid });
756
+ // Handoff watchdog (field evidence 2026-08-24: restarted TUI mounts but
757
+ // takes no input). Two jobs, both diagnosis-grade:
758
+ // 1. SAMPLE this process's stdin state every second — if anything
759
+ // re-attaches a reader after the funnel's detachStdinForHandoff, the
760
+ // sample (taken before the re-assert below) shows it in the log.
761
+ // 2. RE-ASSERT the detach and finally destroy the stream: this process
762
+ // must never read the shared console again — every keypress belongs
763
+ // to the replacement, and a resumed pump here is exactly the
764
+ // "restarted TUI sees dropped or swallowed input" failure (#284/#307).
765
+ let watchdogTicks = 0;
766
+ const watchdog = setInterval(() => {
767
+ watchdogTicks += 1;
768
+ const stdin = process.stdin;
769
+ logRestartEvent('parent: stdin watchdog', {
770
+ tick: watchdogTicks,
771
+ readableListeners: stdin.listenerCount('readable'),
772
+ dataListeners: stdin.listenerCount('data'),
773
+ paused: stdin.isPaused,
774
+ raw: stdin.isRaw === true,
775
+ buffered: stdin.readableLength,
776
+ });
777
+ try {
778
+ stdin.removeAllListeners('readable');
779
+ stdin.removeAllListeners('data');
780
+ stdin.pause();
781
+ }
782
+ catch {
783
+ // Diagnosis/mitigation only.
784
+ }
785
+ if (watchdogTicks === 15) {
786
+ clearInterval(watchdog);
787
+ try {
788
+ // Terminal safeguard: a destroyed stream can never be resumed by
789
+ // any late re-attachment. The child holds its own inherited
790
+ // handle, so closing ours does not affect it.
791
+ process.stdin.destroy();
792
+ logRestartEvent('parent: stdin destroyed after watchdog');
793
+ }
794
+ catch {
795
+ // Best effort.
796
+ }
797
+ }
798
+ }, 1000);
799
+ watchdog.unref();
800
+ let childStderr = '';
801
+ let loggedStderrBytes = 0;
802
+ child.stderr?.on('data', (chunk) => {
803
+ childStderr += chunk.toString('utf8');
804
+ // Mirror chunks to the log capped: the full text still reaches the
805
+ // terminal report below, and unbounded output must not grow the file.
806
+ if (loggedStderrBytes < 4096) {
807
+ loggedStderrBytes += chunk.length;
808
+ logRestartEvent(`${tag}: replacement stderr`, { text: chunk.toString('utf8').slice(0, 2048) });
809
+ }
810
+ });
811
+ // Diagnosis marker only — never resolves: after the window the child owns
812
+ // the terminal, and this process must keep the launch chain open until
813
+ // the replacement exits (see the header comment).
814
+ const timer = setTimeout(() => {
815
+ logRestartEvent(`${tag}: survival window passed, following the replacement until it exits`);
816
+ }, 4000);
817
+ timer.unref();
818
+ child.once('error', error => {
819
+ clearTimeout(timer);
820
+ logRestartEvent(`${tag}: spawn error`, { message: error.message });
821
+ writeHandoffNotice(`dsh-tui: failed to spawn the restart: ${error.message}\n`);
822
+ resolve(127);
823
+ });
824
+ child.once('close', (code, signal) => {
825
+ clearTimeout(timer);
826
+ const elapsedMs = Date.now() - startedAt;
827
+ logRestartEvent(`${tag}: replacement exited`, {
828
+ code: code ?? null,
829
+ signal: signal ?? null,
830
+ elapsedMs,
831
+ });
832
+ if (elapsedMs < 4000) {
833
+ // Fast death: the TUI never came up. Synchronous stderr write —
834
+ // process.exit() right after an async stream write skips the flush,
835
+ // and a vanished diagnosis is indistinguishable from silent failure.
836
+ const suffix = childStderr.trim() === '' ? '' : `\n${childStderr.trimEnd()}`;
837
+ writeHandoffNotice(`dsh-tui: restart child exited during the handoff (code ${code ?? 'null'}` +
838
+ `${signal === undefined || signal === null ? '' : `, signal ${signal}`}) — the TUI did not come up.` +
839
+ `${suffix}\nYour session is preserved; resume with the launcher or retry the command.\n`);
840
+ }
841
+ else if (code !== 0 && code !== null) {
842
+ // Late nonzero exit: the session ended abnormally, say so briefly.
843
+ writeHandoffNotice(`\ndsh-tui: the restarted session exited with code ${code}.\n`);
844
+ }
845
+ resolve(code ?? 1);
846
+ });
380
847
  });
381
- return { updateCode, restartCode };
382
848
  }
@@ -6,7 +6,7 @@
6
6
  */
7
7
  export declare function logForDebugging(message: string, fields?: Record<string, unknown>): void;
8
8
  /**
9
- * Mouse-chain diagnostics: appends to `~/.dsh-cc/mouse-debug.log` when
9
+ * Mouse-chain diagnostics: appends to `~/.dsh-tui/mouse-debug.log` when
10
10
  * `DSH_TUI_DEBUG_MOUSE` is set. Unlike logForDebugging (stderr), a file
11
11
  * keeps the fullscreen alt screen unpolluted and survives the session for
12
12
  * post-mortem reading. Every append is try/catch-guarded — diagnostics
@@ -1,5 +1,5 @@
1
1
  import { appendFileSync } from 'node:fs';
2
- import { homedir } from 'node:os';
2
+ import { DATA_DIR } from './paths.js';
3
3
  import { join } from 'node:path';
4
4
  /**
5
5
  * Debug logger for the ported Ink core. Writes to stderr only when
@@ -14,7 +14,7 @@ export function logForDebugging(message, fields) {
14
14
  process.stderr.write(`[dsh-tui] ${message}${suffix}\n`);
15
15
  }
16
16
  /**
17
- * Mouse-chain diagnostics: appends to `~/.dsh-cc/mouse-debug.log` when
17
+ * Mouse-chain diagnostics: appends to `~/.dsh-tui/mouse-debug.log` when
18
18
  * `DSH_TUI_DEBUG_MOUSE` is set. Unlike logForDebugging (stderr), a file
19
19
  * keeps the fullscreen alt screen unpolluted and survives the session for
20
20
  * post-mortem reading. Every append is try/catch-guarded — diagnostics
@@ -28,7 +28,7 @@ export function logMouseDebug(message, fields) {
28
28
  const suffix = fields === undefined ? '' : ` ${JSON.stringify(fields)}`;
29
29
  try {
30
30
  const line = `${new Date().toISOString()} ${message}${suffix}\n`;
31
- appendFileSync(join(homedir(), '.dsh-cc', 'mouse-debug.log'), line);
31
+ appendFileSync(join(DATA_DIR, 'mouse-debug.log'), line);
32
32
  }
33
33
  catch {
34
34
  // ignore — see doc comment