neural-loom 0.2.16 → 0.2.18

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 (67) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/build-manifest.json +3 -3
  3. package/.next/cache/.previewinfo +1 -1
  4. package/.next/cache/.rscinfo +1 -1
  5. package/.next/cache/.tsbuildinfo +1 -1
  6. package/.next/diagnostics/route-bundle-stats.json +2 -2
  7. package/.next/fallback-build-manifest.json +3 -3
  8. package/.next/prerender-manifest.json +3 -3
  9. package/.next/server/app/_global-error.html +1 -1
  10. package/.next/server/app/_global-error.rsc +1 -1
  11. package/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  12. package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  13. package/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  14. package/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  15. package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  16. package/.next/server/app/_not-found.html +1 -1
  17. package/.next/server/app/_not-found.rsc +1 -1
  18. package/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  19. package/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  20. package/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  21. package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  22. package/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  23. package/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  24. package/.next/server/app/index.html +1 -1
  25. package/.next/server/app/index.rsc +2 -2
  26. package/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  27. package/.next/server/app/index.segments/_full.segment.rsc +2 -2
  28. package/.next/server/app/index.segments/_head.segment.rsc +1 -1
  29. package/.next/server/app/index.segments/_index.segment.rsc +1 -1
  30. package/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  31. package/.next/server/app/page/react-loadable-manifest.json +3 -3
  32. package/.next/server/app/page_client-reference-manifest.js +1 -1
  33. package/.next/server/chunks/[root-of-the-server]__0d0pykb._.js +1 -1
  34. package/.next/server/chunks/[root-of-the-server]__0d0pykb._.js.map +1 -1
  35. package/.next/server/chunks/[root-of-the-server]__0iqaxhx._.js +5 -5
  36. package/.next/server/chunks/[root-of-the-server]__0iqaxhx._.js.map +1 -1
  37. package/.next/server/chunks/[root-of-the-server]__0qyrt6_._.js +1 -1
  38. package/.next/server/chunks/[root-of-the-server]__0qyrt6_._.js.map +1 -1
  39. package/.next/server/chunks/[root-of-the-server]__0qz~w0~._.js +6 -6
  40. package/.next/server/chunks/[root-of-the-server]__0qz~w0~._.js.map +1 -1
  41. package/.next/server/chunks/[root-of-the-server]__0ty8hwz._.js +1 -1
  42. package/.next/server/chunks/[root-of-the-server]__0ty8hwz._.js.map +1 -1
  43. package/.next/server/chunks/ssr/_0t7oqy6._.js +1 -1
  44. package/.next/server/chunks/ssr/_0t7oqy6._.js.map +1 -1
  45. package/.next/server/middleware-build-manifest.js +3 -3
  46. package/.next/server/pages/404.html +1 -1
  47. package/.next/server/pages/500.html +1 -1
  48. package/.next/server/server-reference-manifest.js +1 -1
  49. package/.next/server/server-reference-manifest.json +1 -1
  50. package/.next/static/chunks/0ek0280331wie.js +1 -0
  51. package/.next/static/chunks/0whfmx9l_25d4.js +1 -0
  52. package/.next/static/chunks/{06z14vx5drwj0.js → 0x__nd4avi0fg.js} +2 -2
  53. package/.next/trace +1 -1
  54. package/.next/trace-build +1 -1
  55. package/package.json +1 -1
  56. package/src/app/api/sessions/launch/route.ts +4 -1
  57. package/src/app/api/sessions/route.ts +7 -1
  58. package/src/app/components/IdeLayout.tsx +4 -3
  59. package/src/app/components/TerminalConsole.tsx +5 -4
  60. package/src/app/page.tsx +101 -14
  61. package/src/lib/agents/WebSocketServer.ts +115 -90
  62. package/src/lib/recents.ts +19 -2
  63. package/.next/static/chunks/0cv9~vnm0atn4.js +0 -1
  64. package/.next/static/chunks/0t~533m2dl-bb.js +0 -1
  65. /package/.next/static/{Sqg0WwYlho2e4abhJPPMw → OOK46QJncZAPJTv2oCT-X}/_buildManifest.js +0 -0
  66. /package/.next/static/{Sqg0WwYlho2e4abhJPPMw → OOK46QJncZAPJTv2oCT-X}/_clientMiddlewareManifest.js +0 -0
  67. /package/.next/static/{Sqg0WwYlho2e4abhJPPMw → OOK46QJncZAPJTv2oCT-X}/_ssgManifest.js +0 -0
package/.next/trace CHANGED
@@ -1 +1 @@
1
- [{"name":"generate-buildid","duration":282,"timestamp":118007911269,"id":4,"parentId":1,"tags":{},"startTime":1781338743682,"traceId":"9cb8826ecd92b75b"},{"name":"load-custom-routes","duration":343,"timestamp":118007911664,"id":5,"parentId":1,"tags":{},"startTime":1781338743682,"traceId":"9cb8826ecd92b75b"},{"name":"create-dist-dir","duration":1906,"timestamp":118007912037,"id":6,"parentId":1,"tags":{},"startTime":1781338743683,"traceId":"9cb8826ecd92b75b"},{"name":"clean","duration":508,"timestamp":118007914735,"id":7,"parentId":1,"tags":{},"startTime":1781338743685,"traceId":"9cb8826ecd92b75b"},{"name":"discover-routes","duration":7369,"timestamp":118007936353,"id":8,"parentId":1,"tags":{},"startTime":1781338743707,"traceId":"9cb8826ecd92b75b"},{"name":"create-root-mapping","duration":59,"timestamp":118007943770,"id":9,"parentId":1,"tags":{},"startTime":1781338743714,"traceId":"9cb8826ecd92b75b"},{"name":"generate-route-types","duration":17441,"timestamp":118007945233,"id":10,"parentId":1,"tags":{},"startTime":1781338743716,"traceId":"9cb8826ecd92b75b"},{"name":"public-dir-conflict-check","duration":84,"timestamp":118007962798,"id":11,"parentId":1,"tags":{},"startTime":1781338743733,"traceId":"9cb8826ecd92b75b"},{"name":"generate-routes-manifest","duration":2186,"timestamp":118007962977,"id":12,"parentId":1,"tags":{},"startTime":1781338743734,"traceId":"9cb8826ecd92b75b"},{"name":"run-turbopack","duration":2289163,"timestamp":118007971439,"id":14,"parentId":1,"tags":{},"startTime":1781338743742,"traceId":"9cb8826ecd92b75b"},{"name":"turbopack-build-events","duration":64,"timestamp":118008264417,"id":15,"parentId":1,"tags":{},"startTime":1781338744035,"traceId":"9cb8826ecd92b75b"},{"name":"run-typescript","duration":3434329,"timestamp":118010272734,"id":16,"parentId":1,"tags":{},"startTime":1781338746043,"traceId":"9cb8826ecd92b75b"},{"name":"generate-required-server-files","duration":1351,"timestamp":118013707902,"id":18,"parentId":1,"tags":{},"startTime":1781338749478,"traceId":"9cb8826ecd92b75b"},{"name":"check-static-error-page","duration":7348,"timestamp":118013829609,"id":20,"parentId":19,"tags":{},"startTime":1781338749600,"traceId":"9cb8826ecd92b75b"},{"name":"is-page-static","duration":515970,"timestamp":118013841895,"id":41,"parentId":22,"tags":{},"startTime":1781338749612,"traceId":"9cb8826ecd92b75b"},{"name":"check-page","duration":525707,"timestamp":118013832279,"id":22,"parentId":19,"tags":{"page":"/_global-error"},"startTime":1781338749603,"traceId":"9cb8826ecd92b75b"},{"name":"is-page-static","duration":639263,"timestamp":118013842115,"id":43,"parentId":24,"tags":{},"startTime":1781338749613,"traceId":"9cb8826ecd92b75b"},{"name":"check-page","duration":648585,"timestamp":118013832862,"id":24,"parentId":19,"tags":{"page":"/api/context/claude"},"startTime":1781338749603,"traceId":"9cb8826ecd92b75b"},{"name":"is-page-static","duration":648704,"timestamp":118013842021,"id":42,"parentId":23,"tags":{},"startTime":1781338749613,"traceId":"9cb8826ecd92b75b"},{"name":"check-page","duration":658186,"timestamp":118013832592,"id":23,"parentId":19,"tags":{"page":"/api/context/aider"},"startTime":1781338749603,"traceId":"9cb8826ecd92b75b"},{"name":"is-page-static","duration":649657,"timestamp":118013842303,"id":45,"parentId":26,"tags":{},"startTime":1781338749613,"traceId":"9cb8826ecd92b75b"},{"name":"check-page","duration":658525,"timestamp":118013833481,"id":26,"parentId":19,"tags":{"page":"/api/context/ssh"},"startTime":1781338749604,"traceId":"9cb8826ecd92b75b"},{"name":"is-page-static","duration":654791,"timestamp":118013842209,"id":44,"parentId":25,"tags":{},"startTime":1781338749613,"traceId":"9cb8826ecd92b75b"},{"name":"check-page","duration":663932,"timestamp":118013833114,"id":25,"parentId":19,"tags":{"page":"/api/context"},"startTime":1781338749604,"traceId":"9cb8826ecd92b75b"},{"name":"is-page-static","duration":655914,"timestamp":118013842808,"id":47,"parentId":28,"tags":{},"startTime":1781338749613,"traceId":"9cb8826ecd92b75b"},{"name":"check-page","duration":664750,"timestamp":118013834019,"id":28,"parentId":19,"tags":{"page":"/api/git"},"startTime":1781338749605,"traceId":"9cb8826ecd92b75b"},{"name":"is-page-static","duration":658504,"timestamp":118013842927,"id":48,"parentId":29,"tags":{},"startTime":1781338749613,"traceId":"9cb8826ecd92b75b"},{"name":"check-page","duration":667255,"timestamp":118013834231,"id":29,"parentId":19,"tags":{"page":"/api/recents"},"startTime":1781338749605,"traceId":"9cb8826ecd92b75b"},{"name":"is-page-static","duration":662542,"timestamp":118013843513,"id":52,"parentId":33,"tags":{},"startTime":1781338749614,"traceId":"9cb8826ecd92b75b"},{"name":"check-page","duration":671071,"timestamp":118013835036,"id":33,"parentId":19,"tags":{"page":"/api/sessions"},"startTime":1781338749606,"traceId":"9cb8826ecd92b75b"},{"name":"is-page-static","duration":663896,"timestamp":118013847267,"id":58,"parentId":39,"tags":{},"startTime":1781338749618,"traceId":"9cb8826ecd92b75b"},{"name":"check-page","duration":674545,"timestamp":118013836663,"id":39,"parentId":19,"tags":{"page":"/"},"startTime":1781338749607,"traceId":"9cb8826ecd92b75b"},{"name":"is-page-static","duration":671435,"timestamp":118013843760,"id":56,"parentId":37,"tags":{},"startTime":1781338749614,"traceId":"9cb8826ecd92b75b"},{"name":"check-page","duration":679130,"timestamp":118013836117,"id":37,"parentId":19,"tags":{"page":"/api/sessions/stop"},"startTime":1781338749607,"traceId":"9cb8826ecd92b75b"},{"name":"is-page-static","duration":674171,"timestamp":118013841449,"id":40,"parentId":21,"tags":{},"startTime":1781338749612,"traceId":"9cb8826ecd92b75b"},{"name":"check-page","duration":684474,"timestamp":118013831188,"id":21,"parentId":19,"tags":{"page":"/_not-found"},"startTime":1781338749602,"traceId":"9cb8826ecd92b75b"},{"name":"is-page-static","duration":673413,"timestamp":118013844141,"id":57,"parentId":38,"tags":{},"startTime":1781338749615,"traceId":"9cb8826ecd92b75b"},{"name":"check-page","duration":681313,"timestamp":118013836300,"id":38,"parentId":19,"tags":{"page":"/favicon.ico"},"startTime":1781338749607,"traceId":"9cb8826ecd92b75b"},{"name":"is-page-static","duration":678441,"timestamp":118013843713,"id":55,"parentId":36,"tags":{},"startTime":1781338749614,"traceId":"9cb8826ecd92b75b"},{"name":"check-page","duration":686315,"timestamp":118013835895,"id":36,"parentId":19,"tags":{"page":"/api/sessions/stats"},"startTime":1781338749606,"traceId":"9cb8826ecd92b75b"},{"name":"is-page-static","duration":679345,"timestamp":118013843148,"id":49,"parentId":30,"tags":{},"startTime":1781338749614,"traceId":"9cb8826ecd92b75b"},{"name":"check-page","duration":688097,"timestamp":118013834431,"id":30,"parentId":19,"tags":{"page":"/api/sessions/inject"},"startTime":1781338749605,"traceId":"9cb8826ecd92b75b"},{"name":"is-page-static","duration":680072,"timestamp":118013842550,"id":46,"parentId":27,"tags":{},"startTime":1781338749613,"traceId":"9cb8826ecd92b75b"},{"name":"check-page","duration":688908,"timestamp":118013833738,"id":27,"parentId":19,"tags":{"page":"/api/files"},"startTime":1781338749604,"traceId":"9cb8826ecd92b75b"},{"name":"is-page-static","duration":681725,"timestamp":118013843663,"id":54,"parentId":35,"tags":{},"startTime":1781338749614,"traceId":"9cb8826ecd92b75b"},{"name":"check-page","duration":689938,"timestamp":118013835495,"id":35,"parentId":19,"tags":{"page":"/api/sessions/start"},"startTime":1781338749606,"traceId":"9cb8826ecd92b75b"},{"name":"is-page-static","duration":685057,"timestamp":118013843601,"id":53,"parentId":34,"tags":{},"startTime":1781338749614,"traceId":"9cb8826ecd92b75b"},{"name":"check-page","duration":693485,"timestamp":118013835212,"id":34,"parentId":19,"tags":{"page":"/api/sessions/scope"},"startTime":1781338749606,"traceId":"9cb8826ecd92b75b"},{"name":"is-page-static","duration":685674,"timestamp":118013843363,"id":51,"parentId":32,"tags":{},"startTime":1781338749614,"traceId":"9cb8826ecd92b75b"},{"name":"check-page","duration":694198,"timestamp":118013834859,"id":32,"parentId":19,"tags":{"page":"/api/sessions/launch"},"startTime":1781338749605,"traceId":"9cb8826ecd92b75b"},{"name":"is-page-static","duration":687810,"timestamp":118013843286,"id":50,"parentId":31,"tags":{},"startTime":1781338749614,"traceId":"9cb8826ecd92b75b"},{"name":"check-page","duration":696471,"timestamp":118013834656,"id":31,"parentId":19,"tags":{"page":"/api/sessions/input"},"startTime":1781338749605,"traceId":"9cb8826ecd92b75b"},{"name":"static-check","duration":702012,"timestamp":118013829142,"id":19,"parentId":1,"tags":{},"startTime":1781338749600,"traceId":"9cb8826ecd92b75b"},{"name":"write-routes-manifest","duration":825,"timestamp":118014532169,"id":60,"parentId":1,"tags":{},"startTime":1781338750303,"traceId":"9cb8826ecd92b75b"},{"name":"load-dotenv","duration":37,"timestamp":118014549398,"id":63,"parentId":62,"tags":{},"startTime":1781338750320,"traceId":"9cb8826ecd92b75b"},{"name":"run-export-path-map","duration":511,"timestamp":118014554631,"id":64,"parentId":62,"tags":{},"startTime":1781338750325,"traceId":"9cb8826ecd92b75b"},{"name":"next-export","duration":662660,"timestamp":118014548449,"id":62,"parentId":1,"tags":{},"startTime":1781338750319,"traceId":"9cb8826ecd92b75b"},{"name":"move-exported-app-not-found-","duration":2015,"timestamp":118015212509,"id":65,"parentId":61,"tags":{},"startTime":1781338750983,"traceId":"9cb8826ecd92b75b"},{"name":"move-exported-app-global-error-","duration":1369,"timestamp":118015214626,"id":66,"parentId":61,"tags":{},"startTime":1781338750985,"traceId":"9cb8826ecd92b75b"},{"name":"static-generation","duration":681180,"timestamp":118014537878,"id":61,"parentId":1,"tags":{},"startTime":1781338750308,"traceId":"9cb8826ecd92b75b"},{"name":"write-routes-manifest","duration":791,"timestamp":118015219174,"id":67,"parentId":1,"tags":{},"startTime":1781338750990,"traceId":"9cb8826ecd92b75b"},{"name":"print-tree-view","duration":2954,"timestamp":118015229447,"id":68,"parentId":1,"tags":{},"startTime":1781338751000,"traceId":"9cb8826ecd92b75b"},{"name":"write-route-bundle-stats","duration":16441,"timestamp":118015232437,"id":69,"parentId":1,"tags":{},"startTime":1781338751003,"traceId":"9cb8826ecd92b75b"},{"name":"telemetry-flush","duration":169841,"timestamp":118015248909,"id":70,"parentId":1,"tags":{},"startTime":1781338751019,"traceId":"9cb8826ecd92b75b"},{"name":"next-build","duration":7554364,"timestamp":118007864407,"id":1,"tags":{"buildMode":"default","version":"16.2.6","bundler":"turbopack","has-custom-webpack-config":"false","use-build-worker":"true"},"startTime":1781338743635,"traceId":"9cb8826ecd92b75b"}]
1
+ [{"name":"generate-buildid","duration":233,"timestamp":122936787499,"id":4,"parentId":1,"tags":{},"startTime":1781343672559,"traceId":"6671a738675edec6"},{"name":"load-custom-routes","duration":416,"timestamp":122936787853,"id":5,"parentId":1,"tags":{},"startTime":1781343672559,"traceId":"6671a738675edec6"},{"name":"create-dist-dir","duration":655,"timestamp":122936788306,"id":6,"parentId":1,"tags":{},"startTime":1781343672560,"traceId":"6671a738675edec6"},{"name":"clean","duration":505,"timestamp":122936789824,"id":7,"parentId":1,"tags":{},"startTime":1781343672561,"traceId":"6671a738675edec6"},{"name":"discover-routes","duration":7895,"timestamp":122936814021,"id":8,"parentId":1,"tags":{},"startTime":1781343672585,"traceId":"6671a738675edec6"},{"name":"create-root-mapping","duration":65,"timestamp":122936821969,"id":9,"parentId":1,"tags":{},"startTime":1781343672593,"traceId":"6671a738675edec6"},{"name":"generate-route-types","duration":18916,"timestamp":122936823530,"id":10,"parentId":1,"tags":{},"startTime":1781343672595,"traceId":"6671a738675edec6"},{"name":"public-dir-conflict-check","duration":112,"timestamp":122936842618,"id":11,"parentId":1,"tags":{},"startTime":1781343672614,"traceId":"6671a738675edec6"},{"name":"generate-routes-manifest","duration":2601,"timestamp":122936842847,"id":12,"parentId":1,"tags":{},"startTime":1781343672614,"traceId":"6671a738675edec6"},{"name":"run-turbopack","duration":2481070,"timestamp":122936852892,"id":14,"parentId":1,"tags":{},"startTime":1781343672624,"traceId":"6671a738675edec6"},{"name":"turbopack-build-events","duration":73,"timestamp":122937178078,"id":15,"parentId":1,"tags":{},"startTime":1781343672949,"traceId":"6671a738675edec6"},{"name":"run-typescript","duration":4017829,"timestamp":122939341501,"id":16,"parentId":1,"tags":{},"startTime":1781343675113,"traceId":"6671a738675edec6"},{"name":"generate-required-server-files","duration":1447,"timestamp":122943360185,"id":18,"parentId":1,"tags":{},"startTime":1781343679132,"traceId":"6671a738675edec6"},{"name":"check-static-error-page","duration":8108,"timestamp":122943512253,"id":20,"parentId":19,"tags":{},"startTime":1781343679284,"traceId":"6671a738675edec6"},{"name":"is-page-static","duration":585753,"timestamp":122943525922,"id":41,"parentId":22,"tags":{},"startTime":1781343679297,"traceId":"6671a738675edec6"},{"name":"check-page","duration":596750,"timestamp":122943515212,"id":22,"parentId":19,"tags":{"page":"/_global-error"},"startTime":1781343679287,"traceId":"6671a738675edec6"},{"name":"is-page-static","duration":766859,"timestamp":122943526780,"id":48,"parentId":29,"tags":{},"startTime":1781343679298,"traceId":"6671a738675edec6"},{"name":"check-page","duration":776640,"timestamp":122943517064,"id":29,"parentId":19,"tags":{"page":"/api/recents"},"startTime":1781343679288,"traceId":"6671a738675edec6"},{"name":"is-page-static","duration":781361,"timestamp":122943527001,"id":49,"parentId":30,"tags":{},"startTime":1781343679298,"traceId":"6671a738675edec6"},{"name":"check-page","duration":791109,"timestamp":122943517307,"id":30,"parentId":19,"tags":{"page":"/api/sessions/inject"},"startTime":1781343679289,"traceId":"6671a738675edec6"},{"name":"is-page-static","duration":783998,"timestamp":122943526248,"id":44,"parentId":25,"tags":{},"startTime":1781343679298,"traceId":"6671a738675edec6"},{"name":"check-page","duration":794120,"timestamp":122943516181,"id":25,"parentId":19,"tags":{"page":"/api/context"},"startTime":1781343679288,"traceId":"6671a738675edec6"},{"name":"is-page-static","duration":787189,"timestamp":122943526318,"id":45,"parentId":26,"tags":{},"startTime":1781343679298,"traceId":"6671a738675edec6"},{"name":"check-page","duration":797177,"timestamp":122943516382,"id":26,"parentId":19,"tags":{"page":"/api/context/ssh"},"startTime":1781343679288,"traceId":"6671a738675edec6"},{"name":"is-page-static","duration":787503,"timestamp":122943526675,"id":47,"parentId":28,"tags":{},"startTime":1781343679298,"traceId":"6671a738675edec6"},{"name":"check-page","duration":797395,"timestamp":122943516838,"id":28,"parentId":19,"tags":{"page":"/api/git"},"startTime":1781343679288,"traceId":"6671a738675edec6"},{"name":"is-page-static","duration":802754,"timestamp":122943525584,"id":40,"parentId":21,"tags":{},"startTime":1781343679297,"traceId":"6671a738675edec6"},{"name":"check-page","duration":814472,"timestamp":122943513913,"id":21,"parentId":19,"tags":{"page":"/_not-found"},"startTime":1781343679285,"traceId":"6671a738675edec6"},{"name":"is-page-static","duration":811742,"timestamp":122943526060,"id":42,"parentId":23,"tags":{},"startTime":1781343679297,"traceId":"6671a738675edec6"},{"name":"check-page","duration":822208,"timestamp":122943515644,"id":23,"parentId":19,"tags":{"page":"/api/context/aider"},"startTime":1781343679287,"traceId":"6671a738675edec6"},{"name":"is-page-static","duration":812137,"timestamp":122943527223,"id":51,"parentId":32,"tags":{},"startTime":1781343679299,"traceId":"6671a738675edec6"},{"name":"check-page","duration":821595,"timestamp":122943517818,"id":32,"parentId":19,"tags":{"page":"/api/sessions/launch"},"startTime":1781343679289,"traceId":"6671a738675edec6"},{"name":"is-page-static","duration":814442,"timestamp":122943527291,"id":52,"parentId":33,"tags":{},"startTime":1781343679299,"traceId":"6671a738675edec6"},{"name":"check-page","duration":823715,"timestamp":122943518063,"id":33,"parentId":19,"tags":{"page":"/api/sessions"},"startTime":1781343679289,"traceId":"6671a738675edec6"},{"name":"is-page-static","duration":815509,"timestamp":122943527411,"id":54,"parentId":35,"tags":{},"startTime":1781343679299,"traceId":"6671a738675edec6"},{"name":"check-page","duration":824290,"timestamp":122943518666,"id":35,"parentId":19,"tags":{"page":"/api/sessions/start"},"startTime":1781343679290,"traceId":"6671a738675edec6"},{"name":"is-page-static","duration":816612,"timestamp":122943527642,"id":57,"parentId":38,"tags":{},"startTime":1781343679299,"traceId":"6671a738675edec6"},{"name":"check-page","duration":824697,"timestamp":122943519593,"id":38,"parentId":19,"tags":{"page":"/favicon.ico"},"startTime":1781343679291,"traceId":"6671a738675edec6"},{"name":"is-page-static","duration":817880,"timestamp":122943526555,"id":46,"parentId":27,"tags":{},"startTime":1781343679298,"traceId":"6671a738675edec6"},{"name":"check-page","duration":827861,"timestamp":122943516594,"id":27,"parentId":19,"tags":{"page":"/api/files"},"startTime":1781343679288,"traceId":"6671a738675edec6"},{"name":"is-page-static","duration":818726,"timestamp":122943526160,"id":43,"parentId":24,"tags":{},"startTime":1781343679297,"traceId":"6671a738675edec6"},{"name":"check-page","duration":828955,"timestamp":122943515955,"id":24,"parentId":19,"tags":{"page":"/api/context/claude"},"startTime":1781343679287,"traceId":"6671a738675edec6"},{"name":"is-page-static","duration":819367,"timestamp":122943527444,"id":55,"parentId":36,"tags":{},"startTime":1781343679299,"traceId":"6671a738675edec6"},{"name":"check-page","duration":827716,"timestamp":122943519127,"id":36,"parentId":19,"tags":{"page":"/api/sessions/stats"},"startTime":1781343679290,"traceId":"6671a738675edec6"},{"name":"is-page-static","duration":822135,"timestamp":122943527144,"id":50,"parentId":31,"tags":{},"startTime":1781343679298,"traceId":"6671a738675edec6"},{"name":"check-page","duration":831733,"timestamp":122943517584,"id":31,"parentId":19,"tags":{"page":"/api/sessions/input"},"startTime":1781343679289,"traceId":"6671a738675edec6"},{"name":"is-page-static","duration":822223,"timestamp":122943527362,"id":53,"parentId":34,"tags":{},"startTime":1781343679299,"traceId":"6671a738675edec6"},{"name":"check-page","duration":831331,"timestamp":122943518290,"id":34,"parentId":19,"tags":{"page":"/api/sessions/scope"},"startTime":1781343679290,"traceId":"6671a738675edec6"},{"name":"is-page-static","duration":822073,"timestamp":122943531435,"id":58,"parentId":39,"tags":{},"startTime":1781343679303,"traceId":"6671a738675edec6"},{"name":"check-page","duration":833537,"timestamp":122943520002,"id":39,"parentId":19,"tags":{"page":"/"},"startTime":1781343679291,"traceId":"6671a738675edec6"},{"name":"is-page-static","duration":831907,"timestamp":122943527480,"id":56,"parentId":37,"tags":{},"startTime":1781343679299,"traceId":"6671a738675edec6"},{"name":"check-page","duration":840023,"timestamp":122943519404,"id":37,"parentId":19,"tags":{"page":"/api/sessions/stop"},"startTime":1781343679291,"traceId":"6671a738675edec6"},{"name":"static-check","duration":847689,"timestamp":122943511766,"id":19,"parentId":1,"tags":{},"startTime":1781343679283,"traceId":"6671a738675edec6"},{"name":"write-routes-manifest","duration":822,"timestamp":122944360519,"id":60,"parentId":1,"tags":{},"startTime":1781343680132,"traceId":"6671a738675edec6"},{"name":"load-dotenv","duration":34,"timestamp":122944376710,"id":63,"parentId":62,"tags":{},"startTime":1781343680148,"traceId":"6671a738675edec6"},{"name":"run-export-path-map","duration":483,"timestamp":122944381824,"id":64,"parentId":62,"tags":{},"startTime":1781343680153,"traceId":"6671a738675edec6"},{"name":"next-export","duration":699880,"timestamp":122944375844,"id":62,"parentId":1,"tags":{},"startTime":1781343680147,"traceId":"6671a738675edec6"},{"name":"move-exported-app-not-found-","duration":2207,"timestamp":122945077590,"id":65,"parentId":61,"tags":{},"startTime":1781343680849,"traceId":"6671a738675edec6"},{"name":"move-exported-app-global-error-","duration":1556,"timestamp":122945079921,"id":66,"parentId":61,"tags":{},"startTime":1781343680851,"traceId":"6671a738675edec6"},{"name":"static-generation","duration":718716,"timestamp":122944366415,"id":61,"parentId":1,"tags":{},"startTime":1781343680138,"traceId":"6671a738675edec6"},{"name":"write-routes-manifest","duration":795,"timestamp":122945085252,"id":67,"parentId":1,"tags":{},"startTime":1781343680857,"traceId":"6671a738675edec6"},{"name":"print-tree-view","duration":3065,"timestamp":122945096735,"id":68,"parentId":1,"tags":{},"startTime":1781343680868,"traceId":"6671a738675edec6"},{"name":"write-route-bundle-stats","duration":14376,"timestamp":122945099835,"id":69,"parentId":1,"tags":{},"startTime":1781343680871,"traceId":"6671a738675edec6"},{"name":"telemetry-flush","duration":174402,"timestamp":122945114263,"id":70,"parentId":1,"tags":{},"startTime":1781343680886,"traceId":"6671a738675edec6"},{"name":"next-build","duration":8551307,"timestamp":122936737382,"id":1,"tags":{"buildMode":"default","version":"16.2.6","bundler":"turbopack","has-custom-webpack-config":"false","use-build-worker":"true"},"startTime":1781343672509,"traceId":"6671a738675edec6"}]
package/.next/trace-build CHANGED
@@ -1 +1 @@
1
- [{"name":"run-turbopack","duration":2289163,"timestamp":118007971439,"id":14,"parentId":1,"tags":{},"startTime":1781338743742,"traceId":"9cb8826ecd92b75b"},{"name":"turbopack-build-events","duration":64,"timestamp":118008264417,"id":15,"parentId":1,"tags":{},"startTime":1781338744035,"traceId":"9cb8826ecd92b75b"},{"name":"run-typescript","duration":3434329,"timestamp":118010272734,"id":16,"parentId":1,"tags":{},"startTime":1781338746043,"traceId":"9cb8826ecd92b75b"},{"name":"static-check","duration":702012,"timestamp":118013829142,"id":19,"parentId":1,"tags":{},"startTime":1781338749600,"traceId":"9cb8826ecd92b75b"},{"name":"static-generation","duration":681180,"timestamp":118014537878,"id":61,"parentId":1,"tags":{},"startTime":1781338750308,"traceId":"9cb8826ecd92b75b"},{"name":"telemetry-flush","duration":169841,"timestamp":118015248909,"id":70,"parentId":1,"tags":{},"startTime":1781338751019,"traceId":"9cb8826ecd92b75b"},{"name":"next-build","duration":7554364,"timestamp":118007864407,"id":1,"tags":{"buildMode":"default","version":"16.2.6","bundler":"turbopack","has-custom-webpack-config":"false","use-build-worker":"true"},"startTime":1781338743635,"traceId":"9cb8826ecd92b75b"}]
1
+ [{"name":"run-turbopack","duration":2481070,"timestamp":122936852892,"id":14,"parentId":1,"tags":{},"startTime":1781343672624,"traceId":"6671a738675edec6"},{"name":"turbopack-build-events","duration":73,"timestamp":122937178078,"id":15,"parentId":1,"tags":{},"startTime":1781343672949,"traceId":"6671a738675edec6"},{"name":"run-typescript","duration":4017829,"timestamp":122939341501,"id":16,"parentId":1,"tags":{},"startTime":1781343675113,"traceId":"6671a738675edec6"},{"name":"static-check","duration":847689,"timestamp":122943511766,"id":19,"parentId":1,"tags":{},"startTime":1781343679283,"traceId":"6671a738675edec6"},{"name":"static-generation","duration":718716,"timestamp":122944366415,"id":61,"parentId":1,"tags":{},"startTime":1781343680138,"traceId":"6671a738675edec6"},{"name":"telemetry-flush","duration":174402,"timestamp":122945114263,"id":70,"parentId":1,"tags":{},"startTime":1781343680886,"traceId":"6671a738675edec6"},{"name":"next-build","duration":8551307,"timestamp":122936737382,"id":1,"tags":{"buildMode":"default","version":"16.2.6","bundler":"turbopack","has-custom-webpack-config":"false","use-build-worker":"true"},"startTime":1781343672509,"traceId":"6671a738675edec6"}]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "neural-loom",
3
- "version": "0.2.16",
3
+ "version": "0.2.18",
4
4
  "bin": {
5
5
  "neural-loom": "./bin/cli.js"
6
6
  },
@@ -1,6 +1,6 @@
1
1
  import { NextResponse } from "next/server";
2
2
  import { SessionManager } from "@/lib/agents/SessionManager";
3
- import { addRecent } from "@/lib/recents";
3
+ import { addRecent, saveLastSession } from "@/lib/recents";
4
4
 
5
5
  export async function POST(request: Request) {
6
6
  try {
@@ -15,6 +15,9 @@ export async function POST(request: Request) {
15
15
  const manager = SessionManager.getInstance();
16
16
  const session = await manager.createSession(type, name, workspaceRoot, scopedDirs);
17
17
 
18
+ // Save session config to recents
19
+ saveLastSession({ type, name, workspaceRoot, scopedDirs });
20
+
18
21
  // Save workspace root to recents list
19
22
  if (workspaceRoot) {
20
23
  addRecent("workspace", workspaceRoot);
@@ -1,6 +1,10 @@
1
1
  import { NextResponse } from "next/server";
2
2
  import { SessionManager } from "@/lib/agents/SessionManager";
3
3
 
4
+ declare global {
5
+ var wsPort: number | undefined;
6
+ }
7
+
4
8
  export async function GET(request: Request) {
5
9
  try {
6
10
  const { searchParams } = new URL(request.url);
@@ -15,12 +19,14 @@ export async function GET(request: Request) {
15
19
  return NextResponse.json({
16
20
  ...session.getStatus(),
17
21
  logs: session.logs,
22
+ wsPort: global.wsPort || 3001,
18
23
  });
19
24
  }
20
25
 
21
26
  const sessions = manager.listSessions();
22
27
  const originalCwd = process.env.ORIGINAL_CWD || process.cwd();
23
- return NextResponse.json({ sessions, originalCwd });
28
+ const wsPort = global.wsPort || 3001;
29
+ return NextResponse.json({ sessions, originalCwd, wsPort });
24
30
  } catch (error) {
25
31
  const errorMessage = error instanceof Error ? error.message : "Unknown error";
26
32
  return NextResponse.json({ error: errorMessage }, { status: 500 });
@@ -73,6 +73,7 @@ interface IdeLayoutProps {
73
73
  sessions: RunnerStatus[];
74
74
  onSwitchSession: (id: string) => void;
75
75
  onRefreshSessions?: () => void;
76
+ wsPort?: number;
76
77
  }
77
78
 
78
79
  interface OpenFile {
@@ -82,7 +83,7 @@ interface OpenFile {
82
83
  isModified: boolean;
83
84
  }
84
85
 
85
- export default function IdeLayout({ sessionId, workspaceRoot, scopedDirs, onClose, sessions, onSwitchSession, onRefreshSessions }: IdeLayoutProps) {
86
+ export default function IdeLayout({ sessionId, workspaceRoot, scopedDirs, onClose, sessions, onSwitchSession, onRefreshSessions, wsPort = 3001 }: IdeLayoutProps) {
86
87
  const scopedDirsStr = JSON.stringify(scopedDirs);
87
88
  const activeSession = sessions.find(s => s.id === sessionId);
88
89
  const [workspace, setWorkspace] = useState<WorkspaceData | null>(null);
@@ -1411,7 +1412,7 @@ export default function IdeLayout({ sessionId, workspaceRoot, scopedDirs, onClos
1411
1412
 
1412
1413
  <div style={{ ...terminalConsoleWrapperStyle, display: "flex", width: "100%", height: "100%" }}>
1413
1414
  <div style={{ flex: 1, height: "100%", borderRight: splitSessionId ? "1px solid var(--border)" : "none", overflow: "hidden" }}>
1414
- <TerminalConsole sessionId={sessionId} height="100%" />
1415
+ <TerminalConsole sessionId={sessionId} height="100%" wsPort={wsPort} />
1415
1416
  </div>
1416
1417
  {splitSessionId && (
1417
1418
  <div style={{ flex: 1, height: "100%", overflow: "hidden", display: "flex", flexDirection: "column" }}>
@@ -1426,7 +1427,7 @@ export default function IdeLayout({ sessionId, workspaceRoot, scopedDirs, onClos
1426
1427
  </button>
1427
1428
  </div>
1428
1429
  <div style={{ flex: 1, overflow: "hidden" }}>
1429
- <TerminalConsole sessionId={splitSessionId} height="100%" />
1430
+ <TerminalConsole sessionId={splitSessionId} height="100%" wsPort={wsPort} />
1430
1431
  </div>
1431
1432
  </div>
1432
1433
  )}
@@ -9,9 +9,10 @@ import "xterm/css/xterm.css";
9
9
  interface TerminalConsoleProps {
10
10
  sessionId: string;
11
11
  height?: string | number;
12
+ wsPort?: number;
12
13
  }
13
14
 
14
- export default function TerminalConsole({ sessionId, height = "400px" }: TerminalConsoleProps) {
15
+ export default function TerminalConsole({ sessionId, height = "400px", wsPort = 3001 }: TerminalConsoleProps) {
15
16
  const terminalRef = useRef<HTMLDivElement>(null);
16
17
  const xtermRef = useRef<Terminal | null>(null);
17
18
  const wsRef = useRef<WebSocket | null>(null);
@@ -130,9 +131,9 @@ export default function TerminalConsole({ sessionId, height = "400px" }: Termina
130
131
 
131
132
  xtermRef.current = term;
132
133
 
133
- // Connect WebSocket client to gateway server on port 3001
134
+ // Connect WebSocket client to gateway server on dynamic port
134
135
  const protocol = window.location.protocol === "https:" ? "wss:" : "ws:";
135
- const wsUrl = `${protocol}//${window.location.hostname}:3001`;
136
+ const wsUrl = `${protocol}//${window.location.hostname}:${wsPort}`;
136
137
  const ws = new WebSocket(wsUrl);
137
138
  wsRef.current = ws;
138
139
 
@@ -216,7 +217,7 @@ export default function TerminalConsole({ sessionId, height = "400px" }: Termina
216
217
  console.warn("Error disposing terminal:", e);
217
218
  }
218
219
  };
219
- }, [sessionId]);
220
+ }, [sessionId, wsPort]);
220
221
 
221
222
  return (
222
223
  <div
package/src/app/page.tsx CHANGED
@@ -88,6 +88,13 @@ export default function Home() {
88
88
  const [isClaudeWizardOpen, setIsClaudeWizardOpen] = useState<boolean>(false);
89
89
  const [isClaudeDockerLaunch, setIsClaudeDockerLaunch] = useState<boolean>(false);
90
90
  const [serverCwd, setServerCwd] = useState<string>("");
91
+ const [wsPort, setWsPort] = useState<number>(3001);
92
+ const [lastSessionConfig, setLastSessionConfig] = useState<{
93
+ type: 'mock' | 'claude' | 'claude-docker' | 'claude-ssh' | 'aider' | 'aider-docker';
94
+ name?: string;
95
+ workspaceRoot?: string;
96
+ scopedDirs?: string[];
97
+ } | null>(null);
91
98
 
92
99
  const activeSession = sessions.find(s => s.id === activeSessionId);
93
100
  const isDockerActive = sessions.some(s => (s.type === 'claude-docker' || s.type === 'aider-docker') && s.status === 'running');
@@ -101,6 +108,9 @@ export default function Home() {
101
108
  if (data.sessions) {
102
109
  setSessions(data.sessions);
103
110
  }
111
+ if (data.wsPort) {
112
+ setWsPort(data.wsPort);
113
+ }
104
114
  if (data.originalCwd) {
105
115
  setServerCwd(data.originalCwd);
106
116
  }
@@ -109,9 +119,24 @@ export default function Home() {
109
119
  }
110
120
  };
111
121
 
122
+ const fetchRecents = async () => {
123
+ try {
124
+ const res = await fetch("/api/recents");
125
+ if (res.ok) {
126
+ const data = await res.json();
127
+ if (data.success && data.recents && data.recents.lastSession) {
128
+ setLastSessionConfig(data.recents.lastSession);
129
+ }
130
+ }
131
+ } catch (err) {
132
+ console.error("Failed to fetch recents:", err);
133
+ }
134
+ };
135
+
112
136
  useEffect(() => {
113
137
  const initFetch = async () => {
114
138
  await fetchSessions();
139
+ await fetchRecents();
115
140
  };
116
141
  initFetch();
117
142
  const interval = setInterval(fetchSessions, 4000);
@@ -173,6 +198,7 @@ export default function Home() {
173
198
  if (data.success) {
174
199
  await fetchSessions();
175
200
  setActiveSessionId(data.session.id);
201
+ setLastSessionConfig({ type, name: sessionName, workspaceRoot: customWorkspaceRoot, scopedDirs: customScopedDirs });
176
202
  }
177
203
  } catch (err) {
178
204
  console.error("Failed to launch session:", err);
@@ -226,6 +252,7 @@ export default function Home() {
226
252
  sessions={sessions}
227
253
  onSwitchSession={(id) => setActiveSessionId(id)}
228
254
  onRefreshSessions={fetchSessions}
255
+ wsPort={wsPort}
229
256
  />
230
257
  </div>
231
258
  ) : (
@@ -239,16 +266,71 @@ export default function Home() {
239
266
  {sessions.length === 0 ? (
240
267
  <div className="card" style={{ padding: "3rem 1.5rem", textAlign: "center" }}>
241
268
  <p style={{ color: "var(--muted)", marginBottom: "1.5rem" }}>
242
- No active agent sessions in this workspace. Launch a simulated session to verify the control bridge.
269
+ No active agent sessions in this workspace. Launch a Claude or Aider agent session to begin.
243
270
  </p>
244
- <button
245
- className={styles.quickLaunchBtn}
246
- onClick={() => handleLaunchSession('mock')}
247
- disabled={isLoading}
248
- style={{ maxWidth: "240px", margin: "0 auto" }}
249
- >
250
- {isLoading ? "Launching..." : "⚡ Launch Simulated Session"}
251
- </button>
271
+ <div style={{ display: "flex", gap: "1rem", justifyContent: "center", flexWrap: "wrap" }}>
272
+ {lastSessionConfig && (
273
+ <button
274
+ className={styles.quickLaunchBtn}
275
+ onClick={() => {
276
+ handleLaunchSession(
277
+ lastSessionConfig.type,
278
+ lastSessionConfig.name,
279
+ lastSessionConfig.workspaceRoot,
280
+ lastSessionConfig.scopedDirs
281
+ );
282
+ }}
283
+ disabled={isLoading}
284
+ style={{
285
+ background: "linear-gradient(135deg, hsl(38, 92%, 50%) 0%, hsl(0, 84%, 60%) 100%)",
286
+ border: "none",
287
+ maxWidth: "200px"
288
+ }}
289
+ title={`Re-launch: ${lastSessionConfig.name || lastSessionConfig.type}`}
290
+ >
291
+ ⚡ Re-launch Last Session
292
+ </button>
293
+ )}
294
+ <button
295
+ className={styles.quickLaunchBtn}
296
+ onClick={() => {
297
+ setIsClaudeDockerLaunch(false);
298
+ setIsClaudeWizardOpen(true);
299
+ }}
300
+ disabled={isLoading}
301
+ style={{
302
+ background: "linear-gradient(135deg, hsl(262, 83%, 60%) 0%, hsl(195, 100%, 45%) 100%)",
303
+ border: "none",
304
+ maxWidth: "200px"
305
+ }}
306
+ >
307
+ Launch Claude PTY
308
+ </button>
309
+ <button
310
+ className={styles.quickLaunchBtn}
311
+ onClick={() => {
312
+ setIsAiderDockerLaunch(false);
313
+ setIsAiderWizardOpen(true);
314
+ }}
315
+ disabled={isLoading}
316
+ style={{
317
+ background: "linear-gradient(135deg, hsl(210, 80%, 45%) 0%, hsl(262, 80%, 55%) 100%)",
318
+ border: "none",
319
+ maxWidth: "200px"
320
+ }}
321
+ >
322
+ Launch Aider PTY
323
+ </button>
324
+ </div>
325
+ <div style={{ marginTop: "1.5rem", fontSize: "0.75rem", color: "var(--muted)" }}>
326
+ Want to verify layout features first?{" "}
327
+ <span
328
+ onClick={() => handleLaunchSession('mock')}
329
+ style={{ color: "var(--primary)", cursor: "pointer", textDecoration: "underline" }}
330
+ >
331
+ Start simulated sandbox session
332
+ </span>
333
+ </div>
252
334
  </div>
253
335
  ) : (
254
336
  <div className={styles.agentGrid}>
@@ -383,7 +465,7 @@ export default function Home() {
383
465
  >Close Panel ✖</button>
384
466
  </div>
385
467
  </div>
386
- <TerminalConsole sessionId={activeSessionId} />
468
+ <TerminalConsole sessionId={activeSessionId} wsPort={wsPort} />
387
469
  </section>
388
470
  )}
389
471
 
@@ -482,10 +564,6 @@ export default function Home() {
482
564
  </section>
483
565
 
484
566
  <section style={{ display: "flex", flexDirection: "column", gap: "0.75rem" }}>
485
- <button className={styles.quickLaunchBtn} onClick={() => handleLaunchSession('mock')} disabled={isLoading}>
486
- <span>⚡</span>
487
- <span>Launch Mock Agent</span>
488
- </button>
489
567
  <button
490
568
  className={styles.quickLaunchBtn}
491
569
  onClick={() => {
@@ -576,6 +654,15 @@ export default function Home() {
576
654
  <span style={{ color: "var(--primary)", cursor: "pointer" }}>→</span>
577
655
  </li>
578
656
  </ul>
657
+ <div style={{ marginTop: "1.2rem", textAlign: "center", fontSize: "0.72rem", color: "var(--muted)" }}>
658
+ Need to troubleshoot?{" "}
659
+ <span
660
+ onClick={() => handleLaunchSession('mock')}
661
+ style={{ color: "var(--primary)", cursor: "pointer", textDecoration: "underline" }}
662
+ >
663
+ Launch simulated playground sandbox
664
+ </span>
665
+ </div>
579
666
  </section>
580
667
  </aside>
581
668
 
@@ -3,6 +3,7 @@ import { AgentRunner } from "./AgentRunner";
3
3
 
4
4
  declare global {
5
5
  var wss: WebSocketServer | undefined;
6
+ var wsPort: number | undefined;
6
7
  }
7
8
 
8
9
  let getSessionCallback: ((id: string) => AgentRunner | undefined) | null = null;
@@ -13,120 +14,144 @@ export function initWebSocketServer(port = 3001, getSession?: (id: string) => Ag
13
14
  }
14
15
  // Prevent duplicate WebSocket servers during hot reloads in development
15
16
  if (global.wss) {
16
- console.log("[WebSocketServer] WebSocket Server already active on port 3001 (cached).");
17
+ console.log(`[WebSocketServer] WebSocket Server already active on port ${global.wsPort || 3001} (cached).`);
17
18
  return;
18
19
  }
19
20
 
20
- console.log(`[WebSocketServer] Launching NeuralLoom WebSocket Gateway on port ${port}...`);
21
+ let currentPort = port;
22
+ const maxPort = port + 10;
21
23
 
22
- try {
23
- const wss = new WebSocketServer({ port });
24
- wss.on("error", (err) => {
25
- console.error("[WebSocketServer] WebSocket server error:", err);
26
- });
27
- global.wss = wss;
24
+ function tryBind() {
25
+ console.log(`[WebSocketServer] Launching NeuralLoom WebSocket Gateway on port ${currentPort}...`);
28
26
 
29
- wss.on("connection", (ws: WebSocket & { sessionId?: string }) => {
30
- console.log("[WebSocketServer] New client connected via gateway.");
27
+ try {
28
+ const wss = new WebSocketServer({ port: currentPort });
31
29
 
32
- let activeSessionId: string | null = null;
33
- let outputListener: ((data: string) => void) | null = null;
34
- let mockBuffer = "";
30
+ wss.on("error", (err: unknown) => {
31
+ const socketErr = err as { code?: string };
32
+ if (socketErr.code === "EADDRINUSE" && currentPort < maxPort) {
33
+ console.warn(`[WebSocketServer] Port ${currentPort} in use, trying next port...`);
34
+ currentPort++;
35
+ tryBind();
36
+ } else {
37
+ console.error("[WebSocketServer] WebSocket server error:", err);
38
+ }
39
+ });
35
40
 
36
- ws.on("message", async (rawMsg) => {
37
- try {
38
- const msg = JSON.parse(rawMsg.toString());
41
+ wss.on("listening", () => {
42
+ global.wss = wss;
43
+ global.wsPort = currentPort;
44
+ console.log(`[WebSocketServer] WebSocket gateway successfully listening on port ${currentPort}.`);
45
+ });
39
46
 
40
- // Client handshake to establish connection to a specific session
41
- if (msg.type === "handshake") {
42
- const { sessionId } = msg;
43
- const session = getSessionCallback ? getSessionCallback(sessionId) : null;
47
+ wss.on("connection", (ws: WebSocket & { sessionId?: string }) => {
48
+ console.log("[WebSocketServer] New client connected via gateway.");
44
49
 
45
- if (session) {
46
- activeSessionId = sessionId;
47
- ws.sessionId = sessionId;
48
- console.log(`[WebSocketServer] Client connected to active session: ${sessionId}`);
49
-
50
- // Subscribe to runner output events
51
- outputListener = (data: string) => {
52
- if (ws.readyState === WebSocket.OPEN) {
53
- ws.send(JSON.stringify({ type: "output", data }));
54
- }
55
- };
56
- session.onOutput(outputListener);
57
-
58
- // Send handshake acknowledgment and full historical logs
59
- ws.send(
60
- JSON.stringify({
61
- type: "handshake-ack",
62
- sessionId,
63
- history: session.logs.join(""),
64
- })
65
- );
66
- } else {
67
- ws.send(JSON.stringify({ type: "error", message: "Requested session not found" }));
50
+ let activeSessionId: string | null = null;
51
+ let outputListener: ((data: string) => void) | null = null;
52
+ let mockBuffer = "";
53
+
54
+ ws.on("message", async (rawMsg) => {
55
+ try {
56
+ const msg = JSON.parse(rawMsg.toString());
57
+
58
+ // Client handshake to establish connection to a specific session
59
+ if (msg.type === "handshake") {
60
+ const { sessionId } = msg;
61
+ const session = getSessionCallback ? getSessionCallback(sessionId) : null;
62
+
63
+ if (session) {
64
+ activeSessionId = sessionId;
65
+ ws.sessionId = sessionId;
66
+ console.log(`[WebSocketServer] Client connected to active session: ${sessionId}`);
67
+
68
+ // Subscribe to runner output events
69
+ outputListener = (data: string) => {
70
+ if (ws.readyState === WebSocket.OPEN) {
71
+ ws.send(JSON.stringify({ type: "output", data }));
72
+ }
73
+ };
74
+ session.onOutput(outputListener);
75
+
76
+ // Send handshake acknowledgment and full historical logs
77
+ ws.send(
78
+ JSON.stringify({
79
+ type: "handshake-ack",
80
+ sessionId,
81
+ history: session.logs.join(""),
82
+ })
83
+ );
84
+ } else {
85
+ ws.send(JSON.stringify({ type: "error", message: "Requested session not found" }));
86
+ }
68
87
  }
69
- }
70
88
 
71
- // User terminal input routing
72
- if (msg.type === "input" && activeSessionId) {
73
- const session = getSessionCallback ? getSessionCallback(activeSessionId) : null;
74
- if (session && session.status === "running") {
75
- if (session.type === "mock") {
76
- // Buffer input for simulated command parsing in MockRunner
77
- const data = msg.data;
78
- if (data === "\r" || data === "\r\n") {
79
- ws.send(JSON.stringify({ type: "output", data: "\r\n" }));
80
- const cmd = mockBuffer;
81
- mockBuffer = "";
82
- await session.sendInput(cmd);
83
- } else if (data === "\u007f" || data === "\b") { // Backspace
84
- if (mockBuffer.length > 0) {
85
- mockBuffer = mockBuffer.slice(0, -1);
86
- ws.send(JSON.stringify({ type: "output", data: "\b \b" }));
89
+ // User terminal input routing
90
+ if (msg.type === "input" && activeSessionId) {
91
+ const session = getSessionCallback ? getSessionCallback(activeSessionId) : null;
92
+ if (session && session.status === "running") {
93
+ if (session.type === "mock") {
94
+ // Buffer input for simulated command parsing in MockRunner
95
+ const data = msg.data;
96
+ if (data === "\r" || data === "\r\n") {
97
+ ws.send(JSON.stringify({ type: "output", data: "\r\n" }));
98
+ const cmd = mockBuffer;
99
+ mockBuffer = "";
100
+ await session.sendInput(cmd);
101
+ } else if (data === "\u007f" || data === "\b") { // Backspace
102
+ if (mockBuffer.length > 0) {
103
+ mockBuffer = mockBuffer.slice(0, -1);
104
+ ws.send(JSON.stringify({ type: "output", data: "\b \b" }));
105
+ }
106
+ } else {
107
+ mockBuffer += data;
108
+ ws.send(JSON.stringify({ type: "output", data })); // Local echo of key
87
109
  }
88
110
  } else {
89
- mockBuffer += data;
90
- ws.send(JSON.stringify({ type: "output", data })); // Local echo of key
111
+ // Pass raw keystrokes straight to Claude Code PTY
112
+ await session.sendInput(msg.data, true);
91
113
  }
92
- } else {
93
- // Pass raw keystrokes straight to Claude Code PTY
94
- await session.sendInput(msg.data, true);
95
114
  }
96
115
  }
116
+
117
+ // User terminal resize routing
118
+ if (msg.type === "resize" && activeSessionId) {
119
+ const { cols, rows } = msg;
120
+ const session = getSessionCallback ? getSessionCallback(activeSessionId) : null;
121
+ if (session) {
122
+ session.resize(cols, rows);
123
+ }
124
+ }
125
+ } catch (err) {
126
+ console.error("[WebSocketServer] Error processing socket packet:", err);
97
127
  }
128
+ });
98
129
 
99
- // User terminal resize routing
100
- if (msg.type === "resize" && activeSessionId) {
101
- const { cols, rows } = msg;
130
+ ws.on("close", () => {
131
+ if (activeSessionId && outputListener) {
102
132
  const session = getSessionCallback ? getSessionCallback(activeSessionId) : null;
103
133
  if (session) {
104
- session.resize(cols, rows);
134
+ session.offOutput(outputListener);
105
135
  }
106
136
  }
107
- } catch (err) {
108
- console.error("[WebSocketServer] Error processing socket packet:", err);
109
- }
110
- });
111
-
112
- ws.on("close", () => {
113
- if (activeSessionId && outputListener) {
114
- const session = getSessionCallback ? getSessionCallback(activeSessionId) : null;
115
- if (session) {
116
- session.offOutput(outputListener);
117
- }
118
- }
119
- console.log("[WebSocketServer] Client connection closed.");
120
- });
137
+ console.log("[WebSocketServer] Client connection closed.");
138
+ });
121
139
 
122
- ws.on("error", (err) => {
123
- console.error("[WebSocketServer] Socket error:", err);
140
+ ws.on("error", (err) => {
141
+ console.error("[WebSocketServer] Socket error:", err);
142
+ });
124
143
  });
125
- });
126
144
 
127
- console.log(`[WebSocketServer] WebSocket gateway successfully listening on port ${port}.`);
128
- } catch (error) {
129
- const errorMessage = error instanceof Error ? error.message : "Unknown error";
130
- console.error(`[WebSocketServer] Failed to initialize WebSocket Server: ${errorMessage}`);
145
+ } catch (error) {
146
+ if (currentPort < maxPort) {
147
+ currentPort++;
148
+ tryBind();
149
+ } else {
150
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
151
+ console.error(`[WebSocketServer] Failed to initialize WebSocket Server: ${errorMessage}`);
152
+ }
153
+ }
131
154
  }
155
+
156
+ tryBind();
132
157
  }