@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,25 @@
1
+ /** 登录保护会话只放在当前浏览器标签页,关闭标签页即失效。 */
2
+ export const LOGIN_PROTECTION_SESSION_KEY = 'codingns4dsh.login-protection.relay-token';
3
+ export const LOGIN_PROTECTION_SESSION_EVENT = 'codingns4dsh-login-protection-session';
4
+ let memoryToken;
5
+ export function readLoginProtectionSession() {
6
+ try {
7
+ const value = globalThis.sessionStorage.getItem(LOGIN_PROTECTION_SESSION_KEY)?.trim();
8
+ return value === undefined || value === '' ? memoryToken : value;
9
+ }
10
+ catch {
11
+ return memoryToken;
12
+ }
13
+ }
14
+ export function writeLoginProtectionSession(token) {
15
+ memoryToken = token === undefined || token === '' ? undefined : token;
16
+ try {
17
+ if (memoryToken === undefined)
18
+ globalThis.sessionStorage.removeItem(LOGIN_PROTECTION_SESSION_KEY);
19
+ else
20
+ globalThis.sessionStorage.setItem(LOGIN_PROTECTION_SESSION_KEY, memoryToken);
21
+ }
22
+ catch { /* 浏览器隐私模式禁用存储时,使用当前页面内存中的票据。 */ }
23
+ globalThis.dispatchEvent(new Event(LOGIN_PROTECTION_SESSION_EVENT));
24
+ }
25
+ //# sourceMappingURL=login-protection-session.js.map
@@ -0,0 +1,4 @@
1
+ import type { CodingNsClientFeatureModule } from './types.js';
2
+ /** 独立的本地账号登录保护模块,位于局域网和中继访问模块之间。 */
3
+ export declare const loginProtectionFeature: CodingNsClientFeatureModule;
4
+ //# sourceMappingURL=login-protection.d.ts.map
@@ -0,0 +1,23 @@
1
+ import { LoginProtectionPanel } from './login-protection-panel.js';
2
+ /** 独立的本地账号登录保护模块,位于局域网和中继访问模块之间。 */
3
+ export const loginProtectionFeature = {
4
+ descriptor: {
5
+ name: 'loginProtection',
6
+ version: '0.2.0',
7
+ enabledByDefault: true,
8
+ dependencies: [],
9
+ runtime: 'client',
10
+ ui: {
11
+ label: '登录保护',
12
+ description: '为局域网和中继访问设置统一的本地账号验证,本机回环地址始终放行。',
13
+ labelKey: 'feature.loginProtection.label',
14
+ descriptionKey: 'feature.loginProtection.description',
15
+ order: 15,
16
+ alwaysEnabled: true,
17
+ defaultOpen: true,
18
+ },
19
+ },
20
+ start: () => undefined,
21
+ settingsPanel: LoginProtectionPanel,
22
+ };
23
+ //# sourceMappingURL=login-protection.js.map
@@ -0,0 +1,9 @@
1
+ import type { ReactElement } from 'react';
2
+ import type { FeaturePanelProps } from './types.js';
3
+ /**
4
+ * 「中转访问服务」卡片的设置面板:Control API 地址、登录和 DSH 独立设备。
5
+ *
6
+ * 密码只在单次 RPC 中经过 Host,表单不保存它;refresh token 只存在于 Host。
7
+ */
8
+ export declare function ReverseProxyPanel({ services, enabled, snapshot }: FeaturePanelProps): ReactElement;
9
+ //# sourceMappingURL=reverse-proxy-panel.d.ts.map
@@ -0,0 +1,159 @@
1
+ import { createElement, useEffect, useState } from 'react';
2
+ import { CODINGNS_CONTROL_BASE_URL_FIELD, CODINGNS_CONTROL_BASE_URLS_FIELD, DEFAULT_CODINGNS_CONTROL_BASE_URLS, } from '../../shared/contracts/config.js';
3
+ import { CODINGNS_RPC_CHANNEL } from '../../shared/contracts/transport.js';
4
+ import { dshFormRootStyle, dshPopupSurfaceStyle, dshSettingsButtonStyle, dshSettingsFieldLabelStyle, dshSettingsFieldStyle, dshSettingsNoteStyle, dshSettingsPrimaryButtonStyle, dshSettingsRowStyle, dshThemeColor, } from '../theme.js';
5
+ import { useCodingNsTranslator } from '../locale.js';
6
+ /**
7
+ * 「中转访问服务」卡片的设置面板:Control API 地址、登录和 DSH 独立设备。
8
+ *
9
+ * 密码只在单次 RPC 中经过 Host,表单不保存它;refresh token 只存在于 Host。
10
+ */
11
+ export function ReverseProxyPanel({ services, enabled, snapshot }) {
12
+ const { settings, rpc } = services;
13
+ const t = useCodingNsTranslator(services.locale);
14
+ const disabled = !enabled;
15
+ const controlsDisabled = disabled || snapshot.status === 'loading' || !snapshot.writable;
16
+ const [email, setEmail] = useState('');
17
+ const [password, setPassword] = useState('');
18
+ const [controlBaseUrl, setControlBaseUrl] = useState(resolveControlBaseUrl(snapshot.value?.controlBaseUrl));
19
+ const [controlBaseUrls, setControlBaseUrls] = useState(() => uniqueControlBaseUrls(snapshot.value?.controlBaseUrls, snapshot.value?.controlBaseUrl));
20
+ const [newControlBaseUrl, setNewControlBaseUrl] = useState('');
21
+ const [addAddressOpen, setAddAddressOpen] = useState(false);
22
+ const [addressError, setAddressError] = useState('');
23
+ const [auth, setAuth] = useState(loggedOutSnapshot());
24
+ const [devices, setDevices] = useState(null);
25
+ const [selectedDeviceId, setSelectedDeviceId] = useState('');
26
+ const [busy, setBusy] = useState(false);
27
+ const [message, setMessage] = useState('');
28
+ useEffect(() => {
29
+ const saved = snapshot.value?.controlBaseUrl;
30
+ const resolved = resolveControlBaseUrl(saved);
31
+ setControlBaseUrls(uniqueControlBaseUrls(snapshot.value?.controlBaseUrls, resolved));
32
+ setControlBaseUrl(resolved);
33
+ if (snapshot.status === 'ready' && snapshot.writable && saved !== resolved) {
34
+ void settings.set(CODINGNS_CONTROL_BASE_URL_FIELD, resolved).catch(() => undefined);
35
+ }
36
+ }, [settings, snapshot.status, snapshot.writable, snapshot.value?.controlBaseUrl, snapshot.value?.controlBaseUrls]);
37
+ useEffect(() => {
38
+ void callCodingNsRpc(rpc, 'auth/snapshot', {})
39
+ .then(setAuth)
40
+ .catch(() => setAuth(loggedOutSnapshot()));
41
+ }, [rpc]);
42
+ // 设置页打开时 Host 可能正在恢复登录会话;认证快照变为 authenticated
43
+ // 后立即刷新设备列表,避免用户必须再点一次“刷新设备”。
44
+ useEffect(() => {
45
+ if (!enabled || auth.status !== 'authenticated')
46
+ return;
47
+ void loadDevicesInternal().catch((error) => {
48
+ setMessage(error instanceof Error ? error.message : String(error));
49
+ });
50
+ }, [auth.status, enabled, rpc]);
51
+ const run = async (operation) => {
52
+ setBusy(true);
53
+ setMessage('');
54
+ try {
55
+ await operation();
56
+ }
57
+ catch (error) {
58
+ setMessage(error instanceof Error ? error.message : String(error));
59
+ }
60
+ finally {
61
+ setBusy(false);
62
+ }
63
+ };
64
+ const login = () => run(async () => {
65
+ const selectedUrl = normalizeControlBaseUrl(controlBaseUrl);
66
+ const nextUrls = uniqueControlBaseUrls(controlBaseUrls, selectedUrl);
67
+ await settings.set(CODINGNS_CONTROL_BASE_URLS_FIELD, nextUrls);
68
+ await settings.set(CODINGNS_CONTROL_BASE_URL_FIELD, selectedUrl);
69
+ const next = await callCodingNsRpc(rpc, 'auth/login', {
70
+ controlBaseUrl: selectedUrl,
71
+ email,
72
+ password,
73
+ });
74
+ setPassword('');
75
+ setAuth(next);
76
+ setMessage(t('relay.loginSuccess'));
77
+ });
78
+ const logout = () => run(async () => {
79
+ await callCodingNsRpc(rpc, 'auth/logout', {});
80
+ setAuth(loggedOutSnapshot());
81
+ setDevices(null);
82
+ setSelectedDeviceId('');
83
+ setMessage(t('relay.loggedOut'));
84
+ });
85
+ const loadDevices = () => run(async () => {
86
+ await loadDevicesInternal();
87
+ });
88
+ async function loadDevicesInternal() {
89
+ const next = await callCodingNsRpc(rpc, 'auth/dsh/device/list', {});
90
+ setDevices(next);
91
+ const preferred = next.devices.find((device) => device.online && device.status === 'active')?.dshDeviceId ?? '';
92
+ setSelectedDeviceId(preferred);
93
+ }
94
+ const addControlBaseUrl = async () => {
95
+ setBusy(true);
96
+ setAddressError('');
97
+ try {
98
+ const addedUrl = normalizeControlBaseUrl(newControlBaseUrl);
99
+ const nextUrls = uniqueControlBaseUrls(controlBaseUrls, addedUrl);
100
+ await settings.set(CODINGNS_CONTROL_BASE_URLS_FIELD, nextUrls);
101
+ await settings.set(CODINGNS_CONTROL_BASE_URL_FIELD, addedUrl);
102
+ setControlBaseUrls(nextUrls);
103
+ setControlBaseUrl(addedUrl);
104
+ setNewControlBaseUrl('');
105
+ setAddAddressOpen(false);
106
+ setMessage(t('relay.add'));
107
+ }
108
+ catch (error) {
109
+ setAddressError(error instanceof Error ? error.message : String(error));
110
+ }
111
+ finally {
112
+ setBusy(false);
113
+ }
114
+ };
115
+ const chooseControlBaseUrl = (value) => {
116
+ setControlBaseUrl(value);
117
+ void settings.set(CODINGNS_CONTROL_BASE_URL_FIELD, value).catch((error) => {
118
+ setMessage(error instanceof Error ? error.message : String(error));
119
+ });
120
+ };
121
+ const fieldStyle = dshSettingsFieldStyle;
122
+ const buttonStyle = dshSettingsButtonStyle;
123
+ const authenticated = auth.status === 'authenticated';
124
+ return createElement('div', { 'aria-disabled': controlsDisabled, style: { ...dshFormRootStyle, display: 'flex', flexDirection: 'column', gap: 16, opacity: controlsDisabled ? 0.5 : 1, pointerEvents: controlsDisabled ? 'none' : 'auto' } }, createElement('div', undefined, createElement('h3', { style: { margin: 0, fontSize: 17 } }, t('relay.settings')), createElement('p', { style: { margin: '6px 0 0', color: dshThemeColor.labelSecondary, fontSize: 13, lineHeight: 1.5 } }, t('relay.loginHint'))), createElement('label', { style: { display: 'flex', flexDirection: 'column', gap: 6 } }, createElement('span', { style: dshSettingsFieldLabelStyle }, t('relay.controlApi')), createElement('div', { style: dshSettingsRowStyle }, createElement('select', { value: controlBaseUrl, disabled: controlsDisabled || busy, onChange: (event) => chooseControlBaseUrl(event.currentTarget.value), style: { ...fieldStyle, flex: 1, minWidth: 0 } }, ...controlBaseUrls.map((url) => createElement('option', { key: url, value: url }, url))), createElement('button', { type: 'button', 'aria-haspopup': 'dialog', disabled: controlsDisabled || busy, onClick: () => { setAddressError(''); setAddAddressOpen(true); }, style: { ...buttonStyle, flex: '0 0 auto' } }, t('relay.add')))), addAddressOpen && createElement('div', { role: 'dialog', 'aria-modal': true, 'aria-labelledby': 'codingns-add-address-title', style: { position: 'fixed', inset: 0, zIndex: 1000, display: 'flex', alignItems: 'center', justifyContent: 'center', padding: 24, background: dshThemeColor.overlay } }, createElement('div', { style: { ...dshPopupSurfaceStyle, width: 'min(100%, 480px)', boxSizing: 'border-box', padding: 24, borderRadius: 8 } }, createElement('h3', { id: 'codingns-add-address-title', style: { margin: 0, fontSize: 18 } }, t('relay.addServer')), createElement('p', { style: { margin: '8px 0 16px', opacity: 0.7 } }, t('relay.addServerHint')), createElement('input', { type: 'url', autoFocus: true, value: newControlBaseUrl, placeholder: 'https://example.com:1443', disabled: disabled || busy, onChange: (event) => setNewControlBaseUrl(event.currentTarget.value), style: fieldStyle }), addressError && createElement('div', { role: 'alert', style: { marginTop: 8, color: dshThemeColor.error } }, addressError), createElement('div', { style: { display: 'flex', justifyContent: 'flex-end', gap: 8, marginTop: 20 } }, createElement('button', { type: 'button', disabled: busy, onClick: () => { setAddAddressOpen(false); setAddressError(''); }, style: buttonStyle }, t('relay.cancel')), createElement('button', { type: 'button', disabled: busy || !newControlBaseUrl.trim(), onClick: () => void addControlBaseUrl(), style: buttonStyle }, busy ? t('relay.adding') : t('relay.add'))))), !authenticated && createElement('form', { onSubmit: (event) => { event.preventDefault(); void login(); }, style: { display: 'flex', flexDirection: 'column', gap: 12 } }, createElement('label', { style: { display: 'flex', flexDirection: 'column', gap: 6 } }, createElement('span', { style: dshSettingsFieldLabelStyle }, t('relay.email')), createElement('input', { type: 'email', autoComplete: 'username', value: email, disabled: controlsDisabled || busy, onChange: (event) => setEmail(event.currentTarget.value), style: fieldStyle })), createElement('label', { style: { display: 'flex', flexDirection: 'column', gap: 6 } }, createElement('span', { style: dshSettingsFieldLabelStyle }, t('relay.password')), createElement('input', { type: 'password', autoComplete: 'current-password', value: password, disabled: controlsDisabled || busy, onChange: (event) => setPassword(event.currentTarget.value), style: fieldStyle })), createElement('button', { type: 'submit', disabled: controlsDisabled || busy || !controlBaseUrl || !email || !password, style: dshSettingsPrimaryButtonStyle }, busy ? t('relay.loggingIn') : t('relay.login'))), authenticated && createElement('div', { style: { display: 'flex', flexDirection: 'column', gap: 12 } }, createElement('div', { style: dshSettingsNoteStyle }, createElement('strong', undefined, auth.account?.email ?? t('relay.loggedIn')), createElement('div', { style: { marginTop: 6, opacity: 0.7 } }, t('relay.device', { value: devices?.devices.find((device) => device.dshDeviceId === selectedDeviceId)?.displayName ?? t('relay.unrecognized') })), createElement('div', { style: { marginTop: 4, opacity: 0.7 } }, t('relay.host', { value: selectedDeviceId || t('relay.unbound') }))), createElement('div', { style: { display: 'flex', gap: 8, flexWrap: 'wrap' } }, createElement('button', { type: 'button', disabled: controlsDisabled || busy, onClick: () => void loadDevices(), style: buttonStyle }, t('relay.refreshDevices')), createElement('button', { type: 'button', disabled: controlsDisabled || busy, onClick: () => void logout(), style: buttonStyle }, t('relay.logout'))), devices && createElement('div', { style: { display: 'flex', flexDirection: 'column', gap: 8 } }, createElement('strong', { style: dshSettingsFieldLabelStyle }, t('relay.dshDevices')), createElement('select', { value: selectedDeviceId, disabled: controlsDisabled || busy, onChange: (event) => setSelectedDeviceId(event.currentTarget.value), style: fieldStyle }, createElement('option', { value: '' }, t('relay.selectDevice')), ...devices.devices.map((device) => createElement('option', { key: device.dshDeviceId, value: device.dshDeviceId, disabled: !device.online || device.status !== 'active' }, `${device.displayName} · ${device.online ? t('relay.online') : t('relay.offline')}`))), createElement('div', { style: { fontSize: 13, opacity: 0.75 } }, t('relay.devicesSummary', { current: selectedDeviceId || t('relay.unknown'), count: devices.devices.length })))), message && createElement('div', { role: 'status', style: { color: message.includes('成功') ? dshThemeColor.success : dshThemeColor.error } }, message));
125
+ }
126
+ async function callCodingNsRpc(rpc, endpoint, payload) {
127
+ let response;
128
+ try {
129
+ response = await rpc.call(CODINGNS_RPC_CHANNEL, endpoint, payload);
130
+ }
131
+ catch (error) {
132
+ const message = error instanceof Error ? error.message : String(error);
133
+ if (!/HTTP (?:404|405)\b/u.test(message))
134
+ throw error;
135
+ response = await rpc.call('/api', `codingns/${endpoint}`, payload);
136
+ }
137
+ if (!response.ok)
138
+ throw new Error(response.error.message);
139
+ return response.value;
140
+ }
141
+ function loggedOutSnapshot() {
142
+ return { status: 'logged_out', account: null, currentDevice: null, binding: null, expiresAt: null, errorCode: null };
143
+ }
144
+ function uniqueControlBaseUrls(saved, selected) {
145
+ const values = [...(saved ?? DEFAULT_CODINGNS_CONTROL_BASE_URLS), selected ?? ''];
146
+ return [...new Set(values.map((value) => value.trim()).filter((value) => value.length > 0))];
147
+ }
148
+ function resolveControlBaseUrl(value) {
149
+ const trimmed = value?.trim();
150
+ return trimmed || DEFAULT_CODINGNS_CONTROL_BASE_URLS[0] || '';
151
+ }
152
+ function normalizeControlBaseUrl(value) {
153
+ const trimmed = value.trim();
154
+ const parsed = new URL(trimmed);
155
+ if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:')
156
+ throw new TypeError('Control API 地址必须使用 HTTP(S)');
157
+ return parsed.toString().replace(/\/+$/u, '');
158
+ }
159
+ //# sourceMappingURL=reverse-proxy-panel.js.map
@@ -0,0 +1,12 @@
1
+ import type { CodingNsClientFeatureModule } from './types.js';
2
+ import type { CodingNsRpcClient } from './types.js';
3
+ /**
4
+ * 中转访问服务模块。
5
+ *
6
+ * 它把 DSH 页面接入 DSH-Codingns4DSH 独立设备隧道。登录和设备列表由 Host RPC 提供,
7
+ * 连接状态和设备选择由设置面板承载;隧道本身在模块启用时建立。
8
+ */
9
+ export declare const reverseProxyFeature: CodingNsClientFeatureModule;
10
+ /** 浏览器侧真实中继连接;refresh token 和 access token 只经过 Host RPC。 */
11
+ export declare function startBrowserRelayConnection(rpc: CodingNsRpcClient, signal: AbortSignal, loginProtectionToken?: string): Promise<() => Promise<void>>;
12
+ //# sourceMappingURL=reverse-proxy.d.ts.map
@@ -0,0 +1,112 @@
1
+ import { ReverseProxyPanel } from './reverse-proxy-panel.js';
2
+ import { startDshH5Bootstrap } from '../dsh-h5-bootstrap.js';
3
+ import { LOGIN_PROTECTION_SESSION_EVENT, readLoginProtectionSession } from './login-protection-session.js';
4
+ import { CODINGNS_RPC_CHANNEL } from '../../shared/contracts/transport.js';
5
+ function isRemoteWebContext() {
6
+ return globalThis.__CODINGNS4DSH_REMOTE_WEB_CONTEXT__ === true;
7
+ }
8
+ /**
9
+ * 中转访问服务模块。
10
+ *
11
+ * 它把 DSH 页面接入 DSH-Codingns4DSH 独立设备隧道。登录和设备列表由 Host RPC 提供,
12
+ * 连接状态和设备选择由设置面板承载;隧道本身在模块启用时建立。
13
+ */
14
+ export const reverseProxyFeature = {
15
+ descriptor: {
16
+ name: 'reverseProxy',
17
+ version: '0.1.0',
18
+ enabledByDefault: false,
19
+ dependencies: [],
20
+ runtime: 'client',
21
+ ui: {
22
+ label: '中转访问服务',
23
+ description: '通过 DSH-Codingns4DSH 独立设备隧道访问 Host。',
24
+ labelKey: 'feature.reverseProxy.label',
25
+ descriptionKey: 'feature.reverseProxy.description',
26
+ order: 20,
27
+ defaultOpen: true,
28
+ },
29
+ },
30
+ /**
31
+ * 隧道连接属于后续阶段,当前模块只提供配置面,因此这里不创建任何资源。
32
+ * 接入连接后,流与订阅必须登记到 context.resources,由停用自动清理。
33
+ */
34
+ start(context) {
35
+ if (isRemoteWebContext()) {
36
+ // 远程 DSH Web 的外层已经拥有有效 Tunnel;这里只保留设置 UI 和其它
37
+ // 插件贡献,禁止同一页面重新申请票据并建立第二条中继连接。
38
+ return () => { };
39
+ }
40
+ const abort = new AbortController();
41
+ let disposeConnection;
42
+ let retryTimer;
43
+ let stopped = false;
44
+ let waitingForLogin = false;
45
+ const onLoginProtectionSession = () => {
46
+ waitingForLogin = false;
47
+ void attempt();
48
+ };
49
+ globalThis.addEventListener(LOGIN_PROTECTION_SESSION_EVENT, onLoginProtectionSession);
50
+ const attempt = async () => {
51
+ if (stopped || waitingForLogin)
52
+ return;
53
+ try {
54
+ const protection = await readLoginProtectionSettings(context.services.rpc);
55
+ const loginProtectionToken = readLoginProtectionSession();
56
+ if (protection.enabled && protection.scopes.relay && loginProtectionToken === undefined) {
57
+ waitingForLogin = true;
58
+ return;
59
+ }
60
+ const dispose = await startBrowserRelayConnection(context.services.rpc, abort.signal, loginProtectionToken);
61
+ if (stopped || abort.signal.aborted) {
62
+ await dispose();
63
+ return;
64
+ }
65
+ disposeConnection = dispose;
66
+ }
67
+ catch (error) {
68
+ if (stopped || abort.signal.aborted)
69
+ return;
70
+ console.error('codingns4dsh: 中继连接建立失败,将在稍后重试', error);
71
+ retryTimer = setTimeout(() => { void attempt(); }, 5_000);
72
+ }
73
+ };
74
+ void attempt();
75
+ return async () => {
76
+ stopped = true;
77
+ globalThis.removeEventListener(LOGIN_PROTECTION_SESSION_EVENT, onLoginProtectionSession);
78
+ abort.abort();
79
+ if (retryTimer !== undefined)
80
+ clearTimeout(retryTimer);
81
+ await disposeConnection?.();
82
+ };
83
+ },
84
+ settingsPanel: ReverseProxyPanel,
85
+ };
86
+ /** 浏览器侧真实中继连接;refresh token 和 access token 只经过 Host RPC。 */
87
+ export async function startBrowserRelayConnection(rpc, signal, loginProtectionToken) {
88
+ const bootstrap = await startDshH5Bootstrap({ rpc, signal, ...(loginProtectionToken === undefined ? {} : { loginProtectionToken }) });
89
+ const state = globalThis;
90
+ state.__CODINGNS4DSH_RELAY_MODE__ = bootstrap.relayMode;
91
+ return async () => {
92
+ if (state.__CODINGNS4DSH_RELAY_MODE__ === bootstrap.relayMode)
93
+ delete state.__CODINGNS4DSH_RELAY_MODE__;
94
+ await bootstrap.dispose();
95
+ };
96
+ }
97
+ async function readLoginProtectionSettings(rpc) {
98
+ let response;
99
+ try {
100
+ response = await rpc.call(CODINGNS_RPC_CHANNEL, 'lanAccessDsh/login/get', {});
101
+ }
102
+ catch (error) {
103
+ const message = error instanceof Error ? error.message : String(error);
104
+ if (!/HTTP (?:404|405)\b/u.test(message))
105
+ throw error;
106
+ response = await rpc.call('/api', 'codingns/lanAccessDsh/login/get', {});
107
+ }
108
+ if (!response.ok)
109
+ throw new Error(response.error.message);
110
+ return response.value;
111
+ }
112
+ //# sourceMappingURL=reverse-proxy.js.map
@@ -0,0 +1,5 @@
1
+ import type { ReactElement } from 'react';
2
+ import type { FeaturePanelProps } from './types.js';
3
+ /** 终端默认行为与外观设置;终端 UI 由独立 Sidebar 模块负责。 */
4
+ export declare function TerminalEnhancementPanel({ services, enabled, snapshot }: FeaturePanelProps): ReactElement;
5
+ //# sourceMappingURL=terminal-enhancement-panel.d.ts.map
@@ -0,0 +1,160 @@
1
+ import { createElement, useEffect, useState } from 'react';
2
+ import { CODINGNS_TERMINAL_ENHANCEMENT_FIELD, DEFAULT_TERMINAL_ENHANCEMENT_SETTINGS, } from '../../shared/contracts/config.js';
3
+ import { CODINGNS_RPC_CHANNEL } from '../../shared/contracts/transport.js';
4
+ import { dshFormRootStyle, dshSettingsButtonStyle, dshSettingsFieldLabelStyle, dshSettingsFieldStyle, dshSettingsHelpStyle, dshSettingsNoteStyle, dshSettingsRowStyle, dshThemeColor, } from '../theme.js';
5
+ import { useCodingNsTranslator } from '../locale.js';
6
+ const profileLabels = {
7
+ system: 'terminal.systemRecommended',
8
+ zsh: 'zsh',
9
+ bash: 'bash',
10
+ powershell: 'PowerShell',
11
+ cmd: 'cmd',
12
+ 'git-bash': 'Git Bash',
13
+ };
14
+ /** 终端默认行为与外观设置;终端 UI 由独立 Sidebar 模块负责。 */
15
+ export function TerminalEnhancementPanel({ services, enabled, snapshot }) {
16
+ const t = useCodingNsTranslator(services.locale);
17
+ const value = snapshot.value?.terminalEnhancement ?? DEFAULT_TERMINAL_ENHANCEMENT_SETTINGS;
18
+ const [fontFamily, setFontFamily] = useState(value.appearance.fontFamily ?? '');
19
+ const [message, setMessage] = useState('');
20
+ const [hostStatus, setHostStatus] = useState(null);
21
+ const disabled = !enabled || snapshot.status === 'loading' || !snapshot.writable;
22
+ const customDisabled = disabled || value.appearance.theme !== 'custom';
23
+ useEffect(() => setFontFamily(value.appearance.fontFamily ?? ''), [value.appearance.fontFamily]);
24
+ useEffect(() => {
25
+ let active = true;
26
+ void callTerminalStatus(services.rpc)
27
+ .then((status) => { if (active)
28
+ setHostStatus(status); })
29
+ .catch((error) => {
30
+ if (active)
31
+ setMessage(error instanceof Error ? error.message : String(error));
32
+ });
33
+ return () => { active = false; };
34
+ }, [services.rpc]);
35
+ const save = (next) => {
36
+ setMessage('');
37
+ void services.settings.set(CODINGNS_TERMINAL_ENHANCEMENT_FIELD, next)
38
+ .then(() => setMessage(t('terminal.saved')))
39
+ .catch((error) => setMessage(error instanceof Error ? error.message : String(error)));
40
+ };
41
+ const updateAppearance = (patch) => {
42
+ save({ ...value, appearance: { ...value.appearance, ...patch } });
43
+ };
44
+ const saveFontFamily = () => {
45
+ try {
46
+ updateAppearance({ fontFamily: normalizeFontFamily(fontFamily) });
47
+ }
48
+ catch (error) {
49
+ setMessage(error instanceof Error ? error.message : String(error));
50
+ }
51
+ };
52
+ return createElement('div', {
53
+ 'aria-disabled': disabled,
54
+ style: { ...dshFormRootStyle, display: 'flex', flexDirection: 'column', gap: 16, opacity: disabled ? 0.5 : 1, pointerEvents: disabled ? 'none' : 'auto' },
55
+ }, createElement('div', { role: 'note', style: noteStyle }, t('terminal.restartNote')), createElement('div', { role: 'status', style: noteStyle }, hostStatus === null
56
+ ? t('terminal.readingStatus')
57
+ : t('terminal.currentStatus', { status: hostStatus.effectiveEnabled ? t('terminal.enhanced') : t('terminal.basic'), platform: platformLabel(hostStatus.platform, t) })), createElement(Field, { label: t('terminal.newDefault') }, createElement('select', {
58
+ value: value.defaultProfile,
59
+ disabled,
60
+ onChange: (event) => save({ ...value, defaultProfile: event.currentTarget.value }),
61
+ style: fieldStyle,
62
+ }, ...profileOptions(value.defaultProfile, hostStatus, t).map((profile) => createElement('option', { key: profile.profileId, value: profile.profileId, disabled: !profile.available }, profile.label))), createElement('small', { style: helpStyle }, t('terminal.defaultProfileHelp')), hostStatus?.fallbackReason === undefined ? null : createElement('small', { style: helpStyle }, hostStatus.fallbackReason)), createElement(Field, { label: t('terminal.bindingScope') }, createElement('select', {
63
+ value: value.bindingScope ?? 'workspace',
64
+ disabled,
65
+ onChange: (event) => save({
66
+ ...value,
67
+ bindingScope: event.currentTarget.value === 'session' ? 'session' : 'workspace',
68
+ }),
69
+ style: fieldStyle,
70
+ }, createElement('option', { value: 'workspace' }, t('terminal.workspaceBinding')), createElement('option', { value: 'session' }, t('terminal.sessionBinding'))), createElement('small', { style: helpStyle }, t('terminal.workspaceBindingHelp'))), createElement(Field, { label: t('terminal.theme') }, createElement('select', {
71
+ value: value.appearance.theme,
72
+ disabled,
73
+ onChange: (event) => updateAppearance({ theme: event.currentTarget.value === 'custom' ? 'custom' : 'inherit' }),
74
+ style: fieldStyle,
75
+ }, createElement('option', { value: 'inherit' }, t('terminal.inheritDshTheme')), createElement('option', { value: 'custom' }, t('terminal.custom')))), createElement(ColorField, { label: t('terminal.background'), value: value.appearance.background, disabled: customDisabled, onChange: (background) => updateAppearance({ background }), inheritLabel: t('terminal.inherit'), resetLabel: t('terminal.resetInherit') }), createElement(ColorField, { label: t('terminal.foreground'), value: value.appearance.foreground, disabled: customDisabled, onChange: (foreground) => updateAppearance({ foreground }), inheritLabel: t('terminal.inherit'), resetLabel: t('terminal.resetInherit') }), createElement(ColorField, { label: t('terminal.cursorColor'), value: value.appearance.cursorColor, disabled: customDisabled, onChange: (cursorColor) => updateAppearance({ cursorColor }), inheritLabel: t('terminal.inherit'), resetLabel: t('terminal.resetInherit') }), createElement(Field, { label: t('terminal.font') }, createElement('div', { style: rowStyle }, createElement('input', {
76
+ type: 'text', value: fontFamily, maxLength: 128, disabled: customDisabled,
77
+ placeholder: t('terminal.inheritFont'),
78
+ onChange: (event) => setFontFamily(event.currentTarget.value),
79
+ onBlur: saveFontFamily,
80
+ style: { ...fieldStyle, flex: 1, minWidth: 0 },
81
+ }), createElement(ResetButton, { disabled: customDisabled || value.appearance.fontFamily === null, onClick: () => { setFontFamily(''); updateAppearance({ fontFamily: null }); }, label: t('terminal.resetInherit') }))), createElement(NumberField, { label: t('terminal.fontSize'), value: value.appearance.fontSize, min: 10, max: 32, step: 1, disabled: customDisabled, onChange: (fontSize) => updateAppearance({ fontSize }), inheritLabel: t('terminal.inherit'), resetLabel: t('terminal.resetInherit') }), createElement(NumberField, { label: t('terminal.lineHeight'), value: value.appearance.lineHeight, min: 1, max: 2, step: 0.1, disabled: customDisabled, onChange: (lineHeight) => updateAppearance({ lineHeight }), inheritLabel: t('terminal.inherit'), resetLabel: t('terminal.resetInherit') }), createElement(Field, { label: t('terminal.cursorShape') }, createElement('select', {
82
+ value: value.appearance.cursorStyle ?? '', disabled: customDisabled,
83
+ onChange: (event) => updateAppearance({ cursorStyle: parseCursorStyle(event.currentTarget.value) }),
84
+ style: fieldStyle,
85
+ }, createElement('option', { value: '' }, t('terminal.cursorInherit')), createElement('option', { value: 'block' }, t('terminal.cursorBlock')), createElement('option', { value: 'bar' }, t('terminal.cursorBar')), createElement('option', { value: 'underline' }, t('terminal.cursorUnderline')))), createElement(Field, { label: t('terminal.cursorBlink') }, createElement('div', { style: rowStyle }, createElement('label', { style: { display: 'inline-flex', alignItems: 'center', gap: 8 } }, createElement('input', {
86
+ type: 'checkbox', role: 'switch', 'aria-label': t('terminal.cursorBlink'),
87
+ checked: value.appearance.cursorBlink ?? false, disabled: customDisabled,
88
+ onChange: (event) => updateAppearance({ cursorBlink: event.currentTarget.checked }),
89
+ }), value.appearance.cursorBlink === null ? t('terminal.inherit') : (value.appearance.cursorBlink ? t('terminal.cursorOn') : t('terminal.cursorOff'))), createElement(ResetButton, { disabled: customDisabled || value.appearance.cursorBlink === null, onClick: () => updateAppearance({ cursorBlink: null }), label: t('terminal.resetInherit') }))), createElement(NumberField, { label: t('terminal.scrollback'), value: value.appearance.scrollback, min: 1000, max: 100000, step: 1000, disabled, onChange: (scrollback) => updateAppearance({ scrollback }), inheritLabel: t('terminal.inherit'), resetLabel: t('terminal.resetInherit') }), message && createElement('div', { role: 'status', style: { color: message.includes('已保存') ? dshThemeColor.success : dshThemeColor.error } }, message));
90
+ }
91
+ function Field({ label, children }) {
92
+ return createElement('label', { style: { display: 'flex', flexDirection: 'column', gap: 6 } }, createElement('span', { style: dshSettingsFieldLabelStyle }, label), children);
93
+ }
94
+ function ColorField({ label, value, disabled, onChange, inheritLabel, resetLabel }) {
95
+ return createElement(Field, { label }, createElement('div', { style: rowStyle }, createElement('input', { type: 'color', value: value ?? '#000000', disabled, 'aria-label': label, onChange: (event) => onChange(event.currentTarget.value), style: { width: 48, height: 34 } }), createElement('code', { style: { flex: 1 } }, value ?? inheritLabel), createElement(ResetButton, { disabled: disabled || value === null, onClick: () => onChange(null), label: resetLabel })));
96
+ }
97
+ function NumberField({ label, value, min, max, step, disabled, onChange, inheritLabel, resetLabel }) {
98
+ return createElement(Field, { label }, createElement('div', { style: rowStyle }, createElement('input', {
99
+ type: 'number', value: value ?? '', min, max, step, disabled, placeholder: inheritLabel,
100
+ onChange: (event) => onChange(event.currentTarget.value === '' ? null : Number(event.currentTarget.value)),
101
+ style: { ...fieldStyle, flex: 1, minWidth: 0 },
102
+ }), createElement(ResetButton, { disabled: disabled || value === null, onClick: () => onChange(null), label: resetLabel })));
103
+ }
104
+ function ResetButton({ disabled, onClick, label }) {
105
+ return createElement('button', { type: 'button', disabled, onClick, style: buttonStyle }, label);
106
+ }
107
+ function profileOptions(selected, status, t) {
108
+ if (status === null)
109
+ return [{ profileId: 'system', label: t(profileLabels.system), available: true }];
110
+ const profiles = status.profiles.map((profile) => ({
111
+ profileId: profile.profileId,
112
+ label: profile.name,
113
+ available: true,
114
+ }));
115
+ const options = [{ profileId: 'system', label: t(profileLabels.system), available: profiles.length > 0 }, ...profiles];
116
+ if (options.some((profile) => profile.profileId === selected))
117
+ return options;
118
+ return [...options, { profileId: selected, label: t('terminal.profileUnavailable', { profile: t(profileLabels[selected]) }), available: false }];
119
+ }
120
+ function platformLabel(platform, t) {
121
+ if (platform === 'darwin')
122
+ return 'macOS';
123
+ if (platform === 'win32')
124
+ return 'Windows';
125
+ if (platform === 'linux')
126
+ return 'Linux';
127
+ return t('terminal.unsupportedPlatform');
128
+ }
129
+ async function callTerminalStatus(rpc) {
130
+ let response;
131
+ try {
132
+ response = await rpc.call(CODINGNS_RPC_CHANNEL, 'terminal/status', {});
133
+ }
134
+ catch (error) {
135
+ const message = error instanceof Error ? error.message : String(error);
136
+ if (!/HTTP (?:404|405)\b/u.test(message))
137
+ throw error;
138
+ response = await rpc.call('/api', 'codingns/terminal/status', {});
139
+ }
140
+ if (!response.ok)
141
+ throw new Error(response.error.message);
142
+ return response.value;
143
+ }
144
+ function normalizeFontFamily(value) {
145
+ const trimmed = value.trim();
146
+ if (trimmed === '')
147
+ return null;
148
+ if (trimmed.length > 128 || /[\u0000-\u001F\u007F]/u.test(trimmed))
149
+ throw new TypeError('字体名称格式无效');
150
+ return trimmed;
151
+ }
152
+ function parseCursorStyle(value) {
153
+ return value === 'block' || value === 'bar' || value === 'underline' ? value : null;
154
+ }
155
+ const fieldStyle = dshSettingsFieldStyle;
156
+ const buttonStyle = { ...dshSettingsButtonStyle, flex: '0 0 auto', minWidth: 92 };
157
+ const rowStyle = dshSettingsRowStyle;
158
+ const helpStyle = dshSettingsHelpStyle;
159
+ const noteStyle = dshSettingsNoteStyle;
160
+ //# sourceMappingURL=terminal-enhancement-panel.js.map
@@ -0,0 +1,4 @@
1
+ import type { CodingNsClientFeatureModule } from './types.js';
2
+ /** “终端强化”只切换 backend;插件 controller 与 Sidebar UI 始终存在。 */
3
+ export declare const terminalEnhancementFeature: CodingNsClientFeatureModule;
4
+ //# sourceMappingURL=terminal-enhancement.d.ts.map
@@ -0,0 +1,24 @@
1
+ import { TerminalEnhancementPanel } from './terminal-enhancement-panel.js';
2
+ /** “终端强化”只切换 backend;插件 controller 与 Sidebar UI 始终存在。 */
3
+ export const terminalEnhancementFeature = {
4
+ descriptor: {
5
+ name: 'terminalEnhancement',
6
+ version: '0.1.0',
7
+ enabledByDefault: false,
8
+ dependencies: [],
9
+ runtime: 'client',
10
+ activation: 'restart',
11
+ ui: {
12
+ label: '终端强化',
13
+ description: '配置持久终端的默认 shell 与插件终端外观。启用和禁用均需重启 DSH。',
14
+ labelKey: 'feature.terminal.label',
15
+ descriptionKey: 'feature.terminal.description',
16
+ order: 40,
17
+ defaultOpen: true,
18
+ legacyFallback: true,
19
+ },
20
+ },
21
+ start: () => undefined,
22
+ settingsPanel: TerminalEnhancementPanel,
23
+ };
24
+ //# sourceMappingURL=terminal-enhancement.js.map
@@ -0,0 +1,58 @@
1
+ import type { ReactElement } from 'react';
2
+ import type { Context } from '@deepseek-ai/cordis';
3
+ import type { SlotRegistry } from '@deepseek-ai/dsh-client-ui-renderer/client';
4
+ import type { SettingsScope, SettingsScopeSnapshot } from '@deepseek-ai/dsh-client-ui-settings/client';
5
+ import type { FeatureModule } from '../../shared/contracts/feature.js';
6
+ import type { CodingNsSettings } from '../../shared/contracts/config.js';
7
+ import type { CodingNsLocale } from '../locale.js';
8
+ /** 一次 Codingns4DSH RPC 的结果,与 DSH Connection 的结果形状一致。 */
9
+ export type CodingNsRpcResult = {
10
+ readonly ok: true;
11
+ readonly value: unknown;
12
+ } | {
13
+ readonly ok: false;
14
+ readonly error: {
15
+ readonly code: string;
16
+ readonly message: string;
17
+ };
18
+ };
19
+ /** Client 侧 RPC 调用句柄,由 DSH Connection 提供。 */
20
+ export interface CodingNsRpcClient {
21
+ call(channel: string, endpoint: string, payload: unknown, signal?: AbortSignal): Promise<CodingNsRpcResult>;
22
+ }
23
+ /** Client 侧功能模块在 start 中取用的服务集合。 */
24
+ export interface CodingNsClientServices {
25
+ /** 当前 DSH 实际运行版本,由 Host 注入。 */
26
+ readonly dshVersion: string;
27
+ readonly settings: SettingsScope<CodingNsSettings>;
28
+ readonly rpc: CodingNsRpcClient;
29
+ /** DSH Typert Remote;归档会话模块只通过运行时探测调用可选方法。 */
30
+ readonly remote?: unknown;
31
+ /** DSH 语言运行时;所有 Client 文案都从 Codingns4DSH 命名空间读取。 */
32
+ readonly locale: CodingNsLocale;
33
+ /** DSH 对话装配服务;用于注册不写入 Session 的流式临时节点。 */
34
+ readonly uiConversation?: unknown;
35
+ /** 对话工具栏 Slot 服务;测试和非 Web 宿主可以不提供。 */
36
+ readonly slots?: SlotRegistry;
37
+ /** 当前 Client Cordis 上下文;只供需要注册 DSH UI Slot 的功能模块使用。 */
38
+ readonly uiContext?: Context;
39
+ }
40
+ /** 设置卡片传给模块面板的属性。 */
41
+ export interface FeaturePanelProps {
42
+ /** 宿主注入的服务,与模块 start 中拿到的是同一份。 */
43
+ readonly services: CodingNsClientServices;
44
+ /** 当前是否启用;未启用时面板需要自行灰显并禁用输入。 */
45
+ readonly enabled: boolean;
46
+ /** 设置快照,用于读取表单初值和可写状态。 */
47
+ readonly snapshot: SettingsScopeSnapshot<CodingNsSettings>;
48
+ }
49
+ /**
50
+ * 可在浏览器侧启停、并可出现在设置页的功能模块。
51
+ *
52
+ * 模块自带设置面板,设置页只负责遍历注册表渲染,因此新增模块不需要修改设置页。
53
+ */
54
+ export interface CodingNsClientFeatureModule extends FeatureModule<CodingNsClientServices> {
55
+ /** 卡片内容;不提供时该模块只显示标题栏开关。 */
56
+ readonly settingsPanel?: (props: FeaturePanelProps) => ReactElement | null;
57
+ }
58
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1,5 @@
1
+ import type { ReactElement } from 'react';
2
+ import type { FeaturePanelProps } from './types.js';
3
+ /** 工作区会话增强的单列设置面板。 */
4
+ export declare function WorkspaceSessionEnhancementPanel({ services, enabled, snapshot }: FeaturePanelProps): ReactElement;
5
+ //# sourceMappingURL=workspace-session-enhancement-panel.d.ts.map