@cluesmith/codev 2.0.0-rc.6 → 2.0.0-rc.60

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 (374) 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-CXloFYpB.css +32 -0
  5. package/dashboard/dist/assets/index-Ca2fjOJf.js +131 -0
  6. package/dashboard/dist/assets/index-Ca2fjOJf.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 +94 -65
  10. package/dist/agent-farm/cli.js.map +1 -1
  11. package/dist/agent-farm/commands/architect.d.ts.map +1 -1
  12. package/dist/agent-farm/commands/architect.js +13 -6
  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 +202 -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 +30 -3
  20. package/dist/agent-farm/commands/cleanup.js.map +1 -1
  21. package/dist/agent-farm/commands/consult.js +1 -1
  22. package/dist/agent-farm/commands/consult.js.map +1 -1
  23. package/dist/agent-farm/commands/index.d.ts +2 -2
  24. package/dist/agent-farm/commands/index.d.ts.map +1 -1
  25. package/dist/agent-farm/commands/index.js +2 -2
  26. package/dist/agent-farm/commands/index.js.map +1 -1
  27. package/dist/agent-farm/commands/open.d.ts +4 -2
  28. package/dist/agent-farm/commands/open.d.ts.map +1 -1
  29. package/dist/agent-farm/commands/open.js +37 -70
  30. package/dist/agent-farm/commands/open.js.map +1 -1
  31. package/dist/agent-farm/commands/send.d.ts.map +1 -1
  32. package/dist/agent-farm/commands/send.js +55 -17
  33. package/dist/agent-farm/commands/send.js.map +1 -1
  34. package/dist/agent-farm/commands/{util.d.ts → shell.d.ts} +5 -5
  35. package/dist/agent-farm/commands/shell.d.ts.map +1 -0
  36. package/dist/agent-farm/commands/{util.js → shell.js} +23 -36
  37. package/dist/agent-farm/commands/shell.js.map +1 -0
  38. package/dist/agent-farm/commands/spawn.d.ts.map +1 -1
  39. package/dist/agent-farm/commands/spawn.js +503 -226
  40. package/dist/agent-farm/commands/spawn.js.map +1 -1
  41. package/dist/agent-farm/commands/start.d.ts +3 -0
  42. package/dist/agent-farm/commands/start.d.ts.map +1 -1
  43. package/dist/agent-farm/commands/start.js +55 -265
  44. package/dist/agent-farm/commands/start.js.map +1 -1
  45. package/dist/agent-farm/commands/status.d.ts +2 -0
  46. package/dist/agent-farm/commands/status.d.ts.map +1 -1
  47. package/dist/agent-farm/commands/status.js +61 -3
  48. package/dist/agent-farm/commands/status.js.map +1 -1
  49. package/dist/agent-farm/commands/stop.d.ts +6 -0
  50. package/dist/agent-farm/commands/stop.d.ts.map +1 -1
  51. package/dist/agent-farm/commands/stop.js +116 -12
  52. package/dist/agent-farm/commands/stop.js.map +1 -1
  53. package/dist/agent-farm/commands/tower.d.ts +9 -0
  54. package/dist/agent-farm/commands/tower.d.ts.map +1 -1
  55. package/dist/agent-farm/commands/tower.js +59 -19
  56. package/dist/agent-farm/commands/tower.js.map +1 -1
  57. package/dist/agent-farm/db/index.d.ts.map +1 -1
  58. package/dist/agent-farm/db/index.js +124 -0
  59. package/dist/agent-farm/db/index.js.map +1 -1
  60. package/dist/agent-farm/db/schema.d.ts +2 -2
  61. package/dist/agent-farm/db/schema.d.ts.map +1 -1
  62. package/dist/agent-farm/db/schema.js +26 -5
  63. package/dist/agent-farm/db/schema.js.map +1 -1
  64. package/dist/agent-farm/db/types.d.ts +3 -0
  65. package/dist/agent-farm/db/types.d.ts.map +1 -1
  66. package/dist/agent-farm/db/types.js +3 -0
  67. package/dist/agent-farm/db/types.js.map +1 -1
  68. package/dist/agent-farm/hq-connector.d.ts +2 -6
  69. package/dist/agent-farm/hq-connector.d.ts.map +1 -1
  70. package/dist/agent-farm/hq-connector.js +2 -17
  71. package/dist/agent-farm/hq-connector.js.map +1 -1
  72. package/dist/agent-farm/lib/tower-client.d.ts +157 -0
  73. package/dist/agent-farm/lib/tower-client.d.ts.map +1 -0
  74. package/dist/agent-farm/lib/tower-client.js +223 -0
  75. package/dist/agent-farm/lib/tower-client.js.map +1 -0
  76. package/dist/agent-farm/servers/tower-server.js +2137 -112
  77. package/dist/agent-farm/servers/tower-server.js.map +1 -1
  78. package/dist/agent-farm/state.d.ts +4 -10
  79. package/dist/agent-farm/state.d.ts.map +1 -1
  80. package/dist/agent-farm/state.js +30 -31
  81. package/dist/agent-farm/state.js.map +1 -1
  82. package/dist/agent-farm/types.d.ts +48 -1
  83. package/dist/agent-farm/types.d.ts.map +1 -1
  84. package/dist/agent-farm/utils/config.d.ts.map +1 -1
  85. package/dist/agent-farm/utils/config.js +13 -14
  86. package/dist/agent-farm/utils/config.js.map +1 -1
  87. package/dist/agent-farm/utils/deps.d.ts.map +1 -1
  88. package/dist/agent-farm/utils/deps.js +0 -16
  89. package/dist/agent-farm/utils/deps.js.map +1 -1
  90. package/dist/agent-farm/utils/notifications.d.ts +30 -0
  91. package/dist/agent-farm/utils/notifications.d.ts.map +1 -0
  92. package/dist/agent-farm/utils/notifications.js +121 -0
  93. package/dist/agent-farm/utils/notifications.js.map +1 -0
  94. package/dist/agent-farm/utils/port-registry.d.ts +0 -1
  95. package/dist/agent-farm/utils/port-registry.d.ts.map +1 -1
  96. package/dist/agent-farm/utils/port-registry.js +1 -1
  97. package/dist/agent-farm/utils/port-registry.js.map +1 -1
  98. package/dist/agent-farm/utils/server-utils.d.ts +4 -4
  99. package/dist/agent-farm/utils/server-utils.d.ts.map +1 -1
  100. package/dist/agent-farm/utils/server-utils.js +4 -15
  101. package/dist/agent-farm/utils/server-utils.js.map +1 -1
  102. package/dist/agent-farm/utils/shell.d.ts +9 -22
  103. package/dist/agent-farm/utils/shell.d.ts.map +1 -1
  104. package/dist/agent-farm/utils/shell.js +34 -34
  105. package/dist/agent-farm/utils/shell.js.map +1 -1
  106. package/dist/agent-farm/utils/terminal-ports.d.ts +1 -1
  107. package/dist/agent-farm/utils/terminal-ports.js +1 -1
  108. package/dist/cli.d.ts.map +1 -1
  109. package/dist/cli.js +7 -54
  110. package/dist/cli.js.map +1 -1
  111. package/dist/commands/adopt.d.ts.map +1 -1
  112. package/dist/commands/adopt.js +49 -4
  113. package/dist/commands/adopt.js.map +1 -1
  114. package/dist/commands/consult/index.d.ts +1 -0
  115. package/dist/commands/consult/index.d.ts.map +1 -1
  116. package/dist/commands/consult/index.js +85 -6
  117. package/dist/commands/consult/index.js.map +1 -1
  118. package/dist/commands/doctor.d.ts.map +1 -1
  119. package/dist/commands/doctor.js +0 -15
  120. package/dist/commands/doctor.js.map +1 -1
  121. package/dist/commands/init.d.ts.map +1 -1
  122. package/dist/commands/init.js +41 -2
  123. package/dist/commands/init.js.map +1 -1
  124. package/dist/commands/porch/build-counter.d.ts +5 -0
  125. package/dist/commands/porch/build-counter.d.ts.map +1 -0
  126. package/dist/commands/porch/build-counter.js +5 -0
  127. package/dist/commands/porch/build-counter.js.map +1 -0
  128. package/dist/commands/porch/checks.d.ts +17 -29
  129. package/dist/commands/porch/checks.d.ts.map +1 -1
  130. package/dist/commands/porch/checks.js +96 -144
  131. package/dist/commands/porch/checks.js.map +1 -1
  132. package/dist/commands/porch/index.d.ts +21 -43
  133. package/dist/commands/porch/index.d.ts.map +1 -1
  134. package/dist/commands/porch/index.js +418 -1123
  135. package/dist/commands/porch/index.js.map +1 -1
  136. package/dist/commands/porch/next.d.ts +22 -0
  137. package/dist/commands/porch/next.d.ts.map +1 -0
  138. package/dist/commands/porch/next.js +479 -0
  139. package/dist/commands/porch/next.js.map +1 -0
  140. package/dist/commands/porch/plan.d.ts +70 -0
  141. package/dist/commands/porch/plan.d.ts.map +1 -0
  142. package/dist/commands/porch/plan.js +190 -0
  143. package/dist/commands/porch/plan.js.map +1 -0
  144. package/dist/commands/porch/prompts.d.ts +19 -0
  145. package/dist/commands/porch/prompts.d.ts.map +1 -0
  146. package/dist/commands/porch/prompts.js +255 -0
  147. package/dist/commands/porch/prompts.js.map +1 -0
  148. package/dist/commands/porch/protocol.d.ts +59 -0
  149. package/dist/commands/porch/protocol.d.ts.map +1 -0
  150. package/dist/commands/porch/protocol.js +294 -0
  151. package/dist/commands/porch/protocol.js.map +1 -0
  152. package/dist/commands/porch/state.d.ts +23 -112
  153. package/dist/commands/porch/state.d.ts.map +1 -1
  154. package/dist/commands/porch/state.js +81 -699
  155. package/dist/commands/porch/state.js.map +1 -1
  156. package/dist/commands/porch/types.d.ts +99 -164
  157. package/dist/commands/porch/types.d.ts.map +1 -1
  158. package/dist/commands/porch/types.js +2 -1
  159. package/dist/commands/porch/types.js.map +1 -1
  160. package/dist/commands/porch/verdict.d.ts +31 -0
  161. package/dist/commands/porch/verdict.d.ts.map +1 -0
  162. package/dist/commands/porch/verdict.js +59 -0
  163. package/dist/commands/porch/verdict.js.map +1 -0
  164. package/dist/commands/update.d.ts.map +1 -1
  165. package/dist/commands/update.js +31 -0
  166. package/dist/commands/update.js.map +1 -1
  167. package/dist/lib/scaffold.d.ts +37 -0
  168. package/dist/lib/scaffold.d.ts.map +1 -1
  169. package/dist/lib/scaffold.js +114 -0
  170. package/dist/lib/scaffold.js.map +1 -1
  171. package/dist/terminal/index.d.ts +8 -0
  172. package/dist/terminal/index.d.ts.map +1 -0
  173. package/dist/terminal/index.js +5 -0
  174. package/dist/terminal/index.js.map +1 -0
  175. package/dist/terminal/pty-manager.d.ts +60 -0
  176. package/dist/terminal/pty-manager.d.ts.map +1 -0
  177. package/dist/terminal/pty-manager.js +334 -0
  178. package/dist/terminal/pty-manager.js.map +1 -0
  179. package/dist/terminal/pty-session.d.ts +79 -0
  180. package/dist/terminal/pty-session.d.ts.map +1 -0
  181. package/dist/terminal/pty-session.js +215 -0
  182. package/dist/terminal/pty-session.js.map +1 -0
  183. package/dist/terminal/ring-buffer.d.ts +27 -0
  184. package/dist/terminal/ring-buffer.d.ts.map +1 -0
  185. package/dist/terminal/ring-buffer.js +74 -0
  186. package/dist/terminal/ring-buffer.js.map +1 -0
  187. package/dist/terminal/ws-protocol.d.ts +27 -0
  188. package/dist/terminal/ws-protocol.d.ts.map +1 -0
  189. package/dist/terminal/ws-protocol.js +44 -0
  190. package/dist/terminal/ws-protocol.js.map +1 -0
  191. package/package.json +18 -5
  192. package/skeleton/.claude/skills/af/SKILL.md +74 -0
  193. package/skeleton/.claude/skills/codev/SKILL.md +41 -0
  194. package/skeleton/.claude/skills/consult/SKILL.md +81 -0
  195. package/skeleton/.claude/skills/generate-image/SKILL.md +56 -0
  196. package/skeleton/DEPENDENCIES.md +3 -29
  197. package/skeleton/builders.md +1 -1
  198. package/skeleton/porch/prompts/defend.md +1 -1
  199. package/skeleton/porch/prompts/evaluate.md +2 -2
  200. package/skeleton/porch/prompts/implement.md +1 -1
  201. package/skeleton/porch/prompts/plan.md +1 -1
  202. package/skeleton/porch/prompts/review.md +4 -4
  203. package/skeleton/porch/prompts/specify.md +1 -1
  204. package/skeleton/porch/prompts/understand.md +2 -2
  205. package/skeleton/protocol-schema.json +282 -0
  206. package/skeleton/protocols/bugfix/builder-prompt.md +54 -0
  207. package/skeleton/protocols/bugfix/prompts/fix.md +77 -0
  208. package/skeleton/protocols/bugfix/prompts/investigate.md +77 -0
  209. package/skeleton/protocols/bugfix/prompts/pr.md +61 -0
  210. package/skeleton/protocols/bugfix/protocol.json +19 -2
  211. package/skeleton/protocols/experiment/builder-prompt.md +52 -0
  212. package/skeleton/protocols/experiment/protocol.json +101 -0
  213. package/skeleton/protocols/experiment/protocol.md +3 -3
  214. package/skeleton/protocols/experiment/templates/notes.md +1 -1
  215. package/skeleton/protocols/maintain/builder-prompt.md +46 -0
  216. package/skeleton/protocols/maintain/prompts/audit.md +111 -0
  217. package/skeleton/protocols/maintain/prompts/clean.md +91 -0
  218. package/skeleton/protocols/maintain/prompts/sync.md +113 -0
  219. package/skeleton/protocols/maintain/prompts/verify.md +110 -0
  220. package/skeleton/protocols/maintain/protocol.json +141 -0
  221. package/skeleton/protocols/maintain/protocol.md +14 -8
  222. package/skeleton/protocols/protocol-schema.json +54 -1
  223. package/skeleton/protocols/spir/builder-prompt.md +59 -0
  224. package/skeleton/protocols/spir/prompts/implement.md +208 -0
  225. package/skeleton/protocols/{spider → spir}/prompts/plan.md +6 -70
  226. package/skeleton/protocols/{spider → spir}/prompts/review.md +7 -25
  227. package/skeleton/protocols/{spider → spir}/prompts/specify.md +33 -61
  228. package/skeleton/protocols/spir/protocol.json +152 -0
  229. package/skeleton/protocols/{spider → spir}/protocol.md +35 -21
  230. package/skeleton/protocols/{spider → spir}/templates/plan.md +14 -0
  231. package/skeleton/protocols/{spider → spir}/templates/review.md +1 -1
  232. package/skeleton/protocols/tick/builder-prompt.md +56 -0
  233. package/skeleton/protocols/tick/protocol.json +7 -2
  234. package/skeleton/protocols/tick/protocol.md +18 -18
  235. package/skeleton/protocols/tick/templates/review.md +1 -1
  236. package/skeleton/resources/commands/agent-farm.md +25 -43
  237. package/skeleton/resources/commands/overview.md +7 -17
  238. package/skeleton/resources/workflow-reference.md +4 -4
  239. package/skeleton/roles/architect.md +152 -315
  240. package/skeleton/roles/builder.md +109 -218
  241. package/skeleton/templates/AGENTS.md +2 -2
  242. package/skeleton/templates/CLAUDE.md +2 -2
  243. package/skeleton/templates/cheatsheet.md +7 -5
  244. package/skeleton/templates/projectlist.md +1 -1
  245. package/templates/dashboard/index.html +17 -43
  246. package/templates/dashboard/js/dialogs.js +7 -7
  247. package/templates/dashboard/js/files.js +2 -2
  248. package/templates/dashboard/js/main.js +4 -4
  249. package/templates/dashboard/js/projects.js +3 -3
  250. package/templates/dashboard/js/tabs.js +1 -1
  251. package/templates/dashboard/js/utils.js +22 -87
  252. package/templates/open.html +26 -0
  253. package/templates/tower.html +542 -27
  254. package/dist/agent-farm/commands/kickoff.d.ts +0 -19
  255. package/dist/agent-farm/commands/kickoff.d.ts.map +0 -1
  256. package/dist/agent-farm/commands/kickoff.js +0 -331
  257. package/dist/agent-farm/commands/kickoff.js.map +0 -1
  258. package/dist/agent-farm/commands/rename.d.ts +0 -13
  259. package/dist/agent-farm/commands/rename.d.ts.map +0 -1
  260. package/dist/agent-farm/commands/rename.js +0 -33
  261. package/dist/agent-farm/commands/rename.js.map +0 -1
  262. package/dist/agent-farm/commands/tutorial.d.ts +0 -10
  263. package/dist/agent-farm/commands/tutorial.d.ts.map +0 -1
  264. package/dist/agent-farm/commands/tutorial.js +0 -49
  265. package/dist/agent-farm/commands/tutorial.js.map +0 -1
  266. package/dist/agent-farm/commands/util.d.ts.map +0 -1
  267. package/dist/agent-farm/commands/util.js.map +0 -1
  268. package/dist/agent-farm/servers/dashboard-server.d.ts +0 -7
  269. package/dist/agent-farm/servers/dashboard-server.d.ts.map +0 -1
  270. package/dist/agent-farm/servers/dashboard-server.js +0 -1872
  271. package/dist/agent-farm/servers/dashboard-server.js.map +0 -1
  272. package/dist/agent-farm/servers/open-server.d.ts +0 -7
  273. package/dist/agent-farm/servers/open-server.d.ts.map +0 -1
  274. package/dist/agent-farm/servers/open-server.js +0 -315
  275. package/dist/agent-farm/servers/open-server.js.map +0 -1
  276. package/dist/agent-farm/tutorial/index.d.ts +0 -8
  277. package/dist/agent-farm/tutorial/index.d.ts.map +0 -1
  278. package/dist/agent-farm/tutorial/index.js +0 -8
  279. package/dist/agent-farm/tutorial/index.js.map +0 -1
  280. package/dist/agent-farm/tutorial/prompts.d.ts +0 -57
  281. package/dist/agent-farm/tutorial/prompts.d.ts.map +0 -1
  282. package/dist/agent-farm/tutorial/prompts.js +0 -147
  283. package/dist/agent-farm/tutorial/prompts.js.map +0 -1
  284. package/dist/agent-farm/tutorial/runner.d.ts +0 -52
  285. package/dist/agent-farm/tutorial/runner.d.ts.map +0 -1
  286. package/dist/agent-farm/tutorial/runner.js +0 -204
  287. package/dist/agent-farm/tutorial/runner.js.map +0 -1
  288. package/dist/agent-farm/tutorial/state.d.ts +0 -26
  289. package/dist/agent-farm/tutorial/state.d.ts.map +0 -1
  290. package/dist/agent-farm/tutorial/state.js +0 -89
  291. package/dist/agent-farm/tutorial/state.js.map +0 -1
  292. package/dist/agent-farm/tutorial/steps/first-spec.d.ts +0 -7
  293. package/dist/agent-farm/tutorial/steps/first-spec.d.ts.map +0 -1
  294. package/dist/agent-farm/tutorial/steps/first-spec.js +0 -136
  295. package/dist/agent-farm/tutorial/steps/first-spec.js.map +0 -1
  296. package/dist/agent-farm/tutorial/steps/implementation.d.ts +0 -7
  297. package/dist/agent-farm/tutorial/steps/implementation.d.ts.map +0 -1
  298. package/dist/agent-farm/tutorial/steps/implementation.js +0 -76
  299. package/dist/agent-farm/tutorial/steps/implementation.js.map +0 -1
  300. package/dist/agent-farm/tutorial/steps/index.d.ts +0 -10
  301. package/dist/agent-farm/tutorial/steps/index.d.ts.map +0 -1
  302. package/dist/agent-farm/tutorial/steps/index.js +0 -10
  303. package/dist/agent-farm/tutorial/steps/index.js.map +0 -1
  304. package/dist/agent-farm/tutorial/steps/planning.d.ts +0 -7
  305. package/dist/agent-farm/tutorial/steps/planning.d.ts.map +0 -1
  306. package/dist/agent-farm/tutorial/steps/planning.js +0 -143
  307. package/dist/agent-farm/tutorial/steps/planning.js.map +0 -1
  308. package/dist/agent-farm/tutorial/steps/review.d.ts +0 -7
  309. package/dist/agent-farm/tutorial/steps/review.d.ts.map +0 -1
  310. package/dist/agent-farm/tutorial/steps/review.js +0 -78
  311. package/dist/agent-farm/tutorial/steps/review.js.map +0 -1
  312. package/dist/agent-farm/tutorial/steps/setup.d.ts +0 -7
  313. package/dist/agent-farm/tutorial/steps/setup.d.ts.map +0 -1
  314. package/dist/agent-farm/tutorial/steps/setup.js +0 -126
  315. package/dist/agent-farm/tutorial/steps/setup.js.map +0 -1
  316. package/dist/agent-farm/tutorial/steps/welcome.d.ts +0 -7
  317. package/dist/agent-farm/tutorial/steps/welcome.d.ts.map +0 -1
  318. package/dist/agent-farm/tutorial/steps/welcome.js +0 -50
  319. package/dist/agent-farm/tutorial/steps/welcome.js.map +0 -1
  320. package/dist/commands/pcheck/cache.d.ts +0 -48
  321. package/dist/commands/pcheck/cache.d.ts.map +0 -1
  322. package/dist/commands/pcheck/cache.js +0 -170
  323. package/dist/commands/pcheck/cache.js.map +0 -1
  324. package/dist/commands/pcheck/evaluator.d.ts +0 -15
  325. package/dist/commands/pcheck/evaluator.d.ts.map +0 -1
  326. package/dist/commands/pcheck/evaluator.js +0 -246
  327. package/dist/commands/pcheck/evaluator.js.map +0 -1
  328. package/dist/commands/pcheck/index.d.ts +0 -12
  329. package/dist/commands/pcheck/index.d.ts.map +0 -1
  330. package/dist/commands/pcheck/index.js +0 -249
  331. package/dist/commands/pcheck/index.js.map +0 -1
  332. package/dist/commands/pcheck/parser.d.ts +0 -39
  333. package/dist/commands/pcheck/parser.d.ts.map +0 -1
  334. package/dist/commands/pcheck/parser.js +0 -155
  335. package/dist/commands/pcheck/parser.js.map +0 -1
  336. package/dist/commands/pcheck/types.d.ts +0 -82
  337. package/dist/commands/pcheck/types.d.ts.map +0 -1
  338. package/dist/commands/pcheck/types.js +0 -5
  339. package/dist/commands/pcheck/types.js.map +0 -1
  340. package/dist/commands/porch/consultation.d.ts +0 -56
  341. package/dist/commands/porch/consultation.d.ts.map +0 -1
  342. package/dist/commands/porch/consultation.js +0 -330
  343. package/dist/commands/porch/consultation.js.map +0 -1
  344. package/dist/commands/porch/notifications.d.ts +0 -99
  345. package/dist/commands/porch/notifications.d.ts.map +0 -1
  346. package/dist/commands/porch/notifications.js +0 -223
  347. package/dist/commands/porch/notifications.js.map +0 -1
  348. package/dist/commands/porch/plan-parser.d.ts +0 -38
  349. package/dist/commands/porch/plan-parser.d.ts.map +0 -1
  350. package/dist/commands/porch/plan-parser.js +0 -166
  351. package/dist/commands/porch/plan-parser.js.map +0 -1
  352. package/dist/commands/porch/protocol-loader.d.ts +0 -46
  353. package/dist/commands/porch/protocol-loader.d.ts.map +0 -1
  354. package/dist/commands/porch/protocol-loader.js +0 -253
  355. package/dist/commands/porch/protocol-loader.js.map +0 -1
  356. package/dist/commands/porch/signal-parser.d.ts +0 -88
  357. package/dist/commands/porch/signal-parser.d.ts.map +0 -1
  358. package/dist/commands/porch/signal-parser.js +0 -148
  359. package/dist/commands/porch/signal-parser.js.map +0 -1
  360. package/dist/commands/tower.d.ts +0 -16
  361. package/dist/commands/tower.d.ts.map +0 -1
  362. package/dist/commands/tower.js +0 -21
  363. package/dist/commands/tower.js.map +0 -1
  364. package/skeleton/config.json +0 -7
  365. package/skeleton/porch/protocols/bugfix.json +0 -85
  366. package/skeleton/porch/protocols/spider.json +0 -135
  367. package/skeleton/porch/protocols/tick.json +0 -76
  368. package/skeleton/protocols/spider/prompts/defend.md +0 -215
  369. package/skeleton/protocols/spider/prompts/evaluate.md +0 -241
  370. package/skeleton/protocols/spider/prompts/implement.md +0 -149
  371. package/skeleton/protocols/spider/protocol.json +0 -210
  372. package/templates/dashboard/css/activity.css +0 -151
  373. package/templates/dashboard/js/activity.js +0 -112
  374. /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;
@@ -611,6 +645,132 @@
611
645
  }
612
646
  }
613
647
 
648
+ /* Mobile optimizations */
649
+ @media (max-width: 600px) {
650
+ body {
651
+ padding-bottom: env(safe-area-inset-bottom, 0);
652
+ }
653
+
654
+ .header {
655
+ padding: 16px;
656
+ padding-top: calc(16px + env(safe-area-inset-top, 0));
657
+ flex-wrap: wrap;
658
+ gap: 12px;
659
+ }
660
+
661
+ .header h1 {
662
+ font-size: 18px;
663
+ width: 100%;
664
+ }
665
+
666
+ .header h1 .emoji {
667
+ font-size: 22px;
668
+ }
669
+
670
+ .header-actions {
671
+ width: 100%;
672
+ justify-content: flex-end;
673
+ }
674
+
675
+ .main {
676
+ padding: 16px;
677
+ padding-left: calc(16px + env(safe-area-inset-left, 0));
678
+ padding-right: calc(16px + env(safe-area-inset-right, 0));
679
+ }
680
+
681
+ .btn {
682
+ min-height: 44px;
683
+ min-width: 44px;
684
+ padding: 12px 16px;
685
+ }
686
+
687
+ .btn-small {
688
+ min-height: 44px;
689
+ padding: 10px 14px;
690
+ }
691
+
692
+ .instance-header {
693
+ flex-direction: column;
694
+ align-items: flex-start;
695
+ gap: 12px;
696
+ padding: 16px;
697
+ }
698
+
699
+ .instance-actions {
700
+ width: 100%;
701
+ justify-content: flex-end;
702
+ }
703
+
704
+ .port-item {
705
+ flex-direction: column;
706
+ align-items: flex-start;
707
+ gap: 8px;
708
+ }
709
+
710
+ .port-actions {
711
+ width: 100%;
712
+ }
713
+
714
+ .port-actions a {
715
+ min-height: 44px;
716
+ display: flex;
717
+ align-items: center;
718
+ justify-content: center;
719
+ flex: 1;
720
+ }
721
+
722
+ .launch-section {
723
+ padding: 16px;
724
+ }
725
+
726
+ .launch-form {
727
+ flex-direction: column;
728
+ }
729
+
730
+ .launch-form input[type="text"] {
731
+ min-height: 44px;
732
+ }
733
+
734
+ .btn-launch {
735
+ width: 100%;
736
+ min-height: 44px;
737
+ }
738
+
739
+ .recent-item {
740
+ flex-direction: column;
741
+ align-items: flex-start;
742
+ gap: 12px;
743
+ padding: 16px;
744
+ }
745
+
746
+ .dialog-box {
747
+ min-width: auto;
748
+ width: calc(100% - 32px);
749
+ margin: 16px;
750
+ }
751
+
752
+ .toast-container {
753
+ left: 16px;
754
+ right: 16px;
755
+ bottom: calc(16px + env(safe-area-inset-bottom, 0));
756
+ }
757
+ }
758
+
759
+ /* Touch-friendly tap targets */
760
+ @media (pointer: coarse) {
761
+ .btn, .port-actions a, .autocomplete-item, .recent-item {
762
+ min-height: 44px;
763
+ }
764
+
765
+ .copy-btn {
766
+ min-width: 44px;
767
+ min-height: 44px;
768
+ display: flex;
769
+ align-items: center;
770
+ justify-content: center;
771
+ }
772
+ }
773
+
614
774
  /* Dialog styles */
615
775
  .dialog-overlay {
616
776
  position: fixed;
@@ -680,6 +840,7 @@
680
840
  Agent Farm Control Tower
681
841
  </h1>
682
842
  <div class="header-actions">
843
+ <button class="btn" id="share-btn" onclick="showShareDialog()" style="display: none;" title="Share via QR code">📱 Share</button>
683
844
  <button class="btn" onclick="refresh()">Refresh</button>
684
845
  </div>
685
846
  </header>
@@ -733,6 +894,23 @@
733
894
  </div>
734
895
  </div>
735
896
  </div>
897
+
898
+ <!-- Share Dialog (QR Code) -->
899
+ <div class="dialog-overlay" id="share-dialog" style="display: none;">
900
+ <div class="dialog-box" style="text-align: center;">
901
+ <h3>📱 Share via QR Code</h3>
902
+ <div id="share-content">
903
+ <div class="loading">
904
+ <div class="spinner"></div>
905
+ <p>Starting tunnel...</p>
906
+ </div>
907
+ </div>
908
+ <div class="dialog-actions" style="margin-top: 16px;">
909
+ <button class="btn" onclick="hideShareDialog()">Close</button>
910
+ <button class="btn" id="stop-tunnel-btn" onclick="stopTunnelAndClose()" style="display: none;">Stop Tunnel</button>
911
+ </div>
912
+ </div>
913
+ </div>
736
914
  </main>
737
915
 
738
916
  <div class="toast-container" id="toast-container"></div>
@@ -742,17 +920,153 @@
742
920
  let runningInstances = [];
743
921
  let recentInstances = [];
744
922
 
923
+ // Auth helper: get headers with auth token if available
924
+ function getAuthHeaders() {
925
+ const key = localStorage.getItem('codev_web_key');
926
+ return key ? { 'Authorization': `Bearer ${key}` } : {};
927
+ }
928
+
929
+ // Auth helper: make authenticated fetch request
930
+ async function authFetch(url, options = {}) {
931
+ const headers = { ...getAuthHeaders(), ...(options.headers || {}) };
932
+ const response = await fetch(url, { ...options, headers });
933
+
934
+ // If 401, clear key and redirect to login
935
+ if (response.status === 401) {
936
+ localStorage.removeItem('codev_web_key');
937
+ location.reload();
938
+ throw new Error('Unauthorized');
939
+ }
940
+ return response;
941
+ }
942
+
943
+ // Logout function
944
+ function logout() {
945
+ localStorage.removeItem('codev_web_key');
946
+ window.location.href = '/';
947
+ }
948
+
745
949
  // Initialize
746
950
  async function init() {
951
+ // Check if cloudflared is available and show share button
952
+ try {
953
+ const tunnelRes = await fetch('/api/tunnel/status');
954
+ const tunnelStatus = await tunnelRes.json();
955
+ if (tunnelStatus.available) {
956
+ document.getElementById('share-btn').style.display = 'inline-block';
957
+ }
958
+ } catch (e) {
959
+ // Tunnel not available, button stays hidden
960
+ }
961
+
962
+ // Request notification permission
963
+ if ('Notification' in window && Notification.permission === 'default') {
964
+ try {
965
+ await Notification.requestPermission();
966
+ } catch (e) {
967
+ // Ignore errors, notifications are optional
968
+ }
969
+ }
970
+
971
+ // Subscribe to SSE for push notifications
972
+ subscribeToEvents();
973
+
747
974
  await refresh();
748
975
  // Poll every 5 seconds
749
976
  setInterval(refresh, 5000);
750
977
  }
751
978
 
979
+ // SSE subscription for real-time notifications
980
+ // Uses fetch + ReadableStream to support Authorization header
981
+ let sseController = null;
982
+
983
+ async function subscribeToEvents() {
984
+ // Abort any existing connection
985
+ if (sseController) {
986
+ sseController.abort();
987
+ }
988
+
989
+ sseController = new AbortController();
990
+
991
+ try {
992
+ const response = await fetch('/api/events', {
993
+ headers: getAuthHeaders(),
994
+ signal: sseController.signal,
995
+ });
996
+
997
+ if (!response.ok) {
998
+ console.log('SSE connection failed:', response.status);
999
+ setTimeout(subscribeToEvents, 5000);
1000
+ return;
1001
+ }
1002
+
1003
+ const reader = response.body.getReader();
1004
+ const decoder = new TextDecoder();
1005
+ let buffer = '';
1006
+
1007
+ while (true) {
1008
+ const { done, value } = await reader.read();
1009
+
1010
+ if (done) {
1011
+ console.log('SSE stream ended, reconnecting...');
1012
+ setTimeout(subscribeToEvents, 5000);
1013
+ return;
1014
+ }
1015
+
1016
+ buffer += decoder.decode(value, { stream: true });
1017
+
1018
+ // Parse SSE format: "data: {...}\n\n"
1019
+ const lines = buffer.split('\n\n');
1020
+ buffer = lines.pop() || ''; // Keep incomplete message in buffer
1021
+
1022
+ for (const chunk of lines) {
1023
+ const dataMatch = chunk.match(/^data:\s*(.+)$/m);
1024
+ if (!dataMatch) continue;
1025
+
1026
+ try {
1027
+ const data = JSON.parse(dataMatch[1]);
1028
+
1029
+ if (data.type === 'connected') {
1030
+ console.log('SSE connected:', data.id);
1031
+ continue;
1032
+ }
1033
+
1034
+ // Show browser notification
1035
+ if ('Notification' in window && Notification.permission === 'granted') {
1036
+ const notification = new Notification(data.title, {
1037
+ body: data.body,
1038
+ icon: '/favicon.ico',
1039
+ tag: 'tower-notification-' + data.id,
1040
+ });
1041
+
1042
+ // Auto-close after 5 seconds
1043
+ setTimeout(() => notification.close(), 5000);
1044
+ }
1045
+
1046
+ // Also show in-app toast
1047
+ showToast(`${data.title}: ${data.body}`, data.type || 'info');
1048
+
1049
+ // Refresh to show updated status
1050
+ refresh();
1051
+ } catch (e) {
1052
+ console.error('SSE parse error:', e);
1053
+ }
1054
+ }
1055
+ }
1056
+ } catch (e) {
1057
+ if (e.name === 'AbortError') {
1058
+ // Intentional abort, don't reconnect
1059
+ return;
1060
+ }
1061
+ console.error('SSE error:', e);
1062
+ setTimeout(subscribeToEvents, 5000);
1063
+ }
1064
+ }
1065
+
752
1066
  // Refresh data from API
753
1067
  async function refresh() {
754
1068
  try {
755
- const response = await fetch('/api/status');
1069
+ const response = await authFetch('/api/status');
756
1070
  if (!response.ok) throw new Error('Failed to fetch status');
757
1071
 
758
1072
  const data = await response.json();
@@ -774,7 +1088,7 @@
774
1088
  <div class="empty-state">
775
1089
  <div class="icon">📭</div>
776
1090
  <h2>No running instances</h2>
777
- <p>Start a new instance below or run <code>af start</code> in a project directory.</p>
1091
+ <p>Start a new instance below or run <code>af dash start</code> in a project directory.</p>
778
1092
  </div>
779
1093
  `;
780
1094
  } else {
@@ -810,28 +1124,69 @@
810
1124
  function renderInstance(instance) {
811
1125
  const statusClass = instance.running ? 'running' : 'stopped';
812
1126
  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>
1127
+ const hasGate = instance.gateStatus?.hasGate;
1128
+
1129
+ // Render terminals list - always show Overview first, then individual terminals
1130
+ const terminals = instance.terminals || [];
1131
+ let terminalsHtml = '';
1132
+
1133
+ if (instance.running) {
1134
+ // Always show Overview link first (opens split view with Architect + Status panel)
1135
+ terminalsHtml = `
1136
+ <div class="port-item">
1137
+ <div class="port-info">
1138
+ <span class="port-status active"></span>
1139
+ <span class="port-type">Overview</span>
1140
+ </div>
1141
+ <div class="port-actions">
1142
+ <a href="${escapeHtml(instance.proxyUrl)}" target="_blank">Open</a>
1143
+ </div>
820
1144
  </div>
821
- <div class="port-actions">
822
- <a href="${escapeHtml(port.url)}" target="_blank" class="${port.active ? '' : 'disabled'}">
823
- Open
824
- </a>
1145
+ `;
1146
+
1147
+ // Then show individual terminals (open full-screen terminal view)
1148
+ if (terminals.length > 0) {
1149
+ terminalsHtml += terminals.map(terminal => `
1150
+ <div class="port-item">
1151
+ <div class="port-info">
1152
+ <span class="port-status ${terminal.active ? 'active' : 'inactive'}"></span>
1153
+ <span class="port-type">${escapeHtml(terminal.label)}</span>
1154
+ </div>
1155
+ <div class="port-actions">
1156
+ <a href="${escapeHtml(terminal.url)}&fullscreen=1" target="_blank">Open</a>
1157
+ </div>
1158
+ </div>
1159
+ `).join('');
1160
+ }
1161
+
1162
+ // Add "New Shell" button for this instance
1163
+ terminalsHtml += `
1164
+ <div class="port-item" style="border-top: 1px dashed var(--border); margin-top: 8px; padding-top: 8px;">
1165
+ <div class="port-info">
1166
+ <span class="port-status" style="background: var(--accent);"></span>
1167
+ <span class="port-type">New Shell</span>
1168
+ </div>
1169
+ <div class="port-actions">
1170
+ <button class="btn btn-small" onclick="createShell('${escapeHtml(instance.projectPath)}')">+ Create</button>
1171
+ </div>
825
1172
  </div>
826
- </div>
827
- `).join('');
1173
+ `;
1174
+ }
828
1175
 
829
1176
  const lastUsed = instance.lastUsed
830
1177
  ? formatDate(instance.lastUsed)
831
1178
  : 'Never';
832
1179
 
1180
+ const gateIndicatorHtml = hasGate ? `
1181
+ <div class="gate-indicator">
1182
+ <span>⏳</span>
1183
+ <span class="gate-name">${escapeHtml(instance.gateStatus.gateName || 'approval')}</span>
1184
+ ${instance.gateStatus.builderId ? `<span class="gate-builder">(${escapeHtml(instance.gateStatus.builderId)})</span>` : ''}
1185
+ </div>
1186
+ ` : '';
1187
+
833
1188
  return `
834
- <div class="instance">
1189
+ <div class="instance ${hasGate ? 'gate-pending' : ''}" data-project="${escapeHtml(instance.projectPath)}">
835
1190
  <div class="instance-header">
836
1191
  <div class="instance-title">
837
1192
  <span class="instance-name">${escapeHtml(instance.projectName)}</span>
@@ -849,6 +1204,7 @@
849
1204
  `}
850
1205
  </div>
851
1206
  </div>
1207
+ ${gateIndicatorHtml}
852
1208
  <div class="instance-path-row">
853
1209
  <span class="instance-path" title="${escapeHtml(instance.projectPath)}">
854
1210
  ${escapeHtml(instance.projectPath)}
@@ -857,11 +1213,10 @@
857
1213
  </div>
858
1214
  <div class="instance-body">
859
1215
  <div class="ports-list">
860
- ${portsHtml}
1216
+ ${terminalsHtml}
861
1217
  </div>
862
1218
  <div class="instance-meta">
863
1219
  <span>Last active: ${lastUsed}</span>
864
- <span>Port block: ${instance.basePort}-${instance.basePort + 99}</span>
865
1220
  </div>
866
1221
  </div>
867
1222
  </div>
@@ -952,7 +1307,7 @@
952
1307
  }
953
1308
 
954
1309
  try {
955
- const response = await fetch('/api/browse?path=' + encodeURIComponent(inputPath));
1310
+ const response = await authFetch('/api/browse?path=' + encodeURIComponent(inputPath));
956
1311
  const data = await response.json();
957
1312
  suggestions = data.suggestions || [];
958
1313
  selectedIndex = -1;
@@ -1007,7 +1362,7 @@
1007
1362
  // Launch a specific path (from recents)
1008
1363
  async function launchPath(projectPath) {
1009
1364
  try {
1010
- const response = await fetch('/api/launch', {
1365
+ const response = await authFetch('/api/launch', {
1011
1366
  method: 'POST',
1012
1367
  headers: { 'Content-Type': 'application/json' },
1013
1368
  body: JSON.stringify({ projectPath })
@@ -1033,7 +1388,7 @@
1033
1388
  // Stop an instance by port
1034
1389
  async function stopInstance(basePort) {
1035
1390
  try {
1036
- const response = await fetch('/api/stop', {
1391
+ const response = await authFetch('/api/stop', {
1037
1392
  method: 'POST',
1038
1393
  headers: { 'Content-Type': 'application/json' },
1039
1394
  body: JSON.stringify({ basePort })
@@ -1056,7 +1411,7 @@
1056
1411
  async function restartInstance(basePort, projectPath) {
1057
1412
  try {
1058
1413
  // First stop
1059
- await fetch('/api/stop', {
1414
+ await authFetch('/api/stop', {
1060
1415
  method: 'POST',
1061
1416
  headers: { 'Content-Type': 'application/json' },
1062
1417
  body: JSON.stringify({ basePort })
@@ -1066,7 +1421,7 @@
1066
1421
  await new Promise(r => setTimeout(r, 1000));
1067
1422
 
1068
1423
  // Then start
1069
- const response = await fetch('/api/launch', {
1424
+ const response = await authFetch('/api/launch', {
1070
1425
  method: 'POST',
1071
1426
  headers: { 'Content-Type': 'application/json' },
1072
1427
  body: JSON.stringify({ projectPath })
@@ -1096,7 +1451,7 @@
1096
1451
  }
1097
1452
 
1098
1453
  try {
1099
- const response = await fetch('/api/launch', {
1454
+ const response = await authFetch('/api/launch', {
1100
1455
  method: 'POST',
1101
1456
  headers: { 'Content-Type': 'application/json' },
1102
1457
  body: JSON.stringify({ projectPath })
@@ -1122,6 +1477,34 @@
1122
1477
  }
1123
1478
  }
1124
1479
 
1480
+ // Create a new shell for a running instance (via tower proxy)
1481
+ async function createShell(projectPath) {
1482
+ try {
1483
+ // Use tower proxy to route to the project's dashboard API
1484
+ const encodedPath = toBase64URL(projectPath);
1485
+ const response = await authFetch(`/project/${encodedPath}/api/tabs/shell`, {
1486
+ method: 'POST',
1487
+ headers: { 'Content-Type': 'application/json' },
1488
+ });
1489
+
1490
+ if (!response.ok) {
1491
+ const errorText = await response.text();
1492
+ throw new Error(errorText || 'Failed to create shell');
1493
+ }
1494
+
1495
+ const result = await response.json();
1496
+
1497
+ if (result.success === false) {
1498
+ throw new Error(result.error || 'Failed to create shell');
1499
+ }
1500
+
1501
+ showToast(`Shell created: ${result.name || 'shell'}`, 'success');
1502
+ setTimeout(refresh, 1000);
1503
+ } catch (err) {
1504
+ showToast('Failed to create shell: ' + err.message, 'error');
1505
+ }
1506
+ }
1507
+
1125
1508
  // Format date for display
1126
1509
  function formatDate(isoString) {
1127
1510
  const date = new Date(isoString);
@@ -1170,6 +1553,23 @@
1170
1553
  .replace(/'/g, '&#39;');
1171
1554
  }
1172
1555
 
1556
+ // Base64URL encoding (RFC 4648) for project paths
1557
+ // IMPORTANT: Use TextEncoder for Unicode support (btoa only handles Latin-1)
1558
+ function toBase64URL(str) {
1559
+ // Encode string to UTF-8 bytes, then to Base64, then to Base64URL
1560
+ const bytes = new TextEncoder().encode(str);
1561
+ const base64 = btoa(String.fromCharCode(...bytes));
1562
+ return base64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
1563
+ }
1564
+
1565
+ // Generate proxy URL for a project
1566
+ // All terminals are now multiplexed on a single port via WebSocket (Spec 0085)
1567
+ // The React dashboard handles tab selection internally
1568
+ function getProxyUrl(instance, portType) {
1569
+ const encodedPath = toBase64URL(instance.projectPath);
1570
+ return `/project/${encodedPath}/`;
1571
+ }
1572
+
1173
1573
  // Toast notifications
1174
1574
  function showToast(message, type = 'info') {
1175
1575
  const container = document.getElementById('toast-container');
@@ -1183,6 +1583,121 @@
1183
1583
  }, 4000);
1184
1584
  }
1185
1585
 
1586
+ // Share dialog functions (QR code for mobile access)
1587
+ let tunnelAbortController = null;
1588
+
1589
+ async function showShareDialog() {
1590
+ const dialog = document.getElementById('share-dialog');
1591
+ const content = document.getElementById('share-content');
1592
+ const stopBtn = document.getElementById('stop-tunnel-btn');
1593
+
1594
+ dialog.style.display = 'flex';
1595
+ stopBtn.style.display = 'none';
1596
+
1597
+ // Create abort controller for cancellation
1598
+ tunnelAbortController = new AbortController();
1599
+
1600
+ content.innerHTML = `
1601
+ <div class="loading">
1602
+ <div class="spinner"></div>
1603
+ <p>Starting tunnel...</p>
1604
+ <p style="font-size: 12px; color: var(--muted); margin-top: 8px;">This may take up to 30 seconds</p>
1605
+ <button class="btn" style="margin-top: 16px;" onclick="cancelTunnelStart()">Cancel</button>
1606
+ </div>
1607
+ `;
1608
+
1609
+ try {
1610
+ // Check if tunnel is already running
1611
+ const statusRes = await fetch('/api/tunnel/status', { signal: tunnelAbortController.signal });
1612
+ const status = await statusRes.json();
1613
+
1614
+ if (status.running && status.url) {
1615
+ showQRCode(status.url);
1616
+ return;
1617
+ }
1618
+
1619
+ // Start tunnel
1620
+ const res = await fetch('/api/tunnel/start', { method: 'POST', signal: tunnelAbortController.signal });
1621
+ const result = await res.json();
1622
+
1623
+ if (result.success && result.url) {
1624
+ showQRCode(result.url);
1625
+ } else {
1626
+ content.innerHTML = `<p style="color: var(--danger);">Failed to start tunnel: ${result.error || 'Unknown error'}</p>`;
1627
+ }
1628
+ } catch (e) {
1629
+ if (e.name === 'AbortError') {
1630
+ content.innerHTML = '<p>Tunnel start cancelled.</p>';
1631
+ } else {
1632
+ content.innerHTML = `<p style="color: var(--danger);">Error: ${e.message}</p>`;
1633
+ }
1634
+ } finally {
1635
+ tunnelAbortController = null;
1636
+ }
1637
+ }
1638
+
1639
+ function cancelTunnelStart() {
1640
+ if (tunnelAbortController) {
1641
+ tunnelAbortController.abort();
1642
+ }
1643
+ // Also stop any tunnel that might have started
1644
+ fetch('/api/tunnel/stop', { method: 'POST' }).catch(() => {});
1645
+ }
1646
+
1647
+ function showQRCode(tunnelUrl) {
1648
+ const content = document.getElementById('share-content');
1649
+ const stopBtn = document.getElementById('stop-tunnel-btn');
1650
+
1651
+ // Generate QR code using qrcode.js (loaded from CDN)
1652
+ content.innerHTML = `
1653
+ <p style="margin-bottom: 16px;">Scan to access from mobile:</p>
1654
+ <div id="qrcode" style="display: inline-block; padding: 16px; background: white; border-radius: 8px;"></div>
1655
+ <p style="margin-top: 16px; font-size: 12px; color: var(--muted);">
1656
+ <a href="${tunnelUrl}" target="_blank" style="color: var(--primary);">${tunnelUrl}</a>
1657
+ </p>
1658
+ `;
1659
+
1660
+ // Load QRCode library and generate
1661
+ if (!window.QRCode) {
1662
+ const script = document.createElement('script');
1663
+ script.src = 'https://cdn.jsdelivr.net/npm/qrcodejs@1.0.0/qrcode.min.js';
1664
+ script.onload = () => generateQR(tunnelUrl);
1665
+ document.head.appendChild(script);
1666
+ } else {
1667
+ generateQR(tunnelUrl);
1668
+ }
1669
+
1670
+ stopBtn.style.display = 'inline-block';
1671
+ }
1672
+
1673
+ function generateQR(url) {
1674
+ const container = document.getElementById('qrcode');
1675
+ if (container && window.QRCode) {
1676
+ container.innerHTML = '';
1677
+ new QRCode(container, {
1678
+ text: url,
1679
+ width: 200,
1680
+ height: 200,
1681
+ colorDark: '#000000',
1682
+ colorLight: '#ffffff',
1683
+ });
1684
+ }
1685
+ }
1686
+
1687
+ function hideShareDialog() {
1688
+ document.getElementById('share-dialog').style.display = 'none';
1689
+ }
1690
+
1691
+ async function stopTunnelAndClose() {
1692
+ try {
1693
+ await fetch('/api/tunnel/stop', { method: 'POST' });
1694
+ } catch (e) {
1695
+ // Ignore
1696
+ }
1697
+ hideShareDialog();
1698
+ showToast('Tunnel stopped', 'success');
1699
+ }
1700
+
1186
1701
  // Create project dialog functions
1187
1702
  function showCreateProjectDialog() {
1188
1703
  const dialog = document.getElementById('create-dialog');
@@ -1219,7 +1734,7 @@
1219
1734
  hideCreateProjectDialog();
1220
1735
 
1221
1736
  try {
1222
- const response = await fetch('/api/create', {
1737
+ const response = await authFetch('/api/create', {
1223
1738
  method: 'POST',
1224
1739
  headers: { 'Content-Type': 'application/json' },
1225
1740
  body: JSON.stringify({ parent, name })