@cluesmith/codev 2.0.0-rc.4 → 2.0.0-rc.40

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 (316) hide show
  1. package/bin/porch.js +6 -35
  2. package/dashboard/dist/assets/index-BLqoFC1H.js +120 -0
  3. package/dashboard/dist/assets/index-BLqoFC1H.js.map +1 -0
  4. package/dashboard/dist/assets/index-CXwnJkPh.css +32 -0
  5. package/dashboard/dist/index.html +13 -0
  6. package/dist/agent-farm/cli.d.ts.map +1 -1
  7. package/dist/agent-farm/cli.js +93 -64
  8. package/dist/agent-farm/cli.js.map +1 -1
  9. package/dist/agent-farm/commands/architect.d.ts.map +1 -1
  10. package/dist/agent-farm/commands/architect.js +13 -6
  11. package/dist/agent-farm/commands/architect.js.map +1 -1
  12. package/dist/agent-farm/commands/attach.d.ts +13 -0
  13. package/dist/agent-farm/commands/attach.d.ts.map +1 -0
  14. package/dist/agent-farm/commands/attach.js +179 -0
  15. package/dist/agent-farm/commands/attach.js.map +1 -0
  16. package/dist/agent-farm/commands/cleanup.d.ts.map +1 -1
  17. package/dist/agent-farm/commands/cleanup.js +30 -3
  18. package/dist/agent-farm/commands/cleanup.js.map +1 -1
  19. package/dist/agent-farm/commands/consult.js +1 -1
  20. package/dist/agent-farm/commands/consult.js.map +1 -1
  21. package/dist/agent-farm/commands/index.d.ts +2 -2
  22. package/dist/agent-farm/commands/index.d.ts.map +1 -1
  23. package/dist/agent-farm/commands/index.js +2 -2
  24. package/dist/agent-farm/commands/index.js.map +1 -1
  25. package/dist/agent-farm/commands/{util.d.ts → shell.d.ts} +5 -5
  26. package/dist/agent-farm/commands/shell.d.ts.map +1 -0
  27. package/dist/agent-farm/commands/{util.js → shell.js} +23 -36
  28. package/dist/agent-farm/commands/shell.js.map +1 -0
  29. package/dist/agent-farm/commands/spawn.d.ts.map +1 -1
  30. package/dist/agent-farm/commands/spawn.js +455 -217
  31. package/dist/agent-farm/commands/spawn.js.map +1 -1
  32. package/dist/agent-farm/commands/start.d.ts.map +1 -1
  33. package/dist/agent-farm/commands/start.js +27 -79
  34. package/dist/agent-farm/commands/start.js.map +1 -1
  35. package/dist/agent-farm/commands/stop.d.ts.map +1 -1
  36. package/dist/agent-farm/commands/stop.js +79 -10
  37. package/dist/agent-farm/commands/stop.js.map +1 -1
  38. package/dist/agent-farm/commands/tower.d.ts +9 -0
  39. package/dist/agent-farm/commands/tower.d.ts.map +1 -1
  40. package/dist/agent-farm/commands/tower.js +54 -18
  41. package/dist/agent-farm/commands/tower.js.map +1 -1
  42. package/dist/agent-farm/db/index.d.ts.map +1 -1
  43. package/dist/agent-farm/db/index.js +15 -0
  44. package/dist/agent-farm/db/index.js.map +1 -1
  45. package/dist/agent-farm/db/schema.d.ts +1 -1
  46. package/dist/agent-farm/db/schema.d.ts.map +1 -1
  47. package/dist/agent-farm/db/schema.js +6 -3
  48. package/dist/agent-farm/db/schema.js.map +1 -1
  49. package/dist/agent-farm/db/types.d.ts +3 -0
  50. package/dist/agent-farm/db/types.d.ts.map +1 -1
  51. package/dist/agent-farm/db/types.js +3 -0
  52. package/dist/agent-farm/db/types.js.map +1 -1
  53. package/dist/agent-farm/hq-connector.d.ts +2 -2
  54. package/dist/agent-farm/hq-connector.js +2 -2
  55. package/dist/agent-farm/servers/dashboard-server.js +435 -131
  56. package/dist/agent-farm/servers/dashboard-server.js.map +1 -1
  57. package/dist/agent-farm/servers/tower-server.js +430 -17
  58. package/dist/agent-farm/servers/tower-server.js.map +1 -1
  59. package/dist/agent-farm/state.d.ts +4 -10
  60. package/dist/agent-farm/state.d.ts.map +1 -1
  61. package/dist/agent-farm/state.js +30 -31
  62. package/dist/agent-farm/state.js.map +1 -1
  63. package/dist/agent-farm/types.d.ts +48 -0
  64. package/dist/agent-farm/types.d.ts.map +1 -1
  65. package/dist/agent-farm/utils/config.d.ts.map +1 -1
  66. package/dist/agent-farm/utils/config.js +12 -11
  67. package/dist/agent-farm/utils/config.js.map +1 -1
  68. package/dist/agent-farm/utils/deps.d.ts.map +1 -1
  69. package/dist/agent-farm/utils/deps.js +0 -16
  70. package/dist/agent-farm/utils/deps.js.map +1 -1
  71. package/dist/agent-farm/utils/notifications.d.ts +30 -0
  72. package/dist/agent-farm/utils/notifications.d.ts.map +1 -0
  73. package/dist/agent-farm/utils/notifications.js +121 -0
  74. package/dist/agent-farm/utils/notifications.js.map +1 -0
  75. package/dist/agent-farm/utils/server-utils.d.ts +2 -1
  76. package/dist/agent-farm/utils/server-utils.d.ts.map +1 -1
  77. package/dist/agent-farm/utils/server-utils.js +11 -1
  78. package/dist/agent-farm/utils/server-utils.js.map +1 -1
  79. package/dist/agent-farm/utils/shell.d.ts +9 -22
  80. package/dist/agent-farm/utils/shell.d.ts.map +1 -1
  81. package/dist/agent-farm/utils/shell.js +34 -34
  82. package/dist/agent-farm/utils/shell.js.map +1 -1
  83. package/dist/agent-farm/utils/terminal-ports.d.ts +1 -1
  84. package/dist/agent-farm/utils/terminal-ports.js +1 -1
  85. package/dist/cli.d.ts.map +1 -1
  86. package/dist/cli.js +5 -54
  87. package/dist/cli.js.map +1 -1
  88. package/dist/commands/adopt.d.ts.map +1 -1
  89. package/dist/commands/adopt.js +39 -4
  90. package/dist/commands/adopt.js.map +1 -1
  91. package/dist/commands/consult/index.d.ts.map +1 -1
  92. package/dist/commands/consult/index.js +63 -3
  93. package/dist/commands/consult/index.js.map +1 -1
  94. package/dist/commands/doctor.d.ts.map +1 -1
  95. package/dist/commands/doctor.js +0 -15
  96. package/dist/commands/doctor.js.map +1 -1
  97. package/dist/commands/init.d.ts.map +1 -1
  98. package/dist/commands/init.js +31 -2
  99. package/dist/commands/init.js.map +1 -1
  100. package/dist/commands/porch/build-counter.d.ts +5 -0
  101. package/dist/commands/porch/build-counter.d.ts.map +1 -0
  102. package/dist/commands/porch/build-counter.js +5 -0
  103. package/dist/commands/porch/build-counter.js.map +1 -0
  104. package/dist/commands/porch/checks.d.ts +16 -29
  105. package/dist/commands/porch/checks.d.ts.map +1 -1
  106. package/dist/commands/porch/checks.js +90 -144
  107. package/dist/commands/porch/checks.js.map +1 -1
  108. package/dist/commands/porch/claude.d.ts +27 -0
  109. package/dist/commands/porch/claude.d.ts.map +1 -0
  110. package/dist/commands/porch/claude.js +107 -0
  111. package/dist/commands/porch/claude.js.map +1 -0
  112. package/dist/commands/porch/index.d.ts +21 -43
  113. package/dist/commands/porch/index.d.ts.map +1 -1
  114. package/dist/commands/porch/index.js +466 -926
  115. package/dist/commands/porch/index.js.map +1 -1
  116. package/dist/commands/porch/plan.d.ts +70 -0
  117. package/dist/commands/porch/plan.d.ts.map +1 -0
  118. package/dist/commands/porch/plan.js +190 -0
  119. package/dist/commands/porch/plan.js.map +1 -0
  120. package/dist/commands/porch/prompts.d.ts +19 -0
  121. package/dist/commands/porch/prompts.d.ts.map +1 -0
  122. package/dist/commands/porch/prompts.js +250 -0
  123. package/dist/commands/porch/prompts.js.map +1 -0
  124. package/dist/commands/porch/protocol.d.ts +59 -0
  125. package/dist/commands/porch/protocol.d.ts.map +1 -0
  126. package/dist/commands/porch/protocol.js +260 -0
  127. package/dist/commands/porch/protocol.js.map +1 -0
  128. package/dist/commands/porch/run.d.ts +40 -0
  129. package/dist/commands/porch/run.d.ts.map +1 -0
  130. package/dist/commands/porch/run.js +893 -0
  131. package/dist/commands/porch/run.js.map +1 -0
  132. package/dist/commands/porch/state.d.ts +23 -112
  133. package/dist/commands/porch/state.d.ts.map +1 -1
  134. package/dist/commands/porch/state.js +81 -699
  135. package/dist/commands/porch/state.js.map +1 -1
  136. package/dist/commands/porch/types.d.ts +72 -173
  137. package/dist/commands/porch/types.d.ts.map +1 -1
  138. package/dist/commands/porch/types.js +2 -1
  139. package/dist/commands/porch/types.js.map +1 -1
  140. package/dist/commands/update.d.ts.map +1 -1
  141. package/dist/commands/update.js +22 -0
  142. package/dist/commands/update.js.map +1 -1
  143. package/dist/lib/scaffold.d.ts +24 -0
  144. package/dist/lib/scaffold.d.ts.map +1 -1
  145. package/dist/lib/scaffold.js +78 -0
  146. package/dist/lib/scaffold.js.map +1 -1
  147. package/dist/terminal/index.d.ts +8 -0
  148. package/dist/terminal/index.d.ts.map +1 -0
  149. package/dist/terminal/index.js +5 -0
  150. package/dist/terminal/index.js.map +1 -0
  151. package/dist/terminal/pty-manager.d.ts +60 -0
  152. package/dist/terminal/pty-manager.d.ts.map +1 -0
  153. package/dist/terminal/pty-manager.js +334 -0
  154. package/dist/terminal/pty-manager.js.map +1 -0
  155. package/dist/terminal/pty-session.d.ts +79 -0
  156. package/dist/terminal/pty-session.d.ts.map +1 -0
  157. package/dist/terminal/pty-session.js +215 -0
  158. package/dist/terminal/pty-session.js.map +1 -0
  159. package/dist/terminal/ring-buffer.d.ts +27 -0
  160. package/dist/terminal/ring-buffer.d.ts.map +1 -0
  161. package/dist/terminal/ring-buffer.js +74 -0
  162. package/dist/terminal/ring-buffer.js.map +1 -0
  163. package/dist/terminal/ws-protocol.d.ts +27 -0
  164. package/dist/terminal/ws-protocol.d.ts.map +1 -0
  165. package/dist/terminal/ws-protocol.js +44 -0
  166. package/dist/terminal/ws-protocol.js.map +1 -0
  167. package/package.json +18 -3
  168. package/skeleton/DEPENDENCIES.md +3 -29
  169. package/skeleton/builders.md +1 -1
  170. package/skeleton/protocol-schema.json +282 -0
  171. package/skeleton/protocols/bugfix/builder-prompt.md +49 -0
  172. package/skeleton/protocols/bugfix/protocol.json +14 -2
  173. package/skeleton/protocols/experiment/builder-prompt.md +47 -0
  174. package/skeleton/protocols/experiment/protocol.json +101 -0
  175. package/skeleton/protocols/maintain/builder-prompt.md +41 -0
  176. package/skeleton/protocols/maintain/prompts/audit.md +111 -0
  177. package/skeleton/protocols/maintain/prompts/clean.md +91 -0
  178. package/skeleton/protocols/maintain/prompts/sync.md +113 -0
  179. package/skeleton/protocols/maintain/prompts/verify.md +110 -0
  180. package/skeleton/protocols/maintain/protocol.json +141 -0
  181. package/skeleton/protocols/maintain/protocol.md +13 -7
  182. package/skeleton/protocols/protocol-schema.json +53 -0
  183. package/skeleton/protocols/spider/builder-prompt.md +53 -0
  184. package/skeleton/protocols/spider/prompts/implement.md +109 -50
  185. package/skeleton/protocols/spider/prompts/specify.md +29 -4
  186. package/skeleton/protocols/spider/protocol.json +96 -154
  187. package/skeleton/protocols/spider/protocol.md +26 -16
  188. package/skeleton/protocols/spider/templates/plan.md +14 -0
  189. package/skeleton/protocols/tick/builder-prompt.md +51 -0
  190. package/skeleton/protocols/tick/protocol.json +7 -2
  191. package/skeleton/resources/commands/agent-farm.md +25 -43
  192. package/skeleton/resources/commands/overview.md +6 -16
  193. package/skeleton/resources/workflow-reference.md +2 -2
  194. package/skeleton/roles/architect.md +152 -315
  195. package/skeleton/roles/builder.md +109 -218
  196. package/skeleton/templates/AGENTS.md +1 -1
  197. package/skeleton/templates/CLAUDE.md +1 -1
  198. package/skeleton/templates/cheatsheet.md +4 -2
  199. package/templates/dashboard/index.html +17 -43
  200. package/templates/dashboard/js/dialogs.js +7 -7
  201. package/templates/dashboard/js/files.js +2 -2
  202. package/templates/dashboard/js/main.js +3 -3
  203. package/templates/dashboard/js/projects.js +3 -3
  204. package/templates/dashboard/js/tabs.js +1 -1
  205. package/templates/dashboard/js/utils.js +22 -87
  206. package/templates/tower.html +497 -26
  207. package/dist/agent-farm/commands/kickoff.d.ts +0 -19
  208. package/dist/agent-farm/commands/kickoff.d.ts.map +0 -1
  209. package/dist/agent-farm/commands/kickoff.js +0 -331
  210. package/dist/agent-farm/commands/kickoff.js.map +0 -1
  211. package/dist/agent-farm/commands/rename.d.ts +0 -13
  212. package/dist/agent-farm/commands/rename.d.ts.map +0 -1
  213. package/dist/agent-farm/commands/rename.js +0 -33
  214. package/dist/agent-farm/commands/rename.js.map +0 -1
  215. package/dist/agent-farm/commands/tutorial.d.ts +0 -10
  216. package/dist/agent-farm/commands/tutorial.d.ts.map +0 -1
  217. package/dist/agent-farm/commands/tutorial.js +0 -49
  218. package/dist/agent-farm/commands/tutorial.js.map +0 -1
  219. package/dist/agent-farm/commands/util.d.ts.map +0 -1
  220. package/dist/agent-farm/commands/util.js.map +0 -1
  221. package/dist/agent-farm/tutorial/index.d.ts +0 -8
  222. package/dist/agent-farm/tutorial/index.d.ts.map +0 -1
  223. package/dist/agent-farm/tutorial/index.js +0 -8
  224. package/dist/agent-farm/tutorial/index.js.map +0 -1
  225. package/dist/agent-farm/tutorial/prompts.d.ts +0 -57
  226. package/dist/agent-farm/tutorial/prompts.d.ts.map +0 -1
  227. package/dist/agent-farm/tutorial/prompts.js +0 -147
  228. package/dist/agent-farm/tutorial/prompts.js.map +0 -1
  229. package/dist/agent-farm/tutorial/runner.d.ts +0 -52
  230. package/dist/agent-farm/tutorial/runner.d.ts.map +0 -1
  231. package/dist/agent-farm/tutorial/runner.js +0 -204
  232. package/dist/agent-farm/tutorial/runner.js.map +0 -1
  233. package/dist/agent-farm/tutorial/state.d.ts +0 -26
  234. package/dist/agent-farm/tutorial/state.d.ts.map +0 -1
  235. package/dist/agent-farm/tutorial/state.js +0 -89
  236. package/dist/agent-farm/tutorial/state.js.map +0 -1
  237. package/dist/agent-farm/tutorial/steps/first-spec.d.ts +0 -7
  238. package/dist/agent-farm/tutorial/steps/first-spec.d.ts.map +0 -1
  239. package/dist/agent-farm/tutorial/steps/first-spec.js +0 -136
  240. package/dist/agent-farm/tutorial/steps/first-spec.js.map +0 -1
  241. package/dist/agent-farm/tutorial/steps/implementation.d.ts +0 -7
  242. package/dist/agent-farm/tutorial/steps/implementation.d.ts.map +0 -1
  243. package/dist/agent-farm/tutorial/steps/implementation.js +0 -76
  244. package/dist/agent-farm/tutorial/steps/implementation.js.map +0 -1
  245. package/dist/agent-farm/tutorial/steps/index.d.ts +0 -10
  246. package/dist/agent-farm/tutorial/steps/index.d.ts.map +0 -1
  247. package/dist/agent-farm/tutorial/steps/index.js +0 -10
  248. package/dist/agent-farm/tutorial/steps/index.js.map +0 -1
  249. package/dist/agent-farm/tutorial/steps/planning.d.ts +0 -7
  250. package/dist/agent-farm/tutorial/steps/planning.d.ts.map +0 -1
  251. package/dist/agent-farm/tutorial/steps/planning.js +0 -143
  252. package/dist/agent-farm/tutorial/steps/planning.js.map +0 -1
  253. package/dist/agent-farm/tutorial/steps/review.d.ts +0 -7
  254. package/dist/agent-farm/tutorial/steps/review.d.ts.map +0 -1
  255. package/dist/agent-farm/tutorial/steps/review.js +0 -78
  256. package/dist/agent-farm/tutorial/steps/review.js.map +0 -1
  257. package/dist/agent-farm/tutorial/steps/setup.d.ts +0 -7
  258. package/dist/agent-farm/tutorial/steps/setup.d.ts.map +0 -1
  259. package/dist/agent-farm/tutorial/steps/setup.js +0 -126
  260. package/dist/agent-farm/tutorial/steps/setup.js.map +0 -1
  261. package/dist/agent-farm/tutorial/steps/welcome.d.ts +0 -7
  262. package/dist/agent-farm/tutorial/steps/welcome.d.ts.map +0 -1
  263. package/dist/agent-farm/tutorial/steps/welcome.js +0 -50
  264. package/dist/agent-farm/tutorial/steps/welcome.js.map +0 -1
  265. package/dist/commands/pcheck/cache.d.ts +0 -48
  266. package/dist/commands/pcheck/cache.d.ts.map +0 -1
  267. package/dist/commands/pcheck/cache.js +0 -170
  268. package/dist/commands/pcheck/cache.js.map +0 -1
  269. package/dist/commands/pcheck/evaluator.d.ts +0 -15
  270. package/dist/commands/pcheck/evaluator.d.ts.map +0 -1
  271. package/dist/commands/pcheck/evaluator.js +0 -246
  272. package/dist/commands/pcheck/evaluator.js.map +0 -1
  273. package/dist/commands/pcheck/index.d.ts +0 -12
  274. package/dist/commands/pcheck/index.d.ts.map +0 -1
  275. package/dist/commands/pcheck/index.js +0 -249
  276. package/dist/commands/pcheck/index.js.map +0 -1
  277. package/dist/commands/pcheck/parser.d.ts +0 -39
  278. package/dist/commands/pcheck/parser.d.ts.map +0 -1
  279. package/dist/commands/pcheck/parser.js +0 -155
  280. package/dist/commands/pcheck/parser.js.map +0 -1
  281. package/dist/commands/pcheck/types.d.ts +0 -82
  282. package/dist/commands/pcheck/types.d.ts.map +0 -1
  283. package/dist/commands/pcheck/types.js +0 -5
  284. package/dist/commands/pcheck/types.js.map +0 -1
  285. package/dist/commands/porch/consultation.d.ts +0 -56
  286. package/dist/commands/porch/consultation.d.ts.map +0 -1
  287. package/dist/commands/porch/consultation.js +0 -330
  288. package/dist/commands/porch/consultation.js.map +0 -1
  289. package/dist/commands/porch/notifications.d.ts +0 -99
  290. package/dist/commands/porch/notifications.d.ts.map +0 -1
  291. package/dist/commands/porch/notifications.js +0 -223
  292. package/dist/commands/porch/notifications.js.map +0 -1
  293. package/dist/commands/porch/plan-parser.d.ts +0 -38
  294. package/dist/commands/porch/plan-parser.d.ts.map +0 -1
  295. package/dist/commands/porch/plan-parser.js +0 -166
  296. package/dist/commands/porch/plan-parser.js.map +0 -1
  297. package/dist/commands/porch/protocol-loader.d.ts +0 -46
  298. package/dist/commands/porch/protocol-loader.d.ts.map +0 -1
  299. package/dist/commands/porch/protocol-loader.js +0 -249
  300. package/dist/commands/porch/protocol-loader.js.map +0 -1
  301. package/dist/commands/porch/signal-parser.d.ts +0 -88
  302. package/dist/commands/porch/signal-parser.d.ts.map +0 -1
  303. package/dist/commands/porch/signal-parser.js +0 -148
  304. package/dist/commands/porch/signal-parser.js.map +0 -1
  305. package/dist/commands/tower.d.ts +0 -16
  306. package/dist/commands/tower.d.ts.map +0 -1
  307. package/dist/commands/tower.js +0 -21
  308. package/dist/commands/tower.js.map +0 -1
  309. package/skeleton/config.json +0 -7
  310. package/skeleton/porch/protocols/bugfix.json +0 -85
  311. package/skeleton/porch/protocols/spider.json +0 -135
  312. package/skeleton/porch/protocols/tick.json +0 -76
  313. package/skeleton/protocols/spider/prompts/defend.md +0 -215
  314. package/skeleton/protocols/spider/prompts/evaluate.md +0 -241
  315. package/templates/dashboard/css/activity.css +0 -151
  316. package/templates/dashboard/js/activity.js +0 -112
@@ -2,7 +2,10 @@
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">
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">
6
9
  <title>AF Control Tower</title>
7
10
  <style>
8
11
  * {
@@ -199,6 +202,36 @@
199
202
  overflow: hidden;
200
203
  }
201
204
 
205
+ .instance.gate-pending {
206
+ border-color: #f59e0b;
207
+ animation: gate-pulse 2s infinite;
208
+ }
209
+
210
+ @keyframes gate-pulse {
211
+ 0%, 100% { border-color: #f59e0b; }
212
+ 50% { border-color: #d97706; }
213
+ }
214
+
215
+ .gate-indicator {
216
+ padding: 8px 20px;
217
+ background: rgba(245, 158, 11, 0.15);
218
+ border-bottom: 1px solid var(--border);
219
+ font-size: 14px;
220
+ color: #f59e0b;
221
+ display: flex;
222
+ align-items: center;
223
+ gap: 8px;
224
+ }
225
+
226
+ .gate-indicator .gate-name {
227
+ font-weight: 600;
228
+ }
229
+
230
+ .gate-indicator .gate-builder {
231
+ color: var(--text-muted);
232
+ font-size: 12px;
233
+ }
234
+
202
235
  .instance-header {
203
236
  display: flex;
204
237
  justify-content: space-between;
@@ -611,6 +644,132 @@
611
644
  }
612
645
  }
613
646
 
647
+ /* Mobile optimizations */
648
+ @media (max-width: 600px) {
649
+ body {
650
+ padding-bottom: env(safe-area-inset-bottom, 0);
651
+ }
652
+
653
+ .header {
654
+ padding: 16px;
655
+ padding-top: calc(16px + env(safe-area-inset-top, 0));
656
+ flex-wrap: wrap;
657
+ gap: 12px;
658
+ }
659
+
660
+ .header h1 {
661
+ font-size: 18px;
662
+ width: 100%;
663
+ }
664
+
665
+ .header h1 .emoji {
666
+ font-size: 22px;
667
+ }
668
+
669
+ .header-actions {
670
+ width: 100%;
671
+ justify-content: flex-end;
672
+ }
673
+
674
+ .main {
675
+ padding: 16px;
676
+ padding-left: calc(16px + env(safe-area-inset-left, 0));
677
+ padding-right: calc(16px + env(safe-area-inset-right, 0));
678
+ }
679
+
680
+ .btn {
681
+ min-height: 44px;
682
+ min-width: 44px;
683
+ padding: 12px 16px;
684
+ }
685
+
686
+ .btn-small {
687
+ min-height: 44px;
688
+ padding: 10px 14px;
689
+ }
690
+
691
+ .instance-header {
692
+ flex-direction: column;
693
+ align-items: flex-start;
694
+ gap: 12px;
695
+ padding: 16px;
696
+ }
697
+
698
+ .instance-actions {
699
+ width: 100%;
700
+ justify-content: flex-end;
701
+ }
702
+
703
+ .port-item {
704
+ flex-direction: column;
705
+ align-items: flex-start;
706
+ gap: 8px;
707
+ }
708
+
709
+ .port-actions {
710
+ width: 100%;
711
+ }
712
+
713
+ .port-actions a {
714
+ min-height: 44px;
715
+ display: flex;
716
+ align-items: center;
717
+ justify-content: center;
718
+ flex: 1;
719
+ }
720
+
721
+ .launch-section {
722
+ padding: 16px;
723
+ }
724
+
725
+ .launch-form {
726
+ flex-direction: column;
727
+ }
728
+
729
+ .launch-form input[type="text"] {
730
+ min-height: 44px;
731
+ }
732
+
733
+ .btn-launch {
734
+ width: 100%;
735
+ min-height: 44px;
736
+ }
737
+
738
+ .recent-item {
739
+ flex-direction: column;
740
+ align-items: flex-start;
741
+ gap: 12px;
742
+ padding: 16px;
743
+ }
744
+
745
+ .dialog-box {
746
+ min-width: auto;
747
+ width: calc(100% - 32px);
748
+ margin: 16px;
749
+ }
750
+
751
+ .toast-container {
752
+ left: 16px;
753
+ right: 16px;
754
+ bottom: calc(16px + env(safe-area-inset-bottom, 0));
755
+ }
756
+ }
757
+
758
+ /* Touch-friendly tap targets */
759
+ @media (pointer: coarse) {
760
+ .btn, .port-actions a, .autocomplete-item, .recent-item {
761
+ min-height: 44px;
762
+ }
763
+
764
+ .copy-btn {
765
+ min-width: 44px;
766
+ min-height: 44px;
767
+ display: flex;
768
+ align-items: center;
769
+ justify-content: center;
770
+ }
771
+ }
772
+
614
773
  /* Dialog styles */
615
774
  .dialog-overlay {
616
775
  position: fixed;
@@ -680,6 +839,7 @@
680
839
  Agent Farm Control Tower
681
840
  </h1>
682
841
  <div class="header-actions">
842
+ <button class="btn" id="share-btn" onclick="showShareDialog()" style="display: none;" title="Share via QR code">📱 Share</button>
683
843
  <button class="btn" onclick="refresh()">Refresh</button>
684
844
  </div>
685
845
  </header>
@@ -733,6 +893,23 @@
733
893
  </div>
734
894
  </div>
735
895
  </div>
896
+
897
+ <!-- Share Dialog (QR Code) -->
898
+ <div class="dialog-overlay" id="share-dialog" style="display: none;">
899
+ <div class="dialog-box" style="text-align: center;">
900
+ <h3>📱 Share via QR Code</h3>
901
+ <div id="share-content">
902
+ <div class="loading">
903
+ <div class="spinner"></div>
904
+ <p>Starting tunnel...</p>
905
+ </div>
906
+ </div>
907
+ <div class="dialog-actions" style="margin-top: 16px;">
908
+ <button class="btn" onclick="hideShareDialog()">Close</button>
909
+ <button class="btn" id="stop-tunnel-btn" onclick="stopTunnelAndClose()" style="display: none;">Stop Tunnel</button>
910
+ </div>
911
+ </div>
912
+ </div>
736
913
  </main>
737
914
 
738
915
  <div class="toast-container" id="toast-container"></div>
@@ -742,17 +919,153 @@
742
919
  let runningInstances = [];
743
920
  let recentInstances = [];
744
921
 
922
+ // Auth helper: get headers with auth token if available
923
+ function getAuthHeaders() {
924
+ const key = localStorage.getItem('codev_web_key');
925
+ return key ? { 'Authorization': `Bearer ${key}` } : {};
926
+ }
927
+
928
+ // Auth helper: make authenticated fetch request
929
+ async function authFetch(url, options = {}) {
930
+ const headers = { ...getAuthHeaders(), ...(options.headers || {}) };
931
+ const response = await fetch(url, { ...options, headers });
932
+
933
+ // If 401, clear key and redirect to login
934
+ if (response.status === 401) {
935
+ localStorage.removeItem('codev_web_key');
936
+ location.reload();
937
+ throw new Error('Unauthorized');
938
+ }
939
+ return response;
940
+ }
941
+
942
+ // Logout function
943
+ function logout() {
944
+ localStorage.removeItem('codev_web_key');
945
+ window.location.href = '/';
946
+ }
947
+
745
948
  // Initialize
746
949
  async function init() {
950
+ // Check if cloudflared is available and show share button
951
+ try {
952
+ const tunnelRes = await fetch('/api/tunnel/status');
953
+ const tunnelStatus = await tunnelRes.json();
954
+ if (tunnelStatus.available) {
955
+ document.getElementById('share-btn').style.display = 'inline-block';
956
+ }
957
+ } catch (e) {
958
+ // Tunnel not available, button stays hidden
959
+ }
960
+
961
+ // Request notification permission
962
+ if ('Notification' in window && Notification.permission === 'default') {
963
+ try {
964
+ await Notification.requestPermission();
965
+ } catch (e) {
966
+ // Ignore errors, notifications are optional
967
+ }
968
+ }
969
+
970
+ // Subscribe to SSE for push notifications
971
+ subscribeToEvents();
972
+
747
973
  await refresh();
748
974
  // Poll every 5 seconds
749
975
  setInterval(refresh, 5000);
750
976
  }
751
977
 
978
+ // SSE subscription for real-time notifications
979
+ // Uses fetch + ReadableStream to support Authorization header
980
+ let sseController = null;
981
+
982
+ async function subscribeToEvents() {
983
+ // Abort any existing connection
984
+ if (sseController) {
985
+ sseController.abort();
986
+ }
987
+
988
+ sseController = new AbortController();
989
+
990
+ try {
991
+ const response = await fetch('/api/events', {
992
+ headers: getAuthHeaders(),
993
+ signal: sseController.signal,
994
+ });
995
+
996
+ if (!response.ok) {
997
+ console.log('SSE connection failed:', response.status);
998
+ setTimeout(subscribeToEvents, 5000);
999
+ return;
1000
+ }
1001
+
1002
+ const reader = response.body.getReader();
1003
+ const decoder = new TextDecoder();
1004
+ let buffer = '';
1005
+
1006
+ while (true) {
1007
+ const { done, value } = await reader.read();
1008
+
1009
+ if (done) {
1010
+ console.log('SSE stream ended, reconnecting...');
1011
+ setTimeout(subscribeToEvents, 5000);
1012
+ return;
1013
+ }
1014
+
1015
+ buffer += decoder.decode(value, { stream: true });
1016
+
1017
+ // Parse SSE format: "data: {...}\n\n"
1018
+ const lines = buffer.split('\n\n');
1019
+ buffer = lines.pop() || ''; // Keep incomplete message in buffer
1020
+
1021
+ for (const chunk of lines) {
1022
+ const dataMatch = chunk.match(/^data:\s*(.+)$/m);
1023
+ if (!dataMatch) continue;
1024
+
1025
+ try {
1026
+ const data = JSON.parse(dataMatch[1]);
1027
+
1028
+ if (data.type === 'connected') {
1029
+ console.log('SSE connected:', data.id);
1030
+ continue;
1031
+ }
1032
+
1033
+ // Show browser notification
1034
+ if ('Notification' in window && Notification.permission === 'granted') {
1035
+ const notification = new Notification(data.title, {
1036
+ body: data.body,
1037
+ icon: '/favicon.ico',
1038
+ tag: 'tower-notification-' + data.id,
1039
+ });
1040
+
1041
+ // Auto-close after 5 seconds
1042
+ setTimeout(() => notification.close(), 5000);
1043
+ }
1044
+
1045
+ // Also show in-app toast
1046
+ showToast(`${data.title}: ${data.body}`, data.type || 'info');
1047
+
1048
+ // Refresh to show updated status
1049
+ refresh();
1050
+ } catch (e) {
1051
+ console.error('SSE parse error:', e);
1052
+ }
1053
+ }
1054
+ }
1055
+ } catch (e) {
1056
+ if (e.name === 'AbortError') {
1057
+ // Intentional abort, don't reconnect
1058
+ return;
1059
+ }
1060
+ console.error('SSE error:', e);
1061
+ setTimeout(subscribeToEvents, 5000);
1062
+ }
1063
+ }
1064
+
752
1065
  // Refresh data from API
753
1066
  async function refresh() {
754
1067
  try {
755
- const response = await fetch('/api/status');
1068
+ const response = await authFetch('/api/status');
756
1069
  if (!response.ok) throw new Error('Failed to fetch status');
757
1070
 
758
1071
  const data = await response.json();
@@ -774,7 +1087,7 @@
774
1087
  <div class="empty-state">
775
1088
  <div class="icon">📭</div>
776
1089
  <h2>No running instances</h2>
777
- <p>Start a new instance below or run <code>af start</code> in a project directory.</p>
1090
+ <p>Start a new instance below or run <code>af dash start</code> in a project directory.</p>
778
1091
  </div>
779
1092
  `;
780
1093
  } else {
@@ -810,28 +1123,54 @@
810
1123
  function renderInstance(instance) {
811
1124
  const statusClass = instance.running ? 'running' : 'stopped';
812
1125
  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>
1126
+ const hasGate = instance.gateStatus?.hasGate;
1127
+
1128
+ // Render terminals list (Architect, Builders, Shells)
1129
+ const terminals = instance.terminals || [];
1130
+ let terminalsHtml = '';
1131
+
1132
+ if (instance.running && terminals.length > 0) {
1133
+ // Show each terminal as a link
1134
+ terminalsHtml = terminals.map(terminal => `
1135
+ <div class="port-item">
1136
+ <div class="port-info">
1137
+ <span class="port-status ${terminal.active ? 'active' : 'inactive'}"></span>
1138
+ <span class="port-type">${escapeHtml(terminal.label)}</span>
1139
+ </div>
1140
+ <div class="port-actions">
1141
+ <a href="${escapeHtml(terminal.url)}" target="_blank">Open</a>
1142
+ </div>
820
1143
  </div>
821
- <div class="port-actions">
822
- <a href="${escapeHtml(port.url)}" target="_blank" class="${port.active ? '' : 'disabled'}">
823
- Open
824
- </a>
1144
+ `).join('');
1145
+ } else if (instance.running) {
1146
+ // Fallback to Dashboard link if no terminals available
1147
+ terminalsHtml = `
1148
+ <div class="port-item">
1149
+ <div class="port-info">
1150
+ <span class="port-status active"></span>
1151
+ <span class="port-type">Dashboard</span>
1152
+ </div>
1153
+ <div class="port-actions">
1154
+ <a href="${escapeHtml(instance.proxyUrl)}" target="_blank">Open</a>
1155
+ </div>
825
1156
  </div>
826
- </div>
827
- `).join('');
1157
+ `;
1158
+ }
828
1159
 
829
1160
  const lastUsed = instance.lastUsed
830
1161
  ? formatDate(instance.lastUsed)
831
1162
  : 'Never';
832
1163
 
1164
+ const gateIndicatorHtml = hasGate ? `
1165
+ <div class="gate-indicator">
1166
+ <span>⏳</span>
1167
+ <span class="gate-name">${escapeHtml(instance.gateStatus.gateName || 'approval')}</span>
1168
+ ${instance.gateStatus.builderId ? `<span class="gate-builder">(${escapeHtml(instance.gateStatus.builderId)})</span>` : ''}
1169
+ </div>
1170
+ ` : '';
1171
+
833
1172
  return `
834
- <div class="instance">
1173
+ <div class="instance ${hasGate ? 'gate-pending' : ''}" data-project="${escapeHtml(instance.projectPath)}">
835
1174
  <div class="instance-header">
836
1175
  <div class="instance-title">
837
1176
  <span class="instance-name">${escapeHtml(instance.projectName)}</span>
@@ -849,6 +1188,7 @@
849
1188
  `}
850
1189
  </div>
851
1190
  </div>
1191
+ ${gateIndicatorHtml}
852
1192
  <div class="instance-path-row">
853
1193
  <span class="instance-path" title="${escapeHtml(instance.projectPath)}">
854
1194
  ${escapeHtml(instance.projectPath)}
@@ -857,11 +1197,10 @@
857
1197
  </div>
858
1198
  <div class="instance-body">
859
1199
  <div class="ports-list">
860
- ${portsHtml}
1200
+ ${terminalsHtml}
861
1201
  </div>
862
1202
  <div class="instance-meta">
863
1203
  <span>Last active: ${lastUsed}</span>
864
- <span>Port block: ${instance.basePort}-${instance.basePort + 99}</span>
865
1204
  </div>
866
1205
  </div>
867
1206
  </div>
@@ -952,7 +1291,7 @@
952
1291
  }
953
1292
 
954
1293
  try {
955
- const response = await fetch('/api/browse?path=' + encodeURIComponent(inputPath));
1294
+ const response = await authFetch('/api/browse?path=' + encodeURIComponent(inputPath));
956
1295
  const data = await response.json();
957
1296
  suggestions = data.suggestions || [];
958
1297
  selectedIndex = -1;
@@ -1007,7 +1346,7 @@
1007
1346
  // Launch a specific path (from recents)
1008
1347
  async function launchPath(projectPath) {
1009
1348
  try {
1010
- const response = await fetch('/api/launch', {
1349
+ const response = await authFetch('/api/launch', {
1011
1350
  method: 'POST',
1012
1351
  headers: { 'Content-Type': 'application/json' },
1013
1352
  body: JSON.stringify({ projectPath })
@@ -1033,7 +1372,7 @@
1033
1372
  // Stop an instance by port
1034
1373
  async function stopInstance(basePort) {
1035
1374
  try {
1036
- const response = await fetch('/api/stop', {
1375
+ const response = await authFetch('/api/stop', {
1037
1376
  method: 'POST',
1038
1377
  headers: { 'Content-Type': 'application/json' },
1039
1378
  body: JSON.stringify({ basePort })
@@ -1056,7 +1395,7 @@
1056
1395
  async function restartInstance(basePort, projectPath) {
1057
1396
  try {
1058
1397
  // First stop
1059
- await fetch('/api/stop', {
1398
+ await authFetch('/api/stop', {
1060
1399
  method: 'POST',
1061
1400
  headers: { 'Content-Type': 'application/json' },
1062
1401
  body: JSON.stringify({ basePort })
@@ -1066,7 +1405,7 @@
1066
1405
  await new Promise(r => setTimeout(r, 1000));
1067
1406
 
1068
1407
  // Then start
1069
- const response = await fetch('/api/launch', {
1408
+ const response = await authFetch('/api/launch', {
1070
1409
  method: 'POST',
1071
1410
  headers: { 'Content-Type': 'application/json' },
1072
1411
  body: JSON.stringify({ projectPath })
@@ -1096,7 +1435,7 @@
1096
1435
  }
1097
1436
 
1098
1437
  try {
1099
- const response = await fetch('/api/launch', {
1438
+ const response = await authFetch('/api/launch', {
1100
1439
  method: 'POST',
1101
1440
  headers: { 'Content-Type': 'application/json' },
1102
1441
  body: JSON.stringify({ projectPath })
@@ -1170,6 +1509,23 @@
1170
1509
  .replace(/'/g, '&#39;');
1171
1510
  }
1172
1511
 
1512
+ // Base64URL encoding (RFC 4648) for project paths
1513
+ // IMPORTANT: Use TextEncoder for Unicode support (btoa only handles Latin-1)
1514
+ function toBase64URL(str) {
1515
+ // Encode string to UTF-8 bytes, then to Base64, then to Base64URL
1516
+ const bytes = new TextEncoder().encode(str);
1517
+ const base64 = btoa(String.fromCharCode(...bytes));
1518
+ return base64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
1519
+ }
1520
+
1521
+ // Generate proxy URL for a project
1522
+ // All terminals are now multiplexed on a single port via WebSocket (Spec 0085)
1523
+ // The React dashboard handles tab selection internally
1524
+ function getProxyUrl(instance, portType) {
1525
+ const encodedPath = toBase64URL(instance.projectPath);
1526
+ return `/project/${encodedPath}/`;
1527
+ }
1528
+
1173
1529
  // Toast notifications
1174
1530
  function showToast(message, type = 'info') {
1175
1531
  const container = document.getElementById('toast-container');
@@ -1183,6 +1539,121 @@
1183
1539
  }, 4000);
1184
1540
  }
1185
1541
 
1542
+ // Share dialog functions (QR code for mobile access)
1543
+ let tunnelAbortController = null;
1544
+
1545
+ async function showShareDialog() {
1546
+ const dialog = document.getElementById('share-dialog');
1547
+ const content = document.getElementById('share-content');
1548
+ const stopBtn = document.getElementById('stop-tunnel-btn');
1549
+
1550
+ dialog.style.display = 'flex';
1551
+ stopBtn.style.display = 'none';
1552
+
1553
+ // Create abort controller for cancellation
1554
+ tunnelAbortController = new AbortController();
1555
+
1556
+ content.innerHTML = `
1557
+ <div class="loading">
1558
+ <div class="spinner"></div>
1559
+ <p>Starting tunnel...</p>
1560
+ <p style="font-size: 12px; color: var(--muted); margin-top: 8px;">This may take up to 30 seconds</p>
1561
+ <button class="btn" style="margin-top: 16px;" onclick="cancelTunnelStart()">Cancel</button>
1562
+ </div>
1563
+ `;
1564
+
1565
+ try {
1566
+ // Check if tunnel is already running
1567
+ const statusRes = await fetch('/api/tunnel/status', { signal: tunnelAbortController.signal });
1568
+ const status = await statusRes.json();
1569
+
1570
+ if (status.running && status.url) {
1571
+ showQRCode(status.url);
1572
+ return;
1573
+ }
1574
+
1575
+ // Start tunnel
1576
+ const res = await fetch('/api/tunnel/start', { method: 'POST', signal: tunnelAbortController.signal });
1577
+ const result = await res.json();
1578
+
1579
+ if (result.success && result.url) {
1580
+ showQRCode(result.url);
1581
+ } else {
1582
+ content.innerHTML = `<p style="color: var(--danger);">Failed to start tunnel: ${result.error || 'Unknown error'}</p>`;
1583
+ }
1584
+ } catch (e) {
1585
+ if (e.name === 'AbortError') {
1586
+ content.innerHTML = '<p>Tunnel start cancelled.</p>';
1587
+ } else {
1588
+ content.innerHTML = `<p style="color: var(--danger);">Error: ${e.message}</p>`;
1589
+ }
1590
+ } finally {
1591
+ tunnelAbortController = null;
1592
+ }
1593
+ }
1594
+
1595
+ function cancelTunnelStart() {
1596
+ if (tunnelAbortController) {
1597
+ tunnelAbortController.abort();
1598
+ }
1599
+ // Also stop any tunnel that might have started
1600
+ fetch('/api/tunnel/stop', { method: 'POST' }).catch(() => {});
1601
+ }
1602
+
1603
+ function showQRCode(tunnelUrl) {
1604
+ const content = document.getElementById('share-content');
1605
+ const stopBtn = document.getElementById('stop-tunnel-btn');
1606
+
1607
+ // Generate QR code using qrcode.js (loaded from CDN)
1608
+ content.innerHTML = `
1609
+ <p style="margin-bottom: 16px;">Scan to access from mobile:</p>
1610
+ <div id="qrcode" style="display: inline-block; padding: 16px; background: white; border-radius: 8px;"></div>
1611
+ <p style="margin-top: 16px; font-size: 12px; color: var(--muted);">
1612
+ <a href="${tunnelUrl}" target="_blank" style="color: var(--primary);">${tunnelUrl}</a>
1613
+ </p>
1614
+ `;
1615
+
1616
+ // Load QRCode library and generate
1617
+ if (!window.QRCode) {
1618
+ const script = document.createElement('script');
1619
+ script.src = 'https://cdn.jsdelivr.net/npm/qrcodejs@1.0.0/qrcode.min.js';
1620
+ script.onload = () => generateQR(tunnelUrl);
1621
+ document.head.appendChild(script);
1622
+ } else {
1623
+ generateQR(tunnelUrl);
1624
+ }
1625
+
1626
+ stopBtn.style.display = 'inline-block';
1627
+ }
1628
+
1629
+ function generateQR(url) {
1630
+ const container = document.getElementById('qrcode');
1631
+ if (container && window.QRCode) {
1632
+ container.innerHTML = '';
1633
+ new QRCode(container, {
1634
+ text: url,
1635
+ width: 200,
1636
+ height: 200,
1637
+ colorDark: '#000000',
1638
+ colorLight: '#ffffff',
1639
+ });
1640
+ }
1641
+ }
1642
+
1643
+ function hideShareDialog() {
1644
+ document.getElementById('share-dialog').style.display = 'none';
1645
+ }
1646
+
1647
+ async function stopTunnelAndClose() {
1648
+ try {
1649
+ await fetch('/api/tunnel/stop', { method: 'POST' });
1650
+ } catch (e) {
1651
+ // Ignore
1652
+ }
1653
+ hideShareDialog();
1654
+ showToast('Tunnel stopped', 'success');
1655
+ }
1656
+
1186
1657
  // Create project dialog functions
1187
1658
  function showCreateProjectDialog() {
1188
1659
  const dialog = document.getElementById('create-dialog');
@@ -1219,7 +1690,7 @@
1219
1690
  hideCreateProjectDialog();
1220
1691
 
1221
1692
  try {
1222
- const response = await fetch('/api/create', {
1693
+ const response = await authFetch('/api/create', {
1223
1694
  method: 'POST',
1224
1695
  headers: { 'Content-Type': 'application/json' },
1225
1696
  body: JSON.stringify({ parent, name })
@@ -1,19 +0,0 @@
1
- /**
2
- * Kickoff command - Start a new protocol-driven project
3
- *
4
- * This command combines:
5
- * 1. Creating a git worktree
6
- * 2. Initializing porch state
7
- * 3. Starting the porch loop in the builder
8
- */
9
- export interface KickoffOptions {
10
- project: string;
11
- protocol?: string;
12
- noRole?: boolean;
13
- resume?: boolean;
14
- }
15
- /**
16
- * Kickoff a new protocol-driven project
17
- */
18
- export declare function kickoff(options: KickoffOptions): Promise<void>;
19
- //# sourceMappingURL=kickoff.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"kickoff.d.ts","sourceRoot":"","sources":["../../../src/agent-farm/commands/kickoff.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAYH,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAqKD;;GAEG;AACH,wBAAsB,OAAO,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CA6MpE"}