@clinebot/core 0.0.34 → 0.0.36

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