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
@@ -1 +0,0 @@
1
- (globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,61766,e=>{"use strict";var c=e.i(95187);let r=(0,c.createServerReference)("00c489341bec725518180a01caf1fa64413a4c4d6c",c.callServer,void 0,c.findSourceMapURL,"createOrReadKeylessAction");e.s(["createOrReadKeylessAction",()=>r])},23151,e=>{"use strict";e.s([],29135),e.i(29135);var c=e.i(95187);let r=(0,c.createServerReference)("40445bd28d7a736859660d7057c8c48b8ec362ec73",c.callServer,void 0,c.findSourceMapURL,"syncKeylessConfigAction");var t=e.i(61766);let s=(0,c.createServerReference)("00cc4dda20652bb1f29681e8287b1ca5afe99700b6",c.callServer,void 0,c.findSourceMapURL,"deleteKeylessAction");var a=e.i(45070);e.s(["createOrReadKeylessAction",()=>t.createOrReadKeylessAction,"deleteKeylessAction",()=>s,"detectKeylessEnvDriftAction",()=>a.detectKeylessEnvDriftAction,"syncKeylessConfigAction",()=>r],23151)}]);
@@ -1,80 +0,0 @@
1
- import { getDashboardAccess, guardApiAccess } from "@/lib/auth";
2
- import { forwardedAccessAuthenticated } from "@/lib/guardedRustProxy";
3
- import { hasRustBackend } from "@/lib/rustBackendProxy";
4
- import { NextResponse } from "next/server";
5
-
6
- export const dynamic = "force-dynamic";
7
- export const runtime = "nodejs";
8
-
9
- /**
10
- * SSE streaming endpoint.
11
- *
12
- * In Next.js standalone mode, the default route-handler pipeline may buffer
13
- * the entire Response body (via blob()) before sending, which kills SSE.
14
- *
15
- * We handle SSE manually here: open a fetch to the Rust backend, then pipe
16
- * the raw readable stream back with the correct headers so Next.js treats it
17
- * as an unbuffered passthrough.
18
- */
19
- export async function GET(
20
- request: Request,
21
- context: { params: Promise<{ id: string }> },
22
- ): Promise<Response> {
23
- const denied = await guardApiAccess(request, "viewer");
24
- if (denied) return denied;
25
-
26
- if (!hasRustBackend()) {
27
- return NextResponse.json(
28
- { error: "Rust backend URL is not configured" },
29
- { status: 503 },
30
- );
31
- }
32
-
33
- const { id } = await context.params;
34
- const backendUrl = process.env.CONDUCTOR_BACKEND_URL?.trim() ?? "";
35
- const target = new URL(
36
- `/api/sessions/${encodeURIComponent(id)}/feed/stream`,
37
- backendUrl,
38
- );
39
-
40
- // Forward query params (e.g. ?since=)
41
- const incomingUrl = new URL(request.url);
42
- target.search = incomingUrl.search;
43
-
44
- const access = await getDashboardAccess(request);
45
- const headers = new Headers({
46
- "Accept": "text/event-stream",
47
- "Cache-Control": "no-cache",
48
- "x-conductor-proxy-authorized": "true",
49
- "x-conductor-access-authenticated": forwardedAccessAuthenticated(access) ? "true" : "false",
50
- });
51
- if (access.role) headers.set("x-conductor-access-role", access.role);
52
- if (access.email) headers.set("x-conductor-access-email", access.email);
53
- if (access.provider) headers.set("x-conductor-access-provider", access.provider);
54
-
55
- const upstream = await fetch(target, {
56
- method: "GET",
57
- headers,
58
- cache: "no-store",
59
- signal: request.signal,
60
- });
61
-
62
- if (!upstream.ok || !upstream.body) {
63
- return new Response(upstream.body, {
64
- status: upstream.status,
65
- statusText: upstream.statusText,
66
- });
67
- }
68
-
69
- // Return the raw stream with SSE headers.
70
- // Do NOT use NextResponse — it may buffer.
71
- return new Response(upstream.body, {
72
- status: 200,
73
- headers: {
74
- "Content-Type": "text/event-stream",
75
- "Cache-Control": "no-cache, no-transform",
76
- "Connection": "keep-alive",
77
- "X-Accel-Buffering": "no",
78
- },
79
- });
80
- }
@@ -1,80 +0,0 @@
1
- import { getDashboardAccess, guardApiAccess } from "@/lib/auth";
2
- import { forwardedAccessAuthenticated } from "@/lib/guardedRustProxy";
3
- import { hasRustBackend } from "@/lib/rustBackendProxy";
4
- import { NextResponse } from "next/server";
5
-
6
- export const dynamic = "force-dynamic";
7
- export const runtime = "nodejs";
8
-
9
- /**
10
- * SSE streaming endpoint for session output.
11
- *
12
- * In Next.js standalone mode, the default route-handler pipeline may buffer
13
- * the entire Response body (via blob()) before sending, which kills SSE.
14
- *
15
- * We handle SSE manually here: open a fetch to the Rust backend, then pipe
16
- * the raw readable stream back with the correct headers so Next.js treats it
17
- * as an unbuffered passthrough.
18
- */
19
- export async function GET(
20
- request: Request,
21
- context: { params: Promise<{ id: string }> },
22
- ): Promise<Response> {
23
- const denied = await guardApiAccess(request, "viewer");
24
- if (denied) return denied;
25
-
26
- if (!hasRustBackend()) {
27
- return NextResponse.json(
28
- { error: "Rust backend URL is not configured" },
29
- { status: 503 },
30
- );
31
- }
32
-
33
- const { id } = await context.params;
34
- const backendUrl = process.env.CONDUCTOR_BACKEND_URL?.trim() ?? "";
35
- const target = new URL(
36
- `/api/sessions/${encodeURIComponent(id)}/output/stream`,
37
- backendUrl,
38
- );
39
-
40
- // Forward query params
41
- const incomingUrl = new URL(request.url);
42
- target.search = incomingUrl.search;
43
-
44
- const access = await getDashboardAccess(request);
45
- const headers = new Headers({
46
- "Accept": "text/event-stream",
47
- "Cache-Control": "no-cache",
48
- "x-conductor-proxy-authorized": "true",
49
- "x-conductor-access-authenticated": forwardedAccessAuthenticated(access) ? "true" : "false",
50
- });
51
- if (access.role) headers.set("x-conductor-access-role", access.role);
52
- if (access.email) headers.set("x-conductor-access-email", access.email);
53
- if (access.provider) headers.set("x-conductor-access-provider", access.provider);
54
-
55
- const upstream = await fetch(target, {
56
- method: "GET",
57
- headers,
58
- cache: "no-store",
59
- signal: request.signal,
60
- });
61
-
62
- if (!upstream.ok || !upstream.body) {
63
- return new Response(upstream.body, {
64
- status: upstream.status,
65
- statusText: upstream.statusText,
66
- });
67
- }
68
-
69
- // Return the raw stream with SSE headers.
70
- // Do NOT use NextResponse — it may buffer.
71
- return new Response(upstream.body, {
72
- status: 200,
73
- headers: {
74
- "Content-Type": "text/event-stream",
75
- "Cache-Control": "no-cache, no-transform",
76
- "Connection": "keep-alive",
77
- "X-Accel-Buffering": "no",
78
- },
79
- });
80
- }
@@ -1,343 +0,0 @@
1
- import assert from "node:assert/strict";
2
- import test from "node:test";
3
- import { tmpdir } from "node:os";
4
- import { join } from "node:path";
5
- import { NextRequest } from "next/server";
6
- import {
7
- clearRemoteAccessRuntimeState,
8
- writeRemoteAccessRuntimeState,
9
- } from "@/lib/remoteAccessRuntime";
10
- import { GET } from "./route";
11
-
12
- const originalBackendUrl = process.env.CONDUCTOR_BACKEND_URL;
13
- const originalConfigPath = process.env.CO_CONFIG_PATH;
14
- const originalWorkspace = process.env.CONDUCTOR_WORKSPACE;
15
- const originalRequireAuth = process.env.CONDUCTOR_REQUIRE_AUTH;
16
- const originalDefaultRole = process.env.CONDUCTOR_ACCESS_DEFAULT_ROLE;
17
- const originalRemoteAccessRuntimePath =
18
- process.env.CONDUCTOR_REMOTE_ACCESS_RUNTIME_PATH;
19
- const originalRemoteAccessToken = process.env.CONDUCTOR_REMOTE_ACCESS_TOKEN;
20
- const originalRemoteSessionSecret = process.env.CONDUCTOR_REMOTE_SESSION_SECRET;
21
- const originalFetch = global.fetch;
22
-
23
- type TerminalConnectionPayload = {
24
- ptyWsUrl: string | null;
25
- interactive: boolean;
26
- };
27
-
28
- function resetEnv(): void {
29
- delete process.env.CONDUCTOR_BACKEND_URL;
30
- process.env.CO_CONFIG_PATH =
31
- "/tmp/conductor-terminal-connection-route-test-config-does-not-exist.yaml";
32
- process.env.CONDUCTOR_WORKSPACE = "terminal-connection-route-test-workspace";
33
- process.env.CONDUCTOR_REQUIRE_AUTH = "";
34
- delete process.env.CONDUCTOR_ACCESS_DEFAULT_ROLE;
35
- delete process.env.CONDUCTOR_REMOTE_ACCESS_TOKEN;
36
- delete process.env.CONDUCTOR_REMOTE_SESSION_SECRET;
37
- process.env.CONDUCTOR_REMOTE_ACCESS_RUNTIME_PATH = join(
38
- tmpdir(),
39
- "conductor-terminal-connection-route-runtime.json"
40
- );
41
- clearRemoteAccessRuntimeState();
42
- }
43
-
44
- type MockSessionConnectionFetchOptions = {
45
- id: string;
46
- token?: {
47
- token?: string | null;
48
- required?: boolean;
49
- expiresInSeconds?: number | null;
50
- error?: string;
51
- };
52
- };
53
-
54
- function setMockSessionConnectionFetch({
55
- id,
56
- token,
57
- }: MockSessionConnectionFetchOptions): void {
58
- const terminalTokenUrl = `/api/sessions/${encodeURIComponent(
59
- id
60
- )}/terminal/token`;
61
- global.fetch = (async (input: string | Request | URL) => {
62
- const url =
63
- typeof input === "string" || input instanceof URL
64
- ? new URL(input)
65
- : new URL(input.url);
66
- if (url.pathname === terminalTokenUrl) {
67
- if (token === undefined) {
68
- throw new Error("terminal token lookup should not run for this test");
69
- }
70
- return new Response(JSON.stringify(token), {
71
- status: 200,
72
- headers: { "Content-Type": "application/json" },
73
- });
74
- }
75
-
76
- throw new Error(
77
- `Unexpected fetch in terminal connection route test: ${url.pathname}`
78
- );
79
- }) as typeof fetch;
80
- }
81
-
82
- function assertJsonResponse(
83
- response: Response
84
- ): Promise<TerminalConnectionPayload> {
85
- return response.json() as Promise<TerminalConnectionPayload>;
86
- }
87
-
88
- test.afterEach(() => {
89
- resetEnv();
90
- });
91
-
92
- test.after(() => {
93
- if (originalBackendUrl === undefined) {
94
- delete process.env.CONDUCTOR_BACKEND_URL;
95
- } else {
96
- process.env.CONDUCTOR_BACKEND_URL = originalBackendUrl;
97
- }
98
-
99
- if (originalConfigPath === undefined) {
100
- delete process.env.CO_CONFIG_PATH;
101
- } else {
102
- process.env.CO_CONFIG_PATH = originalConfigPath;
103
- }
104
-
105
- if (originalWorkspace === undefined) {
106
- delete process.env.CONDUCTOR_WORKSPACE;
107
- } else {
108
- process.env.CONDUCTOR_WORKSPACE = originalWorkspace;
109
- }
110
-
111
- if (originalRequireAuth === undefined) {
112
- delete process.env.CONDUCTOR_REQUIRE_AUTH;
113
- } else {
114
- process.env.CONDUCTOR_REQUIRE_AUTH = originalRequireAuth;
115
- }
116
-
117
- if (originalDefaultRole === undefined) {
118
- delete process.env.CONDUCTOR_ACCESS_DEFAULT_ROLE;
119
- } else {
120
- process.env.CONDUCTOR_ACCESS_DEFAULT_ROLE = originalDefaultRole;
121
- }
122
-
123
- if (originalRemoteAccessRuntimePath === undefined) {
124
- delete process.env.CONDUCTOR_REMOTE_ACCESS_RUNTIME_PATH;
125
- } else {
126
- process.env.CONDUCTOR_REMOTE_ACCESS_RUNTIME_PATH =
127
- originalRemoteAccessRuntimePath;
128
- }
129
-
130
- if (originalRemoteAccessToken === undefined) {
131
- delete process.env.CONDUCTOR_REMOTE_ACCESS_TOKEN;
132
- } else {
133
- process.env.CONDUCTOR_REMOTE_ACCESS_TOKEN = originalRemoteAccessToken;
134
- }
135
-
136
- if (originalRemoteSessionSecret === undefined) {
137
- delete process.env.CONDUCTOR_REMOTE_SESSION_SECRET;
138
- } else {
139
- process.env.CONDUCTOR_REMOTE_SESSION_SECRET = originalRemoteSessionSecret;
140
- }
141
-
142
- global.fetch = originalFetch;
143
- clearRemoteAccessRuntimeState();
144
- });
145
-
146
- test("GET returns ptyWsUrl and interactive for loopback dashboard requests", async () => {
147
- resetEnv();
148
- process.env.CONDUCTOR_BACKEND_URL = "http://127.0.0.1:4749";
149
-
150
- global.fetch = (async (input: string | Request | URL) => {
151
- const url =
152
- typeof input === "string" || input instanceof URL
153
- ? new URL(input)
154
- : new URL(input.url);
155
- if (url.pathname.includes("/terminal/token")) {
156
- return new Response(JSON.stringify({ required: false }), {
157
- status: 200,
158
- headers: { "Content-Type": "application/json" },
159
- });
160
- }
161
- throw new Error(`Unexpected fetch: ${url.pathname}`);
162
- }) as typeof fetch;
163
-
164
- try {
165
- const response = await GET(
166
- new NextRequest(
167
- "http://127.0.0.1:3000/api/sessions/session-1/terminal/connection"
168
- ),
169
- { params: Promise.resolve({ id: "session-1" }) }
170
- );
171
-
172
- assert.equal(response.status, 200);
173
- const payload = await assertJsonResponse(response);
174
-
175
- assert.equal(
176
- payload.ptyWsUrl,
177
- "ws://127.0.0.1:4749/api/sessions/session-1/terminal/ws?protocol=ttyd"
178
- );
179
- assert.equal(payload.interactive, true);
180
- } finally {
181
- global.fetch = originalFetch;
182
- }
183
- });
184
-
185
- test("GET returns null ptyWsUrl for viewers without operator access", async () => {
186
- resetEnv();
187
- process.env.CONDUCTOR_BACKEND_URL = "http://127.0.0.1:4749";
188
- process.env.CONDUCTOR_ACCESS_DEFAULT_ROLE = "viewer";
189
-
190
- writeRemoteAccessRuntimeState({
191
- status: "ready",
192
- provider: "tailscale",
193
- publicUrl: "https://laptop.tailnet.ts.net",
194
- localUrl: "http://127.0.0.1:3000",
195
- accessToken: null,
196
- sessionSecret: null,
197
- tunnelPid: null,
198
- logPath: null,
199
- lastError: null,
200
- startedAt: new Date().toISOString(),
201
- });
202
-
203
- try {
204
- const request = new NextRequest(
205
- "https://laptop.tailnet.ts.net/api/sessions/session-1/terminal/connection",
206
- {
207
- headers: {
208
- "Tailscale-User-Login": "viewer@example.com",
209
- },
210
- }
211
- );
212
- const response = await GET(request, {
213
- params: Promise.resolve({ id: "session-1" }),
214
- });
215
-
216
- assert.equal(response.status, 200);
217
- const payload = await assertJsonResponse(response);
218
-
219
- assert.equal(payload.ptyWsUrl, null);
220
- assert.equal(payload.interactive, false);
221
- } finally {
222
- global.fetch = originalFetch;
223
- }
224
- });
225
-
226
- test("GET appends a control token to the direct PTY websocket when auth is required", async () => {
227
- resetEnv();
228
- process.env.CONDUCTOR_BACKEND_URL = "http://127.0.0.1:4749";
229
- process.env.CONDUCTOR_REQUIRE_AUTH = "true";
230
-
231
- setMockSessionConnectionFetch({
232
- id: "session-1",
233
- token: {
234
- required: true,
235
- token: "signed-terminal-token",
236
- expiresInSeconds: 60,
237
- },
238
- });
239
-
240
- try {
241
- const response = await GET(
242
- new NextRequest(
243
- "http://127.0.0.1:3000/api/sessions/session-1/terminal/connection"
244
- ),
245
- { params: Promise.resolve({ id: "session-1" }) }
246
- );
247
-
248
- assert.equal(response.status, 200);
249
- const payload = await assertJsonResponse(response);
250
- assert.equal(
251
- payload.ptyWsUrl,
252
- "ws://127.0.0.1:4749/api/sessions/session-1/terminal/ws?protocol=ttyd&token=signed-terminal-token"
253
- );
254
- assert.equal(payload.interactive, true);
255
- } finally {
256
- global.fetch = originalFetch;
257
- }
258
- });
259
-
260
- test("GET falls back to ptyWsUrl without token when token fetch fails", async () => {
261
- resetEnv();
262
- process.env.CONDUCTOR_BACKEND_URL = "http://127.0.0.1:4749";
263
-
264
- global.fetch = (async () => {
265
- throw new Error("token endpoint unreachable");
266
- }) as typeof fetch;
267
-
268
- try {
269
- const response = await GET(
270
- new NextRequest(
271
- "http://127.0.0.1:3000/api/sessions/session-1/terminal/connection"
272
- ),
273
- { params: Promise.resolve({ id: "session-1" }) }
274
- );
275
-
276
- assert.equal(response.status, 200);
277
- const payload = await assertJsonResponse(response);
278
-
279
- assert.equal(
280
- payload.ptyWsUrl,
281
- "ws://127.0.0.1:4749/api/sessions/session-1/terminal/ws?protocol=ttyd"
282
- );
283
- assert.equal(payload.interactive, true);
284
- } finally {
285
- global.fetch = originalFetch;
286
- }
287
- });
288
-
289
- test("GET uses wss: protocol when backend URL is https", async () => {
290
- resetEnv();
291
- process.env.CONDUCTOR_BACKEND_URL = "https://backend.example.com:4749";
292
-
293
- writeRemoteAccessRuntimeState({
294
- status: "ready",
295
- provider: "tailscale",
296
- publicUrl: "https://dashboard.example.com",
297
- localUrl: "http://127.0.0.1:3000",
298
- accessToken: null,
299
- sessionSecret: null,
300
- tunnelPid: null,
301
- logPath: null,
302
- lastError: null,
303
- startedAt: new Date().toISOString(),
304
- });
305
-
306
- global.fetch = (async (input: string | Request | URL) => {
307
- const url =
308
- typeof input === "string" || input instanceof URL
309
- ? new URL(input)
310
- : new URL(input.url);
311
- if (url.pathname.includes("/terminal/token")) {
312
- return new Response(JSON.stringify({ required: false }), {
313
- status: 200,
314
- headers: { "Content-Type": "application/json" },
315
- });
316
- }
317
- throw new Error(`Unexpected fetch: ${url.pathname}`);
318
- }) as typeof fetch;
319
-
320
- try {
321
- const response = await GET(
322
- new NextRequest(
323
- "https://dashboard.example.com/api/sessions/session-1/terminal/connection",
324
- {
325
- headers: {
326
- "Tailscale-User-Login": "dev@example.com",
327
- },
328
- }
329
- ),
330
- { params: Promise.resolve({ id: "session-1" }) }
331
- );
332
-
333
- assert.equal(response.status, 200);
334
- const payload = await assertJsonResponse(response);
335
- assert.equal(
336
- payload.ptyWsUrl,
337
- "wss://backend.example.com:4749/api/sessions/session-1/terminal/ws?protocol=ttyd"
338
- );
339
- assert.equal(payload.interactive, true);
340
- } finally {
341
- global.fetch = originalFetch;
342
- }
343
- });
@@ -1,120 +0,0 @@
1
- import { roleMeetsRequirement } from "@/lib/accessControl";
2
- import {
3
- getDashboardAccess,
4
- guardApiAccess,
5
- } from "@/lib/auth";
6
- import { buildForwardedAccessHeaders } from "@/lib/guardedRustProxy";
7
- import { NextResponse } from "next/server";
8
-
9
- export const dynamic = "force-dynamic";
10
- export const runtime = "nodejs";
11
-
12
- type TerminalTokenPayload = {
13
- token?: string | null;
14
- required?: boolean;
15
- expiresInSeconds?: number | null;
16
- error?: string;
17
- };
18
-
19
- function resolveBackendTerminalWsUrl(backendUrl: string, id: string): URL {
20
- const wsUrl = new URL(backendUrl);
21
- wsUrl.protocol = wsUrl.protocol === "https:" ? "wss:" : "ws:";
22
- wsUrl.pathname = `/api/sessions/${encodeURIComponent(id)}/terminal/ws`;
23
- wsUrl.search = "";
24
- return wsUrl;
25
- }
26
-
27
- /**
28
- * Resolve the direct ttyd WebSocket URL for a session.
29
- * Always asks the backend whether a terminal token is required — the backend
30
- * is the single source of truth for access-control decisions.
31
- */
32
- async function resolvePtyWsUrl(
33
- request: Request,
34
- backendUrl: string,
35
- id: string
36
- ): Promise<string> {
37
- const wsUrl = resolveBackendTerminalWsUrl(backendUrl, id);
38
- wsUrl.searchParams.set("protocol", "ttyd");
39
-
40
- const tokenUrl = new URL(
41
- `/api/sessions/${encodeURIComponent(id)}/terminal/token`,
42
- backendUrl
43
- );
44
-
45
- let payload: TerminalTokenPayload | null = null;
46
- try {
47
- const response = await fetch(tokenUrl, {
48
- method: "GET",
49
- headers: await buildForwardedAccessHeaders(request),
50
- cache: "no-store",
51
- signal: request.signal,
52
- });
53
- payload = (await response.json().catch(() => null)) as
54
- | TerminalTokenPayload
55
- | null;
56
- if (!response.ok) {
57
- throw new Error(
58
- payload?.error ?? `Failed to resolve terminal token: ${response.status}`
59
- );
60
- }
61
- } catch (err) {
62
- console.warn("[Terminal Connection] Token fetch failed, proceeding without token:", {
63
- error: err instanceof Error ? err.message : String(err),
64
- });
65
- return wsUrl.toString();
66
- }
67
-
68
- if (payload?.required !== true) {
69
- return wsUrl.toString();
70
- }
71
-
72
- const token =
73
- typeof payload.token === "string" ? payload.token.trim() : "";
74
- if (!token) {
75
- throw new Error("Terminal token response did not include a control token");
76
- }
77
-
78
- wsUrl.searchParams.set("token", token);
79
- return wsUrl.toString();
80
- }
81
-
82
- export async function GET(
83
- request: Request,
84
- context: { params: Promise<{ id: string }> }
85
- ): Promise<Response> {
86
- const denied = await guardApiAccess(request, "viewer");
87
- if (denied) return denied;
88
-
89
- const { id } = await context.params;
90
- const access = await getDashboardAccess(request);
91
- const interactive = access.role
92
- ? roleMeetsRequirement(access.role, "operator")
93
- : false;
94
-
95
- if (!interactive) {
96
- return NextResponse.json({
97
- ptyWsUrl: null,
98
- interactive: false,
99
- });
100
- }
101
-
102
- const configuredBackendUrl = process.env.CONDUCTOR_BACKEND_URL?.trim();
103
- const backendUrl = configuredBackendUrl || "http://127.0.0.1:4749";
104
-
105
- let ptyWsUrl: string | null = null;
106
- try {
107
- ptyWsUrl = await resolvePtyWsUrl(request, backendUrl, id);
108
- } catch (error) {
109
- console.warn("[Terminal Connection] Failed to resolve direct terminal websocket URL", {
110
- error: error instanceof Error ? error.message : String(error),
111
- backendUrl,
112
- sessionId: id,
113
- });
114
- }
115
-
116
- return NextResponse.json({
117
- ptyWsUrl,
118
- interactive: true,
119
- });
120
- }