@cluesmith/codev 2.0.0-rc.9 → 2.0.0

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 (537) hide show
  1. package/bin/af.js +2 -2
  2. package/bin/consult.js +1 -1
  3. package/dashboard/dist/assets/index-4n9zpWLY.css +32 -0
  4. package/dashboard/dist/assets/index-b38SaXk5.js +136 -0
  5. package/dashboard/dist/assets/index-b38SaXk5.js.map +1 -0
  6. package/dashboard/dist/index.html +14 -0
  7. package/dist/agent-farm/cli.d.ts.map +1 -1
  8. package/dist/agent-farm/cli.js +179 -104
  9. package/dist/agent-farm/cli.js.map +1 -1
  10. package/dist/agent-farm/commands/architect.d.ts +3 -3
  11. package/dist/agent-farm/commands/architect.d.ts.map +1 -1
  12. package/dist/agent-farm/commands/architect.js +20 -147
  13. package/dist/agent-farm/commands/architect.js.map +1 -1
  14. package/dist/agent-farm/commands/attach.d.ts +13 -0
  15. package/dist/agent-farm/commands/attach.d.ts.map +1 -0
  16. package/dist/agent-farm/commands/attach.js +144 -0
  17. package/dist/agent-farm/commands/attach.js.map +1 -0
  18. package/dist/agent-farm/commands/cleanup.d.ts.map +1 -1
  19. package/dist/agent-farm/commands/cleanup.js +35 -19
  20. package/dist/agent-farm/commands/cleanup.js.map +1 -1
  21. package/dist/agent-farm/commands/consult.d.ts +3 -4
  22. package/dist/agent-farm/commands/consult.d.ts.map +1 -1
  23. package/dist/agent-farm/commands/consult.js +27 -37
  24. package/dist/agent-farm/commands/consult.js.map +1 -1
  25. package/dist/agent-farm/commands/index.d.ts +2 -2
  26. package/dist/agent-farm/commands/index.d.ts.map +1 -1
  27. package/dist/agent-farm/commands/index.js +2 -2
  28. package/dist/agent-farm/commands/index.js.map +1 -1
  29. package/dist/agent-farm/commands/open.d.ts +4 -2
  30. package/dist/agent-farm/commands/open.d.ts.map +1 -1
  31. package/dist/agent-farm/commands/open.js +33 -83
  32. package/dist/agent-farm/commands/open.js.map +1 -1
  33. package/dist/agent-farm/commands/send.d.ts +1 -1
  34. package/dist/agent-farm/commands/send.d.ts.map +1 -1
  35. package/dist/agent-farm/commands/send.js +70 -79
  36. package/dist/agent-farm/commands/send.js.map +1 -1
  37. package/dist/agent-farm/commands/shell.d.ts +15 -0
  38. package/dist/agent-farm/commands/shell.d.ts.map +1 -0
  39. package/dist/agent-farm/commands/shell.js +50 -0
  40. package/dist/agent-farm/commands/shell.js.map +1 -0
  41. package/dist/agent-farm/commands/spawn-roles.d.ts +80 -0
  42. package/dist/agent-farm/commands/spawn-roles.d.ts.map +1 -0
  43. package/dist/agent-farm/commands/spawn-roles.js +278 -0
  44. package/dist/agent-farm/commands/spawn-roles.js.map +1 -0
  45. package/dist/agent-farm/commands/spawn-worktree.d.ts +96 -0
  46. package/dist/agent-farm/commands/spawn-worktree.d.ts.map +1 -0
  47. package/dist/agent-farm/commands/spawn-worktree.js +305 -0
  48. package/dist/agent-farm/commands/spawn-worktree.js.map +1 -0
  49. package/dist/agent-farm/commands/spawn.d.ts +5 -1
  50. package/dist/agent-farm/commands/spawn.d.ts.map +1 -1
  51. package/dist/agent-farm/commands/spawn.js +241 -561
  52. package/dist/agent-farm/commands/spawn.js.map +1 -1
  53. package/dist/agent-farm/commands/start.d.ts +10 -20
  54. package/dist/agent-farm/commands/start.d.ts.map +1 -1
  55. package/dist/agent-farm/commands/start.js +45 -449
  56. package/dist/agent-farm/commands/start.js.map +1 -1
  57. package/dist/agent-farm/commands/status.d.ts +2 -0
  58. package/dist/agent-farm/commands/status.d.ts.map +1 -1
  59. package/dist/agent-farm/commands/status.js +75 -24
  60. package/dist/agent-farm/commands/status.js.map +1 -1
  61. package/dist/agent-farm/commands/stop.d.ts +6 -0
  62. package/dist/agent-farm/commands/stop.d.ts.map +1 -1
  63. package/dist/agent-farm/commands/stop.js +49 -109
  64. package/dist/agent-farm/commands/stop.js.map +1 -1
  65. package/dist/agent-farm/commands/tower-cloud.d.ts +48 -0
  66. package/dist/agent-farm/commands/tower-cloud.d.ts.map +1 -0
  67. package/dist/agent-farm/commands/tower-cloud.js +293 -0
  68. package/dist/agent-farm/commands/tower-cloud.js.map +1 -0
  69. package/dist/agent-farm/commands/tower.d.ts +9 -0
  70. package/dist/agent-farm/commands/tower.d.ts.map +1 -1
  71. package/dist/agent-farm/commands/tower.js +59 -19
  72. package/dist/agent-farm/commands/tower.js.map +1 -1
  73. package/dist/agent-farm/db/index.d.ts +6 -2
  74. package/dist/agent-farm/db/index.d.ts.map +1 -1
  75. package/dist/agent-farm/db/index.js +301 -19
  76. package/dist/agent-farm/db/index.js.map +1 -1
  77. package/dist/agent-farm/db/migrate.d.ts +0 -4
  78. package/dist/agent-farm/db/migrate.d.ts.map +1 -1
  79. package/dist/agent-farm/db/migrate.js +6 -55
  80. package/dist/agent-farm/db/migrate.js.map +1 -1
  81. package/dist/agent-farm/db/schema.d.ts +3 -3
  82. package/dist/agent-farm/db/schema.d.ts.map +1 -1
  83. package/dist/agent-farm/db/schema.js +25 -19
  84. package/dist/agent-farm/db/schema.js.map +1 -1
  85. package/dist/agent-farm/db/types.d.ts +3 -13
  86. package/dist/agent-farm/db/types.d.ts.map +1 -1
  87. package/dist/agent-farm/db/types.js +3 -11
  88. package/dist/agent-farm/db/types.js.map +1 -1
  89. package/dist/agent-farm/hq-connector.d.ts +2 -6
  90. package/dist/agent-farm/hq-connector.d.ts.map +1 -1
  91. package/dist/agent-farm/hq-connector.js +2 -17
  92. package/dist/agent-farm/hq-connector.js.map +1 -1
  93. package/dist/agent-farm/lib/cloud-config.d.ts +59 -0
  94. package/dist/agent-farm/lib/cloud-config.d.ts.map +1 -0
  95. package/dist/agent-farm/lib/cloud-config.js +143 -0
  96. package/dist/agent-farm/lib/cloud-config.js.map +1 -0
  97. package/dist/agent-farm/lib/device-name.d.ts +25 -0
  98. package/dist/agent-farm/lib/device-name.d.ts.map +1 -0
  99. package/dist/agent-farm/lib/device-name.js +46 -0
  100. package/dist/agent-farm/lib/device-name.js.map +1 -0
  101. package/dist/agent-farm/lib/nonce-store.d.ts +28 -0
  102. package/dist/agent-farm/lib/nonce-store.d.ts.map +1 -0
  103. package/dist/agent-farm/lib/nonce-store.js +60 -0
  104. package/dist/agent-farm/lib/nonce-store.js.map +1 -0
  105. package/dist/agent-farm/lib/token-exchange.d.ts +18 -0
  106. package/dist/agent-farm/lib/token-exchange.d.ts.map +1 -0
  107. package/dist/agent-farm/lib/token-exchange.js +48 -0
  108. package/dist/agent-farm/lib/token-exchange.js.map +1 -0
  109. package/dist/agent-farm/lib/tower-client.d.ts +163 -0
  110. package/dist/agent-farm/lib/tower-client.d.ts.map +1 -0
  111. package/dist/agent-farm/lib/tower-client.js +233 -0
  112. package/dist/agent-farm/lib/tower-client.js.map +1 -0
  113. package/dist/agent-farm/lib/tunnel-client.d.ts +117 -0
  114. package/dist/agent-farm/lib/tunnel-client.d.ts.map +1 -0
  115. package/dist/agent-farm/lib/tunnel-client.js +504 -0
  116. package/dist/agent-farm/lib/tunnel-client.js.map +1 -0
  117. package/dist/agent-farm/servers/tower-instances.d.ts +82 -0
  118. package/dist/agent-farm/servers/tower-instances.d.ts.map +1 -0
  119. package/dist/agent-farm/servers/tower-instances.js +454 -0
  120. package/dist/agent-farm/servers/tower-instances.js.map +1 -0
  121. package/dist/agent-farm/servers/tower-routes.d.ts +34 -0
  122. package/dist/agent-farm/servers/tower-routes.d.ts.map +1 -0
  123. package/dist/agent-farm/servers/tower-routes.js +1445 -0
  124. package/dist/agent-farm/servers/tower-routes.js.map +1 -0
  125. package/dist/agent-farm/servers/tower-server.d.ts +5 -2
  126. package/dist/agent-farm/servers/tower-server.d.ts.map +1 -1
  127. package/dist/agent-farm/servers/tower-server.js +157 -475
  128. package/dist/agent-farm/servers/tower-server.js.map +1 -1
  129. package/dist/agent-farm/servers/tower-terminals.d.ts +119 -0
  130. package/dist/agent-farm/servers/tower-terminals.d.ts.map +1 -0
  131. package/dist/agent-farm/servers/tower-terminals.js +629 -0
  132. package/dist/agent-farm/servers/tower-terminals.js.map +1 -0
  133. package/dist/agent-farm/servers/tower-tunnel.d.ts +34 -0
  134. package/dist/agent-farm/servers/tower-tunnel.d.ts.map +1 -0
  135. package/dist/agent-farm/servers/tower-tunnel.js +473 -0
  136. package/dist/agent-farm/servers/tower-tunnel.js.map +1 -0
  137. package/dist/agent-farm/servers/tower-types.d.ts +86 -0
  138. package/dist/agent-farm/servers/tower-types.d.ts.map +1 -0
  139. package/dist/agent-farm/servers/tower-types.js +6 -0
  140. package/dist/agent-farm/servers/tower-types.js.map +1 -0
  141. package/dist/agent-farm/servers/tower-utils.d.ts +58 -0
  142. package/dist/agent-farm/servers/tower-utils.d.ts.map +1 -0
  143. package/dist/agent-farm/servers/tower-utils.js +182 -0
  144. package/dist/agent-farm/servers/tower-utils.js.map +1 -0
  145. package/dist/agent-farm/servers/tower-websocket.d.ts +25 -0
  146. package/dist/agent-farm/servers/tower-websocket.d.ts.map +1 -0
  147. package/dist/agent-farm/servers/tower-websocket.js +171 -0
  148. package/dist/agent-farm/servers/tower-websocket.js.map +1 -0
  149. package/dist/agent-farm/state.d.ts +6 -2
  150. package/dist/agent-farm/state.d.ts.map +1 -1
  151. package/dist/agent-farm/state.js +34 -25
  152. package/dist/agent-farm/state.js.map +1 -1
  153. package/dist/agent-farm/types.d.ts +49 -26
  154. package/dist/agent-farm/types.d.ts.map +1 -1
  155. package/dist/agent-farm/utils/config.d.ts +0 -5
  156. package/dist/agent-farm/utils/config.d.ts.map +1 -1
  157. package/dist/agent-farm/utils/config.js +12 -44
  158. package/dist/agent-farm/utils/config.js.map +1 -1
  159. package/dist/agent-farm/utils/deps.d.ts.map +1 -1
  160. package/dist/agent-farm/utils/deps.js +0 -32
  161. package/dist/agent-farm/utils/deps.js.map +1 -1
  162. package/dist/agent-farm/utils/file-tabs.d.ts +27 -0
  163. package/dist/agent-farm/utils/file-tabs.d.ts.map +1 -0
  164. package/dist/agent-farm/utils/file-tabs.js +46 -0
  165. package/dist/agent-farm/utils/file-tabs.js.map +1 -0
  166. package/dist/agent-farm/utils/gate-status.d.ts +16 -0
  167. package/dist/agent-farm/utils/gate-status.d.ts.map +1 -0
  168. package/dist/agent-farm/utils/gate-status.js +79 -0
  169. package/dist/agent-farm/utils/gate-status.js.map +1 -0
  170. package/dist/agent-farm/utils/gate-watcher.d.ts +38 -0
  171. package/dist/agent-farm/utils/gate-watcher.d.ts.map +1 -0
  172. package/dist/agent-farm/utils/gate-watcher.js +122 -0
  173. package/dist/agent-farm/utils/gate-watcher.js.map +1 -0
  174. package/dist/agent-farm/utils/index.d.ts +0 -1
  175. package/dist/agent-farm/utils/index.d.ts.map +1 -1
  176. package/dist/agent-farm/utils/index.js +0 -1
  177. package/dist/agent-farm/utils/index.js.map +1 -1
  178. package/dist/agent-farm/utils/notifications.d.ts +30 -0
  179. package/dist/agent-farm/utils/notifications.d.ts.map +1 -0
  180. package/dist/agent-farm/utils/notifications.js +121 -0
  181. package/dist/agent-farm/utils/notifications.js.map +1 -0
  182. package/dist/agent-farm/utils/server-utils.d.ts +5 -5
  183. package/dist/agent-farm/utils/server-utils.d.ts.map +1 -1
  184. package/dist/agent-farm/utils/server-utils.js +5 -16
  185. package/dist/agent-farm/utils/server-utils.js.map +1 -1
  186. package/dist/agent-farm/utils/session.d.ts +32 -0
  187. package/dist/agent-farm/utils/session.d.ts.map +1 -0
  188. package/dist/agent-farm/utils/session.js +57 -0
  189. package/dist/agent-farm/utils/session.js.map +1 -0
  190. package/dist/agent-farm/utils/shell.d.ts +9 -22
  191. package/dist/agent-farm/utils/shell.d.ts.map +1 -1
  192. package/dist/agent-farm/utils/shell.js +34 -34
  193. package/dist/agent-farm/utils/shell.js.map +1 -1
  194. package/dist/cli.d.ts.map +1 -1
  195. package/dist/cli.js +6 -37
  196. package/dist/cli.js.map +1 -1
  197. package/dist/commands/adopt.d.ts.map +1 -1
  198. package/dist/commands/adopt.js +33 -4
  199. package/dist/commands/adopt.js.map +1 -1
  200. package/dist/commands/consult/index.d.ts +13 -2
  201. package/dist/commands/consult/index.d.ts.map +1 -1
  202. package/dist/commands/consult/index.js +244 -29
  203. package/dist/commands/consult/index.js.map +1 -1
  204. package/dist/commands/doctor.d.ts.map +1 -1
  205. package/dist/commands/doctor.js +96 -79
  206. package/dist/commands/doctor.js.map +1 -1
  207. package/dist/commands/init.d.ts.map +1 -1
  208. package/dist/commands/init.js +36 -3
  209. package/dist/commands/init.js.map +1 -1
  210. package/dist/commands/porch/build-counter.d.ts +5 -0
  211. package/dist/commands/porch/build-counter.d.ts.map +1 -0
  212. package/dist/commands/porch/build-counter.js +5 -0
  213. package/dist/commands/porch/build-counter.js.map +1 -0
  214. package/dist/commands/porch/checks.d.ts +3 -2
  215. package/dist/commands/porch/checks.d.ts.map +1 -1
  216. package/dist/commands/porch/checks.js +8 -2
  217. package/dist/commands/porch/checks.js.map +1 -1
  218. package/dist/commands/porch/index.d.ts +4 -0
  219. package/dist/commands/porch/index.d.ts.map +1 -1
  220. package/dist/commands/porch/index.js +109 -70
  221. package/dist/commands/porch/index.js.map +1 -1
  222. package/dist/commands/porch/next.d.ts +22 -0
  223. package/dist/commands/porch/next.d.ts.map +1 -0
  224. package/dist/commands/porch/next.js +571 -0
  225. package/dist/commands/porch/next.js.map +1 -0
  226. package/dist/commands/porch/plan.d.ts +11 -1
  227. package/dist/commands/porch/plan.d.ts.map +1 -1
  228. package/dist/commands/porch/plan.js +33 -5
  229. package/dist/commands/porch/plan.js.map +1 -1
  230. package/dist/commands/porch/prompts.d.ts.map +1 -1
  231. package/dist/commands/porch/prompts.js +44 -26
  232. package/dist/commands/porch/prompts.js.map +1 -1
  233. package/dist/commands/porch/protocol.d.ts +6 -4
  234. package/dist/commands/porch/protocol.d.ts.map +1 -1
  235. package/dist/commands/porch/protocol.js +59 -15
  236. package/dist/commands/porch/protocol.js.map +1 -1
  237. package/dist/commands/porch/state.d.ts +29 -2
  238. package/dist/commands/porch/state.d.ts.map +1 -1
  239. package/dist/commands/porch/state.js +71 -3
  240. package/dist/commands/porch/state.js.map +1 -1
  241. package/dist/commands/porch/types.d.ts +45 -2
  242. package/dist/commands/porch/types.d.ts.map +1 -1
  243. package/dist/commands/porch/verdict.d.ts +31 -0
  244. package/dist/commands/porch/verdict.d.ts.map +1 -0
  245. package/dist/commands/porch/verdict.js +59 -0
  246. package/dist/commands/porch/verdict.js.map +1 -0
  247. package/dist/commands/update.d.ts.map +1 -1
  248. package/dist/commands/update.js +18 -6
  249. package/dist/commands/update.js.map +1 -1
  250. package/dist/lib/scaffold.d.ts +13 -0
  251. package/dist/lib/scaffold.d.ts.map +1 -1
  252. package/dist/lib/scaffold.js +36 -0
  253. package/dist/lib/scaffold.js.map +1 -1
  254. package/dist/terminal/index.d.ts +8 -0
  255. package/dist/terminal/index.d.ts.map +1 -0
  256. package/dist/terminal/index.js +5 -0
  257. package/dist/terminal/index.js.map +1 -0
  258. package/dist/terminal/pty-manager.d.ts +69 -0
  259. package/dist/terminal/pty-manager.d.ts.map +1 -0
  260. package/dist/terminal/pty-manager.js +377 -0
  261. package/dist/terminal/pty-manager.js.map +1 -0
  262. package/dist/terminal/pty-session.d.ts +104 -0
  263. package/dist/terminal/pty-session.d.ts.map +1 -0
  264. package/dist/terminal/pty-session.js +327 -0
  265. package/dist/terminal/pty-session.js.map +1 -0
  266. package/dist/terminal/ring-buffer.d.ts +34 -0
  267. package/dist/terminal/ring-buffer.d.ts.map +1 -0
  268. package/dist/terminal/ring-buffer.js +94 -0
  269. package/dist/terminal/ring-buffer.js.map +1 -0
  270. package/dist/terminal/session-manager.d.ts +115 -0
  271. package/dist/terminal/session-manager.d.ts.map +1 -0
  272. package/dist/terminal/session-manager.js +582 -0
  273. package/dist/terminal/session-manager.js.map +1 -0
  274. package/dist/terminal/shellper-client.d.ts +66 -0
  275. package/dist/terminal/shellper-client.d.ts.map +1 -0
  276. package/dist/terminal/shellper-client.js +234 -0
  277. package/dist/terminal/shellper-client.js.map +1 -0
  278. package/dist/terminal/shellper-main.d.ts +19 -0
  279. package/dist/terminal/shellper-main.d.ts.map +1 -0
  280. package/dist/terminal/shellper-main.js +153 -0
  281. package/dist/terminal/shellper-main.js.map +1 -0
  282. package/dist/terminal/shellper-process.d.ts +75 -0
  283. package/dist/terminal/shellper-process.d.ts.map +1 -0
  284. package/dist/terminal/shellper-process.js +279 -0
  285. package/dist/terminal/shellper-process.js.map +1 -0
  286. package/dist/terminal/shellper-protocol.d.ts +115 -0
  287. package/dist/terminal/shellper-protocol.d.ts.map +1 -0
  288. package/dist/terminal/shellper-protocol.js +214 -0
  289. package/dist/terminal/shellper-protocol.js.map +1 -0
  290. package/dist/terminal/shellper-replay-buffer.d.ts +38 -0
  291. package/dist/terminal/shellper-replay-buffer.d.ts.map +1 -0
  292. package/dist/terminal/shellper-replay-buffer.js +94 -0
  293. package/dist/terminal/shellper-replay-buffer.js.map +1 -0
  294. package/dist/terminal/ws-protocol.d.ts +27 -0
  295. package/dist/terminal/ws-protocol.d.ts.map +1 -0
  296. package/dist/terminal/ws-protocol.js +44 -0
  297. package/dist/terminal/ws-protocol.js.map +1 -0
  298. package/package.json +17 -5
  299. package/skeleton/.claude/skills/af/SKILL.md +89 -0
  300. package/skeleton/.claude/skills/codev/SKILL.md +41 -0
  301. package/skeleton/.claude/skills/consult/SKILL.md +81 -0
  302. package/skeleton/.claude/skills/generate-image/SKILL.md +56 -0
  303. package/skeleton/DEPENDENCIES.md +4 -62
  304. package/skeleton/builders.md +1 -1
  305. package/skeleton/consult-types/impl-review.md +18 -9
  306. package/skeleton/consult-types/integration-review.md +1 -1
  307. package/skeleton/consult-types/plan-review.md +1 -1
  308. package/skeleton/consult-types/pr-ready.md +1 -1
  309. package/skeleton/consult-types/spec-review.md +1 -1
  310. package/skeleton/porch/prompts/defend.md +1 -1
  311. package/skeleton/porch/prompts/evaluate.md +2 -2
  312. package/skeleton/porch/prompts/implement.md +1 -1
  313. package/skeleton/porch/prompts/plan.md +1 -1
  314. package/skeleton/porch/prompts/review.md +4 -4
  315. package/skeleton/porch/prompts/specify.md +1 -1
  316. package/skeleton/porch/prompts/understand.md +2 -2
  317. package/skeleton/protocol-schema.json +282 -0
  318. package/skeleton/protocols/bugfix/builder-prompt.md +60 -0
  319. package/skeleton/protocols/bugfix/prompts/fix.md +77 -0
  320. package/skeleton/protocols/bugfix/prompts/investigate.md +77 -0
  321. package/skeleton/protocols/bugfix/prompts/pr.md +84 -0
  322. package/skeleton/protocols/bugfix/protocol.json +20 -33
  323. package/skeleton/protocols/experiment/builder-prompt.md +52 -0
  324. package/skeleton/protocols/experiment/protocol.json +101 -0
  325. package/skeleton/protocols/experiment/protocol.md +3 -3
  326. package/skeleton/protocols/experiment/templates/notes.md +1 -1
  327. package/skeleton/protocols/maintain/builder-prompt.md +46 -0
  328. package/skeleton/protocols/maintain/prompts/audit.md +111 -0
  329. package/skeleton/protocols/maintain/prompts/clean.md +91 -0
  330. package/skeleton/protocols/maintain/prompts/sync.md +113 -0
  331. package/skeleton/protocols/maintain/prompts/verify.md +110 -0
  332. package/skeleton/protocols/maintain/protocol.json +141 -0
  333. package/skeleton/protocols/maintain/protocol.md +17 -11
  334. package/skeleton/protocols/protocol-schema.json +54 -1
  335. package/skeleton/protocols/spir/builder-prompt.md +66 -0
  336. package/skeleton/protocols/spir/prompts/implement.md +208 -0
  337. package/skeleton/protocols/{spider → spir}/prompts/plan.md +6 -70
  338. package/skeleton/protocols/{spider → spir}/prompts/review.md +20 -39
  339. package/skeleton/protocols/{spider → spir}/prompts/specify.md +24 -59
  340. package/skeleton/protocols/{spider → spir}/protocol.json +30 -10
  341. package/skeleton/protocols/{spider → spir}/protocol.md +35 -21
  342. package/skeleton/protocols/spir/templates/review.md +89 -0
  343. package/skeleton/protocols/tick/builder-prompt.md +56 -0
  344. package/skeleton/protocols/tick/protocol.json +7 -2
  345. package/skeleton/protocols/tick/protocol.md +18 -18
  346. package/skeleton/protocols/tick/templates/review.md +1 -1
  347. package/skeleton/resources/commands/agent-farm.md +63 -46
  348. package/skeleton/resources/commands/codev.md +0 -2
  349. package/skeleton/resources/commands/overview.md +7 -17
  350. package/skeleton/resources/workflow-reference.md +4 -4
  351. package/skeleton/roles/architect.md +151 -306
  352. package/skeleton/roles/builder.md +115 -332
  353. package/skeleton/roles/consultant.md +6 -6
  354. package/skeleton/templates/AGENTS.md +2 -2
  355. package/skeleton/templates/CLAUDE.md +2 -2
  356. package/skeleton/templates/cheatsheet.md +7 -5
  357. package/skeleton/templates/projectlist.md +1 -1
  358. package/templates/dashboard/index.html +17 -16
  359. package/templates/dashboard/js/dialogs.js +7 -7
  360. package/templates/dashboard/js/files.js +2 -2
  361. package/templates/dashboard/js/main.js +4 -4
  362. package/templates/dashboard/js/projects.js +3 -3
  363. package/templates/dashboard/js/tabs.js +1 -1
  364. package/templates/dashboard/js/utils.js +22 -1
  365. package/templates/open.html +26 -0
  366. package/templates/tower.html +731 -91
  367. package/dist/agent-farm/commands/kickoff.d.ts +0 -20
  368. package/dist/agent-farm/commands/kickoff.d.ts.map +0 -1
  369. package/dist/agent-farm/commands/kickoff.js +0 -273
  370. package/dist/agent-farm/commands/kickoff.js.map +0 -1
  371. package/dist/agent-farm/commands/rename.d.ts +0 -13
  372. package/dist/agent-farm/commands/rename.d.ts.map +0 -1
  373. package/dist/agent-farm/commands/rename.js +0 -33
  374. package/dist/agent-farm/commands/rename.js.map +0 -1
  375. package/dist/agent-farm/commands/tutorial.d.ts +0 -10
  376. package/dist/agent-farm/commands/tutorial.d.ts.map +0 -1
  377. package/dist/agent-farm/commands/tutorial.js +0 -49
  378. package/dist/agent-farm/commands/tutorial.js.map +0 -1
  379. package/dist/agent-farm/commands/util.d.ts +0 -15
  380. package/dist/agent-farm/commands/util.d.ts.map +0 -1
  381. package/dist/agent-farm/commands/util.js +0 -108
  382. package/dist/agent-farm/commands/util.js.map +0 -1
  383. package/dist/agent-farm/servers/dashboard-server.d.ts +0 -7
  384. package/dist/agent-farm/servers/dashboard-server.d.ts.map +0 -1
  385. package/dist/agent-farm/servers/dashboard-server.js +0 -1858
  386. package/dist/agent-farm/servers/dashboard-server.js.map +0 -1
  387. package/dist/agent-farm/servers/open-server.d.ts +0 -7
  388. package/dist/agent-farm/servers/open-server.d.ts.map +0 -1
  389. package/dist/agent-farm/servers/open-server.js +0 -315
  390. package/dist/agent-farm/servers/open-server.js.map +0 -1
  391. package/dist/agent-farm/tutorial/index.d.ts +0 -8
  392. package/dist/agent-farm/tutorial/index.d.ts.map +0 -1
  393. package/dist/agent-farm/tutorial/index.js +0 -8
  394. package/dist/agent-farm/tutorial/index.js.map +0 -1
  395. package/dist/agent-farm/tutorial/prompts.d.ts +0 -57
  396. package/dist/agent-farm/tutorial/prompts.d.ts.map +0 -1
  397. package/dist/agent-farm/tutorial/prompts.js +0 -147
  398. package/dist/agent-farm/tutorial/prompts.js.map +0 -1
  399. package/dist/agent-farm/tutorial/runner.d.ts +0 -52
  400. package/dist/agent-farm/tutorial/runner.d.ts.map +0 -1
  401. package/dist/agent-farm/tutorial/runner.js +0 -204
  402. package/dist/agent-farm/tutorial/runner.js.map +0 -1
  403. package/dist/agent-farm/tutorial/state.d.ts +0 -26
  404. package/dist/agent-farm/tutorial/state.d.ts.map +0 -1
  405. package/dist/agent-farm/tutorial/state.js +0 -89
  406. package/dist/agent-farm/tutorial/state.js.map +0 -1
  407. package/dist/agent-farm/tutorial/steps/first-spec.d.ts +0 -7
  408. package/dist/agent-farm/tutorial/steps/first-spec.d.ts.map +0 -1
  409. package/dist/agent-farm/tutorial/steps/first-spec.js +0 -136
  410. package/dist/agent-farm/tutorial/steps/first-spec.js.map +0 -1
  411. package/dist/agent-farm/tutorial/steps/implementation.d.ts +0 -7
  412. package/dist/agent-farm/tutorial/steps/implementation.d.ts.map +0 -1
  413. package/dist/agent-farm/tutorial/steps/implementation.js +0 -76
  414. package/dist/agent-farm/tutorial/steps/implementation.js.map +0 -1
  415. package/dist/agent-farm/tutorial/steps/index.d.ts +0 -10
  416. package/dist/agent-farm/tutorial/steps/index.d.ts.map +0 -1
  417. package/dist/agent-farm/tutorial/steps/index.js +0 -10
  418. package/dist/agent-farm/tutorial/steps/index.js.map +0 -1
  419. package/dist/agent-farm/tutorial/steps/planning.d.ts +0 -7
  420. package/dist/agent-farm/tutorial/steps/planning.d.ts.map +0 -1
  421. package/dist/agent-farm/tutorial/steps/planning.js +0 -143
  422. package/dist/agent-farm/tutorial/steps/planning.js.map +0 -1
  423. package/dist/agent-farm/tutorial/steps/review.d.ts +0 -7
  424. package/dist/agent-farm/tutorial/steps/review.d.ts.map +0 -1
  425. package/dist/agent-farm/tutorial/steps/review.js +0 -78
  426. package/dist/agent-farm/tutorial/steps/review.js.map +0 -1
  427. package/dist/agent-farm/tutorial/steps/setup.d.ts +0 -7
  428. package/dist/agent-farm/tutorial/steps/setup.d.ts.map +0 -1
  429. package/dist/agent-farm/tutorial/steps/setup.js +0 -126
  430. package/dist/agent-farm/tutorial/steps/setup.js.map +0 -1
  431. package/dist/agent-farm/tutorial/steps/welcome.d.ts +0 -7
  432. package/dist/agent-farm/tutorial/steps/welcome.d.ts.map +0 -1
  433. package/dist/agent-farm/tutorial/steps/welcome.js +0 -50
  434. package/dist/agent-farm/tutorial/steps/welcome.js.map +0 -1
  435. package/dist/agent-farm/utils/orphan-handler.d.ts +0 -27
  436. package/dist/agent-farm/utils/orphan-handler.d.ts.map +0 -1
  437. package/dist/agent-farm/utils/orphan-handler.js +0 -149
  438. package/dist/agent-farm/utils/orphan-handler.js.map +0 -1
  439. package/dist/agent-farm/utils/port-registry.d.ts +0 -58
  440. package/dist/agent-farm/utils/port-registry.d.ts.map +0 -1
  441. package/dist/agent-farm/utils/port-registry.js +0 -166
  442. package/dist/agent-farm/utils/port-registry.js.map +0 -1
  443. package/dist/agent-farm/utils/terminal-ports.d.ts +0 -18
  444. package/dist/agent-farm/utils/terminal-ports.d.ts.map +0 -1
  445. package/dist/agent-farm/utils/terminal-ports.js +0 -35
  446. package/dist/agent-farm/utils/terminal-ports.js.map +0 -1
  447. package/dist/commands/pcheck/cache.d.ts +0 -48
  448. package/dist/commands/pcheck/cache.d.ts.map +0 -1
  449. package/dist/commands/pcheck/cache.js +0 -170
  450. package/dist/commands/pcheck/cache.js.map +0 -1
  451. package/dist/commands/pcheck/evaluator.d.ts +0 -15
  452. package/dist/commands/pcheck/evaluator.d.ts.map +0 -1
  453. package/dist/commands/pcheck/evaluator.js +0 -246
  454. package/dist/commands/pcheck/evaluator.js.map +0 -1
  455. package/dist/commands/pcheck/index.d.ts +0 -12
  456. package/dist/commands/pcheck/index.d.ts.map +0 -1
  457. package/dist/commands/pcheck/index.js +0 -249
  458. package/dist/commands/pcheck/index.js.map +0 -1
  459. package/dist/commands/pcheck/parser.d.ts +0 -39
  460. package/dist/commands/pcheck/parser.d.ts.map +0 -1
  461. package/dist/commands/pcheck/parser.js +0 -155
  462. package/dist/commands/pcheck/parser.js.map +0 -1
  463. package/dist/commands/pcheck/types.d.ts +0 -82
  464. package/dist/commands/pcheck/types.d.ts.map +0 -1
  465. package/dist/commands/pcheck/types.js +0 -5
  466. package/dist/commands/pcheck/types.js.map +0 -1
  467. package/dist/commands/porch/claude.d.ts +0 -29
  468. package/dist/commands/porch/claude.d.ts.map +0 -1
  469. package/dist/commands/porch/claude.js +0 -79
  470. package/dist/commands/porch/claude.js.map +0 -1
  471. package/dist/commands/porch/consultation.d.ts +0 -56
  472. package/dist/commands/porch/consultation.d.ts.map +0 -1
  473. package/dist/commands/porch/consultation.js +0 -330
  474. package/dist/commands/porch/consultation.js.map +0 -1
  475. package/dist/commands/porch/notifications.d.ts +0 -99
  476. package/dist/commands/porch/notifications.d.ts.map +0 -1
  477. package/dist/commands/porch/notifications.js +0 -223
  478. package/dist/commands/porch/notifications.js.map +0 -1
  479. package/dist/commands/porch/plan-parser.d.ts +0 -38
  480. package/dist/commands/porch/plan-parser.d.ts.map +0 -1
  481. package/dist/commands/porch/plan-parser.js +0 -166
  482. package/dist/commands/porch/plan-parser.js.map +0 -1
  483. package/dist/commands/porch/protocol-loader.d.ts +0 -46
  484. package/dist/commands/porch/protocol-loader.d.ts.map +0 -1
  485. package/dist/commands/porch/protocol-loader.js +0 -262
  486. package/dist/commands/porch/protocol-loader.js.map +0 -1
  487. package/dist/commands/porch/repl.d.ts +0 -33
  488. package/dist/commands/porch/repl.d.ts.map +0 -1
  489. package/dist/commands/porch/repl.js +0 -206
  490. package/dist/commands/porch/repl.js.map +0 -1
  491. package/dist/commands/porch/run.d.ts +0 -15
  492. package/dist/commands/porch/run.d.ts.map +0 -1
  493. package/dist/commands/porch/run.js +0 -551
  494. package/dist/commands/porch/run.js.map +0 -1
  495. package/dist/commands/porch/signal-parser.d.ts +0 -102
  496. package/dist/commands/porch/signal-parser.d.ts.map +0 -1
  497. package/dist/commands/porch/signal-parser.js +0 -199
  498. package/dist/commands/porch/signal-parser.js.map +0 -1
  499. package/dist/commands/porch/signals.d.ts +0 -35
  500. package/dist/commands/porch/signals.d.ts.map +0 -1
  501. package/dist/commands/porch/signals.js +0 -76
  502. package/dist/commands/porch/signals.js.map +0 -1
  503. package/dist/commands/porch2/checks.d.ts +0 -29
  504. package/dist/commands/porch2/checks.d.ts.map +0 -1
  505. package/dist/commands/porch2/checks.js +0 -141
  506. package/dist/commands/porch2/checks.js.map +0 -1
  507. package/dist/commands/porch2/index.d.ts +0 -38
  508. package/dist/commands/porch2/index.d.ts.map +0 -1
  509. package/dist/commands/porch2/index.js +0 -483
  510. package/dist/commands/porch2/index.js.map +0 -1
  511. package/dist/commands/porch2/plan.d.ts +0 -70
  512. package/dist/commands/porch2/plan.d.ts.map +0 -1
  513. package/dist/commands/porch2/plan.js +0 -227
  514. package/dist/commands/porch2/plan.js.map +0 -1
  515. package/dist/commands/porch2/protocol.d.ts +0 -37
  516. package/dist/commands/porch2/protocol.d.ts.map +0 -1
  517. package/dist/commands/porch2/protocol.js +0 -183
  518. package/dist/commands/porch2/protocol.js.map +0 -1
  519. package/dist/commands/porch2/state.d.ts +0 -35
  520. package/dist/commands/porch2/state.d.ts.map +0 -1
  521. package/dist/commands/porch2/state.js +0 -124
  522. package/dist/commands/porch2/state.js.map +0 -1
  523. package/dist/commands/porch2/types.d.ts +0 -79
  524. package/dist/commands/porch2/types.d.ts.map +0 -1
  525. package/dist/commands/porch2/types.js +0 -8
  526. package/dist/commands/porch2/types.js.map +0 -1
  527. package/dist/commands/tower.d.ts +0 -16
  528. package/dist/commands/tower.d.ts.map +0 -1
  529. package/dist/commands/tower.js +0 -21
  530. package/dist/commands/tower.js.map +0 -1
  531. package/skeleton/config.json +0 -7
  532. package/skeleton/protocols/spider/prompts/defend.md +0 -215
  533. package/skeleton/protocols/spider/prompts/evaluate.md +0 -241
  534. package/skeleton/protocols/spider/prompts/implement.md +0 -149
  535. package/skeleton/protocols/spider/templates/review.md +0 -207
  536. /package/skeleton/protocols/{spider → spir}/templates/plan.md +0 -0
  537. /package/skeleton/protocols/{spider → spir}/templates/spec.md +0 -0
@@ -1,23 +1,33 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * Tower server for Agent Farm.
4
- * Provides a centralized view of all agent-farm instances across projects.
3
+ * Tower server for Agent Farm — orchestrator module.
4
+ * Spec 0105: Tower Server Decomposition
5
+ *
6
+ * Creates HTTP/WS servers, initializes all subsystem modules, and
7
+ * delegates HTTP request handling to tower-routes.ts.
5
8
  */
6
9
  import http from 'node:http';
7
10
  import fs from 'node:fs';
8
11
  import path from 'node:path';
9
- import net from 'node:net';
10
- import { spawn, execSync } from 'node:child_process';
11
12
  import { homedir } from 'node:os';
12
13
  import { fileURLToPath } from 'node:url';
13
14
  import { Command } from 'commander';
14
- import { getGlobalDb } from '../db/index.js';
15
- import { cleanupStaleEntries } from '../utils/port-registry.js';
16
- import { parseJsonBody, isRequestAllowed } from '../utils/server-utils.js';
15
+ import { WebSocketServer } from 'ws';
16
+ import { SessionManager } from '../../terminal/session-manager.js';
17
+ import { startRateLimitCleanup } from './tower-utils.js';
18
+ import { initTunnel, shutdownTunnel, } from './tower-tunnel.js';
19
+ import { initInstances, shutdownInstances, registerKnownProject, getKnownProjectPaths, getInstances, } from './tower-instances.js';
20
+ import { initTerminals, shutdownTerminals, getProjectTerminals, getTerminalManager, getProjectTerminalsEntry, saveTerminalSession, deleteTerminalSession, deleteProjectTerminalSessions, getTerminalsForProject, reconcileTerminalSessions, startGateWatcher, } from './tower-terminals.js';
21
+ import { setupUpgradeHandler, } from './tower-websocket.js';
22
+ import { handleRequest } from './tower-routes.js';
17
23
  const __filename = fileURLToPath(import.meta.url);
18
24
  const __dirname = path.dirname(__filename);
19
25
  // Default port for tower dashboard
20
26
  const DEFAULT_PORT = 4100;
27
+ // Rate limiting: cleanup interval for token bucket
28
+ const rateLimitCleanupInterval = startRateLimitCleanup();
29
+ // Shellper session manager (initialized at startup)
30
+ let shellperManager = null;
21
31
  // Parse arguments with Commander
22
32
  const program = new Command()
23
33
  .name('tower-server')
@@ -52,311 +62,74 @@ function log(level, message) {
52
62
  }
53
63
  }
54
64
  }
65
+ // Global exception handlers to catch uncaught errors
66
+ process.on('uncaughtException', (err) => {
67
+ log('ERROR', `Uncaught exception: ${err.message}\n${err.stack}`);
68
+ process.exit(1);
69
+ });
70
+ process.on('unhandledRejection', (reason) => {
71
+ const message = reason instanceof Error ? `${reason.message}\n${reason.stack}` : String(reason);
72
+ log('ERROR', `Unhandled rejection: ${message}`);
73
+ process.exit(1);
74
+ });
75
+ // Graceful shutdown handler (Phase 2 - Spec 0090)
76
+ async function gracefulShutdown(signal) {
77
+ log('INFO', `Received ${signal}, starting graceful shutdown...`);
78
+ // 1. Stop accepting new connections
79
+ server?.close();
80
+ // 2. Close all WebSocket connections
81
+ if (terminalWss) {
82
+ for (const client of terminalWss.clients) {
83
+ client.close(1001, 'Server shutting down');
84
+ }
85
+ terminalWss.close();
86
+ }
87
+ // 3. Shellper clients: do NOT call shellperManager.shutdown() here.
88
+ // SessionManager.shutdown() disconnects sockets, which triggers ShellperClient
89
+ // 'close' events → PtySession exit(-1) → SQLite row deletion. This would erase
90
+ // the rows that reconcileTerminalSessions() needs on restart.
91
+ // Instead, let the process exit naturally — OS closes all sockets, and shellpers
92
+ // detect the disconnection and keep running. SQLite rows are preserved.
93
+ if (shellperManager) {
94
+ log('INFO', 'Shellper sessions will continue running (sockets close on process exit)');
95
+ }
96
+ // 4. Stop rate limit cleanup
97
+ clearInterval(rateLimitCleanupInterval);
98
+ // 5. Disconnect tunnel (Spec 0097 Phase 4 / Spec 0105 Phase 2)
99
+ shutdownTunnel();
100
+ // 6. Tear down instance module (Spec 0105 Phase 3)
101
+ shutdownInstances();
102
+ // 7. Tear down terminal module (Spec 0105 Phase 4) — stops gate watcher, shuts down terminal manager
103
+ shutdownTerminals();
104
+ log('INFO', 'Graceful shutdown complete');
105
+ process.exit(0);
106
+ }
107
+ // Catch signals for clean shutdown
108
+ process.on('SIGINT', () => gracefulShutdown('SIGINT'));
109
+ process.on('SIGTERM', () => gracefulShutdown('SIGTERM'));
55
110
  if (isNaN(port) || port < 1 || port > 65535) {
56
111
  log('ERROR', `Invalid port "${portArg}". Must be a number between 1 and 65535.`);
57
112
  process.exit(1);
58
113
  }
59
114
  log('INFO', `Tower server starting on port ${port}`);
115
+ // SSE (Server-Sent Events) infrastructure for push notifications
116
+ const sseClients = [];
117
+ let notificationIdCounter = 0;
60
118
  /**
61
- * Load port allocations from SQLite database
62
- */
63
- function loadPortAllocations() {
64
- try {
65
- const db = getGlobalDb();
66
- return db.prepare('SELECT * FROM port_allocations ORDER BY last_used_at DESC').all();
67
- }
68
- catch (err) {
69
- log('ERROR', `Error loading port allocations: ${err.message}`);
70
- return [];
71
- }
72
- }
73
- /**
74
- * Check if a port is listening
75
- */
76
- async function isPortListening(port) {
77
- return new Promise((resolve) => {
78
- const socket = new net.Socket();
79
- socket.setTimeout(1000);
80
- socket.on('connect', () => {
81
- socket.destroy();
82
- resolve(true);
83
- });
84
- socket.on('timeout', () => {
85
- socket.destroy();
86
- resolve(false);
87
- });
88
- socket.on('error', () => {
89
- resolve(false);
90
- });
91
- socket.connect(port, '127.0.0.1');
92
- });
93
- }
94
- /**
95
- * Get project name from path
96
- */
97
- function getProjectName(projectPath) {
98
- return path.basename(projectPath);
99
- }
100
- /**
101
- * Get all instances with their status
102
- */
103
- async function getInstances() {
104
- const allocations = loadPortAllocations();
105
- const instances = [];
106
- for (const allocation of allocations) {
107
- // Skip builder worktrees - they're managed by their parent project
108
- if (allocation.project_path.includes('/.builders/')) {
109
- continue;
110
- }
111
- const basePort = allocation.base_port;
112
- const dashboardPort = basePort;
113
- const architectPort = basePort + 1;
114
- // Check if dashboard is running (main indicator of running instance)
115
- const dashboardActive = await isPortListening(dashboardPort);
116
- // Only check architect port if dashboard is active (to avoid unnecessary probing)
117
- const architectActive = dashboardActive ? await isPortListening(architectPort) : false;
118
- const ports = [
119
- {
120
- type: 'Dashboard',
121
- port: dashboardPort,
122
- url: `http://localhost:${dashboardPort}`,
123
- active: dashboardActive,
124
- },
125
- {
126
- type: 'Architect',
127
- port: architectPort,
128
- url: `http://localhost:${architectPort}`,
129
- active: architectActive,
130
- },
131
- ];
132
- instances.push({
133
- projectPath: allocation.project_path,
134
- projectName: getProjectName(allocation.project_path),
135
- basePort,
136
- dashboardPort,
137
- architectPort,
138
- registered: allocation.registered_at,
139
- lastUsed: allocation.last_used_at,
140
- running: dashboardActive,
141
- ports,
142
- });
143
- }
144
- // Sort: running first, then by last used (most recent first)
145
- instances.sort((a, b) => {
146
- if (a.running !== b.running) {
147
- return a.running ? -1 : 1;
148
- }
149
- const aTime = a.lastUsed ? new Date(a.lastUsed).getTime() : 0;
150
- const bTime = b.lastUsed ? new Date(b.lastUsed).getTime() : 0;
151
- return bTime - aTime;
152
- });
153
- return instances;
154
- }
155
- /**
156
- * Get directory suggestions for autocomplete
157
- */
158
- async function getDirectorySuggestions(inputPath) {
159
- // Default to home directory if empty
160
- if (!inputPath) {
161
- inputPath = homedir();
162
- }
163
- // Expand ~ to home directory
164
- if (inputPath.startsWith('~')) {
165
- inputPath = inputPath.replace('~', homedir());
166
- }
167
- // Determine the directory to list and the prefix to filter by
168
- let dirToList;
169
- let prefix;
170
- if (inputPath.endsWith('/')) {
171
- // User typed a complete directory path, list its contents
172
- dirToList = inputPath;
173
- prefix = '';
174
- }
175
- else {
176
- // User is typing a partial name, list parent and filter
177
- dirToList = path.dirname(inputPath);
178
- prefix = path.basename(inputPath).toLowerCase();
179
- }
180
- // Check if directory exists
181
- if (!fs.existsSync(dirToList)) {
182
- return [];
183
- }
184
- const stat = fs.statSync(dirToList);
185
- if (!stat.isDirectory()) {
186
- return [];
187
- }
188
- // Read directory contents
189
- const entries = fs.readdirSync(dirToList, { withFileTypes: true });
190
- // Filter to directories only, apply prefix filter, and check for codev/
191
- const suggestions = [];
192
- for (const entry of entries) {
193
- if (!entry.isDirectory())
194
- continue;
195
- if (entry.name.startsWith('.'))
196
- continue; // Skip hidden directories
197
- const name = entry.name.toLowerCase();
198
- if (prefix && !name.startsWith(prefix))
199
- continue;
200
- const fullPath = path.join(dirToList, entry.name);
201
- const isProject = fs.existsSync(path.join(fullPath, 'codev'));
202
- suggestions.push({ path: fullPath, isProject });
203
- }
204
- // Sort: projects first, then alphabetically
205
- suggestions.sort((a, b) => {
206
- if (a.isProject !== b.isProject) {
207
- return a.isProject ? -1 : 1;
208
- }
209
- return a.path.localeCompare(b.path);
210
- });
211
- // Limit to 20 suggestions
212
- return suggestions.slice(0, 20);
213
- }
214
- /**
215
- * Launch a new agent-farm instance
216
- * First stops any stale state, then starts fresh
217
- * Auto-adopts non-codev directories
119
+ * Broadcast a notification to all connected SSE clients
218
120
  */
219
- async function launchInstance(projectPath) {
220
- // Clean up stale port allocations before launching (handles machine restarts)
221
- cleanupStaleEntries();
222
- // Validate path exists
223
- if (!fs.existsSync(projectPath)) {
224
- return { success: false, error: `Path does not exist: ${projectPath}` };
225
- }
226
- // Validate it's a directory
227
- const stat = fs.statSync(projectPath);
228
- if (!stat.isDirectory()) {
229
- return { success: false, error: `Not a directory: ${projectPath}` };
230
- }
231
- // Auto-adopt non-codev directories
232
- const codevDir = path.join(projectPath, 'codev');
233
- let adopted = false;
234
- if (!fs.existsSync(codevDir)) {
121
+ function broadcastNotification(notification) {
122
+ const id = ++notificationIdCounter;
123
+ const data = JSON.stringify({ ...notification, id });
124
+ const message = `id: ${id}\ndata: ${data}\n\n`;
125
+ for (const client of sseClients) {
235
126
  try {
236
- // Run codev adopt --yes to set up the project
237
- execSync('npx codev adopt --yes', {
238
- cwd: projectPath,
239
- stdio: 'pipe',
240
- timeout: 30000,
241
- });
242
- adopted = true;
243
- log('INFO', `Auto-adopted codev in: ${projectPath}`);
244
- }
245
- catch (err) {
246
- return { success: false, error: `Failed to adopt codev: ${err.message}` };
247
- }
248
- }
249
- // Use codev af command (avoids npx cache issues)
250
- // Falls back to npx codev af if codev not in PATH
251
- // SECURITY: Use spawn with cwd option to avoid command injection
252
- // Do NOT use bash -c with string concatenation
253
- try {
254
- // First, stop any existing (possibly stale) instance
255
- const stopChild = spawn('codev', ['af', 'stop'], {
256
- cwd: projectPath,
257
- stdio: 'ignore',
258
- });
259
- // Wait for stop to complete
260
- await new Promise((resolve) => {
261
- stopChild.on('close', () => resolve());
262
- stopChild.on('error', () => resolve());
263
- // Timeout after 3 seconds
264
- setTimeout(() => resolve(), 3000);
265
- });
266
- // Small delay to ensure cleanup
267
- await new Promise((resolve) => setTimeout(resolve, 500));
268
- // Now start using codev af (avoids npx caching issues)
269
- // Capture output to detect errors
270
- const child = spawn('codev', ['af', 'start'], {
271
- detached: true,
272
- stdio: ['ignore', 'pipe', 'pipe'],
273
- cwd: projectPath,
274
- });
275
- let stdout = '';
276
- let stderr = '';
277
- child.stdout?.on('data', (data) => {
278
- stdout += data.toString();
279
- });
280
- child.stderr?.on('data', (data) => {
281
- stderr += data.toString();
282
- });
283
- // Wait a moment for the process to start (or fail)
284
- await new Promise((resolve) => setTimeout(resolve, 2000));
285
- // Check if the dashboard port is listening
286
- // Resolve symlinks (macOS /tmp -> /private/tmp)
287
- const resolvedPath = fs.realpathSync(projectPath);
288
- const db = getGlobalDb();
289
- const allocation = db
290
- .prepare('SELECT base_port FROM port_allocations WHERE project_path = ? OR project_path = ?')
291
- .get(projectPath, resolvedPath);
292
- if (allocation) {
293
- const dashboardPort = allocation.base_port;
294
- const isRunning = await isPortListening(dashboardPort);
295
- if (!isRunning) {
296
- // Process failed to start - try to get error info
297
- const errorInfo = stderr || stdout || 'Unknown error - check codev installation';
298
- child.unref();
299
- return {
300
- success: false,
301
- error: `Failed to start: ${errorInfo.trim().split('\n')[0]}`,
302
- };
303
- }
127
+ client.res.write(message);
304
128
  }
305
- else {
306
- // No allocation found - process might have failed before registering
307
- if (stderr || stdout) {
308
- const errorInfo = stderr || stdout;
309
- child.unref();
310
- return {
311
- success: false,
312
- error: `Failed to start: ${errorInfo.trim().split('\n')[0]}`,
313
- };
314
- }
315
- }
316
- child.unref();
317
- return { success: true, adopted };
318
- }
319
- catch (err) {
320
- return { success: false, error: `Failed to launch: ${err.message}` };
321
- }
322
- }
323
- /**
324
- * Get PID of process listening on a port
325
- */
326
- function getProcessOnPort(targetPort) {
327
- try {
328
- const result = execSync(`lsof -ti :${targetPort} 2>/dev/null`, { encoding: 'utf-8' });
329
- const pid = parseInt(result.trim().split('\n')[0], 10);
330
- return isNaN(pid) ? null : pid;
331
- }
332
- catch {
333
- return null;
334
- }
335
- }
336
- /**
337
- * Stop an agent-farm instance by killing processes on its ports
338
- */
339
- async function stopInstance(basePort) {
340
- const stopped = [];
341
- // Kill processes on the main port range (dashboard, architect, builders)
342
- // Dashboard is basePort, architect is basePort+1, builders start at basePort+100
343
- const portsToCheck = [basePort, basePort + 1];
344
- for (const p of portsToCheck) {
345
- const pid = getProcessOnPort(p);
346
- if (pid) {
347
- try {
348
- process.kill(pid, 'SIGTERM');
349
- stopped.push(p);
350
- }
351
- catch {
352
- // Process may have already exited
353
- }
129
+ catch {
130
+ // Client disconnected, will be cleaned up on next iteration
354
131
  }
355
132
  }
356
- if (stopped.length === 0) {
357
- return { success: true, error: 'No processes found to stop', stopped };
358
- }
359
- return { success: true, stopped };
360
133
  }
361
134
  /**
362
135
  * Find the tower template
@@ -372,185 +145,94 @@ function findTemplatePath() {
372
145
  }
373
146
  return null;
374
147
  }
375
- // escapeHtml, parseJsonBody, isRequestAllowed imported from ../utils/server-utils.js
376
148
  // Find template path
377
149
  const templatePath = findTemplatePath();
378
- // Create server
150
+ // WebSocket server for terminal connections (Phase 2 - Spec 0090)
151
+ let terminalWss = null;
152
+ // React dashboard dist path (for serving directly from tower)
153
+ // Phase 4 (Spec 0090): Tower serves everything directly, no dashboard-server
154
+ const reactDashboardPath = path.resolve(__dirname, '../../../dashboard/dist');
155
+ const hasReactDashboard = fs.existsSync(reactDashboardPath);
156
+ if (hasReactDashboard) {
157
+ log('INFO', `React dashboard found at: ${reactDashboardPath}`);
158
+ }
159
+ else {
160
+ log('WARN', 'React dashboard not found - project dashboards will not work');
161
+ }
162
+ // ============================================================================
163
+ // Route context — wires orchestrator state into route handlers
164
+ // ============================================================================
165
+ const routeCtx = {
166
+ log,
167
+ port,
168
+ templatePath,
169
+ reactDashboardPath,
170
+ hasReactDashboard,
171
+ getShellperManager: () => shellperManager,
172
+ broadcastNotification,
173
+ addSseClient: (client) => {
174
+ sseClients.push(client);
175
+ },
176
+ removeSseClient: (id) => {
177
+ const index = sseClients.findIndex(c => c.id === id);
178
+ if (index !== -1) {
179
+ sseClients.splice(index, 1);
180
+ }
181
+ },
182
+ };
183
+ // ============================================================================
184
+ // Create server — delegates all HTTP handling to tower-routes.ts
185
+ // ============================================================================
379
186
  const server = http.createServer(async (req, res) => {
380
- // Security: Validate Host and Origin headers
381
- if (!isRequestAllowed(req)) {
382
- res.writeHead(403, { 'Content-Type': 'text/plain' });
383
- res.end('Forbidden');
384
- return;
385
- }
386
- // CORS headers
387
- const origin = req.headers.origin;
388
- if (origin && (origin.startsWith('http://localhost:') || origin.startsWith('http://127.0.0.1:'))) {
389
- res.setHeader('Access-Control-Allow-Origin', origin);
390
- }
391
- res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
392
- res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
393
- res.setHeader('Cache-Control', 'no-store');
394
- if (req.method === 'OPTIONS') {
395
- res.writeHead(200);
396
- res.end();
397
- return;
398
- }
399
- const url = new URL(req.url || '/', `http://localhost:${port}`);
400
- try {
401
- // API: Get status of all instances
402
- if (req.method === 'GET' && url.pathname === '/api/status') {
403
- const instances = await getInstances();
404
- res.writeHead(200, { 'Content-Type': 'application/json' });
405
- res.end(JSON.stringify({ instances }));
406
- return;
407
- }
408
- // API: Browse directories for autocomplete
409
- if (req.method === 'GET' && url.pathname === '/api/browse') {
410
- const inputPath = url.searchParams.get('path') || '';
411
- try {
412
- const suggestions = await getDirectorySuggestions(inputPath);
413
- res.writeHead(200, { 'Content-Type': 'application/json' });
414
- res.end(JSON.stringify({ suggestions }));
415
- }
416
- catch (err) {
417
- res.writeHead(200, { 'Content-Type': 'application/json' });
418
- res.end(JSON.stringify({ suggestions: [], error: err.message }));
419
- }
420
- return;
421
- }
422
- // API: Create new project
423
- if (req.method === 'POST' && url.pathname === '/api/create') {
424
- const body = await parseJsonBody(req);
425
- const parentPath = body.parent;
426
- const projectName = body.name;
427
- if (!parentPath || !projectName) {
428
- res.writeHead(400, { 'Content-Type': 'application/json' });
429
- res.end(JSON.stringify({ success: false, error: 'Missing parent or name' }));
430
- return;
431
- }
432
- // Validate project name
433
- if (!/^[a-zA-Z0-9_-]+$/.test(projectName)) {
434
- res.writeHead(400, { 'Content-Type': 'application/json' });
435
- res.end(JSON.stringify({ success: false, error: 'Invalid project name' }));
436
- return;
437
- }
438
- // Expand ~ to home directory
439
- let expandedParent = parentPath;
440
- if (expandedParent.startsWith('~')) {
441
- expandedParent = expandedParent.replace('~', homedir());
442
- }
443
- // Validate parent exists
444
- if (!fs.existsSync(expandedParent)) {
445
- res.writeHead(400, { 'Content-Type': 'application/json' });
446
- res.end(JSON.stringify({ success: false, error: `Parent directory does not exist: ${parentPath}` }));
447
- return;
448
- }
449
- const projectPath = path.join(expandedParent, projectName);
450
- // Check if project already exists
451
- if (fs.existsSync(projectPath)) {
452
- res.writeHead(400, { 'Content-Type': 'application/json' });
453
- res.end(JSON.stringify({ success: false, error: `Directory already exists: ${projectPath}` }));
454
- return;
455
- }
456
- try {
457
- // Run codev init (it creates the directory)
458
- execSync(`codev init --yes "${projectName}"`, {
459
- cwd: expandedParent,
460
- stdio: 'pipe',
461
- timeout: 60000,
462
- });
463
- // Launch the instance
464
- const launchResult = await launchInstance(projectPath);
465
- if (!launchResult.success) {
466
- res.writeHead(500, { 'Content-Type': 'application/json' });
467
- res.end(JSON.stringify({ success: false, error: launchResult.error }));
468
- return;
469
- }
470
- res.writeHead(200, { 'Content-Type': 'application/json' });
471
- res.end(JSON.stringify({ success: true, projectPath }));
472
- }
473
- catch (err) {
474
- // Clean up on failure
475
- try {
476
- if (fs.existsSync(projectPath)) {
477
- fs.rmSync(projectPath, { recursive: true });
478
- }
479
- }
480
- catch {
481
- // Ignore cleanup errors
482
- }
483
- res.writeHead(500, { 'Content-Type': 'application/json' });
484
- res.end(JSON.stringify({ success: false, error: `Failed to create project: ${err.message}` }));
485
- }
486
- return;
487
- }
488
- // API: Launch new instance
489
- if (req.method === 'POST' && url.pathname === '/api/launch') {
490
- const body = await parseJsonBody(req);
491
- const projectPath = body.projectPath;
492
- if (!projectPath) {
493
- res.writeHead(400, { 'Content-Type': 'application/json' });
494
- res.end(JSON.stringify({ success: false, error: 'Missing projectPath' }));
495
- return;
496
- }
497
- const result = await launchInstance(projectPath);
498
- res.writeHead(result.success ? 200 : 400, { 'Content-Type': 'application/json' });
499
- res.end(JSON.stringify(result));
500
- return;
501
- }
502
- // API: Stop an instance
503
- if (req.method === 'POST' && url.pathname === '/api/stop') {
504
- const body = await parseJsonBody(req);
505
- const basePort = body.basePort;
506
- if (!basePort) {
507
- res.writeHead(400, { 'Content-Type': 'application/json' });
508
- res.end(JSON.stringify({ success: false, error: 'Missing basePort' }));
509
- return;
510
- }
511
- const result = await stopInstance(basePort);
512
- res.writeHead(200, { 'Content-Type': 'application/json' });
513
- res.end(JSON.stringify(result));
514
- return;
515
- }
516
- // Serve dashboard
517
- if (req.method === 'GET' && (url.pathname === '/' || url.pathname === '/index.html')) {
518
- if (!templatePath) {
519
- res.writeHead(500, { 'Content-Type': 'text/plain' });
520
- res.end('Template not found. Make sure tower.html exists in agent-farm/templates/');
521
- return;
522
- }
523
- try {
524
- const template = fs.readFileSync(templatePath, 'utf-8');
525
- res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
526
- res.end(template);
527
- }
528
- catch (err) {
529
- res.writeHead(500, { 'Content-Type': 'text/plain' });
530
- res.end('Error loading template: ' + err.message);
531
- }
532
- return;
533
- }
534
- // 404 for everything else
535
- res.writeHead(404, { 'Content-Type': 'text/plain' });
536
- res.end('Not found');
537
- }
538
- catch (err) {
539
- log('ERROR', `Request error: ${err.message}`);
540
- res.writeHead(500, { 'Content-Type': 'text/plain' });
541
- res.end('Internal server error: ' + err.message);
542
- }
187
+ await handleRequest(req, res, routeCtx);
543
188
  });
544
189
  // SECURITY: Bind to localhost only to prevent network exposure
545
- server.listen(port, '127.0.0.1', () => {
190
+ server.listen(port, '127.0.0.1', async () => {
546
191
  log('INFO', `Tower server listening at http://localhost:${port}`);
192
+ // Initialize shellper session manager for persistent terminals
193
+ const socketDir = path.join(homedir(), '.codev', 'run');
194
+ const shellperScript = path.join(__dirname, '..', '..', 'terminal', 'shellper-main.js');
195
+ shellperManager = new SessionManager({
196
+ socketDir,
197
+ shellperScript,
198
+ nodeExecutable: process.execPath,
199
+ });
200
+ const staleCleaned = await shellperManager.cleanupStaleSockets();
201
+ if (staleCleaned > 0) {
202
+ log('INFO', `Cleaned up ${staleCleaned} stale shellper socket(s)`);
203
+ }
204
+ log('INFO', 'Shellper session manager initialized');
205
+ // Spec 0105 Phase 4: Initialize terminal management module
206
+ initTerminals({
207
+ log,
208
+ shellperManager,
209
+ registerKnownProject,
210
+ getKnownProjectPaths,
211
+ });
212
+ // Spec 0105 Phase 3: Initialize instance lifecycle module
213
+ // Must be before reconcileTerminalSessions() so instance APIs are available
214
+ // as soon as the server starts accepting requests.
215
+ initInstances({
216
+ log,
217
+ projectTerminals: getProjectTerminals(),
218
+ getTerminalManager,
219
+ shellperManager,
220
+ getProjectTerminalsEntry,
221
+ saveTerminalSession,
222
+ deleteTerminalSession,
223
+ deleteProjectTerminalSessions,
224
+ getTerminalsForProject,
225
+ });
226
+ // TICK-001: Reconcile terminal sessions from previous run
227
+ await reconcileTerminalSessions();
228
+ // Spec 0100: Start background gate watcher for af send notifications
229
+ startGateWatcher();
230
+ log('INFO', 'Gate watcher started (10s poll interval)');
231
+ // Spec 0097 Phase 4 / Spec 0105 Phase 2: Initialize cloud tunnel
232
+ await initTunnel({ port, log, projectTerminals: getProjectTerminals(), terminalManager: getTerminalManager() }, { getInstances });
547
233
  });
548
- // Handle uncaught errors
549
- process.on('uncaughtException', (err) => {
550
- log('ERROR', `Uncaught exception: ${err.message}\n${err.stack}`);
551
- process.exit(1);
552
- });
553
- process.on('unhandledRejection', (reason) => {
554
- log('ERROR', `Unhandled rejection: ${reason}`);
555
- });
234
+ // Initialize terminal WebSocket server (Phase 2 - Spec 0090)
235
+ terminalWss = new WebSocketServer({ noServer: true });
236
+ // Spec 0105 Phase 5: WebSocket upgrade handler extracted to tower-websocket.ts
237
+ setupUpgradeHandler(server, terminalWss, port);
556
238
  //# sourceMappingURL=tower-server.js.map