wind-agent-cli 1.37.2 → 1.38.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (510) hide show
  1. package/LICENSE +718 -718
  2. package/README.md +17 -2
  3. package/dist/api-docs.html +901 -901
  4. package/dist/assets/{CodeEditor-B2Wf7Fg0.js → CodeEditor-FvgsnJ7J.js} +6 -6
  5. package/dist/assets/FileTree-NLULrBDi.js +1 -0
  6. package/dist/assets/GitPanel-51KUBbEW.js +4 -0
  7. package/dist/assets/{MarkdownPreview-C0tqYnVu.js → MarkdownPreview-PEEWVWeS.js} +17 -27
  8. package/dist/assets/Shell-BEx66cW_.js +76 -0
  9. package/dist/assets/Shell-DrlLKa8f.css +1 -0
  10. package/dist/assets/StandaloneShell-BkuUCPZP.js +1 -0
  11. package/dist/assets/TaskMasterPanel-DfQG80SF.js +2 -0
  12. package/dist/assets/abnfDiagram-N423BO3Z-Cc0ul87U.js +1 -0
  13. package/dist/assets/arc-BAuugHw_.js +1 -0
  14. package/dist/assets/architectureDiagram-T3A2C74G-DA3G7QZD.js +36 -0
  15. package/dist/assets/{blockDiagram-VBNYF7ZC-DpaEf3mv.js → blockDiagram-VBNYF7ZC-tJ12IJs3.js} +4 -4
  16. package/dist/assets/c4Diagram-5PPSVZJV-5BTMCXL4.js +10 -0
  17. package/dist/assets/channel-BGDgNNQz.js +1 -0
  18. package/dist/assets/{chunk-2GRJ4B5K-DoSdNo8u.js → chunk-2GRJ4B5K-BPaI06Ou.js} +1 -1
  19. package/dist/assets/{chunk-2Q5K7J3B-CE7I3uMn.js → chunk-2Q5K7J3B-HV4zmsEe.js} +1 -1
  20. package/dist/assets/chunk-5RXB4S5H-BAwutJoU.js +231 -0
  21. package/dist/assets/{chunk-5VM5RSS4-B55XgqHZ.js → chunk-5VM5RSS4-B9QAJPrv.js} +1 -1
  22. package/dist/assets/{chunk-6Q2QTUOP-BS1KpefW.js → chunk-6Q2QTUOP-BOIW8bzI.js} +1 -1
  23. package/dist/assets/chunk-GF5L2VYU-DD13o00-.js +206 -0
  24. package/dist/assets/{chunk-JWPE2WC7-DOtkNhMm.js → chunk-JWPE2WC7-CFBm23VK.js} +1 -1
  25. package/dist/assets/{chunk-KBJHAD2P-BXLjviqM.js → chunk-KBJHAD2P-C0i0v8Op.js} +1 -1
  26. package/dist/assets/{chunk-RYQCIY6F-C50fjNNz.js → chunk-RYQCIY6F-DfeK0taV.js} +1 -1
  27. package/dist/assets/{chunk-XXDRQBXY-QBYqCkb7.js → chunk-XXDRQBXY-BKWvf-QE.js} +1 -1
  28. package/dist/assets/classDiagram-JCYQIIEL-C2SNM4oy.js +1 -0
  29. package/dist/assets/classDiagram-v2-OCEON4UE-C2SNM4oy.js +1 -0
  30. package/dist/assets/cose-bilkent-JH36ORCC-CBE8oM7L.js +1 -0
  31. package/dist/assets/{cynefin-VYW2F7L2-DnJa_OlQ.js → cynefin-VYW2F7L2-DziGp35A.js} +11 -23
  32. package/dist/assets/cynefinDiagram-MW4NZA55-CNvtHl39.js +62 -0
  33. package/dist/assets/cytoscape.esm-yzknjiTM.js +321 -0
  34. package/dist/assets/dagre-VZM6K2ZE-Bz9lsU7m.js +4 -0
  35. package/dist/assets/{diagram-7IWD3JNH-pFzs9HEu.js → diagram-7IWD3JNH-CV7D3VSd.js} +5 -5
  36. package/dist/assets/{diagram-B4RE2ZJO-CcehQC7x.js → diagram-B4RE2ZJO-Dj4EjDNC.js} +2 -2
  37. package/dist/assets/diagram-LBJQPF4R-wJ6FHvf-.js +24 -0
  38. package/dist/assets/diagram-Q27KOJAE-DMFYMhOB.js +24 -0
  39. package/dist/assets/diagram-UB23O5K3-DeBSbWAI.js +41 -0
  40. package/dist/assets/ebnfDiagram-BXEA7PRR-2NnJSGhT.js +1 -0
  41. package/dist/assets/erDiagram-JOGREHBK-DrSlg8hr.js +85 -0
  42. package/dist/assets/feature-chat-CvOCyaLe.js +88 -0
  43. package/dist/assets/feature-workspace-CAB6bgPy.js +39 -0
  44. package/dist/assets/{flowDiagram-UKHOOZJN-CJV8fQPR.js → flowDiagram-UKHOOZJN-C0pejU6W.js} +4 -4
  45. package/dist/assets/ganttDiagram-PKOTCBZU-BM0VCUUz.js +292 -0
  46. package/dist/assets/gitGraphDiagram-DS77QQ5N-SbLG0c5_.js +106 -0
  47. package/dist/assets/graph-DOmOIIwC.js +1 -0
  48. package/dist/assets/{index-GFhXZyl2.js → index-BflGfU1C.js} +3 -3
  49. package/dist/assets/index-Btkf0_rU.js +1 -0
  50. package/dist/assets/index-Cr5MbHuj.css +1 -0
  51. package/dist/assets/index-Gqazqfs3.js +1 -0
  52. package/dist/assets/index-nyrkoQK6.js +35 -0
  53. package/dist/assets/infoDiagram-6WML65LV-Cf5DSa66.js +2 -0
  54. package/dist/assets/ishikawaDiagram-WSZJBQD7-DwyFutKy.js +70 -0
  55. package/dist/assets/journeyDiagram-NVQOT4AX-Df0FRhLo.js +139 -0
  56. package/dist/assets/{kanban-definition-27J2QSJJ-B3iL0ztJ.js → kanban-definition-27J2QSJJ-HmpbjrZz.js} +6 -6
  57. package/dist/assets/{layout-D6BstSzF.js → layout-D-LzfAck.js} +1 -1
  58. package/dist/assets/{linear-Cwe9dEKA.js → linear-DOM01LBe.js} +1 -1
  59. package/dist/assets/map-DxJ2ADlA.js +1 -0
  60. package/dist/assets/mermaid.core-C2Ma-_S6.js +306 -0
  61. package/dist/assets/{mindmap-definition-FAOFIHXS-BgfBYMzr.js → mindmap-definition-FAOFIHXS-B4GOIn76.js} +4 -4
  62. package/dist/assets/pegDiagram-VL7TDLO6-BHur7aH2.js +1 -0
  63. package/dist/assets/{pieDiagram-7S7Q4E2Y-DeSrxc4I.js → pieDiagram-7S7Q4E2Y-Cw0UELLV.js} +2 -2
  64. package/dist/assets/quadrantDiagram-CIZ2JOQS-CB2BrX3g.js +7 -0
  65. package/dist/assets/railroadDiagram-AXF67PYL-DPYZYmLX.js +1 -0
  66. package/dist/assets/requirementDiagram-LRYGKXZP-DSX4IJ1n.js +84 -0
  67. package/dist/assets/sankeyDiagram-W5VNT64P-TenC3ZQo.js +40 -0
  68. package/dist/assets/sequenceDiagram-SI44F4Z6-DltGsf3F.js +162 -0
  69. package/dist/assets/sizeCapture-X5ZJPWSS-D7llwbJZ.js +1 -0
  70. package/dist/assets/stateDiagram-OKZ733FA-C-hAvbnY.js +1 -0
  71. package/dist/assets/stateDiagram-v2-UEYNNEHI-DaYSFcRQ.js +1 -0
  72. package/dist/assets/swimlanes-SLNWSIFB-DWD30fv6.js +2 -0
  73. package/dist/assets/swimlanesDiagram-ULZ7WXOC-LTkF2T6t.js +8 -0
  74. package/dist/assets/timeline-definition-Z64GVDOM-quWmimSE.js +120 -0
  75. package/dist/assets/vendor-data-DsEZeNPr.js +4 -0
  76. package/dist/assets/vendor-i18n-B6HjLCnN.js +1 -0
  77. package/dist/assets/vendor-markdown-DkITEES2.js +298 -0
  78. package/dist/assets/vendor-react-C2GTiUCy.js +10 -0
  79. package/dist/assets/vendor-ui-CAUHSABu.js +49 -0
  80. package/dist/assets/vennDiagram-T6HMQDX7-aUQcm8h5.js +34 -0
  81. package/dist/assets/{wardleyDiagram-T6FBY63Y-BNCpsFZV.js → wardleyDiagram-T6FBY63Y-BZ0bwsFQ.js} +2 -2
  82. package/dist/assets/workbox-window.prod.es5-BBnX5xw4.js +2 -0
  83. package/dist/assets/xychartDiagram-ELKLHX3M-DOVzAq63.js +7 -0
  84. package/dist/clear-cache.html +85 -85
  85. package/dist/convert-icons.md +52 -52
  86. package/dist/favicon.png +0 -0
  87. package/dist/favicon.svg +8 -8
  88. package/dist/generate-icons.js +48 -48
  89. package/dist/icons/codex-white.svg +3 -3
  90. package/dist/icons/codex.svg +3 -3
  91. package/dist/icons/cursor-white.svg +11 -11
  92. package/dist/icons/icon-128x128.png +0 -0
  93. package/dist/icons/icon-128x128.svg +11 -11
  94. package/dist/icons/icon-144x144.png +0 -0
  95. package/dist/icons/icon-144x144.svg +11 -11
  96. package/dist/icons/icon-152x152.png +0 -0
  97. package/dist/icons/icon-152x152.svg +11 -11
  98. package/dist/icons/icon-180x180.png +0 -0
  99. package/dist/icons/icon-192x192.png +0 -0
  100. package/dist/icons/icon-192x192.svg +11 -11
  101. package/dist/icons/icon-384x384.png +0 -0
  102. package/dist/icons/icon-384x384.svg +11 -11
  103. package/dist/icons/icon-512x512.png +0 -0
  104. package/dist/icons/icon-512x512.svg +11 -11
  105. package/dist/icons/icon-72x72.png +0 -0
  106. package/dist/icons/icon-72x72.svg +11 -11
  107. package/dist/icons/icon-96x96.png +0 -0
  108. package/dist/icons/icon-96x96.svg +11 -11
  109. package/dist/icons/icon-maskable-192x192.png +0 -0
  110. package/dist/icons/icon-maskable-512x512.png +0 -0
  111. package/dist/icons/icon-template.svg +11 -11
  112. package/dist/icons/new-icon.png +0 -0
  113. package/dist/index.html +48 -58
  114. package/dist/logo-128.png +0 -0
  115. package/dist/logo-256.png +0 -0
  116. package/dist/logo.svg +17 -17
  117. package/dist/manifest.webmanifest +1 -0
  118. package/dist/push-sw.js +48 -0
  119. package/dist/screenshots/setup-screen-mobile.png +0 -0
  120. package/dist/screenshots/setup-screen.png +0 -0
  121. package/dist/sw.js +1 -124
  122. package/dist/workbox-ae5c0bb2.js +1 -0
  123. package/dist-server/server/index.js +1 -1
  124. package/dist-server/server/index.js.map +1 -1
  125. package/dist-server/server/load-env.js +4 -2
  126. package/dist-server/server/load-env.js.map +1 -1
  127. package/dist-server/server/modules/agent/agent.routes.js +1 -1
  128. package/dist-server/server/modules/agent/agent.routes.js.map +1 -1
  129. package/dist-server/server/modules/browser-use/browser-use.service.js +1 -1
  130. package/dist-server/server/modules/browser-use/browser-use.service.js.map +1 -1
  131. package/dist-server/server/modules/cli/cli.service.js +37 -37
  132. package/dist-server/server/modules/cli/cli.service.js.map +1 -1
  133. package/dist-server/server/modules/cli/sandbox.service.js +43 -43
  134. package/dist-server/server/modules/cli/sandbox.service.js.map +1 -1
  135. package/dist-server/server/modules/cli/tests/cli-environment-bootstrap.test.js +74 -74
  136. package/dist-server/server/modules/commands/commands.routes.js +25 -25
  137. package/dist-server/server/modules/commands/commands.routes.js.map +1 -1
  138. package/dist-server/server/modules/commands/tests/commands.test.js +1 -1
  139. package/dist-server/server/modules/commands/tests/commands.test.js.map +1 -1
  140. package/dist-server/server/modules/database/connection.js +1 -1
  141. package/dist-server/server/modules/database/connection.js.map +1 -1
  142. package/dist-server/server/modules/database/migrations.js +190 -190
  143. package/dist-server/server/modules/database/repositories/api-keys.js +3 -3
  144. package/dist-server/server/modules/database/repositories/github-tokens.js +2 -2
  145. package/dist-server/server/modules/database/repositories/notification-channel-endpoints.js +28 -28
  146. package/dist-server/server/modules/database/repositories/notification-preferences.js +4 -4
  147. package/dist-server/server/modules/database/repositories/projects.db.js +61 -61
  148. package/dist-server/server/modules/database/repositories/provider-models.js +35 -35
  149. package/dist-server/server/modules/database/repositories/push-subscriptions.js +5 -5
  150. package/dist-server/server/modules/database/repositories/scan-state.db.js +5 -5
  151. package/dist-server/server/modules/database/repositories/sessions.db.js +93 -93
  152. package/dist-server/server/modules/database/schema.js +177 -177
  153. package/dist-server/server/modules/database/tests/projects.db.integration.test.js +3 -2
  154. package/dist-server/server/modules/database/tests/projects.db.integration.test.js.map +1 -1
  155. package/dist-server/server/modules/database/tests/provider-models.db.integration.test.js +6 -6
  156. package/dist-server/server/modules/database/tests/sessions-provider-mapping.test.js +4 -3
  157. package/dist-server/server/modules/database/tests/sessions-provider-mapping.test.js.map +1 -1
  158. package/dist-server/server/modules/database/tests/sessions.db.integration.test.js +2 -1
  159. package/dist-server/server/modules/database/tests/sessions.db.integration.test.js.map +1 -1
  160. package/dist-server/server/modules/git/git.routes.js +17 -17
  161. package/dist-server/server/modules/notifications/services/notification-orchestrator.service.js +1 -1
  162. package/dist-server/server/modules/notifications/services/notification-orchestrator.service.js.map +1 -1
  163. package/dist-server/server/modules/notifications/vapid-keys.service.js +1 -1
  164. package/dist-server/server/modules/notifications/vapid-keys.service.js.map +1 -1
  165. package/dist-server/server/modules/projects/tests/project-management.service.test.js +2 -1
  166. package/dist-server/server/modules/projects/tests/project-management.service.test.js.map +1 -1
  167. package/dist-server/server/modules/providers/list/codex/codex-app-server.client.js +2 -2
  168. package/dist-server/server/modules/providers/list/codex/codex-app-server.client.js.map +1 -1
  169. package/dist-server/server/modules/providers/list/codex/codex-models.provider.js +1 -1
  170. package/dist-server/server/modules/providers/list/codex/codex-models.provider.js.map +1 -1
  171. package/dist-server/server/modules/providers/list/cursor/cursor-models.provider.js +1 -1
  172. package/dist-server/server/modules/providers/list/cursor/cursor-models.provider.js.map +1 -1
  173. package/dist-server/server/modules/providers/list/cursor/cursor-session-synchronizer.provider.js.map +1 -1
  174. package/dist-server/server/modules/providers/list/opencode/opencode-models.provider.js +13 -13
  175. package/dist-server/server/modules/providers/list/opencode/opencode-runtime.provider.js +9 -9
  176. package/dist-server/server/modules/providers/list/opencode/opencode-runtime.provider.test.js +17 -17
  177. package/dist-server/server/modules/providers/list/opencode/opencode-session-synchronizer.provider.js +25 -25
  178. package/dist-server/server/modules/providers/list/opencode/opencode-sessions.provider.js +27 -27
  179. package/dist-server/server/modules/providers/services/provider-token-usage.service.js +9 -9
  180. package/dist-server/server/modules/providers/services/sessions.service.js +1 -1
  181. package/dist-server/server/modules/providers/tests/claude-auth.test.js +8 -0
  182. package/dist-server/server/modules/providers/tests/claude-auth.test.js.map +1 -1
  183. package/dist-server/server/modules/providers/tests/codex-sessions.test.js +1 -1
  184. package/dist-server/server/modules/providers/tests/codex-sessions.test.js.map +1 -1
  185. package/dist-server/server/modules/providers/tests/mcp.test.js +3 -3
  186. package/dist-server/server/modules/providers/tests/opencode-sessions.test.js +98 -98
  187. package/dist-server/server/modules/providers/tests/opencode-sessions.test.js.map +1 -1
  188. package/dist-server/server/modules/providers/tests/provider-token-usage.service.test.js +18 -18
  189. package/dist-server/server/modules/providers/tests/provider.routes.test.js +1 -1
  190. package/dist-server/server/modules/providers/tests/provider.routes.test.js.map +1 -1
  191. package/dist-server/server/modules/system/system.service.js +1 -1
  192. package/dist-server/server/modules/system/system.service.js.map +1 -1
  193. package/dist-server/server/modules/system/tests/system.service.test.js +1 -1
  194. package/dist-server/server/modules/system/tests/system.service.test.js.map +1 -1
  195. package/dist-server/server/modules/taskmaster/taskmaster.routes.js +423 -422
  196. package/dist-server/server/modules/taskmaster/taskmaster.routes.js.map +1 -1
  197. package/dist-server/server/modules/websocket/services/shell-websocket.service.js +1 -1
  198. package/dist-server/server/modules/websocket/services/shell-websocket.service.js.map +1 -1
  199. package/dist-server/server/modules/worktrees/services/worktree-list.service.js +1 -1
  200. package/dist-server/server/modules/worktrees/services/worktree-open.service.js +1 -1
  201. package/dist-server/server/modules/worktrees/services/worktree-remove.service.js +1 -1
  202. package/electron/cloud.js +260 -260
  203. package/electron/desktopNotifications.js +378 -378
  204. package/electron/desktopWindow.js +766 -766
  205. package/electron/launcher/index.html +14 -14
  206. package/electron/launcher/launcher.css +801 -801
  207. package/electron/launcher/launcher.js +687 -687
  208. package/electron/localServer.js +549 -549
  209. package/electron/main.js +944 -944
  210. package/electron/preload.cjs +60 -60
  211. package/electron/scripts/generate-macos-icon.js +62 -62
  212. package/electron/serverInstaller.js +277 -277
  213. package/electron/tabs.js +87 -87
  214. package/electron/viewHost.js +331 -331
  215. package/package.json +262 -247
  216. package/public/api-docs.html +901 -901
  217. package/scripts/build-client.mjs +30 -0
  218. package/scripts/fix-node-pty.js +67 -67
  219. package/scripts/generate-pwa-icons.ps1 +33 -0
  220. package/scripts/promote-dist-server.mjs +50 -50
  221. package/scripts/release/build-server-bundle.js +176 -176
  222. package/scripts/release/prepare-desktop-app.js +152 -152
  223. package/scripts/verify-pwa-build.mjs +37 -0
  224. package/server/index.ts +399 -399
  225. package/server/load-env.ts +49 -47
  226. package/server/modules/agent/agent.module.ts +52 -52
  227. package/server/modules/agent/agent.routes.ts +1287 -1287
  228. package/server/modules/agent/index.ts +2 -2
  229. package/server/modules/agent/tests/agent.routes.test.ts +205 -205
  230. package/server/modules/assets/assets.routes.ts +150 -150
  231. package/server/modules/assets/index.ts +3 -3
  232. package/server/modules/assets/services/image-assets.service.ts +127 -127
  233. package/server/modules/assets/tests/image-assets.service.test.ts +74 -74
  234. package/server/modules/auth/auth.middleware.ts +163 -163
  235. package/server/modules/auth/auth.module.ts +38 -38
  236. package/server/modules/auth/auth.routes.ts +57 -57
  237. package/server/modules/auth/auth.service.ts +155 -155
  238. package/server/modules/auth/index.ts +9 -9
  239. package/server/modules/auth/tests/auth.service.test.ts +95 -95
  240. package/server/modules/browser-use/browser-use-mcp.routes.ts +120 -120
  241. package/server/modules/browser-use/browser-use-mcp.ts +386 -386
  242. package/server/modules/browser-use/browser-use-runtime.ts +10 -10
  243. package/server/modules/browser-use/browser-use.routes.ts +96 -96
  244. package/server/modules/browser-use/browser-use.service.ts +836 -836
  245. package/server/modules/browser-use/index.ts +11 -11
  246. package/server/modules/browser-use/tests/browser-use.service.test.ts +10 -10
  247. package/server/modules/cli/cli.module.ts +93 -93
  248. package/server/modules/cli/cli.service.ts +248 -248
  249. package/server/modules/cli/cli.ts +19 -19
  250. package/server/modules/cli/index.ts +2 -2
  251. package/server/modules/cli/sandbox.service.ts +398 -398
  252. package/server/modules/cli/tests/cli-environment-bootstrap.test.ts +186 -186
  253. package/server/modules/cli/tests/cli.service.test.ts +86 -86
  254. package/server/modules/cli/tests/sandbox.service.test.ts +58 -58
  255. package/server/modules/commands/commands.module.ts +22 -22
  256. package/server/modules/commands/commands.routes.ts +587 -587
  257. package/server/modules/commands/index.ts +2 -2
  258. package/server/modules/commands/tests/commands.test.ts +110 -110
  259. package/server/modules/database/connection.ts +144 -143
  260. package/server/modules/database/index.ts +17 -17
  261. package/server/modules/database/init-db.ts +17 -17
  262. package/server/modules/database/migrations.ts +515 -515
  263. package/server/modules/database/repositories/api-keys.ts +119 -119
  264. package/server/modules/database/repositories/app-config.ts +53 -53
  265. package/server/modules/database/repositories/credentials.ts +106 -106
  266. package/server/modules/database/repositories/github-tokens.ts +100 -100
  267. package/server/modules/database/repositories/notification-channel-endpoints.ts +153 -153
  268. package/server/modules/database/repositories/notification-preferences.ts +117 -117
  269. package/server/modules/database/repositories/projects.db.ts +196 -196
  270. package/server/modules/database/repositories/provider-models.ts +168 -168
  271. package/server/modules/database/repositories/push-subscriptions.ts +80 -80
  272. package/server/modules/database/repositories/scan-state.db.ts +42 -42
  273. package/server/modules/database/repositories/sessions.db.ts +524 -524
  274. package/server/modules/database/repositories/users.ts +140 -140
  275. package/server/modules/database/repositories/vapid-keys.ts +57 -57
  276. package/server/modules/database/schema.ts +210 -210
  277. package/server/modules/database/tests/projects.db.integration.test.ts +73 -72
  278. package/server/modules/database/tests/provider-models.db.integration.test.ts +125 -125
  279. package/server/modules/database/tests/sessions-provider-mapping.test.ts +110 -109
  280. package/server/modules/database/tests/sessions.db.integration.test.ts +117 -116
  281. package/server/modules/file-tree/file-tree.module.ts +116 -116
  282. package/server/modules/file-tree/file-tree.routes.ts +258 -258
  283. package/server/modules/file-tree/file-tree.service.ts +675 -675
  284. package/server/modules/file-tree/index.ts +2 -2
  285. package/server/modules/file-tree/tests/file-tree.routes.test.ts +157 -157
  286. package/server/modules/file-tree/tests/file-tree.service.test.ts +371 -371
  287. package/server/modules/git/git-branch.service.ts +43 -43
  288. package/server/modules/git/git-parsing.service.ts +66 -66
  289. package/server/modules/git/git.module.ts +22 -22
  290. package/server/modules/git/git.routes.ts +1596 -1596
  291. package/server/modules/git/index.ts +2 -2
  292. package/server/modules/git/tests/git-branch.service.test.ts +60 -60
  293. package/server/modules/git/tests/git-init.routes.test.ts +132 -132
  294. package/server/modules/git/tests/git.test.ts +106 -106
  295. package/server/modules/notifications/index.ts +24 -24
  296. package/server/modules/notifications/notifications.routes.ts +127 -127
  297. package/server/modules/notifications/services/desktop-notification-clients.service.ts +124 -124
  298. package/server/modules/notifications/services/notification-orchestrator.service.js +310 -310
  299. package/server/modules/notifications/tests/notification-orchestrator.integration.test.ts +55 -55
  300. package/server/modules/notifications/vapid-keys.service.ts +38 -38
  301. package/server/modules/notifications/websocket/desktop-notifications-websocket.service.ts +109 -109
  302. package/server/modules/plugins/index.ts +9 -9
  303. package/server/modules/plugins/plugin-process.service.ts +217 -217
  304. package/server/modules/plugins/plugin-registry.service.ts +459 -459
  305. package/server/modules/plugins/plugins.module.ts +33 -33
  306. package/server/modules/plugins/plugins.routes.ts +66 -66
  307. package/server/modules/plugins/plugins.service.ts +147 -147
  308. package/server/modules/plugins/tests/plugins.service.test.ts +32 -32
  309. package/server/modules/projects/index.ts +11 -11
  310. package/server/modules/projects/projects.routes.ts +273 -273
  311. package/server/modules/projects/services/project-clone.service.ts +323 -323
  312. package/server/modules/projects/services/project-delete.service.ts +90 -90
  313. package/server/modules/projects/services/project-management.service.ts +144 -144
  314. package/server/modules/projects/services/project-star.service.ts +78 -78
  315. package/server/modules/projects/services/projects-has-taskmaster.service.ts +248 -248
  316. package/server/modules/projects/services/projects-with-sessions-fetch.service.ts +314 -314
  317. package/server/modules/projects/tests/project-clone.service.test.ts +183 -183
  318. package/server/modules/projects/tests/project-management.service.test.ts +118 -117
  319. package/server/modules/projects/tests/project-star.service.test.ts +123 -123
  320. package/server/modules/projects/tests/projects-has-taskmaster.service.test.ts +105 -105
  321. package/server/modules/providers/README.md +380 -380
  322. package/server/modules/providers/index.ts +10 -10
  323. package/server/modules/providers/list/claude/claude-auth.provider.ts +160 -160
  324. package/server/modules/providers/list/claude/claude-mcp.provider.ts +135 -135
  325. package/server/modules/providers/list/claude/claude-models.provider.ts +294 -294
  326. package/server/modules/providers/list/claude/claude-runtime.provider.js +1068 -1068
  327. package/server/modules/providers/list/claude/claude-session-synchronizer.provider.ts +203 -203
  328. package/server/modules/providers/list/claude/claude-sessions.provider.ts +684 -684
  329. package/server/modules/providers/list/claude/claude-skills.provider.ts +265 -265
  330. package/server/modules/providers/list/claude/claude.provider.ts +30 -30
  331. package/server/modules/providers/list/codex/codex-app-server.client.ts +2 -2
  332. package/server/modules/providers/list/codex/codex-auth.provider.ts +100 -100
  333. package/server/modules/providers/list/codex/codex-mcp.provider.ts +135 -135
  334. package/server/modules/providers/list/codex/codex-models.provider.ts +123 -123
  335. package/server/modules/providers/list/codex/codex-runtime.provider.js +533 -533
  336. package/server/modules/providers/list/codex/codex-session-synchronizer.provider.ts +203 -203
  337. package/server/modules/providers/list/codex/codex-sessions.provider.ts +938 -938
  338. package/server/modules/providers/list/codex/codex-skills.provider.ts +73 -73
  339. package/server/modules/providers/list/codex/codex.provider.ts +28 -28
  340. package/server/modules/providers/list/cursor/cursor-auth.provider.ts +143 -143
  341. package/server/modules/providers/list/cursor/cursor-mcp.provider.ts +108 -108
  342. package/server/modules/providers/list/cursor/cursor-models.provider.ts +119 -119
  343. package/server/modules/providers/list/cursor/cursor-runtime.provider.js +385 -385
  344. package/server/modules/providers/list/cursor/cursor-session-synchronizer.provider.ts +158 -159
  345. package/server/modules/providers/list/cursor/cursor-sessions.provider.ts +654 -654
  346. package/server/modules/providers/list/cursor/cursor-skills.provider.ts +39 -39
  347. package/server/modules/providers/list/cursor/cursor.provider.ts +30 -30
  348. package/server/modules/providers/list/opencode/opencode-auth.provider.ts +110 -110
  349. package/server/modules/providers/list/opencode/opencode-mcp.provider.ts +228 -228
  350. package/server/modules/providers/list/opencode/opencode-models.provider.ts +203 -203
  351. package/server/modules/providers/list/opencode/opencode-runtime.provider.js +434 -434
  352. package/server/modules/providers/list/opencode/opencode-runtime.provider.test.js +261 -261
  353. package/server/modules/providers/list/opencode/opencode-session-synchronizer.provider.ts +179 -179
  354. package/server/modules/providers/list/opencode/opencode-sessions.provider.ts +500 -500
  355. package/server/modules/providers/list/opencode/opencode-skills.provider.ts +78 -78
  356. package/server/modules/providers/list/opencode/opencode.provider.ts +30 -30
  357. package/server/modules/providers/provider.registry.ts +36 -36
  358. package/server/modules/providers/provider.routes.ts +877 -877
  359. package/server/modules/providers/services/mcp.service.ts +109 -109
  360. package/server/modules/providers/services/provider-auth.service.ts +26 -26
  361. package/server/modules/providers/services/provider-capabilities.service.ts +97 -97
  362. package/server/modules/providers/services/provider-models.service.ts +401 -401
  363. package/server/modules/providers/services/provider-runtime.service.ts +108 -108
  364. package/server/modules/providers/services/provider-token-usage.service.ts +357 -357
  365. package/server/modules/providers/services/session-conversations-search.service.ts +1321 -1321
  366. package/server/modules/providers/services/session-synchronizer.service.ts +74 -74
  367. package/server/modules/providers/services/sessions-watcher.service.ts +247 -247
  368. package/server/modules/providers/services/sessions.service.ts +471 -471
  369. package/server/modules/providers/services/skills.service.ts +39 -39
  370. package/server/modules/providers/shared/base/abstract.provider.ts +32 -32
  371. package/server/modules/providers/shared/mcp/mcp.provider.ts +151 -151
  372. package/server/modules/providers/shared/skills/skills.provider.ts +287 -287
  373. package/server/modules/providers/tests/claude-auth.test.ts +157 -150
  374. package/server/modules/providers/tests/claude-sessions.test.ts +61 -61
  375. package/server/modules/providers/tests/codex-sessions.test.ts +231 -231
  376. package/server/modules/providers/tests/mcp.test.ts +349 -349
  377. package/server/modules/providers/tests/opencode-models.test.ts +41 -41
  378. package/server/modules/providers/tests/opencode-sessions.test.ts +522 -522
  379. package/server/modules/providers/tests/provider-attachment-history.test.ts +247 -247
  380. package/server/modules/providers/tests/provider-models.service.test.ts +353 -353
  381. package/server/modules/providers/tests/provider-runtime.service.test.ts +138 -138
  382. package/server/modules/providers/tests/provider-token-usage.service.test.ts +186 -186
  383. package/server/modules/providers/tests/provider.routes.test.ts +244 -244
  384. package/server/modules/providers/tests/sessions-details.test.ts +73 -73
  385. package/server/modules/providers/tests/sessions.service.test.ts +127 -127
  386. package/server/modules/providers/tests/skills.test.ts +692 -692
  387. package/server/modules/settings/index.ts +2 -2
  388. package/server/modules/settings/settings.module.ts +50 -50
  389. package/server/modules/settings/settings.routes.ts +51 -51
  390. package/server/modules/settings/settings.service.ts +186 -186
  391. package/server/modules/settings/tests/settings.service.test.ts +54 -54
  392. package/server/modules/system/index.ts +2 -2
  393. package/server/modules/system/system.module.ts +62 -62
  394. package/server/modules/system/system.routes.ts +21 -21
  395. package/server/modules/system/system.service.ts +76 -76
  396. package/server/modules/system/tests/system.service.test.ts +118 -118
  397. package/server/modules/taskmaster/index.ts +2 -2
  398. package/server/modules/taskmaster/taskmaster.module.ts +24 -24
  399. package/server/modules/taskmaster/taskmaster.routes.ts +1483 -1482
  400. package/server/modules/taskmaster/taskmaster.service.ts +132 -132
  401. package/server/modules/taskmaster/tests/taskmaster.routes.test.ts +144 -144
  402. package/server/modules/taskmaster/tests/taskmaster.service.test.ts +97 -97
  403. package/server/modules/user/index.ts +2 -2
  404. package/server/modules/user/tests/user.service.test.ts +61 -61
  405. package/server/modules/user/user.module.ts +58 -58
  406. package/server/modules/user/user.routes.ts +50 -50
  407. package/server/modules/user/user.service.ts +85 -85
  408. package/server/modules/voice/index.ts +2 -2
  409. package/server/modules/voice/tests/voice.service.test.ts +98 -98
  410. package/server/modules/voice/voice.module.ts +47 -47
  411. package/server/modules/voice/voice.routes.ts +113 -113
  412. package/server/modules/voice/voice.service.ts +192 -192
  413. package/server/modules/websocket/README.md +273 -273
  414. package/server/modules/websocket/index.ts +3 -3
  415. package/server/modules/websocket/services/chat-run-registry.service.ts +343 -343
  416. package/server/modules/websocket/services/chat-session-writer.service.ts +145 -145
  417. package/server/modules/websocket/services/chat-websocket.service.ts +425 -425
  418. package/server/modules/websocket/services/plugin-websocket-proxy.service.ts +65 -65
  419. package/server/modules/websocket/services/shell-websocket.service.ts +503 -503
  420. package/server/modules/websocket/services/websocket-auth.service.ts +59 -59
  421. package/server/modules/websocket/services/websocket-server.service.ts +124 -124
  422. package/server/modules/websocket/services/websocket-state.service.ts +16 -16
  423. package/server/modules/websocket/services/websocket-writer.service.ts +38 -38
  424. package/server/modules/websocket/tests/chat-attachment-filter.test.ts +59 -59
  425. package/server/modules/websocket/tests/chat-run-registry.test.ts +282 -282
  426. package/server/modules/websocket/tests/shell-websocket.service.test.ts +100 -100
  427. package/server/modules/websocket/tests/websocket-heartbeat.service.test.ts +78 -78
  428. package/server/modules/worktrees/index.ts +2 -2
  429. package/server/modules/worktrees/services/worktree-create-and-open.service.ts +53 -53
  430. package/server/modules/worktrees/services/worktree-create.service.ts +104 -104
  431. package/server/modules/worktrees/services/worktree-git.service.ts +207 -207
  432. package/server/modules/worktrees/services/worktree-list.service.ts +143 -143
  433. package/server/modules/worktrees/services/worktree-merge.service.ts +157 -157
  434. package/server/modules/worktrees/services/worktree-open.service.ts +85 -85
  435. package/server/modules/worktrees/services/worktree-remove.service.ts +88 -88
  436. package/server/modules/worktrees/tests/worktree-create-and-open.service.test.ts +68 -68
  437. package/server/modules/worktrees/tests/worktree-create.service.test.ts +142 -142
  438. package/server/modules/worktrees/tests/worktree-git.service.test.ts +84 -84
  439. package/server/modules/worktrees/tests/worktree-list.service.test.ts +45 -45
  440. package/server/modules/worktrees/tests/worktree-merge.service.test.ts +213 -213
  441. package/server/modules/worktrees/tests/worktree-open.service.test.ts +122 -122
  442. package/server/modules/worktrees/tests/worktree-remove.service.test.ts +227 -227
  443. package/server/modules/worktrees/tests/worktrees.routes.test.ts +199 -199
  444. package/server/modules/worktrees/worktrees.module.ts +113 -113
  445. package/server/modules/worktrees/worktrees.routes.ts +116 -116
  446. package/server/shared/claude-cli-path.ts +139 -139
  447. package/server/shared/frontmatter.ts +18 -18
  448. package/server/shared/image-attachments.ts +435 -435
  449. package/server/shared/interfaces.ts +179 -179
  450. package/server/shared/tests/claude-cli-path.test.ts +61 -61
  451. package/server/shared/tests/image-attachments.test.ts +353 -353
  452. package/server/shared/tests/slice-tail-page.test.ts +42 -42
  453. package/server/shared/types.ts +1228 -1228
  454. package/server/shared/utils.ts +1161 -1161
  455. package/server/tsconfig.json +40 -40
  456. package/shared/networkHosts.js +22 -22
  457. package/dist/assets/FileTree-N2IkkHlv.js +0 -127
  458. package/dist/assets/GitPanel-DZCeuFZ2.js +0 -29
  459. package/dist/assets/PluginTabContent-Bnuf-fb8.js +0 -1
  460. package/dist/assets/Shell-BfFnMZ-f.js +0 -81
  461. package/dist/assets/Shell-qxJ8_QYu.css +0 -32
  462. package/dist/assets/StandaloneShell-QmyYwNAl.js +0 -1
  463. package/dist/assets/TaskMasterPanel-fMsmvN9L.js +0 -32
  464. package/dist/assets/abnfDiagram-N423BO3Z-DjPU7HLs.js +0 -1
  465. package/dist/assets/arc-Pdq6Tvr3.js +0 -1
  466. package/dist/assets/architectureDiagram-T3A2C74G-D0JnT0Vh.js +0 -36
  467. package/dist/assets/c4Diagram-5PPSVZJV-MpqlPGFi.js +0 -10
  468. package/dist/assets/channel-Bm0ZLE4z.js +0 -1
  469. package/dist/assets/chunk-5RXB4S5H-B4UQ8snD.js +0 -231
  470. package/dist/assets/chunk-GF5L2VYU-DkEPedJP.js +0 -206
  471. package/dist/assets/classDiagram-JCYQIIEL-DqEOcNxw.js +0 -1
  472. package/dist/assets/classDiagram-v2-OCEON4UE-DqEOcNxw.js +0 -1
  473. package/dist/assets/code-xml-VK1YfShP.js +0 -6
  474. package/dist/assets/cose-bilkent-JH36ORCC-DrVTo1Nh.js +0 -1
  475. package/dist/assets/cynefinDiagram-MW4NZA55-CCxBjw9E.js +0 -62
  476. package/dist/assets/cytoscape.esm-C10L9Icf.js +0 -331
  477. package/dist/assets/dagre-VZM6K2ZE-C_GvE4lu.js +0 -4
  478. package/dist/assets/diagram-LBJQPF4R-hfKcrrxe.js +0 -24
  479. package/dist/assets/diagram-Q27KOJAE-C1semJfK.js +0 -24
  480. package/dist/assets/diagram-UB23O5K3-57hvXQXO.js +0 -41
  481. package/dist/assets/ebnfDiagram-BXEA7PRR-9KIAlAVw.js +0 -1
  482. package/dist/assets/erDiagram-JOGREHBK-B7OzJ767.js +0 -85
  483. package/dist/assets/ganttDiagram-PKOTCBZU-CbMNWWQu.js +0 -292
  484. package/dist/assets/gitGraphDiagram-DS77QQ5N-I_vZ5waO.js +0 -106
  485. package/dist/assets/graph-Cx-EqisW.js +0 -1
  486. package/dist/assets/index-DFnVoqpx.js +0 -1174
  487. package/dist/assets/index-DlSeAkw_.js +0 -11
  488. package/dist/assets/index-ZHwIojzi.js +0 -1
  489. package/dist/assets/index-t1w8KNxn.css +0 -1
  490. package/dist/assets/infoDiagram-6WML65LV-Dh20Tsbq.js +0 -2
  491. package/dist/assets/ishikawaDiagram-WSZJBQD7-DH2Sm3ko.js +0 -70
  492. package/dist/assets/journeyDiagram-NVQOT4AX-BNFQqlVu.js +0 -139
  493. package/dist/assets/map-CV9uif0g.js +0 -1
  494. package/dist/assets/mermaid.core-BYRQSshi.js +0 -312
  495. package/dist/assets/pegDiagram-VL7TDLO6-CNYpECr8.js +0 -1
  496. package/dist/assets/quadrantDiagram-CIZ2JOQS-BXVVQsa6.js +0 -7
  497. package/dist/assets/railroadDiagram-AXF67PYL-JTroIW8F.js +0 -1
  498. package/dist/assets/requirementDiagram-LRYGKXZP-C1Z_inLz.js +0 -84
  499. package/dist/assets/sankeyDiagram-W5VNT64P-DCJgKFUk.js +0 -40
  500. package/dist/assets/sequenceDiagram-SI44F4Z6-D8Wrx5Vc.js +0 -162
  501. package/dist/assets/sizeCapture-X5ZJPWSS-CXqD68vD.js +0 -1
  502. package/dist/assets/stateDiagram-OKZ733FA-CF0Ekqjo.js +0 -1
  503. package/dist/assets/stateDiagram-v2-UEYNNEHI-DARLpEFJ.js +0 -1
  504. package/dist/assets/swimlanes-SLNWSIFB-DVIax_k4.js +0 -2
  505. package/dist/assets/swimlanesDiagram-ULZ7WXOC-CrgDgaY8.js +0 -8
  506. package/dist/assets/timeline-definition-Z64GVDOM-DSqk3ApG.js +0 -120
  507. package/dist/assets/vendor-react-whEHfigN.js +0 -59
  508. package/dist/assets/vennDiagram-T6HMQDX7-DNbTqyG5.js +0 -34
  509. package/dist/assets/xychartDiagram-ELKLHX3M-XsVZphHr.js +0 -7
  510. package/dist/manifest.json +0 -61
@@ -1,153 +1,153 @@
1
1
  import fs from 'node:fs';
2
2
  import crypto from 'node:crypto';
3
- import os from 'node:os';
4
- import path from 'node:path';
5
-
6
- import pty, { type IPty } from 'node-pty';
7
- import { WebSocket, type RawData } from 'ws';
8
-
3
+ import os from 'node:os';
4
+ import path from 'node:path';
5
+
6
+ import pty, { type IPty } from 'node-pty';
7
+ import { WebSocket, type RawData } from 'ws';
8
+
9
9
  import { parseIncomingJsonObject } from '@/shared/utils.js';
10
10
  import { terminalReplayStore } from '@/modules/websocket/services/terminal-replay-store.js';
11
-
12
- type ShellIncomingMessage = {
13
- type?: string;
14
- data?: string;
15
- cols?: number;
16
- rows?: number;
17
- projectPath?: string;
18
- sessionId?: string;
19
- hasSession?: boolean;
20
- provider?: string;
21
- initialCommand?: string;
22
- isPlainShell?: boolean;
11
+
12
+ type ShellIncomingMessage = {
13
+ type?: string;
14
+ data?: string;
15
+ cols?: number;
16
+ rows?: number;
17
+ projectPath?: string;
18
+ sessionId?: string;
19
+ hasSession?: boolean;
20
+ provider?: string;
21
+ initialCommand?: string;
22
+ isPlainShell?: boolean;
23
23
  forceRestart?: boolean;
24
24
  shellMode?: string;
25
25
  };
26
-
27
- type PtySessionEntry = {
26
+
27
+ type PtySessionEntry = {
28
28
  pty: IPty;
29
29
  ws: WebSocket | null;
30
30
  timeoutId: NodeJS.Timeout | null;
31
- projectPath: string;
32
- sessionId: string | null;
33
- };
34
-
35
- const ptySessionsMap = new Map<string, PtySessionEntry>();
36
- const PTY_SESSION_TIMEOUT = 30 * 60 * 1000;
37
- const SHELL_URL_PARSE_BUFFER_LIMIT = 32768;
38
- const ANSI_ESCAPE_SEQUENCE_REGEX = /\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~]|\][^\x07]*(?:\x07|\x1B\\))/g;
31
+ projectPath: string;
32
+ sessionId: string | null;
33
+ };
34
+
35
+ const ptySessionsMap = new Map<string, PtySessionEntry>();
36
+ const PTY_SESSION_TIMEOUT = 30 * 60 * 1000;
37
+ const SHELL_URL_PARSE_BUFFER_LIMIT = 32768;
38
+ const ANSI_ESCAPE_SEQUENCE_REGEX = /\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~]|\][^\x07]*(?:\x07|\x1B\\))/g;
39
39
  const TRAILING_URL_PUNCTUATION_REGEX = /[)\]}>.,;:!?]+$/;
40
40
  const TERMINAL_REPLAY_TRUNCATED_MESSAGE =
41
41
  '\x1b[33m[Earlier terminal output was omitted to limit memory usage]\x1b[0m\r\n';
42
-
43
- function stripAnsiSequences(value: string): string {
44
- return value.replace(ANSI_ESCAPE_SEQUENCE_REGEX, '');
45
- }
46
-
47
- function normalizeDetectedUrl(url: string): string | null {
48
- const cleanedUrl = url.trim().replace(TRAILING_URL_PUNCTUATION_REGEX, '');
49
- if (!cleanedUrl) {
50
- return null;
51
- }
52
-
53
- try {
54
- const parsedUrl = new URL(cleanedUrl);
55
- if (parsedUrl.protocol !== 'http:' && parsedUrl.protocol !== 'https:') {
56
- return null;
57
- }
58
- return parsedUrl.toString();
59
- } catch {
60
- return null;
61
- }
62
- }
63
-
64
- function extractUrlsFromText(value: string): string[] {
65
- const directMatches = value.match(/https?:\/\/[^\s<>"'`\\\x1b\x07]+/gi) ?? [];
66
-
67
- // Terminal width can split a URL across lines, so valid URL characters on
68
- // immediately following lines are joined before the URL is validated.
69
- const wrappedMatches: string[] = [];
70
- const urlContinuationPattern = /^[A-Za-z0-9\-._~:/?#\[\]@!$&'()*+,;=%]+$/;
71
- const lines = value.split(/\r?\n/);
72
- for (let lineIndex = 0; lineIndex < lines.length; lineIndex += 1) {
73
- const line = lines[lineIndex].trim();
74
- const startMatch = line.match(/https?:\/\/[^\s<>"'`\\\x1b\x07]+/i);
75
- if (!startMatch) {
76
- continue;
77
- }
78
-
79
- let combinedUrl = startMatch[0];
80
- let continuationIndex = lineIndex + 1;
81
- while (continuationIndex < lines.length) {
82
- const continuation = lines[continuationIndex].trim();
83
- if (!continuation || !urlContinuationPattern.test(continuation)) {
84
- break;
85
- }
86
- combinedUrl += continuation;
87
- continuationIndex += 1;
88
- }
89
-
90
- wrappedMatches.push(combinedUrl);
91
- }
92
-
93
- return Array.from(new Set([...directMatches, ...wrappedMatches]));
94
- }
95
-
96
- function shouldAutoOpenUrlFromOutput(value: string): boolean {
97
- const normalizedOutput = value.toLowerCase();
98
- return (
99
- normalizedOutput.includes("browser didn't open") ||
100
- normalizedOutput.includes('open this url') ||
101
- normalizedOutput.includes('continue in your browser') ||
102
- normalizedOutput.includes('press enter to open') ||
103
- normalizedOutput.includes('open_url:')
104
- );
105
- }
106
-
42
+
43
+ function stripAnsiSequences(value: string): string {
44
+ return value.replace(ANSI_ESCAPE_SEQUENCE_REGEX, '');
45
+ }
46
+
47
+ function normalizeDetectedUrl(url: string): string | null {
48
+ const cleanedUrl = url.trim().replace(TRAILING_URL_PUNCTUATION_REGEX, '');
49
+ if (!cleanedUrl) {
50
+ return null;
51
+ }
52
+
53
+ try {
54
+ const parsedUrl = new URL(cleanedUrl);
55
+ if (parsedUrl.protocol !== 'http:' && parsedUrl.protocol !== 'https:') {
56
+ return null;
57
+ }
58
+ return parsedUrl.toString();
59
+ } catch {
60
+ return null;
61
+ }
62
+ }
63
+
64
+ function extractUrlsFromText(value: string): string[] {
65
+ const directMatches = value.match(/https?:\/\/[^\s<>"'`\\\x1b\x07]+/gi) ?? [];
66
+
67
+ // Terminal width can split a URL across lines, so valid URL characters on
68
+ // immediately following lines are joined before the URL is validated.
69
+ const wrappedMatches: string[] = [];
70
+ const urlContinuationPattern = /^[A-Za-z0-9\-._~:/?#\[\]@!$&'()*+,;=%]+$/;
71
+ const lines = value.split(/\r?\n/);
72
+ for (let lineIndex = 0; lineIndex < lines.length; lineIndex += 1) {
73
+ const line = lines[lineIndex].trim();
74
+ const startMatch = line.match(/https?:\/\/[^\s<>"'`\\\x1b\x07]+/i);
75
+ if (!startMatch) {
76
+ continue;
77
+ }
78
+
79
+ let combinedUrl = startMatch[0];
80
+ let continuationIndex = lineIndex + 1;
81
+ while (continuationIndex < lines.length) {
82
+ const continuation = lines[continuationIndex].trim();
83
+ if (!continuation || !urlContinuationPattern.test(continuation)) {
84
+ break;
85
+ }
86
+ combinedUrl += continuation;
87
+ continuationIndex += 1;
88
+ }
89
+
90
+ wrappedMatches.push(combinedUrl);
91
+ }
92
+
93
+ return Array.from(new Set([...directMatches, ...wrappedMatches]));
94
+ }
95
+
96
+ function shouldAutoOpenUrlFromOutput(value: string): boolean {
97
+ const normalizedOutput = value.toLowerCase();
98
+ return (
99
+ normalizedOutput.includes("browser didn't open") ||
100
+ normalizedOutput.includes('open this url') ||
101
+ normalizedOutput.includes('continue in your browser') ||
102
+ normalizedOutput.includes('press enter to open') ||
103
+ normalizedOutput.includes('open_url:')
104
+ );
105
+ }
106
+
107
107
  type ShellWebSocketDependencies = {
108
- resolveProviderSessionId: (
109
- sessionId: string,
110
- provider: string,
108
+ resolveProviderSessionId: (
109
+ sessionId: string,
110
+ provider: string,
111
111
  ) => string | null | undefined;
112
112
  spawnPty?: typeof pty.spawn;
113
113
  platform?: () => NodeJS.Platform;
114
114
  environment?: NodeJS.ProcessEnv;
115
115
  };
116
-
117
- /**
118
- * Reads a string field from untyped payloads and falls back when absent.
119
- */
120
- function readString(value: unknown, fallback = ''): string {
121
- return typeof value === 'string' ? value : fallback;
122
- }
123
-
124
- /**
125
- * Reads a boolean field from untyped payloads and falls back when absent.
126
- */
127
- function readBoolean(value: unknown, fallback = false): boolean {
128
- return typeof value === 'boolean' ? value : fallback;
129
- }
130
-
131
- /**
132
- * Reads a finite number field from untyped payloads and falls back when absent.
133
- */
134
- function readNumber(value: unknown, fallback: number): number {
135
- return typeof value === 'number' && Number.isFinite(value) ? value : fallback;
136
- }
137
-
138
- /**
139
- * Parses incoming websocket shell messages and keeps processing safe when
140
- * malformed payloads are received.
141
- */
142
- function parseShellMessage(rawMessage: RawData): ShellIncomingMessage | null {
143
- const payload = parseIncomingJsonObject(rawMessage);
144
- if (!payload) {
145
- return null;
146
- }
147
-
148
- return payload as ShellIncomingMessage;
149
- }
150
-
116
+
117
+ /**
118
+ * Reads a string field from untyped payloads and falls back when absent.
119
+ */
120
+ function readString(value: unknown, fallback = ''): string {
121
+ return typeof value === 'string' ? value : fallback;
122
+ }
123
+
124
+ /**
125
+ * Reads a boolean field from untyped payloads and falls back when absent.
126
+ */
127
+ function readBoolean(value: unknown, fallback = false): boolean {
128
+ return typeof value === 'boolean' ? value : fallback;
129
+ }
130
+
131
+ /**
132
+ * Reads a finite number field from untyped payloads and falls back when absent.
133
+ */
134
+ function readNumber(value: unknown, fallback: number): number {
135
+ return typeof value === 'number' && Number.isFinite(value) ? value : fallback;
136
+ }
137
+
138
+ /**
139
+ * Parses incoming websocket shell messages and keeps processing safe when
140
+ * malformed payloads are received.
141
+ */
142
+ function parseShellMessage(rawMessage: RawData): ShellIncomingMessage | null {
143
+ const payload = parseIncomingJsonObject(rawMessage);
144
+ if (!payload) {
145
+ return null;
146
+ }
147
+
148
+ return payload as ShellIncomingMessage;
149
+ }
150
+
151
151
  const SAFE_SESSION_ID_PATTERN = /^[a-zA-Z0-9_.\-:]+$/;
152
152
  const BASE64_PATTERN = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/;
153
153
  const MAX_TERMINAL_BINARY_INPUT_BYTES = 1024;
@@ -158,63 +158,63 @@ const HERDR_PANE_ENVIRONMENT_KEYS = [
158
158
  'HERDR_TAB_ID',
159
159
  'HERDR_WORKSPACE_ID',
160
160
  ] as const;
161
-
162
- function resolveResumeSessionId(
163
- message: ShellIncomingMessage,
164
- dependencies: ShellWebSocketDependencies
165
- ): string {
166
- const hasSession = readBoolean(message.hasSession);
167
- const sessionId = readString(message.sessionId);
168
- const provider = readString(message.provider, 'claude');
169
-
170
- if (!hasSession || !sessionId) {
171
- return '';
172
- }
173
-
174
- let resumeSessionId: string | null | undefined;
175
- try {
176
- resumeSessionId = dependencies.resolveProviderSessionId(sessionId, provider);
177
- } catch (error) {
178
- console.error('Failed to resolve provider session ID:', error);
179
- resumeSessionId = undefined;
180
- }
181
-
182
- const resolvedSessionId = resumeSessionId === undefined ? sessionId : resumeSessionId;
183
- if (!resolvedSessionId || !SAFE_SESSION_ID_PATTERN.test(resolvedSessionId)) {
184
- return '';
185
- }
186
-
187
- return resolvedSessionId;
188
- }
189
-
190
- /**
191
- * Resolves provider command line for plain shell and agent-backed shell modes.
192
- */
161
+
162
+ function resolveResumeSessionId(
163
+ message: ShellIncomingMessage,
164
+ dependencies: ShellWebSocketDependencies
165
+ ): string {
166
+ const hasSession = readBoolean(message.hasSession);
167
+ const sessionId = readString(message.sessionId);
168
+ const provider = readString(message.provider, 'claude');
169
+
170
+ if (!hasSession || !sessionId) {
171
+ return '';
172
+ }
173
+
174
+ let resumeSessionId: string | null | undefined;
175
+ try {
176
+ resumeSessionId = dependencies.resolveProviderSessionId(sessionId, provider);
177
+ } catch (error) {
178
+ console.error('Failed to resolve provider session ID:', error);
179
+ resumeSessionId = undefined;
180
+ }
181
+
182
+ const resolvedSessionId = resumeSessionId === undefined ? sessionId : resumeSessionId;
183
+ if (!resolvedSessionId || !SAFE_SESSION_ID_PATTERN.test(resolvedSessionId)) {
184
+ return '';
185
+ }
186
+
187
+ return resolvedSessionId;
188
+ }
189
+
190
+ /**
191
+ * Resolves provider command line for plain shell and agent-backed shell modes.
192
+ */
193
193
  function buildShellCommand(
194
194
  message: ShellIncomingMessage,
195
195
  dependencies: ShellWebSocketDependencies
196
196
  ): string {
197
- const hasSession = readBoolean(message.hasSession);
198
- const initialCommand = readString(message.initialCommand);
197
+ const hasSession = readBoolean(message.hasSession);
198
+ const initialCommand = readString(message.initialCommand);
199
199
  const provider = readString(message.provider, 'claude');
200
200
  const resumeSessionId = resolveResumeSessionId(message, dependencies);
201
201
  const platform = dependencies.platform?.() ?? os.platform();
202
- const isPlainShell =
203
- readBoolean(message.isPlainShell) ||
204
- (!!initialCommand && !hasSession) ||
205
- provider === 'plain-shell';
206
-
207
- if (isPlainShell) {
208
- return initialCommand;
209
- }
210
-
211
- if (provider === 'cursor') {
212
- if (resumeSessionId) {
213
- return `cursor-agent --resume="${resumeSessionId}"`;
214
- }
215
- return 'cursor-agent';
216
- }
217
-
202
+ const isPlainShell =
203
+ readBoolean(message.isPlainShell) ||
204
+ (!!initialCommand && !hasSession) ||
205
+ provider === 'plain-shell';
206
+
207
+ if (isPlainShell) {
208
+ return initialCommand;
209
+ }
210
+
211
+ if (provider === 'cursor') {
212
+ if (resumeSessionId) {
213
+ return `cursor-agent --resume="${resumeSessionId}"`;
214
+ }
215
+ return 'cursor-agent';
216
+ }
217
+
218
218
  if (provider === 'codex') {
219
219
  const codexCommand = platform === 'win32' ? 'codex.cmd' : 'codex';
220
220
  if (resumeSessionId) {
@@ -224,78 +224,78 @@ function buildShellCommand(
224
224
  return `${codexCommand} resume "${resumeSessionId}" || ${codexCommand}`;
225
225
  }
226
226
  return codexCommand;
227
- }
228
-
229
- if (provider === 'opencode') {
230
- if (resumeSessionId) {
231
- return `opencode --session "${resumeSessionId}"`;
232
- }
233
- return initialCommand || 'opencode';
234
- }
235
-
236
- const command = initialCommand || 'claude';
237
- if (resumeSessionId) {
227
+ }
228
+
229
+ if (provider === 'opencode') {
230
+ if (resumeSessionId) {
231
+ return `opencode --session "${resumeSessionId}"`;
232
+ }
233
+ return initialCommand || 'opencode';
234
+ }
235
+
236
+ const command = initialCommand || 'claude';
237
+ if (resumeSessionId) {
238
238
  if (platform === 'win32') {
239
- return `claude --resume "${resumeSessionId}"; if ($LASTEXITCODE -ne 0) { claude }`;
240
- }
241
- return `claude --resume "${resumeSessionId}" || claude`;
242
- }
243
- return command;
244
- }
245
-
246
- function readEnvValue(env: NodeJS.ProcessEnv, key: string): string | undefined {
247
- const resolvedKey = Object.keys(env).find((envKey) => envKey.toLowerCase() === key.toLowerCase());
248
- return resolvedKey ? env[resolvedKey] : undefined;
249
- }
250
-
251
- function getPathEnvKey(env: NodeJS.ProcessEnv): string {
252
- return Object.keys(env).find((key) => key.toLowerCase() === 'path') || 'PATH';
253
- }
254
-
239
+ return `claude --resume "${resumeSessionId}"; if ($LASTEXITCODE -ne 0) { claude }`;
240
+ }
241
+ return `claude --resume "${resumeSessionId}" || claude`;
242
+ }
243
+ return command;
244
+ }
245
+
246
+ function readEnvValue(env: NodeJS.ProcessEnv, key: string): string | undefined {
247
+ const resolvedKey = Object.keys(env).find((envKey) => envKey.toLowerCase() === key.toLowerCase());
248
+ return resolvedKey ? env[resolvedKey] : undefined;
249
+ }
250
+
251
+ function getPathEnvKey(env: NodeJS.ProcessEnv): string {
252
+ return Object.keys(env).find((key) => key.toLowerCase() === 'path') || 'PATH';
253
+ }
254
+
255
255
  function prioritizeUserNpmGlobalBin(env: NodeJS.ProcessEnv): { key: string; value: string | undefined } {
256
- const pathKey = getPathEnvKey(env);
257
- const currentPath = env[pathKey];
258
- if (!currentPath) {
259
- return { key: pathKey, value: currentPath };
260
- }
261
-
262
- const delimiter = path.delimiter;
263
- const pathEntries = currentPath.split(delimiter).filter(Boolean);
264
- const npmPrefix = readEnvValue(env, 'npm_config_prefix');
265
- const appData = readEnvValue(env, 'APPDATA');
266
- const candidates = [
267
- npmPrefix || '',
268
- npmPrefix ? path.join(npmPrefix, 'bin') : '',
269
- appData ? path.join(appData, 'npm') : '',
270
- path.join(os.homedir(), 'AppData', 'Roaming', 'npm'),
271
- path.join(os.homedir(), '.npm-global', 'bin'),
272
- ].filter(Boolean);
273
-
274
- const normalizedPathEntries = pathEntries.map((entry) => os.platform() === 'win32' ? entry.toLowerCase() : entry);
275
- const preferredEntries = candidates.filter((candidate, index) => {
276
- const normalizedCandidate = os.platform() === 'win32' ? candidate.toLowerCase() : candidate;
277
- return (
278
- candidates.indexOf(candidate) === index &&
279
- normalizedPathEntries.includes(normalizedCandidate)
280
- );
281
- });
282
-
283
- if (preferredEntries.length === 0) {
284
- return { key: pathKey, value: currentPath };
285
- }
286
-
287
- const normalizedPreferredEntries = preferredEntries.map((entry) =>
288
- os.platform() === 'win32' ? entry.toLowerCase() : entry
289
- );
290
-
291
- const value = [
292
- ...preferredEntries,
293
- ...pathEntries.filter((entry) => {
294
- const normalizedEntry = os.platform() === 'win32' ? entry.toLowerCase() : entry;
295
- return !normalizedPreferredEntries.includes(normalizedEntry);
296
- }),
297
- ].join(delimiter);
298
-
256
+ const pathKey = getPathEnvKey(env);
257
+ const currentPath = env[pathKey];
258
+ if (!currentPath) {
259
+ return { key: pathKey, value: currentPath };
260
+ }
261
+
262
+ const delimiter = path.delimiter;
263
+ const pathEntries = currentPath.split(delimiter).filter(Boolean);
264
+ const npmPrefix = readEnvValue(env, 'npm_config_prefix');
265
+ const appData = readEnvValue(env, 'APPDATA');
266
+ const candidates = [
267
+ npmPrefix || '',
268
+ npmPrefix ? path.join(npmPrefix, 'bin') : '',
269
+ appData ? path.join(appData, 'npm') : '',
270
+ path.join(os.homedir(), 'AppData', 'Roaming', 'npm'),
271
+ path.join(os.homedir(), '.npm-global', 'bin'),
272
+ ].filter(Boolean);
273
+
274
+ const normalizedPathEntries = pathEntries.map((entry) => os.platform() === 'win32' ? entry.toLowerCase() : entry);
275
+ const preferredEntries = candidates.filter((candidate, index) => {
276
+ const normalizedCandidate = os.platform() === 'win32' ? candidate.toLowerCase() : candidate;
277
+ return (
278
+ candidates.indexOf(candidate) === index &&
279
+ normalizedPathEntries.includes(normalizedCandidate)
280
+ );
281
+ });
282
+
283
+ if (preferredEntries.length === 0) {
284
+ return { key: pathKey, value: currentPath };
285
+ }
286
+
287
+ const normalizedPreferredEntries = preferredEntries.map((entry) =>
288
+ os.platform() === 'win32' ? entry.toLowerCase() : entry
289
+ );
290
+
291
+ const value = [
292
+ ...preferredEntries,
293
+ ...pathEntries.filter((entry) => {
294
+ const normalizedEntry = os.platform() === 'win32' ? entry.toLowerCase() : entry;
295
+ return !normalizedPreferredEntries.includes(normalizedEntry);
296
+ }),
297
+ ].join(delimiter);
298
+
299
299
  return { key: pathKey, value };
300
300
  }
301
301
 
@@ -315,7 +315,7 @@ function buildShellEnvironment(
315
315
 
316
316
  if (readString(message.shellMode) === 'herdr') {
317
317
  // The web terminal is a new Herdr client, not a pane nested inside the
318
- // Herdr pane that may have launched CloudCLI. Keep HERDR_SOCKET_PATH so it
318
+ // Herdr pane that may have launched WindCli. Keep HERDR_SOCKET_PATH so it
319
319
  // attaches to the running session, but remove inherited pane identity.
320
320
  HERDR_PANE_ENVIRONMENT_KEYS.forEach((key) => delete environment[key]);
321
321
  }
@@ -376,53 +376,53 @@ function buildPtySessionKey({
376
376
  });
377
377
  return `pty:${crypto.createHash('sha256').update(identity).digest('hex')}`;
378
378
  }
379
-
380
- /**
381
- * Used by this module's websocket gateway to connect the standalone Shell UI
382
- * to a retained PTY while keeping process lifecycle ownership on the server.
383
- */
384
- export function handleShellConnection(
385
- ws: WebSocket,
386
- dependencies: ShellWebSocketDependencies
387
- ): void {
388
- console.log('[INFO] Shell websocket connected');
389
-
379
+
380
+ /**
381
+ * Used by this module's websocket gateway to connect the standalone Shell UI
382
+ * to a retained PTY while keeping process lifecycle ownership on the server.
383
+ */
384
+ export function handleShellConnection(
385
+ ws: WebSocket,
386
+ dependencies: ShellWebSocketDependencies
387
+ ): void {
388
+ console.log('[INFO] Shell websocket connected');
389
+
390
390
  let shellProcess: IPty | null = null;
391
391
  let ptySessionKey: string | null = null;
392
392
  let isHerdrConnection = false;
393
- let urlDetectionBuffer = '';
394
- const announcedAuthUrls = new Set<string>();
395
-
396
- ws.on('message', async (rawMessage) => {
397
- try {
398
- const data = parseShellMessage(rawMessage);
399
- if (!data?.type) {
400
- throw new Error('Invalid websocket payload');
401
- }
402
-
403
- if (data.type === 'init') {
404
- const projectPath = readString(data.projectPath, process.cwd());
405
- const sessionId = readString(data.sessionId) || null;
406
- const hasSession = readBoolean(data.hasSession);
407
- const provider = readString(data.provider, 'claude');
408
- const initialCommand = readString(data.initialCommand);
393
+ let urlDetectionBuffer = '';
394
+ const announcedAuthUrls = new Set<string>();
395
+
396
+ ws.on('message', async (rawMessage) => {
397
+ try {
398
+ const data = parseShellMessage(rawMessage);
399
+ if (!data?.type) {
400
+ throw new Error('Invalid websocket payload');
401
+ }
402
+
403
+ if (data.type === 'init') {
404
+ const projectPath = readString(data.projectPath, process.cwd());
405
+ const sessionId = readString(data.sessionId) || null;
406
+ const hasSession = readBoolean(data.hasSession);
407
+ const provider = readString(data.provider, 'claude');
408
+ const initialCommand = readString(data.initialCommand);
409
409
  const forceRestart = readBoolean(data.forceRestart);
410
410
  const isHerdrMode = readString(data.shellMode) === 'herdr';
411
411
  isHerdrConnection = isHerdrMode;
412
412
  const isPlainShell =
413
- readBoolean(data.isPlainShell) ||
414
- (!!initialCommand && !hasSession) ||
415
- provider === 'plain-shell';
416
-
417
- urlDetectionBuffer = '';
418
- announcedAuthUrls.clear();
419
-
420
- const isLoginCommand =
421
- !!initialCommand &&
422
- (initialCommand.includes('setup-token') ||
423
- initialCommand.includes('cursor-agent login') ||
424
- initialCommand.includes('auth login'));
425
-
413
+ readBoolean(data.isPlainShell) ||
414
+ (!!initialCommand && !hasSession) ||
415
+ provider === 'plain-shell';
416
+
417
+ urlDetectionBuffer = '';
418
+ announcedAuthUrls.clear();
419
+
420
+ const isLoginCommand =
421
+ !!initialCommand &&
422
+ (initialCommand.includes('setup-token') ||
423
+ initialCommand.includes('cursor-agent login') ||
424
+ initialCommand.includes('auth login'));
425
+
426
426
  const resolvedProjectPath = path.resolve(projectPath);
427
427
  try {
428
428
  const stats = fs.statSync(resolvedProjectPath);
@@ -446,25 +446,25 @@ export function handleShellConnection(
446
446
  shellMode: readString(data.shellMode),
447
447
  initialCommand: isPlainShell ? initialCommand : '',
448
448
  });
449
-
450
- if (isLoginCommand || forceRestart) {
451
- const oldSession = ptySessionsMap.get(ptySessionKey);
452
- if (oldSession) {
453
- if (oldSession.timeoutId) {
454
- clearTimeout(oldSession.timeoutId);
449
+
450
+ if (isLoginCommand || forceRestart) {
451
+ const oldSession = ptySessionsMap.get(ptySessionKey);
452
+ if (oldSession) {
453
+ if (oldSession.timeoutId) {
454
+ clearTimeout(oldSession.timeoutId);
455
455
  }
456
456
  terminalReplayStore.delete(ptySessionKey);
457
457
  oldSession.pty.kill();
458
- ptySessionsMap.delete(ptySessionKey);
459
- }
460
- }
461
-
462
- const existingSession =
463
- isLoginCommand || forceRestart ? null : ptySessionsMap.get(ptySessionKey);
458
+ ptySessionsMap.delete(ptySessionKey);
459
+ }
460
+ }
461
+
462
+ const existingSession =
463
+ isLoginCommand || forceRestart ? null : ptySessionsMap.get(ptySessionKey);
464
464
  if (existingSession) {
465
465
  shellProcess = existingSession.pty;
466
- if (existingSession.timeoutId) {
467
- clearTimeout(existingSession.timeoutId);
466
+ if (existingSession.timeoutId) {
467
+ clearTimeout(existingSession.timeoutId);
468
468
  existingSession.timeoutId = null;
469
469
  }
470
470
 
@@ -487,31 +487,31 @@ export function handleShellConnection(
487
487
  }
488
488
 
489
489
  ws.send(
490
- JSON.stringify({
491
- type: 'output',
492
- data: '\x1b[36m[Reconnected to existing session]\x1b[0m\r\n',
493
- })
494
- );
495
-
490
+ JSON.stringify({
491
+ type: 'output',
492
+ data: '\x1b[36m[Reconnected to existing session]\x1b[0m\r\n',
493
+ })
494
+ );
495
+
496
496
  const replay = terminalReplayStore.snapshot(ptySessionKey);
497
497
  if (replay.truncated) {
498
498
  ws.send(JSON.stringify({ type: 'output', data: TERMINAL_REPLAY_TRUNCATED_MESSAGE }));
499
499
  }
500
500
  if (replay.chunks.length > 0) {
501
501
  replay.chunks.forEach((bufferedData) => {
502
- ws.send(
503
- JSON.stringify({
504
- type: 'output',
505
- data: bufferedData,
506
- })
507
- );
508
- });
509
- }
510
-
502
+ ws.send(
503
+ JSON.stringify({
504
+ type: 'output',
505
+ data: bufferedData,
506
+ })
507
+ );
508
+ });
509
+ }
510
+
511
511
  existingSession.pty.resize(reconnectCols, reconnectRows);
512
512
  return;
513
513
  }
514
-
514
+
515
515
  const shellCommand = buildShellCommand(data, dependencies);
516
516
  const resumeSessionId = resolveResumeSessionId(data, dependencies);
517
517
  const platform = dependencies.platform?.() ?? os.platform();
@@ -528,153 +528,153 @@ export function handleShellConnection(
528
528
  : platform === 'win32'
529
529
  ? ['-Command', shellCommand]
530
530
  : ['-c', shellCommand];
531
- const termCols = readNumber(data.cols, 80);
532
- const termRows = readNumber(data.rows, 24);
531
+ const termCols = readNumber(data.cols, 80);
532
+ const termRows = readNumber(data.rows, 24);
533
533
  const shellEnvironment = buildShellEnvironment(data, dependencies);
534
-
535
- shellProcess = (dependencies.spawnPty ?? pty.spawn)(shell, shellArgs, {
536
- name: 'xterm-256color',
537
- cols: termCols,
538
- rows: termRows,
539
- cwd: resolvedProjectPath,
534
+
535
+ shellProcess = (dependencies.spawnPty ?? pty.spawn)(shell, shellArgs, {
536
+ name: 'xterm-256color',
537
+ cols: termCols,
538
+ rows: termRows,
539
+ cwd: resolvedProjectPath,
540
540
  env: shellEnvironment,
541
- });
542
-
541
+ });
542
+
543
543
  ptySessionsMap.set(ptySessionKey, {
544
544
  pty: shellProcess,
545
545
  ws,
546
546
  timeoutId: null,
547
- projectPath,
548
- sessionId,
549
- });
550
-
551
- shellProcess.onData((chunk) => {
552
- if (!ptySessionKey) {
553
- return;
554
- }
555
-
556
- const session = ptySessionsMap.get(ptySessionKey);
557
- if (!session) {
558
- return;
559
- }
560
-
547
+ projectPath,
548
+ sessionId,
549
+ });
550
+
551
+ shellProcess.onData((chunk) => {
552
+ if (!ptySessionKey) {
553
+ return;
554
+ }
555
+
556
+ const session = ptySessionsMap.get(ptySessionKey);
557
+ if (!session) {
558
+ return;
559
+ }
560
+
561
561
  if (!isHerdrMode) {
562
562
  terminalReplayStore.append(ptySessionKey, chunk);
563
- }
564
-
565
- if (session.ws && session.ws.readyState === WebSocket.OPEN) {
566
- let outputData = chunk;
567
- const cleanChunk = stripAnsiSequences(chunk);
568
- urlDetectionBuffer = `${urlDetectionBuffer}${cleanChunk}`.slice(-SHELL_URL_PARSE_BUFFER_LIMIT);
569
-
570
- outputData = outputData.replace(
571
- /OPEN_URL:\s*(https?:\/\/[^\s\x1b\x07]+)/g,
572
- '[INFO] Opening in browser: $1'
573
- );
574
-
575
- const emitAuthUrl = (detectedUrl: string, autoOpen = false) => {
576
- const normalizedUrl = normalizeDetectedUrl(detectedUrl);
577
- if (!normalizedUrl) {
578
- return;
579
- }
580
-
581
- const isNewUrl = !announcedAuthUrls.has(normalizedUrl);
582
- if (isNewUrl) {
583
- announcedAuthUrls.add(normalizedUrl);
584
- session.ws?.send(
585
- JSON.stringify({
586
- type: 'auth_url',
587
- url: normalizedUrl,
588
- autoOpen,
589
- })
590
- );
591
- }
592
- };
593
-
594
- const normalizedDetectedUrls = extractUrlsFromText(urlDetectionBuffer)
595
- .map((url) => normalizeDetectedUrl(url))
596
- .filter((url): url is string => Boolean(url));
597
-
598
- const dedupedDetectedUrls = Array.from(new Set(normalizedDetectedUrls)).filter(
599
- (url, _, urls) =>
600
- !urls.some((otherUrl) => otherUrl !== url && otherUrl.startsWith(url))
601
- );
602
-
603
- dedupedDetectedUrls.forEach((url) => emitAuthUrl(url, false));
604
-
605
- if (
606
- shouldAutoOpenUrlFromOutput(cleanChunk) &&
607
- dedupedDetectedUrls.length > 0
608
- ) {
609
- const bestUrl = dedupedDetectedUrls.reduce((longest, current) =>
610
- current.length > longest.length ? current : longest
611
- );
612
- emitAuthUrl(bestUrl, true);
613
- }
614
-
615
- session.ws.send(
616
- JSON.stringify({
617
- type: 'output',
618
- data: outputData,
619
- })
620
- );
621
- }
622
- });
623
-
624
- shellProcess.onExit((exitCode) => {
625
- if (!ptySessionKey) {
626
- return;
627
- }
628
-
629
- const session = ptySessionsMap.get(ptySessionKey);
630
- if (session && session.pty !== shellProcess) {
631
- return;
632
- }
633
-
634
- if (session && session.ws && session.ws.readyState === WebSocket.OPEN) {
635
- session.ws.send(
636
- JSON.stringify({
637
- type: 'output',
638
- data: `\r\n\x1b[33mProcess exited with code ${exitCode.exitCode}${
639
- exitCode.signal != null ? ` (${exitCode.signal})` : ''
640
- }\x1b[0m\r\n`,
641
- })
642
- );
643
- }
644
-
563
+ }
564
+
565
+ if (session.ws && session.ws.readyState === WebSocket.OPEN) {
566
+ let outputData = chunk;
567
+ const cleanChunk = stripAnsiSequences(chunk);
568
+ urlDetectionBuffer = `${urlDetectionBuffer}${cleanChunk}`.slice(-SHELL_URL_PARSE_BUFFER_LIMIT);
569
+
570
+ outputData = outputData.replace(
571
+ /OPEN_URL:\s*(https?:\/\/[^\s\x1b\x07]+)/g,
572
+ '[INFO] Opening in browser: $1'
573
+ );
574
+
575
+ const emitAuthUrl = (detectedUrl: string, autoOpen = false) => {
576
+ const normalizedUrl = normalizeDetectedUrl(detectedUrl);
577
+ if (!normalizedUrl) {
578
+ return;
579
+ }
580
+
581
+ const isNewUrl = !announcedAuthUrls.has(normalizedUrl);
582
+ if (isNewUrl) {
583
+ announcedAuthUrls.add(normalizedUrl);
584
+ session.ws?.send(
585
+ JSON.stringify({
586
+ type: 'auth_url',
587
+ url: normalizedUrl,
588
+ autoOpen,
589
+ })
590
+ );
591
+ }
592
+ };
593
+
594
+ const normalizedDetectedUrls = extractUrlsFromText(urlDetectionBuffer)
595
+ .map((url) => normalizeDetectedUrl(url))
596
+ .filter((url): url is string => Boolean(url));
597
+
598
+ const dedupedDetectedUrls = Array.from(new Set(normalizedDetectedUrls)).filter(
599
+ (url, _, urls) =>
600
+ !urls.some((otherUrl) => otherUrl !== url && otherUrl.startsWith(url))
601
+ );
602
+
603
+ dedupedDetectedUrls.forEach((url) => emitAuthUrl(url, false));
604
+
605
+ if (
606
+ shouldAutoOpenUrlFromOutput(cleanChunk) &&
607
+ dedupedDetectedUrls.length > 0
608
+ ) {
609
+ const bestUrl = dedupedDetectedUrls.reduce((longest, current) =>
610
+ current.length > longest.length ? current : longest
611
+ );
612
+ emitAuthUrl(bestUrl, true);
613
+ }
614
+
615
+ session.ws.send(
616
+ JSON.stringify({
617
+ type: 'output',
618
+ data: outputData,
619
+ })
620
+ );
621
+ }
622
+ });
623
+
624
+ shellProcess.onExit((exitCode) => {
625
+ if (!ptySessionKey) {
626
+ return;
627
+ }
628
+
629
+ const session = ptySessionsMap.get(ptySessionKey);
630
+ if (session && session.pty !== shellProcess) {
631
+ return;
632
+ }
633
+
634
+ if (session && session.ws && session.ws.readyState === WebSocket.OPEN) {
635
+ session.ws.send(
636
+ JSON.stringify({
637
+ type: 'output',
638
+ data: `\r\n\x1b[33mProcess exited with code ${exitCode.exitCode}${
639
+ exitCode.signal != null ? ` (${exitCode.signal})` : ''
640
+ }\x1b[0m\r\n`,
641
+ })
642
+ );
643
+ }
644
+
645
645
  if (session?.timeoutId) {
646
646
  clearTimeout(session.timeoutId);
647
647
  }
648
648
 
649
649
  terminalReplayStore.delete(ptySessionKey);
650
650
  ptySessionsMap.delete(ptySessionKey);
651
- shellProcess = null;
652
- });
653
-
654
- let welcomeMsg = `\x1b[36mStarting terminal in: ${projectPath}\x1b[0m\r\n`;
655
- if (!isPlainShell) {
656
- const providerName =
657
- provider === 'cursor'
658
- ? 'Cursor'
659
- : provider === 'codex'
660
- ? 'Codex'
661
- : provider === 'opencode'
662
- ? 'OpenCode'
663
- : 'Claude';
664
- welcomeMsg = hasSession && resumeSessionId
665
- ? `\x1b[36mResuming ${providerName} session ${resumeSessionId} in: ${projectPath}\x1b[0m\r\n`
666
- : `\x1b[36mStarting new ${providerName} session in: ${projectPath}\x1b[0m\r\n`;
667
- }
668
-
669
- ws.send(
670
- JSON.stringify({
671
- type: 'output',
672
- data: welcomeMsg,
673
- })
674
- );
675
- return;
676
- }
677
-
651
+ shellProcess = null;
652
+ });
653
+
654
+ let welcomeMsg = `\x1b[36mStarting terminal in: ${projectPath}\x1b[0m\r\n`;
655
+ if (!isPlainShell) {
656
+ const providerName =
657
+ provider === 'cursor'
658
+ ? 'Cursor'
659
+ : provider === 'codex'
660
+ ? 'Codex'
661
+ : provider === 'opencode'
662
+ ? 'OpenCode'
663
+ : 'Claude';
664
+ welcomeMsg = hasSession && resumeSessionId
665
+ ? `\x1b[36mResuming ${providerName} session ${resumeSessionId} in: ${projectPath}\x1b[0m\r\n`
666
+ : `\x1b[36mStarting new ${providerName} session in: ${projectPath}\x1b[0m\r\n`;
667
+ }
668
+
669
+ ws.send(
670
+ JSON.stringify({
671
+ type: 'output',
672
+ data: welcomeMsg,
673
+ })
674
+ );
675
+ return;
676
+ }
677
+
678
678
  if (data.type === 'input') {
679
679
  const activeSession = ptySessionKey ? ptySessionsMap.get(ptySessionKey) : null;
680
680
  if (shellProcess && activeSession?.pty === shellProcess && activeSession.ws === ws) {
@@ -723,54 +723,54 @@ export function handleShellConnection(
723
723
  );
724
724
  }
725
725
  }
726
- } catch (error) {
727
- const message = error instanceof Error ? error.message : String(error);
728
- console.error('[ERROR] Shell WebSocket error:', message);
729
- if (ws.readyState === WebSocket.OPEN) {
730
- ws.send(
731
- JSON.stringify({
732
- type: 'output',
733
- data: `\r\n\x1b[31mError: ${message}\x1b[0m\r\n`,
734
- })
735
- );
736
- }
737
- }
738
- });
739
-
740
- ws.on('close', () => {
741
- if (!ptySessionKey) {
742
- return;
743
- }
744
-
745
- const session = ptySessionsMap.get(ptySessionKey);
746
- if (!session) {
747
- return;
748
- }
749
-
750
- // Mobile networks can deliver an old socket's close after its replacement
751
- // has attached. Only the socket that currently owns the PTY may detach it.
752
- if (session.ws !== ws) {
753
- return;
754
- }
755
-
756
- session.ws = null;
757
- if (session.timeoutId) {
758
- clearTimeout(session.timeoutId);
759
- }
760
- session.timeoutId = setTimeout(() => {
761
- // A reconnect may win just as this timer becomes runnable. Re-check the
762
- // active socket so a queued cleanup can never kill a reattached PTY.
763
- if (ptySessionsMap.get(ptySessionKey as string) !== session || session.ws !== null) {
764
- return;
765
- }
766
-
726
+ } catch (error) {
727
+ const message = error instanceof Error ? error.message : String(error);
728
+ console.error('[ERROR] Shell WebSocket error:', message);
729
+ if (ws.readyState === WebSocket.OPEN) {
730
+ ws.send(
731
+ JSON.stringify({
732
+ type: 'output',
733
+ data: `\r\n\x1b[31mError: ${message}\x1b[0m\r\n`,
734
+ })
735
+ );
736
+ }
737
+ }
738
+ });
739
+
740
+ ws.on('close', () => {
741
+ if (!ptySessionKey) {
742
+ return;
743
+ }
744
+
745
+ const session = ptySessionsMap.get(ptySessionKey);
746
+ if (!session) {
747
+ return;
748
+ }
749
+
750
+ // Mobile networks can deliver an old socket's close after its replacement
751
+ // has attached. Only the socket that currently owns the PTY may detach it.
752
+ if (session.ws !== ws) {
753
+ return;
754
+ }
755
+
756
+ session.ws = null;
757
+ if (session.timeoutId) {
758
+ clearTimeout(session.timeoutId);
759
+ }
760
+ session.timeoutId = setTimeout(() => {
761
+ // A reconnect may win just as this timer becomes runnable. Re-check the
762
+ // active socket so a queued cleanup can never kill a reattached PTY.
763
+ if (ptySessionsMap.get(ptySessionKey as string) !== session || session.ws !== null) {
764
+ return;
765
+ }
766
+
767
767
  session.pty.kill();
768
768
  terminalReplayStore.delete(ptySessionKey as string);
769
769
  ptySessionsMap.delete(ptySessionKey as string);
770
- }, PTY_SESSION_TIMEOUT);
771
- });
772
-
773
- ws.on('error', (error) => {
774
- console.error('[ERROR] Shell WebSocket error:', error);
775
- });
776
- }
770
+ }, PTY_SESSION_TIMEOUT);
771
+ });
772
+
773
+ ws.on('error', (error) => {
774
+ console.error('[ERROR] Shell WebSocket error:', error);
775
+ });
776
+ }