@cloudcli-ai/cloudcli 1.36.3 → 1.37.1

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 (569) hide show
  1. package/dist/assets/index-D3jfzuKN.css +32 -0
  2. package/dist/assets/index-DTMWjJj8.js +1550 -0
  3. package/dist/index.html +2 -2
  4. package/dist-server/server/constants/config.js.map +1 -1
  5. package/dist-server/server/index.js +73 -1224
  6. package/dist-server/server/index.js.map +1 -1
  7. package/dist-server/server/load-env.js +17 -4
  8. package/dist-server/server/load-env.js.map +1 -1
  9. package/dist-server/server/modules/agent/agent.module.js +37 -0
  10. package/dist-server/server/modules/agent/agent.module.js.map +1 -0
  11. package/dist-server/server/modules/agent/agent.routes.js +1161 -0
  12. package/dist-server/server/modules/agent/agent.routes.js.map +1 -0
  13. package/dist-server/server/modules/agent/index.js +3 -0
  14. package/dist-server/server/modules/agent/index.js.map +1 -0
  15. package/dist-server/server/modules/agent/tests/agent.routes.test.js +176 -0
  16. package/dist-server/server/modules/agent/tests/agent.routes.test.js.map +1 -0
  17. package/dist-server/server/modules/assets/assets.routes.js +56 -15
  18. package/dist-server/server/modules/assets/assets.routes.js.map +1 -1
  19. package/dist-server/server/modules/assets/index.js +2 -2
  20. package/dist-server/server/modules/assets/index.js.map +1 -1
  21. package/dist-server/server/modules/assets/services/image-assets.service.js +39 -1
  22. package/dist-server/server/modules/assets/services/image-assets.service.js.map +1 -1
  23. package/dist-server/server/modules/assets/tests/image-assets.service.test.js +21 -1
  24. package/dist-server/server/modules/assets/tests/image-assets.service.test.js.map +1 -1
  25. package/dist-server/server/{middleware/auth.js → modules/auth/auth.middleware.js} +30 -8
  26. package/dist-server/server/modules/auth/auth.middleware.js.map +1 -0
  27. package/dist-server/server/modules/auth/auth.module.js +29 -0
  28. package/dist-server/server/modules/auth/auth.module.js.map +1 -0
  29. package/dist-server/server/modules/auth/auth.routes.js +45 -0
  30. package/dist-server/server/modules/auth/auth.routes.js.map +1 -0
  31. package/dist-server/server/modules/auth/auth.service.js +113 -0
  32. package/dist-server/server/modules/auth/auth.service.js.map +1 -0
  33. package/dist-server/server/modules/auth/index.js +9 -0
  34. package/dist-server/server/modules/auth/index.js.map +1 -0
  35. package/dist-server/server/modules/auth/tests/auth.service.test.js +80 -0
  36. package/dist-server/server/modules/auth/tests/auth.service.test.js.map +1 -0
  37. package/dist-server/server/{browser-use-mcp.js → modules/browser-use/browser-use-mcp.js} +3 -1
  38. package/dist-server/server/modules/browser-use/browser-use-mcp.js.map +1 -0
  39. package/dist-server/server/modules/browser-use/browser-use.service.js +3 -4
  40. package/dist-server/server/modules/browser-use/browser-use.service.js.map +1 -1
  41. package/dist-server/server/modules/browser-use/index.js +9 -0
  42. package/dist-server/server/modules/browser-use/index.js.map +1 -0
  43. package/dist-server/server/modules/cli/cli.js +11 -0
  44. package/dist-server/server/modules/cli/cli.js.map +1 -0
  45. package/dist-server/server/modules/cli/cli.module.js +86 -0
  46. package/dist-server/server/modules/cli/cli.module.js.map +1 -0
  47. package/dist-server/server/modules/cli/cli.service.js +244 -0
  48. package/dist-server/server/modules/cli/cli.service.js.map +1 -0
  49. package/dist-server/server/modules/cli/index.js +3 -0
  50. package/dist-server/server/modules/cli/index.js.map +1 -0
  51. package/dist-server/server/modules/cli/sandbox.service.js +332 -0
  52. package/dist-server/server/modules/cli/sandbox.service.js.map +1 -0
  53. package/dist-server/server/modules/cli/tests/cli.service.test.js +78 -0
  54. package/dist-server/server/modules/cli/tests/cli.service.test.js.map +1 -0
  55. package/dist-server/server/modules/cli/tests/sandbox.service.test.js +55 -0
  56. package/dist-server/server/modules/cli/tests/sandbox.service.test.js.map +1 -0
  57. package/dist-server/server/modules/commands/commands.module.js +20 -0
  58. package/dist-server/server/modules/commands/commands.module.js.map +1 -0
  59. package/dist-server/server/modules/commands/commands.routes.js +497 -0
  60. package/dist-server/server/modules/commands/commands.routes.js.map +1 -0
  61. package/dist-server/server/modules/commands/index.js +3 -0
  62. package/dist-server/server/modules/commands/index.js.map +1 -0
  63. package/dist-server/server/modules/commands/tests/commands.test.js +91 -0
  64. package/dist-server/server/modules/commands/tests/commands.test.js.map +1 -0
  65. package/dist-server/server/modules/database/index.js +3 -0
  66. package/dist-server/server/modules/database/index.js.map +1 -1
  67. package/dist-server/server/modules/database/migrations.js +31 -1
  68. package/dist-server/server/modules/database/migrations.js.map +1 -1
  69. package/dist-server/server/modules/database/repositories/provider-models.js +112 -0
  70. package/dist-server/server/modules/database/repositories/provider-models.js.map +1 -0
  71. package/dist-server/server/modules/database/repositories/sessions.db.js +76 -7
  72. package/dist-server/server/modules/database/repositories/sessions.db.js.map +1 -1
  73. package/dist-server/server/modules/database/schema.js +29 -0
  74. package/dist-server/server/modules/database/schema.js.map +1 -1
  75. package/dist-server/server/modules/database/tests/provider-models.db.integration.test.js +102 -0
  76. package/dist-server/server/modules/database/tests/provider-models.db.integration.test.js.map +1 -0
  77. package/dist-server/server/modules/database/tests/sessions-provider-mapping.test.js +2 -1
  78. package/dist-server/server/modules/database/tests/sessions-provider-mapping.test.js.map +1 -1
  79. package/dist-server/server/modules/database/tests/sessions.db.integration.test.js +22 -0
  80. package/dist-server/server/modules/database/tests/sessions.db.integration.test.js.map +1 -1
  81. package/dist-server/server/modules/file-tree/file-tree.module.js +95 -0
  82. package/dist-server/server/modules/file-tree/file-tree.module.js.map +1 -0
  83. package/dist-server/server/modules/file-tree/file-tree.routes.js +208 -0
  84. package/dist-server/server/modules/file-tree/file-tree.routes.js.map +1 -0
  85. package/dist-server/server/modules/file-tree/file-tree.service.js +576 -0
  86. package/dist-server/server/modules/file-tree/file-tree.service.js.map +1 -0
  87. package/dist-server/server/modules/file-tree/index.js +3 -0
  88. package/dist-server/server/modules/file-tree/index.js.map +1 -0
  89. package/dist-server/server/modules/file-tree/tests/file-tree.routes.test.js +123 -0
  90. package/dist-server/server/modules/file-tree/tests/file-tree.routes.test.js.map +1 -0
  91. package/dist-server/server/modules/file-tree/tests/file-tree.service.test.js +260 -0
  92. package/dist-server/server/modules/file-tree/tests/file-tree.service.test.js.map +1 -0
  93. package/dist-server/server/modules/git/git-parsing.service.js +61 -0
  94. package/dist-server/server/modules/git/git-parsing.service.js.map +1 -0
  95. package/dist-server/server/modules/git/git.module.js +14 -0
  96. package/dist-server/server/modules/git/git.module.js.map +1 -0
  97. package/dist-server/server/modules/git/git.routes.js +1339 -0
  98. package/dist-server/server/modules/git/git.routes.js.map +1 -0
  99. package/dist-server/server/modules/git/index.js +3 -0
  100. package/dist-server/server/modules/git/index.js.map +1 -0
  101. package/dist-server/server/modules/git/tests/git-init.routes.test.js +48 -0
  102. package/dist-server/server/modules/git/tests/git-init.routes.test.js.map +1 -0
  103. package/dist-server/server/{routes → modules/git/tests}/git.test.js +1 -1
  104. package/dist-server/server/modules/git/tests/git.test.js.map +1 -0
  105. package/dist-server/server/modules/notifications/index.js +15 -1
  106. package/dist-server/server/modules/notifications/index.js.map +1 -1
  107. package/dist-server/server/modules/notifications/tests/notification-orchestrator.integration.test.js +44 -0
  108. package/dist-server/server/modules/notifications/tests/notification-orchestrator.integration.test.js.map +1 -0
  109. package/dist-server/server/{services/vapid-keys.js → modules/notifications/vapid-keys.service.js} +3 -2
  110. package/dist-server/server/modules/notifications/vapid-keys.service.js.map +1 -0
  111. package/dist-server/server/modules/plugins/index.js +9 -0
  112. package/dist-server/server/modules/plugins/index.js.map +1 -0
  113. package/dist-server/server/{utils/plugin-process-manager.js → modules/plugins/plugin-process.service.js} +3 -2
  114. package/dist-server/server/modules/plugins/plugin-process.service.js.map +1 -0
  115. package/dist-server/server/{utils/plugin-loader.js → modules/plugins/plugin-registry.service.js} +2 -1
  116. package/dist-server/server/modules/plugins/plugin-registry.service.js.map +1 -0
  117. package/dist-server/server/modules/plugins/plugins.module.js +30 -0
  118. package/dist-server/server/modules/plugins/plugins.module.js.map +1 -0
  119. package/dist-server/server/modules/plugins/plugins.routes.js +70 -0
  120. package/dist-server/server/modules/plugins/plugins.routes.js.map +1 -0
  121. package/dist-server/server/modules/plugins/plugins.service.js +123 -0
  122. package/dist-server/server/modules/plugins/plugins.service.js.map +1 -0
  123. package/dist-server/server/modules/plugins/tests/plugins.service.test.js +28 -0
  124. package/dist-server/server/modules/plugins/tests/plugins.service.test.js.map +1 -0
  125. package/dist-server/server/modules/projects/index.js +5 -0
  126. package/dist-server/server/modules/projects/index.js.map +1 -1
  127. package/dist-server/server/modules/providers/index.js +3 -0
  128. package/dist-server/server/modules/providers/index.js.map +1 -1
  129. package/dist-server/server/modules/providers/list/claude/claude-auth.provider.js +6 -0
  130. package/dist-server/server/modules/providers/list/claude/claude-auth.provider.js.map +1 -1
  131. package/dist-server/server/modules/providers/list/claude/claude-models.provider.js +47 -18
  132. package/dist-server/server/modules/providers/list/claude/claude-models.provider.js.map +1 -1
  133. package/dist-server/server/{claude-sdk.js → modules/providers/list/claude/claude-runtime.provider.js} +69 -55
  134. package/dist-server/server/modules/providers/list/claude/claude-runtime.provider.js.map +1 -0
  135. package/dist-server/server/modules/providers/list/claude/claude-session-synchronizer.provider.js +6 -4
  136. package/dist-server/server/modules/providers/list/claude/claude-session-synchronizer.provider.js.map +1 -1
  137. package/dist-server/server/modules/providers/list/claude/claude-sessions.provider.js +23 -4
  138. package/dist-server/server/modules/providers/list/claude/claude-sessions.provider.js.map +1 -1
  139. package/dist-server/server/modules/providers/list/claude/claude.provider.js +2 -0
  140. package/dist-server/server/modules/providers/list/claude/claude.provider.js.map +1 -1
  141. package/dist-server/server/modules/providers/list/codex/codex-models.provider.js +59 -75
  142. package/dist-server/server/modules/providers/list/codex/codex-models.provider.js.map +1 -1
  143. package/dist-server/server/{openai-codex.js → modules/providers/list/codex/codex-runtime.provider.js} +48 -34
  144. package/dist-server/server/modules/providers/list/codex/codex-runtime.provider.js.map +1 -0
  145. package/dist-server/server/modules/providers/list/codex/codex-session-synchronizer.provider.js +1 -54
  146. package/dist-server/server/modules/providers/list/codex/codex-session-synchronizer.provider.js.map +1 -1
  147. package/dist-server/server/modules/providers/list/codex/codex-sessions.provider.js +290 -6
  148. package/dist-server/server/modules/providers/list/codex/codex-sessions.provider.js.map +1 -1
  149. package/dist-server/server/modules/providers/list/codex/codex-skills.provider.js +5 -0
  150. package/dist-server/server/modules/providers/list/codex/codex-skills.provider.js.map +1 -1
  151. package/dist-server/server/modules/providers/list/codex/codex.provider.js +2 -0
  152. package/dist-server/server/modules/providers/list/codex/codex.provider.js.map +1 -1
  153. package/dist-server/server/modules/providers/list/cursor/cursor-models.provider.js +33 -673
  154. package/dist-server/server/modules/providers/list/cursor/cursor-models.provider.js.map +1 -1
  155. package/dist-server/server/{cursor-cli.js → modules/providers/list/cursor/cursor-runtime.provider.js} +40 -28
  156. package/dist-server/server/modules/providers/list/cursor/cursor-runtime.provider.js.map +1 -0
  157. package/dist-server/server/modules/providers/list/cursor/cursor-sessions.provider.js +21 -14
  158. package/dist-server/server/modules/providers/list/cursor/cursor-sessions.provider.js.map +1 -1
  159. package/dist-server/server/modules/providers/list/cursor/cursor.provider.js +2 -0
  160. package/dist-server/server/modules/providers/list/cursor/cursor.provider.js.map +1 -1
  161. package/dist-server/server/modules/providers/list/opencode/opencode-models.provider.js +112 -333
  162. package/dist-server/server/modules/providers/list/opencode/opencode-models.provider.js.map +1 -1
  163. package/dist-server/server/{opencode-cli.js → modules/providers/list/opencode/opencode-runtime.provider.js} +38 -25
  164. package/dist-server/server/modules/providers/list/opencode/opencode-runtime.provider.js.map +1 -0
  165. package/dist-server/server/{opencode-cli.test.js → modules/providers/list/opencode/opencode-runtime.provider.test.js} +27 -4
  166. package/dist-server/server/modules/providers/list/opencode/opencode-runtime.provider.test.js.map +1 -0
  167. package/dist-server/server/modules/providers/list/opencode/opencode-session-synchronizer.provider.js +0 -12
  168. package/dist-server/server/modules/providers/list/opencode/opencode-session-synchronizer.provider.js.map +1 -1
  169. package/dist-server/server/modules/providers/list/opencode/opencode-sessions.provider.js +11 -5
  170. package/dist-server/server/modules/providers/list/opencode/opencode-sessions.provider.js.map +1 -1
  171. package/dist-server/server/modules/providers/list/opencode/opencode.provider.js +2 -0
  172. package/dist-server/server/modules/providers/list/opencode/opencode.provider.js.map +1 -1
  173. package/dist-server/server/modules/providers/provider.routes.js +169 -37
  174. package/dist-server/server/modules/providers/provider.routes.js.map +1 -1
  175. package/dist-server/server/modules/providers/services/provider-capabilities.service.js +5 -1
  176. package/dist-server/server/modules/providers/services/provider-capabilities.service.js.map +1 -1
  177. package/dist-server/server/modules/providers/services/provider-models.service.js +247 -184
  178. package/dist-server/server/modules/providers/services/provider-models.service.js.map +1 -1
  179. package/dist-server/server/modules/providers/services/provider-runtime.service.js +66 -0
  180. package/dist-server/server/modules/providers/services/provider-runtime.service.js.map +1 -0
  181. package/dist-server/server/modules/providers/services/provider-token-usage.service.js +268 -0
  182. package/dist-server/server/modules/providers/services/provider-token-usage.service.js.map +1 -0
  183. package/dist-server/server/modules/providers/services/session-conversations-search.service.js +12 -4
  184. package/dist-server/server/modules/providers/services/session-conversations-search.service.js.map +1 -1
  185. package/dist-server/server/modules/providers/services/sessions-watcher.service.js +2 -0
  186. package/dist-server/server/modules/providers/services/sessions-watcher.service.js.map +1 -1
  187. package/dist-server/server/modules/providers/services/sessions.service.js +116 -3
  188. package/dist-server/server/modules/providers/services/sessions.service.js.map +1 -1
  189. package/dist-server/server/modules/providers/shared/base/abstract.provider.js +2 -3
  190. package/dist-server/server/modules/providers/shared/base/abstract.provider.js.map +1 -1
  191. package/dist-server/server/modules/providers/tests/claude-auth.test.js +122 -0
  192. package/dist-server/server/modules/providers/tests/claude-auth.test.js.map +1 -0
  193. package/dist-server/server/modules/providers/tests/claude-sessions.test.js +44 -0
  194. package/dist-server/server/modules/providers/tests/claude-sessions.test.js.map +1 -0
  195. package/dist-server/server/modules/providers/tests/codex-sessions.test.js +76 -4
  196. package/dist-server/server/modules/providers/tests/codex-sessions.test.js.map +1 -1
  197. package/dist-server/server/modules/providers/tests/opencode-models.test.js +15 -126
  198. package/dist-server/server/modules/providers/tests/opencode-models.test.js.map +1 -1
  199. package/dist-server/server/modules/providers/tests/opencode-sessions.test.js +5 -5
  200. package/dist-server/server/modules/providers/tests/opencode-sessions.test.js.map +1 -1
  201. package/dist-server/server/modules/providers/tests/{provider-image-history.test.js → provider-attachment-history.test.js} +60 -2
  202. package/dist-server/server/modules/providers/tests/provider-attachment-history.test.js.map +1 -0
  203. package/dist-server/server/modules/providers/tests/provider-models.service.test.js +241 -271
  204. package/dist-server/server/modules/providers/tests/provider-models.service.test.js.map +1 -1
  205. package/dist-server/server/modules/providers/tests/provider-runtime.service.test.js +126 -0
  206. package/dist-server/server/modules/providers/tests/provider-runtime.service.test.js.map +1 -0
  207. package/dist-server/server/modules/providers/tests/provider-token-usage.service.test.js +166 -0
  208. package/dist-server/server/modules/providers/tests/provider-token-usage.service.test.js.map +1 -0
  209. package/dist-server/server/modules/providers/tests/provider.routes.test.js +126 -0
  210. package/dist-server/server/modules/providers/tests/provider.routes.test.js.map +1 -0
  211. package/dist-server/server/modules/providers/tests/sessions-details.test.js +62 -0
  212. package/dist-server/server/modules/providers/tests/sessions-details.test.js.map +1 -0
  213. package/dist-server/server/modules/providers/tests/sessions.service.test.js +87 -0
  214. package/dist-server/server/modules/providers/tests/sessions.service.test.js.map +1 -0
  215. package/dist-server/server/modules/providers/tests/skills.test.js +4 -2
  216. package/dist-server/server/modules/providers/tests/skills.test.js.map +1 -1
  217. package/dist-server/server/modules/settings/index.js +3 -0
  218. package/dist-server/server/modules/settings/index.js.map +1 -0
  219. package/dist-server/server/modules/settings/settings.module.js +35 -0
  220. package/dist-server/server/modules/settings/settings.module.js.map +1 -0
  221. package/dist-server/server/modules/settings/settings.routes.js +34 -0
  222. package/dist-server/server/modules/settings/settings.routes.js.map +1 -0
  223. package/dist-server/server/modules/settings/settings.service.js +116 -0
  224. package/dist-server/server/modules/settings/settings.service.js.map +1 -0
  225. package/dist-server/server/modules/settings/tests/settings.service.test.js +48 -0
  226. package/dist-server/server/modules/settings/tests/settings.service.test.js.map +1 -0
  227. package/dist-server/server/modules/system/index.js +3 -0
  228. package/dist-server/server/modules/system/index.js.map +1 -0
  229. package/dist-server/server/modules/system/system.module.js +44 -0
  230. package/dist-server/server/modules/system/system.module.js.map +1 -0
  231. package/dist-server/server/modules/system/system.routes.js +16 -0
  232. package/dist-server/server/modules/system/system.routes.js.map +1 -0
  233. package/dist-server/server/modules/system/system.service.js +45 -0
  234. package/dist-server/server/modules/system/system.service.js.map +1 -0
  235. package/dist-server/server/modules/system/tests/system.service.test.js +100 -0
  236. package/dist-server/server/modules/system/tests/system.service.test.js.map +1 -0
  237. package/dist-server/server/modules/taskmaster/index.js +3 -0
  238. package/dist-server/server/modules/taskmaster/index.js.map +1 -0
  239. package/dist-server/server/modules/taskmaster/taskmaster.module.js +20 -0
  240. package/dist-server/server/modules/taskmaster/taskmaster.module.js.map +1 -0
  241. package/dist-server/server/modules/taskmaster/taskmaster.routes.js +1372 -0
  242. package/dist-server/server/modules/taskmaster/taskmaster.routes.js.map +1 -0
  243. package/dist-server/server/modules/taskmaster/taskmaster.service.js +107 -0
  244. package/dist-server/server/modules/taskmaster/taskmaster.service.js.map +1 -0
  245. package/dist-server/server/modules/taskmaster/tests/taskmaster.routes.test.js +132 -0
  246. package/dist-server/server/modules/taskmaster/tests/taskmaster.routes.test.js.map +1 -0
  247. package/dist-server/server/modules/taskmaster/tests/taskmaster.service.test.js +85 -0
  248. package/dist-server/server/modules/taskmaster/tests/taskmaster.service.test.js.map +1 -0
  249. package/dist-server/server/modules/user/index.js +3 -0
  250. package/dist-server/server/modules/user/index.js.map +1 -0
  251. package/dist-server/server/modules/user/tests/user.service.test.js +53 -0
  252. package/dist-server/server/modules/user/tests/user.service.test.js.map +1 -0
  253. package/dist-server/server/modules/user/user.module.js +47 -0
  254. package/dist-server/server/modules/user/user.module.js.map +1 -0
  255. package/dist-server/server/modules/user/user.routes.js +44 -0
  256. package/dist-server/server/modules/user/user.routes.js.map +1 -0
  257. package/dist-server/server/modules/user/user.service.js +59 -0
  258. package/dist-server/server/modules/user/user.service.js.map +1 -0
  259. package/dist-server/server/modules/voice/index.js +3 -0
  260. package/dist-server/server/modules/voice/index.js.map +1 -0
  261. package/dist-server/server/modules/voice/tests/voice.service.test.js +86 -0
  262. package/dist-server/server/modules/voice/tests/voice.service.test.js.map +1 -0
  263. package/dist-server/server/modules/voice/voice.module.js +44 -0
  264. package/dist-server/server/modules/voice/voice.module.js.map +1 -0
  265. package/dist-server/server/modules/voice/voice.routes.js +87 -0
  266. package/dist-server/server/modules/voice/voice.routes.js.map +1 -0
  267. package/dist-server/server/modules/voice/voice.service.js +148 -0
  268. package/dist-server/server/modules/voice/voice.service.js.map +1 -0
  269. package/dist-server/server/modules/websocket/services/chat-session-writer.service.js +2 -2
  270. package/dist-server/server/modules/websocket/services/chat-websocket.service.js +44 -29
  271. package/dist-server/server/modules/websocket/services/chat-websocket.service.js.map +1 -1
  272. package/dist-server/server/modules/websocket/services/shell-websocket.service.js +76 -8
  273. package/dist-server/server/modules/websocket/services/shell-websocket.service.js.map +1 -1
  274. package/dist-server/server/modules/websocket/services/websocket-server.service.js +51 -20
  275. package/dist-server/server/modules/websocket/services/websocket-server.service.js.map +1 -1
  276. package/dist-server/server/modules/websocket/tests/{chat-image-filter.test.js → chat-attachment-filter.test.js} +10 -3
  277. package/dist-server/server/modules/websocket/tests/chat-attachment-filter.test.js.map +1 -0
  278. package/dist-server/server/modules/websocket/tests/shell-websocket.service.test.js +97 -0
  279. package/dist-server/server/modules/websocket/tests/shell-websocket.service.test.js.map +1 -0
  280. package/dist-server/server/modules/websocket/tests/websocket-heartbeat.service.test.js +61 -0
  281. package/dist-server/server/modules/websocket/tests/websocket-heartbeat.service.test.js.map +1 -0
  282. package/dist-server/server/modules/worktrees/index.js +3 -0
  283. package/dist-server/server/modules/worktrees/index.js.map +1 -0
  284. package/dist-server/server/modules/worktrees/services/worktree-create-and-open.service.js +37 -0
  285. package/dist-server/server/modules/worktrees/services/worktree-create-and-open.service.js.map +1 -0
  286. package/dist-server/server/modules/worktrees/services/worktree-create.service.js +67 -0
  287. package/dist-server/server/modules/worktrees/services/worktree-create.service.js.map +1 -0
  288. package/dist-server/server/modules/worktrees/services/worktree-git.service.js +172 -0
  289. package/dist-server/server/modules/worktrees/services/worktree-git.service.js.map +1 -0
  290. package/dist-server/server/modules/worktrees/services/worktree-list.service.js +91 -0
  291. package/dist-server/server/modules/worktrees/services/worktree-list.service.js.map +1 -0
  292. package/dist-server/server/modules/worktrees/services/worktree-merge.service.js +118 -0
  293. package/dist-server/server/modules/worktrees/services/worktree-merge.service.js.map +1 -0
  294. package/dist-server/server/modules/worktrees/services/worktree-open.service.js +58 -0
  295. package/dist-server/server/modules/worktrees/services/worktree-open.service.js.map +1 -0
  296. package/dist-server/server/modules/worktrees/services/worktree-remove.service.js +65 -0
  297. package/dist-server/server/modules/worktrees/services/worktree-remove.service.js.map +1 -0
  298. package/dist-server/server/modules/worktrees/tests/worktree-create-and-open.service.test.js +58 -0
  299. package/dist-server/server/modules/worktrees/tests/worktree-create-and-open.service.test.js.map +1 -0
  300. package/dist-server/server/modules/worktrees/tests/worktree-create.service.test.js +85 -0
  301. package/dist-server/server/modules/worktrees/tests/worktree-create.service.test.js.map +1 -0
  302. package/dist-server/server/modules/worktrees/tests/worktree-git.service.test.js +58 -0
  303. package/dist-server/server/modules/worktrees/tests/worktree-git.service.test.js.map +1 -0
  304. package/dist-server/server/modules/worktrees/tests/worktree-list.service.test.js +34 -0
  305. package/dist-server/server/modules/worktrees/tests/worktree-list.service.test.js.map +1 -0
  306. package/dist-server/server/modules/worktrees/tests/worktree-merge.service.test.js +137 -0
  307. package/dist-server/server/modules/worktrees/tests/worktree-merge.service.test.js.map +1 -0
  308. package/dist-server/server/modules/worktrees/tests/worktree-open.service.test.js +99 -0
  309. package/dist-server/server/modules/worktrees/tests/worktree-open.service.test.js.map +1 -0
  310. package/dist-server/server/modules/worktrees/tests/worktree-remove.service.test.js +157 -0
  311. package/dist-server/server/modules/worktrees/tests/worktree-remove.service.test.js.map +1 -0
  312. package/dist-server/server/modules/worktrees/tests/worktrees.routes.test.js +167 -0
  313. package/dist-server/server/modules/worktrees/tests/worktrees.routes.test.js.map +1 -0
  314. package/dist-server/server/modules/worktrees/worktrees.module.js +97 -0
  315. package/dist-server/server/modules/worktrees/worktrees.module.js.map +1 -0
  316. package/dist-server/server/modules/worktrees/worktrees.routes.js +84 -0
  317. package/dist-server/server/modules/worktrees/worktrees.routes.js.map +1 -0
  318. package/dist-server/server/shared/image-attachments.js +90 -17
  319. package/dist-server/server/shared/image-attachments.js.map +1 -1
  320. package/dist-server/server/shared/tests/image-attachments.test.js +43 -1
  321. package/dist-server/server/shared/tests/image-attachments.test.js.map +1 -1
  322. package/dist-server/server/shared/utils.js +66 -142
  323. package/dist-server/server/shared/utils.js.map +1 -1
  324. package/package.json +9 -5
  325. package/server/index.ts +401 -0
  326. package/server/{load-env.js → load-env.ts} +18 -5
  327. package/server/modules/agent/agent.module.ts +51 -0
  328. package/server/modules/agent/agent.routes.ts +1287 -0
  329. package/server/modules/agent/index.ts +2 -0
  330. package/server/modules/agent/tests/agent.routes.test.ts +205 -0
  331. package/server/modules/assets/assets.routes.ts +63 -16
  332. package/server/modules/assets/index.ts +2 -2
  333. package/server/modules/assets/services/image-assets.service.ts +46 -1
  334. package/server/modules/assets/tests/image-assets.service.test.ts +28 -0
  335. package/server/{middleware/auth.js → modules/auth/auth.middleware.ts} +38 -7
  336. package/server/modules/auth/auth.module.ts +38 -0
  337. package/server/modules/auth/auth.routes.ts +57 -0
  338. package/server/modules/auth/auth.service.ts +155 -0
  339. package/server/modules/auth/index.ts +9 -0
  340. package/server/modules/auth/tests/auth.service.test.ts +95 -0
  341. package/server/{browser-use-mcp.ts → modules/browser-use/browser-use-mcp.ts} +3 -1
  342. package/server/modules/browser-use/browser-use.service.ts +3 -4
  343. package/server/modules/browser-use/index.ts +8 -0
  344. package/server/modules/cli/cli.module.ts +96 -0
  345. package/server/modules/cli/cli.service.ts +281 -0
  346. package/server/modules/cli/cli.ts +13 -0
  347. package/server/modules/cli/index.ts +2 -0
  348. package/server/modules/cli/sandbox.service.ts +413 -0
  349. package/server/modules/cli/tests/cli.service.test.ts +91 -0
  350. package/server/modules/cli/tests/sandbox.service.test.ts +61 -0
  351. package/server/modules/commands/commands.module.ts +22 -0
  352. package/server/{routes/commands.js → modules/commands/commands.routes.ts} +46 -38
  353. package/server/modules/commands/index.ts +2 -0
  354. package/server/modules/commands/tests/commands.test.ts +114 -0
  355. package/server/modules/database/index.ts +3 -0
  356. package/server/modules/database/migrations.ts +35 -0
  357. package/server/modules/database/repositories/provider-models.ts +168 -0
  358. package/server/modules/database/repositories/sessions.db.ts +103 -8
  359. package/server/modules/database/schema.ts +30 -0
  360. package/server/modules/database/tests/provider-models.db.integration.test.ts +125 -0
  361. package/server/modules/database/tests/sessions-provider-mapping.test.ts +2 -1
  362. package/server/modules/database/tests/sessions.db.integration.test.ts +32 -0
  363. package/server/modules/file-tree/file-tree.module.ts +116 -0
  364. package/server/modules/file-tree/file-tree.routes.ts +258 -0
  365. package/server/modules/file-tree/file-tree.service.ts +664 -0
  366. package/server/modules/file-tree/index.ts +2 -0
  367. package/server/modules/file-tree/tests/file-tree.routes.test.ts +157 -0
  368. package/server/modules/file-tree/tests/file-tree.service.test.ts +317 -0
  369. package/server/modules/git/git-parsing.service.ts +66 -0
  370. package/server/modules/git/git.module.ts +22 -0
  371. package/server/{routes/git.js → modules/git/git.routes.ts} +91 -132
  372. package/server/modules/git/index.ts +2 -0
  373. package/server/modules/git/tests/git-init.routes.test.ts +52 -0
  374. package/server/{routes/git.test.js → modules/git/tests/git.test.ts} +3 -3
  375. package/server/modules/notifications/index.ts +9 -0
  376. package/server/modules/notifications/tests/notification-orchestrator.integration.test.ts +55 -0
  377. package/server/{services/vapid-keys.js → modules/notifications/vapid-keys.service.ts} +3 -1
  378. package/server/modules/plugins/index.ts +9 -0
  379. package/server/{utils/plugin-process-manager.js → modules/plugins/plugin-process.service.ts} +3 -1
  380. package/server/{utils/plugin-loader.js → modules/plugins/plugin-registry.service.ts} +1 -0
  381. package/server/modules/plugins/plugins.module.ts +33 -0
  382. package/server/modules/plugins/plugins.routes.ts +66 -0
  383. package/server/modules/plugins/plugins.service.ts +147 -0
  384. package/server/modules/plugins/tests/plugins.service.test.ts +32 -0
  385. package/server/modules/projects/index.ts +5 -0
  386. package/server/modules/providers/README.md +41 -9
  387. package/server/modules/providers/index.ts +4 -0
  388. package/server/modules/providers/list/claude/claude-auth.provider.ts +8 -0
  389. package/server/modules/providers/list/claude/claude-models.provider.ts +47 -26
  390. package/server/{claude-sdk.js → modules/providers/list/claude/claude-runtime.provider.js} +73 -59
  391. package/server/modules/providers/list/claude/claude-session-synchronizer.provider.ts +6 -4
  392. package/server/modules/providers/list/claude/claude-sessions.provider.ts +27 -4
  393. package/server/modules/providers/list/claude/claude.provider.ts +3 -0
  394. package/server/modules/providers/list/codex/codex-models.provider.ts +58 -108
  395. package/server/{openai-codex.js → modules/providers/list/codex/codex-runtime.provider.js} +54 -36
  396. package/server/modules/providers/list/codex/codex-session-synchronizer.provider.ts +1 -60
  397. package/server/modules/providers/list/codex/codex-sessions.provider.ts +331 -7
  398. package/server/modules/providers/list/codex/codex-skills.provider.ts +5 -0
  399. package/server/modules/providers/list/codex/codex.provider.ts +3 -0
  400. package/server/modules/providers/list/cursor/cursor-models.provider.ts +32 -719
  401. package/server/{cursor-cli.js → modules/providers/list/cursor/cursor-runtime.provider.js} +58 -27
  402. package/server/modules/providers/list/cursor/cursor-sessions.provider.ts +26 -15
  403. package/server/modules/providers/list/cursor/cursor.provider.ts +3 -0
  404. package/server/modules/providers/list/opencode/opencode-models.provider.ts +112 -423
  405. package/server/{opencode-cli.js → modules/providers/list/opencode/opencode-runtime.provider.js} +56 -28
  406. package/server/{opencode-cli.test.js → modules/providers/list/opencode/opencode-runtime.provider.test.js} +41 -3
  407. package/server/modules/providers/list/opencode/opencode-session-synchronizer.provider.ts +0 -13
  408. package/server/modules/providers/list/opencode/opencode-sessions.provider.ts +13 -5
  409. package/server/modules/providers/list/opencode/opencode.provider.ts +3 -0
  410. package/server/modules/providers/provider.routes.ts +228 -39
  411. package/server/modules/providers/services/provider-capabilities.service.ts +7 -1
  412. package/server/modules/providers/services/provider-models.service.ts +312 -269
  413. package/server/modules/providers/services/provider-runtime.service.ts +108 -0
  414. package/server/modules/providers/services/provider-token-usage.service.ts +357 -0
  415. package/server/modules/providers/services/session-conversations-search.service.ts +12 -4
  416. package/server/modules/providers/services/sessions-watcher.service.ts +2 -0
  417. package/server/modules/providers/services/sessions.service.ts +166 -3
  418. package/server/modules/providers/shared/base/abstract.provider.ts +4 -3
  419. package/server/modules/providers/tests/claude-auth.test.ts +150 -0
  420. package/server/modules/providers/tests/claude-sessions.test.ts +61 -0
  421. package/server/modules/providers/tests/codex-sessions.test.ts +83 -4
  422. package/server/modules/providers/tests/opencode-models.test.ts +34 -135
  423. package/server/modules/providers/tests/opencode-sessions.test.ts +5 -5
  424. package/server/modules/providers/tests/{provider-image-history.test.ts → provider-attachment-history.test.ts} +68 -1
  425. package/server/modules/providers/tests/provider-models.service.test.ts +300 -296
  426. package/server/modules/providers/tests/provider-runtime.service.test.ts +138 -0
  427. package/server/modules/providers/tests/provider-token-usage.service.test.ts +186 -0
  428. package/server/modules/providers/tests/provider.routes.test.ts +183 -0
  429. package/server/modules/providers/tests/sessions-details.test.ts +73 -0
  430. package/server/modules/providers/tests/sessions.service.test.ts +127 -0
  431. package/server/modules/providers/tests/skills.test.ts +9 -2
  432. package/server/modules/settings/index.ts +2 -0
  433. package/server/modules/settings/settings.module.ts +50 -0
  434. package/server/modules/settings/settings.routes.ts +51 -0
  435. package/server/modules/settings/settings.service.ts +186 -0
  436. package/server/modules/settings/tests/settings.service.test.ts +54 -0
  437. package/server/modules/system/index.ts +2 -0
  438. package/server/modules/system/system.module.ts +62 -0
  439. package/server/modules/system/system.routes.ts +21 -0
  440. package/server/modules/system/system.service.ts +76 -0
  441. package/server/modules/system/tests/system.service.test.ts +118 -0
  442. package/server/modules/taskmaster/index.ts +2 -0
  443. package/server/modules/taskmaster/taskmaster.module.ts +24 -0
  444. package/server/modules/taskmaster/taskmaster.routes.ts +1482 -0
  445. package/server/modules/taskmaster/taskmaster.service.ts +132 -0
  446. package/server/modules/taskmaster/tests/taskmaster.routes.test.ts +144 -0
  447. package/server/modules/taskmaster/tests/taskmaster.service.test.ts +97 -0
  448. package/server/modules/user/index.ts +2 -0
  449. package/server/modules/user/tests/user.service.test.ts +61 -0
  450. package/server/modules/user/user.module.ts +58 -0
  451. package/server/modules/user/user.routes.ts +50 -0
  452. package/server/modules/user/user.service.ts +85 -0
  453. package/server/modules/voice/index.ts +2 -0
  454. package/server/modules/voice/tests/voice.service.test.ts +98 -0
  455. package/server/modules/voice/voice.module.ts +47 -0
  456. package/server/modules/voice/voice.routes.ts +113 -0
  457. package/server/modules/voice/voice.service.ts +192 -0
  458. package/server/modules/websocket/README.md +7 -7
  459. package/server/modules/websocket/services/chat-session-writer.service.ts +2 -2
  460. package/server/modules/websocket/services/chat-websocket.service.ts +77 -61
  461. package/server/modules/websocket/services/shell-websocket.service.ts +88 -12
  462. package/server/modules/websocket/services/websocket-server.service.ts +63 -19
  463. package/server/modules/websocket/tests/{chat-image-filter.test.ts → chat-attachment-filter.test.ts} +17 -2
  464. package/server/modules/websocket/tests/shell-websocket.service.test.ts +119 -0
  465. package/server/modules/websocket/tests/websocket-heartbeat.service.test.ts +78 -0
  466. package/server/modules/worktrees/index.ts +2 -0
  467. package/server/modules/worktrees/services/worktree-create-and-open.service.ts +53 -0
  468. package/server/modules/worktrees/services/worktree-create.service.ts +104 -0
  469. package/server/modules/worktrees/services/worktree-git.service.ts +207 -0
  470. package/server/modules/worktrees/services/worktree-list.service.ts +143 -0
  471. package/server/modules/worktrees/services/worktree-merge.service.ts +157 -0
  472. package/server/modules/worktrees/services/worktree-open.service.ts +85 -0
  473. package/server/modules/worktrees/services/worktree-remove.service.ts +88 -0
  474. package/server/modules/worktrees/tests/worktree-create-and-open.service.test.ts +68 -0
  475. package/server/modules/worktrees/tests/worktree-create.service.test.ts +142 -0
  476. package/server/modules/worktrees/tests/worktree-git.service.test.ts +84 -0
  477. package/server/modules/worktrees/tests/worktree-list.service.test.ts +45 -0
  478. package/server/modules/worktrees/tests/worktree-merge.service.test.ts +213 -0
  479. package/server/modules/worktrees/tests/worktree-open.service.test.ts +122 -0
  480. package/server/modules/worktrees/tests/worktree-remove.service.test.ts +227 -0
  481. package/server/modules/worktrees/tests/worktrees.routes.test.ts +199 -0
  482. package/server/modules/worktrees/worktrees.module.ts +113 -0
  483. package/server/modules/worktrees/worktrees.routes.ts +116 -0
  484. package/server/shared/image-attachments.ts +112 -18
  485. package/server/shared/interfaces.ts +33 -25
  486. package/server/shared/tests/image-attachments.test.ts +55 -0
  487. package/server/shared/types.ts +685 -36
  488. package/server/shared/utils.ts +77 -212
  489. package/dist/assets/index-CcCsKitq.js +0 -1423
  490. package/dist/assets/index-DLmhQ15L.css +0 -32
  491. package/dist-server/server/browser-use-mcp.js.map +0 -1
  492. package/dist-server/server/claude-sdk.js.map +0 -1
  493. package/dist-server/server/cli.js +0 -647
  494. package/dist-server/server/cli.js.map +0 -1
  495. package/dist-server/server/cursor-cli.js.map +0 -1
  496. package/dist-server/server/middleware/auth.js.map +0 -1
  497. package/dist-server/server/modules/providers/tests/provider-image-history.test.js.map +0 -1
  498. package/dist-server/server/modules/websocket/tests/chat-image-filter.test.js.map +0 -1
  499. package/dist-server/server/openai-codex.js.map +0 -1
  500. package/dist-server/server/opencode-cli.js.map +0 -1
  501. package/dist-server/server/opencode-cli.test.js.map +0 -1
  502. package/dist-server/server/routes/agent.js +0 -1155
  503. package/dist-server/server/routes/agent.js.map +0 -1
  504. package/dist-server/server/routes/auth.js +0 -119
  505. package/dist-server/server/routes/auth.js.map +0 -1
  506. package/dist-server/server/routes/commands.js +0 -492
  507. package/dist-server/server/routes/commands.js.map +0 -1
  508. package/dist-server/server/routes/cursor.js +0 -49
  509. package/dist-server/server/routes/cursor.js.map +0 -1
  510. package/dist-server/server/routes/git.js +0 -1389
  511. package/dist-server/server/routes/git.js.map +0 -1
  512. package/dist-server/server/routes/git.test.js.map +0 -1
  513. package/dist-server/server/routes/mcp-utils.js +0 -29
  514. package/dist-server/server/routes/mcp-utils.js.map +0 -1
  515. package/dist-server/server/routes/plugins.js +0 -266
  516. package/dist-server/server/routes/plugins.js.map +0 -1
  517. package/dist-server/server/routes/settings.js +0 -249
  518. package/dist-server/server/routes/settings.js.map +0 -1
  519. package/dist-server/server/routes/taskmaster.js +0 -1362
  520. package/dist-server/server/routes/taskmaster.js.map +0 -1
  521. package/dist-server/server/routes/tests/commands.test.js +0 -76
  522. package/dist-server/server/routes/tests/commands.test.js.map +0 -1
  523. package/dist-server/server/routes/user.js +0 -116
  524. package/dist-server/server/routes/user.js.map +0 -1
  525. package/dist-server/server/services/notification-orchestrator.js +0 -2
  526. package/dist-server/server/services/notification-orchestrator.js.map +0 -1
  527. package/dist-server/server/services/tests/notification-orchestrator.test.js +0 -64
  528. package/dist-server/server/services/tests/notification-orchestrator.test.js.map +0 -1
  529. package/dist-server/server/services/vapid-keys.js.map +0 -1
  530. package/dist-server/server/utils/colors.js +0 -20
  531. package/dist-server/server/utils/colors.js.map +0 -1
  532. package/dist-server/server/utils/commandParser.js +0 -255
  533. package/dist-server/server/utils/commandParser.js.map +0 -1
  534. package/dist-server/server/utils/gitConfig.js +0 -37
  535. package/dist-server/server/utils/gitConfig.js.map +0 -1
  536. package/dist-server/server/utils/mcp-detector.js +0 -134
  537. package/dist-server/server/utils/mcp-detector.js.map +0 -1
  538. package/dist-server/server/utils/plugin-loader.js.map +0 -1
  539. package/dist-server/server/utils/plugin-process-manager.js.map +0 -1
  540. package/dist-server/server/utils/runtime-paths.js +0 -30
  541. package/dist-server/server/utils/runtime-paths.js.map +0 -1
  542. package/dist-server/server/utils/taskmaster-websocket.js +0 -124
  543. package/dist-server/server/utils/taskmaster-websocket.js.map +0 -1
  544. package/dist-server/server/utils/url-detection.js +0 -58
  545. package/dist-server/server/utils/url-detection.js.map +0 -1
  546. package/dist-server/server/voice-proxy.js +0 -226
  547. package/dist-server/server/voice-proxy.js.map +0 -1
  548. package/server/cli.js +0 -695
  549. package/server/index.js +0 -1651
  550. package/server/routes/agent.js +0 -1257
  551. package/server/routes/auth.js +0 -137
  552. package/server/routes/cursor.js +0 -52
  553. package/server/routes/mcp-utils.js +0 -31
  554. package/server/routes/plugins.js +0 -307
  555. package/server/routes/settings.js +0 -282
  556. package/server/routes/taskmaster.js +0 -1470
  557. package/server/routes/tests/commands.test.js +0 -82
  558. package/server/routes/user.js +0 -124
  559. package/server/services/notification-orchestrator.js +0 -7
  560. package/server/services/tests/notification-orchestrator.test.js +0 -80
  561. package/server/utils/colors.js +0 -21
  562. package/server/utils/commandParser.js +0 -305
  563. package/server/utils/gitConfig.js +0 -35
  564. package/server/utils/mcp-detector.js +0 -147
  565. package/server/utils/runtime-paths.js +0 -37
  566. package/server/utils/taskmaster-websocket.js +0 -135
  567. package/server/utils/url-detection.js +0 -71
  568. package/server/voice-proxy.js +0 -224
  569. /package/server/constants/{config.js → config.ts} +0 -0
@@ -3,13 +3,21 @@ import path from 'node:path';
3
3
  import type { WebSocket } from 'ws';
4
4
 
5
5
  import { sessionsDb } from '@/modules/database/index.js';
6
+ import { providerModelsService } from '@/modules/providers/index.js';
6
7
  import { chatRunRegistry } from '@/modules/websocket/services/chat-run-registry.service.js';
7
8
  import { connectedClients, WS_OPEN_STATE } from '@/modules/websocket/services/websocket-state.service.js';
8
- import { getGlobalImageAssetsDir, normalizeImageDescriptors } from '@/shared/image-attachments.js';
9
+ import {
10
+ getGlobalImageAssetsDir,
11
+ isImageAttachmentDescriptor,
12
+ normalizeAttachmentDescriptors,
13
+ type ChatAttachmentDescriptor,
14
+ } from '@/shared/image-attachments.js';
9
15
  import type {
10
16
  AnyRecord,
11
17
  AuthenticatedWebSocketRequest,
12
18
  LLMProvider,
19
+ ProviderPermissionDecision,
20
+ ProviderRuntimeWriter,
13
21
  } from '@/shared/types.js';
14
22
  import { parseIncomingJsonObject } from '@/shared/utils.js';
15
23
 
@@ -23,10 +31,13 @@ import { parseIncomingJsonObject } from '@/shared/utils.js';
23
31
  *
24
32
  * Exported for tests; `assetsRootOverride` exists only for them.
25
33
  */
26
- export function filterImagesToUploadStore(images: unknown, assetsRootOverride?: string): AnyRecord[] {
34
+ export function filterAttachmentsToUploadStore(
35
+ attachments: unknown,
36
+ assetsRootOverride?: string,
37
+ ): ChatAttachmentDescriptor[] {
27
38
  const assetsRoot = path.resolve(assetsRootOverride ?? getGlobalImageAssetsDir());
28
39
 
29
- return normalizeImageDescriptors(images).filter((descriptor) => {
40
+ return normalizeAttachmentDescriptors(attachments).filter((descriptor) => {
30
41
  // Relative paths are anchored in the store; absolute ones must already be in it.
31
42
  const resolved = path.resolve(assetsRoot, descriptor.path);
32
43
  const relative = path.relative(assetsRoot, resolved);
@@ -38,43 +49,37 @@ export function filterImagesToUploadStore(images: unknown, assetsRootOverride?:
38
49
  !relative.includes('/');
39
50
 
40
51
  if (!isDirectChild) {
41
- console.warn(`[Chat] Dropping image outside the upload store: ${descriptor.path}`);
52
+ console.warn(`[Chat] Dropping attachment outside the upload store: ${descriptor.path}`);
42
53
  }
43
54
  return isDirectChild;
44
55
  });
45
56
  }
46
57
 
47
- /**
48
- * One provider runtime entry point. All five runtimes share this signature,
49
- * which lets the chat handler dispatch through a provider-keyed map instead
50
- * of provider-specific branches.
51
- */
52
- type ProviderSpawnFn = (
53
- command: string,
54
- options: AnyRecord,
55
- writer: unknown
56
- ) => Promise<unknown>;
58
+ /** Backward-compatible image filter consumed by existing websocket tests. */
59
+ export function filterImagesToUploadStore(
60
+ images: unknown,
61
+ assetsRootOverride?: string,
62
+ ): ChatAttachmentDescriptor[] {
63
+ return filterAttachmentsToUploadStore(images, assetsRootOverride);
64
+ }
65
+
66
+ /** Application boundary for dispatching provider runs and approvals. */
67
+ type ProviderRuntimeGateway = {
68
+ hasRuntime(provider: string): boolean;
69
+ run(
70
+ provider: LLMProvider,
71
+ command: string,
72
+ options: AnyRecord,
73
+ writer: ProviderRuntimeWriter,
74
+ ): Promise<unknown>;
75
+ abort(provider: LLMProvider, sessionId: string): Promise<boolean>;
76
+ resolveToolApproval(requestId: string, payload: ProviderPermissionDecision): void;
77
+ getPendingApprovalsForSession(sessionId: string): unknown[];
78
+ };
57
79
 
58
80
  type ChatWebSocketDependencies = {
59
- /** Provider runtimes keyed by provider id. */
60
- spawnFns: Record<LLMProvider, ProviderSpawnFn>;
61
- /**
62
- * Abort functions keyed by provider id. They are addressed with the
63
- * provider-native session id (that is how runtimes key their process maps).
64
- * The Claude abort is async; the rest are sync — both shapes are accepted.
65
- */
66
- abortFns: Record<LLMProvider, (providerSessionId: string) => boolean | Promise<boolean>>;
67
- resolveToolApproval: (
68
- requestId: string,
69
- payload: {
70
- allow: boolean;
71
- updatedInput?: unknown;
72
- message?: string;
73
- rememberEntry?: unknown;
74
- }
75
- ) => void;
76
- /** Claude-only today: pending tool approvals included in `chat_subscribed`. */
77
- getPendingApprovalsForSession: (providerSessionId: string) => unknown[];
81
+ /** Central dispatcher for every provider SDK/CLI runtime. */
82
+ runtime: ProviderRuntimeGateway;
78
83
  };
79
84
 
80
85
  /**
@@ -162,8 +167,7 @@ async function handleChatSend(
162
167
  }
163
168
 
164
169
  const provider = session.provider as LLMProvider;
165
- const spawnFn = dependencies.spawnFns[provider];
166
- if (!spawnFn) {
170
+ if (!dependencies.runtime.hasRuntime(provider)) {
167
171
  sendProtocolError(ws, 'UNSUPPORTED_PROVIDER', `Provider "${provider}" is not available.`, sessionId);
168
172
  return;
169
173
  }
@@ -189,23 +193,46 @@ async function handleChatSend(
189
193
  const clientOptions = (data.options ?? {}) as AnyRecord;
190
194
  const command = typeof data.content === 'string' ? data.content : '';
191
195
 
192
- // The provider runtimes receive the provider-native session id (that is the
193
- // id their CLI/SDK understands for resume). Brand-new sessions have no
194
- // provider id yet, so the runtime starts fresh and announces one, which the
195
- // gateway writer captures and maps back to the app session id.
196
+ // Record what this turn runs with so reopening the session later restores the
197
+ // same model and reasoning effort, and so the resume path has a
198
+ // session-scoped model answer to use.
199
+ if (typeof clientOptions.model === 'string' && clientOptions.model.trim()) {
200
+ providerModelsService.setSessionModel(provider, sessionId, clientOptions.model);
201
+ }
202
+ if (typeof clientOptions.effort === 'string' && clientOptions.effort.trim()) {
203
+ providerModelsService.setSessionEffort(provider, sessionId, clientOptions.effort);
204
+ }
205
+
206
+ const attachmentCandidates = [
207
+ ...normalizeAttachmentDescriptors(clientOptions.images),
208
+ ...normalizeAttachmentDescriptors(clientOptions.files),
209
+ ...normalizeAttachmentDescriptors(clientOptions.attachments),
210
+ ];
211
+ const verifiedAttachments = filterAttachmentsToUploadStore(attachmentCandidates);
212
+ const uniqueAttachments = verifiedAttachments.filter(
213
+ (descriptor, index, all) => all.findIndex((candidate) => candidate.path === descriptor.path) === index,
214
+ );
215
+
216
+ // The provider runtimes receive the stable app session id. When their
217
+ // CLI/SDK needs the provider-native id for resume, they resolve it from the
218
+ // session row themselves (sessionsService.resolveProviderSessionId).
219
+ // Brand-new sessions have no provider id yet, so the runtime starts fresh
220
+ // and announces one, which the gateway writer captures and maps back to the
221
+ // app session id.
196
222
  const runtimeOptions: AnyRecord = {
197
223
  ...clientOptions,
198
- // Image attachments are re-validated server-side: only files inside the
199
- // global upload store may reach the provider runtimes' file reads.
200
- images: filterImagesToUploadStore(clientOptions.images),
201
- sessionId: session.provider_session_id ?? undefined,
202
- resume: Boolean(session.provider_session_id),
224
+ // Attachments are re-validated server-side: only direct children of the
225
+ // global upload store may reach provider runtimes or their file tools.
226
+ attachments: uniqueAttachments,
227
+ images: uniqueAttachments.filter(isImageAttachmentDescriptor),
228
+ files: uniqueAttachments.filter((descriptor) => !isImageAttachmentDescriptor(descriptor)),
229
+ sessionId,
203
230
  cwd: clientOptions.cwd ?? session.project_path ?? undefined,
204
231
  projectPath: session.project_path ?? clientOptions.projectPath,
205
232
  };
206
233
 
207
234
  try {
208
- await spawnFn(command, runtimeOptions, run.writer);
235
+ await dependencies.runtime.run(provider, command, runtimeOptions, run.writer);
209
236
  } catch (error) {
210
237
  const message = error instanceof Error ? error.message : String(error);
211
238
  console.error(`[Chat] Provider runtime "${provider}" failed`, { sessionId, error: message });
@@ -241,11 +268,7 @@ async function handleChatAbort(
241
268
  return;
242
269
  }
243
270
 
244
- const abortFn = dependencies.abortFns[run.provider];
245
- let success = false;
246
- if (abortFn && run.providerSessionId) {
247
- success = Boolean(await abortFn(run.providerSessionId));
248
- }
271
+ const success = await dependencies.runtime.abort(run.provider, sessionId);
249
272
 
250
273
  chatRunRegistry.completeRun(sessionId, {
251
274
  exitCode: success ? 0 : 1,
@@ -294,16 +317,9 @@ function handleChatSubscribe(
294
317
  chatRunRegistry.attachConnection(sessionId, ws);
295
318
  }
296
319
 
297
- // Pending approvals are tracked under the provider-native id inside the
298
- // Claude runtime; remap their sessionId so the client only sees app ids.
299
- const pendingPermissions = (run?.providerSessionId
300
- ? dependencies.getPendingApprovalsForSession(run.providerSessionId)
301
- : []
302
- ).map((approval) =>
303
- approval && typeof approval === 'object'
304
- ? { ...(approval as AnyRecord), sessionId }
305
- : approval,
306
- );
320
+ // Pending approvals are tracked under the app session id inside the
321
+ // Claude runtime, so they can be looked up directly.
322
+ const pendingPermissions = dependencies.runtime.getPendingApprovalsForSession(sessionId);
307
323
 
308
324
  sendJson(ws, {
309
325
  kind: 'chat_subscribed',
@@ -336,7 +352,7 @@ function handlePermissionResponse(data: AnyRecord, dependencies: ChatWebSocketDe
336
352
  return;
337
353
  }
338
354
 
339
- dependencies.resolveToolApproval(data.requestId, {
355
+ dependencies.runtime.resolveToolApproval(data.requestId, {
340
356
  allow: Boolean(data.allow),
341
357
  updatedInput: data.updatedInput,
342
358
  message: typeof data.message === 'string' ? data.message : undefined,
@@ -33,16 +33,79 @@ type PtySessionEntry = {
33
33
  const ptySessionsMap = new Map<string, PtySessionEntry>();
34
34
  const PTY_SESSION_TIMEOUT = 30 * 60 * 1000;
35
35
  const SHELL_URL_PARSE_BUFFER_LIMIT = 32768;
36
+ const ANSI_ESCAPE_SEQUENCE_REGEX = /\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~]|\][^\x07]*(?:\x07|\x1B\\))/g;
37
+ const TRAILING_URL_PUNCTUATION_REGEX = /[)\]}>.,;:!?]+$/;
38
+
39
+ function stripAnsiSequences(value: string): string {
40
+ return value.replace(ANSI_ESCAPE_SEQUENCE_REGEX, '');
41
+ }
42
+
43
+ function normalizeDetectedUrl(url: string): string | null {
44
+ const cleanedUrl = url.trim().replace(TRAILING_URL_PUNCTUATION_REGEX, '');
45
+ if (!cleanedUrl) {
46
+ return null;
47
+ }
48
+
49
+ try {
50
+ const parsedUrl = new URL(cleanedUrl);
51
+ if (parsedUrl.protocol !== 'http:' && parsedUrl.protocol !== 'https:') {
52
+ return null;
53
+ }
54
+ return parsedUrl.toString();
55
+ } catch {
56
+ return null;
57
+ }
58
+ }
59
+
60
+ function extractUrlsFromText(value: string): string[] {
61
+ const directMatches = value.match(/https?:\/\/[^\s<>"'`\\\x1b\x07]+/gi) ?? [];
62
+
63
+ // Terminal width can split a URL across lines, so valid URL characters on
64
+ // immediately following lines are joined before the URL is validated.
65
+ const wrappedMatches: string[] = [];
66
+ const urlContinuationPattern = /^[A-Za-z0-9\-._~:/?#\[\]@!$&'()*+,;=%]+$/;
67
+ const lines = value.split(/\r?\n/);
68
+ for (let lineIndex = 0; lineIndex < lines.length; lineIndex += 1) {
69
+ const line = lines[lineIndex].trim();
70
+ const startMatch = line.match(/https?:\/\/[^\s<>"'`\\\x1b\x07]+/i);
71
+ if (!startMatch) {
72
+ continue;
73
+ }
74
+
75
+ let combinedUrl = startMatch[0];
76
+ let continuationIndex = lineIndex + 1;
77
+ while (continuationIndex < lines.length) {
78
+ const continuation = lines[continuationIndex].trim();
79
+ if (!continuation || !urlContinuationPattern.test(continuation)) {
80
+ break;
81
+ }
82
+ combinedUrl += continuation;
83
+ continuationIndex += 1;
84
+ }
85
+
86
+ wrappedMatches.push(combinedUrl);
87
+ }
88
+
89
+ return Array.from(new Set([...directMatches, ...wrappedMatches]));
90
+ }
91
+
92
+ function shouldAutoOpenUrlFromOutput(value: string): boolean {
93
+ const normalizedOutput = value.toLowerCase();
94
+ return (
95
+ normalizedOutput.includes("browser didn't open") ||
96
+ normalizedOutput.includes('open this url') ||
97
+ normalizedOutput.includes('continue in your browser') ||
98
+ normalizedOutput.includes('press enter to open') ||
99
+ normalizedOutput.includes('open_url:')
100
+ );
101
+ }
36
102
 
37
103
  type ShellWebSocketDependencies = {
38
104
  resolveProviderSessionId: (
39
105
  sessionId: string,
40
106
  provider: string,
41
107
  ) => string | null | undefined;
42
- stripAnsiSequences: (content: string) => string;
43
- normalizeDetectedUrl: (url: string) => string | null;
44
- extractUrlsFromText: (content: string) => string[];
45
- shouldAutoOpenUrlFromOutput: (content: string) => boolean;
108
+ spawnPty?: typeof pty.spawn;
46
109
  };
47
110
 
48
111
  /**
@@ -220,7 +283,8 @@ function prioritizeUserNpmGlobalBin(env: NodeJS.ProcessEnv): { key: string; valu
220
283
  }
221
284
 
222
285
  /**
223
- * Handles websocket connections used by the standalone shell terminal UI.
286
+ * Used by this module's websocket gateway to connect the standalone Shell UI
287
+ * to a retained PTY while keeping process lifecycle ownership on the server.
224
288
  */
225
289
  export function handleShellConnection(
226
290
  ws: WebSocket,
@@ -284,6 +348,7 @@ export function handleShellConnection(
284
348
  shellProcess = existingSession.pty;
285
349
  if (existingSession.timeoutId) {
286
350
  clearTimeout(existingSession.timeoutId);
351
+ existingSession.timeoutId = null;
287
352
  }
288
353
 
289
354
  ws.send(
@@ -334,7 +399,7 @@ export function handleShellConnection(
334
399
  const termRows = readNumber(data.rows, 24);
335
400
  const prioritizedPath = prioritizeUserNpmGlobalBin(process.env);
336
401
 
337
- shellProcess = pty.spawn(shell, shellArgs, {
402
+ shellProcess = (dependencies.spawnPty ?? pty.spawn)(shell, shellArgs, {
338
403
  name: 'xterm-256color',
339
404
  cols: termCols,
340
405
  rows: termRows,
@@ -376,7 +441,7 @@ export function handleShellConnection(
376
441
 
377
442
  if (session.ws && session.ws.readyState === WebSocket.OPEN) {
378
443
  let outputData = chunk;
379
- const cleanChunk = dependencies.stripAnsiSequences(chunk);
444
+ const cleanChunk = stripAnsiSequences(chunk);
380
445
  urlDetectionBuffer = `${urlDetectionBuffer}${cleanChunk}`.slice(-SHELL_URL_PARSE_BUFFER_LIMIT);
381
446
 
382
447
  outputData = outputData.replace(
@@ -385,7 +450,7 @@ export function handleShellConnection(
385
450
  );
386
451
 
387
452
  const emitAuthUrl = (detectedUrl: string, autoOpen = false) => {
388
- const normalizedUrl = dependencies.normalizeDetectedUrl(detectedUrl);
453
+ const normalizedUrl = normalizeDetectedUrl(detectedUrl);
389
454
  if (!normalizedUrl) {
390
455
  return;
391
456
  }
@@ -403,8 +468,8 @@ export function handleShellConnection(
403
468
  }
404
469
  };
405
470
 
406
- const normalizedDetectedUrls = dependencies.extractUrlsFromText(urlDetectionBuffer)
407
- .map((url) => dependencies.normalizeDetectedUrl(url))
471
+ const normalizedDetectedUrls = extractUrlsFromText(urlDetectionBuffer)
472
+ .map((url) => normalizeDetectedUrl(url))
408
473
  .filter((url): url is string => Boolean(url));
409
474
 
410
475
  const dedupedDetectedUrls = Array.from(new Set(normalizedDetectedUrls)).filter(
@@ -415,7 +480,7 @@ export function handleShellConnection(
415
480
  dedupedDetectedUrls.forEach((url) => emitAuthUrl(url, false));
416
481
 
417
482
  if (
418
- dependencies.shouldAutoOpenUrlFromOutput(cleanChunk) &&
483
+ shouldAutoOpenUrlFromOutput(cleanChunk) &&
419
484
  dedupedDetectedUrls.length > 0
420
485
  ) {
421
486
  const bestUrl = dedupedDetectedUrls.reduce((longest, current) =>
@@ -522,9 +587,20 @@ export function handleShellConnection(
522
587
  return;
523
588
  }
524
589
 
590
+ // Mobile networks can deliver an old socket's close after its replacement
591
+ // has attached. Only the socket that currently owns the PTY may detach it.
592
+ if (session.ws !== ws) {
593
+ return;
594
+ }
595
+
525
596
  session.ws = null;
597
+ if (session.timeoutId) {
598
+ clearTimeout(session.timeoutId);
599
+ }
526
600
  session.timeoutId = setTimeout(() => {
527
- if (ptySessionsMap.get(ptySessionKey as string) !== session) {
601
+ // A reconnect may win just as this timer becomes runnable. Re-check the
602
+ // active socket so a queued cleanup can never kill a reattached PTY.
603
+ if (ptySessionsMap.get(ptySessionKey as string) !== session || session.ws !== null) {
528
604
  return;
529
605
  }
530
606
 
@@ -1,6 +1,6 @@
1
1
  import type { Server as HttpServer } from 'node:http';
2
2
 
3
- import { WebSocketServer, type VerifyClientCallbackSync } from 'ws';
3
+ import { WebSocket, WebSocketServer, type VerifyClientCallbackSync } from 'ws';
4
4
 
5
5
  import { handleChatConnection } from '@/modules/websocket/services/chat-websocket.service.js';
6
6
  import { verifyWebSocketClient } from '@/modules/websocket/services/websocket-auth.service.js';
@@ -16,9 +16,69 @@ type WebSocketServerDependencies = {
16
16
  getPluginPort: Parameters<typeof handlePluginWsProxy>[2];
17
17
  };
18
18
 
19
+ /**
20
+ * Used by this module's websocket gateway to keep active transports alive and
21
+ * close half-open connections so their route-specific clients can reconnect.
22
+ */
23
+ export function attachWebSocketHeartbeat(
24
+ ws: WebSocket,
25
+ intervalMs = 30_000,
26
+ scheduler = {
27
+ setInterval,
28
+ clearInterval,
29
+ },
30
+ ): () => void {
31
+ let isAlive = true;
32
+ let stopped = false;
33
+
34
+ const markAlive = () => {
35
+ isAlive = true;
36
+ };
37
+
38
+ const stopHeartbeat = () => {
39
+ if (stopped) {
40
+ return;
41
+ }
42
+
43
+ stopped = true;
44
+ scheduler.clearInterval(heartbeat);
45
+ ws.off('pong', markAlive);
46
+ ws.off('close', stopHeartbeat);
47
+ ws.off('error', stopHeartbeat);
48
+ };
49
+
50
+ ws.on('pong', markAlive);
51
+ ws.on('close', stopHeartbeat);
52
+ ws.on('error', stopHeartbeat);
53
+
54
+ const heartbeat = scheduler.setInterval(() => {
55
+ if (ws.readyState !== WebSocket.OPEN) {
56
+ return;
57
+ }
58
+
59
+ // A socket that did not answer the previous ping is half-open from the
60
+ // server's perspective. Terminating it emits close and lets clients resume.
61
+ if (!isAlive) {
62
+ stopHeartbeat();
63
+ ws.terminate();
64
+ return;
65
+ }
66
+
67
+ isAlive = false;
68
+ try {
69
+ ws.ping();
70
+ } catch {
71
+ stopHeartbeat();
72
+ ws.terminate();
73
+ }
74
+ }, intervalMs);
75
+
76
+ return stopHeartbeat;
77
+ }
78
+
19
79
  /**
20
80
  * Creates and wires the server-wide websocket gateway used for chat, shell, and
21
- * plugin proxy routes.
81
+ * plugin proxy routes. Exported through the websocket module for server startup.
22
82
  */
23
83
  export function createWebSocketServer(
24
84
  server: HttpServer,
@@ -32,23 +92,7 @@ export function createWebSocketServer(
32
92
  });
33
93
 
34
94
  wss.on('connection', (ws, request) => {
35
- // Keep WebSocket alive across reverse-proxy idle timeouts (Cloudflare ~100s,
36
- // AWS ALB 60s, nginx 60s, etc.). Without app-level pings these connections
37
- // are silently torn down even when the UI is active, causing repeated
38
- // reconnect cycles. ws library heartbeat is opt-in.
39
- const HEARTBEAT_INTERVAL_MS = 30_000;
40
- const heartbeat = setInterval(() => {
41
- if (ws.readyState === ws.OPEN) {
42
- try {
43
- ws.ping();
44
- } catch {
45
- // socket may have been closed concurrently — interval will be cleared below
46
- }
47
- }
48
- }, HEARTBEAT_INTERVAL_MS);
49
- const stopHeartbeat = () => clearInterval(heartbeat);
50
- ws.on('close', stopHeartbeat);
51
- ws.on('error', stopHeartbeat);
95
+ attachWebSocketHeartbeat(ws);
52
96
 
53
97
  const incomingRequest = request as AuthenticatedWebSocketRequest;
54
98
  const url = incomingRequest.url ?? '/';
@@ -3,7 +3,10 @@ import os from 'node:os';
3
3
  import path from 'node:path';
4
4
  import test from 'node:test';
5
5
 
6
- import { filterImagesToUploadStore } from '@/modules/websocket/services/chat-websocket.service.js';
6
+ import {
7
+ filterAttachmentsToUploadStore,
8
+ filterImagesToUploadStore,
9
+ } from '@/modules/websocket/services/chat-websocket.service.js';
7
10
 
8
11
  const STORE = path.join(os.tmpdir(), 'cloudcli-assets-store');
9
12
 
@@ -37,8 +40,20 @@ test('paths outside the store, traversal, and subdirs are dropped', () => {
37
40
  assert.deepEqual(result, []);
38
41
  });
39
42
 
40
- test('malformed payloads yield no images', () => {
43
+ test('malformed payloads yield no attachments', () => {
41
44
  assert.deepEqual(filterImagesToUploadStore(undefined, STORE), []);
42
45
  assert.deepEqual(filterImagesToUploadStore('nope', STORE), []);
43
46
  assert.deepEqual(filterImagesToUploadStore([{ name: 'no-path' }, 42], STORE), []);
44
47
  });
48
+
49
+ test('general files inside the upload store preserve their metadata', () => {
50
+ const inside = path.join(STORE, 'brief.pdf');
51
+ const result = filterAttachmentsToUploadStore(
52
+ [{ path: inside, name: 'brief.pdf', mimeType: 'application/pdf', size: 2048 }],
53
+ STORE,
54
+ );
55
+
56
+ assert.deepEqual(result, [
57
+ { path: inside, name: 'brief.pdf', mimeType: 'application/pdf', size: 2048 },
58
+ ]);
59
+ });
@@ -0,0 +1,119 @@
1
+ import assert from 'node:assert/strict';
2
+ import { EventEmitter } from 'node:events';
3
+ import test from 'node:test';
4
+
5
+ import { WebSocket } from 'ws';
6
+
7
+ import { handleShellConnection } from '@/modules/websocket/services/shell-websocket.service.js';
8
+
9
+ function createFakeSocket() {
10
+ const socket = new EventEmitter() as EventEmitter & {
11
+ readyState: number;
12
+ frames: string[];
13
+ send: (data: string) => void;
14
+ };
15
+ socket.readyState = WebSocket.OPEN;
16
+ socket.frames = [];
17
+ socket.send = (data: string) => socket.frames.push(data);
18
+ return socket;
19
+ }
20
+
21
+ function createFakePty() {
22
+ let dataListener: ((data: string) => void) | null = null;
23
+ let exitListener: ((event: { exitCode: number; signal?: number }) => void) | null = null;
24
+
25
+ return {
26
+ killed: false,
27
+ onData(listener: (data: string) => void) {
28
+ dataListener = listener;
29
+ return { dispose: () => undefined };
30
+ },
31
+ onExit(listener: (event: { exitCode: number; signal?: number }) => void) {
32
+ exitListener = listener;
33
+ return { dispose: () => undefined };
34
+ },
35
+ emitData(data: string) {
36
+ dataListener?.(data);
37
+ },
38
+ emitExit() {
39
+ exitListener?.({ exitCode: 0 });
40
+ },
41
+ write() {},
42
+ resize() {},
43
+ kill() {
44
+ this.killed = true;
45
+ },
46
+ };
47
+ }
48
+
49
+ test('a stale socket close cannot detach the socket that replaced it', () => {
50
+ const pty = createFakePty();
51
+ const dependencies = {
52
+ resolveProviderSessionId: () => null,
53
+ spawnPty: () => pty as never,
54
+ };
55
+ const initMessage = JSON.stringify({
56
+ type: 'init',
57
+ projectPath: process.cwd(),
58
+ sessionId: `stale-close-${Date.now()}`,
59
+ hasSession: false,
60
+ provider: 'plain-shell',
61
+ isPlainShell: true,
62
+ initialCommand: 'test-command',
63
+ });
64
+
65
+ const firstSocket = createFakeSocket();
66
+ handleShellConnection(firstSocket as never, dependencies);
67
+ firstSocket.emit('message', initMessage);
68
+
69
+ const replacementSocket = createFakeSocket();
70
+ handleShellConnection(replacementSocket as never, dependencies);
71
+ replacementSocket.emit('message', initMessage);
72
+ replacementSocket.frames.length = 0;
73
+
74
+ // This ordering reproduces a delayed close from a backgrounded mobile tab.
75
+ firstSocket.emit('close');
76
+ pty.emitData('output-after-stale-close');
77
+
78
+ assert.equal(pty.killed, false);
79
+ assert.equal(replacementSocket.frames.length, 1);
80
+ assert.match(replacementSocket.frames[0], /output-after-stale-close/);
81
+
82
+ pty.emitExit();
83
+ });
84
+
85
+ test('shell output detects and normalizes a wrapped authentication URL', () => {
86
+ const pty = createFakePty();
87
+ const socket = createFakeSocket();
88
+ const dependencies = {
89
+ resolveProviderSessionId: () => null,
90
+ spawnPty: () => pty as never,
91
+ };
92
+
93
+ handleShellConnection(socket as never, dependencies);
94
+ socket.emit(
95
+ 'message',
96
+ JSON.stringify({
97
+ type: 'init',
98
+ projectPath: process.cwd(),
99
+ sessionId: `wrapped-url-${Date.now()}`,
100
+ hasSession: false,
101
+ provider: 'plain-shell',
102
+ isPlainShell: true,
103
+ initialCommand: 'test-command',
104
+ })
105
+ );
106
+ socket.frames.length = 0;
107
+
108
+ pty.emitData("Continue in your browser: https://example.com/authorize?\ncode=abc\x1b[0m");
109
+
110
+ const frames = socket.frames.map((frame) => JSON.parse(frame) as Record<string, unknown>);
111
+ const authenticationFrame = frames.find((frame) => frame.type === 'auth_url');
112
+ assert.deepEqual(authenticationFrame, {
113
+ type: 'auth_url',
114
+ url: 'https://example.com/authorize?code=abc',
115
+ autoOpen: false,
116
+ });
117
+
118
+ pty.emitExit();
119
+ });