@jingyi0605/codingns4dsh 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (312) hide show
  1. package/README.md +1114 -0
  2. package/assets/provider-icons/claude-code.png +0 -0
  3. package/assets/provider-icons/codex.png +0 -0
  4. package/assets/provider-icons/command-code.svg +5 -0
  5. package/assets/provider-icons/deepseek-harness.svg +9 -0
  6. package/assets/provider-icons/gemini.png +0 -0
  7. package/assets/provider-icons/grok.png +0 -0
  8. package/assets/provider-icons/kimi.png +0 -0
  9. package/assets/provider-icons/opencode.png +0 -0
  10. package/assets/provider-icons/pi.svg +7 -0
  11. package/data/build/dist/bootstrap/dsh-connection-adapter.d.ts +97 -0
  12. package/data/build/dist/bootstrap/dsh-connection-adapter.js +169 -0
  13. package/data/build/dist/bootstrap/index.d.ts +38 -0
  14. package/data/build/dist/bootstrap/index.js +44 -0
  15. package/data/build/dist/client/account-bar.d.ts +9 -0
  16. package/data/build/dist/client/account-bar.js +398 -0
  17. package/data/build/dist/client/bundle.js +26167 -0
  18. package/data/build/dist/client/cli-catalog.d.ts +24 -0
  19. package/data/build/dist/client/cli-catalog.js +108 -0
  20. package/data/build/dist/client/cli-slots.d.ts +38 -0
  21. package/data/build/dist/client/cli-slots.js +297 -0
  22. package/data/build/dist/client/debug/ui.d.ts +12 -0
  23. package/data/build/dist/client/debug/ui.js +451 -0
  24. package/data/build/dist/client/dsh-h5-bootstrap.d.ts +64 -0
  25. package/data/build/dist/client/dsh-h5-bootstrap.js +293 -0
  26. package/data/build/dist/client/dsh-runtime-version.d.ts +3 -0
  27. package/data/build/dist/client/dsh-runtime-version.js +11 -0
  28. package/data/build/dist/client/external-tool-stream.d.ts +24 -0
  29. package/data/build/dist/client/external-tool-stream.js +114 -0
  30. package/data/build/dist/client/features/cli-adapters.d.ts +7 -0
  31. package/data/build/dist/client/features/cli-adapters.js +215 -0
  32. package/data/build/dist/client/features/debug.d.ts +4 -0
  33. package/data/build/dist/client/features/debug.js +24 -0
  34. package/data/build/dist/client/features/index.d.ts +33 -0
  35. package/data/build/dist/client/features/index.js +44 -0
  36. package/data/build/dist/client/features/lan-access-panel.d.ts +5 -0
  37. package/data/build/dist/client/features/lan-access-panel.js +146 -0
  38. package/data/build/dist/client/features/lan-access.d.ts +9 -0
  39. package/data/build/dist/client/features/lan-access.js +30 -0
  40. package/data/build/dist/client/features/login-protection-panel.d.ts +5 -0
  41. package/data/build/dist/client/features/login-protection-panel.js +91 -0
  42. package/data/build/dist/client/features/login-protection-session.d.ts +6 -0
  43. package/data/build/dist/client/features/login-protection-session.js +25 -0
  44. package/data/build/dist/client/features/login-protection.d.ts +4 -0
  45. package/data/build/dist/client/features/login-protection.js +23 -0
  46. package/data/build/dist/client/features/reverse-proxy-panel.d.ts +9 -0
  47. package/data/build/dist/client/features/reverse-proxy-panel.js +159 -0
  48. package/data/build/dist/client/features/reverse-proxy.d.ts +12 -0
  49. package/data/build/dist/client/features/reverse-proxy.js +112 -0
  50. package/data/build/dist/client/features/terminal-enhancement-panel.d.ts +5 -0
  51. package/data/build/dist/client/features/terminal-enhancement-panel.js +160 -0
  52. package/data/build/dist/client/features/terminal-enhancement.d.ts +4 -0
  53. package/data/build/dist/client/features/terminal-enhancement.js +24 -0
  54. package/data/build/dist/client/features/types.d.ts +58 -0
  55. package/data/build/dist/client/features/types.js +2 -0
  56. package/data/build/dist/client/features/workspace-session-enhancement-panel.d.ts +5 -0
  57. package/data/build/dist/client/features/workspace-session-enhancement-panel.js +66 -0
  58. package/data/build/dist/client/features/workspace-session-enhancement.d.ts +4 -0
  59. package/data/build/dist/client/features/workspace-session-enhancement.js +101 -0
  60. package/data/build/dist/client/h5-bootstrap-entry.d.ts +2 -0
  61. package/data/build/dist/client/h5-bootstrap-entry.js +17 -0
  62. package/data/build/dist/client/index.d.ts +31 -0
  63. package/data/build/dist/client/index.js +98 -0
  64. package/data/build/dist/client/lan-access.d.ts +28 -0
  65. package/data/build/dist/client/lan-access.js +68 -0
  66. package/data/build/dist/client/locale.d.ts +20 -0
  67. package/data/build/dist/client/locale.js +393 -0
  68. package/data/build/dist/client/provider-icon-assets.d.ts +2 -0
  69. package/data/build/dist/client/provider-icon-assets.js +23 -0
  70. package/data/build/dist/client/provider-icons.d.ts +19 -0
  71. package/data/build/dist/client/provider-icons.js +39 -0
  72. package/data/build/dist/client/remote-web-context.d.ts +57 -0
  73. package/data/build/dist/client/remote-web-context.js +939 -0
  74. package/data/build/dist/client/session-adapter-cache.d.ts +18 -0
  75. package/data/build/dist/client/session-adapter-cache.js +84 -0
  76. package/data/build/dist/client/settings-bridge.d.ts +32 -0
  77. package/data/build/dist/client/settings-bridge.js +126 -0
  78. package/data/build/dist/client/settings-section.d.ts +30 -0
  79. package/data/build/dist/client/settings-section.js +93 -0
  80. package/data/build/dist/client/subscription-slot.d.ts +18 -0
  81. package/data/build/dist/client/subscription-slot.js +223 -0
  82. package/data/build/dist/client/terminal/index.d.ts +4 -0
  83. package/data/build/dist/client/terminal/index.js +3 -0
  84. package/data/build/dist/client/terminal/model.d.ts +116 -0
  85. package/data/build/dist/client/terminal/model.js +435 -0
  86. package/data/build/dist/client/terminal/styles.d.ts +21 -0
  87. package/data/build/dist/client/terminal/styles.js +51 -0
  88. package/data/build/dist/client/terminal/ui.d.ts +19 -0
  89. package/data/build/dist/client/terminal/ui.js +257 -0
  90. package/data/build/dist/client/terminal/xterm-view.d.ts +13 -0
  91. package/data/build/dist/client/terminal/xterm-view.js +213 -0
  92. package/data/build/dist/client/theme.d.ts +56 -0
  93. package/data/build/dist/client/theme.js +186 -0
  94. package/data/build/dist/client/workspace-session-archive-dom.d.ts +29 -0
  95. package/data/build/dist/client/workspace-session-archive-dom.js +525 -0
  96. package/data/build/dist/client/workspace-session-fiber.d.ts +3 -0
  97. package/data/build/dist/client/workspace-session-fiber.js +33 -0
  98. package/data/build/dist/client/workspace-session-logo-dom.d.ts +26 -0
  99. package/data/build/dist/client/workspace-session-logo-dom.js +128 -0
  100. package/data/build/dist/features/index.d.ts +3 -0
  101. package/data/build/dist/features/index.js +2 -0
  102. package/data/build/dist/features/registry.d.ts +74 -0
  103. package/data/build/dist/features/registry.js +382 -0
  104. package/data/build/dist/features/resource-scope/index.d.ts +39 -0
  105. package/data/build/dist/features/resource-scope/index.js +161 -0
  106. package/data/build/dist/host/auth-session.d.ts +42 -0
  107. package/data/build/dist/host/auth-session.js +186 -0
  108. package/data/build/dist/host/cli-adapters/claude-driver.d.ts +14 -0
  109. package/data/build/dist/host/cli-adapters/claude-driver.js +103 -0
  110. package/data/build/dist/host/cli-adapters/codex-driver.d.ts +45 -0
  111. package/data/build/dist/host/cli-adapters/codex-driver.js +534 -0
  112. package/data/build/dist/host/cli-adapters/command-code-driver.d.ts +38 -0
  113. package/data/build/dist/host/cli-adapters/command-code-driver.js +394 -0
  114. package/data/build/dist/host/cli-adapters/command-code-subscription.d.ts +19 -0
  115. package/data/build/dist/host/cli-adapters/command-code-subscription.js +126 -0
  116. package/data/build/dist/host/cli-adapters/driver.d.ts +41 -0
  117. package/data/build/dist/host/cli-adapters/driver.js +2 -0
  118. package/data/build/dist/host/cli-adapters/dsh-message-projector.d.ts +37 -0
  119. package/data/build/dist/host/cli-adapters/dsh-message-projector.js +180 -0
  120. package/data/build/dist/host/cli-adapters/dsh-tool-history.d.ts +48 -0
  121. package/data/build/dist/host/cli-adapters/dsh-tool-history.js +373 -0
  122. package/data/build/dist/host/cli-adapters/feature.d.ts +7 -0
  123. package/data/build/dist/host/cli-adapters/feature.js +335 -0
  124. package/data/build/dist/host/cli-adapters/gemini-driver.d.ts +24 -0
  125. package/data/build/dist/host/cli-adapters/gemini-driver.js +427 -0
  126. package/data/build/dist/host/cli-adapters/grok-driver.d.ts +38 -0
  127. package/data/build/dist/host/cli-adapters/grok-driver.js +372 -0
  128. package/data/build/dist/host/cli-adapters/http-sse-client.d.ts +22 -0
  129. package/data/build/dist/host/cli-adapters/http-sse-client.js +99 -0
  130. package/data/build/dist/host/cli-adapters/interaction-events.d.ts +11 -0
  131. package/data/build/dist/host/cli-adapters/interaction-events.js +80 -0
  132. package/data/build/dist/host/cli-adapters/json-rpc-process.d.ts +65 -0
  133. package/data/build/dist/host/cli-adapters/json-rpc-process.js +256 -0
  134. package/data/build/dist/host/cli-adapters/kimi-driver.d.ts +21 -0
  135. package/data/build/dist/host/cli-adapters/kimi-driver.js +382 -0
  136. package/data/build/dist/host/cli-adapters/model-catalog.d.ts +22 -0
  137. package/data/build/dist/host/cli-adapters/model-catalog.js +89 -0
  138. package/data/build/dist/host/cli-adapters/opencode-driver.d.ts +55 -0
  139. package/data/build/dist/host/cli-adapters/opencode-driver.js +728 -0
  140. package/data/build/dist/host/cli-adapters/pi-driver.d.ts +43 -0
  141. package/data/build/dist/host/cli-adapters/pi-driver.js +312 -0
  142. package/data/build/dist/host/cli-adapters/process-utils.d.ts +16 -0
  143. package/data/build/dist/host/cli-adapters/process-utils.js +27 -0
  144. package/data/build/dist/host/cli-adapters/provider-subscription.d.ts +82 -0
  145. package/data/build/dist/host/cli-adapters/provider-subscription.js +610 -0
  146. package/data/build/dist/host/cli-adapters/registry.d.ts +95 -0
  147. package/data/build/dist/host/cli-adapters/registry.js +659 -0
  148. package/data/build/dist/host/cli-adapters/rpc-driver-utils.d.ts +24 -0
  149. package/data/build/dist/host/cli-adapters/rpc-driver-utils.js +119 -0
  150. package/data/build/dist/host/cli-adapters/session-probe.d.ts +20 -0
  151. package/data/build/dist/host/cli-adapters/session-probe.js +139 -0
  152. package/data/build/dist/host/cli-adapters/session-store.d.ts +68 -0
  153. package/data/build/dist/host/cli-adapters/session-store.js +180 -0
  154. package/data/build/dist/host/cli-adapters/standard-stream-driver.d.ts +45 -0
  155. package/data/build/dist/host/cli-adapters/standard-stream-driver.js +205 -0
  156. package/data/build/dist/host/cli-adapters/stream-normalizer.d.ts +25 -0
  157. package/data/build/dist/host/cli-adapters/stream-normalizer.js +83 -0
  158. package/data/build/dist/host/cli-adapters/tool-observation.d.ts +9 -0
  159. package/data/build/dist/host/cli-adapters/tool-observation.js +39 -0
  160. package/data/build/dist/host/control-api-client.d.ts +84 -0
  161. package/data/build/dist/host/control-api-client.js +130 -0
  162. package/data/build/dist/host/credential-store.d.ts +54 -0
  163. package/data/build/dist/host/credential-store.js +114 -0
  164. package/data/build/dist/host/debug.d.ts +79 -0
  165. package/data/build/dist/host/debug.js +295 -0
  166. package/data/build/dist/host/dsh-device-runtime.d.ts +39 -0
  167. package/data/build/dist/host/dsh-device-runtime.js +118 -0
  168. package/data/build/dist/host/dsh-gateway-feature.d.ts +5 -0
  169. package/data/build/dist/host/dsh-gateway-feature.js +55 -0
  170. package/data/build/dist/host/dsh-runtime-version.d.ts +12 -0
  171. package/data/build/dist/host/dsh-runtime-version.js +69 -0
  172. package/data/build/dist/host/features/auth.d.ts +11 -0
  173. package/data/build/dist/host/features/auth.js +273 -0
  174. package/data/build/dist/host/features/debug.d.ts +5 -0
  175. package/data/build/dist/host/features/debug.js +90 -0
  176. package/data/build/dist/host/features/host-status.d.ts +12 -0
  177. package/data/build/dist/host/features/host-status.js +54 -0
  178. package/data/build/dist/host/features/index.d.ts +19 -0
  179. package/data/build/dist/host/features/index.js +29 -0
  180. package/data/build/dist/host/features/lan-access-dsh.d.ts +8 -0
  181. package/data/build/dist/host/features/lan-access-dsh.js +45 -0
  182. package/data/build/dist/host/features/terminal-process.d.ts +5 -0
  183. package/data/build/dist/host/features/terminal-process.js +110 -0
  184. package/data/build/dist/host/features/terminal-status.d.ts +18 -0
  185. package/data/build/dist/host/features/terminal-status.js +56 -0
  186. package/data/build/dist/host/features/types.d.ts +41 -0
  187. package/data/build/dist/host/features/types.js +2 -0
  188. package/data/build/dist/host/index.d.ts +43 -0
  189. package/data/build/dist/host/index.js +135 -0
  190. package/data/build/dist/host/lan-access-dsh.d.ts +83 -0
  191. package/data/build/dist/host/lan-access-dsh.js +831 -0
  192. package/data/build/dist/host/native-session-bridge.d.ts +130 -0
  193. package/data/build/dist/host/native-session-bridge.js +317 -0
  194. package/data/build/dist/host/relay-tunnel-runtime.d.ts +97 -0
  195. package/data/build/dist/host/relay-tunnel-runtime.js +361 -0
  196. package/data/build/dist/host/remote-web-runtime.d.ts +78 -0
  197. package/data/build/dist/host/remote-web-runtime.js +559 -0
  198. package/data/build/dist/host/rpc-table.d.ts +32 -0
  199. package/data/build/dist/host/rpc-table.js +47 -0
  200. package/data/build/dist/host/rpc.d.ts +18 -0
  201. package/data/build/dist/host/rpc.js +248 -0
  202. package/data/build/dist/host/settings.d.ts +19 -0
  203. package/data/build/dist/host/settings.js +80 -0
  204. package/data/build/dist/host/terminal/attachment-registry.d.ts +17 -0
  205. package/data/build/dist/host/terminal/attachment-registry.js +49 -0
  206. package/data/build/dist/host/terminal/backends/conpty-backend.d.ts +39 -0
  207. package/data/build/dist/host/terminal/backends/conpty-backend.js +160 -0
  208. package/data/build/dist/host/terminal/backends/local-pty-backend.d.ts +36 -0
  209. package/data/build/dist/host/terminal/backends/local-pty-backend.js +178 -0
  210. package/data/build/dist/host/terminal/backends/tmux-backend.d.ts +40 -0
  211. package/data/build/dist/host/terminal/backends/tmux-backend.js +191 -0
  212. package/data/build/dist/host/terminal/broker/conpty-broker-client.d.ts +27 -0
  213. package/data/build/dist/host/terminal/broker/conpty-broker-client.js +154 -0
  214. package/data/build/dist/host/terminal/broker/conpty-broker-process.d.ts +10 -0
  215. package/data/build/dist/host/terminal/broker/conpty-broker-process.js +147 -0
  216. package/data/build/dist/host/terminal/broker/conpty-broker-protocol.d.ts +92 -0
  217. package/data/build/dist/host/terminal/broker/conpty-broker-protocol.js +134 -0
  218. package/data/build/dist/host/terminal/controller-factory.d.ts +37 -0
  219. package/data/build/dist/host/terminal/controller-factory.js +82 -0
  220. package/data/build/dist/host/terminal/index.d.ts +17 -0
  221. package/data/build/dist/host/terminal/index.js +17 -0
  222. package/data/build/dist/host/terminal/runtime-adapter.d.ts +77 -0
  223. package/data/build/dist/host/terminal/runtime-adapter.js +18 -0
  224. package/data/build/dist/host/terminal/runtime-manager.d.ts +41 -0
  225. package/data/build/dist/host/terminal/runtime-manager.js +179 -0
  226. package/data/build/dist/host/terminal/shell-detection.d.ts +23 -0
  227. package/data/build/dist/host/terminal/shell-detection.js +105 -0
  228. package/data/build/dist/host/terminal/startup.d.ts +33 -0
  229. package/data/build/dist/host/terminal/startup.js +100 -0
  230. package/data/build/dist/host/terminal/terminal-controller.d.ts +78 -0
  231. package/data/build/dist/host/terminal/terminal-controller.js +324 -0
  232. package/data/build/dist/host/terminal/terminal-process-service.d.ts +30 -0
  233. package/data/build/dist/host/terminal/terminal-process-service.js +194 -0
  234. package/data/build/dist/host/terminal/terminal-process-store.d.ts +45 -0
  235. package/data/build/dist/host/terminal/terminal-process-store.js +183 -0
  236. package/data/build/dist/host/terminal/terminal-service.d.ts +87 -0
  237. package/data/build/dist/host/terminal/terminal-service.js +528 -0
  238. package/data/build/dist/host/terminal/terminal-store.d.ts +49 -0
  239. package/data/build/dist/host/terminal/terminal-store.js +242 -0
  240. package/data/build/dist/index.d.ts +37 -0
  241. package/data/build/dist/index.js +26 -0
  242. package/data/build/dist/shared/contracts/auth.d.ts +122 -0
  243. package/data/build/dist/shared/contracts/auth.js +8 -0
  244. package/data/build/dist/shared/contracts/cli-adapter.d.ts +192 -0
  245. package/data/build/dist/shared/contracts/cli-adapter.js +2 -0
  246. package/data/build/dist/shared/contracts/command-code.d.ts +5 -0
  247. package/data/build/dist/shared/contracts/command-code.js +2 -0
  248. package/data/build/dist/shared/contracts/config.d.ts +113 -0
  249. package/data/build/dist/shared/contracts/config.js +86 -0
  250. package/data/build/dist/shared/contracts/debug-config.d.ts +28 -0
  251. package/data/build/dist/shared/contracts/debug-config.js +78 -0
  252. package/data/build/dist/shared/contracts/debug.d.ts +75 -0
  253. package/data/build/dist/shared/contracts/debug.js +121 -0
  254. package/data/build/dist/shared/contracts/dsh-device.d.ts +72 -0
  255. package/data/build/dist/shared/contracts/dsh-device.js +2 -0
  256. package/data/build/dist/shared/contracts/errors.d.ts +33 -0
  257. package/data/build/dist/shared/contracts/errors.js +41 -0
  258. package/data/build/dist/shared/contracts/feature.d.ts +71 -0
  259. package/data/build/dist/shared/contracts/feature.js +2 -0
  260. package/data/build/dist/shared/contracts/host-status.d.ts +14 -0
  261. package/data/build/dist/shared/contracts/host-status.js +2 -0
  262. package/data/build/dist/shared/contracts/lan-access-dsh.d.ts +30 -0
  263. package/data/build/dist/shared/contracts/lan-access-dsh.js +2 -0
  264. package/data/build/dist/shared/contracts/peer-host.d.ts +37 -0
  265. package/data/build/dist/shared/contracts/peer-host.js +9 -0
  266. package/data/build/dist/shared/contracts/signaling.d.ts +67 -0
  267. package/data/build/dist/shared/contracts/signaling.js +2 -0
  268. package/data/build/dist/shared/contracts/subscription.d.ts +69 -0
  269. package/data/build/dist/shared/contracts/subscription.js +2 -0
  270. package/data/build/dist/shared/contracts/terminal-process.d.ts +65 -0
  271. package/data/build/dist/shared/contracts/terminal-process.js +2 -0
  272. package/data/build/dist/shared/contracts/terminal.d.ts +127 -0
  273. package/data/build/dist/shared/contracts/terminal.js +2 -0
  274. package/data/build/dist/shared/contracts/transport.d.ts +45 -0
  275. package/data/build/dist/shared/contracts/transport.js +3 -0
  276. package/data/build/dist/shared/contracts/version.d.ts +19 -0
  277. package/data/build/dist/shared/contracts/version.js +74 -0
  278. package/data/build/dist/shared/index.d.ts +22 -0
  279. package/data/build/dist/shared/index.js +9 -0
  280. package/data/build/dist/transport/carrier.d.ts +38 -0
  281. package/data/build/dist/transport/carrier.js +268 -0
  282. package/data/build/dist/transport/debug.d.ts +22 -0
  283. package/data/build/dist/transport/debug.js +57 -0
  284. package/data/build/dist/transport/dsh-envelope.d.ts +41 -0
  285. package/data/build/dist/transport/dsh-envelope.js +107 -0
  286. package/data/build/dist/transport/dsh-gateway.d.ts +64 -0
  287. package/data/build/dist/transport/dsh-gateway.js +254 -0
  288. package/data/build/dist/transport/dsh-session.d.ts +55 -0
  289. package/data/build/dist/transport/dsh-session.js +256 -0
  290. package/data/build/dist/transport/dsh-transport.d.ts +57 -0
  291. package/data/build/dist/transport/dsh-transport.js +125 -0
  292. package/data/build/dist/transport/frame.d.ts +132 -0
  293. package/data/build/dist/transport/frame.js +168 -0
  294. package/data/build/dist/transport/index.d.ts +11 -0
  295. package/data/build/dist/transport/index.js +11 -0
  296. package/data/build/dist/transport/multiplexer.d.ts +63 -0
  297. package/data/build/dist/transport/multiplexer.js +323 -0
  298. package/data/build/dist/transport/webrtc-client.d.ts +65 -0
  299. package/data/build/dist/transport/webrtc-client.js +255 -0
  300. package/data/build/dist/transport/webrtc-host.d.ts +58 -0
  301. package/data/build/dist/transport/webrtc-host.js +252 -0
  302. package/data/build/dist/typert.host.d.ts +25 -0
  303. package/data/build/dist/typert.host.js +150 -0
  304. package/dsh.bundle.patch +12 -0
  305. package/package.json +135 -0
  306. package/profile/README.md +27 -0
  307. package/profile/cordis.patch.yml +2 -0
  308. package/profile/package.json +20 -0
  309. package/profile/scripts/check-dsh-install.mjs +71 -0
  310. package/profile/version.json +5 -0
  311. package/scripts/check-dsh-install.mjs +88 -0
  312. package/version.json +6 -0
@@ -0,0 +1,528 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ /**
3
+ * 持久终端生命周期服务。
4
+ *
5
+ * store 决定“终端是什么”,runtime manager 决定“当前如何连上它”。插件停用时
6
+ * dispose 只释放后者;只有 close 会调用 backend.terminate。
7
+ */
8
+ export class CodingNsTerminalService {
9
+ store;
10
+ runtimes;
11
+ now;
12
+ controllers = new Map();
13
+ followers = new Map();
14
+ operations = new Map();
15
+ exitCallbacks = new Map();
16
+ initialized = false;
17
+ constructor(store, runtimes, now = () => new Date()) {
18
+ this.store = store;
19
+ this.runtimes = runtimes;
20
+ this.now = now;
21
+ }
22
+ async initialize() {
23
+ if (this.initialized)
24
+ return;
25
+ await this.store.load();
26
+ this.initialized = true;
27
+ }
28
+ list(scope) {
29
+ this.requireInitialized();
30
+ return this.store.list(scope)
31
+ .filter((record) => record.state !== 'closed')
32
+ .map((record) => this.info(record));
33
+ }
34
+ /** 兼容旧调用名;真正的持久归属只按 Host + workspace 查询。 */
35
+ listSession(hostId, dshSessionId, workspaceId) {
36
+ this.requireInitialized();
37
+ if (workspaceId !== undefined)
38
+ return this.listWorkspace(hostId, workspaceId);
39
+ return this.store.list()
40
+ .filter((record) => record.hostId === hostId && record.dshSessionId === dshSessionId && record.state !== 'closed')
41
+ .map((record) => this.info(record));
42
+ }
43
+ listWorkspace(hostId, workspaceId) {
44
+ this.requireInitialized();
45
+ return this.store.list({ hostId, workspaceId })
46
+ .filter((record) => record.state !== 'closed')
47
+ .map((record) => this.info(record));
48
+ }
49
+ findIdentity(hostId, dshSessionId, terminalId, workspaceId) {
50
+ this.requireInitialized();
51
+ const matches = this.store.list().filter((record) => record.hostId === hostId
52
+ && (workspaceId === undefined ? record.dshSessionId === dshSessionId : record.workspaceId === workspaceId)
53
+ && record.terminalId === terminalId
54
+ && record.state !== 'closed');
55
+ if (matches.length !== 1)
56
+ throw new TerminalServiceError('TERMINAL_UNAVAILABLE', '终端不存在或作用域不唯一');
57
+ const record = matches[0];
58
+ return {
59
+ hostId: record.hostId,
60
+ workspaceId: record.workspaceId,
61
+ dshSessionId,
62
+ terminalId: record.terminalId,
63
+ };
64
+ }
65
+ async create(input) {
66
+ this.requireInitialized();
67
+ validateTerminalId(input.terminalId);
68
+ validateSize(input.cols, input.rows);
69
+ const identity = { ...input.scope, terminalId: input.terminalId };
70
+ return this.enqueue(identity, async () => {
71
+ const existing = this.store.get(identity);
72
+ if (existing !== undefined) {
73
+ if (existing.state === 'closed' || existing.state === 'closing') {
74
+ throw new TerminalServiceError('TERMINAL_UNAVAILABLE', '终端已关闭,不能复用相同标识');
75
+ }
76
+ const runtime = await this.runtimes.inspect(existing);
77
+ if (runtime.alive) {
78
+ const running = existing.state === 'running'
79
+ ? existing
80
+ : await this.update(existing, { state: 'running', error: '' });
81
+ return this.info(running);
82
+ }
83
+ await this.update(existing, { state: 'lost', error: '持久终端运行时不存在' });
84
+ throw new TerminalServiceError('TERMINAL_RUNTIME_LOST', '持久终端运行时不存在');
85
+ }
86
+ const timestamp = this.now().toISOString();
87
+ const record = {
88
+ ...input.scope,
89
+ terminalId: input.terminalId,
90
+ runtimeSessionKey: randomUUID(),
91
+ runtimeType: input.runtimeType,
92
+ shellPath: input.shell.path,
93
+ shellProfileId: input.shell.profileId,
94
+ shellName: input.shell.name,
95
+ shellArgs: [...input.shell.args],
96
+ ...(input.commandPath === undefined ? {} : { commandPath: input.commandPath }),
97
+ ...(input.commandArgs === undefined ? {} : { commandArgs: [...input.commandArgs] }),
98
+ ...(input.commandEnv === undefined ? {} : { commandEnv: { ...input.commandEnv } }),
99
+ ...(input.launchProfileId === undefined ? {} : { launchProfileId: input.launchProfileId }),
100
+ cwd: input.cwd,
101
+ title: input.title?.trim() || input.shell.name,
102
+ cols: input.cols,
103
+ rows: input.rows,
104
+ state: 'starting',
105
+ exitCode: null,
106
+ createdAt: timestamp,
107
+ updatedAt: timestamp,
108
+ };
109
+ await this.store.put(record);
110
+ try {
111
+ const runtime = await this.runtimes.create(record);
112
+ if (!runtime.alive)
113
+ throw new Error('backend 创建后未报告存活状态');
114
+ if (input.onExit !== undefined)
115
+ this.exitCallbacks.set(identityKey(identity), input.onExit);
116
+ const running = await this.update(record, { state: 'running', error: '' });
117
+ if (input.onExit !== undefined)
118
+ await this.runtimes.monitor(running, (exitCode) => { void this.markExited(identity, exitCode); });
119
+ return this.info(running);
120
+ }
121
+ catch (error) {
122
+ this.exitCallbacks.delete(identityKey(identity));
123
+ await this.update(record, { state: 'error', error: errorMessage(error) });
124
+ throw error;
125
+ }
126
+ });
127
+ }
128
+ /** 返回 Host 侧完整记录,供进程服务读取 runtimeSessionKey 和运行身份。 */
129
+ getRecord(identity) {
130
+ this.requireInitialized();
131
+ return this.store.get(identity);
132
+ }
133
+ async inspect(identity) {
134
+ const record = this.getRecord(identity);
135
+ if (record === undefined)
136
+ throw new TerminalServiceError('TERMINAL_UNAVAILABLE', '终端不存在');
137
+ return this.runtimes.inspect(record);
138
+ }
139
+ async recover() {
140
+ this.requireInitialized();
141
+ for (const record of this.store.list()) {
142
+ if (record.state === 'closed' || record.state === 'exited')
143
+ continue;
144
+ await this.enqueue(record, async () => {
145
+ const current = this.store.get(record);
146
+ if (current === undefined || current.state === 'closed')
147
+ return;
148
+ if (current.state === 'closing') {
149
+ await this.runtimes.terminate(current);
150
+ await this.runtimes.detachTerminal(current);
151
+ await this.update(current, { state: 'closed', exitCode: null, error: '' });
152
+ return;
153
+ }
154
+ try {
155
+ const identity = await this.runtimes.inspect(current);
156
+ await this.update(current, identity.alive
157
+ ? { state: 'running', error: '' }
158
+ : { state: 'lost', error: identity.detail ?? '持久终端运行时不存在' });
159
+ }
160
+ catch (error) {
161
+ await this.update(current, { state: 'error', error: errorMessage(error) });
162
+ }
163
+ });
164
+ }
165
+ }
166
+ async *retain(identity, signal) {
167
+ this.requireAvailable(identity);
168
+ signal.throwIfAborted();
169
+ yield { type: 'retained' };
170
+ await untilAborted(signal);
171
+ }
172
+ async *follow(input) {
173
+ const record = this.requireAvailable(input.identity);
174
+ const key = identityKey(input.identity);
175
+ const queue = new TerminalFrameQueue(2 * 1024 * 1024);
176
+ const follower = { attachmentId: input.attachmentId, generation: input.generation, queue };
177
+ this.addFollower(key, follower);
178
+ // 无论 backend attach 是否同步吐出恢复画面,wire 的第一帧都必须是 snapshot。
179
+ queue.pushSnapshot(this.info(record));
180
+ let attachment;
181
+ try {
182
+ attachment = await this.runtimes.attach(record, {
183
+ identity: input.identity,
184
+ generation: input.generation,
185
+ streamId: input.attachmentId,
186
+ requestedAttachmentId: input.attachmentId,
187
+ cols: record.cols,
188
+ rows: record.rows,
189
+ onData: (data) => queue.pushOutput(data),
190
+ onExit: (exitCode) => { void this.handleRuntimeExit(input.identity, exitCode, queue); },
191
+ });
192
+ }
193
+ catch (error) {
194
+ this.removeFollower(key, follower);
195
+ queue.finish();
196
+ throw error;
197
+ }
198
+ this.controllers.set(key, {
199
+ attachmentId: input.attachmentId,
200
+ subscriptionId: attachment.subscriptionId,
201
+ generation: input.generation,
202
+ });
203
+ this.broadcastState(input.identity);
204
+ const abort = () => queue.finish();
205
+ input.signal.addEventListener('abort', abort, { once: true });
206
+ if (input.signal.aborted)
207
+ abort();
208
+ try {
209
+ yield* queue.read();
210
+ }
211
+ finally {
212
+ input.signal.removeEventListener('abort', abort);
213
+ this.removeFollower(key, follower);
214
+ if (this.controllers.get(key)?.subscriptionId === attachment.subscriptionId) {
215
+ this.controllers.delete(key);
216
+ this.broadcastState(input.identity);
217
+ }
218
+ await this.runtimes.detach(attachment.subscriptionId);
219
+ }
220
+ }
221
+ async write(identity, attachmentId, data) {
222
+ const controller = this.requireController(identity, attachmentId);
223
+ await this.runtimes.write(controller.subscriptionId, data);
224
+ }
225
+ /** Host 内部向持久终端发送一次输入,不改变浏览器 attach 控制权。 */
226
+ async writeInitialInput(identity, data) {
227
+ if (data.length === 0)
228
+ throw new TypeError('终端输入不能为空');
229
+ this.requireInitialized();
230
+ await this.enqueue(identity, async () => {
231
+ const record = this.requireAvailable(identity);
232
+ await this.runtimes.writeSession(record, data);
233
+ });
234
+ }
235
+ async resize(identity, attachmentId, cols, rows) {
236
+ validateSize(cols, rows);
237
+ const controller = this.requireController(identity, attachmentId);
238
+ const record = this.requireAvailable(identity);
239
+ // ResizeObserver 可能重复报告同一尺寸;相同尺寸不应再次向 PTY 发送 SIGWINCH。
240
+ if (record.cols === cols && record.rows === rows)
241
+ return;
242
+ await this.runtimes.resize(controller.subscriptionId, cols, rows);
243
+ await this.update(record, { cols, rows });
244
+ this.broadcastState(identity);
245
+ }
246
+ async rename(identity, title) {
247
+ const normalized = title.trim();
248
+ if (normalized.length === 0 || normalized.length > 120)
249
+ throw new TypeError('终端标题必须包含 1 到 120 个字符');
250
+ const record = this.requireAvailable(identity);
251
+ await this.update(record, { title: normalized });
252
+ this.broadcastState(identity);
253
+ }
254
+ async close(identity) {
255
+ this.requireInitialized();
256
+ await this.enqueue(identity, async () => {
257
+ const record = this.store.get(identity);
258
+ if (record === undefined || record.state === 'closed')
259
+ return;
260
+ const closing = record.state === 'closing' ? record : await this.update(record, { state: 'closing' });
261
+ this.broadcastState(identity);
262
+ await this.runtimes.terminate(closing);
263
+ await this.runtimes.detachTerminal(identity);
264
+ this.controllers.delete(identityKey(identity));
265
+ this.exitCallbacks.delete(identityKey(identity));
266
+ const closed = await this.update(closing, { state: 'closed', exitCode: null, error: '' });
267
+ this.broadcastState(identity, closed);
268
+ this.finishFollowers(identityKey(identity));
269
+ });
270
+ }
271
+ async detachGeneration(generation) {
272
+ await this.runtimes.detachGeneration(generation);
273
+ const changed = new Set();
274
+ for (const [key, binding] of this.controllers) {
275
+ if (binding.generation !== generation)
276
+ continue;
277
+ this.controllers.delete(key);
278
+ changed.add(key);
279
+ }
280
+ for (const [key, followers] of this.followers) {
281
+ for (const follower of followers) {
282
+ if (follower.generation === generation)
283
+ follower.queue.finish();
284
+ }
285
+ if ([...followers].some((follower) => follower.generation === generation))
286
+ changed.add(key);
287
+ }
288
+ for (const key of changed)
289
+ this.broadcastStateByKey(key);
290
+ }
291
+ /** 插件卸载只断开 attach,持久 backend 必须继续运行。 */
292
+ async dispose() {
293
+ for (const followers of this.followers.values())
294
+ for (const follower of followers)
295
+ follower.queue.finish();
296
+ this.followers.clear();
297
+ this.controllers.clear();
298
+ this.exitCallbacks.clear();
299
+ await this.runtimes.dispose();
300
+ }
301
+ requireController(identity, attachmentId) {
302
+ this.requireAvailable(identity);
303
+ const controller = this.controllers.get(identityKey(identity));
304
+ if (controller?.attachmentId !== attachmentId) {
305
+ throw new TerminalServiceError('TERMINAL_CONTROL_UNAVAILABLE', '当前 attach 没有终端输入控制权');
306
+ }
307
+ return controller;
308
+ }
309
+ requireAvailable(identity) {
310
+ this.requireInitialized();
311
+ const record = this.store.get(identity);
312
+ if (record === undefined || record.state !== 'running') {
313
+ throw new TerminalServiceError('TERMINAL_UNAVAILABLE', '终端不存在或当前不可用');
314
+ }
315
+ return record;
316
+ }
317
+ async refreshRuntimeState(identity) {
318
+ const record = this.store.get(identity);
319
+ if (record === undefined || record.state !== 'running')
320
+ return;
321
+ try {
322
+ const runtime = await this.runtimes.inspect(record);
323
+ if (!runtime.alive) {
324
+ const lost = await this.update(record, { state: 'lost', error: runtime.detail ?? '持久终端运行时不存在' });
325
+ this.broadcastState(identity, lost);
326
+ }
327
+ }
328
+ catch (error) {
329
+ const failed = await this.update(record, { state: 'error', error: errorMessage(error) });
330
+ this.broadcastState(identity, failed);
331
+ }
332
+ }
333
+ async handleRuntimeExit(identity, exitCode, queue) {
334
+ try {
335
+ await this.markExited(identity, exitCode);
336
+ }
337
+ finally {
338
+ // 先发 exited 元数据,再结束 Remote 流;否则官方 Client 会把正常退出误判为 attach 故障。
339
+ queue.finish();
340
+ }
341
+ }
342
+ async markExited(identity, exitCode) {
343
+ await this.enqueue(identity, async () => {
344
+ const record = this.store.get(identity);
345
+ if (record === undefined || record.state !== 'running')
346
+ return;
347
+ const exited = await this.update(record, { state: 'exited', exitCode, error: '' });
348
+ this.broadcastState(identity, exited);
349
+ const callback = this.exitCallbacks.get(identityKey(identity));
350
+ this.exitCallbacks.delete(identityKey(identity));
351
+ await callback?.(exitCode);
352
+ });
353
+ }
354
+ info(record) {
355
+ const controllerId = this.controllers.get(identityKey(record))?.attachmentId;
356
+ const state = record.state === 'running' || record.state === 'starting'
357
+ ? 'running'
358
+ : record.state === 'closed' || record.state === 'exited'
359
+ ? 'exited'
360
+ : 'failed';
361
+ return {
362
+ id: record.terminalId,
363
+ title: record.title,
364
+ shell: shellInfo(record),
365
+ cwd: record.cwd,
366
+ cols: record.cols,
367
+ rows: record.rows,
368
+ state,
369
+ exitCode: record.exitCode,
370
+ ...(record.error && record.state !== 'closed' ? { error: record.error } : {}),
371
+ ...(controllerId === undefined ? {} : { controllerId }),
372
+ };
373
+ }
374
+ async update(record, patch) {
375
+ const next = await this.store.patch(record, { ...patch, updatedAt: this.now().toISOString() });
376
+ if (next === undefined)
377
+ throw new Error('终端记录在更新期间消失');
378
+ return next;
379
+ }
380
+ broadcastState(identity, record = this.store.get(identity)) {
381
+ if (record === undefined)
382
+ return;
383
+ const info = this.info(record);
384
+ for (const follower of this.followers.get(identityKey(identity)) ?? [])
385
+ follower.queue.pushState(info);
386
+ }
387
+ broadcastStateByKey(key) {
388
+ const record = this.store.list().find((candidate) => identityKey(candidate) === key);
389
+ if (record !== undefined)
390
+ this.broadcastState(record, record);
391
+ }
392
+ addFollower(key, follower) {
393
+ let followers = this.followers.get(key);
394
+ if (followers === undefined) {
395
+ followers = new Set();
396
+ this.followers.set(key, followers);
397
+ }
398
+ followers.add(follower);
399
+ }
400
+ removeFollower(key, follower) {
401
+ const followers = this.followers.get(key);
402
+ followers?.delete(follower);
403
+ if (followers?.size === 0)
404
+ this.followers.delete(key);
405
+ }
406
+ finishFollowers(key) {
407
+ for (const follower of this.followers.get(key) ?? [])
408
+ follower.queue.finish();
409
+ }
410
+ enqueue(identity, operation) {
411
+ const key = identityKey(identity);
412
+ const previous = this.operations.get(key) ?? Promise.resolve();
413
+ const pending = previous.then(operation, operation);
414
+ const tail = pending.then(() => undefined, () => undefined);
415
+ this.operations.set(key, tail);
416
+ void tail.finally(() => {
417
+ if (this.operations.get(key) === tail)
418
+ this.operations.delete(key);
419
+ });
420
+ return pending;
421
+ }
422
+ requireInitialized() {
423
+ if (!this.initialized)
424
+ throw new Error('终端服务尚未初始化');
425
+ }
426
+ }
427
+ export class TerminalServiceError extends Error {
428
+ code;
429
+ constructor(code, message) {
430
+ super(message);
431
+ this.code = code;
432
+ this.name = 'TerminalServiceError';
433
+ }
434
+ }
435
+ class TerminalFrameQueue {
436
+ maxBytes;
437
+ frames = [];
438
+ bytes = 0;
439
+ sequence = 0;
440
+ wake;
441
+ ended = false;
442
+ failure;
443
+ constructor(maxBytes) {
444
+ this.maxBytes = maxBytes;
445
+ }
446
+ pushSnapshot(info) {
447
+ this.push({ type: 'snapshot', sequence: this.sequence, screen: '', info });
448
+ }
449
+ pushOutput(data) {
450
+ this.sequence += 1;
451
+ this.push({ type: 'output', sequence: this.sequence, data });
452
+ }
453
+ pushState(info) {
454
+ this.push({ type: 'state', info });
455
+ }
456
+ finish() {
457
+ this.ended = true;
458
+ this.wake?.();
459
+ }
460
+ async *read() {
461
+ while (!this.ended || this.frames.length > 0) {
462
+ const frame = this.frames.shift();
463
+ if (frame !== undefined) {
464
+ this.bytes -= frameBytes(frame);
465
+ yield frame;
466
+ continue;
467
+ }
468
+ await new Promise((resolve) => { this.wake = resolve; });
469
+ this.wake = undefined;
470
+ }
471
+ if (this.failure !== undefined)
472
+ throw this.failure;
473
+ }
474
+ push(frame) {
475
+ if (this.ended)
476
+ return;
477
+ const bytes = frameBytes(frame);
478
+ if (this.bytes + bytes > this.maxBytes) {
479
+ this.failure = new Error('终端输出消费者超过缓冲上限,请重新连接以恢复当前屏幕');
480
+ this.frames.length = 0;
481
+ this.bytes = 0;
482
+ this.finish();
483
+ return;
484
+ }
485
+ this.frames.push(frame);
486
+ this.bytes += bytes;
487
+ this.wake?.();
488
+ }
489
+ }
490
+ function shellInfo(record) {
491
+ const profileId = record.shellProfileId ?? (record.runtimeType === 'tmux'
492
+ ? record.shellPath.endsWith('/bash') ? 'bash' : 'zsh'
493
+ : record.runtimeType === 'conpty-cmd'
494
+ ? 'cmd'
495
+ : record.runtimeType === 'conpty-git-bash'
496
+ ? 'git-bash'
497
+ : 'powershell');
498
+ return {
499
+ profileId,
500
+ path: record.shellPath,
501
+ args: record.shellArgs ?? (profileId === 'cmd' ? [] : profileId === 'powershell' ? ['-NoLogo'] : ['-i']),
502
+ name: record.shellName ?? profileId,
503
+ };
504
+ }
505
+ function identityKey(identity) {
506
+ return JSON.stringify([identity.hostId, identity.workspaceId, identity.terminalId]);
507
+ }
508
+ function validateTerminalId(id) {
509
+ if (!/^[\w-]{1,128}$/u.test(id))
510
+ throw new TypeError('终端标识无效');
511
+ }
512
+ function validateSize(cols, rows) {
513
+ if (!Number.isSafeInteger(cols) || cols < 2 || cols > 500 || !Number.isSafeInteger(rows) || rows < 1 || rows > 300) {
514
+ throw new TypeError('终端尺寸超过允许范围');
515
+ }
516
+ }
517
+ function frameBytes(frame) {
518
+ return new TextEncoder().encode(JSON.stringify(frame)).byteLength;
519
+ }
520
+ function untilAborted(signal) {
521
+ if (signal.aborted)
522
+ return Promise.resolve();
523
+ return new Promise((resolve) => signal.addEventListener('abort', () => resolve(), { once: true }));
524
+ }
525
+ function errorMessage(error) {
526
+ return error instanceof Error ? error.message : String(error);
527
+ }
528
+ //# sourceMappingURL=terminal-service.js.map
@@ -0,0 +1,49 @@
1
+ import type { PersistentTerminalRecord, TerminalOwnerScope, TerminalRecordIdentity } from '../../shared/contracts/terminal.js';
2
+ declare const STORE_VERSION = 1;
3
+ interface TerminalStoreDocument {
4
+ readonly version: typeof STORE_VERSION;
5
+ readonly records: readonly PersistentTerminalRecord[];
6
+ }
7
+ export interface TerminalStorePersistence {
8
+ load(): Promise<unknown>;
9
+ save(document: TerminalStoreDocument): Promise<void>;
10
+ }
11
+ /** JSON 文件写入采用同目录临时文件加 rename,避免崩溃留下半截记录。 */
12
+ export declare class JsonFileTerminalStorePersistence implements TerminalStorePersistence {
13
+ private readonly filename;
14
+ constructor(filename: string);
15
+ load(): Promise<unknown>;
16
+ save(document: TerminalStoreDocument): Promise<void>;
17
+ }
18
+ /**
19
+ * 持久终端记录表。
20
+ *
21
+ * 所有修改在一条 promise 队列中执行;调用完成代表新状态已经落盘。这样 create、
22
+ * recover 与 close 不会用最后写入者覆盖彼此的结果。
23
+ */
24
+ export declare class CodingNsTerminalStore {
25
+ private readonly persistence;
26
+ private readonly records;
27
+ private writeTail;
28
+ private loaded;
29
+ constructor(persistence: TerminalStorePersistence);
30
+ load(): Promise<void>;
31
+ list(scope?: TerminalOwnerScope): readonly PersistentTerminalRecord[];
32
+ get(identity: TerminalRecordIdentity): PersistentTerminalRecord | undefined;
33
+ put(record: PersistentTerminalRecord): Promise<void>;
34
+ patch(identity: TerminalRecordIdentity, patch: Partial<Pick<PersistentTerminalRecord, 'state' | 'title' | 'cols' | 'rows' | 'exitCode' | 'error' | 'updatedAt'>>): Promise<PersistentTerminalRecord | undefined>;
35
+ remove(identity: TerminalRecordIdentity): Promise<boolean>;
36
+ private enqueue;
37
+ private persist;
38
+ private requireLoaded;
39
+ }
40
+ export declare class InMemoryTerminalStorePersistence implements TerminalStorePersistence {
41
+ private document;
42
+ constructor(initial?: unknown);
43
+ load(): Promise<unknown>;
44
+ save(document: TerminalStoreDocument): Promise<void>;
45
+ snapshot(): unknown;
46
+ }
47
+ export declare function terminalStorePath(settingsDocumentPath: string): string;
48
+ export {};
49
+ //# sourceMappingURL=terminal-store.d.ts.map