repoburg 1.3.201 → 1.3.204

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 (325) hide show
  1. package/agent-pane/backend/dist/index.js +2714 -32395
  2. package/agent-pane/backend/dist/server.js +45304 -0
  3. package/askuser-after-step5-postgres-selected.png +0 -0
  4. package/askuser-after-step7-answer-sent.png +0 -0
  5. package/askuser-card-visible.png +0 -0
  6. package/backend/dist/src/agent-runtime/agent-runtime-event-emitter.d.ts +16 -0
  7. package/backend/dist/src/agent-runtime/agent-runtime-event-emitter.js +87 -0
  8. package/backend/dist/src/agent-runtime/agent-runtime-event-emitter.js.map +1 -1
  9. package/backend/dist/src/agent-runtime/agent-runtime.types.d.ts +16 -1
  10. package/backend/dist/src/app.controller.d.ts +1 -0
  11. package/backend/dist/src/app.service.d.ts +1 -0
  12. package/backend/dist/src/app.service.js +4 -1
  13. package/backend/dist/src/app.service.js.map +1 -1
  14. package/backend/dist/src/interactive-chat/chat.service.d.ts +4 -0
  15. package/backend/dist/src/interactive-chat/chat.service.js +21 -1
  16. package/backend/dist/src/interactive-chat/chat.service.js.map +1 -1
  17. package/backend/dist/src/llm-orchestration/action-handlers/invoke-subagent.handler.js +12 -1
  18. package/backend/dist/src/llm-orchestration/action-handlers/invoke-subagent.handler.js.map +1 -1
  19. package/backend/dist/src/llm-orchestration/llm-orchestration.interfaces.d.ts +1 -0
  20. package/backend/dist/src/llm-orchestration/llm-orchestration.interfaces.js +1 -0
  21. package/backend/dist/src/llm-orchestration/llm-orchestration.interfaces.js.map +1 -1
  22. package/backend/dist/src/llm-orchestration/llm-turn-processor.service.js +2 -0
  23. package/backend/dist/src/llm-orchestration/llm-turn-processor.service.js.map +1 -1
  24. package/backend/dist/tsconfig.build.tsbuildinfo +1 -1
  25. package/daemon/dist/agentPane.js +128 -0
  26. package/daemon/dist/agentPane.spec.js +183 -0
  27. package/daemon/dist/api/services.js +4 -0
  28. package/daemon/dist/index.js +28 -8
  29. package/daemon/dist/serviceManager.js +30 -11
  30. package/daemon/dist/serviceManager.spec.js +26 -0
  31. package/daemon/dist/workspace-validation.js +63 -0
  32. package/daemon/dist/workspace-validation.spec.js +63 -0
  33. package/daemon/package.json +1 -1
  34. package/package.json +15 -2
  35. package/r2-step4-apple-banana.png +0 -0
  36. package/r2-step5-after-rewind-apple-last.png +0 -0
  37. package/r2-step7-apple-cherry.png +0 -0
  38. package/r2-step8-cherry-menu-no-restore.png +0 -0
  39. package/r3-step3-live-task-card-running.png +0 -0
  40. package/r3-step4-after-completion.png +0 -0
  41. package/r3-step5-persistence-after-reload.png +0 -0
  42. package/r3-step6-tasks-tab-card.png +0 -0
  43. package/reverify-answered-state-answer-sent.png +0 -0
  44. package/reverify-card-visible-after-completion.png +0 -0
  45. package/rewind-step3-apple-banana-turns.png +0 -0
  46. package/rewind-step5-ui-unchanged-after-confirmed-rewind.png +0 -0
  47. package/rewind-step6-apple-menu-no-restore.png +0 -0
  48. package/rewind-step7-final-turnlist-unchanged.png +0 -0
  49. package/rewind-step7-negative-error-toast.png +0 -0
  50. package/rewind-step7-negative-error-toast2.png +0 -0
  51. package/subagent-r2-step5-turn-after-reload-no-card.png +0 -0
  52. package/subagent-r2-step6-tasks-panel-card.png +0 -0
  53. package/subagent-step3-tasks-tab-done-card.png +0 -0
  54. package/subagent-step4-turn-stuck-no-cards.png +0 -0
  55. package/agent-pane/.prettierignore +0 -5
  56. package/agent-pane/.prettierrc +0 -8
  57. package/agent-pane/backend/coverage/base.css +0 -224
  58. package/agent-pane/backend/coverage/block-navigation.js +0 -87
  59. package/agent-pane/backend/coverage/clover.xml +0 -887
  60. package/agent-pane/backend/coverage/coverage-final.json +0 -10
  61. package/agent-pane/backend/coverage/favicon.png +0 -0
  62. package/agent-pane/backend/coverage/index.html +0 -176
  63. package/agent-pane/backend/coverage/prettify.css +0 -1
  64. package/agent-pane/backend/coverage/prettify.js +0 -2
  65. package/agent-pane/backend/coverage/sort-arrow-sprite.png +0 -0
  66. package/agent-pane/backend/coverage/sorter.js +0 -210
  67. package/agent-pane/backend/coverage/src/codex/codex-provider.ts.html +0 -1402
  68. package/agent-pane/backend/coverage/src/codex/index.html +0 -131
  69. package/agent-pane/backend/coverage/src/codex/item-mapper.ts.html +0 -331
  70. package/agent-pane/backend/coverage/src/db/index.html +0 -131
  71. package/agent-pane/backend/coverage/src/db/index.ts.html +0 -436
  72. package/agent-pane/backend/coverage/src/db/schema.ts.html +0 -175
  73. package/agent-pane/backend/coverage/src/index.html +0 -116
  74. package/agent-pane/backend/coverage/src/index.ts.html +0 -262
  75. package/agent-pane/backend/coverage/src/routes/index.html +0 -146
  76. package/agent-pane/backend/coverage/src/routes/sessions.ts.html +0 -991
  77. package/agent-pane/backend/coverage/src/routes/turns.ts.html +0 -256
  78. package/agent-pane/backend/coverage/src/routes/workspaces.ts.html +0 -406
  79. package/agent-pane/backend/coverage/src/ws/index.html +0 -116
  80. package/agent-pane/backend/coverage/src/ws/index.ts.html +0 -238
  81. package/agent-pane/backend/data.db +0 -0
  82. package/agent-pane/backend/data.db-shm +0 -0
  83. package/agent-pane/backend/data.db-wal +0 -0
  84. package/agent-pane/backend/package-lock.json +0 -5945
  85. package/agent-pane/backend/package.json +0 -33
  86. package/agent-pane/backend/scripts/backfill-from-jsonl.ts +0 -206
  87. package/agent-pane/backend/scripts/import-session-from-jsonl.ts +0 -264
  88. package/agent-pane/backend/src/claude/claude-message-mapper.test.ts +0 -271
  89. package/agent-pane/backend/src/claude/claude-message-mapper.ts +0 -259
  90. package/agent-pane/backend/src/claude/claude-provider.test.ts +0 -455
  91. package/agent-pane/backend/src/claude/claude-provider.ts +0 -1319
  92. package/agent-pane/backend/src/claude/claude-types.ts +0 -45
  93. package/agent-pane/backend/src/codex/app-server-client.test.ts +0 -183
  94. package/agent-pane/backend/src/codex/app-server-client.ts +0 -247
  95. package/agent-pane/backend/src/codex/app-server-types.ts +0 -259
  96. package/agent-pane/backend/src/codex/codex-provider-runtime-config.test.ts +0 -56
  97. package/agent-pane/backend/src/codex/codex-provider.test.ts +0 -60
  98. package/agent-pane/backend/src/codex/codex-provider.ts +0 -619
  99. package/agent-pane/backend/src/codex/item-mapper.test.ts +0 -221
  100. package/agent-pane/backend/src/codex/item-mapper.ts +0 -86
  101. package/agent-pane/backend/src/codex/shared-app-server.ts +0 -25
  102. package/agent-pane/backend/src/db/index.test.ts +0 -125
  103. package/agent-pane/backend/src/db/index.ts +0 -230
  104. package/agent-pane/backend/src/db/schema.ts +0 -34
  105. package/agent-pane/backend/src/index.ts +0 -89
  106. package/agent-pane/backend/src/integration/__fixtures__/cancel-turn.json +0 -29
  107. package/agent-pane/backend/src/integration/__fixtures__/delta-streaming.json +0 -40
  108. package/agent-pane/backend/src/integration/__fixtures__/failed-turn.json +0 -16
  109. package/agent-pane/backend/src/integration/__fixtures__/mcp-tool-call.json +0 -60
  110. package/agent-pane/backend/src/integration/__fixtures__/missing-item-started.json +0 -34
  111. package/agent-pane/backend/src/integration/__fixtures__/multi-item-turn.json +0 -85
  112. package/agent-pane/backend/src/integration/__fixtures__/multi-turn.json +0 -48
  113. package/agent-pane/backend/src/integration/__fixtures__/simple-turn.json +0 -35
  114. package/agent-pane/backend/src/integration/delta-streaming.test.ts +0 -130
  115. package/agent-pane/backend/src/integration/helpers/app-server-mock.ts +0 -272
  116. package/agent-pane/backend/src/integration/helpers/test-app.ts +0 -110
  117. package/agent-pane/backend/src/integration/helpers/ws-capture.ts +0 -64
  118. package/agent-pane/backend/src/integration/item-lifecycle.test.ts +0 -133
  119. package/agent-pane/backend/src/integration/mcp-tool-call.test.ts +0 -102
  120. package/agent-pane/backend/src/integration/missing-item-started.test.ts +0 -91
  121. package/agent-pane/backend/src/integration/multi-turn.test.ts +0 -121
  122. package/agent-pane/backend/src/integration/session-operations.test.ts +0 -175
  123. package/agent-pane/backend/src/integration/threads-claude-fork.test.ts +0 -241
  124. package/agent-pane/backend/src/integration/threads.test.ts +0 -309
  125. package/agent-pane/backend/src/integration/title-generation.test.ts +0 -109
  126. package/agent-pane/backend/src/integration/turn-cancel.test.ts +0 -111
  127. package/agent-pane/backend/src/integration/turn-failure.test.ts +0 -97
  128. package/agent-pane/backend/src/integration/turn-lifecycle.test.ts +0 -141
  129. package/agent-pane/backend/src/providers/index.ts +0 -16
  130. package/agent-pane/backend/src/providers/registry.test.ts +0 -92
  131. package/agent-pane/backend/src/providers/registry.ts +0 -43
  132. package/agent-pane/backend/src/providers/runtime-provider.ts +0 -43
  133. package/agent-pane/backend/src/repoburg/action-mapping.ts +0 -216
  134. package/agent-pane/backend/src/repoburg/agent-runtime-client.test.ts +0 -271
  135. package/agent-pane/backend/src/repoburg/agent-runtime-client.ts +0 -262
  136. package/agent-pane/backend/src/repoburg/backend-resolver.test.ts +0 -271
  137. package/agent-pane/backend/src/repoburg/backend-resolver.ts +0 -246
  138. package/agent-pane/backend/src/repoburg/repoburg-client.test.ts +0 -159
  139. package/agent-pane/backend/src/repoburg/repoburg-client.ts +0 -181
  140. package/agent-pane/backend/src/repoburg/repoburg-provider.test.ts +0 -723
  141. package/agent-pane/backend/src/repoburg/repoburg-provider.ts +0 -422
  142. package/agent-pane/backend/src/repoburg/turn-projector.test.ts +0 -475
  143. package/agent-pane/backend/src/repoburg/turn-projector.ts +0 -537
  144. package/agent-pane/backend/src/routes/files.ts +0 -31
  145. package/agent-pane/backend/src/routes/mcp.ts +0 -33
  146. package/agent-pane/backend/src/routes/models.test.ts +0 -92
  147. package/agent-pane/backend/src/routes/models.ts +0 -16
  148. package/agent-pane/backend/src/routes/plugins.ts +0 -40
  149. package/agent-pane/backend/src/routes/providers.test.ts +0 -86
  150. package/agent-pane/backend/src/routes/providers.ts +0 -86
  151. package/agent-pane/backend/src/routes/sessions.test.ts +0 -558
  152. package/agent-pane/backend/src/routes/sessions.ts +0 -504
  153. package/agent-pane/backend/src/routes/skills.test.ts +0 -114
  154. package/agent-pane/backend/src/routes/skills.ts +0 -70
  155. package/agent-pane/backend/src/routes/threads.ts +0 -323
  156. package/agent-pane/backend/src/routes/turns.test.ts +0 -196
  157. package/agent-pane/backend/src/routes/turns.ts +0 -103
  158. package/agent-pane/backend/src/routes/workspaces.test.ts +0 -197
  159. package/agent-pane/backend/src/routes/workspaces.ts +0 -128
  160. package/agent-pane/backend/src/ws/index.test.ts +0 -107
  161. package/agent-pane/backend/src/ws/index.ts +0 -51
  162. package/agent-pane/backend/tsconfig.json +0 -25
  163. package/agent-pane/backend/vitest.config.ts +0 -20
  164. package/agent-pane/eslint.config.js +0 -33
  165. package/agent-pane/frontend/coverage/base.css +0 -224
  166. package/agent-pane/frontend/coverage/block-navigation.js +0 -87
  167. package/agent-pane/frontend/coverage/clover.xml +0 -2962
  168. package/agent-pane/frontend/coverage/coverage-final.json +0 -37
  169. package/agent-pane/frontend/coverage/favicon.png +0 -0
  170. package/agent-pane/frontend/coverage/index.html +0 -251
  171. package/agent-pane/frontend/coverage/prettify.css +0 -1
  172. package/agent-pane/frontend/coverage/prettify.js +0 -2
  173. package/agent-pane/frontend/coverage/sort-arrow-sprite.png +0 -0
  174. package/agent-pane/frontend/coverage/sorter.js +0 -210
  175. package/agent-pane/frontend/coverage/src/App.tsx.html +0 -217
  176. package/agent-pane/frontend/coverage/src/AppInitializer.tsx.html +0 -223
  177. package/agent-pane/frontend/coverage/src/api/client.ts.html +0 -187
  178. package/agent-pane/frontend/coverage/src/api/index.html +0 -161
  179. package/agent-pane/frontend/coverage/src/api/sessions.ts.html +0 -316
  180. package/agent-pane/frontend/coverage/src/api/websocket.ts.html +0 -544
  181. package/agent-pane/frontend/coverage/src/api/workspaces.ts.html +0 -199
  182. package/agent-pane/frontend/coverage/src/components/CollapsibleToolCalls.tsx.html +0 -226
  183. package/agent-pane/frontend/coverage/src/components/CommandInputBar.tsx.html +0 -382
  184. package/agent-pane/frontend/coverage/src/components/CommandPalette.tsx.html +0 -715
  185. package/agent-pane/frontend/coverage/src/components/IdeWorkspace.tsx.html +0 -160
  186. package/agent-pane/frontend/coverage/src/components/ModelSelector.tsx.html +0 -478
  187. package/agent-pane/frontend/coverage/src/components/NewSessionDialog.tsx.html +0 -472
  188. package/agent-pane/frontend/coverage/src/components/RenderArea.tsx.html +0 -574
  189. package/agent-pane/frontend/coverage/src/components/RightPanel.tsx.html +0 -205
  190. package/agent-pane/frontend/coverage/src/components/RuntimeIcon.tsx.html +0 -133
  191. package/agent-pane/frontend/coverage/src/components/SessionTabs.tsx.html +0 -217
  192. package/agent-pane/frontend/coverage/src/components/StatusBar.tsx.html +0 -271
  193. package/agent-pane/frontend/coverage/src/components/StatusDot.tsx.html +0 -109
  194. package/agent-pane/frontend/coverage/src/components/Toast.tsx.html +0 -313
  195. package/agent-pane/frontend/coverage/src/components/TurnContent.tsx.html +0 -1216
  196. package/agent-pane/frontend/coverage/src/components/WorkspaceSidebar.tsx.html +0 -1138
  197. package/agent-pane/frontend/coverage/src/components/index.html +0 -326
  198. package/agent-pane/frontend/coverage/src/components/panels/ChangesPanel.tsx.html +0 -247
  199. package/agent-pane/frontend/coverage/src/components/panels/LogsPanel.tsx.html +0 -160
  200. package/agent-pane/frontend/coverage/src/components/panels/MetricsPanel.tsx.html +0 -427
  201. package/agent-pane/frontend/coverage/src/components/panels/TurnsPanel.tsx.html +0 -397
  202. package/agent-pane/frontend/coverage/src/components/panels/index.html +0 -161
  203. package/agent-pane/frontend/coverage/src/constants/index.html +0 -116
  204. package/agent-pane/frontend/coverage/src/constants/models.ts.html +0 -406
  205. package/agent-pane/frontend/coverage/src/hooks/index.html +0 -146
  206. package/agent-pane/frontend/coverage/src/hooks/useEscapeKey.ts.html +0 -124
  207. package/agent-pane/frontend/coverage/src/hooks/useOutsideClick.ts.html +0 -142
  208. package/agent-pane/frontend/coverage/src/hooks/useSessionStatus.ts.html +0 -187
  209. package/agent-pane/frontend/coverage/src/index.html +0 -146
  210. package/agent-pane/frontend/coverage/src/main.tsx.html +0 -115
  211. package/agent-pane/frontend/coverage/src/store/index.html +0 -116
  212. package/agent-pane/frontend/coverage/src/store/store.ts.html +0 -2404
  213. package/agent-pane/frontend/coverage/src/test/component-helpers.ts.html +0 -190
  214. package/agent-pane/frontend/coverage/src/test/fixtures.ts.html +0 -367
  215. package/agent-pane/frontend/coverage/src/test/index.html +0 -131
  216. package/agent-pane/frontend/coverage/src/types/index.html +0 -116
  217. package/agent-pane/frontend/coverage/src/types/index.ts.html +0 -466
  218. package/agent-pane/frontend/coverage/src/utils/changes.ts.html +0 -202
  219. package/agent-pane/frontend/coverage/src/utils/duration.ts.html +0 -127
  220. package/agent-pane/frontend/coverage/src/utils/index.html +0 -131
  221. package/agent-pane/frontend/dist/assets/index-34WaZYsW.css +0 -1
  222. package/agent-pane/frontend/dist/assets/index-y7xlT86e.js +0 -362
  223. package/agent-pane/frontend/dist/index.html +0 -19
  224. package/agent-pane/frontend/index.html +0 -18
  225. package/agent-pane/frontend/package-lock.json +0 -4211
  226. package/agent-pane/frontend/package.json +0 -35
  227. package/agent-pane/frontend/src/App.test.tsx +0 -74
  228. package/agent-pane/frontend/src/App.tsx +0 -44
  229. package/agent-pane/frontend/src/AppInitializer.test.tsx +0 -142
  230. package/agent-pane/frontend/src/AppInitializer.tsx +0 -46
  231. package/agent-pane/frontend/src/__tests__/ThreadsPanel.test.tsx +0 -192
  232. package/agent-pane/frontend/src/api/client.test.ts +0 -126
  233. package/agent-pane/frontend/src/api/client.ts +0 -34
  234. package/agent-pane/frontend/src/api/models.ts +0 -7
  235. package/agent-pane/frontend/src/api/providers.ts +0 -17
  236. package/agent-pane/frontend/src/api/sessions.test.ts +0 -245
  237. package/agent-pane/frontend/src/api/sessions.ts +0 -97
  238. package/agent-pane/frontend/src/api/skills.ts +0 -12
  239. package/agent-pane/frontend/src/api/websocket.test.ts +0 -288
  240. package/agent-pane/frontend/src/api/websocket.ts +0 -238
  241. package/agent-pane/frontend/src/api/workspaces.test.ts +0 -112
  242. package/agent-pane/frontend/src/api/workspaces.ts +0 -38
  243. package/agent-pane/frontend/src/components/CollapsibleToolCalls.test.tsx +0 -49
  244. package/agent-pane/frontend/src/components/CollapsibleToolCalls.tsx +0 -56
  245. package/agent-pane/frontend/src/components/CommandInputBar.test.tsx +0 -133
  246. package/agent-pane/frontend/src/components/CommandInputBar.tsx +0 -584
  247. package/agent-pane/frontend/src/components/CommandPalette.test.tsx +0 -226
  248. package/agent-pane/frontend/src/components/CommandPalette.tsx +0 -194
  249. package/agent-pane/frontend/src/components/CommentableText.test.tsx +0 -196
  250. package/agent-pane/frontend/src/components/CommentableText.tsx +0 -379
  251. package/agent-pane/frontend/src/components/IdeWorkspace.test.tsx +0 -39
  252. package/agent-pane/frontend/src/components/IdeWorkspace.tsx +0 -51
  253. package/agent-pane/frontend/src/components/ModelSelector.test.tsx +0 -129
  254. package/agent-pane/frontend/src/components/ModelSelector.tsx +0 -191
  255. package/agent-pane/frontend/src/components/NewSessionDialog.test.tsx +0 -95
  256. package/agent-pane/frontend/src/components/NewSessionDialog.tsx +0 -129
  257. package/agent-pane/frontend/src/components/RenderArea.test.tsx +0 -160
  258. package/agent-pane/frontend/src/components/RenderArea.tsx +0 -170
  259. package/agent-pane/frontend/src/components/ResizeHandle.tsx +0 -29
  260. package/agent-pane/frontend/src/components/RightPanel.test.tsx +0 -43
  261. package/agent-pane/frontend/src/components/RightPanel.tsx +0 -46
  262. package/agent-pane/frontend/src/components/RuntimeIcon.test.tsx +0 -32
  263. package/agent-pane/frontend/src/components/RuntimeIcon.tsx +0 -16
  264. package/agent-pane/frontend/src/components/SessionTabs.test.tsx +0 -72
  265. package/agent-pane/frontend/src/components/SessionTabs.tsx +0 -49
  266. package/agent-pane/frontend/src/components/StatusBar.test.tsx +0 -56
  267. package/agent-pane/frontend/src/components/StatusBar.tsx +0 -129
  268. package/agent-pane/frontend/src/components/StatusDot.test.tsx +0 -25
  269. package/agent-pane/frontend/src/components/StatusDot.tsx +0 -8
  270. package/agent-pane/frontend/src/components/Toast.test.tsx +0 -79
  271. package/agent-pane/frontend/src/components/Toast.tsx +0 -76
  272. package/agent-pane/frontend/src/components/TurnContent.test.tsx +0 -298
  273. package/agent-pane/frontend/src/components/TurnContent.tsx +0 -548
  274. package/agent-pane/frontend/src/components/WorkspaceSidebar.test.tsx +0 -297
  275. package/agent-pane/frontend/src/components/WorkspaceSidebar.tsx +0 -351
  276. package/agent-pane/frontend/src/components/input/OptionSubPopup.tsx +0 -32
  277. package/agent-pane/frontend/src/components/input/SkillMentionPopup.test.tsx +0 -63
  278. package/agent-pane/frontend/src/components/input/SkillMentionPopup.tsx +0 -78
  279. package/agent-pane/frontend/src/components/input/SlashPopup.test.tsx +0 -96
  280. package/agent-pane/frontend/src/components/input/SlashPopup.tsx +0 -82
  281. package/agent-pane/frontend/src/components/panels/ChangesPanel.test.tsx +0 -61
  282. package/agent-pane/frontend/src/components/panels/ChangesPanel.tsx +0 -54
  283. package/agent-pane/frontend/src/components/panels/TasksPanel.test.tsx +0 -97
  284. package/agent-pane/frontend/src/components/panels/TasksPanel.tsx +0 -208
  285. package/agent-pane/frontend/src/components/panels/ThreadsPanel.tsx +0 -382
  286. package/agent-pane/frontend/src/components/panels/TurnsPanel.test.tsx +0 -83
  287. package/agent-pane/frontend/src/components/panels/TurnsPanel.tsx +0 -126
  288. package/agent-pane/frontend/src/constants/models.test.ts +0 -97
  289. package/agent-pane/frontend/src/constants/models.ts +0 -166
  290. package/agent-pane/frontend/src/hooks/useEscapeKey.test.ts +0 -42
  291. package/agent-pane/frontend/src/hooks/useEscapeKey.ts +0 -13
  292. package/agent-pane/frontend/src/hooks/useOutsideClick.test.ts +0 -92
  293. package/agent-pane/frontend/src/hooks/useOutsideClick.ts +0 -19
  294. package/agent-pane/frontend/src/hooks/useProvider.ts +0 -44
  295. package/agent-pane/frontend/src/hooks/useResizable.test.ts +0 -97
  296. package/agent-pane/frontend/src/hooks/useResizable.ts +0 -75
  297. package/agent-pane/frontend/src/hooks/useSessionStatus.test.ts +0 -116
  298. package/agent-pane/frontend/src/hooks/useSessionStatus.ts +0 -34
  299. package/agent-pane/frontend/src/main.test.tsx +0 -37
  300. package/agent-pane/frontend/src/main.tsx +0 -10
  301. package/agent-pane/frontend/src/store/sessionSlice.ts +0 -440
  302. package/agent-pane/frontend/src/store/store.test.ts +0 -1319
  303. package/agent-pane/frontend/src/store/store.ts +0 -37
  304. package/agent-pane/frontend/src/store/threadSlice.ts +0 -136
  305. package/agent-pane/frontend/src/store/uiSlice.ts +0 -133
  306. package/agent-pane/frontend/src/store/workspaceSlice.ts +0 -77
  307. package/agent-pane/frontend/src/store/wsSlice.ts +0 -361
  308. package/agent-pane/frontend/src/styles/app.css +0 -611
  309. package/agent-pane/frontend/src/styles/tokens.css +0 -62
  310. package/agent-pane/frontend/src/test/component-helpers.ts +0 -38
  311. package/agent-pane/frontend/src/test/fixtures.ts +0 -100
  312. package/agent-pane/frontend/src/test-setup.ts +0 -5
  313. package/agent-pane/frontend/src/types/index.test.ts +0 -128
  314. package/agent-pane/frontend/src/types/index.ts +0 -132
  315. package/agent-pane/frontend/src/utils/changes.test.ts +0 -81
  316. package/agent-pane/frontend/src/utils/changes.ts +0 -39
  317. package/agent-pane/frontend/src/utils/duration.test.ts +0 -75
  318. package/agent-pane/frontend/src/utils/duration.ts +0 -14
  319. package/agent-pane/frontend/tsconfig.json +0 -25
  320. package/agent-pane/frontend/vite.config.ts +0 -22
  321. package/agent-pane/frontend/vitest.config.ts +0 -29
  322. package/agent-pane/package-lock.json +0 -1527
  323. package/agent-pane/package.json +0 -19
  324. package/agent-pane/shared/provider-types.ts +0 -67
  325. package/agent-pane/shared/types.ts +0 -167
@@ -0,0 +1,128 @@
1
+ "use strict";
2
+ /**
3
+ * Agent-pane embed — loads the agent-pane backend bundle in-process so the
4
+ * daemon serves it behind its own port (no second listener).
5
+ *
6
+ * The bundle (agent-pane/backend/dist/server.js, ESM, built by esbuild) is
7
+ * loaded with a dynamic import; its `createAgentPaneApp` factory builds the
8
+ * Fastify app with an explicit DB path. The daemon delegates `/api/*` requests
9
+ * to `fastify.routing()` and routes `/ws` upgrades to the app's WebSocket
10
+ * server. Env kill-switch: REPOBURG_EMBED_AGENT_PANE=0 disables the embed.
11
+ */
12
+ var __importDefault = (this && this.__importDefault) || function (mod) {
13
+ return (mod && mod.__esModule) ? mod : { "default": mod };
14
+ };
15
+ Object.defineProperty(exports, "__esModule", { value: true });
16
+ exports.resolveAgentPaneDist = resolveAgentPaneDist;
17
+ exports.agentPaneDbDir = agentPaneDbDir;
18
+ exports.migrateLegacyDb = migrateLegacyDb;
19
+ exports.loadAgentPane = loadAgentPane;
20
+ exports.createPaneDelegate = createPaneDelegate;
21
+ exports.createUpgradeRouter = createUpgradeRouter;
22
+ const fs_extra_1 = __importDefault(require("fs-extra"));
23
+ const path_1 = __importDefault(require("path"));
24
+ const os_1 = __importDefault(require("os"));
25
+ const url_1 = require("url");
26
+ /**
27
+ * Resolve the agent-pane bundle path ABSOLUTELY (pm2 God-daemon-cwd bug class:
28
+ * never resolve runtime paths against the process cwd).
29
+ *
30
+ * Works for dev (daemon/src or daemon/dist under the repo) and for the
31
+ * published layout (repoburg/daemon/dist -> repoburg/agent-pane/backend/dist).
32
+ */
33
+ function resolveAgentPaneDist(fromDir) {
34
+ return path_1.default.resolve(fromDir, '../../agent-pane/backend/dist/server.js');
35
+ }
36
+ /** Directory holding the embedded app's DB (alongside services.json). */
37
+ function agentPaneDbDir(home = os_1.default.homedir()) {
38
+ return path_1.default.join(home, '.repoburg', 'agent-pane');
39
+ }
40
+ /**
41
+ * One-time migration: copy the legacy dev DB into the embedded location.
42
+ *
43
+ * Copies the DB file plus any WAL/SHM sidecars. No-op when the source is
44
+ * missing (published installs) or the destination already exists.
45
+ *
46
+ * @returns true when a copy was performed.
47
+ */
48
+ function migrateLegacyDb(sourceDbPath, destDbPath) {
49
+ if (!fs_extra_1.default.existsSync(sourceDbPath) || fs_extra_1.default.existsSync(destDbPath)) {
50
+ return false;
51
+ }
52
+ fs_extra_1.default.ensureDirSync(path_1.default.dirname(destDbPath));
53
+ const sidecars = ['', '-wal', '-shm'];
54
+ for (const suffix of sidecars) {
55
+ const src = `${sourceDbPath}${suffix}`;
56
+ if (fs_extra_1.default.existsSync(src)) {
57
+ fs_extra_1.default.copyFileSync(src, `${destDbPath}${suffix}`);
58
+ }
59
+ }
60
+ console.log(`[agentPane] migrated legacy DB ${sourceDbPath} -> ${destDbPath}`);
61
+ return true;
62
+ }
63
+ /** Load and initialise the embedded agent-pane app. Returns null when disabled/absent. */
64
+ async function loadAgentPane(opts = {}) {
65
+ if ((opts.envDisable ?? process.env.REPOBURG_EMBED_AGENT_PANE) === '0') {
66
+ console.log('[agentPane] embed disabled via REPOBURG_EMBED_AGENT_PANE=0');
67
+ return null;
68
+ }
69
+ const distPath = opts.distPath ?? resolveAgentPaneDist(__dirname);
70
+ if (!fs_extra_1.default.existsSync(distPath)) {
71
+ console.warn(`[agentPane] bundle not found at ${distPath} — /api/* responds 503`);
72
+ return null;
73
+ }
74
+ const dbPath = opts.dbPath ?? path_1.default.join(agentPaneDbDir(), 'data.db');
75
+ fs_extra_1.default.ensureDirSync(path_1.default.dirname(dbPath));
76
+ migrateLegacyDb(opts.legacyDbPath ?? resolveAgentPaneDist(__dirname).replace('dist/server.js', 'data.db'), dbPath);
77
+ // Dynamic import: the bundle is ESM; the daemon itself is CommonJS.
78
+ // new Function keeps the RUNTIME import even under ts-jest, which would
79
+ // otherwise transpile import() into require() (ESM-incompatible).
80
+ const dynamicImport = new Function('specifier', 'return import(specifier);');
81
+ const mod = await dynamicImport((0, url_1.pathToFileURL)(distPath).href);
82
+ const { fastify, wss } = await mod.createAgentPaneApp(dbPath);
83
+ await fastify.ready();
84
+ console.log(`[agentPane] embedded app ready (db: ${dbPath})`);
85
+ return {
86
+ routing: fastify.routing.bind(fastify),
87
+ handleWsUpgrade: (req, socket, head) => {
88
+ wss.handleUpgrade(req, socket, head, (ws) => {
89
+ wss.emit('connection', ws, req);
90
+ });
91
+ },
92
+ shutdown: mod.shutdownAllProviders,
93
+ close: () => fastify.close(),
94
+ };
95
+ }
96
+ /**
97
+ * Express middleware delegating `/api/*` to the embedded app.
98
+ *
99
+ * Mount BEFORE `express.json()` — Fastify parses its own request bodies, and
100
+ * the stream can only be consumed once.
101
+ */
102
+ function createPaneDelegate(pane) {
103
+ return (req, res) => {
104
+ if (!pane) {
105
+ res.status(503).json({ error: 'agent-pane is not available on this daemon' });
106
+ return;
107
+ }
108
+ pane.routing(req, res);
109
+ };
110
+ }
111
+ /**
112
+ * Single upgrade router for the daemon's HTTP server.
113
+ *
114
+ * `/ws` goes to the embedded agent-pane app (when present); everything else —
115
+ * e.g. the platform frontend's auth WebSocket — goes to the daemon wss.
116
+ */
117
+ function createUpgradeRouter(pane, daemonWss) {
118
+ return (req, socket, head) => {
119
+ const url = req.url || '';
120
+ if (pane && url.startsWith('/ws')) {
121
+ pane.handleWsUpgrade(req, socket, head);
122
+ return;
123
+ }
124
+ daemonWss.handleUpgrade(req, socket, head, (ws) => {
125
+ daemonWss.emit('connection', ws, req);
126
+ });
127
+ };
128
+ }
@@ -0,0 +1,183 @@
1
+ "use strict";
2
+ /**
3
+ * Specs for the agent-pane embed: path resolution, legacy DB migration,
4
+ * delegation middleware, upgrade routing, and the loader (stub + real bundle).
5
+ */
6
+ var __importDefault = (this && this.__importDefault) || function (mod) {
7
+ return (mod && mod.__esModule) ? mod : { "default": mod };
8
+ };
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ const fs_extra_1 = __importDefault(require("fs-extra"));
11
+ const os_1 = __importDefault(require("os"));
12
+ const path_1 = __importDefault(require("path"));
13
+ const agentPane_1 = require("./agentPane");
14
+ // Stub bundle contents: an ESM module satisfying the embed contract.
15
+ const STUB_DIST = `
16
+ export async function createAgentPaneApp(dbPath) {
17
+ globalThis.__stubDbPath = dbPath;
18
+ return {
19
+ fastify: {
20
+ ready: async () => {},
21
+ routing: (req, res) => { res.end('pane:' + req.url); },
22
+ close: async () => {},
23
+ },
24
+ wss: {
25
+ handleUpgrade: (req, socket, head, cb) => { cb({ stub: true }); },
26
+ emit: () => {},
27
+ },
28
+ };
29
+ }
30
+ export async function shutdownAllProviders() { globalThis.__stubShutdown = true; }
31
+ `;
32
+ function writeStubDist(dir) {
33
+ fs_extra_1.default.ensureDirSync(dir);
34
+ const distPath = path_1.default.join(dir, 'server.js');
35
+ fs_extra_1.default.writeFileSync(distPath, STUB_DIST);
36
+ return distPath;
37
+ }
38
+ function tmpDir(name) {
39
+ return fs_extra_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), `agentpane-${name}-`));
40
+ }
41
+ function makeLegacyDb(dir, content = 'legacy-data') {
42
+ const legacyDb = path_1.default.join(dir, 'legacy', 'data.db');
43
+ fs_extra_1.default.ensureDirSync(path_1.default.dirname(legacyDb));
44
+ fs_extra_1.default.writeFileSync(legacyDb, content);
45
+ return legacyDb;
46
+ }
47
+ describe('resolveAgentPaneDist', () => {
48
+ it('resolves an absolute path under the repo/package layout from either src or dist', () => {
49
+ const fromSrc = (0, agentPane_1.resolveAgentPaneDist)('/repo/daemon/src');
50
+ const fromDist = (0, agentPane_1.resolveAgentPaneDist)('/repo/daemon/dist');
51
+ expect(fromSrc).toBe('/repo/agent-pane/backend/dist/server.js');
52
+ expect(fromDist).toBe('/repo/agent-pane/backend/dist/server.js');
53
+ expect(path_1.default.isAbsolute(fromSrc)).toBe(true);
54
+ });
55
+ });
56
+ describe('agentPaneDbDir', () => {
57
+ it('lives under ~/.repoburg/agent-pane', () => {
58
+ expect((0, agentPane_1.agentPaneDbDir)('/home/tester')).toBe(path_1.default.join('/home/tester', '.repoburg', 'agent-pane'));
59
+ });
60
+ });
61
+ describe('migrateLegacyDb', () => {
62
+ it('copies the DB plus WAL/SHM sidecars when the destination is missing', () => {
63
+ const dir = tmpDir('migrate');
64
+ const src = path_1.default.join(dir, 'data.db');
65
+ fs_extra_1.default.writeFileSync(src, 'base');
66
+ fs_extra_1.default.writeFileSync(`${src}-wal`, 'wal');
67
+ const dest = path_1.default.join(dir, 'dest', 'data.db');
68
+ expect((0, agentPane_1.migrateLegacyDb)(src, dest)).toBe(true);
69
+ expect(fs_extra_1.default.readFileSync(dest, 'utf-8')).toBe('base');
70
+ expect(fs_extra_1.default.readFileSync(`${dest}-wal`, 'utf-8')).toBe('wal');
71
+ });
72
+ it('is a no-op when the destination already exists', () => {
73
+ const dir = tmpDir('migrate-dest-exists');
74
+ const src = path_1.default.join(dir, 'data.db');
75
+ const dest = path_1.default.join(dir, 'out', 'data.db');
76
+ fs_extra_1.default.writeFileSync(src, 'base');
77
+ fs_extra_1.default.ensureDirSync(path_1.default.dirname(dest));
78
+ fs_extra_1.default.writeFileSync(dest, 'existing');
79
+ expect((0, agentPane_1.migrateLegacyDb)(src, dest)).toBe(false);
80
+ expect(fs_extra_1.default.readFileSync(dest, 'utf-8')).toBe('existing');
81
+ });
82
+ it('is a no-op when the source is missing', () => {
83
+ const dir = tmpDir('migrate-no-source');
84
+ expect((0, agentPane_1.migrateLegacyDb)(path_1.default.join(dir, 'absent.db'), path_1.default.join(dir, 'out', 'data.db'))).toBe(false);
85
+ });
86
+ });
87
+ describe('createPaneDelegate', () => {
88
+ it('responds 503 when the pane is not loaded', () => {
89
+ const res = { status: jest.fn().mockReturnThis(), json: jest.fn() };
90
+ (0, agentPane_1.createPaneDelegate)(null)({}, res);
91
+ expect(res.status).toHaveBeenCalledWith(503);
92
+ expect(res.json).toHaveBeenCalledWith({ error: 'agent-pane is not available on this daemon' });
93
+ });
94
+ it('routes raw request/response into the embedded fastify router', () => {
95
+ const routing = jest.fn();
96
+ const pane = {
97
+ routing,
98
+ handleWsUpgrade: jest.fn(),
99
+ shutdown: jest.fn(),
100
+ close: jest.fn(),
101
+ };
102
+ const req = { url: '/api/sessions' };
103
+ const res = {};
104
+ (0, agentPane_1.createPaneDelegate)(pane)(req, res);
105
+ expect(routing).toHaveBeenCalledWith(req, res);
106
+ });
107
+ });
108
+ describe('createUpgradeRouter', () => {
109
+ it('sends /ws upgrades to the embedded pane', () => {
110
+ const pane = {
111
+ routing: jest.fn(),
112
+ handleWsUpgrade: jest.fn(),
113
+ shutdown: jest.fn(),
114
+ close: jest.fn(),
115
+ };
116
+ const daemonWss = { handleUpgrade: jest.fn(), emit: jest.fn() };
117
+ const route = (0, agentPane_1.createUpgradeRouter)(pane, daemonWss);
118
+ route({ url: '/ws' }, {}, Buffer.alloc(0));
119
+ expect(pane.handleWsUpgrade).toHaveBeenCalled();
120
+ expect(daemonWss.handleUpgrade).not.toHaveBeenCalled();
121
+ });
122
+ it('sends non-/ws upgrades to the daemon wss and emits connection', () => {
123
+ const pane = { handleWsUpgrade: jest.fn() };
124
+ const daemonWss = {
125
+ handleUpgrade: jest.fn((_req, _socket, _head, cb) => cb({ daemon: true })),
126
+ emit: jest.fn(),
127
+ };
128
+ const route = (0, agentPane_1.createUpgradeRouter)(pane, daemonWss);
129
+ route({ url: '/' }, {}, Buffer.alloc(0));
130
+ expect(pane.handleWsUpgrade).not.toHaveBeenCalled();
131
+ expect(daemonWss.handleUpgrade).toHaveBeenCalled();
132
+ expect(daemonWss.emit).toHaveBeenCalledWith('connection', { daemon: true }, { url: '/' });
133
+ });
134
+ it('falls back to the daemon wss for /ws when the pane is absent', () => {
135
+ const daemonWss = {
136
+ handleUpgrade: jest.fn((_req, _socket, _head, cb) => cb({})),
137
+ emit: jest.fn(),
138
+ };
139
+ const route = (0, agentPane_1.createUpgradeRouter)(null, daemonWss);
140
+ route({ url: '/ws' }, {}, Buffer.alloc(0));
141
+ expect(daemonWss.handleUpgrade).toHaveBeenCalled();
142
+ });
143
+ });
144
+ describe('loadAgentPane', () => {
145
+ it('returns null when disabled via the kill-switch', async () => {
146
+ expect(await (0, agentPane_1.loadAgentPane)({ envDisable: '0', distPath: 'unused' })).toBeNull();
147
+ });
148
+ it('returns null when the bundle is missing', async () => {
149
+ const dir = tmpDir('no-dist');
150
+ expect(await (0, agentPane_1.loadAgentPane)({ distPath: path_1.default.join(dir, 'absent.js') })).toBeNull();
151
+ });
152
+ it('loads the stub bundle, wires routing/shutdown, and migrates the legacy DB', async () => {
153
+ const dir = tmpDir('full');
154
+ const distPath = writeStubDist(path_1.default.join(dir, 'dist'));
155
+ const dbPath = path_1.default.join(dir, 'home', 'data.db');
156
+ const legacyDb = makeLegacyDb(dir);
157
+ const pane = await (0, agentPane_1.loadAgentPane)({ distPath, dbPath, legacyDbPath: legacyDb });
158
+ expect(pane).not.toBeNull();
159
+ // DB dir created + legacy DB migrated in.
160
+ expect(fs_extra_1.default.readFileSync(dbPath, 'utf-8')).toBe('legacy-data');
161
+ // routing delegates to the stub fastify.
162
+ const chunks = [];
163
+ const res = { end: (s) => chunks.push(s) };
164
+ pane.routing({ url: '/api/sessions' }, res);
165
+ expect(chunks[0]).toBe('pane:/api/sessions');
166
+ await pane.shutdown();
167
+ await pane.close();
168
+ });
169
+ it('does not copy a legacy DB over an existing embedded DB', async () => {
170
+ const dir = tmpDir('no-overwrite');
171
+ const distPath = writeStubDist(path_1.default.join(dir, 'dist'));
172
+ const dbPath = path_1.default.join(dir, 'home', 'data.db');
173
+ const legacyDb = makeLegacyDb(dir);
174
+ fs_extra_1.default.ensureDirSync(path_1.default.dirname(dbPath));
175
+ fs_extra_1.default.writeFileSync(dbPath, 'embedded-data');
176
+ await (0, agentPane_1.loadAgentPane)({ distPath, dbPath, legacyDbPath: legacyDb });
177
+ expect(fs_extra_1.default.readFileSync(dbPath, 'utf-8')).toBe('embedded-data');
178
+ });
179
+ // NOTE: the real bundle is NOT loaded here — jest's vm module loader
180
+ // cannot provide the CJS interop the bundle's native deps need
181
+ // ("exports is not defined"). Real-bundle boot is validated by the live
182
+ // daemon E2E (Phase 5 of the merge plan).
183
+ });
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const express_1 = require("express");
7
7
  const serviceManager_1 = require("../serviceManager");
8
8
  const checkAuth_1 = require("../middleware/checkAuth");
9
+ const workspace_validation_1 = require("../workspace-validation");
9
10
  const fs_extra_1 = __importDefault(require("fs-extra"));
10
11
  const router = (0, express_1.Router)();
11
12
  router.get('/', (req, res) => {
@@ -28,6 +29,9 @@ router.post('/start', checkAuth_1.checkAuth, async (req, res) => {
28
29
  res.status(201).json(service);
29
30
  }
30
31
  catch (error) {
32
+ if (error instanceof workspace_validation_1.WorkspaceValidationError) {
33
+ return res.status(400).json({ error: error.message });
34
+ }
31
35
  const message = error instanceof Error ? error.message : 'An unknown error occurred';
32
36
  res.status(500).json({ error: message });
33
37
  }
@@ -13,17 +13,28 @@ const services_1 = __importDefault(require("./api/services"));
13
13
  const auth_1 = __importDefault(require("./api/auth"));
14
14
  const registry_1 = __importDefault(require("./api/registry"));
15
15
  const mcp_gateway_1 = require("./mcp-gateway");
16
+ const agentPane_1 = require("./agentPane");
16
17
  const PORT = 9998;
17
18
  async function main() {
18
19
  await serviceManager_1.serviceManager.connect();
20
+ // Embedded agent-pane backend — served on THIS port (no second listener).
21
+ // Failure is non-fatal: the daemon keeps managing services, /api/* -> 503.
22
+ const pane = await (0, agentPane_1.loadAgentPane)().catch((err) => {
23
+ console.warn('[agentPane] embed failed:', err);
24
+ return null;
25
+ });
19
26
  const app = (0, express_1.default)();
20
27
  const server = http_1.default.createServer(app);
21
- const wss = new ws_1.WebSocketServer({ server });
28
+ // Daemon wss in noServer mode — the shared upgrade router dispatches below.
29
+ const wss = new ws_1.WebSocketServer({ noServer: true });
22
30
  app.use((0, cors_1.default)({
23
31
  origin: '*', // Allow all
24
32
  methods: 'GET,HEAD,PUT,PATCH,POST,DELETE',
25
33
  credentials: true,
26
34
  }));
35
+ // Embedded agent-pane API. MUST precede express.json() — Fastify parses its
36
+ // own request bodies; the stream can only be consumed once.
37
+ app.all('/api/*', (0, agentPane_1.createPaneDelegate)(pane));
27
38
  app.use(express_1.default.json());
28
39
  // API Routers
29
40
  app.use('/services', services_1.default);
@@ -51,19 +62,28 @@ async function main() {
51
62
  ws.on('close', () => console.log('Client disconnected from daemon WebSocket'));
52
63
  ws.on('error', (error) => console.error('WebSocket error:', error));
53
64
  });
65
+ // Single upgrade router: '/ws' -> embedded agent-pane, everything else -> daemon wss.
66
+ server.on('upgrade', (0, agentPane_1.createUpgradeRouter)(pane, wss));
54
67
  server.listen(PORT, () => {
55
68
  console.log(`Repoburg Platform Daemon listening on http://localhost:${PORT}`);
56
69
  console.log(`MCP Gateway available at http://localhost:${PORT}/mcp`);
57
70
  });
58
- // Clean up MCP gateway sessions on shutdown.
59
- process.on('SIGINT', async () => {
60
- await mcp_gateway_1.mcpGateway.closeAllSessions();
61
- process.exit(0);
62
- });
63
- process.on('SIGTERM', async () => {
71
+ // Clean up MCP gateway sessions and the embedded agent-pane on shutdown.
72
+ const shutdown = async () => {
73
+ if (pane) {
74
+ try {
75
+ await pane.shutdown();
76
+ await pane.close();
77
+ }
78
+ catch (err) {
79
+ console.error('[agentPane] shutdown failed:', err);
80
+ }
81
+ }
64
82
  await mcp_gateway_1.mcpGateway.closeAllSessions();
65
83
  process.exit(0);
66
- });
84
+ };
85
+ process.on('SIGINT', shutdown);
86
+ process.on('SIGTERM', shutdown);
67
87
  }
68
88
  main().catch(err => {
69
89
  console.error("Failed to start daemon:", err);
@@ -37,10 +37,32 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
37
37
  };
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
39
  exports.serviceManager = void 0;
40
+ exports.backendScriptPath = backendScriptPath;
41
+ exports.describeError = describeError;
40
42
  const pm2_1 = __importDefault(require("pm2"));
41
43
  const path_1 = __importDefault(require("path"));
42
44
  const os_1 = __importDefault(require("os"));
43
45
  const stateManager_1 = require("./stateManager");
46
+ const workspace_validation_1 = require("./workspace-validation");
47
+ /**
48
+ * Resolve the backend entry script. Production resolves ABSOLUTELY from the
49
+ * package layout: pm2 resolves bare script names against ITS daemon cwd,
50
+ * which breaks `repoburg-backend` whenever the PM2 God daemon runs from an
51
+ * unrelated directory.
52
+ */
53
+ function backendScriptPath(fromDir, isDevelopment) {
54
+ return isDevelopment
55
+ ? path_1.default.resolve(fromDir, '../../backend/dist/main.js')
56
+ : path_1.default.resolve(fromDir, '../../backend/dist/src/main.js');
57
+ }
58
+ /** Human message for unknown errors (pm2 throws arrays of Errors). */
59
+ function describeError(error) {
60
+ if (error instanceof Error)
61
+ return error.message;
62
+ if (Array.isArray(error))
63
+ return error.map(describeError).join('; ');
64
+ return String(error);
65
+ }
44
66
  const get_port_1 = __importStar(require("./utils/get-port"));
45
67
  class ServiceManager {
46
68
  constructor() {
@@ -137,27 +159,25 @@ class ServiceManager {
137
159
  };
138
160
  }
139
161
  async start(projectPath, customName) {
140
- const absolutePath = path_1.default.resolve(projectPath);
141
- console.log(`Starting service absolutePath ${projectPath}`);
142
- const name = customName || path_1.default.basename(absolutePath);
162
+ const name = customName || path_1.default.basename(path_1.default.resolve(projectPath));
163
+ // Validate BEFORE the same-name replacement below: the service being
164
+ // replaced must not collide with itself, but every other service counts.
165
+ const others = stateManager_1.stateManager.getServices().filter((s) => s.name !== name);
166
+ const absolutePath = (0, workspace_validation_1.validateWorkspacePath)(projectPath, others.map((s) => ({ name: s.name, path: s.path })));
167
+ console.log(`Starting service for workspace ${absolutePath}`);
143
168
  if (stateManager_1.stateManager.getService(name)) {
144
169
  console.log(`Service '${name}' already exists. Removing it before starting a new one.`);
145
170
  await this.remove(name);
146
171
  }
147
172
  const port = await (0, get_port_1.default)({ port: (0, get_port_1.portNumbers)(3400, 3999) });
148
173
  const isDevelopment = process.env.NODE_ENV === 'development';
149
- let script;
150
174
  let interpreter = undefined;
175
+ const script = backendScriptPath(__dirname, isDevelopment);
151
176
  if (isDevelopment) {
152
177
  interpreter = 'node';
153
- // In development, resolve relative to __dirname of this file
154
- script = path_1.default.resolve(__dirname, '../../backend/dist/main.js');
155
178
  console.log(`[ServiceManager] Development mode detected (NODE_ENV=${process.env.NODE_ENV}).`);
156
179
  }
157
180
  else {
158
- // In production, we use the command exposed by the npm package's bin field.
159
- // pm2 will resolve this from the system's PATH.
160
- script = 'repoburg-backend';
161
181
  console.log(`[ServiceManager] Production mode detected (NODE_ENV=${process.env.NODE_ENV}).`);
162
182
  }
163
183
  console.log(`[ServiceManager] Starting backend service using script: ${script}`);
@@ -190,9 +210,8 @@ class ServiceManager {
190
210
  return newService;
191
211
  }
192
212
  catch (error) {
193
- const message = error instanceof Error ? error.message : 'An unknown error occurred';
194
213
  console.error(`Failed to start service for ${projectPath}:`, error);
195
- throw new Error(`Failed to start service: ${message}`);
214
+ throw new Error(`Failed to start service: ${describeError(error)}`);
196
215
  }
197
216
  }
198
217
  async stop(name) {
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const path_1 = __importDefault(require("path"));
7
+ const serviceManager_1 = require("./serviceManager");
8
+ describe('backendScriptPath', () => {
9
+ it('resolves the repo build in development', () => {
10
+ expect((0, serviceManager_1.backendScriptPath)(path_1.default.join('/repo', 'daemon', 'src'), true)).toBe(path_1.default.join('/repo', 'backend', 'dist', 'main.js'));
11
+ });
12
+ it('resolves the package build absolutely in production', () => {
13
+ expect((0, serviceManager_1.backendScriptPath)(path_1.default.join('/pkg', 'repoburg', 'daemon', 'dist'), false)).toBe(path_1.default.join('/pkg', 'repoburg', 'backend', 'dist', 'src', 'main.js'));
14
+ });
15
+ });
16
+ describe('describeError', () => {
17
+ it('returns the message of an Error', () => {
18
+ expect((0, serviceManager_1.describeError)(new Error('boom'))).toBe('boom');
19
+ });
20
+ it('joins pm2 arrays of Errors', () => {
21
+ expect((0, serviceManager_1.describeError)([new Error('a'), new Error('b')])).toBe('a; b');
22
+ });
23
+ it('stringifies non-Error values', () => {
24
+ expect((0, serviceManager_1.describeError)('plain')).toBe('plain');
25
+ });
26
+ });
@@ -0,0 +1,63 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.WorkspaceValidationError = void 0;
4
+ exports.validateWorkspacePath = validateWorkspacePath;
5
+ const fs_1 = require("fs");
6
+ const os_1 = require("os");
7
+ const path_1 = require("path");
8
+ /** Thrown when a workspace path fails start validation (HTTP 400 in the API). */
9
+ class WorkspaceValidationError extends Error {
10
+ constructor(message) {
11
+ super(message);
12
+ this.name = 'WorkspaceValidationError';
13
+ }
14
+ }
15
+ exports.WorkspaceValidationError = WorkspaceValidationError;
16
+ /** true when child is strictly under parent (not equal). */
17
+ function isUnder(child, parent) {
18
+ return child.startsWith(parent + path_1.sep);
19
+ }
20
+ /**
21
+ * Validate a workspace path before a backend is started for it:
22
+ * - must be absolute,
23
+ * - must exist and be a directory,
24
+ * - must not be $HOME or a filesystem root,
25
+ * - must not overlap another service workspace (nested either way).
26
+ * Returns the resolved absolute path; throws WorkspaceValidationError
27
+ * with a clear message otherwise.
28
+ */
29
+ function validateWorkspacePath(projectPath, existingServices) {
30
+ if (!projectPath || !(0, path_1.isAbsolute)(projectPath)) {
31
+ throw new WorkspaceValidationError(`Project path must be absolute (got "${projectPath}").`);
32
+ }
33
+ const resolved = (0, path_1.resolve)(projectPath);
34
+ let stat;
35
+ try {
36
+ stat = (0, fs_1.statSync)(resolved);
37
+ }
38
+ catch {
39
+ throw new WorkspaceValidationError(`Workspace directory does not exist: ${resolved}`);
40
+ }
41
+ if (!stat.isDirectory()) {
42
+ throw new WorkspaceValidationError(`Workspace path is not a directory: ${resolved}`);
43
+ }
44
+ if (resolved === (0, os_1.homedir)()) {
45
+ throw new WorkspaceValidationError(`Refusing to use the home directory as a workspace: ${resolved}`);
46
+ }
47
+ if (resolved === (0, path_1.parse)(resolved).root) {
48
+ throw new WorkspaceValidationError(`Refusing to use a filesystem root as a workspace: ${resolved}`);
49
+ }
50
+ for (const svc of existingServices) {
51
+ const svcPath = (0, path_1.resolve)(svc.path);
52
+ if (svcPath === resolved) {
53
+ throw new WorkspaceValidationError(`Workspace ${resolved} is already served by service "${svc.name}".`);
54
+ }
55
+ if (isUnder(resolved, svcPath)) {
56
+ throw new WorkspaceValidationError(`Workspace ${resolved} is nested inside existing service "${svc.name}" (${svcPath}).`);
57
+ }
58
+ if (isUnder(svcPath, resolved)) {
59
+ throw new WorkspaceValidationError(`Workspace ${resolved} contains existing service "${svc.name}" (${svcPath}).`);
60
+ }
61
+ }
62
+ return resolved;
63
+ }
@@ -0,0 +1,63 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const fs_1 = require("fs");
4
+ const os_1 = require("os");
5
+ const path_1 = require("path");
6
+ const workspace_validation_1 = require("./workspace-validation");
7
+ describe('validateWorkspacePath', () => {
8
+ let dir;
9
+ let otherDir;
10
+ let filePath;
11
+ beforeAll(() => {
12
+ dir = (0, fs_1.mkdtempSync)((0, path_1.join)((0, os_1.tmpdir)(), 'ws-valid-'));
13
+ otherDir = (0, fs_1.mkdtempSync)((0, path_1.join)((0, os_1.tmpdir)(), 'ws-other-'));
14
+ (0, fs_1.mkdirSync)((0, path_1.join)(dir, 'child'));
15
+ filePath = (0, path_1.join)(dir, 'not-a-dir');
16
+ (0, fs_1.writeFileSync)(filePath, 'file');
17
+ });
18
+ afterAll(() => {
19
+ (0, fs_1.rmSync)(dir, { recursive: true, force: true });
20
+ (0, fs_1.rmSync)(otherDir, { recursive: true, force: true });
21
+ });
22
+ it('accepts an existing absolute directory and returns the resolved path', () => {
23
+ const resolved = (0, path_1.resolve)(dir);
24
+ expect((0, workspace_validation_1.validateWorkspacePath)(dir, [])).toBe(resolved);
25
+ });
26
+ it('normalizes redundant path segments', () => {
27
+ const messy = (0, path_1.join)(dir, 'a', '..', 'b', '..');
28
+ expect((0, workspace_validation_1.validateWorkspacePath)(messy, [])).toBe((0, path_1.resolve)(dir));
29
+ });
30
+ it('rejects non-absolute paths', () => {
31
+ expect(() => (0, workspace_validation_1.validateWorkspacePath)('relative/path', [])).toThrow(workspace_validation_1.WorkspaceValidationError);
32
+ expect(() => (0, workspace_validation_1.validateWorkspacePath)('', [])).toThrow(workspace_validation_1.WorkspaceValidationError);
33
+ });
34
+ it('rejects paths that do not exist', () => {
35
+ expect(() => (0, workspace_validation_1.validateWorkspacePath)((0, path_1.join)(dir, 'missing'), [])).toThrow(/Workspace directory does not exist/);
36
+ });
37
+ it('rejects paths that are not directories', () => {
38
+ expect(() => (0, workspace_validation_1.validateWorkspacePath)(filePath, [])).toThrow(/not a directory/);
39
+ });
40
+ it('rejects the home directory', () => {
41
+ expect(() => (0, workspace_validation_1.validateWorkspacePath)((0, os_1.homedir)(), [])).toThrow(/home directory/);
42
+ });
43
+ it('rejects filesystem roots', () => {
44
+ expect(() => (0, workspace_validation_1.validateWorkspacePath)((0, path_1.parse)(dir).root, [])).toThrow(/filesystem root/);
45
+ });
46
+ it('rejects a duplicate of an existing service workspace', () => {
47
+ const existing = [{ name: 'svc', path: otherDir }];
48
+ expect(() => (0, workspace_validation_1.validateWorkspacePath)(otherDir, existing)).toThrow(/already served/);
49
+ });
50
+ it('rejects a workspace nested inside an existing service', () => {
51
+ const existing = [{ name: 'svc', path: dir }];
52
+ expect(() => (0, workspace_validation_1.validateWorkspacePath)((0, path_1.join)(dir, 'child'), [])).not.toThrow();
53
+ expect(() => (0, workspace_validation_1.validateWorkspacePath)((0, path_1.join)(dir, 'child'), existing)).toThrow(/nested inside/);
54
+ });
55
+ it('rejects a workspace that contains an existing service', () => {
56
+ const existing = [{ name: 'svc', path: (0, path_1.join)(dir, 'child') }];
57
+ expect(() => (0, workspace_validation_1.validateWorkspacePath)(dir, existing)).toThrow(/contains existing service/);
58
+ });
59
+ it('accepts a sibling of an existing service', () => {
60
+ const existing = [{ name: 'svc', path: otherDir }];
61
+ expect((0, workspace_validation_1.validateWorkspacePath)(dir, existing)).toBe((0, path_1.resolve)(dir));
62
+ });
63
+ });
@@ -8,7 +8,7 @@
8
8
  "build": "tsc",
9
9
  "start": "cross-env NODE_ENV=production node dist/index.js",
10
10
  "dev": "NODE_ENV=development ts-node-dev --respawn --transpile-only src/index.ts",
11
- "test": "jest"
11
+ "test": "cross-env NODE_OPTIONS=--experimental-vm-modules jest"
12
12
  },
13
13
  "keywords": [],
14
14
  "author": "Celal Ertug",