@cluesmith/codev 2.0.0-rc.7 → 2.0.0-rc.71

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 (456) hide show
  1. package/bin/af.js +2 -2
  2. package/bin/consult.js +1 -1
  3. package/bin/porch.js +6 -35
  4. package/dashboard/dist/assets/index-C7FtNK6Y.css +32 -0
  5. package/dashboard/dist/assets/index-CDAINZKT.js +131 -0
  6. package/dashboard/dist/assets/index-CDAINZKT.js.map +1 -0
  7. package/dashboard/dist/index.html +14 -0
  8. package/dist/agent-farm/cli.d.ts.map +1 -1
  9. package/dist/agent-farm/cli.js +173 -118
  10. package/dist/agent-farm/cli.js.map +1 -1
  11. package/dist/agent-farm/commands/architect.d.ts +3 -3
  12. package/dist/agent-farm/commands/architect.d.ts.map +1 -1
  13. package/dist/agent-farm/commands/architect.js +20 -147
  14. package/dist/agent-farm/commands/architect.js.map +1 -1
  15. package/dist/agent-farm/commands/attach.d.ts +13 -0
  16. package/dist/agent-farm/commands/attach.d.ts.map +1 -0
  17. package/dist/agent-farm/commands/attach.js +144 -0
  18. package/dist/agent-farm/commands/attach.js.map +1 -0
  19. package/dist/agent-farm/commands/cleanup.d.ts.map +1 -1
  20. package/dist/agent-farm/commands/cleanup.js +35 -19
  21. package/dist/agent-farm/commands/cleanup.js.map +1 -1
  22. package/dist/agent-farm/commands/consult.d.ts +3 -4
  23. package/dist/agent-farm/commands/consult.d.ts.map +1 -1
  24. package/dist/agent-farm/commands/consult.js +27 -37
  25. package/dist/agent-farm/commands/consult.js.map +1 -1
  26. package/dist/agent-farm/commands/index.d.ts +2 -2
  27. package/dist/agent-farm/commands/index.d.ts.map +1 -1
  28. package/dist/agent-farm/commands/index.js +2 -2
  29. package/dist/agent-farm/commands/index.js.map +1 -1
  30. package/dist/agent-farm/commands/open.d.ts +4 -2
  31. package/dist/agent-farm/commands/open.d.ts.map +1 -1
  32. package/dist/agent-farm/commands/open.js +33 -83
  33. package/dist/agent-farm/commands/open.js.map +1 -1
  34. package/dist/agent-farm/commands/send.d.ts +1 -1
  35. package/dist/agent-farm/commands/send.d.ts.map +1 -1
  36. package/dist/agent-farm/commands/send.js +60 -79
  37. package/dist/agent-farm/commands/send.js.map +1 -1
  38. package/dist/agent-farm/commands/shell.d.ts +15 -0
  39. package/dist/agent-farm/commands/shell.d.ts.map +1 -0
  40. package/dist/agent-farm/commands/shell.js +50 -0
  41. package/dist/agent-farm/commands/shell.js.map +1 -0
  42. package/dist/agent-farm/commands/spawn.d.ts.map +1 -1
  43. package/dist/agent-farm/commands/spawn.js +597 -281
  44. package/dist/agent-farm/commands/spawn.js.map +1 -1
  45. package/dist/agent-farm/commands/start.d.ts +10 -20
  46. package/dist/agent-farm/commands/start.d.ts.map +1 -1
  47. package/dist/agent-farm/commands/start.js +45 -491
  48. package/dist/agent-farm/commands/start.js.map +1 -1
  49. package/dist/agent-farm/commands/status.d.ts +2 -0
  50. package/dist/agent-farm/commands/status.d.ts.map +1 -1
  51. package/dist/agent-farm/commands/status.js +75 -24
  52. package/dist/agent-farm/commands/status.js.map +1 -1
  53. package/dist/agent-farm/commands/stop.d.ts +6 -0
  54. package/dist/agent-farm/commands/stop.d.ts.map +1 -1
  55. package/dist/agent-farm/commands/stop.js +49 -109
  56. package/dist/agent-farm/commands/stop.js.map +1 -1
  57. package/dist/agent-farm/commands/tower-cloud.d.ts +48 -0
  58. package/dist/agent-farm/commands/tower-cloud.d.ts.map +1 -0
  59. package/dist/agent-farm/commands/tower-cloud.js +329 -0
  60. package/dist/agent-farm/commands/tower-cloud.js.map +1 -0
  61. package/dist/agent-farm/commands/tower.d.ts +9 -0
  62. package/dist/agent-farm/commands/tower.d.ts.map +1 -1
  63. package/dist/agent-farm/commands/tower.js +59 -19
  64. package/dist/agent-farm/commands/tower.js.map +1 -1
  65. package/dist/agent-farm/db/index.d.ts +6 -2
  66. package/dist/agent-farm/db/index.d.ts.map +1 -1
  67. package/dist/agent-farm/db/index.js +246 -18
  68. package/dist/agent-farm/db/index.js.map +1 -1
  69. package/dist/agent-farm/db/migrate.d.ts +0 -4
  70. package/dist/agent-farm/db/migrate.d.ts.map +1 -1
  71. package/dist/agent-farm/db/migrate.js +6 -55
  72. package/dist/agent-farm/db/migrate.js.map +1 -1
  73. package/dist/agent-farm/db/schema.d.ts +3 -3
  74. package/dist/agent-farm/db/schema.d.ts.map +1 -1
  75. package/dist/agent-farm/db/schema.js +25 -19
  76. package/dist/agent-farm/db/schema.js.map +1 -1
  77. package/dist/agent-farm/db/types.d.ts +3 -13
  78. package/dist/agent-farm/db/types.d.ts.map +1 -1
  79. package/dist/agent-farm/db/types.js +3 -11
  80. package/dist/agent-farm/db/types.js.map +1 -1
  81. package/dist/agent-farm/hq-connector.d.ts +2 -6
  82. package/dist/agent-farm/hq-connector.d.ts.map +1 -1
  83. package/dist/agent-farm/hq-connector.js +2 -17
  84. package/dist/agent-farm/hq-connector.js.map +1 -1
  85. package/dist/agent-farm/lib/cloud-config.d.ts +59 -0
  86. package/dist/agent-farm/lib/cloud-config.d.ts.map +1 -0
  87. package/dist/agent-farm/lib/cloud-config.js +143 -0
  88. package/dist/agent-farm/lib/cloud-config.js.map +1 -0
  89. package/dist/agent-farm/lib/tower-client.d.ts +163 -0
  90. package/dist/agent-farm/lib/tower-client.d.ts.map +1 -0
  91. package/dist/agent-farm/lib/tower-client.js +233 -0
  92. package/dist/agent-farm/lib/tower-client.js.map +1 -0
  93. package/dist/agent-farm/lib/tunnel-client.d.ts +117 -0
  94. package/dist/agent-farm/lib/tunnel-client.d.ts.map +1 -0
  95. package/dist/agent-farm/lib/tunnel-client.js +504 -0
  96. package/dist/agent-farm/lib/tunnel-client.js.map +1 -0
  97. package/dist/agent-farm/servers/tower-server.js +2653 -185
  98. package/dist/agent-farm/servers/tower-server.js.map +1 -1
  99. package/dist/agent-farm/state.d.ts +6 -12
  100. package/dist/agent-farm/state.d.ts.map +1 -1
  101. package/dist/agent-farm/state.js +34 -49
  102. package/dist/agent-farm/state.js.map +1 -1
  103. package/dist/agent-farm/types.d.ts +49 -26
  104. package/dist/agent-farm/types.d.ts.map +1 -1
  105. package/dist/agent-farm/utils/config.d.ts +0 -5
  106. package/dist/agent-farm/utils/config.d.ts.map +1 -1
  107. package/dist/agent-farm/utils/config.js +12 -44
  108. package/dist/agent-farm/utils/config.js.map +1 -1
  109. package/dist/agent-farm/utils/deps.d.ts.map +1 -1
  110. package/dist/agent-farm/utils/deps.js +0 -32
  111. package/dist/agent-farm/utils/deps.js.map +1 -1
  112. package/dist/agent-farm/utils/file-tabs.d.ts +27 -0
  113. package/dist/agent-farm/utils/file-tabs.d.ts.map +1 -0
  114. package/dist/agent-farm/utils/file-tabs.js +46 -0
  115. package/dist/agent-farm/utils/file-tabs.js.map +1 -0
  116. package/dist/agent-farm/utils/gate-status.d.ts +16 -0
  117. package/dist/agent-farm/utils/gate-status.d.ts.map +1 -0
  118. package/dist/agent-farm/utils/gate-status.js +79 -0
  119. package/dist/agent-farm/utils/gate-status.js.map +1 -0
  120. package/dist/agent-farm/utils/gate-watcher.d.ts +38 -0
  121. package/dist/agent-farm/utils/gate-watcher.d.ts.map +1 -0
  122. package/dist/agent-farm/utils/gate-watcher.js +122 -0
  123. package/dist/agent-farm/utils/gate-watcher.js.map +1 -0
  124. package/dist/agent-farm/utils/index.d.ts +0 -1
  125. package/dist/agent-farm/utils/index.d.ts.map +1 -1
  126. package/dist/agent-farm/utils/index.js +0 -1
  127. package/dist/agent-farm/utils/index.js.map +1 -1
  128. package/dist/agent-farm/utils/notifications.d.ts +30 -0
  129. package/dist/agent-farm/utils/notifications.d.ts.map +1 -0
  130. package/dist/agent-farm/utils/notifications.js +121 -0
  131. package/dist/agent-farm/utils/notifications.js.map +1 -0
  132. package/dist/agent-farm/utils/server-utils.d.ts +5 -5
  133. package/dist/agent-farm/utils/server-utils.d.ts.map +1 -1
  134. package/dist/agent-farm/utils/server-utils.js +5 -16
  135. package/dist/agent-farm/utils/server-utils.js.map +1 -1
  136. package/dist/agent-farm/utils/session.d.ts +32 -0
  137. package/dist/agent-farm/utils/session.d.ts.map +1 -0
  138. package/dist/agent-farm/utils/session.js +57 -0
  139. package/dist/agent-farm/utils/session.js.map +1 -0
  140. package/dist/agent-farm/utils/shell.d.ts +9 -22
  141. package/dist/agent-farm/utils/shell.d.ts.map +1 -1
  142. package/dist/agent-farm/utils/shell.js +34 -34
  143. package/dist/agent-farm/utils/shell.js.map +1 -1
  144. package/dist/cli.d.ts.map +1 -1
  145. package/dist/cli.js +11 -54
  146. package/dist/cli.js.map +1 -1
  147. package/dist/commands/adopt.d.ts.map +1 -1
  148. package/dist/commands/adopt.js +49 -4
  149. package/dist/commands/adopt.js.map +1 -1
  150. package/dist/commands/consult/index.d.ts +13 -2
  151. package/dist/commands/consult/index.d.ts.map +1 -1
  152. package/dist/commands/consult/index.js +245 -29
  153. package/dist/commands/consult/index.js.map +1 -1
  154. package/dist/commands/doctor.d.ts.map +1 -1
  155. package/dist/commands/doctor.js +96 -79
  156. package/dist/commands/doctor.js.map +1 -1
  157. package/dist/commands/init.d.ts.map +1 -1
  158. package/dist/commands/init.js +41 -2
  159. package/dist/commands/init.js.map +1 -1
  160. package/dist/commands/porch/build-counter.d.ts +5 -0
  161. package/dist/commands/porch/build-counter.d.ts.map +1 -0
  162. package/dist/commands/porch/build-counter.js +5 -0
  163. package/dist/commands/porch/build-counter.js.map +1 -0
  164. package/dist/commands/porch/checks.d.ts +17 -29
  165. package/dist/commands/porch/checks.d.ts.map +1 -1
  166. package/dist/commands/porch/checks.js +96 -144
  167. package/dist/commands/porch/checks.js.map +1 -1
  168. package/dist/commands/porch/index.d.ts +25 -43
  169. package/dist/commands/porch/index.d.ts.map +1 -1
  170. package/dist/commands/porch/index.js +463 -1116
  171. package/dist/commands/porch/index.js.map +1 -1
  172. package/dist/commands/porch/next.d.ts +22 -0
  173. package/dist/commands/porch/next.d.ts.map +1 -0
  174. package/dist/commands/porch/next.js +571 -0
  175. package/dist/commands/porch/next.js.map +1 -0
  176. package/dist/commands/porch/plan.d.ts +70 -0
  177. package/dist/commands/porch/plan.d.ts.map +1 -0
  178. package/dist/commands/porch/plan.js +190 -0
  179. package/dist/commands/porch/plan.js.map +1 -0
  180. package/dist/commands/porch/prompts.d.ts +19 -0
  181. package/dist/commands/porch/prompts.d.ts.map +1 -0
  182. package/dist/commands/porch/prompts.js +277 -0
  183. package/dist/commands/porch/prompts.js.map +1 -0
  184. package/dist/commands/porch/protocol.d.ts +59 -0
  185. package/dist/commands/porch/protocol.d.ts.map +1 -0
  186. package/dist/commands/porch/protocol.js +294 -0
  187. package/dist/commands/porch/protocol.js.map +1 -0
  188. package/dist/commands/porch/state.d.ts +36 -107
  189. package/dist/commands/porch/state.d.ts.map +1 -1
  190. package/dist/commands/porch/state.js +120 -699
  191. package/dist/commands/porch/state.js.map +1 -1
  192. package/dist/commands/porch/types.d.ts +99 -164
  193. package/dist/commands/porch/types.d.ts.map +1 -1
  194. package/dist/commands/porch/types.js +2 -1
  195. package/dist/commands/porch/types.js.map +1 -1
  196. package/dist/commands/porch/verdict.d.ts +31 -0
  197. package/dist/commands/porch/verdict.d.ts.map +1 -0
  198. package/dist/commands/porch/verdict.js +59 -0
  199. package/dist/commands/porch/verdict.js.map +1 -0
  200. package/dist/commands/update.d.ts.map +1 -1
  201. package/dist/commands/update.js +31 -0
  202. package/dist/commands/update.js.map +1 -1
  203. package/dist/lib/scaffold.d.ts +37 -0
  204. package/dist/lib/scaffold.d.ts.map +1 -1
  205. package/dist/lib/scaffold.js +114 -0
  206. package/dist/lib/scaffold.js.map +1 -1
  207. package/dist/terminal/index.d.ts +8 -0
  208. package/dist/terminal/index.d.ts.map +1 -0
  209. package/dist/terminal/index.js +5 -0
  210. package/dist/terminal/index.js.map +1 -0
  211. package/dist/terminal/pty-manager.d.ts +69 -0
  212. package/dist/terminal/pty-manager.d.ts.map +1 -0
  213. package/dist/terminal/pty-manager.js +377 -0
  214. package/dist/terminal/pty-manager.js.map +1 -0
  215. package/dist/terminal/pty-session.d.ts +104 -0
  216. package/dist/terminal/pty-session.d.ts.map +1 -0
  217. package/dist/terminal/pty-session.js +327 -0
  218. package/dist/terminal/pty-session.js.map +1 -0
  219. package/dist/terminal/ring-buffer.d.ts +34 -0
  220. package/dist/terminal/ring-buffer.d.ts.map +1 -0
  221. package/dist/terminal/ring-buffer.js +94 -0
  222. package/dist/terminal/ring-buffer.js.map +1 -0
  223. package/dist/terminal/session-manager.d.ts +115 -0
  224. package/dist/terminal/session-manager.d.ts.map +1 -0
  225. package/dist/terminal/session-manager.js +582 -0
  226. package/dist/terminal/session-manager.js.map +1 -0
  227. package/dist/terminal/shepherd-client.d.ts +66 -0
  228. package/dist/terminal/shepherd-client.d.ts.map +1 -0
  229. package/dist/terminal/shepherd-client.js +234 -0
  230. package/dist/terminal/shepherd-client.js.map +1 -0
  231. package/dist/terminal/shepherd-main.d.ts +19 -0
  232. package/dist/terminal/shepherd-main.d.ts.map +1 -0
  233. package/dist/terminal/shepherd-main.js +153 -0
  234. package/dist/terminal/shepherd-main.js.map +1 -0
  235. package/dist/terminal/shepherd-process.d.ts +75 -0
  236. package/dist/terminal/shepherd-process.d.ts.map +1 -0
  237. package/dist/terminal/shepherd-process.js +279 -0
  238. package/dist/terminal/shepherd-process.js.map +1 -0
  239. package/dist/terminal/shepherd-protocol.d.ts +115 -0
  240. package/dist/terminal/shepherd-protocol.d.ts.map +1 -0
  241. package/dist/terminal/shepherd-protocol.js +214 -0
  242. package/dist/terminal/shepherd-protocol.js.map +1 -0
  243. package/dist/terminal/shepherd-replay-buffer.d.ts +38 -0
  244. package/dist/terminal/shepherd-replay-buffer.d.ts.map +1 -0
  245. package/dist/terminal/shepherd-replay-buffer.js +94 -0
  246. package/dist/terminal/shepherd-replay-buffer.js.map +1 -0
  247. package/dist/terminal/ws-protocol.d.ts +27 -0
  248. package/dist/terminal/ws-protocol.d.ts.map +1 -0
  249. package/dist/terminal/ws-protocol.js +44 -0
  250. package/dist/terminal/ws-protocol.js.map +1 -0
  251. package/package.json +19 -5
  252. package/skeleton/.claude/skills/af/SKILL.md +89 -0
  253. package/skeleton/.claude/skills/codev/SKILL.md +41 -0
  254. package/skeleton/.claude/skills/consult/SKILL.md +81 -0
  255. package/skeleton/.claude/skills/generate-image/SKILL.md +56 -0
  256. package/skeleton/DEPENDENCIES.md +4 -62
  257. package/skeleton/builders.md +1 -1
  258. package/skeleton/consult-types/impl-review.md +18 -9
  259. package/skeleton/consult-types/integration-review.md +1 -1
  260. package/skeleton/consult-types/plan-review.md +1 -1
  261. package/skeleton/consult-types/pr-ready.md +1 -1
  262. package/skeleton/consult-types/spec-review.md +1 -1
  263. package/skeleton/porch/prompts/defend.md +1 -1
  264. package/skeleton/porch/prompts/evaluate.md +2 -2
  265. package/skeleton/porch/prompts/implement.md +1 -1
  266. package/skeleton/porch/prompts/plan.md +1 -1
  267. package/skeleton/porch/prompts/review.md +4 -4
  268. package/skeleton/porch/prompts/specify.md +1 -1
  269. package/skeleton/porch/prompts/understand.md +2 -2
  270. package/skeleton/protocol-schema.json +282 -0
  271. package/skeleton/protocols/bugfix/builder-prompt.md +54 -0
  272. package/skeleton/protocols/bugfix/prompts/fix.md +77 -0
  273. package/skeleton/protocols/bugfix/prompts/investigate.md +77 -0
  274. package/skeleton/protocols/bugfix/prompts/pr.md +84 -0
  275. package/skeleton/protocols/bugfix/protocol.json +20 -33
  276. package/skeleton/protocols/experiment/builder-prompt.md +52 -0
  277. package/skeleton/protocols/experiment/protocol.json +101 -0
  278. package/skeleton/protocols/experiment/protocol.md +3 -3
  279. package/skeleton/protocols/experiment/templates/notes.md +1 -1
  280. package/skeleton/protocols/maintain/builder-prompt.md +46 -0
  281. package/skeleton/protocols/maintain/prompts/audit.md +111 -0
  282. package/skeleton/protocols/maintain/prompts/clean.md +91 -0
  283. package/skeleton/protocols/maintain/prompts/sync.md +113 -0
  284. package/skeleton/protocols/maintain/prompts/verify.md +110 -0
  285. package/skeleton/protocols/maintain/protocol.json +141 -0
  286. package/skeleton/protocols/maintain/protocol.md +17 -11
  287. package/skeleton/protocols/protocol-schema.json +54 -1
  288. package/skeleton/protocols/spir/builder-prompt.md +59 -0
  289. package/skeleton/protocols/spir/prompts/implement.md +208 -0
  290. package/skeleton/protocols/{spider → spir}/prompts/plan.md +6 -70
  291. package/skeleton/protocols/{spider → spir}/prompts/review.md +20 -39
  292. package/skeleton/protocols/{spider → spir}/prompts/specify.md +33 -61
  293. package/skeleton/protocols/spir/protocol.json +156 -0
  294. package/skeleton/protocols/{spider → spir}/protocol.md +35 -21
  295. package/skeleton/protocols/{spider → spir}/templates/plan.md +14 -0
  296. package/skeleton/protocols/spir/templates/review.md +89 -0
  297. package/skeleton/protocols/tick/builder-prompt.md +56 -0
  298. package/skeleton/protocols/tick/protocol.json +7 -2
  299. package/skeleton/protocols/tick/protocol.md +18 -18
  300. package/skeleton/protocols/tick/templates/review.md +1 -1
  301. package/skeleton/resources/commands/agent-farm.md +63 -46
  302. package/skeleton/resources/commands/codev.md +0 -2
  303. package/skeleton/resources/commands/overview.md +7 -17
  304. package/skeleton/resources/workflow-reference.md +4 -4
  305. package/skeleton/roles/architect.md +152 -315
  306. package/skeleton/roles/builder.md +110 -218
  307. package/skeleton/roles/consultant.md +6 -6
  308. package/skeleton/templates/AGENTS.md +2 -2
  309. package/skeleton/templates/CLAUDE.md +2 -2
  310. package/skeleton/templates/cheatsheet.md +7 -5
  311. package/skeleton/templates/projectlist.md +1 -1
  312. package/templates/dashboard/index.html +17 -43
  313. package/templates/dashboard/js/dialogs.js +7 -7
  314. package/templates/dashboard/js/files.js +2 -2
  315. package/templates/dashboard/js/main.js +4 -4
  316. package/templates/dashboard/js/projects.js +3 -3
  317. package/templates/dashboard/js/tabs.js +1 -1
  318. package/templates/dashboard/js/utils.js +22 -87
  319. package/templates/open.html +26 -0
  320. package/templates/tower.html +642 -36
  321. package/dist/agent-farm/commands/kickoff.d.ts +0 -20
  322. package/dist/agent-farm/commands/kickoff.d.ts.map +0 -1
  323. package/dist/agent-farm/commands/kickoff.js +0 -337
  324. package/dist/agent-farm/commands/kickoff.js.map +0 -1
  325. package/dist/agent-farm/commands/rename.d.ts +0 -13
  326. package/dist/agent-farm/commands/rename.d.ts.map +0 -1
  327. package/dist/agent-farm/commands/rename.js +0 -33
  328. package/dist/agent-farm/commands/rename.js.map +0 -1
  329. package/dist/agent-farm/commands/tutorial.d.ts +0 -10
  330. package/dist/agent-farm/commands/tutorial.d.ts.map +0 -1
  331. package/dist/agent-farm/commands/tutorial.js +0 -49
  332. package/dist/agent-farm/commands/tutorial.js.map +0 -1
  333. package/dist/agent-farm/commands/util.d.ts +0 -15
  334. package/dist/agent-farm/commands/util.d.ts.map +0 -1
  335. package/dist/agent-farm/commands/util.js +0 -108
  336. package/dist/agent-farm/commands/util.js.map +0 -1
  337. package/dist/agent-farm/servers/dashboard-server.d.ts +0 -7
  338. package/dist/agent-farm/servers/dashboard-server.d.ts.map +0 -1
  339. package/dist/agent-farm/servers/dashboard-server.js +0 -1872
  340. package/dist/agent-farm/servers/dashboard-server.js.map +0 -1
  341. package/dist/agent-farm/servers/open-server.d.ts +0 -7
  342. package/dist/agent-farm/servers/open-server.d.ts.map +0 -1
  343. package/dist/agent-farm/servers/open-server.js +0 -315
  344. package/dist/agent-farm/servers/open-server.js.map +0 -1
  345. package/dist/agent-farm/tutorial/index.d.ts +0 -8
  346. package/dist/agent-farm/tutorial/index.d.ts.map +0 -1
  347. package/dist/agent-farm/tutorial/index.js +0 -8
  348. package/dist/agent-farm/tutorial/index.js.map +0 -1
  349. package/dist/agent-farm/tutorial/prompts.d.ts +0 -57
  350. package/dist/agent-farm/tutorial/prompts.d.ts.map +0 -1
  351. package/dist/agent-farm/tutorial/prompts.js +0 -147
  352. package/dist/agent-farm/tutorial/prompts.js.map +0 -1
  353. package/dist/agent-farm/tutorial/runner.d.ts +0 -52
  354. package/dist/agent-farm/tutorial/runner.d.ts.map +0 -1
  355. package/dist/agent-farm/tutorial/runner.js +0 -204
  356. package/dist/agent-farm/tutorial/runner.js.map +0 -1
  357. package/dist/agent-farm/tutorial/state.d.ts +0 -26
  358. package/dist/agent-farm/tutorial/state.d.ts.map +0 -1
  359. package/dist/agent-farm/tutorial/state.js +0 -89
  360. package/dist/agent-farm/tutorial/state.js.map +0 -1
  361. package/dist/agent-farm/tutorial/steps/first-spec.d.ts +0 -7
  362. package/dist/agent-farm/tutorial/steps/first-spec.d.ts.map +0 -1
  363. package/dist/agent-farm/tutorial/steps/first-spec.js +0 -136
  364. package/dist/agent-farm/tutorial/steps/first-spec.js.map +0 -1
  365. package/dist/agent-farm/tutorial/steps/implementation.d.ts +0 -7
  366. package/dist/agent-farm/tutorial/steps/implementation.d.ts.map +0 -1
  367. package/dist/agent-farm/tutorial/steps/implementation.js +0 -76
  368. package/dist/agent-farm/tutorial/steps/implementation.js.map +0 -1
  369. package/dist/agent-farm/tutorial/steps/index.d.ts +0 -10
  370. package/dist/agent-farm/tutorial/steps/index.d.ts.map +0 -1
  371. package/dist/agent-farm/tutorial/steps/index.js +0 -10
  372. package/dist/agent-farm/tutorial/steps/index.js.map +0 -1
  373. package/dist/agent-farm/tutorial/steps/planning.d.ts +0 -7
  374. package/dist/agent-farm/tutorial/steps/planning.d.ts.map +0 -1
  375. package/dist/agent-farm/tutorial/steps/planning.js +0 -143
  376. package/dist/agent-farm/tutorial/steps/planning.js.map +0 -1
  377. package/dist/agent-farm/tutorial/steps/review.d.ts +0 -7
  378. package/dist/agent-farm/tutorial/steps/review.d.ts.map +0 -1
  379. package/dist/agent-farm/tutorial/steps/review.js +0 -78
  380. package/dist/agent-farm/tutorial/steps/review.js.map +0 -1
  381. package/dist/agent-farm/tutorial/steps/setup.d.ts +0 -7
  382. package/dist/agent-farm/tutorial/steps/setup.d.ts.map +0 -1
  383. package/dist/agent-farm/tutorial/steps/setup.js +0 -126
  384. package/dist/agent-farm/tutorial/steps/setup.js.map +0 -1
  385. package/dist/agent-farm/tutorial/steps/welcome.d.ts +0 -7
  386. package/dist/agent-farm/tutorial/steps/welcome.d.ts.map +0 -1
  387. package/dist/agent-farm/tutorial/steps/welcome.js +0 -50
  388. package/dist/agent-farm/tutorial/steps/welcome.js.map +0 -1
  389. package/dist/agent-farm/utils/orphan-handler.d.ts +0 -27
  390. package/dist/agent-farm/utils/orphan-handler.d.ts.map +0 -1
  391. package/dist/agent-farm/utils/orphan-handler.js +0 -149
  392. package/dist/agent-farm/utils/orphan-handler.js.map +0 -1
  393. package/dist/agent-farm/utils/port-registry.d.ts +0 -58
  394. package/dist/agent-farm/utils/port-registry.d.ts.map +0 -1
  395. package/dist/agent-farm/utils/port-registry.js +0 -166
  396. package/dist/agent-farm/utils/port-registry.js.map +0 -1
  397. package/dist/agent-farm/utils/terminal-ports.d.ts +0 -18
  398. package/dist/agent-farm/utils/terminal-ports.d.ts.map +0 -1
  399. package/dist/agent-farm/utils/terminal-ports.js +0 -35
  400. package/dist/agent-farm/utils/terminal-ports.js.map +0 -1
  401. package/dist/commands/pcheck/cache.d.ts +0 -48
  402. package/dist/commands/pcheck/cache.d.ts.map +0 -1
  403. package/dist/commands/pcheck/cache.js +0 -170
  404. package/dist/commands/pcheck/cache.js.map +0 -1
  405. package/dist/commands/pcheck/evaluator.d.ts +0 -15
  406. package/dist/commands/pcheck/evaluator.d.ts.map +0 -1
  407. package/dist/commands/pcheck/evaluator.js +0 -246
  408. package/dist/commands/pcheck/evaluator.js.map +0 -1
  409. package/dist/commands/pcheck/index.d.ts +0 -12
  410. package/dist/commands/pcheck/index.d.ts.map +0 -1
  411. package/dist/commands/pcheck/index.js +0 -249
  412. package/dist/commands/pcheck/index.js.map +0 -1
  413. package/dist/commands/pcheck/parser.d.ts +0 -39
  414. package/dist/commands/pcheck/parser.d.ts.map +0 -1
  415. package/dist/commands/pcheck/parser.js +0 -155
  416. package/dist/commands/pcheck/parser.js.map +0 -1
  417. package/dist/commands/pcheck/types.d.ts +0 -82
  418. package/dist/commands/pcheck/types.d.ts.map +0 -1
  419. package/dist/commands/pcheck/types.js +0 -5
  420. package/dist/commands/pcheck/types.js.map +0 -1
  421. package/dist/commands/porch/consultation.d.ts +0 -56
  422. package/dist/commands/porch/consultation.d.ts.map +0 -1
  423. package/dist/commands/porch/consultation.js +0 -330
  424. package/dist/commands/porch/consultation.js.map +0 -1
  425. package/dist/commands/porch/notifications.d.ts +0 -99
  426. package/dist/commands/porch/notifications.d.ts.map +0 -1
  427. package/dist/commands/porch/notifications.js +0 -223
  428. package/dist/commands/porch/notifications.js.map +0 -1
  429. package/dist/commands/porch/plan-parser.d.ts +0 -38
  430. package/dist/commands/porch/plan-parser.d.ts.map +0 -1
  431. package/dist/commands/porch/plan-parser.js +0 -166
  432. package/dist/commands/porch/plan-parser.js.map +0 -1
  433. package/dist/commands/porch/protocol-loader.d.ts +0 -46
  434. package/dist/commands/porch/protocol-loader.d.ts.map +0 -1
  435. package/dist/commands/porch/protocol-loader.js +0 -253
  436. package/dist/commands/porch/protocol-loader.js.map +0 -1
  437. package/dist/commands/porch/signal-parser.d.ts +0 -88
  438. package/dist/commands/porch/signal-parser.d.ts.map +0 -1
  439. package/dist/commands/porch/signal-parser.js +0 -148
  440. package/dist/commands/porch/signal-parser.js.map +0 -1
  441. package/dist/commands/tower.d.ts +0 -16
  442. package/dist/commands/tower.d.ts.map +0 -1
  443. package/dist/commands/tower.js +0 -21
  444. package/dist/commands/tower.js.map +0 -1
  445. package/skeleton/config.json +0 -7
  446. package/skeleton/porch/protocols/bugfix.json +0 -85
  447. package/skeleton/porch/protocols/spider.json +0 -135
  448. package/skeleton/porch/protocols/tick.json +0 -76
  449. package/skeleton/protocols/spider/prompts/defend.md +0 -215
  450. package/skeleton/protocols/spider/prompts/evaluate.md +0 -241
  451. package/skeleton/protocols/spider/prompts/implement.md +0 -149
  452. package/skeleton/protocols/spider/protocol.json +0 -210
  453. package/skeleton/protocols/spider/templates/review.md +0 -207
  454. package/templates/dashboard/css/activity.css +0 -151
  455. package/templates/dashboard/js/activity.js +0 -112
  456. /package/skeleton/protocols/{spider → spir}/templates/spec.md +0 -0
@@ -2,8 +2,12 @@
2
2
  <html lang="en">
3
3
  <head>
4
4
  <meta charset="UTF-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>AF Control Tower</title>
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
6
+ <meta name="apple-mobile-web-app-capable" content="yes">
7
+ <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
8
+ <meta name="theme-color" content="#252525">
9
+ <link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🗼</text></svg>">
10
+ <title>Agent Farm Tower</title>
7
11
  <style>
8
12
  * {
9
13
  box-sizing: border-box;
@@ -199,6 +203,36 @@
199
203
  overflow: hidden;
200
204
  }
201
205
 
206
+ .instance.gate-pending {
207
+ border-color: #f59e0b;
208
+ animation: gate-pulse 2s infinite;
209
+ }
210
+
211
+ @keyframes gate-pulse {
212
+ 0%, 100% { border-color: #f59e0b; }
213
+ 50% { border-color: #d97706; }
214
+ }
215
+
216
+ .gate-indicator {
217
+ padding: 8px 20px;
218
+ background: rgba(245, 158, 11, 0.15);
219
+ border-bottom: 1px solid var(--border);
220
+ font-size: 14px;
221
+ color: #f59e0b;
222
+ display: flex;
223
+ align-items: center;
224
+ gap: 8px;
225
+ }
226
+
227
+ .gate-indicator .gate-name {
228
+ font-weight: 600;
229
+ }
230
+
231
+ .gate-indicator .gate-builder {
232
+ color: var(--text-muted);
233
+ font-size: 12px;
234
+ }
235
+
202
236
  .instance-header {
203
237
  display: flex;
204
238
  justify-content: space-between;
@@ -388,6 +422,11 @@
388
422
  gap: 24px;
389
423
  }
390
424
 
425
+ .new-shell-row {
426
+ margin-top: 8px;
427
+ padding-top: 8px;
428
+ }
429
+
391
430
  /* Recents section */
392
431
  .recents-section {
393
432
  margin-top: 32px;
@@ -603,6 +642,61 @@
603
642
  }
604
643
  }
605
644
 
645
+ /* Cloud status */
646
+ .cloud-status {
647
+ display: inline-flex;
648
+ align-items: center;
649
+ gap: 8px;
650
+ font-size: 14px;
651
+ color: var(--text-secondary);
652
+ }
653
+
654
+ .cloud-dot {
655
+ width: 8px;
656
+ height: 8px;
657
+ border-radius: 50%;
658
+ display: inline-block;
659
+ }
660
+
661
+ .cloud-dot--green { background: var(--status-running); }
662
+ .cloud-dot--yellow { background: #eab308; }
663
+ .cloud-dot--red { background: #ef4444; }
664
+ .cloud-dot--gray { background: var(--text-muted); }
665
+
666
+ .cloud-link {
667
+ color: var(--accent);
668
+ text-decoration: none;
669
+ font-size: 13px;
670
+ }
671
+
672
+ .cloud-link:hover {
673
+ text-decoration: underline;
674
+ }
675
+
676
+ .cloud-uptime {
677
+ color: var(--text-muted);
678
+ font-size: 12px;
679
+ }
680
+
681
+ .cloud-btn {
682
+ padding: 4px 10px;
683
+ border-radius: 4px;
684
+ border: 1px solid var(--border);
685
+ background: var(--bg-tertiary);
686
+ color: var(--text-secondary);
687
+ cursor: pointer;
688
+ font-size: 12px;
689
+ }
690
+
691
+ .cloud-btn:hover {
692
+ background: #333;
693
+ }
694
+
695
+ .cloud-btn:disabled {
696
+ opacity: 0.5;
697
+ cursor: default;
698
+ }
699
+
606
700
  /* Reduced motion */
607
701
  @media (prefers-reduced-motion: reduce) {
608
702
  .status-dot.running,
@@ -611,6 +705,183 @@
611
705
  }
612
706
  }
613
707
 
708
+ /* Mobile optimizations */
709
+ @media (max-width: 600px) {
710
+ body {
711
+ padding-bottom: env(safe-area-inset-bottom, 0);
712
+ }
713
+
714
+ .header {
715
+ padding: 16px;
716
+ padding-top: calc(16px + env(safe-area-inset-top, 0));
717
+ flex-wrap: wrap;
718
+ gap: 12px;
719
+ }
720
+
721
+ .header h1 {
722
+ font-size: 18px;
723
+ width: 100%;
724
+ }
725
+
726
+ .header h1 .emoji {
727
+ font-size: 22px;
728
+ }
729
+
730
+ .header-actions {
731
+ width: 100%;
732
+ justify-content: flex-end;
733
+ }
734
+
735
+ /* 1. Hide Share button on mobile (tunnel is for reaching phone, not FROM phone) */
736
+ #share-btn {
737
+ display: none !important;
738
+ }
739
+
740
+ /* 7. Reduce section spacing */
741
+ .main {
742
+ padding: 12px;
743
+ padding-left: calc(12px + env(safe-area-inset-left, 0));
744
+ padding-right: calc(12px + env(safe-area-inset-right, 0));
745
+ }
746
+
747
+ .section-header {
748
+ margin-bottom: 8px;
749
+ }
750
+
751
+ .instances {
752
+ gap: 10px;
753
+ }
754
+
755
+ .btn {
756
+ min-height: 44px;
757
+ min-width: 44px;
758
+ padding: 12px 16px;
759
+ }
760
+
761
+ .btn-small {
762
+ min-height: 44px;
763
+ padding: 10px 14px;
764
+ }
765
+
766
+ /* 2. Keep project name + status badge + Restart/Stop on one line */
767
+ .instance-header {
768
+ flex-wrap: wrap;
769
+ gap: 8px;
770
+ padding: 12px 16px;
771
+ }
772
+
773
+ .instance-actions {
774
+ margin-left: auto;
775
+ }
776
+
777
+ /* 3. Hide project path row on mobile */
778
+ .instance-path-row {
779
+ display: none;
780
+ }
781
+
782
+ /* 4. Compact terminal list (Overview, Architect, shells) */
783
+ .port-item {
784
+ flex-direction: row;
785
+ align-items: center;
786
+ padding: 8px 12px;
787
+ gap: 8px;
788
+ }
789
+
790
+ .port-actions {
791
+ width: auto;
792
+ }
793
+
794
+ .port-actions a {
795
+ display: flex;
796
+ align-items: center;
797
+ justify-content: center;
798
+ padding: 6px 12px;
799
+ flex: 0;
800
+ /* min-height handled by @media (pointer: coarse) at 44px */
801
+ }
802
+
803
+ .instance-body {
804
+ padding: 12px;
805
+ }
806
+
807
+ /* 5. Compact New Shell row */
808
+ .new-shell-row {
809
+ margin-top: 4px;
810
+ padding-top: 4px;
811
+ }
812
+
813
+ /* 6. Compact Recent Projects */
814
+ .recent-item {
815
+ flex-direction: row;
816
+ align-items: center;
817
+ padding: 12px 16px;
818
+ gap: 8px;
819
+ }
820
+
821
+ .recent-path {
822
+ display: none;
823
+ }
824
+
825
+ .recent-time {
826
+ font-size: 12px;
827
+ }
828
+
829
+ /* 7. Reduce section spacing (continued) */
830
+ .recents-section {
831
+ margin-top: 16px;
832
+ padding-top: 16px;
833
+ }
834
+
835
+ .instance-meta {
836
+ margin-top: 8px;
837
+ padding-top: 8px;
838
+ }
839
+
840
+ .launch-section {
841
+ padding: 16px;
842
+ }
843
+
844
+ .launch-form {
845
+ flex-direction: column;
846
+ }
847
+
848
+ .launch-form input[type="text"] {
849
+ min-height: 44px;
850
+ }
851
+
852
+ .btn-launch {
853
+ width: 100%;
854
+ min-height: 44px;
855
+ }
856
+
857
+ .dialog-box {
858
+ min-width: auto;
859
+ width: calc(100% - 32px);
860
+ margin: 16px;
861
+ }
862
+
863
+ .toast-container {
864
+ left: 16px;
865
+ right: 16px;
866
+ bottom: calc(16px + env(safe-area-inset-bottom, 0));
867
+ }
868
+ }
869
+
870
+ /* Touch-friendly tap targets */
871
+ @media (pointer: coarse) {
872
+ .btn, .port-actions a, .autocomplete-item, .recent-item {
873
+ min-height: 44px;
874
+ }
875
+
876
+ .copy-btn {
877
+ min-width: 44px;
878
+ min-height: 44px;
879
+ display: flex;
880
+ align-items: center;
881
+ justify-content: center;
882
+ }
883
+ }
884
+
614
885
  /* Dialog styles */
615
886
  .dialog-overlay {
616
887
  position: fixed;
@@ -680,6 +951,7 @@
680
951
  Agent Farm Control Tower
681
952
  </h1>
682
953
  <div class="header-actions">
954
+ <span id="cloud-status"></span>
683
955
  <button class="btn" onclick="refresh()">Refresh</button>
684
956
  </div>
685
957
  </header>
@@ -733,6 +1005,7 @@
733
1005
  </div>
734
1006
  </div>
735
1007
  </div>
1008
+
736
1009
  </main>
737
1010
 
738
1011
  <div class="toast-container" id="toast-container"></div>
@@ -742,23 +1015,153 @@
742
1015
  let runningInstances = [];
743
1016
  let recentInstances = [];
744
1017
 
1018
+ // Auth helper: get headers with auth token if available
1019
+ function getAuthHeaders() {
1020
+ const key = localStorage.getItem('codev-web-key');
1021
+ return key ? { 'Authorization': `Bearer ${key}` } : {};
1022
+ }
1023
+
1024
+ // Auth helper: make authenticated fetch request
1025
+ async function authFetch(url, options = {}) {
1026
+ const headers = { ...getAuthHeaders(), ...(options.headers || {}) };
1027
+ const response = await fetch(url, { ...options, headers });
1028
+
1029
+ // If 401, clear key and redirect to login
1030
+ if (response.status === 401) {
1031
+ localStorage.removeItem('codev-web-key');
1032
+ location.reload();
1033
+ throw new Error('Unauthorized');
1034
+ }
1035
+ return response;
1036
+ }
1037
+
1038
+ // Logout function
1039
+ function logout() {
1040
+ localStorage.removeItem('codev-web-key');
1041
+ window.location.href = './';
1042
+ }
1043
+
745
1044
  // Initialize
746
1045
  async function init() {
1046
+ // Request notification permission
1047
+ if ('Notification' in window && Notification.permission === 'default') {
1048
+ try {
1049
+ await Notification.requestPermission();
1050
+ } catch (e) {
1051
+ // Ignore errors, notifications are optional
1052
+ }
1053
+ }
1054
+
1055
+ // Subscribe to SSE for push notifications
1056
+ subscribeToEvents();
1057
+
747
1058
  await refresh();
748
1059
  // Poll every 5 seconds
749
1060
  setInterval(refresh, 5000);
750
1061
  }
751
1062
 
1063
+ // SSE subscription for real-time notifications
1064
+ // Uses fetch + ReadableStream to support Authorization header
1065
+ let sseController = null;
1066
+
1067
+ async function subscribeToEvents() {
1068
+ // Abort any existing connection
1069
+ if (sseController) {
1070
+ sseController.abort();
1071
+ }
1072
+
1073
+ sseController = new AbortController();
1074
+
1075
+ try {
1076
+ const response = await fetch('./api/events', {
1077
+ headers: getAuthHeaders(),
1078
+ signal: sseController.signal,
1079
+ });
1080
+
1081
+ if (!response.ok) {
1082
+ console.log('SSE connection failed:', response.status);
1083
+ setTimeout(subscribeToEvents, 5000);
1084
+ return;
1085
+ }
1086
+
1087
+ const reader = response.body.getReader();
1088
+ const decoder = new TextDecoder();
1089
+ let buffer = '';
1090
+
1091
+ while (true) {
1092
+ const { done, value } = await reader.read();
1093
+
1094
+ if (done) {
1095
+ console.log('SSE stream ended, reconnecting...');
1096
+ setTimeout(subscribeToEvents, 5000);
1097
+ return;
1098
+ }
1099
+
1100
+ buffer += decoder.decode(value, { stream: true });
1101
+
1102
+ // Parse SSE format: "data: {...}\n\n"
1103
+ const lines = buffer.split('\n\n');
1104
+ buffer = lines.pop() || ''; // Keep incomplete message in buffer
1105
+
1106
+ for (const chunk of lines) {
1107
+ const dataMatch = chunk.match(/^data:\s*(.+)$/m);
1108
+ if (!dataMatch) continue;
1109
+
1110
+ try {
1111
+ const data = JSON.parse(dataMatch[1]);
1112
+
1113
+ if (data.type === 'connected') {
1114
+ console.log('SSE connected:', data.id);
1115
+ continue;
1116
+ }
1117
+
1118
+ // Show browser notification
1119
+ if ('Notification' in window && Notification.permission === 'granted') {
1120
+ const notification = new Notification(data.title, {
1121
+ body: data.body,
1122
+ icon: '/favicon.ico',
1123
+ tag: 'tower-notification-' + data.id,
1124
+ });
1125
+
1126
+ // Auto-close after 5 seconds
1127
+ setTimeout(() => notification.close(), 5000);
1128
+ }
1129
+
1130
+ // Also show in-app toast
1131
+ showToast(`${data.title}: ${data.body}`, data.type || 'info');
1132
+
1133
+ // Refresh to show updated status
1134
+ refresh();
1135
+ } catch (e) {
1136
+ console.error('SSE parse error:', e);
1137
+ }
1138
+ }
1139
+ }
1140
+ } catch (e) {
1141
+ if (e.name === 'AbortError') {
1142
+ // Intentional abort, don't reconnect
1143
+ return;
1144
+ }
1145
+ console.error('SSE error:', e);
1146
+ setTimeout(subscribeToEvents, 5000);
1147
+ }
1148
+ }
1149
+
752
1150
  // Refresh data from API
753
1151
  async function refresh() {
754
1152
  try {
755
- const response = await fetch('/api/status');
756
- if (!response.ok) throw new Error('Failed to fetch status');
1153
+ const [statusResponse, cloudStatus] = await Promise.all([
1154
+ authFetch('./api/status'),
1155
+ fetchCloudStatus(),
1156
+ ]);
757
1157
 
758
- const data = await response.json();
1158
+ if (!statusResponse.ok) throw new Error('Failed to fetch status');
1159
+
1160
+ const data = await statusResponse.json();
759
1161
  runningInstances = (data.instances || []).filter(i => i.running);
760
1162
  recentInstances = (data.instances || []).filter(i => !i.running);
761
1163
  render();
1164
+ renderCloudStatus(cloudStatus);
762
1165
  } catch (err) {
763
1166
  console.error('Refresh error:', err);
764
1167
  showToast('Failed to refresh: ' + err.message, 'error');
@@ -774,7 +1177,7 @@
774
1177
  <div class="empty-state">
775
1178
  <div class="icon">📭</div>
776
1179
  <h2>No running instances</h2>
777
- <p>Start a new instance below or run <code>af start</code> in a project directory.</p>
1180
+ <p>Start a new instance below or run <code>af dash start</code> in a project directory.</p>
778
1181
  </div>
779
1182
  `;
780
1183
  } else {
@@ -810,28 +1213,69 @@
810
1213
  function renderInstance(instance) {
811
1214
  const statusClass = instance.running ? 'running' : 'stopped';
812
1215
  const statusText = instance.running ? 'Running' : 'Stopped';
813
-
814
- const portsHtml = instance.ports.map(port => `
815
- <div class="port-item">
816
- <div class="port-info">
817
- <span class="port-status ${port.active ? 'active' : 'inactive'}"></span>
818
- <span class="port-type">${escapeHtml(port.type)}</span>
819
- <span class="port-url">Port ${port.port}</span>
1216
+ const hasGate = instance.gateStatus?.hasGate;
1217
+
1218
+ // Render terminals list - always show Overview first, then individual terminals
1219
+ const terminals = instance.terminals || [];
1220
+ let terminalsHtml = '';
1221
+
1222
+ if (instance.running) {
1223
+ // Always show Overview link first (opens split view with Architect + Status panel)
1224
+ terminalsHtml = `
1225
+ <div class="port-item">
1226
+ <div class="port-info">
1227
+ <span class="port-status active"></span>
1228
+ <span class="port-type">Overview</span>
1229
+ </div>
1230
+ <div class="port-actions">
1231
+ <a href="${escapeHtml(relUrl(instance.proxyUrl))}" target="_blank">Open</a>
1232
+ </div>
820
1233
  </div>
821
- <div class="port-actions">
822
- <a href="${escapeHtml(port.url)}" target="_blank" class="${port.active ? '' : 'disabled'}">
823
- Open
824
- </a>
1234
+ `;
1235
+
1236
+ // Then show individual terminals (open full-screen terminal view)
1237
+ if (terminals.length > 0) {
1238
+ terminalsHtml += terminals.map(terminal => `
1239
+ <div class="port-item">
1240
+ <div class="port-info">
1241
+ <span class="port-status ${terminal.active ? 'active' : 'inactive'}"></span>
1242
+ <span class="port-type">${escapeHtml(terminal.label)}</span>
1243
+ </div>
1244
+ <div class="port-actions">
1245
+ <a href="${escapeHtml(relUrl(terminal.url))}&fullscreen=1" target="_blank">Open</a>
1246
+ </div>
1247
+ </div>
1248
+ `).join('');
1249
+ }
1250
+
1251
+ // Add "New Shell" button for this instance
1252
+ terminalsHtml += `
1253
+ <div class="port-item new-shell-row" style="border-top: 1px dashed var(--border);">
1254
+ <div class="port-info">
1255
+ <span class="port-status" style="background: var(--accent);"></span>
1256
+ <span class="port-type">New Shell</span>
1257
+ </div>
1258
+ <div class="port-actions">
1259
+ <button class="btn btn-small" onclick="createShell('${escapeHtml(instance.projectPath)}')">+ Create</button>
1260
+ </div>
825
1261
  </div>
826
- </div>
827
- `).join('');
1262
+ `;
1263
+ }
828
1264
 
829
1265
  const lastUsed = instance.lastUsed
830
1266
  ? formatDate(instance.lastUsed)
831
1267
  : 'Never';
832
1268
 
1269
+ const gateIndicatorHtml = hasGate ? `
1270
+ <div class="gate-indicator">
1271
+ <span>⏳</span>
1272
+ <span class="gate-name">${escapeHtml(instance.gateStatus.gateName || 'approval')}</span>
1273
+ ${instance.gateStatus.builderId ? `<span class="gate-builder">(${escapeHtml(instance.gateStatus.builderId)})</span>` : ''}
1274
+ </div>
1275
+ ` : '';
1276
+
833
1277
  return `
834
- <div class="instance">
1278
+ <div class="instance ${hasGate ? 'gate-pending' : ''}" data-project="${escapeHtml(instance.projectPath)}">
835
1279
  <div class="instance-header">
836
1280
  <div class="instance-title">
837
1281
  <span class="instance-name">${escapeHtml(instance.projectName)}</span>
@@ -842,13 +1286,14 @@
842
1286
  </div>
843
1287
  <div class="instance-actions">
844
1288
  ${instance.running ? `
845
- <button class="btn btn-small" onclick="restartInstance(${instance.basePort}, '${escapeHtml(instance.projectPath)}')">Restart</button>
846
- <button class="btn btn-small btn-danger" onclick="stopInstance(${instance.basePort})">Stop</button>
1289
+ <button class="btn btn-small" onclick="restartInstance('${escapeHtml(instance.projectPath)}')">Restart</button>
1290
+ <button class="btn btn-small btn-danger" onclick="stopInstance('${escapeHtml(instance.projectPath)}')">Stop</button>
847
1291
  ` : `
848
1292
  <button class="btn btn-small btn-primary" onclick="launchPath('${escapeHtml(instance.projectPath)}')">Start</button>
849
1293
  `}
850
1294
  </div>
851
1295
  </div>
1296
+ ${gateIndicatorHtml}
852
1297
  <div class="instance-path-row">
853
1298
  <span class="instance-path" title="${escapeHtml(instance.projectPath)}">
854
1299
  ${escapeHtml(instance.projectPath)}
@@ -857,11 +1302,10 @@
857
1302
  </div>
858
1303
  <div class="instance-body">
859
1304
  <div class="ports-list">
860
- ${portsHtml}
1305
+ ${terminalsHtml}
861
1306
  </div>
862
1307
  <div class="instance-meta">
863
1308
  <span>Last active: ${lastUsed}</span>
864
- <span>Port block: ${instance.basePort}-${instance.basePort + 99}</span>
865
1309
  </div>
866
1310
  </div>
867
1311
  </div>
@@ -952,7 +1396,7 @@
952
1396
  }
953
1397
 
954
1398
  try {
955
- const response = await fetch('/api/browse?path=' + encodeURIComponent(inputPath));
1399
+ const response = await authFetch('./api/browse?path=' + encodeURIComponent(inputPath));
956
1400
  const data = await response.json();
957
1401
  suggestions = data.suggestions || [];
958
1402
  selectedIndex = -1;
@@ -1007,7 +1451,7 @@
1007
1451
  // Launch a specific path (from recents)
1008
1452
  async function launchPath(projectPath) {
1009
1453
  try {
1010
- const response = await fetch('/api/launch', {
1454
+ const response = await authFetch('./api/launch', {
1011
1455
  method: 'POST',
1012
1456
  headers: { 'Content-Type': 'application/json' },
1013
1457
  body: JSON.stringify({ projectPath })
@@ -1030,13 +1474,13 @@
1030
1474
  }
1031
1475
  }
1032
1476
 
1033
- // Stop an instance by port
1034
- async function stopInstance(basePort) {
1477
+ // Stop an instance by project path
1478
+ async function stopInstance(projectPath) {
1035
1479
  try {
1036
- const response = await fetch('/api/stop', {
1480
+ const response = await authFetch('./api/stop', {
1037
1481
  method: 'POST',
1038
1482
  headers: { 'Content-Type': 'application/json' },
1039
- body: JSON.stringify({ basePort })
1483
+ body: JSON.stringify({ projectPath })
1040
1484
  });
1041
1485
 
1042
1486
  const result = await response.json();
@@ -1053,20 +1497,20 @@
1053
1497
  }
1054
1498
 
1055
1499
  // Restart an instance
1056
- async function restartInstance(basePort, projectPath) {
1500
+ async function restartInstance(projectPath) {
1057
1501
  try {
1058
1502
  // First stop
1059
- await fetch('/api/stop', {
1503
+ await authFetch('./api/stop', {
1060
1504
  method: 'POST',
1061
1505
  headers: { 'Content-Type': 'application/json' },
1062
- body: JSON.stringify({ basePort })
1506
+ body: JSON.stringify({ projectPath })
1063
1507
  });
1064
1508
 
1065
1509
  // Wait a moment for processes to die
1066
1510
  await new Promise(r => setTimeout(r, 1000));
1067
1511
 
1068
1512
  // Then start
1069
- const response = await fetch('/api/launch', {
1513
+ const response = await authFetch('./api/launch', {
1070
1514
  method: 'POST',
1071
1515
  headers: { 'Content-Type': 'application/json' },
1072
1516
  body: JSON.stringify({ projectPath })
@@ -1096,7 +1540,7 @@
1096
1540
  }
1097
1541
 
1098
1542
  try {
1099
- const response = await fetch('/api/launch', {
1543
+ const response = await authFetch('./api/launch', {
1100
1544
  method: 'POST',
1101
1545
  headers: { 'Content-Type': 'application/json' },
1102
1546
  body: JSON.stringify({ projectPath })
@@ -1122,6 +1566,34 @@
1122
1566
  }
1123
1567
  }
1124
1568
 
1569
+ // Create a new shell for a running instance (via tower proxy)
1570
+ async function createShell(projectPath) {
1571
+ try {
1572
+ // Use tower proxy to route to the project's dashboard API
1573
+ const encodedPath = toBase64URL(projectPath);
1574
+ const response = await authFetch(`./project/${encodedPath}/api/tabs/shell`, {
1575
+ method: 'POST',
1576
+ headers: { 'Content-Type': 'application/json' },
1577
+ });
1578
+
1579
+ if (!response.ok) {
1580
+ const errorText = await response.text();
1581
+ throw new Error(errorText || 'Failed to create shell');
1582
+ }
1583
+
1584
+ const result = await response.json();
1585
+
1586
+ if (result.success === false) {
1587
+ throw new Error(result.error || 'Failed to create shell');
1588
+ }
1589
+
1590
+ showToast(`Shell created: ${result.name || 'shell'}`, 'success');
1591
+ setTimeout(refresh, 1000);
1592
+ } catch (err) {
1593
+ showToast('Failed to create shell: ' + err.message, 'error');
1594
+ }
1595
+ }
1596
+
1125
1597
  // Format date for display
1126
1598
  function formatDate(isoString) {
1127
1599
  const date = new Date(isoString);
@@ -1159,6 +1631,13 @@
1159
1631
  }
1160
1632
  }
1161
1633
 
1634
+ // Convert absolute paths to relative so links work behind reverse proxies.
1635
+ // E.g. "/project/abc/" → "./project/abc/"
1636
+ function relUrl(path) {
1637
+ if (path && path.startsWith('/')) return '.' + path;
1638
+ return path || '';
1639
+ }
1640
+
1162
1641
  // HTML escape
1163
1642
  function escapeHtml(str) {
1164
1643
  if (!str) return '';
@@ -1170,6 +1649,23 @@
1170
1649
  .replace(/'/g, '&#39;');
1171
1650
  }
1172
1651
 
1652
+ // Base64URL encoding (RFC 4648) for project paths
1653
+ // IMPORTANT: Use TextEncoder for Unicode support (btoa only handles Latin-1)
1654
+ function toBase64URL(str) {
1655
+ // Encode string to UTF-8 bytes, then to Base64, then to Base64URL
1656
+ const bytes = new TextEncoder().encode(str);
1657
+ const base64 = btoa(String.fromCharCode(...bytes));
1658
+ return base64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
1659
+ }
1660
+
1661
+ // Generate proxy URL for a project
1662
+ // All terminals are now multiplexed on a single port via WebSocket (Spec 0085)
1663
+ // The React dashboard handles tab selection internally
1664
+ function getProxyUrl(instance, portType) {
1665
+ const encodedPath = toBase64URL(instance.projectPath);
1666
+ return `./project/${encodedPath}/`;
1667
+ }
1668
+
1173
1669
  // Toast notifications
1174
1670
  function showToast(message, type = 'info') {
1175
1671
  const container = document.getElementById('toast-container');
@@ -1219,7 +1715,7 @@
1219
1715
  hideCreateProjectDialog();
1220
1716
 
1221
1717
  try {
1222
- const response = await fetch('/api/create', {
1718
+ const response = await authFetch('./api/create', {
1223
1719
  method: 'POST',
1224
1720
  headers: { 'Content-Type': 'application/json' },
1225
1721
  body: JSON.stringify({ parent, name })
@@ -1252,6 +1748,116 @@
1252
1748
  }
1253
1749
  });
1254
1750
 
1751
+ // Cloud status
1752
+ let cloudLoading = false;
1753
+
1754
+ async function fetchCloudStatus() {
1755
+ try {
1756
+ const res = await authFetch('./api/tunnel/status');
1757
+ if (res.status === 404) return null;
1758
+ if (!res.ok) return { state: 'error' };
1759
+ return await res.json();
1760
+ } catch {
1761
+ return null;
1762
+ }
1763
+ }
1764
+
1765
+ function formatUptime(ms) {
1766
+ const s = Math.floor(ms / 1000);
1767
+ if (s < 60) return s + 's';
1768
+ if (s < 3600) return Math.floor(s / 60) + 'm ' + (s % 60) + 's';
1769
+ const h = Math.floor(s / 3600);
1770
+ const m = Math.floor((s % 3600) / 60);
1771
+ return h + 'h ' + m + 'm';
1772
+ }
1773
+
1774
+ function renderCloudStatus(status) {
1775
+ const el = document.getElementById('cloud-status');
1776
+ if (!status || status.state === 'error') {
1777
+ el.innerHTML = `
1778
+ <span class="cloud-status">
1779
+ <span class="cloud-dot cloud-dot--gray"></span>
1780
+ Cloud: not registered
1781
+ </span>`;
1782
+ return;
1783
+ }
1784
+
1785
+ if (!status.registered) {
1786
+ el.innerHTML = `
1787
+ <span class="cloud-status">
1788
+ <span class="cloud-dot cloud-dot--gray"></span>
1789
+ Cloud: not registered
1790
+ </span>`;
1791
+ return;
1792
+ }
1793
+
1794
+ if (status.state === 'auth_failed') {
1795
+ el.innerHTML = `
1796
+ <span class="cloud-status">
1797
+ <span class="cloud-dot cloud-dot--red"></span>
1798
+ Cloud: auth failed
1799
+ </span>`;
1800
+ return;
1801
+ }
1802
+
1803
+ if (status.state === 'connecting') {
1804
+ el.innerHTML = `
1805
+ <span class="cloud-status">
1806
+ <span class="cloud-dot cloud-dot--yellow"></span>
1807
+ Cloud: connecting...
1808
+ </span>`;
1809
+ return;
1810
+ }
1811
+
1812
+ if (status.state === 'connected') {
1813
+ const uptimeStr = status.uptime != null ? ' <span class="cloud-uptime">' + formatUptime(status.uptime) + '</span>' : '';
1814
+ const openLink = status.accessUrl ? ' <a href="' + escapeHtml(status.accessUrl) + '" target="_blank" rel="noopener" class="cloud-link">Open</a>' : '';
1815
+ el.innerHTML = `
1816
+ <span class="cloud-status">
1817
+ <span class="cloud-dot cloud-dot--green"></span>
1818
+ Cloud: ${escapeHtml(status.towerName || 'connected')}
1819
+ ${uptimeStr}
1820
+ ${openLink}
1821
+ <button class="cloud-btn" onclick="cloudDisconnect()" ${cloudLoading ? 'disabled' : ''}>Disconnect</button>
1822
+ </span>`;
1823
+ return;
1824
+ }
1825
+
1826
+ // Disconnected
1827
+ el.innerHTML = `
1828
+ <span class="cloud-status">
1829
+ <span class="cloud-dot cloud-dot--gray"></span>
1830
+ Cloud: disconnected
1831
+ <button class="cloud-btn" onclick="cloudConnect()" ${cloudLoading ? 'disabled' : ''}>Connect</button>
1832
+ </span>`;
1833
+ }
1834
+
1835
+ async function cloudConnect() {
1836
+ cloudLoading = true;
1837
+ renderCloudStatus({ registered: true, state: 'connecting' });
1838
+ try {
1839
+ await authFetch('./api/tunnel/connect', { method: 'POST' });
1840
+ showToast('Connecting to cloud...', 'success');
1841
+ } catch (err) {
1842
+ showToast('Connect failed: ' + err.message, 'error');
1843
+ }
1844
+ cloudLoading = false;
1845
+ // Refresh will pick up new status
1846
+ setTimeout(refresh, 2000);
1847
+ }
1848
+
1849
+ async function cloudDisconnect() {
1850
+ cloudLoading = true;
1851
+ try {
1852
+ await authFetch('./api/tunnel/disconnect', { method: 'POST' });
1853
+ showToast('Disconnected from cloud', 'success');
1854
+ } catch (err) {
1855
+ showToast('Disconnect failed: ' + err.message, 'error');
1856
+ }
1857
+ cloudLoading = false;
1858
+ setTimeout(refresh, 1000);
1859
+ }
1860
+
1255
1861
  // Initialize
1256
1862
  init();
1257
1863
  </script>