@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,659 @@
1
+ import { CodingNsRpcError } from '../rpc-table.js';
2
+ import { CodingNsCliSessionStore } from './session-store.js';
3
+ const DEFAULT_INSTALLED_CACHE_TTL_MS = 5 * 60_000;
4
+ const DEFAULT_UNINSTALLED_CACHE_TTL_MS = 30_000;
5
+ const DEFAULT_MODEL_CACHE_TTL_MS = 10 * 60_000;
6
+ const DEFAULT_MODEL_RETRY_TTL_MS = 15_000;
7
+ export class CodingNsCliAdapterRegistry {
8
+ drivers = new Map();
9
+ sessions = new Map();
10
+ enabled = new Map();
11
+ providerProbeTtlMs;
12
+ missingConfirmationDelayMs;
13
+ providerProbeTimeoutMs;
14
+ providerProbeConcurrency;
15
+ probes = new Map();
16
+ executingSessions = new Set();
17
+ archivingSessions = new Set();
18
+ installedCacheTtlMs;
19
+ uninstalledCacheTtlMs;
20
+ modelCacheTtlMs;
21
+ modelRetryTtlMs;
22
+ detectionCache = new Map();
23
+ detectionRefreshes = new Map();
24
+ detectionTimers = new Map();
25
+ modelCache = new Map();
26
+ modelFailures = new Map();
27
+ modelRefreshes = new Map();
28
+ modelTimers = new Map();
29
+ modelGenerations = new Map();
30
+ requestedModelCatalogs = new Set();
31
+ cacheGeneration = 0;
32
+ disposed = false;
33
+ constructor(drivers, enabled = {}, options = {}) {
34
+ this.sessionStore = options.sessionStore;
35
+ this.nativeSessions = options.nativeSessions;
36
+ this.settings = options.settings;
37
+ this.providerProbeTtlMs = options.providerProbeTtlMs ?? 60_000;
38
+ this.missingConfirmationDelayMs = options.missingConfirmationDelayMs ?? 2_000;
39
+ this.providerProbeTimeoutMs = Math.max(1, options.providerProbeTimeoutMs ?? 10_000);
40
+ this.providerProbeConcurrency = Math.max(1, Math.floor(options.providerProbeConcurrency ?? 4));
41
+ this.installedCacheTtlMs = positiveTtl(options.installedCacheTtlMs, DEFAULT_INSTALLED_CACHE_TTL_MS);
42
+ this.uninstalledCacheTtlMs = positiveTtl(options.uninstalledCacheTtlMs, DEFAULT_UNINSTALLED_CACHE_TTL_MS);
43
+ this.modelCacheTtlMs = positiveTtl(options.modelCacheTtlMs, DEFAULT_MODEL_CACHE_TTL_MS);
44
+ this.modelRetryTtlMs = positiveTtl(options.modelRetryTtlMs, DEFAULT_MODEL_RETRY_TTL_MS);
45
+ this.syncPreferences(options.settings?.get().agentAdapterPreferences);
46
+ for (const driver of drivers) {
47
+ if (this.drivers.has(driver.descriptor.id))
48
+ throw new Error(`重复 Agent: ${driver.descriptor.id}`);
49
+ this.drivers.set(driver.descriptor.id, driver);
50
+ this.enabled.set(driver.descriptor.id, enabled[driver.descriptor.id] !== false);
51
+ }
52
+ for (const record of this.sessionStore?.list({ includeArchived: true }) ?? []) {
53
+ const { dshSessionId: _dshSessionId, title: _title, cwd: _cwd, status: _status, providerState: _providerState, providerCheckedAt: _providerCheckedAt, providerStateReason: _providerStateReason, createdAt: _createdAt, updatedAt: _updatedAt, lastError: _lastError, ...config } = record;
54
+ this.sessions.set(record.dshSessionId, config);
55
+ }
56
+ }
57
+ sessionStore;
58
+ nativeSessions;
59
+ settings;
60
+ preferences = new Map();
61
+ /** Host 启动后预热安装状态;定时器让同步 CLI 探测不阻塞功能模块装配。 */
62
+ warmCatalog() {
63
+ for (const driver of this.drivers.values())
64
+ this.scheduleDetectionRefresh(driver.descriptor.id, 0);
65
+ }
66
+ async catalog() {
67
+ return Promise.all([...this.drivers.values()].map(async (driver) => {
68
+ const detection = await this.readDetection(driver);
69
+ return { ...driver.descriptor, enabled: this.isEnabled(driver.descriptor.id), ...detection };
70
+ }));
71
+ }
72
+ async models(adapterId) {
73
+ const driver = this.requireEnabledDriver(adapterId);
74
+ this.requestedModelCatalogs.add(adapterId);
75
+ const cached = this.modelCache.get(adapterId);
76
+ if (cached !== undefined) {
77
+ if (cached.expiresAt <= Date.now())
78
+ void this.refreshModels(driver).catch(() => undefined);
79
+ return cached.value;
80
+ }
81
+ const failure = this.modelFailures.get(adapterId);
82
+ if (failure !== undefined) {
83
+ if (failure.expiresAt <= Date.now())
84
+ void this.refreshModels(driver).catch(() => undefined);
85
+ throw failure.error;
86
+ }
87
+ return this.refreshModels(driver);
88
+ }
89
+ setEnabled(adapterId, enabled) {
90
+ this.requireDriver(adapterId);
91
+ const previous = this.isEnabled(adapterId);
92
+ this.enabled.set(adapterId, enabled);
93
+ if (previous === enabled)
94
+ return enabled;
95
+ if (!enabled)
96
+ this.clearTimer(this.modelTimers, adapterId);
97
+ else if (this.requestedModelCatalogs.has(adapterId))
98
+ this.scheduleModelRefresh(adapterId, 0);
99
+ return enabled;
100
+ }
101
+ applyEnabledSettings(settings) {
102
+ for (const adapterId of this.enabled.keys())
103
+ this.setEnabled(adapterId, settings?.[adapterId] !== false);
104
+ }
105
+ isEnabled(adapterId) {
106
+ return this.enabled.get(adapterId) ?? false;
107
+ }
108
+ enabledSnapshot() {
109
+ return Object.fromEntries(this.enabled.entries());
110
+ }
111
+ setSession(sessionId, config) {
112
+ if (sessionId.trim() === '')
113
+ throw new CodingNsRpcError('CODINGNS_CLI_INVALID_SESSION', 'sessionId 不能为空');
114
+ // dsh 是 DSH 自带的默认 Agent,不对应一个外部驱动,但仍需要作为会话
115
+ // 配置保存值,方便 Client 从外部 CLI 切回默认 Agent。
116
+ if (config.adapterId !== 'dsh')
117
+ this.requireEnabledDriver(config.adapterId);
118
+ const previous = this.sessions.get(sessionId);
119
+ const sameAdapter = previous?.adapterId === config.adapterId;
120
+ const remembered = this.preferences.get(config.adapterId) ?? this.findRememberedPreference(config.adapterId);
121
+ const providerSessionId = config.providerSessionId?.trim();
122
+ const providerIdentityChanged = providerSessionId !== undefined
123
+ && providerSessionId !== previous?.providerSessionId;
124
+ const normalized = {
125
+ adapterId: config.adapterId,
126
+ ...(config.modelId?.trim()
127
+ ? { modelId: config.modelId.trim() }
128
+ : sameAdapter && previous?.modelId
129
+ ? { modelId: previous.modelId }
130
+ : remembered?.modelId
131
+ ? { modelId: remembered.modelId }
132
+ : {}),
133
+ ...(config.effortId?.trim()
134
+ ? { effortId: config.effortId.trim() }
135
+ : sameAdapter && previous?.effortId
136
+ ? { effortId: previous.effortId }
137
+ : remembered?.effortId
138
+ ? { effortId: remembered.effortId }
139
+ : {}),
140
+ ...(providerSessionId ? { providerSessionId } : sameAdapter && previous?.providerSessionId ? { providerSessionId: previous.providerSessionId } : {}),
141
+ ...(config.rawStoreRef?.trim()
142
+ ? { rawStoreRef: config.rawStoreRef.trim() }
143
+ : sameAdapter && !providerIdentityChanged && previous?.rawStoreRef
144
+ ? { rawStoreRef: previous.rawStoreRef }
145
+ : {}),
146
+ };
147
+ this.sessions.set(sessionId, normalized);
148
+ this.sessionStore?.upsert(sessionId, normalized);
149
+ this.rememberPreference(config.adapterId, normalized);
150
+ return normalized;
151
+ }
152
+ /** 设置服务变更后重新载入适配器级默认选择。 */
153
+ syncPreferences(value) {
154
+ if (value === undefined)
155
+ return;
156
+ this.preferences.clear();
157
+ for (const [adapterId, preference] of Object.entries(value)) {
158
+ const modelId = preference?.modelId?.trim();
159
+ const effortId = preference?.effortId?.trim();
160
+ if (modelId === undefined && effortId === undefined)
161
+ continue;
162
+ this.preferences.set(adapterId, {
163
+ ...(modelId ? { modelId } : {}),
164
+ ...(effortId ? { effortId } : {}),
165
+ });
166
+ }
167
+ }
168
+ findRememberedPreference(adapterId) {
169
+ const records = this.sessionStore?.list({ includeArchived: true, adapterId }) ?? [];
170
+ for (const record of records) {
171
+ if (record.modelId !== undefined || record.effortId !== undefined) {
172
+ const preference = {
173
+ ...(record.modelId ? { modelId: record.modelId } : {}),
174
+ ...(record.effortId ? { effortId: record.effortId } : {}),
175
+ };
176
+ this.preferences.set(adapterId, preference);
177
+ return preference;
178
+ }
179
+ }
180
+ return undefined;
181
+ }
182
+ rememberPreference(adapterId, config) {
183
+ const previous = this.preferences.get(adapterId);
184
+ const modelId = config.modelId?.trim() || previous?.modelId;
185
+ const effortId = config.effortId?.trim() || previous?.effortId;
186
+ if (modelId === undefined && effortId === undefined)
187
+ return;
188
+ if (previous?.modelId === modelId && previous?.effortId === effortId)
189
+ return;
190
+ const preference = {
191
+ ...(modelId ? { modelId } : {}),
192
+ ...(effortId ? { effortId } : {}),
193
+ };
194
+ this.preferences.set(adapterId, preference);
195
+ if (this.settings === undefined)
196
+ return;
197
+ const snapshot = Object.fromEntries([...this.preferences.entries()].map(([id, value]) => [id, { ...value }]));
198
+ void this.settings.update({ agentAdapterPreferences: snapshot }).catch(() => undefined);
199
+ }
200
+ getSession(sessionId) {
201
+ const session = this.sessions.get(sessionId);
202
+ if (session !== undefined && (session.adapterId === 'dsh' || this.isEnabled(session.adapterId)))
203
+ return session;
204
+ const remembered = this.preferences.get('dsh') ?? this.findRememberedPreference('dsh');
205
+ return {
206
+ adapterId: 'dsh',
207
+ ...(remembered?.modelId ? { modelId: remembered.modelId } : {}),
208
+ ...(remembered?.effortId ? { effortId: remembered.effortId } : {}),
209
+ };
210
+ }
211
+ async *execute(input) {
212
+ const driver = this.requireEnabledDriver(input.adapterId);
213
+ if (this.archivingSessions.has(input.sessionId)) {
214
+ throw new CodingNsRpcError('CODINGNS_CLI_INVALID_SESSION', '外部会话正在归档,不能开始新一轮执行');
215
+ }
216
+ if (this.executingSessions.has(input.sessionId)) {
217
+ throw new CodingNsRpcError('CODINGNS_CLI_INVALID_SESSION', '外部会话已有一轮执行正在进行');
218
+ }
219
+ this.executingSessions.add(input.sessionId);
220
+ const previous = this.sessions.get(input.sessionId);
221
+ let current = {
222
+ ...(previous ?? { adapterId: input.adapterId }),
223
+ ...(input.modelId?.trim() ? { modelId: input.modelId.trim() } : {}),
224
+ ...(input.effortId?.trim() ? { effortId: input.effortId.trim() } : {}),
225
+ };
226
+ // 除了 Client 的 session/set,Host 内部和未来的调用方也可能直接执行一轮。
227
+ // 最近使用应由真实执行参数更新,不能依赖某个 UI 一定先发 RPC。
228
+ this.rememberPreference(input.adapterId, input);
229
+ try {
230
+ const stored = this.sessionStore?.get(input.sessionId);
231
+ if (stored?.status === 'archived') {
232
+ throw new CodingNsRpcError('CODINGNS_CLI_INVALID_SESSION', '外部会话已归档,不能继续执行');
233
+ }
234
+ if (stored !== undefined && (stored.providerSessionId !== undefined || stored.rawStoreRef !== undefined)) {
235
+ await this.refreshProviderState(stored, true);
236
+ if (this.sessionStore?.get(input.sessionId)?.providerState === 'missing') {
237
+ throw new CodingNsRpcError('CODINGNS_CLI_SESSION_MISSING', '外部 Agent 原始会话已删除,无法继续恢复');
238
+ }
239
+ }
240
+ this.sessions.set(input.sessionId, current);
241
+ this.sessionStore?.upsert(input.sessionId, {
242
+ ...current,
243
+ adapterId: input.adapterId,
244
+ ...(input.cwd ? { cwd: input.cwd } : {}),
245
+ status: 'active',
246
+ title: input.prompt,
247
+ });
248
+ // Agent Loop 通常已经创建了同名 DSH 原生会话;直接调用 Registry 时才按需补建。
249
+ // 原生服务失败不能阻断外部 Agent,消息仍由现有 llm/stream 链路处理。
250
+ try {
251
+ await this.nativeSessions?.ensure(input.sessionId, input.cwd);
252
+ }
253
+ catch { /* 可选服务降级 */ }
254
+ for await (const event of driver.executeTurn(input)) {
255
+ if (event.type === 'session-binding') {
256
+ const providerIdentityChanged = event.providerSessionId !== current.providerSessionId;
257
+ const { rawStoreRef: previousRawStoreRef, ...currentWithoutRawStoreRef } = current;
258
+ const next = {
259
+ ...currentWithoutRawStoreRef,
260
+ providerSessionId: event.providerSessionId,
261
+ ...(event.rawStoreRef
262
+ ? { rawStoreRef: event.rawStoreRef }
263
+ : !providerIdentityChanged && previousRawStoreRef
264
+ ? { rawStoreRef: previousRawStoreRef }
265
+ : {}),
266
+ };
267
+ this.sessions.set(input.sessionId, next);
268
+ current = next;
269
+ this.sessionStore?.upsert(input.sessionId, {
270
+ ...next,
271
+ status: 'active',
272
+ providerState: 'available',
273
+ providerCheckedAt: new Date().toISOString(),
274
+ });
275
+ }
276
+ if (event.type === 'finish')
277
+ this.sessionStore?.upsert(input.sessionId, { ...this.sessions.get(input.sessionId) ?? current, status: event.reason === 'error' ? 'error' : 'idle' });
278
+ yield event;
279
+ }
280
+ }
281
+ catch (error) {
282
+ this.sessionStore?.upsert(input.sessionId, {
283
+ ...this.sessions.get(input.sessionId) ?? current,
284
+ status: 'error',
285
+ lastError: error instanceof Error ? error.message : String(error),
286
+ });
287
+ throw error;
288
+ }
289
+ finally {
290
+ this.executingSessions.delete(input.sessionId);
291
+ try {
292
+ await this.nativeSessions?.flush(input.sessionId);
293
+ }
294
+ catch { /* DSH 自身检查点策略负责重试 */ }
295
+ }
296
+ }
297
+ async listSessions(options = {}) {
298
+ const candidates = this.sessionStore?.list(options) ?? [];
299
+ await forEachConcurrent(candidates, this.providerProbeConcurrency, (record) => this.refreshProviderState(record, false));
300
+ return (this.sessionStore?.list(options) ?? [])
301
+ .filter((record) => record.adapterId !== 'dsh')
302
+ .map(({ rawStoreRef: _rawStoreRef, ...record }) => record);
303
+ }
304
+ async archiveSession(sessionId) {
305
+ const current = this.sessionStore?.get(sessionId);
306
+ if (current === undefined)
307
+ return undefined;
308
+ if (current.status === 'active' || this.executingSessions.has(sessionId)) {
309
+ throw new CodingNsRpcError('CODINGNS_CLI_INVALID_SESSION', '外部会话正在执行,不能归档');
310
+ }
311
+ if (this.archivingSessions.has(sessionId)) {
312
+ throw new CodingNsRpcError('CODINGNS_CLI_INVALID_SESSION', '外部会话正在归档');
313
+ }
314
+ this.archivingSessions.add(sessionId);
315
+ try {
316
+ // 完整 DSH 中先改变原生侧栏可见性;调用失败时不修改插件索引,避免两边状态分叉。
317
+ if (this.nativeSessions !== undefined) {
318
+ const archived = await this.nativeSessions.archive?.(sessionId) ?? false;
319
+ if (!archived) {
320
+ throw new CodingNsRpcError('CODINGNS_CLI_UNAVAILABLE', 'DSH 原生会话归档服务不可用,未修改外部会话索引');
321
+ }
322
+ }
323
+ const record = this.sessionStore?.archive(sessionId);
324
+ if (record === undefined)
325
+ return undefined;
326
+ const { rawStoreRef: _rawStoreRef, ...safe } = record;
327
+ return safe;
328
+ }
329
+ finally {
330
+ this.archivingSessions.delete(sessionId);
331
+ }
332
+ }
333
+ async respondPermission(sessionId, response) {
334
+ const session = this.requireSession(sessionId);
335
+ const driver = this.requireEnabledDriver(session.adapterId);
336
+ if (driver.respondPermission === undefined)
337
+ throw new CodingNsRpcError('CODINGNS_CLI_UNSUPPORTED', '当前 Agent 不支持权限回传');
338
+ await driver.respondPermission(sessionId, response);
339
+ }
340
+ async respondQuestion(sessionId, response) {
341
+ const session = this.requireSession(sessionId);
342
+ const driver = this.requireEnabledDriver(session.adapterId);
343
+ if (driver.respondQuestion === undefined)
344
+ throw new CodingNsRpcError('CODINGNS_CLI_UNSUPPORTED', '当前 Agent 不支持问题回传');
345
+ await driver.respondQuestion(sessionId, response);
346
+ }
347
+ async steer(sessionId, prompt, followUp = false) {
348
+ const session = this.requireSession(sessionId);
349
+ const driver = this.requireEnabledDriver(session.adapterId);
350
+ const handler = followUp ? driver.followUp : driver.steer;
351
+ if (handler === undefined)
352
+ throw new CodingNsRpcError('CODINGNS_CLI_UNSUPPORTED', '当前 Agent 不支持运行中消息');
353
+ await handler.call(driver, sessionId, prompt);
354
+ }
355
+ async interrupt(sessionId) {
356
+ const session = this.requireSession(sessionId);
357
+ const driver = this.requireEnabledDriver(session.adapterId);
358
+ if (driver.interrupt === undefined)
359
+ throw new CodingNsRpcError('CODINGNS_CLI_UNSUPPORTED', '当前 Agent 不支持中断');
360
+ await driver.interrupt(sessionId);
361
+ }
362
+ async dispose() {
363
+ if (this.disposed)
364
+ return;
365
+ this.disposed = true;
366
+ this.cacheGeneration += 1;
367
+ this.clearAllTimers(this.detectionTimers);
368
+ this.clearAllTimers(this.modelTimers);
369
+ this.detectionCache.clear();
370
+ this.modelCache.clear();
371
+ this.modelFailures.clear();
372
+ this.modelGenerations.clear();
373
+ this.requestedModelCatalogs.clear();
374
+ await Promise.all([...this.drivers.values()].map((driver) => driver.dispose?.()));
375
+ }
376
+ async readDetection(driver) {
377
+ const adapterId = driver.descriptor.id;
378
+ const cached = this.detectionCache.get(adapterId);
379
+ if (cached !== undefined) {
380
+ if (cached.expiresAt <= Date.now())
381
+ void this.refreshDetection(driver);
382
+ return cached.value;
383
+ }
384
+ return this.refreshDetection(driver);
385
+ }
386
+ /** 同一适配器只允许一个探测;刷新失败时保留最后一次可用状态。 */
387
+ refreshDetection(driver) {
388
+ const adapterId = driver.descriptor.id;
389
+ const running = this.detectionRefreshes.get(adapterId);
390
+ if (running !== undefined)
391
+ return running;
392
+ this.clearTimer(this.detectionTimers, adapterId);
393
+ const generation = this.cacheGeneration;
394
+ const previous = this.detectionCache.get(adapterId);
395
+ const refresh = (async () => {
396
+ let detection;
397
+ let failed = false;
398
+ try {
399
+ detection = await driver.detect();
400
+ }
401
+ catch {
402
+ failed = true;
403
+ detection = previous?.value ?? { installed: false, version: null, command: null };
404
+ }
405
+ if (this.disposed || generation !== this.cacheGeneration)
406
+ return detection;
407
+ const ttl = failed
408
+ ? this.uninstalledCacheTtlMs
409
+ : detection.installed
410
+ ? this.installedCacheTtlMs
411
+ : this.uninstalledCacheTtlMs;
412
+ this.detectionCache.set(adapterId, { value: detection, expiresAt: Date.now() + ttl });
413
+ this.scheduleDetectionRefresh(adapterId, ttl);
414
+ if (previous !== undefined && detectionFingerprint(previous.value) !== detectionFingerprint(detection)) {
415
+ this.invalidateModelCache(adapterId);
416
+ if (detection.installed && this.requestedModelCatalogs.has(adapterId) && this.isEnabled(adapterId)) {
417
+ this.scheduleModelRefresh(adapterId, 0);
418
+ }
419
+ }
420
+ return detection;
421
+ })().finally(() => {
422
+ if (this.detectionRefreshes.get(adapterId) === refresh)
423
+ this.detectionRefreshes.delete(adapterId);
424
+ });
425
+ this.detectionRefreshes.set(adapterId, refresh);
426
+ return refresh;
427
+ }
428
+ /** 模型目录使用 stale-while-revalidate;空结果和失败只缩短重试间隔。 */
429
+ refreshModels(driver) {
430
+ const adapterId = driver.descriptor.id;
431
+ const running = this.modelRefreshes.get(adapterId);
432
+ if (running !== undefined)
433
+ return running;
434
+ this.clearTimer(this.modelTimers, adapterId);
435
+ const generation = this.cacheGeneration;
436
+ const modelGeneration = this.modelGenerations.get(adapterId) ?? 0;
437
+ const previous = this.modelCache.get(adapterId);
438
+ const refresh = (async () => {
439
+ try {
440
+ const catalog = await driver.listModels();
441
+ if (this.disposed || generation !== this.cacheGeneration)
442
+ return previous?.value ?? catalog;
443
+ if (modelGeneration !== (this.modelGenerations.get(adapterId) ?? 0))
444
+ return this.refreshModels(driver);
445
+ this.modelFailures.delete(adapterId);
446
+ const hasModels = catalogHasModels(catalog);
447
+ const keepPrevious = !hasModels && previous !== undefined && catalogHasModels(previous.value);
448
+ const value = keepPrevious ? previous.value : catalog;
449
+ const ttl = hasModels ? this.modelCacheTtlMs : this.modelRetryTtlMs;
450
+ this.modelCache.set(adapterId, { value, expiresAt: Date.now() + ttl });
451
+ this.scheduleModelRefresh(adapterId, ttl);
452
+ return value;
453
+ }
454
+ catch (error) {
455
+ if (this.disposed || generation !== this.cacheGeneration) {
456
+ if (previous !== undefined)
457
+ return previous.value;
458
+ throw error;
459
+ }
460
+ if (modelGeneration !== (this.modelGenerations.get(adapterId) ?? 0))
461
+ return this.refreshModels(driver);
462
+ if (previous !== undefined) {
463
+ this.modelCache.set(adapterId, { value: previous.value, expiresAt: Date.now() + this.modelRetryTtlMs });
464
+ this.scheduleModelRefresh(adapterId, this.modelRetryTtlMs);
465
+ return previous.value;
466
+ }
467
+ this.modelFailures.set(adapterId, { error, expiresAt: Date.now() + this.modelRetryTtlMs });
468
+ this.scheduleModelRefresh(adapterId, this.modelRetryTtlMs);
469
+ throw error;
470
+ }
471
+ })().finally(() => {
472
+ if (this.modelRefreshes.get(adapterId) === refresh)
473
+ this.modelRefreshes.delete(adapterId);
474
+ });
475
+ this.modelRefreshes.set(adapterId, refresh);
476
+ return refresh;
477
+ }
478
+ scheduleDetectionRefresh(adapterId, delayMs) {
479
+ if (this.disposed)
480
+ return;
481
+ this.clearTimer(this.detectionTimers, adapterId);
482
+ const timer = setTimeout(() => {
483
+ this.detectionTimers.delete(adapterId);
484
+ const driver = this.drivers.get(adapterId);
485
+ if (driver !== undefined && !this.disposed)
486
+ void this.refreshDetection(driver);
487
+ }, delayMs);
488
+ unrefTimer(timer);
489
+ this.detectionTimers.set(adapterId, timer);
490
+ }
491
+ scheduleModelRefresh(adapterId, delayMs) {
492
+ if (this.disposed || !this.isEnabled(adapterId) || !this.requestedModelCatalogs.has(adapterId))
493
+ return;
494
+ this.clearTimer(this.modelTimers, adapterId);
495
+ const timer = setTimeout(() => {
496
+ this.modelTimers.delete(adapterId);
497
+ const driver = this.drivers.get(adapterId);
498
+ if (driver !== undefined && this.isEnabled(adapterId) && !this.disposed)
499
+ void this.refreshModels(driver).catch(() => undefined);
500
+ }, delayMs);
501
+ unrefTimer(timer);
502
+ this.modelTimers.set(adapterId, timer);
503
+ }
504
+ invalidateModelCache(adapterId) {
505
+ this.clearTimer(this.modelTimers, adapterId);
506
+ this.modelGenerations.set(adapterId, (this.modelGenerations.get(adapterId) ?? 0) + 1);
507
+ this.modelRefreshes.delete(adapterId);
508
+ this.modelCache.delete(adapterId);
509
+ this.modelFailures.delete(adapterId);
510
+ }
511
+ clearTimer(timers, adapterId) {
512
+ const timer = timers.get(adapterId);
513
+ if (timer !== undefined)
514
+ clearTimeout(timer);
515
+ timers.delete(adapterId);
516
+ }
517
+ clearAllTimers(timers) {
518
+ for (const timer of timers.values())
519
+ clearTimeout(timer);
520
+ timers.clear();
521
+ }
522
+ async refreshProviderState(record, force) {
523
+ if (record.status === 'archived')
524
+ return;
525
+ if (!force && record.status === 'active')
526
+ return;
527
+ if (!force && isFreshProviderState(record.providerCheckedAt, this.providerProbeTtlMs))
528
+ return;
529
+ if (record.providerSessionId === undefined && record.rawStoreRef === undefined)
530
+ return;
531
+ const driver = this.drivers.get(record.adapterId);
532
+ if (driver?.probeSession === undefined)
533
+ return;
534
+ const probeKey = providerProbeKey(record);
535
+ const running = this.probes.get(probeKey);
536
+ if (running !== undefined)
537
+ return running;
538
+ const probe = this.runProviderProbe(record, driver).finally(() => {
539
+ if (this.probes.get(probeKey) === probe)
540
+ this.probes.delete(probeKey);
541
+ });
542
+ this.probes.set(probeKey, probe);
543
+ return probe;
544
+ }
545
+ async runProviderProbe(record, driver) {
546
+ const input = {
547
+ ...(record.providerSessionId ? { providerSessionId: record.providerSessionId } : {}),
548
+ ...(record.rawStoreRef ? { rawStoreRef: record.rawStoreRef } : {}),
549
+ ...(record.cwd ? { cwd: record.cwd } : {}),
550
+ };
551
+ let result;
552
+ try {
553
+ result = await this.probeWithTimeout(driver, input);
554
+ if (result.state === 'missing') {
555
+ await delay(this.missingConfirmationDelayMs);
556
+ result = await this.probeWithTimeout(driver, input);
557
+ }
558
+ }
559
+ catch (error) {
560
+ result = {
561
+ state: 'unknown',
562
+ reason: `会话探测失败: ${error instanceof Error ? error.message : String(error)}`,
563
+ };
564
+ }
565
+ // 探测期间绑定可能已经切换;旧结果不得覆盖新 Provider 会话。
566
+ const current = this.sessionStore?.get(record.dshSessionId);
567
+ if (current === undefined || current.adapterId !== record.adapterId)
568
+ return;
569
+ if (current.providerSessionId !== record.providerSessionId || current.rawStoreRef !== record.rawStoreRef)
570
+ return;
571
+ this.sessionStore?.updateProviderState(record.dshSessionId, {
572
+ state: result.state,
573
+ checkedAt: new Date().toISOString(),
574
+ reason: result.reason,
575
+ ...(result.rawStoreRef ? { rawStoreRef: result.rawStoreRef } : {}),
576
+ });
577
+ }
578
+ async probeWithTimeout(driver, input) {
579
+ const controller = new AbortController();
580
+ let timer;
581
+ const timeout = new Promise((resolve) => {
582
+ timer = setTimeout(() => {
583
+ controller.abort();
584
+ resolve({ state: 'unreachable', reason: `Provider 会话探测超过 ${this.providerProbeTimeoutMs}ms` });
585
+ }, this.providerProbeTimeoutMs);
586
+ });
587
+ try {
588
+ return await Promise.race([
589
+ driver.probeSession({ ...input, signal: controller.signal }),
590
+ timeout,
591
+ ]);
592
+ }
593
+ finally {
594
+ if (timer !== undefined)
595
+ clearTimeout(timer);
596
+ }
597
+ }
598
+ requireDriver(adapterId) {
599
+ const driver = this.drivers.get(adapterId);
600
+ if (driver === undefined)
601
+ throw new CodingNsRpcError('CODINGNS_CLI_UNAVAILABLE', `Agent 不可用: ${adapterId}`);
602
+ return driver;
603
+ }
604
+ requireEnabledDriver(adapterId) {
605
+ const driver = this.requireDriver(adapterId);
606
+ if (!this.isEnabled(adapterId))
607
+ throw new CodingNsRpcError('CODINGNS_CLI_DISABLED', `Agent 已停用: ${adapterId}`);
608
+ return driver;
609
+ }
610
+ requireSession(sessionId) {
611
+ const session = this.sessions.get(sessionId);
612
+ if (session === undefined || session.adapterId === 'dsh')
613
+ throw new CodingNsRpcError('CODINGNS_CLI_INVALID_SESSION', '会话未绑定外部 Agent');
614
+ return session;
615
+ }
616
+ }
617
+ function isFreshProviderState(checkedAt, ttlMs) {
618
+ if (checkedAt === undefined || ttlMs <= 0)
619
+ return false;
620
+ const timestamp = Date.parse(checkedAt);
621
+ return Number.isFinite(timestamp) && Date.now() - timestamp < ttlMs;
622
+ }
623
+ function providerProbeKey(record) {
624
+ return JSON.stringify([
625
+ record.dshSessionId,
626
+ record.adapterId,
627
+ record.providerSessionId ?? null,
628
+ record.rawStoreRef ?? null,
629
+ ]);
630
+ }
631
+ function delay(milliseconds) {
632
+ return milliseconds <= 0 ? Promise.resolve() : new Promise((resolve) => setTimeout(resolve, milliseconds));
633
+ }
634
+ async function forEachConcurrent(values, concurrency, task) {
635
+ let nextIndex = 0;
636
+ const workerCount = Math.min(concurrency, values.length);
637
+ const workers = Array.from({ length: workerCount }, async () => {
638
+ while (nextIndex < values.length) {
639
+ const index = nextIndex;
640
+ nextIndex += 1;
641
+ await task(values[index]);
642
+ }
643
+ });
644
+ await Promise.all(workers);
645
+ }
646
+ function positiveTtl(value, fallback) {
647
+ return Math.max(1, Math.floor(value ?? fallback));
648
+ }
649
+ function detectionFingerprint(detection) {
650
+ return JSON.stringify([detection.installed, detection.command, detection.version]);
651
+ }
652
+ function catalogHasModels(catalog) {
653
+ return catalog.groups.some((group) => group.models.length > 0);
654
+ }
655
+ function unrefTimer(timer) {
656
+ const nodeTimer = timer;
657
+ nodeTimer.unref?.();
658
+ }
659
+ //# sourceMappingURL=registry.js.map
@@ -0,0 +1,24 @@
1
+ import { spawnSync, type SpawnSyncResult } from 'node:child_process';
2
+ import type { CodingNsCliModelCatalog, CodingNsAgentEvent } from '../../shared/contracts/cli-adapter.js';
3
+ import { JsonRpcProcess, type JsonRpcMessage } from './json-rpc-process.js';
4
+ export interface RpcBinaryOptions {
5
+ readonly binaries: readonly string[];
6
+ readonly spawnSync?: typeof spawnSync;
7
+ }
8
+ export declare function detectBinary(options: RpcBinaryOptions): Promise<{
9
+ installed: boolean;
10
+ version: string | null;
11
+ command: string | null;
12
+ }>;
13
+ /** 把一个 JSON-RPC 请求期间收到的通知排成异步流,同时等待最终响应。 */
14
+ export declare function streamRpcRequest(process: JsonRpcProcess, method: string, params: unknown, signal: AbortSignal | undefined, options?: {
15
+ readonly dispose?: boolean;
16
+ readonly killOnAbort?: boolean;
17
+ readonly onNotification?: (message: JsonRpcMessage) => void;
18
+ }): AsyncGenerator<JsonRpcMessage, unknown, void>;
19
+ export declare function emptyCatalog(): CodingNsCliModelCatalog;
20
+ export declare function textValue(value: unknown): string | null;
21
+ export declare function usageChunk(value: unknown): CodingNsAgentEvent | null;
22
+ export declare function isRecord(value: unknown): value is Record<string, any>;
23
+ export type { SpawnSyncResult };
24
+ //# sourceMappingURL=rpc-driver-utils.d.ts.map