conductor-oss 0.18.2 → 0.18.4

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 (254) hide show
  1. package/README.md +1 -21
  2. package/package.json +5 -5
  3. package/web/.next/standalone/packages/web/.next/BUILD_ID +1 -1
  4. package/web/.next/standalone/packages/web/.next/app-path-routes-manifest.json +1 -3
  5. package/web/.next/standalone/packages/web/.next/build-manifest.json +2 -2
  6. package/web/.next/standalone/packages/web/.next/prerender-manifest.json +3 -3
  7. package/web/.next/standalone/packages/web/.next/routes-manifest.json +6 -22
  8. package/web/.next/standalone/packages/web/.next/server/app/_global-error.html +2 -2
  9. package/web/.next/standalone/packages/web/.next/server/app/_global-error.rsc +1 -1
  10. package/web/.next/standalone/packages/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  11. package/web/.next/standalone/packages/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  12. package/web/.next/standalone/packages/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  13. package/web/.next/standalone/packages/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  14. package/web/.next/standalone/packages/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  15. package/web/.next/standalone/packages/web/.next/server/app/_not-found/page/server-reference-manifest.json +7 -7
  16. package/web/.next/standalone/packages/web/.next/server/app/_not-found/page.js.nft.json +1 -1
  17. package/web/.next/standalone/packages/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  18. package/web/.next/standalone/packages/web/.next/server/app/_not-found.html +1 -1
  19. package/web/.next/standalone/packages/web/.next/server/app/_not-found.rsc +4 -4
  20. package/web/.next/standalone/packages/web/.next/server/app/_not-found.segments/_full.segment.rsc +4 -4
  21. package/web/.next/standalone/packages/web/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  22. package/web/.next/standalone/packages/web/.next/server/app/_not-found.segments/_index.segment.rsc +4 -4
  23. package/web/.next/standalone/packages/web/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  24. package/web/.next/standalone/packages/web/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  25. package/web/.next/standalone/packages/web/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  26. package/web/.next/standalone/packages/web/.next/server/app/api/access/route.js +1 -1
  27. package/web/.next/standalone/packages/web/.next/server/app/api/access/route.js.nft.json +1 -1
  28. package/web/.next/standalone/packages/web/.next/server/app/api/agents/route.js +1 -1
  29. package/web/.next/standalone/packages/web/.next/server/app/api/agents/route.js.nft.json +1 -1
  30. package/web/.next/standalone/packages/web/.next/server/app/api/app-update/route.js +1 -1
  31. package/web/.next/standalone/packages/web/.next/server/app/api/app-update/route.js.nft.json +1 -1
  32. package/web/.next/standalone/packages/web/.next/server/app/api/attachments/route.js +1 -1
  33. package/web/.next/standalone/packages/web/.next/server/app/api/attachments/route.js.nft.json +1 -1
  34. package/web/.next/standalone/packages/web/.next/server/app/api/auth/session/route.js +1 -1
  35. package/web/.next/standalone/packages/web/.next/server/app/api/auth/session/route.js.nft.json +1 -1
  36. package/web/.next/standalone/packages/web/.next/server/app/api/boards/comments/route.js +1 -1
  37. package/web/.next/standalone/packages/web/.next/server/app/api/boards/comments/route.js.nft.json +1 -1
  38. package/web/.next/standalone/packages/web/.next/server/app/api/boards/route.js +1 -1
  39. package/web/.next/standalone/packages/web/.next/server/app/api/boards/route.js.nft.json +1 -1
  40. package/web/.next/standalone/packages/web/.next/server/app/api/config/route.js +1 -1
  41. package/web/.next/standalone/packages/web/.next/server/app/api/config/route.js.nft.json +1 -1
  42. package/web/.next/standalone/packages/web/.next/server/app/api/context-files/open/route.js +1 -1
  43. package/web/.next/standalone/packages/web/.next/server/app/api/context-files/open/route.js.nft.json +1 -1
  44. package/web/.next/standalone/packages/web/.next/server/app/api/context-files/route.js +1 -1
  45. package/web/.next/standalone/packages/web/.next/server/app/api/context-files/route.js.nft.json +1 -1
  46. package/web/.next/standalone/packages/web/.next/server/app/api/events/route.js +1 -1
  47. package/web/.next/standalone/packages/web/.next/server/app/api/events/route.js.nft.json +1 -1
  48. package/web/.next/standalone/packages/web/.next/server/app/api/executor/health/route.js +1 -1
  49. package/web/.next/standalone/packages/web/.next/server/app/api/executor/health/route.js.nft.json +1 -1
  50. package/web/.next/standalone/packages/web/.next/server/app/api/filesystem/directory/route.js +1 -1
  51. package/web/.next/standalone/packages/web/.next/server/app/api/filesystem/directory/route.js.nft.json +1 -1
  52. package/web/.next/standalone/packages/web/.next/server/app/api/filesystem/pick-directory/route.js +1 -1
  53. package/web/.next/standalone/packages/web/.next/server/app/api/filesystem/pick-directory/route.js.nft.json +1 -1
  54. package/web/.next/standalone/packages/web/.next/server/app/api/github/repos/route.js +1 -1
  55. package/web/.next/standalone/packages/web/.next/server/app/api/github/repos/route.js.nft.json +1 -1
  56. package/web/.next/standalone/packages/web/.next/server/app/api/github/webhook/route.js +1 -1
  57. package/web/.next/standalone/packages/web/.next/server/app/api/github/webhook/route.js.nft.json +1 -1
  58. package/web/.next/standalone/packages/web/.next/server/app/api/health/boards/route.js +1 -1
  59. package/web/.next/standalone/packages/web/.next/server/app/api/health/boards/route.js.nft.json +1 -1
  60. package/web/.next/standalone/packages/web/.next/server/app/api/health/sessions/route.js +1 -1
  61. package/web/.next/standalone/packages/web/.next/server/app/api/health/sessions/route.js.nft.json +1 -1
  62. package/web/.next/standalone/packages/web/.next/server/app/api/notifications/route.js +1 -1
  63. package/web/.next/standalone/packages/web/.next/server/app/api/notifications/route.js.nft.json +1 -1
  64. package/web/.next/standalone/packages/web/.next/server/app/api/preferences/route.js +1 -1
  65. package/web/.next/standalone/packages/web/.next/server/app/api/preferences/route.js.nft.json +1 -1
  66. package/web/.next/standalone/packages/web/.next/server/app/api/remote-access/route.js +1 -1
  67. package/web/.next/standalone/packages/web/.next/server/app/api/remote-access/route.js.nft.json +1 -1
  68. package/web/.next/standalone/packages/web/.next/server/app/api/repositories/[id]/route.js +1 -1
  69. package/web/.next/standalone/packages/web/.next/server/app/api/repositories/[id]/route.js.nft.json +1 -1
  70. package/web/.next/standalone/packages/web/.next/server/app/api/repositories/route.js +1 -1
  71. package/web/.next/standalone/packages/web/.next/server/app/api/repositories/route.js.nft.json +1 -1
  72. package/web/.next/standalone/packages/web/.next/server/app/api/sessions/[id]/actions/route.js +1 -1
  73. package/web/.next/standalone/packages/web/.next/server/app/api/sessions/[id]/actions/route.js.nft.json +1 -1
  74. package/web/.next/standalone/packages/web/.next/server/app/api/sessions/[id]/archive/route.js +1 -1
  75. package/web/.next/standalone/packages/web/.next/server/app/api/sessions/[id]/archive/route.js.nft.json +1 -1
  76. package/web/.next/standalone/packages/web/.next/server/app/api/sessions/[id]/checks/route.js +1 -1
  77. package/web/.next/standalone/packages/web/.next/server/app/api/sessions/[id]/checks/route.js.nft.json +1 -1
  78. package/web/.next/standalone/packages/web/.next/server/app/api/sessions/[id]/diff/route.js +1 -1
  79. package/web/.next/standalone/packages/web/.next/server/app/api/sessions/[id]/diff/route.js.nft.json +1 -1
  80. package/web/.next/standalone/packages/web/.next/server/app/api/sessions/[id]/feed/route.js +1 -1
  81. package/web/.next/standalone/packages/web/.next/server/app/api/sessions/[id]/feed/route.js.nft.json +1 -1
  82. package/web/.next/standalone/packages/web/.next/server/app/api/sessions/[id]/feedback/route.js +1 -1
  83. package/web/.next/standalone/packages/web/.next/server/app/api/sessions/[id]/feedback/route.js.nft.json +1 -1
  84. package/web/.next/standalone/packages/web/.next/server/app/api/sessions/[id]/files/route.js +1 -1
  85. package/web/.next/standalone/packages/web/.next/server/app/api/sessions/[id]/files/route.js.nft.json +1 -1
  86. package/web/.next/standalone/packages/web/.next/server/app/api/sessions/[id]/interrupt/route.js +1 -1
  87. package/web/.next/standalone/packages/web/.next/server/app/api/sessions/[id]/interrupt/route.js.nft.json +1 -1
  88. package/web/.next/standalone/packages/web/.next/server/app/api/sessions/[id]/kill/route.js +1 -1
  89. package/web/.next/standalone/packages/web/.next/server/app/api/sessions/[id]/kill/route.js.nft.json +1 -1
  90. package/web/.next/standalone/packages/web/.next/server/app/api/sessions/[id]/output/route.js +1 -1
  91. package/web/.next/standalone/packages/web/.next/server/app/api/sessions/[id]/output/route.js.nft.json +1 -1
  92. package/web/.next/standalone/packages/web/.next/server/app/api/sessions/[id]/preview/dom/route.js +1 -1
  93. package/web/.next/standalone/packages/web/.next/server/app/api/sessions/[id]/preview/dom/route.js.nft.json +1 -1
  94. package/web/.next/standalone/packages/web/.next/server/app/api/sessions/[id]/preview/route.js +1 -1
  95. package/web/.next/standalone/packages/web/.next/server/app/api/sessions/[id]/preview/route.js.nft.json +1 -1
  96. package/web/.next/standalone/packages/web/.next/server/app/api/sessions/[id]/preview/screenshot/route.js +1 -1
  97. package/web/.next/standalone/packages/web/.next/server/app/api/sessions/[id]/preview/screenshot/route.js.nft.json +1 -1
  98. package/web/.next/standalone/packages/web/.next/server/app/api/sessions/[id]/restore/route.js +1 -1
  99. package/web/.next/standalone/packages/web/.next/server/app/api/sessions/[id]/restore/route.js.nft.json +1 -1
  100. package/web/.next/standalone/packages/web/.next/server/app/api/sessions/[id]/route.js +1 -1
  101. package/web/.next/standalone/packages/web/.next/server/app/api/sessions/[id]/route.js.nft.json +1 -1
  102. package/web/.next/standalone/packages/web/.next/server/app/api/sessions/[id]/terminal/snapshot/route.js +1 -1
  103. package/web/.next/standalone/packages/web/.next/server/app/api/sessions/[id]/terminal/snapshot/route.js.nft.json +1 -1
  104. package/web/.next/standalone/packages/web/.next/server/app/api/sessions/[id]/terminal/token/route/app-paths-manifest.json +3 -0
  105. package/web/.next/standalone/packages/web/.next/server/app/api/sessions/[id]/terminal/token/route.js +10 -0
  106. package/web/.next/standalone/packages/web/.next/server/app/api/sessions/[id]/terminal/token/route.js.nft.json +1 -0
  107. package/web/.next/standalone/packages/web/.next/server/app/api/sessions/[id]/terminal/token/route_client-reference-manifest.js +2 -0
  108. package/web/.next/standalone/packages/web/.next/server/app/api/sessions/route.js +1 -1
  109. package/web/.next/standalone/packages/web/.next/server/app/api/sessions/route.js.nft.json +1 -1
  110. package/web/.next/standalone/packages/web/.next/server/app/api/spawn/route.js +1 -1
  111. package/web/.next/standalone/packages/web/.next/server/app/api/spawn/route.js.nft.json +1 -1
  112. package/web/.next/standalone/packages/web/.next/server/app/api/workspaces/branches/route.js +1 -1
  113. package/web/.next/standalone/packages/web/.next/server/app/api/workspaces/branches/route.js.nft.json +1 -1
  114. package/web/.next/standalone/packages/web/.next/server/app/api/workspaces/route.js +1 -1
  115. package/web/.next/standalone/packages/web/.next/server/app/api/workspaces/route.js.nft.json +1 -1
  116. package/web/.next/standalone/packages/web/.next/server/app/page/react-loadable-manifest.json +1 -1
  117. package/web/.next/standalone/packages/web/.next/server/app/page/server-reference-manifest.json +7 -7
  118. package/web/.next/standalone/packages/web/.next/server/app/page.js.nft.json +1 -1
  119. package/web/.next/standalone/packages/web/.next/server/app/page_client-reference-manifest.js +1 -1
  120. package/web/.next/standalone/packages/web/.next/server/app/sessions/[id]/page/react-loadable-manifest.json +1 -1
  121. package/web/.next/standalone/packages/web/.next/server/app/sessions/[id]/page/server-reference-manifest.json +7 -7
  122. package/web/.next/standalone/packages/web/.next/server/app/sessions/[id]/page.js.nft.json +1 -1
  123. package/web/.next/standalone/packages/web/.next/server/app/sessions/[id]/page_client-reference-manifest.js +1 -1
  124. package/web/.next/standalone/packages/web/.next/server/app/sign-in/[[...sign-in]]/page/server-reference-manifest.json +7 -7
  125. package/web/.next/standalone/packages/web/.next/server/app/sign-in/[[...sign-in]]/page.js.nft.json +1 -1
  126. package/web/.next/standalone/packages/web/.next/server/app/sign-in/[[...sign-in]]/page_client-reference-manifest.js +1 -1
  127. package/web/.next/standalone/packages/web/.next/server/app/unlock/page/server-reference-manifest.json +7 -7
  128. package/web/.next/standalone/packages/web/.next/server/app/unlock/page.js.nft.json +1 -1
  129. package/web/.next/standalone/packages/web/.next/server/app/unlock/page_client-reference-manifest.js +1 -1
  130. package/web/.next/standalone/packages/web/.next/server/app-paths-manifest.json +1 -3
  131. package/web/.next/standalone/packages/web/.next/server/chunks/26076_server_app_api_sessions_[id]_terminal_token_route_actions_9c4b3c06.js +3 -0
  132. package/web/.next/standalone/packages/web/.next/server/chunks/[root-of-the-server]__63017d21._.js +3 -0
  133. package/web/.next/standalone/packages/web/.next/server/chunks/{[root-of-the-server]__f3d09d5c._.js → [root-of-the-server]__9279c912._.js} +1 -1
  134. package/web/.next/standalone/packages/web/.next/server/chunks/_2c837d66._.js +80 -0
  135. package/web/.next/standalone/packages/web/.next/server/chunks/ssr/[root-of-the-server]__379d412d._.js +1 -1
  136. package/web/.next/standalone/packages/web/.next/server/chunks/ssr/{[root-of-the-server]__da08a50a._.js → [root-of-the-server]__443ba186._.js} +2 -2
  137. package/web/.next/standalone/packages/web/.next/server/chunks/ssr/{[root-of-the-server]__a565f9a3._.js → [root-of-the-server]__742dad30._.js} +2 -2
  138. package/web/.next/standalone/packages/web/.next/server/chunks/ssr/[root-of-the-server]__749fe4b2._.js +1 -1
  139. package/web/.next/standalone/packages/web/.next/server/chunks/ssr/{[root-of-the-server]__992cdcf8._.js → [root-of-the-server]__a8fa29c1._.js} +2 -2
  140. package/web/.next/standalone/packages/web/.next/server/chunks/ssr/_0e1412de._.js +1 -1
  141. package/web/.next/standalone/packages/web/.next/server/chunks/ssr/_532f707d._.js +1 -1
  142. package/web/.next/standalone/packages/web/.next/server/chunks/ssr/_69e05fca._.js +1 -1
  143. package/web/.next/standalone/packages/web/.next/server/chunks/ssr/_80efe193._.js +1 -1
  144. package/web/.next/standalone/packages/web/.next/server/chunks/ssr/{_62d206cc._.js → _9bf43d8d._.js} +2 -2
  145. package/web/.next/standalone/packages/web/.next/server/chunks/ssr/_c0f0e227._.js +1 -1
  146. package/web/.next/standalone/packages/web/.next/server/chunks/ssr/_f36ddaa9._.js +1 -1
  147. package/web/.next/standalone/packages/web/.next/server/chunks/ssr/{node_modules_5646ec2d._.js → node_modules_91aa5708._.js} +1 -1
  148. package/web/.next/standalone/packages/web/.next/server/chunks/ssr/node_modules_@clerk_nextjs_dist_esm_app-router_3964db17._.js +3 -0
  149. package/web/.next/standalone/packages/web/.next/server/chunks/ssr/node_modules_@clerk_nextjs_dist_esm_app-router_5c863a0e._.js +3 -0
  150. package/web/.next/standalone/packages/web/.next/server/chunks/ssr/{node_modules_6d2fa1ea._.js → node_modules_be1275d0._.js} +1 -1
  151. package/web/.next/standalone/packages/web/.next/server/chunks/ssr/packages_web_src_components_sessions_SessionTerminal_tsx_eaf9458b._.js +1 -1
  152. package/web/.next/standalone/packages/web/.next/server/functions-config-manifest.json +2 -4
  153. package/web/.next/standalone/packages/web/.next/server/pages/404.html +1 -1
  154. package/web/.next/standalone/packages/web/.next/server/pages/500.html +2 -2
  155. package/web/.next/standalone/packages/web/.next/server/server-reference-manifest.js +1 -1
  156. package/web/.next/standalone/packages/web/.next/server/server-reference-manifest.json +8 -8
  157. package/web/.next/standalone/packages/web/.next/static/chunks/{c1e720eabb98af26.js → 2037d1500c64fbef.js} +1 -1
  158. package/web/.next/standalone/packages/web/.next/static/chunks/{58a9b117e5684e7c.js → 3ad6d404d5657604.js} +1 -1
  159. package/web/.next/standalone/packages/web/.next/static/chunks/4d288f280972fd06.js +1 -0
  160. package/web/.next/standalone/packages/web/.next/static/chunks/65bc9229d60adf9f.css +4 -0
  161. package/web/.next/standalone/packages/web/.next/static/chunks/97e7e5343941de65.js +1 -0
  162. package/web/.next/{static/chunks/8d05dc3b261207bb.js → standalone/packages/web/.next/static/chunks/ab8cea4266d5034c.js} +1 -1
  163. package/web/.next/standalone/packages/web/.next/static/chunks/{655db4d21daaca4d.js → b2b84b9e8ccbeafa.js} +1 -1
  164. package/web/.next/standalone/packages/web/.next/static/chunks/b9a43bac36046bf9.js +138 -0
  165. package/web/.next/{static/chunks/9331c73d4edcd945.js → standalone/packages/web/.next/static/chunks/d1cbb83a98e765b5.js} +1 -1
  166. package/web/.next/standalone/packages/web/.next/static/chunks/{301802e8e898dd01.js → f2fea305b6822999.js} +1 -1
  167. package/web/.next/standalone/packages/web/.next/static/chunks/f48f57293e98e0d8.js +1 -0
  168. package/web/.next/standalone/packages/web/.next/static/chunks/fe52c44944adc7f2.js +1 -0
  169. package/web/.next/standalone/packages/web/src/app/api/sessions/[id]/terminal/token/route.ts +13 -0
  170. package/web/.next/standalone/packages/web/src/components/sessions/SessionTerminal.tsx +77 -39
  171. package/web/.next/standalone/packages/web/src/components/sessions/sessionTerminalUtils.test.ts +0 -122
  172. package/web/.next/standalone/packages/web/src/components/sessions/sessionTerminalUtils.ts +0 -220
  173. package/web/.next/standalone/packages/web/src/components/sessions/terminal/terminalApi.ts +89 -87
  174. package/web/.next/standalone/packages/web/src/components/sessions/terminal/terminalCache.ts +2 -73
  175. package/web/.next/standalone/packages/web/src/components/sessions/terminal/terminalConstants.ts +0 -8
  176. package/web/.next/standalone/packages/web/src/components/sessions/terminal/terminalTypes.ts +0 -19
  177. package/web/.next/standalone/packages/web/src/components/sessions/terminal/ttydClient.ts +122 -27
  178. package/web/.next/standalone/packages/web/src/components/sessions/terminal/useTtydConnection.ts +9 -12
  179. package/web/.next/standalone/packages/web/src/lib/sessionState.ts +0 -473
  180. package/web/.next/static/chunks/{c1e720eabb98af26.js → 2037d1500c64fbef.js} +1 -1
  181. package/web/.next/static/chunks/{58a9b117e5684e7c.js → 3ad6d404d5657604.js} +1 -1
  182. package/web/.next/static/chunks/4d288f280972fd06.js +1 -0
  183. package/web/.next/static/chunks/65bc9229d60adf9f.css +4 -0
  184. package/web/.next/static/chunks/97e7e5343941de65.js +1 -0
  185. package/web/.next/{standalone/packages/web/.next/static/chunks/8d05dc3b261207bb.js → static/chunks/ab8cea4266d5034c.js} +1 -1
  186. package/web/.next/static/chunks/{655db4d21daaca4d.js → b2b84b9e8ccbeafa.js} +1 -1
  187. package/web/.next/static/chunks/b9a43bac36046bf9.js +138 -0
  188. package/web/.next/{standalone/packages/web/.next/static/chunks/9331c73d4edcd945.js → static/chunks/d1cbb83a98e765b5.js} +1 -1
  189. package/web/.next/static/chunks/{301802e8e898dd01.js → f2fea305b6822999.js} +1 -1
  190. package/web/.next/static/chunks/f48f57293e98e0d8.js +1 -0
  191. package/web/.next/static/chunks/fe52c44944adc7f2.js +1 -0
  192. package/web/.next/standalone/packages/web/.next/server/app/api/sessions/[id]/feed/stream/route/app-paths-manifest.json +0 -3
  193. package/web/.next/standalone/packages/web/.next/server/app/api/sessions/[id]/feed/stream/route.js +0 -10
  194. package/web/.next/standalone/packages/web/.next/server/app/api/sessions/[id]/feed/stream/route.js.nft.json +0 -1
  195. package/web/.next/standalone/packages/web/.next/server/app/api/sessions/[id]/feed/stream/route_client-reference-manifest.js +0 -2
  196. package/web/.next/standalone/packages/web/.next/server/app/api/sessions/[id]/output/stream/route/app-paths-manifest.json +0 -3
  197. package/web/.next/standalone/packages/web/.next/server/app/api/sessions/[id]/output/stream/route/build-manifest.json +0 -11
  198. package/web/.next/standalone/packages/web/.next/server/app/api/sessions/[id]/output/stream/route/server-reference-manifest.json +0 -4
  199. package/web/.next/standalone/packages/web/.next/server/app/api/sessions/[id]/output/stream/route.js +0 -10
  200. package/web/.next/standalone/packages/web/.next/server/app/api/sessions/[id]/output/stream/route.js.map +0 -5
  201. package/web/.next/standalone/packages/web/.next/server/app/api/sessions/[id]/output/stream/route.js.nft.json +0 -1
  202. package/web/.next/standalone/packages/web/.next/server/app/api/sessions/[id]/output/stream/route_client-reference-manifest.js +0 -2
  203. package/web/.next/standalone/packages/web/.next/server/app/api/sessions/[id]/terminal/connection/route/app-paths-manifest.json +0 -3
  204. package/web/.next/standalone/packages/web/.next/server/app/api/sessions/[id]/terminal/connection/route/build-manifest.json +0 -11
  205. package/web/.next/standalone/packages/web/.next/server/app/api/sessions/[id]/terminal/connection/route/server-reference-manifest.json +0 -4
  206. package/web/.next/standalone/packages/web/.next/server/app/api/sessions/[id]/terminal/connection/route.js +0 -10
  207. package/web/.next/standalone/packages/web/.next/server/app/api/sessions/[id]/terminal/connection/route.js.map +0 -5
  208. package/web/.next/standalone/packages/web/.next/server/app/api/sessions/[id]/terminal/connection/route.js.nft.json +0 -1
  209. package/web/.next/standalone/packages/web/.next/server/app/api/sessions/[id]/terminal/connection/route_client-reference-manifest.js +0 -2
  210. package/web/.next/standalone/packages/web/.next/server/chunks/26076_server_app_api_sessions_[id]_terminal_connection_route_actions_46c114ee.js +0 -3
  211. package/web/.next/standalone/packages/web/.next/server/chunks/29f24__next-internal_server_app_api_sessions_[id]_feed_stream_route_actions_1262f517.js +0 -3
  212. package/web/.next/standalone/packages/web/.next/server/chunks/43d70_next-internal_server_app_api_sessions_[id]_output_stream_route_actions_9bfa500e.js +0 -3
  213. package/web/.next/standalone/packages/web/.next/server/chunks/[root-of-the-server]__1029f927._.js +0 -3
  214. package/web/.next/standalone/packages/web/.next/server/chunks/[root-of-the-server]__d74c0f7a._.js +0 -3
  215. package/web/.next/standalone/packages/web/.next/server/chunks/[root-of-the-server]__ddad8d14._.js +0 -3
  216. package/web/.next/standalone/packages/web/.next/server/chunks/[root-of-the-server]__ede5c8ca._.js +0 -3
  217. package/web/.next/standalone/packages/web/.next/server/chunks/[root-of-the-server]__f56e5b36._.js +0 -3
  218. package/web/.next/standalone/packages/web/.next/server/chunks/_24c4e75d._.js +0 -80
  219. package/web/.next/standalone/packages/web/.next/server/chunks/_3d39aff4._.js +0 -80
  220. package/web/.next/standalone/packages/web/.next/server/chunks/ssr/_307d7608._.js +0 -3
  221. package/web/.next/standalone/packages/web/.next/server/chunks/ssr/_3ed93faf._.js +0 -3
  222. package/web/.next/standalone/packages/web/.next/server/chunks/ssr/node_modules_@clerk_nextjs_dist_esm_app-router_4f296b1d._.js +0 -3
  223. package/web/.next/standalone/packages/web/.next/server/chunks/ssr/node_modules_@clerk_nextjs_dist_esm_app-router_599a1810._.js +0 -3
  224. package/web/.next/standalone/packages/web/.next/static/chunks/06eb75e40dff98f1.css +0 -4
  225. package/web/.next/standalone/packages/web/.next/static/chunks/1382eff030c401e3.js +0 -1
  226. package/web/.next/standalone/packages/web/.next/static/chunks/1684a3f76eefe776.js +0 -1
  227. package/web/.next/standalone/packages/web/.next/static/chunks/267e541b481c3c75.js +0 -1
  228. package/web/.next/standalone/packages/web/.next/static/chunks/810a3d36795ae9fd.js +0 -138
  229. package/web/.next/standalone/packages/web/.next/static/chunks/a8cd591e904d769e.js +0 -1
  230. package/web/.next/standalone/packages/web/src/app/api/sessions/[id]/feed/stream/route.ts +0 -80
  231. package/web/.next/standalone/packages/web/src/app/api/sessions/[id]/output/stream/route.ts +0 -80
  232. package/web/.next/standalone/packages/web/src/app/api/sessions/[id]/terminal/connection/route.test.ts +0 -343
  233. package/web/.next/standalone/packages/web/src/app/api/sessions/[id]/terminal/connection/route.ts +0 -120
  234. package/web/.next/standalone/packages/web/src/components/Dashboard.tsx +0 -3444
  235. package/web/.next/standalone/packages/web/src/components/TerminalView.tsx +0 -770
  236. package/web/.next/standalone/packages/web/src/components/sessions/ChatPanel.tsx +0 -2097
  237. package/web/.next/standalone/packages/web/src/hooks/useSessionFeed.ts +0 -10
  238. package/web/.next/standalone/packages/web/src/hooks/useSessionOutputStream.ts +0 -166
  239. package/web/.next/standalone/packages/web/src/lib/chatFeed.ts +0 -196
  240. package/web/.next/static/chunks/06eb75e40dff98f1.css +0 -4
  241. package/web/.next/static/chunks/1382eff030c401e3.js +0 -1
  242. package/web/.next/static/chunks/1684a3f76eefe776.js +0 -1
  243. package/web/.next/static/chunks/267e541b481c3c75.js +0 -1
  244. package/web/.next/static/chunks/810a3d36795ae9fd.js +0 -138
  245. package/web/.next/static/chunks/a8cd591e904d769e.js +0 -1
  246. /package/web/.next/standalone/packages/web/.next/server/app/api/sessions/[id]/{feed/stream → terminal/token}/route/build-manifest.json +0 -0
  247. /package/web/.next/standalone/packages/web/.next/server/app/api/sessions/[id]/{feed/stream → terminal/token}/route/server-reference-manifest.json +0 -0
  248. /package/web/.next/standalone/packages/web/.next/server/app/api/sessions/[id]/{feed/stream → terminal/token}/route.js.map +0 -0
  249. /package/web/.next/standalone/packages/web/.next/static/{FHjp9qazH2xWUCRt6mqg4 → O7I3Iz18_tPidBRAWeKh8}/_buildManifest.js +0 -0
  250. /package/web/.next/standalone/packages/web/.next/static/{FHjp9qazH2xWUCRt6mqg4 → O7I3Iz18_tPidBRAWeKh8}/_clientMiddlewareManifest.json +0 -0
  251. /package/web/.next/standalone/packages/web/.next/static/{FHjp9qazH2xWUCRt6mqg4 → O7I3Iz18_tPidBRAWeKh8}/_ssgManifest.js +0 -0
  252. /package/web/.next/static/{FHjp9qazH2xWUCRt6mqg4 → O7I3Iz18_tPidBRAWeKh8}/_buildManifest.js +0 -0
  253. /package/web/.next/static/{FHjp9qazH2xWUCRt6mqg4 → O7I3Iz18_tPidBRAWeKh8}/_clientMiddlewareManifest.json +0 -0
  254. /package/web/.next/static/{FHjp9qazH2xWUCRt6mqg4 → O7I3Iz18_tPidBRAWeKh8}/_ssgManifest.js +0 -0
@@ -2,24 +2,13 @@ import assert from "node:assert/strict";
2
2
  import test from "node:test";
3
3
  import { TERMINAL_FONT_FAMILY } from "@/components/terminal/xtermTheme";
4
4
  import {
5
- buildTerminalSnapshotPayload,
6
5
  calculateMobileTerminalViewportMetrics,
7
- decodeTerminalBase64Payload,
8
6
  detectCompactTerminalChrome,
9
7
  detectMobileTerminalInputRail,
10
8
  getSessionTerminalViewportOptions,
11
- normalizeTerminalSnapshot,
12
- parseTerminalBinaryFrame,
13
- prependTerminalModes,
14
9
  sanitizeRemoteTerminalSnapshot,
15
- stripBrowserTerminalResponses,
16
10
  } from "./sessionTerminalUtils";
17
11
 
18
- test("decodeTerminalBase64Payload decodes terminal stream payload bytes", () => {
19
- const bytes = decodeTerminalBase64Payload("aGVsbG8=");
20
- assert.deepEqual(Array.from(bytes), [104, 101, 108, 108, 111]);
21
- });
22
-
23
12
  test("detectMobileTerminalInputRail only enables compact touch layouts on narrow viewports", () => {
24
13
  assert.equal(detectMobileTerminalInputRail(390, true, 1), true);
25
14
  assert.equal(detectMobileTerminalInputRail(390, false, 1), true);
@@ -42,122 +31,11 @@ test("detectCompactTerminalChrome activates immersive mode below lg breakpoint (
42
31
  assert.equal(detectCompactTerminalChrome(844, 390, false, 1), true);
43
32
  });
44
33
 
45
- test("stripBrowserTerminalResponses removes browser-generated device status chatter", () => {
46
- const raw = "\u001b[Ihello\u001b[12;34R\u001b[?1;2cworld\u001b]11;rgb:0000/0000/0000\u0007";
47
- assert.equal(stripBrowserTerminalResponses(raw), "helloworld");
48
- });
49
-
50
34
  test("sanitizeRemoteTerminalSnapshot strips ANSI control sequences and normalizes newlines", () => {
51
35
  const raw = "\u001b[31merror\u001b[0m\r\nnext\rline\u0000";
52
36
  assert.equal(sanitizeRemoteTerminalSnapshot(raw), "error\nnext\nline");
53
37
  });
54
38
 
55
- test("normalizeTerminalSnapshot converts LF-only snapshots to CRLF for xterm replay", () => {
56
- assert.equal(normalizeTerminalSnapshot("one\ntwo"), "one\r\ntwo");
57
- assert.equal(normalizeTerminalSnapshot("one\r\ntwo"), "one\r\ntwo");
58
- });
59
-
60
- test("buildTerminalSnapshotPayload prefixes mobile/web restore modes before the snapshot bytes", () => {
61
- const payload = buildTerminalSnapshotPayload("prompt> ", {
62
- alternateScreen: true,
63
- applicationKeypad: true,
64
- applicationCursor: true,
65
- hideCursor: true,
66
- bracketedPaste: true,
67
- mouseProtocolMode: "AnyMotion",
68
- mouseProtocolEncoding: "Sgr",
69
- });
70
- const text = new TextDecoder().decode(payload);
71
-
72
- assert.match(text, /\u001b\[\?1049h/);
73
- assert.match(text, /\u001b\[\?2004h/);
74
- assert.match(text, /\u001b\[\?1003h/);
75
- assert.match(text, /\u001b\[\?1006h/);
76
- assert.ok(text.endsWith("prompt> "));
77
- });
78
-
79
- test("prependTerminalModes keeps stream payload untouched when no modes are available", () => {
80
- const payload = new TextEncoder().encode("plain");
81
- assert.deepEqual(prependTerminalModes(payload), payload);
82
- });
83
-
84
- test("parseTerminalBinaryFrame decodes restore frames with explicit mode metadata", () => {
85
- const payload = new TextEncoder().encode("prompt> ");
86
- const frame = new Uint8Array(24 + payload.length);
87
- frame.set([0x43, 0x54, 0x50, 0x32, 2, 1], 0);
88
- const view = new DataView(frame.buffer);
89
- view.setBigUint64(6, 42n, false);
90
- view.setUint8(14, 1);
91
- view.setUint8(15, 2);
92
- view.setUint16(16, 120, false);
93
- view.setUint16(18, 32, false);
94
- view.setUint8(20, 0b0001_1101);
95
- view.setUint8(21, 4);
96
- view.setUint8(22, 2);
97
- frame.set(payload, 24);
98
-
99
- const parsed = parseTerminalBinaryFrame(frame.buffer);
100
- assert.deepEqual(parsed, {
101
- kind: "restore",
102
- sequence: 42,
103
- snapshotVersion: 1,
104
- reason: "lagged",
105
- cols: 120,
106
- rows: 32,
107
- modes: {
108
- alternateScreen: true,
109
- applicationKeypad: false,
110
- applicationCursor: true,
111
- hideCursor: true,
112
- bracketedPaste: true,
113
- mouseProtocolMode: "AnyMotion",
114
- mouseProtocolEncoding: "Sgr",
115
- },
116
- payload,
117
- });
118
- });
119
-
120
- test("parseTerminalBinaryFrame still accepts legacy restore frames", () => {
121
- const payload = new TextEncoder().encode("prompt> ");
122
- const frame = new Uint8Array(20 + payload.length);
123
- frame.set([0x43, 0x54, 0x50, 0x32, 1, 1], 0);
124
- const view = new DataView(frame.buffer);
125
- view.setBigUint64(6, 42n, false);
126
- view.setUint8(14, 1);
127
- view.setUint8(15, 2);
128
- view.setUint16(16, 120, false);
129
- view.setUint16(18, 32, false);
130
- frame.set(payload, 20);
131
-
132
- const parsed = parseTerminalBinaryFrame(frame.buffer);
133
- assert.deepEqual(parsed, {
134
- kind: "restore",
135
- sequence: 42,
136
- snapshotVersion: 1,
137
- reason: "lagged",
138
- cols: 120,
139
- rows: 32,
140
- modes: undefined,
141
- payload,
142
- });
143
- });
144
-
145
- test("parseTerminalBinaryFrame decodes stream frames", () => {
146
- const payload = new TextEncoder().encode("line\r\n");
147
- const frame = new Uint8Array(14 + payload.length);
148
- frame.set([0x43, 0x54, 0x50, 0x32, 2, 2], 0);
149
- const view = new DataView(frame.buffer);
150
- view.setBigUint64(6, 7n, false);
151
- frame.set(payload, 14);
152
-
153
- const parsed = parseTerminalBinaryFrame(frame.buffer);
154
- assert.deepEqual(parsed, {
155
- kind: "stream",
156
- sequence: 7,
157
- payload,
158
- });
159
- });
160
-
161
39
  test("getSessionTerminalViewportOptions keeps compact fonts for phones and larger fonts for desktop", () => {
162
40
  assert.deepEqual(getSessionTerminalViewportOptions(390), {
163
41
  fontFamily: "'SF Mono', Menlo, Monaco, monospace",
@@ -25,228 +25,8 @@ export type TerminalModeState = {
25
25
  const MOBILE_TERMINAL_INPUT_MAX_WIDTH_PX = 1024;
26
26
  /** Must match the Tailwind `lg:` breakpoint (1024px) used in SessionTerminal / SessionDetail. */
27
27
  const COMPACT_TERMINAL_CHROME_MAX_WIDTH_PX = 1024;
28
- const TERMINAL_FRAME_MAGIC = [0x43, 0x54, 0x50, 0x32] as const;
29
- const TERMINAL_FRAME_PROTOCOL_VERSION = 2;
30
- const TERMINAL_FRAME_KIND_RESTORE = 1;
31
- const TERMINAL_FRAME_KIND_STREAM = 2;
32
- const TERMINAL_STREAM_FRAME_HEADER_BYTES = 14;
33
- const TERMINAL_RESTORE_FRAME_HEADER_BYTES_V1 = 20;
34
- const TERMINAL_RESTORE_FRAME_HEADER_BYTES_V2 = 24;
35
- const BROWSER_TERMINAL_RESPONSE_PATTERNS = [
36
- /\x1b\[(?:I|O)/g,
37
- /\x1b\[\d+;\d+R/g,
38
- /\x1b\[(?:[?>])[\d;]*c/g,
39
- /\x1b\](?:10|11|12|4;\d+);[\s\S]*?(?:\x07|\x1b\\)/g,
40
- ];
41
28
  const ANSI_ESCAPE_PATTERN = /\u001b(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~]|\][\s\S]*?(?:\u0007|\u001b\\))/g;
42
29
 
43
- export type TerminalBinaryFrame =
44
- | {
45
- kind: "restore";
46
- sequence: number;
47
- snapshotVersion: number;
48
- reason: "attach" | "lagged" | "unknown";
49
- cols: number;
50
- rows: number;
51
- modes?: TerminalModeState;
52
- payload: Uint8Array;
53
- }
54
- | {
55
- kind: "stream";
56
- sequence: number;
57
- payload: Uint8Array;
58
- };
59
-
60
- function decodeTerminalRestoreReason(code: number): "attach" | "lagged" | "unknown" {
61
- if (code === 1) {
62
- return "attach";
63
- }
64
- if (code === 2) {
65
- return "lagged";
66
- }
67
- return "unknown";
68
- }
69
-
70
- function decodeTerminalMouseProtocolMode(code: number): string {
71
- if (code === 1) return "Press";
72
- if (code === 2) return "PressRelease";
73
- if (code === 3) return "ButtonMotion";
74
- if (code === 4) return "AnyMotion";
75
- return "None";
76
- }
77
-
78
- function decodeTerminalMouseProtocolEncoding(code: number): string {
79
- if (code === 1) return "Utf8";
80
- if (code === 2) return "Sgr";
81
- if (code === 3) return "Urxvt";
82
- return "Default";
83
- }
84
-
85
- function decodeTerminalModes(flags: number, mouseModeCode: number, mouseEncodingCode: number): TerminalModeState {
86
- return {
87
- alternateScreen: (flags & (1 << 0)) !== 0,
88
- applicationKeypad: (flags & (1 << 1)) !== 0,
89
- applicationCursor: (flags & (1 << 2)) !== 0,
90
- hideCursor: (flags & (1 << 3)) !== 0,
91
- bracketedPaste: (flags & (1 << 4)) !== 0,
92
- mouseProtocolMode: decodeTerminalMouseProtocolMode(mouseModeCode),
93
- mouseProtocolEncoding: decodeTerminalMouseProtocolEncoding(mouseEncodingCode),
94
- };
95
- }
96
-
97
- function concatTerminalPayload(prefix: Uint8Array, payload: Uint8Array): Uint8Array {
98
- if (prefix.byteLength === 0) {
99
- return payload;
100
- }
101
-
102
- if (payload.byteLength === 0) {
103
- return prefix;
104
- }
105
-
106
- const merged = new Uint8Array(prefix.byteLength + payload.byteLength);
107
- merged.set(prefix, 0);
108
- merged.set(payload, prefix.byteLength);
109
- return merged;
110
- }
111
-
112
- export function encodeTerminalModesPrefix(modes?: TerminalModeState | null): Uint8Array {
113
- if (!modes) {
114
- return new Uint8Array(0);
115
- }
116
-
117
- const sequences = [
118
- modes.alternateScreen ? "\u001b[?1049h" : "\u001b[?1049l",
119
- modes.applicationCursor ? "\u001b[?1h" : "\u001b[?1l",
120
- modes.applicationKeypad ? "\u001b=" : "\u001b>",
121
- modes.hideCursor ? "\u001b[?25l" : "\u001b[?25h",
122
- modes.bracketedPaste ? "\u001b[?2004h" : "\u001b[?2004l",
123
- "\u001b[?1000l\u001b[?1002l\u001b[?1003l\u001b[?1005l\u001b[?1006l\u001b[?1015l",
124
- ];
125
-
126
- if (modes.mouseProtocolMode === "Press") {
127
- sequences.push("\u001b[?1000h");
128
- } else if (modes.mouseProtocolMode === "ButtonMotion") {
129
- sequences.push("\u001b[?1002h");
130
- } else if (modes.mouseProtocolMode === "AnyMotion") {
131
- sequences.push("\u001b[?1003h");
132
- } else if (modes.mouseProtocolMode === "PressRelease") {
133
- sequences.push("\u001b[?1000h");
134
- }
135
-
136
- if (modes.mouseProtocolEncoding === "Utf8") {
137
- sequences.push("\u001b[?1005h");
138
- } else if (modes.mouseProtocolEncoding === "Sgr") {
139
- sequences.push("\u001b[?1006h");
140
- } else if (modes.mouseProtocolEncoding === "Urxvt") {
141
- sequences.push("\u001b[?1015h");
142
- }
143
-
144
- return new TextEncoder().encode(sequences.join(""));
145
- }
146
-
147
- export function prependTerminalModes(
148
- payload: Uint8Array,
149
- modes?: TerminalModeState | null,
150
- ): Uint8Array {
151
- return concatTerminalPayload(encodeTerminalModesPrefix(modes), payload);
152
- }
153
-
154
- export function buildTerminalSnapshotPayload(
155
- snapshot: string,
156
- modes?: TerminalModeState | null,
157
- ): Uint8Array {
158
- return prependTerminalModes(new TextEncoder().encode(normalizeTerminalSnapshot(snapshot)), modes);
159
- }
160
-
161
- export function decodeTerminalBase64Payload(value: string): Uint8Array {
162
- const normalized = value.trim();
163
- if (normalized.length === 0) {
164
- return new Uint8Array(0);
165
- }
166
-
167
- if (typeof atob === "function") {
168
- const decoded = atob(normalized);
169
- const bytes = new Uint8Array(decoded.length);
170
- for (let index = 0; index < decoded.length; index += 1) {
171
- bytes[index] = decoded.charCodeAt(index);
172
- }
173
- return bytes;
174
- }
175
-
176
- if (typeof Buffer !== "undefined") {
177
- return new Uint8Array(Buffer.from(normalized, "base64"));
178
- }
179
-
180
- throw new Error("A base64 decoder is not available in this environment");
181
- }
182
-
183
- export function parseTerminalBinaryFrame(buffer: ArrayBuffer): TerminalBinaryFrame {
184
- const bytes = new Uint8Array(buffer);
185
- if (bytes.byteLength < TERMINAL_STREAM_FRAME_HEADER_BYTES) {
186
- throw new Error("Terminal frame was shorter than the stream header");
187
- }
188
-
189
- for (let index = 0; index < TERMINAL_FRAME_MAGIC.length; index += 1) {
190
- if (bytes[index] !== TERMINAL_FRAME_MAGIC[index]) {
191
- throw new Error("Terminal frame magic did not match the frame protocol");
192
- }
193
- }
194
-
195
- const view = new DataView(buffer);
196
- const version = view.getUint8(4);
197
- if (version !== 1 && version !== TERMINAL_FRAME_PROTOCOL_VERSION) {
198
- throw new Error(`Unsupported terminal frame protocol version: ${version}`);
199
- }
200
-
201
- const kind = view.getUint8(5);
202
- const sequence = Number(view.getBigUint64(6, false));
203
- if (!Number.isSafeInteger(sequence)) {
204
- throw new Error("Terminal frame sequence exceeded the safe integer range");
205
- }
206
-
207
- if (kind === TERMINAL_FRAME_KIND_RESTORE) {
208
- const headerBytes = version >= 2 ? TERMINAL_RESTORE_FRAME_HEADER_BYTES_V2 : TERMINAL_RESTORE_FRAME_HEADER_BYTES_V1;
209
- if (bytes.byteLength < headerBytes) {
210
- throw new Error("Terminal restore frame was shorter than the restore header");
211
- }
212
-
213
- return {
214
- kind: "restore",
215
- sequence,
216
- snapshotVersion: view.getUint8(14),
217
- reason: decodeTerminalRestoreReason(view.getUint8(15)),
218
- cols: view.getUint16(16, false),
219
- rows: view.getUint16(18, false),
220
- modes: version >= 2
221
- ? decodeTerminalModes(view.getUint8(20), view.getUint8(21), view.getUint8(22))
222
- : undefined,
223
- payload: bytes.slice(headerBytes),
224
- };
225
- }
226
-
227
- if (kind === TERMINAL_FRAME_KIND_STREAM) {
228
- return {
229
- kind: "stream",
230
- sequence,
231
- payload: bytes.slice(TERMINAL_STREAM_FRAME_HEADER_BYTES),
232
- };
233
- }
234
-
235
- throw new Error(`Unsupported terminal frame kind: ${kind}`);
236
- }
237
-
238
- export function normalizeTerminalSnapshot(snapshot: string): string {
239
- return snapshot.replace(/\r?\n/g, "\r\n");
240
- }
241
-
242
- export function stripBrowserTerminalResponses(data: string): string {
243
- let sanitized = data;
244
- for (const pattern of BROWSER_TERMINAL_RESPONSE_PATTERNS) {
245
- sanitized = sanitized.replace(pattern, "");
246
- }
247
- return sanitized;
248
- }
249
-
250
30
  export function sanitizeRemoteTerminalSnapshot(snapshot: string): string {
251
31
  return snapshot
252
32
  .replace(ANSI_ESCAPE_PATTERN, "")
@@ -1,109 +1,111 @@
1
1
  /**
2
- * Fetch/API wrappers for terminal operations:
3
- * connection info and snapshot fetch. All I/O goes through TTyD WebSocket.
2
+ * Terminal API utilities.
3
+ * Connection resolution is client-side for the WS URL the browser builds
4
+ * the ttyd endpoint directly. Authentication still uses a lightweight
5
+ * Next.js proxy to obtain a short-lived terminal token.
4
6
  */
5
7
 
6
- import {
7
- readCachedTerminalConnection,
8
- storeCachedTerminalConnection,
9
- } from "./terminalCache";
10
- import type {
11
- TerminalConnectionInfo,
12
- TerminalSnapshot,
13
- } from "./terminalTypes";
14
- import type { TerminalModeState } from "../sessionTerminalUtils";
15
-
16
- export function parseTerminalModes(value: unknown): TerminalModeState | undefined {
17
- if (!value || typeof value !== "object") {
18
- return undefined;
19
- }
8
+ import type { TerminalConnectionInfo } from "./terminalTypes";
20
9
 
21
- const candidate = value as Record<string, unknown>;
22
- const mouseProtocolMode = typeof candidate["mouseProtocolMode"] === "string"
23
- ? candidate["mouseProtocolMode"]
24
- : "None";
25
- const mouseProtocolEncoding = typeof candidate["mouseProtocolEncoding"] === "string"
26
- ? candidate["mouseProtocolEncoding"]
27
- : "Default";
10
+ // ---------------------------------------------------------------------------
11
+ // Direct backend URL resolution no server round-trip
12
+ // ---------------------------------------------------------------------------
28
13
 
29
- return {
30
- alternateScreen: candidate["alternateScreen"] === true,
31
- applicationKeypad: candidate["applicationKeypad"] === true,
32
- applicationCursor: candidate["applicationCursor"] === true,
33
- hideCursor: candidate["hideCursor"] === true,
34
- bracketedPaste: candidate["bracketedPaste"] === true,
35
- mouseProtocolMode,
36
- mouseProtocolEncoding,
37
- };
38
- }
14
+ /**
15
+ * Resolve the Rust backend origin from the browser.
16
+ *
17
+ * Priority:
18
+ * 1. `NEXT_PUBLIC_CONDUCTOR_BACKEND_URL` (build-time env var)
19
+ * 2. Dev mode heuristic: if the page is served from port 3000 the backend
20
+ * lives on the same hostname at port 4749.
21
+ * 3. Same origin (production: Rust backend serves the dashboard).
22
+ */
23
+ function resolveBackendOrigin(): string {
24
+ // Build-time env var (NEXT_PUBLIC_ prefix exposes it to the client bundle)
25
+ const envUrl = process.env.NEXT_PUBLIC_CONDUCTOR_BACKEND_URL?.trim();
26
+ if (envUrl) return envUrl;
39
27
 
40
- export async function fetchTerminalConnection(sessionId: string): Promise<TerminalConnectionInfo> {
41
- const cached = readCachedTerminalConnection(sessionId);
42
- if (cached) {
43
- return cached;
44
- }
28
+ if (typeof window === "undefined") return "http://127.0.0.1:4749";
45
29
 
46
- const response = await fetch(`/api/sessions/${encodeURIComponent(sessionId)}/terminal/connection`, {
47
- cache: "no-store",
48
- });
49
- const data = (await response.json().catch(() => null)) as
50
- | {
51
- ptyWsUrl?: string | null;
52
- interactive?: boolean;
53
- error?: string;
54
- }
55
- | null;
56
- if (!response.ok) {
57
- throw new Error(data?.error ?? `Failed to resolve terminal connection: ${response.status}`);
30
+ const { protocol, hostname, port } = window.location;
31
+
32
+ // Dev mode: Next.js dev server on :3000, Rust backend on :4749
33
+ if (port === "3000") {
34
+ return `${protocol}//${hostname}:4749`;
58
35
  }
59
36
 
60
- const connection: TerminalConnectionInfo = {
61
- ptyWsUrl:
62
- typeof data?.ptyWsUrl === "string" && data.ptyWsUrl.trim().length > 0
63
- ? data.ptyWsUrl.trim()
64
- : null,
65
- interactive: data?.interactive === true,
66
- };
67
- storeCachedTerminalConnection(sessionId, connection);
68
- return connection;
37
+ // Production / standalone: backend serves dashboard at the same origin
38
+ return window.location.origin;
69
39
  }
70
40
 
71
- export async function fetchTerminalSnapshot(sessionId: string, lines: number): Promise<TerminalSnapshot> {
72
- const response = await fetch(`/api/sessions/${encodeURIComponent(sessionId)}/terminal/snapshot?lines=${lines}`, {
41
+ /**
42
+ * Fetch a terminal token via the Next.js proxy (which handles auth).
43
+ * The token is required by the Rust backend WebSocket endpoint.
44
+ * Fail closed on proxy or backend errors so the browser does not attempt an
45
+ * unauthenticated ttyd websocket with a missing or stale token.
46
+ */
47
+ type TerminalTokenResult =
48
+ | { interactive: true; token: string | null }
49
+ | { interactive: false; token: null };
50
+
51
+ async function fetchTerminalToken(sessionId: string): Promise<TerminalTokenResult> {
52
+ const res = await fetch(`/api/sessions/${encodeURIComponent(sessionId)}/terminal/token`, {
73
53
  cache: "no-store",
74
54
  });
75
- const data = (await response.json().catch(() => null)) as
76
- | { snapshot?: string; transcript?: string; source?: string; live?: boolean; restored?: boolean; sequence?: number; modes?: unknown; error?: string }
55
+ const data = (await res.json().catch(() => null)) as
56
+ | { token?: string; required?: boolean; error?: string }
77
57
  | null;
78
- if (!response.ok) {
79
- throw new Error(data?.error ?? `Failed to resolve terminal snapshot: ${response.status}`);
58
+
59
+ if (res.status === 401 || res.status === 403) {
60
+ return {
61
+ interactive: false,
62
+ token: null,
63
+ };
64
+ }
65
+
66
+ if (!res.ok) {
67
+ throw new Error(data?.error ?? `Failed to resolve terminal token: ${res.status}`);
80
68
  }
81
- const rawSnapshot = typeof data?.snapshot === "string" ? data.snapshot : "";
82
- const transcript = typeof data?.transcript === "string" ? data.transcript : "";
83
- const compactedSnapshot = transcript.trim().length > 0 ? transcript : rawSnapshot;
69
+
70
+ if (data?.required !== true) {
71
+ return {
72
+ interactive: true,
73
+ token: null,
74
+ };
75
+ }
76
+
77
+ const token = typeof data?.token === "string" ? data.token.trim() : "";
78
+ if (token.length === 0) {
79
+ throw new Error("Terminal token response did not include a ttyd token");
80
+ }
81
+
84
82
  return {
85
- snapshot: compactedSnapshot,
86
- transcript: "",
87
- source: typeof data?.source === "string" ? data.source : "empty",
88
- live: data?.live === true,
89
- restored: data?.restored === true,
90
- sequence: typeof data?.sequence === "number" && Number.isSafeInteger(data.sequence)
91
- ? data.sequence
92
- : null,
93
- modes: parseTerminalModes(data?.modes),
83
+ interactive: true,
84
+ token,
94
85
  };
95
86
  }
96
87
 
97
- export async function fetchSessionStatus(sessionId: string): Promise<string | null> {
98
- const response = await fetch(`/api/sessions/${encodeURIComponent(sessionId)}`, {
99
- cache: "no-store",
100
- });
101
- if (!response.ok) {
102
- throw new Error(`Failed to load session: ${response.status}`);
88
+ /**
89
+ * Build the direct ttyd WebSocket URL for a session.
90
+ *
91
+ * 1. Fetch a short-lived token via Next.js proxy (one HTTP call, handles auth).
92
+ * 2. Construct the WebSocket URL pointing directly at the Rust backend.
93
+ *
94
+ * The WebSocket stream itself is direct — no middleware in the data path.
95
+ */
96
+ export async function resolveTerminalConnection(sessionId: string): Promise<TerminalConnectionInfo> {
97
+ const origin = resolveBackendOrigin();
98
+ const wsProtocol = origin.startsWith("https") ? "wss:" : "ws:";
99
+ const url = new URL(origin);
100
+ const auth = await fetchTerminalToken(sessionId);
101
+ if (!auth.interactive) {
102
+ return {
103
+ ptyWsUrl: null,
104
+ interactive: false,
105
+ };
103
106
  }
107
+ const tokenParam = auth.token ? `&token=${encodeURIComponent(auth.token)}` : "";
108
+ const ptyWsUrl = `${wsProtocol}//${url.host}/api/sessions/${encodeURIComponent(sessionId)}/terminal/ws?protocol=ttyd${tokenParam}`;
104
109
 
105
- const data = (await response.json().catch(() => null)) as { status?: unknown } | null;
106
- return typeof data?.status === "string" && data.status.trim().length > 0
107
- ? data.status.trim()
108
- : null;
110
+ return { ptyWsUrl, interactive: true };
109
111
  }
@@ -1,26 +1,13 @@
1
1
  /**
2
- * Module-level Map caches and TTL logic for terminal connections,
3
- * snapshots, and UI state.
2
+ * Module-level cache and TTL logic for terminal UI state.
4
3
  */
5
4
 
6
5
  import {
7
- TERMINAL_CONNECTION_CACHE_MAX_TTL_MS,
8
- TERMINAL_CONNECTION_CACHE_MAX_ENTRIES,
9
- TERMINAL_SNAPSHOT_CACHE_MAX_ENTRIES,
10
6
  TERMINAL_UI_STATE_CACHE_MAX_ENTRIES,
11
- TERMINAL_SNAPSHOT_CACHE_MAX_AGE_MS,
12
7
  TERMINAL_UI_STATE_CACHE_MAX_AGE_MS,
13
8
  } from "./terminalConstants";
14
- import type {
15
- TerminalConnectionInfo,
16
- TerminalSnapshot,
17
- CachedTerminalConnection,
18
- CachedTerminalSnapshot,
19
- CachedTerminalUiState,
20
- } from "./terminalTypes";
9
+ import type { CachedTerminalUiState } from "./terminalTypes";
21
10
 
22
- export const terminalConnectionCache = new Map<string, CachedTerminalConnection>();
23
- export const terminalSnapshotCache = new Map<string, CachedTerminalSnapshot>();
24
11
  export const terminalUiStateCache = new Map<string, CachedTerminalUiState>();
25
12
 
26
13
  export function trimTerminalCache(cache: Map<string, unknown>, maxEntries: number): void {
@@ -33,64 +20,6 @@ export function trimTerminalCache(cache: Map<string, unknown>, maxEntries: numbe
33
20
  }
34
21
  }
35
22
 
36
- export function readCachedTerminalConnection(sessionId: string): TerminalConnectionInfo | null {
37
- const cached = terminalConnectionCache.get(sessionId);
38
- if (!cached) {
39
- return null;
40
- }
41
- if (cached.expiresAt <= Date.now()) {
42
- terminalConnectionCache.delete(sessionId);
43
- return null;
44
- }
45
- return cached.value;
46
- }
47
-
48
- export function storeCachedTerminalConnection(sessionId: string, value: TerminalConnectionInfo): void {
49
- terminalConnectionCache.delete(sessionId);
50
- terminalConnectionCache.set(sessionId, {
51
- value,
52
- expiresAt: Date.now() + TERMINAL_CONNECTION_CACHE_MAX_TTL_MS,
53
- });
54
- trimTerminalCache(terminalConnectionCache, TERMINAL_CONNECTION_CACHE_MAX_ENTRIES);
55
- }
56
-
57
- export function clearCachedTerminalConnection(sessionId: string): void {
58
- terminalConnectionCache.delete(sessionId);
59
- }
60
-
61
- export function readCachedTerminalSnapshot(sessionId: string): TerminalSnapshot | null {
62
- const cached = terminalSnapshotCache.get(sessionId);
63
- if (!cached) {
64
- return null;
65
- }
66
- if (Date.now() - cached.updatedAt > TERMINAL_SNAPSHOT_CACHE_MAX_AGE_MS) {
67
- terminalSnapshotCache.delete(sessionId);
68
- return null;
69
- }
70
- return {
71
- snapshot: cached.snapshot,
72
- transcript: cached.transcript,
73
- source: cached.source,
74
- live: cached.live,
75
- restored: cached.restored,
76
- sequence: cached.sequence,
77
- modes: cached.modes,
78
- };
79
- }
80
-
81
- export function storeCachedTerminalSnapshot(sessionId: string, snapshot: TerminalSnapshot): void {
82
- terminalSnapshotCache.delete(sessionId);
83
- terminalSnapshotCache.set(sessionId, {
84
- ...snapshot,
85
- updatedAt: Date.now(),
86
- });
87
- trimTerminalCache(terminalSnapshotCache, TERMINAL_SNAPSHOT_CACHE_MAX_ENTRIES);
88
- }
89
-
90
- export function clearCachedTerminalSnapshot(sessionId: string): void {
91
- terminalSnapshotCache.delete(sessionId);
92
- }
93
-
94
23
  export function readCachedTerminalUiState(sessionId: string): CachedTerminalUiState | null {
95
24
  const cached = terminalUiStateCache.get(sessionId);
96
25
  if (!cached) {