@clinebot/core 0.0.35 → 0.0.37

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 (441) hide show
  1. package/README.md +1 -2
  2. package/dist/ClineCore.d.ts +362 -39
  3. package/dist/ClineCore.d.ts.map +1 -1
  4. package/dist/account/cline-account-service.d.ts.map +1 -1
  5. package/dist/account/index.d.ts +1 -1
  6. package/dist/account/index.d.ts.map +1 -1
  7. package/dist/account/rpc.d.ts +6 -6
  8. package/dist/account/rpc.d.ts.map +1 -1
  9. package/dist/cron/cron-event-ingress.d.ts +38 -0
  10. package/dist/cron/cron-event-ingress.d.ts.map +1 -0
  11. package/dist/cron/cron-materializer.d.ts +36 -0
  12. package/dist/cron/cron-materializer.d.ts.map +1 -0
  13. package/dist/cron/cron-reconciler.d.ts +62 -0
  14. package/dist/cron/cron-reconciler.d.ts.map +1 -0
  15. package/dist/cron/cron-report-writer.d.ts +41 -0
  16. package/dist/cron/cron-report-writer.d.ts.map +1 -0
  17. package/dist/cron/cron-runner.d.ts +43 -0
  18. package/dist/cron/cron-runner.d.ts.map +1 -0
  19. package/dist/cron/cron-schema.d.ts +3 -0
  20. package/dist/cron/cron-schema.d.ts.map +1 -0
  21. package/dist/cron/cron-service.d.ts +57 -0
  22. package/dist/cron/cron-service.d.ts.map +1 -0
  23. package/dist/cron/cron-spec-parser.d.ts +27 -0
  24. package/dist/cron/cron-spec-parser.d.ts.map +1 -0
  25. package/dist/cron/cron-watcher.d.ts +23 -0
  26. package/dist/cron/cron-watcher.d.ts.map +1 -0
  27. package/dist/cron/resource-limiter.d.ts +9 -0
  28. package/dist/cron/resource-limiter.d.ts.map +1 -0
  29. package/dist/cron/schedule-command-service.d.ts +10 -0
  30. package/dist/cron/schedule-command-service.d.ts.map +1 -0
  31. package/dist/cron/schedule-service.d.ts +100 -0
  32. package/dist/cron/schedule-service.d.ts.map +1 -0
  33. package/dist/cron/scheduler.d.ts +68 -0
  34. package/dist/cron/scheduler.d.ts.map +1 -0
  35. package/dist/cron/sqlite-cron-store.d.ts +230 -0
  36. package/dist/cron/sqlite-cron-store.d.ts.map +1 -0
  37. package/dist/cron/sqlite-schedule-store.d.ts +52 -0
  38. package/dist/cron/sqlite-schedule-store.d.ts.map +1 -0
  39. package/dist/extensions/config/agent-config-loader.d.ts +4 -3
  40. package/dist/extensions/config/agent-config-loader.d.ts.map +1 -1
  41. package/dist/extensions/config/runtime-commands.d.ts +1 -0
  42. package/dist/extensions/config/runtime-commands.d.ts.map +1 -1
  43. package/dist/extensions/config/user-instruction-config-loader.d.ts +1 -0
  44. package/dist/extensions/config/user-instruction-config-loader.d.ts.map +1 -1
  45. package/dist/extensions/context/agentic-compaction.d.ts +2 -2
  46. package/dist/extensions/context/agentic-compaction.d.ts.map +1 -1
  47. package/dist/extensions/context/compaction-shared.d.ts +5 -4
  48. package/dist/extensions/context/compaction-shared.d.ts.map +1 -1
  49. package/dist/extensions/context/compaction.d.ts.map +1 -1
  50. package/dist/extensions/plugin/plugin-config-loader.d.ts +15 -2
  51. package/dist/extensions/plugin/plugin-config-loader.d.ts.map +1 -1
  52. package/dist/extensions/plugin/plugin-loader.d.ts +13 -7
  53. package/dist/extensions/plugin/plugin-loader.d.ts.map +1 -1
  54. package/dist/extensions/plugin/plugin-module-import.d.ts.map +1 -1
  55. package/dist/extensions/plugin/plugin-sandbox.d.ts +21 -2
  56. package/dist/extensions/plugin/plugin-sandbox.d.ts.map +1 -1
  57. package/dist/extensions/plugin/plugin-targeting.d.ts +7 -0
  58. package/dist/extensions/plugin/plugin-targeting.d.ts.map +1 -0
  59. package/dist/extensions/plugin-sandbox-bootstrap.js +237 -276
  60. package/dist/extensions/tools/constants.d.ts +1 -0
  61. package/dist/extensions/tools/constants.d.ts.map +1 -1
  62. package/dist/extensions/tools/definitions.d.ts +3 -4
  63. package/dist/extensions/tools/definitions.d.ts.map +1 -1
  64. package/dist/extensions/tools/executors/apply-patch.d.ts +3 -1
  65. package/dist/extensions/tools/executors/apply-patch.d.ts.map +1 -1
  66. package/dist/extensions/tools/executors/editor.d.ts.map +1 -1
  67. package/dist/extensions/tools/executors/search.d.ts +1 -1
  68. package/dist/extensions/tools/executors/search.d.ts.map +1 -1
  69. package/dist/extensions/tools/helpers.d.ts +1 -0
  70. package/dist/extensions/tools/helpers.d.ts.map +1 -1
  71. package/dist/extensions/tools/index.d.ts +3 -2
  72. package/dist/extensions/tools/index.d.ts.map +1 -1
  73. package/dist/extensions/tools/presets.d.ts +27 -44
  74. package/dist/extensions/tools/presets.d.ts.map +1 -1
  75. package/dist/extensions/tools/runtime.d.ts +25 -0
  76. package/dist/extensions/tools/runtime.d.ts.map +1 -0
  77. package/dist/extensions/tools/schemas.d.ts +25 -3
  78. package/dist/extensions/tools/schemas.d.ts.map +1 -1
  79. package/dist/extensions/tools/team/delegated-agent.d.ts +2 -2
  80. package/dist/extensions/tools/team/delegated-agent.d.ts.map +1 -1
  81. package/dist/extensions/tools/team/multi-agent.d.ts +7 -3
  82. package/dist/extensions/tools/team/multi-agent.d.ts.map +1 -1
  83. package/dist/extensions/tools/team/team-tools.d.ts +1 -0
  84. package/dist/extensions/tools/team/team-tools.d.ts.map +1 -1
  85. package/dist/extensions/tools/types.d.ts +0 -5
  86. package/dist/extensions/tools/types.d.ts.map +1 -1
  87. package/dist/hooks/hook-bridge.d.ts +118 -0
  88. package/dist/hooks/hook-bridge.d.ts.map +1 -0
  89. package/dist/hooks/hook-file-hooks.d.ts +6 -2
  90. package/dist/hooks/hook-file-hooks.d.ts.map +1 -1
  91. package/dist/hooks/hook-registry.d.ts +16 -0
  92. package/dist/hooks/hook-registry.d.ts.map +1 -0
  93. package/dist/hooks/index.d.ts +0 -1
  94. package/dist/hooks/index.d.ts.map +1 -1
  95. package/dist/hooks/subprocess.d.ts +8 -1
  96. package/dist/hooks/subprocess.d.ts.map +1 -1
  97. package/dist/hub/browser-websocket.d.ts +18 -0
  98. package/dist/hub/browser-websocket.d.ts.map +1 -0
  99. package/dist/hub/client.d.ts +51 -0
  100. package/dist/hub/client.d.ts.map +1 -0
  101. package/dist/hub/connect.d.ts +15 -0
  102. package/dist/hub/connect.d.ts.map +1 -0
  103. package/dist/hub/daemon-entry.d.ts +2 -0
  104. package/dist/hub/daemon-entry.d.ts.map +1 -0
  105. package/dist/hub/daemon-entry.js +1305 -0
  106. package/dist/hub/daemon.d.ts +5 -0
  107. package/dist/hub/daemon.d.ts.map +1 -0
  108. package/dist/hub/defaults.d.ts +17 -0
  109. package/dist/hub/defaults.d.ts.map +1 -0
  110. package/dist/hub/discovery.d.ts +29 -0
  111. package/dist/hub/discovery.d.ts.map +1 -0
  112. package/dist/hub/index.d.ts +15 -0
  113. package/dist/hub/index.d.ts.map +1 -0
  114. package/dist/hub/index.js +1294 -0
  115. package/dist/hub/native-transport.d.ts +17 -0
  116. package/dist/hub/native-transport.d.ts.map +1 -0
  117. package/dist/hub/runtime-handlers.d.ts +11 -0
  118. package/dist/hub/runtime-handlers.d.ts.map +1 -0
  119. package/dist/hub/server.d.ts +104 -0
  120. package/dist/hub/server.d.ts.map +1 -0
  121. package/dist/hub/session-client.d.ts +90 -0
  122. package/dist/hub/session-client.d.ts.map +1 -0
  123. package/dist/hub/start-shared-server.d.ts +19 -0
  124. package/dist/hub/start-shared-server.d.ts.map +1 -0
  125. package/dist/hub/transport.d.ts +8 -0
  126. package/dist/hub/transport.d.ts.map +1 -0
  127. package/dist/hub/ui-client.d.ts +45 -0
  128. package/dist/hub/ui-client.d.ts.map +1 -0
  129. package/dist/hub/workspace.d.ts +4 -0
  130. package/dist/hub/workspace.d.ts.map +1 -0
  131. package/dist/index.d.ts +29 -16
  132. package/dist/index.d.ts.map +1 -1
  133. package/dist/index.js +782 -471
  134. package/dist/llms/cline-recommended-models.d.ts +20 -0
  135. package/dist/llms/cline-recommended-models.d.ts.map +1 -0
  136. package/dist/llms/configured-provider-registry.d.ts +28 -0
  137. package/dist/llms/configured-provider-registry.d.ts.map +1 -0
  138. package/dist/llms/handler-factory.d.ts +16 -0
  139. package/dist/llms/handler-factory.d.ts.map +1 -0
  140. package/dist/llms/provider-defaults.d.ts +27 -0
  141. package/dist/llms/provider-defaults.d.ts.map +1 -0
  142. package/dist/llms/provider-settings.d.ts +245 -0
  143. package/dist/llms/provider-settings.d.ts.map +1 -0
  144. package/dist/llms/runtime-config.d.ts +4 -0
  145. package/dist/llms/runtime-config.d.ts.map +1 -0
  146. package/dist/llms/runtime-registry.d.ts +20 -0
  147. package/dist/llms/runtime-registry.d.ts.map +1 -0
  148. package/dist/llms/runtime-types.d.ts +85 -0
  149. package/dist/llms/runtime-types.d.ts.map +1 -0
  150. package/dist/runtime/agent-config-adapter.d.ts +148 -0
  151. package/dist/runtime/agent-config-adapter.d.ts.map +1 -0
  152. package/dist/runtime/agent-runtime-config-builder.d.ts +96 -0
  153. package/dist/runtime/agent-runtime-config-builder.d.ts.map +1 -0
  154. package/dist/runtime/history.d.ts +6 -0
  155. package/dist/runtime/history.d.ts.map +1 -1
  156. package/dist/runtime/host.d.ts +1 -2
  157. package/dist/runtime/host.d.ts.map +1 -1
  158. package/dist/runtime/loop-detection.d.ts +59 -0
  159. package/dist/runtime/loop-detection.d.ts.map +1 -0
  160. package/dist/runtime/mistake-tracker.d.ts +69 -0
  161. package/dist/runtime/mistake-tracker.d.ts.map +1 -0
  162. package/dist/runtime/rules.d.ts +1 -0
  163. package/dist/runtime/rules.d.ts.map +1 -1
  164. package/dist/runtime/runtime-builder.d.ts.map +1 -1
  165. package/dist/runtime/runtime-event-adapter.d.ts +102 -0
  166. package/dist/runtime/runtime-event-adapter.d.ts.map +1 -0
  167. package/dist/runtime/runtime-host.d.ts +49 -26
  168. package/dist/runtime/runtime-host.d.ts.map +1 -1
  169. package/dist/runtime/runtime-oauth-token-manager.d.ts.map +1 -1
  170. package/dist/runtime/session-runtime-orchestrator.d.ts +261 -0
  171. package/dist/runtime/session-runtime-orchestrator.d.ts.map +1 -0
  172. package/dist/runtime/session-runtime.d.ts +16 -21
  173. package/dist/runtime/session-runtime.d.ts.map +1 -1
  174. package/dist/runtime/user-input-builder.d.ts +24 -0
  175. package/dist/runtime/user-input-builder.d.ts.map +1 -0
  176. package/dist/services/global-settings.d.ts +12 -0
  177. package/dist/services/global-settings.d.ts.map +1 -0
  178. package/dist/services/index.js +28 -0
  179. package/dist/services/local-runtime-bootstrap.d.ts +9 -3
  180. package/dist/services/local-runtime-bootstrap.d.ts.map +1 -1
  181. package/dist/services/plugin-tools.d.ts +16 -0
  182. package/dist/services/plugin-tools.d.ts.map +1 -0
  183. package/dist/services/providers/local-provider-registry.d.ts +199 -23
  184. package/dist/services/providers/local-provider-registry.d.ts.map +1 -1
  185. package/dist/services/providers/local-provider-service.d.ts +15 -13
  186. package/dist/services/providers/local-provider-service.d.ts.map +1 -1
  187. package/dist/services/session-data.d.ts +1 -1
  188. package/dist/services/session-data.d.ts.map +1 -1
  189. package/dist/services/session-telemetry.d.ts +7 -2
  190. package/dist/services/session-telemetry.d.ts.map +1 -1
  191. package/dist/services/storage/file-team-store.d.ts.map +1 -1
  192. package/dist/services/storage/provider-settings-legacy-migration.d.ts +1 -1
  193. package/dist/services/storage/provider-settings-legacy-migration.d.ts.map +1 -1
  194. package/dist/services/storage/provider-settings-manager.d.ts +1 -0
  195. package/dist/services/storage/provider-settings-manager.d.ts.map +1 -1
  196. package/dist/services/storage/sqlite-team-store.d.ts.map +1 -1
  197. package/dist/services/workspace-manifest.d.ts +11 -0
  198. package/dist/services/workspace-manifest.d.ts.map +1 -1
  199. package/dist/session/conversation-store.d.ts +30 -0
  200. package/dist/session/conversation-store.d.ts.map +1 -0
  201. package/dist/session/message-builder.d.ts +65 -0
  202. package/dist/session/message-builder.d.ts.map +1 -0
  203. package/dist/session/persistence-service.d.ts +11 -23
  204. package/dist/session/persistence-service.d.ts.map +1 -1
  205. package/dist/session/session-manifest-store.d.ts +22 -0
  206. package/dist/session/session-manifest-store.d.ts.map +1 -0
  207. package/dist/session/session-manifest.d.ts +1 -1
  208. package/dist/session/session-row.d.ts +93 -0
  209. package/dist/session/session-row.d.ts.map +1 -0
  210. package/dist/session/session-service.d.ts +2 -102
  211. package/dist/session/session-service.d.ts.map +1 -1
  212. package/dist/session/subagent-session-manager.d.ts +36 -0
  213. package/dist/session/subagent-session-manager.d.ts.map +1 -0
  214. package/dist/session/team-persistence-store.d.ts +24 -0
  215. package/dist/session/team-persistence-store.d.ts.map +1 -0
  216. package/dist/transports/hub.d.ts +58 -0
  217. package/dist/transports/hub.d.ts.map +1 -0
  218. package/dist/transports/local.d.ts +23 -9
  219. package/dist/transports/local.d.ts.map +1 -1
  220. package/dist/transports/remote.d.ts +10 -0
  221. package/dist/transports/remote.d.ts.map +1 -0
  222. package/dist/transports/runtime-host-support.d.ts +3 -2
  223. package/dist/transports/runtime-host-support.d.ts.map +1 -1
  224. package/dist/types/chat-schema.d.ts +15 -17
  225. package/dist/types/chat-schema.d.ts.map +1 -1
  226. package/dist/types/config.d.ts +17 -7
  227. package/dist/types/config.d.ts.map +1 -1
  228. package/dist/types/events.d.ts +7 -6
  229. package/dist/types/events.d.ts.map +1 -1
  230. package/dist/types/provider-settings.d.ts +4 -5
  231. package/dist/types/provider-settings.d.ts.map +1 -1
  232. package/dist/types/session.d.ts +7 -3
  233. package/dist/types/session.d.ts.map +1 -1
  234. package/dist/types.d.ts +11 -4
  235. package/dist/types.d.ts.map +1 -1
  236. package/package.json +20 -6
  237. package/src/ClineCore.ts +757 -44
  238. package/src/account/cline-account-service.ts +44 -6
  239. package/src/account/index.ts +3 -3
  240. package/src/account/rpc.ts +12 -12
  241. package/src/cron/cron-event-ingress.ts +357 -0
  242. package/src/cron/cron-materializer.ts +97 -0
  243. package/src/cron/cron-reconciler.ts +241 -0
  244. package/src/cron/cron-report-writer.ts +153 -0
  245. package/src/cron/cron-runner.ts +495 -0
  246. package/src/cron/cron-schema.ts +127 -0
  247. package/src/cron/cron-service.ts +163 -0
  248. package/src/cron/cron-spec-parser.ts +489 -0
  249. package/src/cron/cron-watcher.ts +102 -0
  250. package/src/cron/index.ts +15 -0
  251. package/src/cron/resource-limiter.ts +46 -0
  252. package/src/cron/schedule-command-service.ts +193 -0
  253. package/src/cron/schedule-service.ts +703 -0
  254. package/src/cron/scheduler.ts +772 -0
  255. package/src/cron/sqlite-cron-store.ts +1286 -0
  256. package/src/cron/sqlite-schedule-store.ts +708 -0
  257. package/src/extensions/config/agent-config-loader.ts +17 -7
  258. package/src/extensions/config/runtime-commands.ts +6 -0
  259. package/src/extensions/config/user-instruction-config-loader.ts +1 -0
  260. package/src/extensions/context/agentic-compaction.ts +3 -3
  261. package/src/extensions/context/basic-compaction.ts +2 -2
  262. package/src/extensions/context/compaction-shared.ts +5 -4
  263. package/src/extensions/context/compaction.ts +3 -3
  264. package/src/extensions/plugin/plugin-config-loader.ts +37 -2
  265. package/src/extensions/plugin/plugin-loader.ts +69 -9
  266. package/src/extensions/plugin/plugin-module-import.ts +0 -2
  267. package/src/extensions/plugin/plugin-sandbox-bootstrap.ts +243 -39
  268. package/src/extensions/plugin/plugin-sandbox.ts +173 -29
  269. package/src/extensions/plugin/plugin-targeting.ts +32 -0
  270. package/src/extensions/tools/constants.ts +2 -0
  271. package/src/extensions/tools/definitions.ts +61 -71
  272. package/src/extensions/tools/executors/apply-patch.ts +69 -80
  273. package/src/extensions/tools/executors/editor.ts +4 -3
  274. package/src/extensions/tools/executors/search.ts +195 -3
  275. package/src/extensions/tools/helpers.ts +24 -0
  276. package/src/extensions/tools/index.ts +11 -2
  277. package/src/extensions/tools/presets.ts +32 -47
  278. package/src/extensions/tools/runtime.ts +261 -0
  279. package/src/extensions/tools/schemas.ts +17 -20
  280. package/src/extensions/tools/team/delegated-agent.ts +8 -3
  281. package/src/extensions/tools/team/multi-agent.ts +135 -19
  282. package/src/extensions/tools/team/team-tools.ts +172 -91
  283. package/src/extensions/tools/types.ts +0 -6
  284. package/src/hooks/hook-bridge.ts +489 -0
  285. package/src/hooks/hook-file-hooks.ts +66 -5
  286. package/src/hooks/hook-registry.ts +257 -0
  287. package/src/hooks/index.ts +0 -7
  288. package/src/hooks/subprocess-runner.ts +1 -1
  289. package/src/hooks/subprocess.ts +9 -0
  290. package/src/hub/browser-websocket.ts +159 -0
  291. package/src/hub/client.ts +633 -0
  292. package/src/hub/connect.ts +156 -0
  293. package/src/hub/daemon-entry.ts +122 -0
  294. package/src/hub/daemon.ts +284 -0
  295. package/src/hub/defaults.ts +70 -0
  296. package/src/hub/discovery.ts +247 -0
  297. package/src/hub/index.ts +14 -0
  298. package/src/hub/native-transport.ts +31 -0
  299. package/src/hub/runtime-handlers.ts +141 -0
  300. package/src/hub/server.ts +2317 -0
  301. package/src/hub/session-client.ts +502 -0
  302. package/src/hub/start-shared-server.ts +61 -0
  303. package/src/hub/transport.ts +14 -0
  304. package/src/hub/ui-client.ts +126 -0
  305. package/src/hub/workspace.ts +19 -0
  306. package/src/index.ts +169 -68
  307. package/src/llms/cline-recommended-models.ts +167 -0
  308. package/src/llms/configured-provider-registry.ts +193 -0
  309. package/src/llms/handler-factory.ts +56 -0
  310. package/src/llms/provider-defaults.ts +653 -0
  311. package/src/llms/provider-settings.ts +310 -0
  312. package/src/llms/runtime-config.ts +43 -0
  313. package/src/llms/runtime-registry.ts +172 -0
  314. package/src/llms/runtime-types.ts +121 -0
  315. package/src/runtime/agent-config-adapter.ts +636 -0
  316. package/src/runtime/agent-runtime-config-builder.ts +205 -0
  317. package/src/runtime/error-feedback.ts +142 -0
  318. package/src/runtime/history.ts +137 -0
  319. package/src/runtime/host.ts +127 -267
  320. package/src/runtime/index.ts +1 -0
  321. package/src/runtime/loop-detection.ts +162 -0
  322. package/src/runtime/mistake-tracker.ts +221 -0
  323. package/src/runtime/rules.ts +12 -0
  324. package/src/runtime/runtime-builder.ts +85 -13
  325. package/src/runtime/runtime-event-adapter.ts +412 -0
  326. package/src/runtime/runtime-host.ts +134 -62
  327. package/src/runtime/runtime-oauth-token-manager.ts +11 -15
  328. package/src/runtime/session-runtime-orchestrator.ts +1253 -0
  329. package/src/runtime/session-runtime.ts +16 -26
  330. package/src/runtime/user-input-builder.ts +167 -0
  331. package/src/services/global-settings.ts +122 -0
  332. package/src/services/local-runtime-bootstrap.ts +175 -31
  333. package/src/services/plugin-tools.ts +86 -0
  334. package/src/services/providers/local-provider-registry.ts +277 -61
  335. package/src/services/providers/local-provider-service.ts +109 -44
  336. package/src/services/session-data.ts +18 -10
  337. package/src/services/session-telemetry.ts +6 -15
  338. package/src/services/storage/file-team-store.ts +1 -5
  339. package/src/services/storage/provider-settings-legacy-migration.ts +14 -51
  340. package/src/services/storage/provider-settings-manager.ts +17 -2
  341. package/src/services/storage/sqlite-team-store.ts +1 -5
  342. package/src/services/workspace-manifest.ts +18 -0
  343. package/src/session/conversation-store.ts +77 -0
  344. package/src/session/file-session-service.ts +1 -1
  345. package/src/session/index.ts +6 -27
  346. package/src/session/message-builder.ts +941 -0
  347. package/src/session/persistence-service.ts +119 -504
  348. package/src/session/session-manifest-store.ts +158 -0
  349. package/src/session/session-row.ts +199 -0
  350. package/src/session/session-service.ts +17 -376
  351. package/src/session/session-team-coordination.ts +1 -1
  352. package/src/session/subagent-session-manager.ts +397 -0
  353. package/src/session/team-persistence-store.ts +176 -0
  354. package/src/transports/hub.ts +1081 -0
  355. package/src/transports/local.ts +419 -93
  356. package/src/transports/remote.ts +27 -0
  357. package/src/transports/runtime-host-support.ts +63 -9
  358. package/src/types/chat-schema.ts +4 -5
  359. package/src/types/config.ts +17 -7
  360. package/src/types/events.ts +8 -6
  361. package/src/types/index.ts +3 -0
  362. package/src/types/provider-settings.ts +18 -7
  363. package/src/types/session.ts +7 -6
  364. package/src/types.ts +42 -2
  365. package/dist/hooks/persistent.d.ts +0 -64
  366. package/dist/hooks/persistent.d.ts.map +0 -1
  367. package/dist/runtime/rpc-runtime-ensure.d.ts +0 -65
  368. package/dist/runtime/rpc-runtime-ensure.d.ts.map +0 -1
  369. package/dist/runtime/rpc-spawn-lease.d.ts +0 -8
  370. package/dist/runtime/rpc-spawn-lease.d.ts.map +0 -1
  371. package/dist/services/telemetry/index.js +0 -15
  372. package/dist/session/rpc-session-service.d.ts +0 -16
  373. package/dist/session/rpc-session-service.d.ts.map +0 -1
  374. package/dist/session/sqlite-rpc-session-backend.d.ts +0 -31
  375. package/dist/session/sqlite-rpc-session-backend.d.ts.map +0 -1
  376. package/dist/transports/rpc.d.ts +0 -51
  377. package/dist/transports/rpc.d.ts.map +0 -1
  378. package/src/ClineCore.test.ts +0 -226
  379. package/src/account/cline-account-service.test.ts +0 -185
  380. package/src/account/featurebase-token.test.ts +0 -175
  381. package/src/account/rpc.test.ts +0 -63
  382. package/src/auth/bounded-ttl-cache.test.ts +0 -38
  383. package/src/auth/client.test.ts +0 -69
  384. package/src/auth/cline.test.ts +0 -267
  385. package/src/auth/codex.test.ts +0 -170
  386. package/src/auth/oca.test.ts +0 -340
  387. package/src/auth/server.test.ts +0 -287
  388. package/src/auth/utils.test.ts +0 -128
  389. package/src/extensions/config/agent-config-loader.test.ts +0 -236
  390. package/src/extensions/config/hooks-config-loader.test.ts +0 -20
  391. package/src/extensions/config/runtime-commands.test.ts +0 -115
  392. package/src/extensions/config/unified-config-file-watcher.test.ts +0 -196
  393. package/src/extensions/config/user-instruction-config-loader.test.ts +0 -246
  394. package/src/extensions/context/compaction.test.ts +0 -483
  395. package/src/extensions/mcp/config-loader.test.ts +0 -238
  396. package/src/extensions/mcp/manager.test.ts +0 -105
  397. package/src/extensions/plugin/plugin-config-loader.test.ts +0 -184
  398. package/src/extensions/plugin/plugin-loader.test.ts +0 -292
  399. package/src/extensions/plugin/plugin-sandbox.test.ts +0 -423
  400. package/src/extensions/tools/definitions.test.ts +0 -780
  401. package/src/extensions/tools/executors/bash.test.ts +0 -87
  402. package/src/extensions/tools/executors/editor.test.ts +0 -35
  403. package/src/extensions/tools/executors/file-read.test.ts +0 -125
  404. package/src/extensions/tools/model-tool-routing.test.ts +0 -86
  405. package/src/extensions/tools/presets.test.ts +0 -70
  406. package/src/extensions/tools/team/multi-agent.lifecycle.test.ts +0 -455
  407. package/src/extensions/tools/team/spawn-agent-tool.test.ts +0 -381
  408. package/src/extensions/tools/team/team-tools.test.ts +0 -918
  409. package/src/hooks/checkpoint-hooks.test.ts +0 -168
  410. package/src/hooks/hook-file-hooks.test.ts +0 -311
  411. package/src/hooks/persistent.ts +0 -661
  412. package/src/runtime/history.test.ts +0 -114
  413. package/src/runtime/host.test.ts +0 -230
  414. package/src/runtime/rpc-runtime-ensure.test.ts +0 -123
  415. package/src/runtime/rpc-runtime-ensure.ts +0 -659
  416. package/src/runtime/rpc-spawn-lease.test.ts +0 -81
  417. package/src/runtime/rpc-spawn-lease.ts +0 -156
  418. package/src/runtime/runtime-builder.team-persistence.test.ts +0 -245
  419. package/src/runtime/runtime-builder.test.ts +0 -615
  420. package/src/runtime/runtime-oauth-token-manager.test.ts +0 -137
  421. package/src/runtime/runtime-parity.test.ts +0 -143
  422. package/src/services/providers/local-provider-service.test.ts +0 -1062
  423. package/src/services/session-data.test.ts +0 -160
  424. package/src/services/storage/provider-settings-legacy-migration.test.ts +0 -424
  425. package/src/services/storage/provider-settings-manager.test.ts +0 -191
  426. package/src/services/telemetry/OpenTelemetryAdapter.test.ts +0 -157
  427. package/src/services/telemetry/OpenTelemetryProvider.test.ts +0 -326
  428. package/src/services/telemetry/TelemetryLoggerSink.test.ts +0 -42
  429. package/src/services/telemetry/TelemetryService.test.ts +0 -134
  430. package/src/services/telemetry/distinct-id.test.ts +0 -57
  431. package/src/services/workspace/file-indexer.d.ts +0 -11
  432. package/src/services/workspace/file-indexer.test.ts +0 -156
  433. package/src/services/workspace/mention-enricher.test.ts +0 -106
  434. package/src/session/persistence-service.test.ts +0 -300
  435. package/src/session/rpc-session-service.ts +0 -114
  436. package/src/session/session-service.team-persistence.test.ts +0 -48
  437. package/src/session/sqlite-rpc-session-backend.ts +0 -301
  438. package/src/transports/local.e2e.test.ts +0 -380
  439. package/src/transports/local.test.ts +0 -2559
  440. package/src/transports/rpc.test.ts +0 -82
  441. package/src/transports/rpc.ts +0 -665
@@ -0,0 +1,633 @@
1
+ import {
2
+ createSessionId,
3
+ type HubClientRegistration,
4
+ type HubCommandEnvelope,
5
+ type HubEventEnvelope,
6
+ type HubReplyEnvelope,
7
+ type HubTransportFrame,
8
+ } from "@clinebot/shared";
9
+ import { spawnDetachedHubServer } from "./daemon";
10
+ import {
11
+ clearHubDiscovery,
12
+ type HubOwnerContext,
13
+ probeHubServer,
14
+ readHubDiscovery,
15
+ resolveHubBuildId,
16
+ } from "./discovery";
17
+ import { resolveSharedHubOwnerContext } from "./workspace";
18
+
19
+ type PendingReply = {
20
+ resolve: (reply: HubReplyEnvelope) => void;
21
+ reject: (error: unknown) => void;
22
+ };
23
+
24
+ type SubscriptionEntry = {
25
+ listener: (event: HubEventEnvelope) => void;
26
+ sessionId?: string;
27
+ };
28
+
29
+ type WebSocketLike = {
30
+ readyState: number;
31
+ send(data: string): void;
32
+ close(): void;
33
+ addEventListener(type: string, listener: (...args: unknown[]) => void): void;
34
+ };
35
+
36
+ type WebSocketCtor = new (
37
+ url: string,
38
+ protocols?: string | string[],
39
+ ) => WebSocketLike;
40
+
41
+ function getWebSocketCtor(): WebSocketCtor {
42
+ const ctor = (globalThis as { WebSocket?: WebSocketCtor }).WebSocket;
43
+ if (!ctor) {
44
+ throw new Error(
45
+ "Global WebSocket is not available in this runtime. Node 22+ is required for hub mode.",
46
+ );
47
+ }
48
+ return ctor;
49
+ }
50
+
51
+ function decodeSocketData(data: unknown): string {
52
+ if (typeof data === "string") {
53
+ return data;
54
+ }
55
+ if (data instanceof Uint8Array) {
56
+ return Buffer.from(data).toString();
57
+ }
58
+ if (data instanceof ArrayBuffer) {
59
+ return Buffer.from(data).toString();
60
+ }
61
+ if (Array.isArray(data)) {
62
+ return Buffer.concat(data.map((chunk) => Buffer.from(chunk))).toString();
63
+ }
64
+ if (
65
+ data &&
66
+ typeof data === "object" &&
67
+ "data" in data &&
68
+ typeof (data as { data?: unknown }).data !== "undefined"
69
+ ) {
70
+ return decodeSocketData((data as { data?: unknown }).data);
71
+ }
72
+ return String(data);
73
+ }
74
+
75
+ function decodeCloseReason(reason: unknown): string {
76
+ if (typeof reason === "string") {
77
+ return reason;
78
+ }
79
+ if (reason instanceof Uint8Array) {
80
+ return Buffer.from(reason).toString("utf8");
81
+ }
82
+ if (reason instanceof ArrayBuffer) {
83
+ return Buffer.from(reason).toString("utf8");
84
+ }
85
+ return "";
86
+ }
87
+
88
+ function normalizeWebSocketConnectError(error: unknown, url: URL): Error {
89
+ if (error instanceof Error) {
90
+ return error;
91
+ }
92
+ if (
93
+ error &&
94
+ typeof error === "object" &&
95
+ "error" in error &&
96
+ (error as { error?: unknown }).error instanceof Error
97
+ ) {
98
+ return (error as { error: Error }).error;
99
+ }
100
+ const message =
101
+ error &&
102
+ typeof error === "object" &&
103
+ "message" in error &&
104
+ typeof (error as { message?: unknown }).message === "string"
105
+ ? (error as { message: string }).message.trim()
106
+ : "";
107
+ if (message) {
108
+ return new Error(message);
109
+ }
110
+ const eventType =
111
+ error &&
112
+ typeof error === "object" &&
113
+ "type" in error &&
114
+ typeof (error as { type?: unknown }).type === "string"
115
+ ? (error as { type: string }).type.trim()
116
+ : "";
117
+ return new Error(
118
+ eventType
119
+ ? `Failed to connect to hub at ${url.toString()} (${eventType} event before socket open).`
120
+ : `Failed to connect to hub at ${url.toString()}.`,
121
+ );
122
+ }
123
+
124
+ export interface HubClientOptions {
125
+ url: string;
126
+ clientId?: string;
127
+ clientType?: string;
128
+ displayName?: string;
129
+ workspaceRoot?: string;
130
+ cwd?: string;
131
+ authToken?: string;
132
+ }
133
+
134
+ export interface LocalHubResolutionOptions {
135
+ endpoint?: string;
136
+ strategy?: "prefer-hub" | "require-hub";
137
+ workspaceRoot?: string;
138
+ cwd?: string;
139
+ }
140
+
141
+ const HUB_STARTUP_TIMEOUT_MS = 8_000;
142
+ const HUB_STARTUP_POLL_MS = 200;
143
+ const GLOBAL_SUBSCRIPTION_KEY = "*";
144
+ const HUB_CONNECT_TIMEOUT_MS = 8_000;
145
+ const HUB_COMMAND_TIMEOUT_MS = 30_000;
146
+
147
+ export class NodeHubClient {
148
+ private socket: WebSocketLike | undefined;
149
+ private connectPromise: Promise<void> | undefined;
150
+ private readonly clientId: string;
151
+ private readonly pendingReplies = new Map<string, PendingReply>();
152
+ private readonly listeners = new Set<SubscriptionEntry>();
153
+ private readonly subscriptionCounts = new Map<string, number>();
154
+ private lastCloseMessage = "Hub connection closed";
155
+ private registered = false;
156
+
157
+ constructor(private readonly options: HubClientOptions) {
158
+ this.clientId =
159
+ options.clientId ??
160
+ `core-${Math.random().toString(36).slice(2, 10)}-${Date.now().toString(36)}`;
161
+ }
162
+
163
+ getClientId(): string {
164
+ return this.clientId;
165
+ }
166
+
167
+ async connect(): Promise<void> {
168
+ if (
169
+ this.socket &&
170
+ (this.socket.readyState === 1 || this.socket.readyState === 0)
171
+ ) {
172
+ return this.connectPromise ?? Promise.resolve();
173
+ }
174
+
175
+ const url = new URL(this.options.url);
176
+ if (this.options.authToken?.trim()) {
177
+ url.searchParams.set("authToken", this.options.authToken.trim());
178
+ }
179
+
180
+ const WebSocketImpl = getWebSocketCtor();
181
+ const socket = new WebSocketImpl(url.toString());
182
+ this.socket = socket;
183
+ let suppressCloseMessage = false;
184
+ this.connectPromise = new Promise<void>((resolve, reject) => {
185
+ let settled = false;
186
+ const timeout = setTimeout(() => {
187
+ if (settled) {
188
+ return;
189
+ }
190
+ settled = true;
191
+ suppressCloseMessage = true;
192
+ this.lastCloseMessage = `Timed out connecting to hub after ${HUB_CONNECT_TIMEOUT_MS}ms`;
193
+ this.connectPromise = undefined;
194
+ this.socket = undefined;
195
+ try {
196
+ socket.close();
197
+ } catch {
198
+ // best-effort close
199
+ }
200
+ reject(new Error(this.lastCloseMessage));
201
+ }, HUB_CONNECT_TIMEOUT_MS);
202
+ socket.addEventListener("open", () => {
203
+ if (settled) {
204
+ return;
205
+ }
206
+ settled = true;
207
+ clearTimeout(timeout);
208
+ resolve();
209
+ });
210
+ socket.addEventListener("error", (error) => {
211
+ if (settled) {
212
+ return;
213
+ }
214
+ settled = true;
215
+ clearTimeout(timeout);
216
+ this.connectPromise = undefined;
217
+ this.socket = undefined;
218
+ reject(normalizeWebSocketConnectError(error, url));
219
+ });
220
+ });
221
+
222
+ socket.addEventListener("message", (data: unknown) => {
223
+ this.handleFrame(JSON.parse(decodeSocketData(data)) as HubTransportFrame);
224
+ });
225
+ socket.addEventListener("close", (event: unknown) => {
226
+ if (this.socket !== socket && this.connectPromise === undefined) {
227
+ return;
228
+ }
229
+ const closeEvent = event as { code?: number; reason?: unknown };
230
+ const reasonText = decodeCloseReason(closeEvent.reason);
231
+ if (!suppressCloseMessage) {
232
+ this.lastCloseMessage =
233
+ closeEvent.code || reasonText
234
+ ? `Hub connection closed (code=${closeEvent.code ?? 0}${reasonText ? `, reason=${reasonText}` : ""})`
235
+ : "Hub connection closed";
236
+ }
237
+ for (const pending of this.pendingReplies.values()) {
238
+ pending.reject(new Error(this.lastCloseMessage));
239
+ }
240
+ this.pendingReplies.clear();
241
+ this.connectPromise = undefined;
242
+ this.socket = undefined;
243
+ });
244
+
245
+ await this.connectPromise;
246
+ await this.command("client.register", {
247
+ clientId: this.clientId,
248
+ clientType: this.options.clientType ?? "core",
249
+ displayName: this.options.displayName ?? "core",
250
+ transport: "native",
251
+ actorKind: "client",
252
+ workspaceContext: {
253
+ workspaceRoot: this.options.workspaceRoot,
254
+ cwd: this.options.cwd,
255
+ },
256
+ } satisfies HubClientRegistration);
257
+ this.registered = true;
258
+ for (const key of this.subscriptionCounts.keys()) {
259
+ this.sendSubscriptionFrame(
260
+ "stream.subscribe",
261
+ this.subscriptionSessionIdFromKey(key),
262
+ );
263
+ }
264
+ }
265
+
266
+ subscribe(
267
+ listener: (event: HubEventEnvelope) => void,
268
+ options?: { sessionId?: string },
269
+ ): () => void {
270
+ const sessionId = options?.sessionId?.trim() || undefined;
271
+ const entry = { listener, sessionId };
272
+ this.listeners.add(entry);
273
+ this.adjustSubscriptionCount(sessionId, 1);
274
+ return () => {
275
+ if (!this.listeners.delete(entry)) {
276
+ return;
277
+ }
278
+ this.adjustSubscriptionCount(sessionId, -1);
279
+ };
280
+ }
281
+
282
+ async command(
283
+ command: HubCommandEnvelope["command"],
284
+ payload?: Record<string, unknown>,
285
+ sessionId?: string,
286
+ options?: { timeoutMs?: number | null },
287
+ ): Promise<HubReplyEnvelope> {
288
+ await this.connect();
289
+ const requestId = createSessionId("hubreq_");
290
+ const reply = new Promise<HubReplyEnvelope>((resolve, reject) => {
291
+ const timeoutMs = options?.timeoutMs;
292
+ const timeout =
293
+ timeoutMs === null
294
+ ? undefined
295
+ : setTimeout(() => {
296
+ if (!this.pendingReplies.delete(requestId)) {
297
+ return;
298
+ }
299
+ reject(
300
+ new Error(
301
+ `Hub command ${command} timed out after ${timeoutMs ?? HUB_COMMAND_TIMEOUT_MS}ms`,
302
+ ),
303
+ );
304
+ }, timeoutMs ?? HUB_COMMAND_TIMEOUT_MS);
305
+ this.pendingReplies.set(requestId, {
306
+ resolve: (value) => {
307
+ if (timeout) {
308
+ clearTimeout(timeout);
309
+ }
310
+ resolve(value);
311
+ },
312
+ reject: (error) => {
313
+ if (timeout) {
314
+ clearTimeout(timeout);
315
+ }
316
+ reject(error);
317
+ },
318
+ });
319
+ });
320
+ this.sendFrame({
321
+ kind: "command",
322
+ envelope: {
323
+ version: "v1",
324
+ command,
325
+ requestId,
326
+ clientId: this.clientId,
327
+ sessionId,
328
+ payload,
329
+ },
330
+ });
331
+ const resolved = await reply;
332
+ if (!resolved.ok) {
333
+ throw new Error(
334
+ resolved.error?.message ?? `Hub command ${command} failed`,
335
+ );
336
+ }
337
+ return resolved;
338
+ }
339
+
340
+ close(): void {
341
+ const socket = this.socket;
342
+ this.registered = false;
343
+ if (!socket) {
344
+ return;
345
+ }
346
+ this.lastCloseMessage = "Hub connection closed";
347
+ for (const pending of this.pendingReplies.values()) {
348
+ pending.reject(new Error(this.lastCloseMessage));
349
+ }
350
+ this.pendingReplies.clear();
351
+ this.connectPromise = undefined;
352
+ this.socket = undefined;
353
+ try {
354
+ socket.close();
355
+ } catch {
356
+ // best-effort close
357
+ }
358
+ }
359
+
360
+ async dispose(): Promise<void> {
361
+ const socket = this.socket;
362
+ if (socket?.readyState === 1 && this.registered) {
363
+ try {
364
+ await this.command("client.unregister", undefined, undefined, {
365
+ timeoutMs: 2_000,
366
+ });
367
+ } catch {
368
+ // Best-effort unregister during shutdown. The websocket adapter also
369
+ // unregisters clients on close, so failure here should not block teardown.
370
+ }
371
+ }
372
+ this.close();
373
+ }
374
+
375
+ private sendFrame(frame: HubTransportFrame): void {
376
+ if (!this.socket || this.socket.readyState !== 1) {
377
+ throw new Error(
378
+ this.lastCloseMessage === "Hub connection closed"
379
+ ? "Hub connection is not open."
380
+ : this.lastCloseMessage,
381
+ );
382
+ }
383
+ this.socket.send(JSON.stringify(frame));
384
+ }
385
+
386
+ private sendSubscriptionFrame(
387
+ kind: "stream.subscribe" | "stream.unsubscribe",
388
+ sessionId?: string,
389
+ ): void {
390
+ this.sendFrame({
391
+ kind,
392
+ clientId: this.clientId,
393
+ ...(sessionId ? { sessionId } : {}),
394
+ });
395
+ }
396
+
397
+ private adjustSubscriptionCount(
398
+ sessionId: string | undefined,
399
+ delta: 1 | -1,
400
+ ): void {
401
+ const key = this.subscriptionKeyForSessionId(sessionId);
402
+ const next = (this.subscriptionCounts.get(key) ?? 0) + delta;
403
+ if (next <= 0) {
404
+ this.subscriptionCounts.delete(key);
405
+ if (delta < 0 && this.socket?.readyState === 1) {
406
+ this.sendSubscriptionFrame("stream.unsubscribe", sessionId);
407
+ }
408
+ return;
409
+ }
410
+ this.subscriptionCounts.set(key, next);
411
+ if (delta > 0 && next === 1 && this.socket?.readyState === 1) {
412
+ this.sendSubscriptionFrame("stream.subscribe", sessionId);
413
+ }
414
+ }
415
+
416
+ private subscriptionKeyForSessionId(sessionId: string | undefined): string {
417
+ return sessionId ?? GLOBAL_SUBSCRIPTION_KEY;
418
+ }
419
+
420
+ private subscriptionSessionIdFromKey(key: string): string | undefined {
421
+ return key === GLOBAL_SUBSCRIPTION_KEY ? undefined : key;
422
+ }
423
+
424
+ private handleFrame(frame: HubTransportFrame): void {
425
+ switch (frame.kind) {
426
+ case "reply": {
427
+ const requestId = frame.envelope.requestId;
428
+ if (!requestId) {
429
+ return;
430
+ }
431
+ const pending = this.pendingReplies.get(requestId);
432
+ if (!pending) {
433
+ return;
434
+ }
435
+ this.pendingReplies.delete(requestId);
436
+ pending.resolve(frame.envelope);
437
+ return;
438
+ }
439
+ case "event":
440
+ for (const entry of this.listeners) {
441
+ if (
442
+ entry.sessionId &&
443
+ entry.sessionId !== frame.envelope.sessionId?.trim()
444
+ ) {
445
+ continue;
446
+ }
447
+ entry.listener(frame.envelope);
448
+ }
449
+ return;
450
+ case "command":
451
+ case "stream.subscribe":
452
+ case "stream.unsubscribe":
453
+ return;
454
+ }
455
+ }
456
+ }
457
+
458
+ export function normalizeHubWebSocketUrl(url: string): string {
459
+ const parsed = new URL(url);
460
+ if (parsed.protocol === "http:") {
461
+ parsed.protocol = "ws:";
462
+ } else if (parsed.protocol === "https:") {
463
+ parsed.protocol = "wss:";
464
+ }
465
+ return parsed.toString();
466
+ }
467
+
468
+ export async function verifyHubConnection(
469
+ url: string,
470
+ options?: Pick<HubClientOptions, "workspaceRoot" | "cwd">,
471
+ ): Promise<boolean> {
472
+ const client = new NodeHubClient({
473
+ url,
474
+ clientType: "hub-healthcheck",
475
+ displayName: "hub healthcheck",
476
+ workspaceRoot: options?.workspaceRoot,
477
+ cwd: options?.cwd,
478
+ });
479
+ try {
480
+ await client.connect();
481
+ return true;
482
+ } catch {
483
+ return false;
484
+ } finally {
485
+ client.close();
486
+ }
487
+ }
488
+
489
+ type HubProbeResult =
490
+ | {
491
+ status: "compatible";
492
+ url: string;
493
+ }
494
+ | {
495
+ status: "unreachable" | "build_mismatch";
496
+ url: string;
497
+ };
498
+
499
+ async function probeCompatibleHubUrl(
500
+ url: string,
501
+ options?: {
502
+ verifyConnection?: boolean;
503
+ workspaceRoot?: string;
504
+ cwd?: string;
505
+ },
506
+ ): Promise<HubProbeResult> {
507
+ const normalized = normalizeHubWebSocketUrl(url);
508
+ const record = await probeHubServer(normalized);
509
+ if (!record) {
510
+ return {
511
+ status: "unreachable",
512
+ url: normalized,
513
+ };
514
+ }
515
+ const buildId = resolveHubBuildId();
516
+ const recordBuildId = record.buildId?.trim();
517
+ if (!recordBuildId || recordBuildId !== buildId) {
518
+ return {
519
+ status: "build_mismatch",
520
+ url: normalized,
521
+ };
522
+ }
523
+ if (
524
+ options?.verifyConnection === true &&
525
+ !(await verifyHubConnection(normalized, {
526
+ workspaceRoot: options.workspaceRoot,
527
+ cwd: options.cwd,
528
+ }))
529
+ ) {
530
+ return {
531
+ status: "unreachable",
532
+ url: normalized,
533
+ };
534
+ }
535
+ return {
536
+ status: "compatible",
537
+ url: normalized,
538
+ };
539
+ }
540
+
541
+ async function waitForCompatibleHubUrl(
542
+ owner: HubOwnerContext,
543
+ ): Promise<string | undefined> {
544
+ const deadline = Date.now() + HUB_STARTUP_TIMEOUT_MS;
545
+ while (Date.now() < deadline) {
546
+ const record = await readHubDiscovery(owner.discoveryPath);
547
+ if (record?.url) {
548
+ const compatible = await probeCompatibleHubUrl(record.url, {
549
+ verifyConnection: true,
550
+ });
551
+ if (compatible.status === "compatible") {
552
+ return compatible.url;
553
+ }
554
+ }
555
+ await new Promise((resolve) => setTimeout(resolve, HUB_STARTUP_POLL_MS));
556
+ }
557
+ return undefined;
558
+ }
559
+
560
+ export async function resolveCompatibleLocalHubUrl(
561
+ options: LocalHubResolutionOptions = {},
562
+ ): Promise<string | undefined> {
563
+ if (options.endpoint?.trim()) {
564
+ const compatible = await probeCompatibleHubUrl(options.endpoint);
565
+ return compatible.status === "compatible" ? compatible.url : undefined;
566
+ }
567
+
568
+ const owner = resolveSharedHubOwnerContext();
569
+ const record = await readHubDiscovery(owner.discoveryPath);
570
+ if (!record?.url) {
571
+ return undefined;
572
+ }
573
+ const compatible = await probeCompatibleHubUrl(record.url);
574
+ if (compatible.status === "compatible") {
575
+ return compatible.url;
576
+ }
577
+ if (compatible.status === "build_mismatch") {
578
+ await clearHubDiscovery(owner.discoveryPath).catch(() => undefined);
579
+ }
580
+ return undefined;
581
+ }
582
+
583
+ export async function ensureCompatibleLocalHubUrl(
584
+ options: LocalHubResolutionOptions = {},
585
+ ): Promise<string | undefined> {
586
+ const resolved = await resolveCompatibleLocalHubUrl(options);
587
+ if (
588
+ resolved &&
589
+ (await verifyHubConnection(resolved, {
590
+ workspaceRoot: options.workspaceRoot,
591
+ cwd: options.cwd,
592
+ }))
593
+ ) {
594
+ return resolved;
595
+ }
596
+ if (options.endpoint?.trim()) {
597
+ return undefined;
598
+ }
599
+ const owner = resolveSharedHubOwnerContext();
600
+ spawnDetachedHubServer(options.workspaceRoot ?? process.cwd());
601
+ return await waitForCompatibleHubUrl(owner);
602
+ }
603
+
604
+ export async function requestHubShutdown(url: string): Promise<boolean> {
605
+ const parsed = new URL(url);
606
+ if (parsed.protocol === "ws:") {
607
+ parsed.protocol = "http:";
608
+ } else if (parsed.protocol === "wss:") {
609
+ parsed.protocol = "https:";
610
+ }
611
+ parsed.pathname = "/shutdown";
612
+ parsed.search = "";
613
+ parsed.hash = "";
614
+ const response = await fetch(parsed, { method: "POST" });
615
+ return response.ok;
616
+ }
617
+
618
+ export async function stopLocalHubServerGracefully(): Promise<boolean> {
619
+ const owner = resolveSharedHubOwnerContext();
620
+ const discovery = await readHubDiscovery(owner.discoveryPath);
621
+ if (!discovery?.url) {
622
+ return false;
623
+ }
624
+ try {
625
+ const stopped = await requestHubShutdown(discovery.url);
626
+ if (stopped) {
627
+ return true;
628
+ }
629
+ } catch {
630
+ // Fall through so callers can apply a stronger fallback.
631
+ }
632
+ return false;
633
+ }