@aion0/forge 0.5.26 → 0.5.28

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 (255) hide show
  1. package/.forge/worktrees/pipeline-4dd8dc2d/CLAUDE.md +86 -0
  2. package/.forge/worktrees/pipeline-4dd8dc2d/README.md +136 -0
  3. package/.forge/worktrees/pipeline-4dd8dc2d/RELEASE_NOTES.md +36 -0
  4. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/agents/route.ts +17 -0
  5. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/auth/[...nextauth]/route.ts +3 -0
  6. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/auth/verify/route.ts +46 -0
  7. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/claude/[id]/route.ts +31 -0
  8. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/claude/[id]/stream/route.ts +63 -0
  9. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/claude/route.ts +28 -0
  10. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/claude-sessions/[projectName]/entries/route.ts +23 -0
  11. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/claude-sessions/[projectName]/live/route.ts +72 -0
  12. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/claude-sessions/[projectName]/route.ts +37 -0
  13. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/claude-sessions/sync/route.ts +17 -0
  14. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/claude-templates/route.ts +145 -0
  15. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/code/route.ts +299 -0
  16. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/delivery/[id]/route.ts +62 -0
  17. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/delivery/route.ts +40 -0
  18. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/detect-cli/route.ts +46 -0
  19. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/docs/route.ts +176 -0
  20. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/docs/sessions/route.ts +54 -0
  21. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/favorites/route.ts +26 -0
  22. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/flows/route.ts +6 -0
  23. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/flows/run/route.ts +19 -0
  24. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/git/route.ts +149 -0
  25. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/help/route.ts +84 -0
  26. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/issue-scanner/route.ts +116 -0
  27. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/logs/route.ts +100 -0
  28. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/mobile-chat/route.ts +115 -0
  29. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/monitor/route.ts +74 -0
  30. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/notifications/route.ts +42 -0
  31. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/notify/test/route.ts +33 -0
  32. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/online/route.ts +40 -0
  33. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/pipelines/[id]/route.ts +41 -0
  34. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/pipelines/route.ts +90 -0
  35. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/plugins/route.ts +75 -0
  36. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/preview/[...path]/route.ts +64 -0
  37. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/preview/route.ts +156 -0
  38. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/project-pipelines/route.ts +91 -0
  39. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/project-sessions/route.ts +61 -0
  40. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/projects/route.ts +26 -0
  41. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/sessions/[id]/chat/route.ts +64 -0
  42. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/sessions/[id]/messages/route.ts +9 -0
  43. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/sessions/[id]/route.ts +17 -0
  44. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/sessions/route.ts +20 -0
  45. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/settings/route.ts +64 -0
  46. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/skills/local/route.ts +228 -0
  47. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/skills/route.ts +182 -0
  48. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/smith-templates/route.ts +81 -0
  49. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/status/route.ts +12 -0
  50. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/tabs/route.ts +25 -0
  51. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/tasks/[id]/route.ts +51 -0
  52. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/tasks/[id]/stream/route.ts +77 -0
  53. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/tasks/link/route.ts +37 -0
  54. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/tasks/route.ts +44 -0
  55. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/tasks/session/route.ts +14 -0
  56. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/telegram/route.ts +23 -0
  57. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/templates/route.ts +6 -0
  58. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/terminal-bell/route.ts +39 -0
  59. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/terminal-cwd/route.ts +19 -0
  60. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/terminal-state/route.ts +15 -0
  61. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/tunnel/route.ts +26 -0
  62. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/upgrade/route.ts +43 -0
  63. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/usage/route.ts +20 -0
  64. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/version/route.ts +78 -0
  65. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/watchers/route.ts +33 -0
  66. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/workspace/[id]/agents/route.ts +35 -0
  67. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/workspace/[id]/memory/route.ts +23 -0
  68. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/workspace/[id]/smith/route.ts +22 -0
  69. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/workspace/[id]/stream/route.ts +31 -0
  70. package/.forge/worktrees/pipeline-4dd8dc2d/app/api/workspace/route.ts +79 -0
  71. package/.forge/worktrees/pipeline-4dd8dc2d/app/global-error.tsx +21 -0
  72. package/.forge/worktrees/pipeline-4dd8dc2d/app/globals.css +52 -0
  73. package/.forge/worktrees/pipeline-4dd8dc2d/app/icon.ico +0 -0
  74. package/.forge/worktrees/pipeline-4dd8dc2d/app/icon.png +0 -0
  75. package/.forge/worktrees/pipeline-4dd8dc2d/app/icon.svg +106 -0
  76. package/.forge/worktrees/pipeline-4dd8dc2d/app/layout.tsx +17 -0
  77. package/.forge/worktrees/pipeline-4dd8dc2d/app/login/LoginForm.tsx +96 -0
  78. package/.forge/worktrees/pipeline-4dd8dc2d/app/login/page.tsx +10 -0
  79. package/.forge/worktrees/pipeline-4dd8dc2d/app/mobile/page.tsx +10 -0
  80. package/.forge/worktrees/pipeline-4dd8dc2d/app/page.tsx +22 -0
  81. package/.forge/worktrees/pipeline-4dd8dc2d/bin/forge-server.mjs +484 -0
  82. package/.forge/worktrees/pipeline-4dd8dc2d/check-forge-status.sh +71 -0
  83. package/.forge/worktrees/pipeline-4dd8dc2d/cli/mw.ts +579 -0
  84. package/.forge/worktrees/pipeline-4dd8dc2d/components/BrowserPanel.tsx +175 -0
  85. package/.forge/worktrees/pipeline-4dd8dc2d/components/ChatPanel.tsx +191 -0
  86. package/.forge/worktrees/pipeline-4dd8dc2d/components/ClaudeTerminal.tsx +267 -0
  87. package/.forge/worktrees/pipeline-4dd8dc2d/components/CodeViewer.tsx +787 -0
  88. package/.forge/worktrees/pipeline-4dd8dc2d/components/ConversationEditor.tsx +411 -0
  89. package/.forge/worktrees/pipeline-4dd8dc2d/components/ConversationGraphView.tsx +347 -0
  90. package/.forge/worktrees/pipeline-4dd8dc2d/components/ConversationTerminalView.tsx +303 -0
  91. package/.forge/worktrees/pipeline-4dd8dc2d/components/Dashboard.tsx +807 -0
  92. package/.forge/worktrees/pipeline-4dd8dc2d/components/DashboardWrapper.tsx +9 -0
  93. package/.forge/worktrees/pipeline-4dd8dc2d/components/DeliveryFlowEditor.tsx +491 -0
  94. package/.forge/worktrees/pipeline-4dd8dc2d/components/DeliveryList.tsx +230 -0
  95. package/.forge/worktrees/pipeline-4dd8dc2d/components/DeliveryWorkspace.tsx +589 -0
  96. package/.forge/worktrees/pipeline-4dd8dc2d/components/DocTerminal.tsx +187 -0
  97. package/.forge/worktrees/pipeline-4dd8dc2d/components/DocsViewer.tsx +574 -0
  98. package/.forge/worktrees/pipeline-4dd8dc2d/components/HelpDialog.tsx +169 -0
  99. package/.forge/worktrees/pipeline-4dd8dc2d/components/HelpTerminal.tsx +141 -0
  100. package/.forge/worktrees/pipeline-4dd8dc2d/components/InlinePipelineView.tsx +111 -0
  101. package/.forge/worktrees/pipeline-4dd8dc2d/components/LogViewer.tsx +194 -0
  102. package/.forge/worktrees/pipeline-4dd8dc2d/components/MarkdownContent.tsx +73 -0
  103. package/.forge/worktrees/pipeline-4dd8dc2d/components/MobileView.tsx +385 -0
  104. package/.forge/worktrees/pipeline-4dd8dc2d/components/MonitorPanel.tsx +122 -0
  105. package/.forge/worktrees/pipeline-4dd8dc2d/components/NewSessionModal.tsx +93 -0
  106. package/.forge/worktrees/pipeline-4dd8dc2d/components/NewTaskModal.tsx +492 -0
  107. package/.forge/worktrees/pipeline-4dd8dc2d/components/PipelineEditor.tsx +570 -0
  108. package/.forge/worktrees/pipeline-4dd8dc2d/components/PipelineView.tsx +1018 -0
  109. package/.forge/worktrees/pipeline-4dd8dc2d/components/PluginsPanel.tsx +472 -0
  110. package/.forge/worktrees/pipeline-4dd8dc2d/components/ProjectDetail.tsx +1618 -0
  111. package/.forge/worktrees/pipeline-4dd8dc2d/components/ProjectList.tsx +108 -0
  112. package/.forge/worktrees/pipeline-4dd8dc2d/components/ProjectManager.tsx +401 -0
  113. package/.forge/worktrees/pipeline-4dd8dc2d/components/SessionList.tsx +74 -0
  114. package/.forge/worktrees/pipeline-4dd8dc2d/components/SessionView.tsx +726 -0
  115. package/.forge/worktrees/pipeline-4dd8dc2d/components/SettingsModal.tsx +1647 -0
  116. package/.forge/worktrees/pipeline-4dd8dc2d/components/SkillsPanel.tsx +969 -0
  117. package/.forge/worktrees/pipeline-4dd8dc2d/components/StatusBar.tsx +99 -0
  118. package/.forge/worktrees/pipeline-4dd8dc2d/components/TabBar.tsx +46 -0
  119. package/.forge/worktrees/pipeline-4dd8dc2d/components/TaskBoard.tsx +113 -0
  120. package/.forge/worktrees/pipeline-4dd8dc2d/components/TaskDetail.tsx +372 -0
  121. package/.forge/worktrees/pipeline-4dd8dc2d/components/TerminalLauncher.tsx +398 -0
  122. package/.forge/worktrees/pipeline-4dd8dc2d/components/TunnelToggle.tsx +206 -0
  123. package/.forge/worktrees/pipeline-4dd8dc2d/components/UsagePanel.tsx +207 -0
  124. package/.forge/worktrees/pipeline-4dd8dc2d/components/WebTerminal.tsx +1743 -0
  125. package/.forge/worktrees/pipeline-4dd8dc2d/components/WorkspaceTree.tsx +221 -0
  126. package/.forge/worktrees/pipeline-4dd8dc2d/components/WorkspaceView.tsx +4048 -0
  127. package/.forge/worktrees/pipeline-4dd8dc2d/dev-test.sh +5 -0
  128. package/.forge/worktrees/pipeline-4dd8dc2d/docs/Forge_Memory_Layer_Design.docx +0 -0
  129. package/.forge/worktrees/pipeline-4dd8dc2d/docs/Forge_Strategy_Research_2026.docx +0 -0
  130. package/.forge/worktrees/pipeline-4dd8dc2d/docs/LOCAL-DEPLOY.md +144 -0
  131. package/.forge/worktrees/pipeline-4dd8dc2d/docs/roadmap-multi-agent-workflow.md +330 -0
  132. package/.forge/worktrees/pipeline-4dd8dc2d/forge-logo.png +0 -0
  133. package/.forge/worktrees/pipeline-4dd8dc2d/forge-logo.svg +106 -0
  134. package/.forge/worktrees/pipeline-4dd8dc2d/hooks/useSidebarResize.ts +52 -0
  135. package/.forge/worktrees/pipeline-4dd8dc2d/install.sh +29 -0
  136. package/.forge/worktrees/pipeline-4dd8dc2d/instrumentation.ts +35 -0
  137. package/.forge/worktrees/pipeline-4dd8dc2d/lib/agents/claude-adapter.ts +104 -0
  138. package/.forge/worktrees/pipeline-4dd8dc2d/lib/agents/generic-adapter.ts +64 -0
  139. package/.forge/worktrees/pipeline-4dd8dc2d/lib/agents/index.ts +245 -0
  140. package/.forge/worktrees/pipeline-4dd8dc2d/lib/agents/types.ts +70 -0
  141. package/.forge/worktrees/pipeline-4dd8dc2d/lib/artifacts.ts +106 -0
  142. package/.forge/worktrees/pipeline-4dd8dc2d/lib/auth.ts +62 -0
  143. package/.forge/worktrees/pipeline-4dd8dc2d/lib/builtin-plugins/docker.yaml +70 -0
  144. package/.forge/worktrees/pipeline-4dd8dc2d/lib/builtin-plugins/http.yaml +66 -0
  145. package/.forge/worktrees/pipeline-4dd8dc2d/lib/builtin-plugins/jenkins.yaml +92 -0
  146. package/.forge/worktrees/pipeline-4dd8dc2d/lib/builtin-plugins/llm-vision.yaml +85 -0
  147. package/.forge/worktrees/pipeline-4dd8dc2d/lib/builtin-plugins/playwright.yaml +111 -0
  148. package/.forge/worktrees/pipeline-4dd8dc2d/lib/builtin-plugins/shell-command.yaml +60 -0
  149. package/.forge/worktrees/pipeline-4dd8dc2d/lib/builtin-plugins/slack.yaml +48 -0
  150. package/.forge/worktrees/pipeline-4dd8dc2d/lib/builtin-plugins/webhook.yaml +56 -0
  151. package/.forge/worktrees/pipeline-4dd8dc2d/lib/claude-process.ts +361 -0
  152. package/.forge/worktrees/pipeline-4dd8dc2d/lib/claude-sessions.ts +266 -0
  153. package/.forge/worktrees/pipeline-4dd8dc2d/lib/claude-templates.ts +227 -0
  154. package/.forge/worktrees/pipeline-4dd8dc2d/lib/cloudflared.ts +424 -0
  155. package/.forge/worktrees/pipeline-4dd8dc2d/lib/crypto.ts +67 -0
  156. package/.forge/worktrees/pipeline-4dd8dc2d/lib/delivery.ts +787 -0
  157. package/.forge/worktrees/pipeline-4dd8dc2d/lib/dirs.ts +99 -0
  158. package/.forge/worktrees/pipeline-4dd8dc2d/lib/flows.ts +86 -0
  159. package/.forge/worktrees/pipeline-4dd8dc2d/lib/forge-mcp-server.ts +732 -0
  160. package/.forge/worktrees/pipeline-4dd8dc2d/lib/forge-skills/forge-inbox.md +38 -0
  161. package/.forge/worktrees/pipeline-4dd8dc2d/lib/forge-skills/forge-send.md +47 -0
  162. package/.forge/worktrees/pipeline-4dd8dc2d/lib/forge-skills/forge-status.md +32 -0
  163. package/.forge/worktrees/pipeline-4dd8dc2d/lib/forge-skills/forge-workspace-sync.md +37 -0
  164. package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/00-overview.md +40 -0
  165. package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/01-settings.md +194 -0
  166. package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/02-telegram.md +41 -0
  167. package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/03-tunnel.md +31 -0
  168. package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/04-tasks.md +52 -0
  169. package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/05-pipelines.md +460 -0
  170. package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/06-skills.md +43 -0
  171. package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/07-projects.md +73 -0
  172. package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/08-rules.md +53 -0
  173. package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/09-issue-autofix.md +55 -0
  174. package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/10-troubleshooting.md +89 -0
  175. package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/11-workspace.md +810 -0
  176. package/.forge/worktrees/pipeline-4dd8dc2d/lib/help-docs/CLAUDE.md +62 -0
  177. package/.forge/worktrees/pipeline-4dd8dc2d/lib/init.ts +266 -0
  178. package/.forge/worktrees/pipeline-4dd8dc2d/lib/issue-scanner.ts +298 -0
  179. package/.forge/worktrees/pipeline-4dd8dc2d/lib/logger.ts +79 -0
  180. package/.forge/worktrees/pipeline-4dd8dc2d/lib/notifications.ts +75 -0
  181. package/.forge/worktrees/pipeline-4dd8dc2d/lib/notify.ts +108 -0
  182. package/.forge/worktrees/pipeline-4dd8dc2d/lib/password.ts +97 -0
  183. package/.forge/worktrees/pipeline-4dd8dc2d/lib/pipeline-scheduler.ts +373 -0
  184. package/.forge/worktrees/pipeline-4dd8dc2d/lib/pipeline.ts +1565 -0
  185. package/.forge/worktrees/pipeline-4dd8dc2d/lib/plugins/executor.ts +347 -0
  186. package/.forge/worktrees/pipeline-4dd8dc2d/lib/plugins/registry.ts +228 -0
  187. package/.forge/worktrees/pipeline-4dd8dc2d/lib/plugins/types.ts +103 -0
  188. package/.forge/worktrees/pipeline-4dd8dc2d/lib/project-sessions.ts +53 -0
  189. package/.forge/worktrees/pipeline-4dd8dc2d/lib/projects.ts +86 -0
  190. package/.forge/worktrees/pipeline-4dd8dc2d/lib/session-manager.ts +156 -0
  191. package/.forge/worktrees/pipeline-4dd8dc2d/lib/session-utils.ts +53 -0
  192. package/.forge/worktrees/pipeline-4dd8dc2d/lib/session-watcher.ts +345 -0
  193. package/.forge/worktrees/pipeline-4dd8dc2d/lib/settings.ts +195 -0
  194. package/.forge/worktrees/pipeline-4dd8dc2d/lib/skills.ts +458 -0
  195. package/.forge/worktrees/pipeline-4dd8dc2d/lib/task-manager.ts +951 -0
  196. package/.forge/worktrees/pipeline-4dd8dc2d/lib/telegram-bot.ts +1477 -0
  197. package/.forge/worktrees/pipeline-4dd8dc2d/lib/telegram-standalone.ts +83 -0
  198. package/.forge/worktrees/pipeline-4dd8dc2d/lib/terminal-server.ts +70 -0
  199. package/.forge/worktrees/pipeline-4dd8dc2d/lib/terminal-standalone.ts +438 -0
  200. package/.forge/worktrees/pipeline-4dd8dc2d/lib/usage-scanner.ts +249 -0
  201. package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/__tests__/state-machine.test.ts +388 -0
  202. package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/__tests__/workspace.test.ts +311 -0
  203. package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/agent-bus.ts +416 -0
  204. package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/agent-worker.ts +655 -0
  205. package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/backends/api-backend.ts +262 -0
  206. package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/backends/cli-backend.ts +491 -0
  207. package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/index.ts +84 -0
  208. package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/manager.ts +136 -0
  209. package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/orchestrator.ts +3415 -0
  210. package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/persistence.ts +309 -0
  211. package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/presets.ts +649 -0
  212. package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/requests.ts +287 -0
  213. package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/session-monitor.ts +240 -0
  214. package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/skill-installer.ts +275 -0
  215. package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/smith-memory.ts +498 -0
  216. package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/types.ts +241 -0
  217. package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace/watch-manager.ts +560 -0
  218. package/.forge/worktrees/pipeline-4dd8dc2d/lib/workspace-standalone.ts +978 -0
  219. package/.forge/worktrees/pipeline-4dd8dc2d/middleware.ts +51 -0
  220. package/.forge/worktrees/pipeline-4dd8dc2d/next.config.ts +26 -0
  221. package/.forge/worktrees/pipeline-4dd8dc2d/package.json +74 -0
  222. package/.forge/worktrees/pipeline-4dd8dc2d/pnpm-lock.yaml +3719 -0
  223. package/.forge/worktrees/pipeline-4dd8dc2d/pnpm-workspace.yaml +1 -0
  224. package/.forge/worktrees/pipeline-4dd8dc2d/postcss.config.mjs +7 -0
  225. package/.forge/worktrees/pipeline-4dd8dc2d/publish.sh +133 -0
  226. package/.forge/worktrees/pipeline-4dd8dc2d/scripts/bench/README.md +66 -0
  227. package/.forge/worktrees/pipeline-4dd8dc2d/scripts/bench/results/.gitignore +2 -0
  228. package/.forge/worktrees/pipeline-4dd8dc2d/scripts/bench/run.ts +635 -0
  229. package/.forge/worktrees/pipeline-4dd8dc2d/scripts/bench/tasks/01-text-utils/task.md +26 -0
  230. package/.forge/worktrees/pipeline-4dd8dc2d/scripts/bench/tasks/01-text-utils/validator.sh +46 -0
  231. package/.forge/worktrees/pipeline-4dd8dc2d/scripts/bench/tasks/02-pagination/setup.sh +19 -0
  232. package/.forge/worktrees/pipeline-4dd8dc2d/scripts/bench/tasks/02-pagination/task.md +48 -0
  233. package/.forge/worktrees/pipeline-4dd8dc2d/scripts/bench/tasks/02-pagination/validator.sh +69 -0
  234. package/.forge/worktrees/pipeline-4dd8dc2d/scripts/bench/tasks/03-bug-fix/setup.sh +82 -0
  235. package/.forge/worktrees/pipeline-4dd8dc2d/scripts/bench/tasks/03-bug-fix/task.md +30 -0
  236. package/.forge/worktrees/pipeline-4dd8dc2d/scripts/bench/tasks/03-bug-fix/validator.sh +29 -0
  237. package/.forge/worktrees/pipeline-4dd8dc2d/scripts/verify-usage.ts +178 -0
  238. package/.forge/worktrees/pipeline-4dd8dc2d/src/config/index.ts +129 -0
  239. package/.forge/worktrees/pipeline-4dd8dc2d/src/core/db/database.ts +259 -0
  240. package/.forge/worktrees/pipeline-4dd8dc2d/src/core/memory/strategy.ts +32 -0
  241. package/.forge/worktrees/pipeline-4dd8dc2d/src/core/providers/chat.ts +65 -0
  242. package/.forge/worktrees/pipeline-4dd8dc2d/src/core/providers/registry.ts +60 -0
  243. package/.forge/worktrees/pipeline-4dd8dc2d/src/core/session/manager.ts +190 -0
  244. package/.forge/worktrees/pipeline-4dd8dc2d/src/types/index.ts +129 -0
  245. package/.forge/worktrees/pipeline-4dd8dc2d/start.sh +32 -0
  246. package/.forge/worktrees/pipeline-4dd8dc2d/templates/smith-lead.json +45 -0
  247. package/.forge/worktrees/pipeline-4dd8dc2d/tsconfig.json +42 -0
  248. package/RELEASE_NOTES.md +10 -29
  249. package/app/api/terminal-bell/route.ts +6 -2
  250. package/app/api/terminal-cwd/route.ts +7 -4
  251. package/components/CodeViewer.tsx +3 -31
  252. package/components/Dashboard.tsx +34 -20
  253. package/components/WebTerminal.tsx +36 -2
  254. package/lib/terminal-standalone.ts +19 -2
  255. package/package.json +1 -1
@@ -0,0 +1,37 @@
1
+ import { NextResponse } from 'next/server';
2
+ import { createTask } from '@/lib/task-manager';
3
+ import { getProjectInfo } from '@/lib/projects';
4
+ import { getDb } from '@/src/core/db/database';
5
+ import { getDbPath } from '@/src/config';
6
+
7
+ /**
8
+ * Link an existing local Claude Code session to a project.
9
+ * Creates a placeholder task with the conversation_id so future tasks
10
+ * for this project automatically continue that session.
11
+ */
12
+ export async function POST(req: Request) {
13
+ const { projectName, conversationId } = await req.json();
14
+
15
+ if (!projectName || !conversationId) {
16
+ return NextResponse.json({ error: 'projectName and conversationId required' }, { status: 400 });
17
+ }
18
+
19
+ const project = getProjectInfo(projectName);
20
+ if (!project) {
21
+ return NextResponse.json({ error: `Project not found: ${projectName}` }, { status: 404 });
22
+ }
23
+
24
+ // Create a placeholder "done" task that carries the conversation_id
25
+ const db = getDb(getDbPath());
26
+ const id = `link-${Date.now().toString(36)}`;
27
+ db.prepare(`
28
+ INSERT INTO tasks (id, project_name, project_path, prompt, status, priority, conversation_id, log, result_summary, completed_at)
29
+ VALUES (?, ?, ?, ?, 'done', 0, ?, '[]', ?, datetime('now'))
30
+ `).run(id, project.name, project.path, '(linked from local CLI)', conversationId, `Session ${conversationId} linked from local CLI`);
31
+
32
+ return NextResponse.json({
33
+ id,
34
+ projectName: project.name,
35
+ conversationId,
36
+ });
37
+ }
@@ -0,0 +1,44 @@
1
+ import { NextResponse } from 'next/server';
2
+ import { createTask, listTasks } from '@/lib/task-manager';
3
+ import { ensureInitialized } from '@/lib/init';
4
+ import { getProjectInfo } from '@/lib/projects';
5
+ import type { TaskStatus } from '@/src/types';
6
+
7
+ // List tasks — optionally filter by status
8
+ export async function GET(req: Request) {
9
+ ensureInitialized();
10
+ const url = new URL(req.url);
11
+ const status = url.searchParams.get('status') as TaskStatus | null;
12
+ return NextResponse.json(listTasks(status || undefined));
13
+ }
14
+
15
+ // Create a new task
16
+ export async function POST(req: Request) {
17
+ const { projectName, prompt, priority, newSession, conversationId, scheduledAt, mode, watchConfig, agent } = await req.json();
18
+
19
+ if (!projectName || !prompt) {
20
+ return NextResponse.json({ error: 'projectName and prompt are required' }, { status: 400 });
21
+ }
22
+
23
+ const project = getProjectInfo(projectName);
24
+ if (!project) {
25
+ return NextResponse.json({ error: `Project not found: ${projectName}` }, { status: 404 });
26
+ }
27
+
28
+ // conversationId: explicit value → use it; newSession → empty string (force new); otherwise → auto-inherit
29
+ const convId = conversationId || (newSession ? '' : undefined);
30
+
31
+ const task = createTask({
32
+ projectName: project.name,
33
+ projectPath: project.path,
34
+ prompt,
35
+ priority: priority || 0,
36
+ conversationId: convId,
37
+ scheduledAt: scheduledAt || undefined,
38
+ mode: mode || 'prompt',
39
+ watchConfig: watchConfig || undefined,
40
+ agent: agent || undefined,
41
+ });
42
+
43
+ return NextResponse.json(task);
44
+ }
@@ -0,0 +1,14 @@
1
+ import { NextResponse } from 'next/server';
2
+ import { getProjectConversationId } from '@/lib/task-manager';
3
+
4
+ export async function GET(req: Request) {
5
+ const url = new URL(req.url);
6
+ const project = url.searchParams.get('project');
7
+
8
+ if (!project) {
9
+ return NextResponse.json({ error: 'project parameter required' }, { status: 400 });
10
+ }
11
+
12
+ const conversationId = getProjectConversationId(project);
13
+ return NextResponse.json({ conversationId });
14
+ }
@@ -0,0 +1,23 @@
1
+ import { NextResponse, type NextRequest } from 'next/server';
2
+ import { loadSettings } from '@/lib/settings';
3
+ import { handleTelegramMessage } from '@/lib/telegram-bot';
4
+
5
+ // POST /api/telegram — receives messages from telegram-standalone process
6
+ export async function POST(req: NextRequest) {
7
+ const settings = loadSettings();
8
+
9
+ // Verify the request comes from our standalone process
10
+ const secret = req.headers.get('x-telegram-secret');
11
+ if (!secret || secret !== settings.telegramBotToken) {
12
+ return NextResponse.json({ error: 'unauthorized' }, { status: 401 });
13
+ }
14
+
15
+ const message = await req.json();
16
+
17
+ try {
18
+ await handleTelegramMessage(message);
19
+ return NextResponse.json({ ok: true });
20
+ } catch (e: any) {
21
+ return NextResponse.json({ error: e.message }, { status: 500 });
22
+ }
23
+ }
@@ -0,0 +1,6 @@
1
+ import { NextResponse } from 'next/server';
2
+ import { loadAllTemplates } from '@/src/config';
3
+
4
+ export async function GET() {
5
+ return NextResponse.json(loadAllTemplates());
6
+ }
@@ -0,0 +1,39 @@
1
+ import { NextResponse } from 'next/server';
2
+ import { loadSettings } from '@/lib/settings';
3
+ import { addNotification } from '@/lib/notifications';
4
+
5
+ export async function POST(req: Request) {
6
+ const { tabLabel } = await req.json();
7
+ const label = tabLabel || 'Terminal';
8
+
9
+ // Check settings — terminal bell notifications can be disabled
10
+ const settings = loadSettings();
11
+ if ((settings as any).terminalBellEnabled === false) {
12
+ return NextResponse.json({ ok: true, skipped: true });
13
+ }
14
+
15
+ // In-app notification
16
+ try {
17
+ addNotification('terminal_bell', `Terminal idle: ${label}`, `Claude appears to have finished in "${label}".`);
18
+ } catch {}
19
+
20
+ // Telegram notification
21
+ const { telegramBotToken, telegramChatId } = settings;
22
+ if (telegramBotToken && telegramChatId) {
23
+ try {
24
+ const chatIds = String(telegramChatId).split(',').map(s => s.trim()).filter(Boolean);
25
+ for (const chatId of chatIds) {
26
+ await fetch(`https://api.telegram.org/bot${telegramBotToken}/sendMessage`, {
27
+ method: 'POST',
28
+ headers: { 'Content-Type': 'application/json' },
29
+ body: JSON.stringify({
30
+ chat_id: chatId,
31
+ text: `🔔 Forge — Terminal idle\n\n"${label}" appears to have finished.`,
32
+ }),
33
+ });
34
+ }
35
+ } catch {}
36
+ }
37
+
38
+ return NextResponse.json({ ok: true });
39
+ }
@@ -0,0 +1,19 @@
1
+ import { NextResponse } from 'next/server';
2
+ import { execSync } from 'node:child_process';
3
+
4
+ export async function GET(req: Request) {
5
+ const { searchParams } = new URL(req.url);
6
+ const session = searchParams.get('session');
7
+ if (!session || !session.startsWith('mw-')) {
8
+ return NextResponse.json({ path: null });
9
+ }
10
+ try {
11
+ const cwd = execSync(`tmux display-message -p -t ${session} '#{pane_current_path}'`, {
12
+ encoding: 'utf-8',
13
+ timeout: 3000,
14
+ }).trim();
15
+ return NextResponse.json({ path: cwd || null });
16
+ } catch {
17
+ return NextResponse.json({ path: null });
18
+ }
19
+ }
@@ -0,0 +1,15 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { NextResponse } from 'next/server';
4
+ import { getDataDir } from '@/lib/dirs';
5
+
6
+ const STATE_FILE = join(getDataDir(), 'terminal-state.json');
7
+
8
+ export async function GET() {
9
+ try {
10
+ const data = JSON.parse(readFileSync(STATE_FILE, 'utf-8'));
11
+ return NextResponse.json(data);
12
+ } catch {
13
+ return NextResponse.json(null);
14
+ }
15
+ }
@@ -0,0 +1,26 @@
1
+ import { NextResponse } from 'next/server';
2
+ import { startTunnel, stopTunnel, getTunnelStatus } from '@/lib/cloudflared';
3
+ import { verifyAdmin } from '@/lib/password';
4
+
5
+ export async function GET() {
6
+ return NextResponse.json(getTunnelStatus());
7
+ }
8
+
9
+ export async function POST(req: Request) {
10
+ const body = await req.json() as { action: 'start' | 'stop'; password?: string };
11
+
12
+ if (body.action === 'start') {
13
+ if (!body.password || !verifyAdmin(body.password)) {
14
+ return NextResponse.json({ ok: false, error: 'Wrong password' }, { status: 403 });
15
+ }
16
+ const result = await startTunnel();
17
+ return NextResponse.json({ ok: !result.error, ...getTunnelStatus() });
18
+ }
19
+
20
+ if (body.action === 'stop') {
21
+ stopTunnel();
22
+ return NextResponse.json({ ok: true, ...getTunnelStatus() });
23
+ }
24
+
25
+ return NextResponse.json({ ok: false, error: 'Invalid action' }, { status: 400 });
26
+ }
@@ -0,0 +1,43 @@
1
+ import { NextResponse } from 'next/server';
2
+ import { join } from 'node:path';
3
+ import { execSync } from 'node:child_process';
4
+ import { readFileSync } from 'node:fs';
5
+ import { homedir } from 'node:os';
6
+
7
+ export async function POST() {
8
+ try {
9
+ // Get global npm root first (before any cwd changes)
10
+ const pkgRoot = execSync('npm root -g', { encoding: 'utf-8', timeout: 5000, cwd: homedir() }).trim();
11
+ const forgeRoot = join(pkgRoot, '@aion0', 'forge');
12
+
13
+ // Upgrade from npm — use cwd instead of cd
14
+ execSync('npm install -g @aion0/forge@latest --prefer-online 2>&1', {
15
+ encoding: 'utf-8',
16
+ timeout: 120000,
17
+ cwd: homedir(),
18
+ });
19
+
20
+ // Install devDependencies for build
21
+ try {
22
+ execSync('npm install --include=dev 2>&1', { cwd: forgeRoot, timeout: 120000 });
23
+ } catch {}
24
+
25
+ // Read installed version
26
+ let installedVersion = '';
27
+ try {
28
+ const pkg = JSON.parse(readFileSync(join(forgeRoot, 'package.json'), 'utf-8'));
29
+ installedVersion = pkg.version;
30
+ } catch {}
31
+
32
+ return NextResponse.json({
33
+ ok: true,
34
+ message: `Upgraded to v${installedVersion}. Restart server to apply.`,
35
+ });
36
+ } catch (e) {
37
+ const msg = e instanceof Error ? e.message : String(e);
38
+ return NextResponse.json({
39
+ ok: false,
40
+ error: `Upgrade failed: ${msg.slice(0, 300)}`,
41
+ });
42
+ }
43
+ }
@@ -0,0 +1,20 @@
1
+ import { NextResponse } from 'next/server';
2
+ import { scanUsage, queryUsage } from '@/lib/usage-scanner';
3
+
4
+ // GET /api/usage?days=7&project=forge&source=task&model=claude-opus-4
5
+ export async function GET(req: Request) {
6
+ const { searchParams } = new URL(req.url);
7
+ const days = searchParams.get('days') ? parseInt(searchParams.get('days')!) : undefined;
8
+ const projectName = searchParams.get('project') || undefined;
9
+ const source = searchParams.get('source') || undefined;
10
+ const model = searchParams.get('model') || undefined;
11
+
12
+ const data = queryUsage({ days, projectName, source, model });
13
+ return NextResponse.json(data);
14
+ }
15
+
16
+ // POST /api/usage — trigger scan
17
+ export async function POST() {
18
+ const result = scanUsage();
19
+ return NextResponse.json(result);
20
+ }
@@ -0,0 +1,78 @@
1
+ import { NextResponse } from 'next/server';
2
+ import { readFileSync } from 'node:fs';
3
+ import { join } from 'node:path';
4
+
5
+ // Read version once at module load (= server start), not on every request
6
+ const CURRENT_VERSION = (() => {
7
+ try {
8
+ const pkg = JSON.parse(readFileSync(join(process.cwd(), 'package.json'), 'utf-8'));
9
+ return pkg.version as string;
10
+ } catch {
11
+ return '0.0.0';
12
+ }
13
+ })();
14
+
15
+ // Cache npm version check for 10 minutes
16
+ let cachedLatest: { version: string; checkedAt: number } | null = null;
17
+ const CACHE_TTL = 10 * 60 * 1000;
18
+
19
+ // Track which versions we already notified about (avoid duplicates)
20
+ let notifiedVersion = '';
21
+
22
+ async function getLatestVersion(force = false): Promise<string> {
23
+ if (!force && cachedLatest && Date.now() - cachedLatest.checkedAt < CACHE_TTL) {
24
+ return cachedLatest.version;
25
+ }
26
+ try {
27
+ const controller = new AbortController();
28
+ const timeout = setTimeout(() => controller.abort(), 5000);
29
+ const res = await fetch('https://registry.npmjs.org/@aion0/forge/latest', {
30
+ signal: controller.signal,
31
+ headers: { 'Accept': 'application/json' },
32
+ });
33
+ clearTimeout(timeout);
34
+ if (!res.ok) return cachedLatest?.version || '';
35
+ const data = await res.json();
36
+ cachedLatest = { version: data.version, checkedAt: Date.now() };
37
+ return data.version;
38
+ } catch {
39
+ return cachedLatest?.version || '';
40
+ }
41
+ }
42
+
43
+ function compareVersions(a: string, b: string): number {
44
+ const pa = a.split('.').map(Number);
45
+ const pb = b.split('.').map(Number);
46
+ for (let i = 0; i < 3; i++) {
47
+ if ((pa[i] || 0) < (pb[i] || 0)) return -1;
48
+ if ((pa[i] || 0) > (pb[i] || 0)) return 1;
49
+ }
50
+ return 0;
51
+ }
52
+
53
+ export async function GET(req: Request) {
54
+ const { searchParams } = new URL(req.url);
55
+ const force = searchParams.has('force');
56
+ const current = CURRENT_VERSION;
57
+ const latest = await getLatestVersion(force);
58
+ const hasUpdate = !!(latest && compareVersions(current, latest) < 0);
59
+
60
+ // Create a notification when new version is detected (once per version)
61
+ if (hasUpdate && latest !== notifiedVersion) {
62
+ notifiedVersion = latest;
63
+ try {
64
+ const { addNotification } = require('@/lib/notifications');
65
+ addNotification(
66
+ 'system',
67
+ `Update available: v${latest}`,
68
+ `Current: v${current}\nforge upgrade\nnpm install -g @aion0/forge@latest`,
69
+ );
70
+ } catch {}
71
+ }
72
+
73
+ return NextResponse.json({
74
+ current,
75
+ latest: latest || current,
76
+ hasUpdate,
77
+ });
78
+ }
@@ -0,0 +1,33 @@
1
+ import { NextResponse } from 'next/server';
2
+ import { ensureInitialized } from '@/lib/init';
3
+ import { listWatchers, createWatcher, deleteWatcher, toggleWatcher } from '@/lib/session-watcher';
4
+
5
+ export async function GET() {
6
+ ensureInitialized();
7
+ return NextResponse.json(listWatchers());
8
+ }
9
+
10
+ export async function POST(req: Request) {
11
+ ensureInitialized();
12
+ const body = await req.json();
13
+
14
+ if (body.action === 'delete') {
15
+ deleteWatcher(body.id);
16
+ return NextResponse.json({ ok: true });
17
+ }
18
+
19
+ if (body.action === 'toggle') {
20
+ toggleWatcher(body.id, body.active);
21
+ return NextResponse.json({ ok: true });
22
+ }
23
+
24
+ // Create new watcher
25
+ const watcher = createWatcher({
26
+ projectName: body.projectName,
27
+ sessionId: body.sessionId,
28
+ label: body.label,
29
+ checkInterval: body.checkInterval || 60,
30
+ });
31
+
32
+ return NextResponse.json(watcher);
33
+ }
@@ -0,0 +1,35 @@
1
+ import { NextResponse } from 'next/server';
2
+
3
+ const WORKSPACE_PORT = Number(process.env.WORKSPACE_PORT) || 8405;
4
+ const DAEMON_URL = `http://localhost:${WORKSPACE_PORT}`;
5
+
6
+ // Proxy to workspace daemon — Agent operations
7
+ export async function POST(req: Request, { params }: { params: Promise<{ id: string }> }) {
8
+ const { id } = await params;
9
+ const body = await req.json();
10
+
11
+ try {
12
+ const res = await fetch(`${DAEMON_URL}/workspace/${id}/agents`, {
13
+ method: 'POST',
14
+ headers: { 'Content-Type': 'application/json' },
15
+ body: JSON.stringify(body),
16
+ });
17
+ const data = await res.json();
18
+ return NextResponse.json(data, { status: res.status });
19
+ } catch (err: any) {
20
+ return NextResponse.json({ error: 'Workspace daemon not available' }, { status: 503 });
21
+ }
22
+ }
23
+
24
+ // Proxy to workspace daemon — Get agent states
25
+ export async function GET(_req: Request, { params }: { params: Promise<{ id: string }> }) {
26
+ const { id } = await params;
27
+
28
+ try {
29
+ const res = await fetch(`${DAEMON_URL}/workspace/${id}/agents`);
30
+ const data = await res.json();
31
+ return NextResponse.json(data, { status: res.status });
32
+ } catch (err: any) {
33
+ return NextResponse.json({ error: 'Workspace daemon not available' }, { status: 503 });
34
+ }
35
+ }
@@ -0,0 +1,23 @@
1
+ import { NextResponse } from 'next/server';
2
+
3
+ const WORKSPACE_PORT = Number(process.env.WORKSPACE_PORT) || 8405;
4
+ const DAEMON_URL = `http://localhost:${WORKSPACE_PORT}`;
5
+
6
+ // Proxy to workspace daemon — Memory query
7
+ export async function GET(req: Request, { params }: { params: Promise<{ id: string }> }) {
8
+ const { id: workspaceId } = await params;
9
+ const url = new URL(req.url);
10
+ const agentId = url.searchParams.get('agentId');
11
+
12
+ if (!agentId) {
13
+ return NextResponse.json({ error: 'agentId required' }, { status: 400 });
14
+ }
15
+
16
+ try {
17
+ const res = await fetch(`${DAEMON_URL}/workspace/${workspaceId}/memory?agentId=${encodeURIComponent(agentId)}`);
18
+ const data = await res.json();
19
+ return NextResponse.json(data, { status: res.status });
20
+ } catch (err: any) {
21
+ return NextResponse.json({ error: 'Workspace daemon not available' }, { status: 503 });
22
+ }
23
+ }
@@ -0,0 +1,22 @@
1
+ import { NextResponse } from 'next/server';
2
+
3
+ const WORKSPACE_PORT = Number(process.env.WORKSPACE_PORT) || 8405;
4
+ const DAEMON_URL = `http://localhost:${WORKSPACE_PORT}`;
5
+
6
+ // Proxy to workspace daemon — Smith API (called by forge skills in terminal)
7
+ export async function POST(req: Request, { params }: { params: Promise<{ id: string }> }) {
8
+ const { id } = await params;
9
+ const body = await req.json();
10
+
11
+ try {
12
+ const res = await fetch(`${DAEMON_URL}/workspace/${id}/smith`, {
13
+ method: 'POST',
14
+ headers: { 'Content-Type': 'application/json' },
15
+ body: JSON.stringify(body),
16
+ });
17
+ const data = await res.json();
18
+ return NextResponse.json(data, { status: res.status });
19
+ } catch (err: any) {
20
+ return NextResponse.json({ error: 'Workspace daemon not available' }, { status: 503 });
21
+ }
22
+ }
@@ -0,0 +1,31 @@
1
+ export const dynamic = 'force-dynamic';
2
+ export const runtime = 'nodejs';
3
+
4
+ const WORKSPACE_PORT = Number(process.env.WORKSPACE_PORT) || 8405;
5
+ const DAEMON_URL = `http://localhost:${WORKSPACE_PORT}`;
6
+
7
+ // SSE relay — proxy daemon's SSE stream to browser
8
+ export async function GET(req: Request, { params }: { params: Promise<{ id: string }> }) {
9
+ const { id } = await params;
10
+
11
+ try {
12
+ const daemonRes = await fetch(`${DAEMON_URL}/workspace/${id}/stream`, {
13
+ signal: req.signal,
14
+ });
15
+
16
+ if (!daemonRes.ok || !daemonRes.body) {
17
+ return new Response(daemonRes.statusText || 'Daemon error', { status: daemonRes.status });
18
+ }
19
+
20
+ // Pipe daemon SSE stream directly to browser
21
+ return new Response(daemonRes.body, {
22
+ headers: {
23
+ 'Content-Type': 'text/event-stream',
24
+ 'Cache-Control': 'no-cache, no-transform',
25
+ 'Connection': 'keep-alive',
26
+ },
27
+ });
28
+ } catch (err: any) {
29
+ return new Response('Workspace daemon not available', { status: 503 });
30
+ }
31
+ }
@@ -0,0 +1,79 @@
1
+ import { NextResponse } from 'next/server';
2
+ import { listWorkspaces, findWorkspaceByProject, loadWorkspace, deleteWorkspace } from '@/lib/workspace';
3
+ import { randomUUID } from 'node:crypto';
4
+
5
+ // List workspaces, find by projectPath, or export template
6
+ export async function GET(req: Request) {
7
+ const url = new URL(req.url);
8
+ const projectPath = url.searchParams.get('projectPath');
9
+ const exportId = url.searchParams.get('export');
10
+
11
+ if (exportId) {
12
+ // Export workspace as template (agents + positions, no state/logs)
13
+ const ws = loadWorkspace(exportId);
14
+ if (!ws) return NextResponse.json({ error: 'not found' }, { status: 404 });
15
+ const template = {
16
+ name: ws.projectName + ' template',
17
+ agents: ws.agents.map(a => ({ ...a, entries: undefined })), // strip Input entries
18
+ nodePositions: ws.nodePositions,
19
+ exportedAt: Date.now(),
20
+ };
21
+ return NextResponse.json(template);
22
+ }
23
+
24
+ if (projectPath) {
25
+ const ws = findWorkspaceByProject(projectPath);
26
+ return NextResponse.json(ws || null);
27
+ }
28
+
29
+ return NextResponse.json(listWorkspaces());
30
+ }
31
+
32
+ // Create workspace or import template — proxied through the workspace daemon
33
+ // so the daemon remains the exclusive writer of state.json (prevents race conditions).
34
+ export async function POST(req: Request) {
35
+ const body = await req.json();
36
+ const { projectPath, projectName, template } = body;
37
+
38
+ if (!projectPath || !projectName) {
39
+ return NextResponse.json({ error: 'projectPath and projectName are required' }, { status: 400 });
40
+ }
41
+
42
+ const existing = findWorkspaceByProject(projectPath);
43
+ if (existing && !template) {
44
+ return NextResponse.json(existing);
45
+ }
46
+
47
+ const daemonUrl = `http://localhost:${Number(process.env.WORKSPACE_PORT) || 8405}`;
48
+ try {
49
+ const daemonRes = await fetch(`${daemonUrl}/workspace/create`, {
50
+ method: 'POST',
51
+ headers: { 'Content-Type': 'application/json' },
52
+ body: JSON.stringify({
53
+ id: existing?.id || randomUUID(),
54
+ projectPath,
55
+ projectName,
56
+ template,
57
+ createdAt: existing?.createdAt,
58
+ }),
59
+ });
60
+ const data = await daemonRes.json();
61
+ return NextResponse.json(data, { status: daemonRes.status });
62
+ } catch (err: any) {
63
+ return NextResponse.json({ error: `Workspace daemon unreachable: ${err.message}` }, { status: 503 });
64
+ }
65
+ }
66
+
67
+ // Delete a workspace
68
+ export async function DELETE(req: Request) {
69
+ const url = new URL(req.url);
70
+ const id = url.searchParams.get('id');
71
+ if (!id) return NextResponse.json({ error: 'id required' }, { status: 400 });
72
+
73
+ // Unload from daemon if active
74
+ const daemonUrl = `http://localhost:${Number(process.env.WORKSPACE_PORT) || 8405}`;
75
+ try { await fetch(`${daemonUrl}/workspace/${id}/unload`, { method: 'POST' }); } catch {}
76
+
77
+ deleteWorkspace(id);
78
+ return NextResponse.json({ ok: true });
79
+ }
@@ -0,0 +1,21 @@
1
+ 'use client';
2
+
3
+ export default function GlobalError({ error, reset }: { error: Error & { digest?: string }; reset: () => void }) {
4
+ return (
5
+ <html lang="en">
6
+ <head>
7
+ <meta charSet="utf-8" />
8
+ </head>
9
+ <body style={{ background: '#0a0a0a', color: '#e5e5e5', fontFamily: 'monospace', padding: '2rem' }}>
10
+ <h2>Something went wrong</h2>
11
+ <p style={{ color: '#999' }}>{error?.message || 'Unknown error'}</p>
12
+ <button
13
+ onClick={() => reset()}
14
+ style={{ marginTop: '1rem', padding: '0.5rem 1rem', background: '#3b82f6', color: '#fff', border: 'none', borderRadius: '4px', cursor: 'pointer' }}
15
+ >
16
+ Try again
17
+ </button>
18
+ </body>
19
+ </html>
20
+ );
21
+ }