@runcofactor/cli 0.52.0 → 0.53.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (225) hide show
  1. package/README.md +2 -0
  2. package/dist/{account-3ZG24MOE.js → account-MRZ54P5P.js} +5 -5
  3. package/dist/{alert-5G7PTAX2.js → alert-XBHELHEI.js} +8 -8
  4. package/dist/{api-catalog-IBYAFKKQ.js → api-catalog-GWYRX53L.js} +5 -5
  5. package/dist/{api-key-UXC44BDR.js → api-key-FH6JN447.js} +5 -5
  6. package/dist/{attachment-T6IRFQW3.js → attachment-J7GRVT5F.js} +5 -5
  7. package/dist/{auth-SHOCJE6Z.js → auth-VZZQXRDR.js} +3 -3
  8. package/dist/{benchmark-NJ2U6VTC.js → benchmark-A3SNDARF.js} +5 -5
  9. package/dist/{blackcat-RQJVFGXQ.js → blackcat-UM6TBQBN.js} +3 -3
  10. package/dist/{chunk-DHAGRM2F.js → chunk-33U56YGD.js} +14 -5
  11. package/dist/{chunk-DHAGRM2F.js.map → chunk-33U56YGD.js.map} +1 -1
  12. package/dist/{chunk-M752EUK3.js → chunk-3JU4UYYN.js} +5 -5
  13. package/dist/{chunk-G6BCST3G.js → chunk-3ZOUNCA5.js} +7 -7
  14. package/dist/{chunk-76Z6E7XX.js → chunk-4UZN6TVT.js} +74 -131
  15. package/dist/chunk-4UZN6TVT.js.map +1 -0
  16. package/dist/{chunk-ABT3BLRC.js → chunk-5TKXHS3X.js} +3 -3
  17. package/dist/{chunk-QCYSONXI.js → chunk-CRYHWO4M.js} +2 -2
  18. package/dist/{chunk-QTYUM4ZB.js → chunk-EJ3ZPH5U.js} +23 -187
  19. package/dist/chunk-EJ3ZPH5U.js.map +1 -0
  20. package/dist/{chunk-I5RHTCPY.js → chunk-EL6KJ4PO.js} +4 -4
  21. package/dist/{chunk-ZL4O6C42.js → chunk-HXBK3AGD.js} +2 -2
  22. package/dist/{chunk-PRERH4DN.js → chunk-I2SOHQ2C.js} +3 -3
  23. package/dist/{chunk-CYSPCGO6.js → chunk-JEHHEMUL.js} +5 -5
  24. package/dist/{chunk-LIEADTEF.js → chunk-LJFVRJCA.js} +2 -2
  25. package/dist/{chunk-UEJ2HTKU.js → chunk-MJVUMVLO.js} +19 -1
  26. package/dist/chunk-MJVUMVLO.js.map +1 -0
  27. package/dist/chunk-N55RKCGR.js +24 -0
  28. package/dist/chunk-N55RKCGR.js.map +1 -0
  29. package/dist/{chunk-PHNH3CEU.js → chunk-O4Z44BR7.js} +5 -5
  30. package/dist/{chunk-QA37WVF6.js → chunk-OQFCZERZ.js} +6 -6
  31. package/dist/{chunk-EUVB6Y3C.js → chunk-P7L5KELH.js} +8 -8
  32. package/dist/chunk-P7L5KELH.js.map +1 -0
  33. package/dist/{chunk-Z3MDQSZG.js → chunk-PJFFGQSB.js} +2 -2
  34. package/dist/{chunk-IY3ZK46I.js → chunk-QOVJUSSL.js} +2 -2
  35. package/dist/{chunk-RPJMQBB5.js → chunk-QXU4G4YN.js} +3 -3
  36. package/dist/{chunk-SZSTJIYU.js → chunk-RRSUKARV.js} +4 -4
  37. package/dist/{chunk-775FI436.js → chunk-RVD6J6IZ.js} +2 -2
  38. package/dist/{chunk-5UUIBY6E.js → chunk-TAVBFLCR.js} +3 -3
  39. package/dist/{chunk-ROX7N3RK.js → chunk-V7HB5LAU.js} +10 -7
  40. package/dist/chunk-V7HB5LAU.js.map +1 -0
  41. package/dist/{chunk-LL6TSQWV.js → chunk-VKZOHJCE.js} +3 -3
  42. package/dist/chunk-VKZOHJCE.js.map +1 -0
  43. package/dist/{chunk-3HI6QC4V.js → chunk-WRBU6TX7.js} +2 -2
  44. package/dist/{proofs-TXJGAP5F.js → chunk-X7SUSIY3.js} +118 -20
  45. package/dist/chunk-X7SUSIY3.js.map +1 -0
  46. package/dist/{chunk-BF3IPGMP.js → chunk-XSSZMG65.js} +4 -4
  47. package/dist/{credential-TWSWV64F.js → credential-4RFRU6OH.js} +5 -5
  48. package/dist/{credential-template-XO5D5AI3.js → credential-template-36PFUTBN.js} +5 -5
  49. package/dist/{digest-SLEFOLPV.js → digest-THMKFMP7.js} +8 -8
  50. package/dist/{docs-IDC4V4WQ.js → docs-ZGM53L67.js} +5 -5
  51. package/dist/{doctor-SDAYEA6C.js → doctor-Z2XBGBM3.js} +3 -3
  52. package/dist/{environment-VBUIBQ6O.js → environment-5YVZXBN7.js} +7 -7
  53. package/dist/eval-SO5TPD37.js +1177 -0
  54. package/dist/eval-SO5TPD37.js.map +1 -0
  55. package/dist/{exploration-IREIR5PO.js → exploration-GZ4WUJJH.js} +5 -5
  56. package/dist/{exploratory-test-4Q7BMEFT.js → exploratory-test-Y7P4ZDLD.js} +5 -5
  57. package/dist/{external-run-S7LULKPT.js → external-run-IJGGZOGI.js} +7 -7
  58. package/dist/{feature-flag-IASHTUGU.js → feature-flag-UH6Z7774.js} +5 -5
  59. package/dist/{flow-IBEZFZTU.js → flow-MRWAQYZZ.js} +5 -5
  60. package/dist/{github-HAEZRULP.js → github-VDJU3PU3.js} +6 -6
  61. package/dist/index.js +101 -82
  62. package/dist/index.js.map +1 -1
  63. package/dist/{init-LT2TZMQY.js → init-CROSTPA6.js} +5 -5
  64. package/dist/{install-RTEYH6TK.js → install-ECR7OLAY.js} +5 -5
  65. package/dist/{issues-5GE277R2.js → issues-K5TL2S4Z.js} +7 -7
  66. package/dist/{knobs-AMIWGYUZ.js → knobs-OKQFBQLK.js} +5 -5
  67. package/dist/{landing-3VLVVQL5.js → landing-XANIBPVM.js} +7 -7
  68. package/dist/{list-GDJR6UY6.js → list-64GX47Q4.js} +5 -5
  69. package/dist/{local-4R6GVW7U.js → local-QMQY2ZUX.js} +7 -7
  70. package/dist/{login-IXVAWTLO.js → login-I7UQ3GTM.js} +5 -5
  71. package/dist/{mcp-BM2MJWX4.js → mcp-XZV63DX5.js} +11 -11
  72. package/dist/{mcp-BM2MJWX4.js.map → mcp-XZV63DX5.js.map} +1 -1
  73. package/dist/{metrics-SSIP5CEB.js → metrics-KBTQ3KGZ.js} +5 -5
  74. package/dist/{monitor-ESZYHXT2.js → monitor-UGWY43IU.js} +5 -5
  75. package/dist/perf-CCHWHCSR.js +961 -0
  76. package/dist/perf-CCHWHCSR.js.map +1 -0
  77. package/dist/{product-VCGUV5TH.js → product-KLG2ZTHC.js} +5 -5
  78. package/dist/{product-map-AXKZNQDM.js → product-map-AMEZ76EQ.js} +5 -5
  79. package/dist/{prompts-FVPSON2N.js → prompts-ZVQNGL7X.js} +5 -5
  80. package/dist/proofs-QCNSQAPX.js +20 -0
  81. package/dist/{property-XEPCC7KQ.js → property-PJDQFHAL.js} +6 -6
  82. package/dist/{psql-JKF3OYYJ.js → psql-AURAN27C.js} +5 -5
  83. package/dist/{record-REQ37S54.js → record-SYWBTUOB.js} +6 -6
  84. package/dist/{redis-WGHT2GJB.js → redis-2H752SKO.js} +5 -5
  85. package/dist/{release-SNDIVIA6.js → release-OSIYUXSC.js} +5 -5
  86. package/dist/{runbook-XFJG2LFC.js → runbook-7LVHF6KV.js} +5 -5
  87. package/dist/{sandbox-DYCKLCMY.js → sandbox-TJQPMRGS.js} +18 -30
  88. package/dist/sandbox-TJQPMRGS.js.map +1 -0
  89. package/dist/{scenarios-PVHCKP55.js → scenarios-P2ABU4HK.js} +5 -5
  90. package/dist/{schedule-DGC7CX23.js → schedule-XLS56NP5.js} +8 -8
  91. package/dist/{session-ERGUFDDI.js → session-ACSFC3UB.js} +8 -8
  92. package/dist/{skill-6QO2E3TB.js → skill-F7I5P6AI.js} +43 -28
  93. package/dist/{skill-6QO2E3TB.js.map → skill-F7I5P6AI.js.map} +1 -1
  94. package/dist/{slack-WE7THLLH.js → slack-ZEK4GRND.js} +6 -6
  95. package/dist/{spec-SEOY7N3L.js → spec-XPIRRSV6.js} +5 -5
  96. package/dist/{src-CCK3TKBX.js → src-N7ZCYCXU.js} +4 -4
  97. package/dist/{src-AEWV5HYH.js → src-XSBBMVGW.js} +3 -3
  98. package/dist/{start-EPGLHTND.js → start-DKWLOQI6.js} +5 -5
  99. package/dist/{status-DTE2GXM2.js → status-K7KH5DI3.js} +5 -5
  100. package/dist/{tag-GIFEWGBL.js → tag-2Y3OMQQC.js} +6 -6
  101. package/dist/{task-VLJCHSFB.js → task-4PIOC4QV.js} +6 -6
  102. package/dist/{teams-J2OOJBL4.js → teams-KUM56BE2.js} +83 -47
  103. package/dist/teams-KUM56BE2.js.map +1 -0
  104. package/dist/{test-suite-CTACGIAZ.js → test-suite-SW25YGJV.js} +138 -96
  105. package/dist/test-suite-SW25YGJV.js.map +1 -0
  106. package/dist/{trace-RWQDYL4K.js → trace-QPXHYBLA.js} +7 -7
  107. package/dist/{triage-6UAXLCGU.js → triage-EZABRZZM.js} +6 -6
  108. package/dist/trigger-7C7KX5KH.js +137 -0
  109. package/dist/trigger-7C7KX5KH.js.map +1 -0
  110. package/dist/{update-check-QRREAM3N.js → update-check-E3X7UIY6.js} +3 -3
  111. package/dist/{upgrade-5Y655TQ6.js → upgrade-EJGTMW44.js} +4 -4
  112. package/dist/{usage-KCXFY46V.js → usage-GZCQDULF.js} +7 -7
  113. package/dist/{utilities-SUON2KZK.js → utilities-RIITD53A.js} +6 -6
  114. package/dist/{visual-GJZVJC32.js → visual-FH67OKS2.js} +5 -5
  115. package/dist/{workflow-R5QAXWBX.js → workflow-34YJZ2QQ.js} +38 -86
  116. package/dist/workflow-34YJZ2QQ.js.map +1 -0
  117. package/dist/{workflow-cache-PFBTWLGM.js → workflow-cache-YQ3BGJIG.js} +17 -17
  118. package/dist/{workflow-cache-convergence-resolve-UZGRPC3B.js → workflow-cache-convergence-resolve-SPLBRSNK.js} +5 -5
  119. package/dist/{workflow-debug-RACNWWOL.js → workflow-debug-HZB7TJGK.js} +10 -10
  120. package/dist/workflow-debug-HZB7TJGK.js.map +1 -0
  121. package/dist/{workflow-feedback-RJIQ5R3R.js → workflow-feedback-6UOTIWIQ.js} +5 -5
  122. package/dist/{workflow-fork-LINDCYJ3.js → workflow-fork-IDYHAHCX.js} +5 -5
  123. package/dist/workflow-promotion-U6WYFY5U.js +62 -0
  124. package/dist/workflow-promotion-U6WYFY5U.js.map +1 -0
  125. package/dist/{workflow-proposals-E4EXKAGM.js → workflow-proposals-MMVNIQT3.js} +5 -5
  126. package/dist/{workflow-revisions-5M6APN6R.js → workflow-revisions-BPUOLCZB.js} +5 -5
  127. package/dist/{workflow-status-7KL73WS2.js → workflow-status-RA3IEMEW.js} +5 -5
  128. package/package.json +3 -2
  129. package/dist/chat-target-IZ7YLLU3.js +0 -502
  130. package/dist/chat-target-IZ7YLLU3.js.map +0 -1
  131. package/dist/chunk-76Z6E7XX.js.map +0 -1
  132. package/dist/chunk-EUVB6Y3C.js.map +0 -1
  133. package/dist/chunk-LL6TSQWV.js.map +0 -1
  134. package/dist/chunk-QTYUM4ZB.js.map +0 -1
  135. package/dist/chunk-ROX7N3RK.js.map +0 -1
  136. package/dist/chunk-UEJ2HTKU.js.map +0 -1
  137. package/dist/proofs-TXJGAP5F.js.map +0 -1
  138. package/dist/sandbox-DYCKLCMY.js.map +0 -1
  139. package/dist/teams-J2OOJBL4.js.map +0 -1
  140. package/dist/test-suite-CTACGIAZ.js.map +0 -1
  141. package/dist/workflow-R5QAXWBX.js.map +0 -1
  142. /package/dist/{account-3ZG24MOE.js.map → account-MRZ54P5P.js.map} +0 -0
  143. /package/dist/{alert-5G7PTAX2.js.map → alert-XBHELHEI.js.map} +0 -0
  144. /package/dist/{api-catalog-IBYAFKKQ.js.map → api-catalog-GWYRX53L.js.map} +0 -0
  145. /package/dist/{api-key-UXC44BDR.js.map → api-key-FH6JN447.js.map} +0 -0
  146. /package/dist/{attachment-T6IRFQW3.js.map → attachment-J7GRVT5F.js.map} +0 -0
  147. /package/dist/{auth-SHOCJE6Z.js.map → auth-VZZQXRDR.js.map} +0 -0
  148. /package/dist/{benchmark-NJ2U6VTC.js.map → benchmark-A3SNDARF.js.map} +0 -0
  149. /package/dist/{blackcat-RQJVFGXQ.js.map → blackcat-UM6TBQBN.js.map} +0 -0
  150. /package/dist/{chunk-M752EUK3.js.map → chunk-3JU4UYYN.js.map} +0 -0
  151. /package/dist/{chunk-G6BCST3G.js.map → chunk-3ZOUNCA5.js.map} +0 -0
  152. /package/dist/{chunk-ABT3BLRC.js.map → chunk-5TKXHS3X.js.map} +0 -0
  153. /package/dist/{chunk-QCYSONXI.js.map → chunk-CRYHWO4M.js.map} +0 -0
  154. /package/dist/{chunk-I5RHTCPY.js.map → chunk-EL6KJ4PO.js.map} +0 -0
  155. /package/dist/{chunk-ZL4O6C42.js.map → chunk-HXBK3AGD.js.map} +0 -0
  156. /package/dist/{chunk-PRERH4DN.js.map → chunk-I2SOHQ2C.js.map} +0 -0
  157. /package/dist/{chunk-CYSPCGO6.js.map → chunk-JEHHEMUL.js.map} +0 -0
  158. /package/dist/{chunk-LIEADTEF.js.map → chunk-LJFVRJCA.js.map} +0 -0
  159. /package/dist/{chunk-PHNH3CEU.js.map → chunk-O4Z44BR7.js.map} +0 -0
  160. /package/dist/{chunk-QA37WVF6.js.map → chunk-OQFCZERZ.js.map} +0 -0
  161. /package/dist/{chunk-Z3MDQSZG.js.map → chunk-PJFFGQSB.js.map} +0 -0
  162. /package/dist/{chunk-IY3ZK46I.js.map → chunk-QOVJUSSL.js.map} +0 -0
  163. /package/dist/{chunk-RPJMQBB5.js.map → chunk-QXU4G4YN.js.map} +0 -0
  164. /package/dist/{chunk-SZSTJIYU.js.map → chunk-RRSUKARV.js.map} +0 -0
  165. /package/dist/{chunk-775FI436.js.map → chunk-RVD6J6IZ.js.map} +0 -0
  166. /package/dist/{chunk-5UUIBY6E.js.map → chunk-TAVBFLCR.js.map} +0 -0
  167. /package/dist/{chunk-3HI6QC4V.js.map → chunk-WRBU6TX7.js.map} +0 -0
  168. /package/dist/{chunk-BF3IPGMP.js.map → chunk-XSSZMG65.js.map} +0 -0
  169. /package/dist/{credential-TWSWV64F.js.map → credential-4RFRU6OH.js.map} +0 -0
  170. /package/dist/{credential-template-XO5D5AI3.js.map → credential-template-36PFUTBN.js.map} +0 -0
  171. /package/dist/{digest-SLEFOLPV.js.map → digest-THMKFMP7.js.map} +0 -0
  172. /package/dist/{docs-IDC4V4WQ.js.map → docs-ZGM53L67.js.map} +0 -0
  173. /package/dist/{doctor-SDAYEA6C.js.map → doctor-Z2XBGBM3.js.map} +0 -0
  174. /package/dist/{environment-VBUIBQ6O.js.map → environment-5YVZXBN7.js.map} +0 -0
  175. /package/dist/{exploration-IREIR5PO.js.map → exploration-GZ4WUJJH.js.map} +0 -0
  176. /package/dist/{exploratory-test-4Q7BMEFT.js.map → exploratory-test-Y7P4ZDLD.js.map} +0 -0
  177. /package/dist/{external-run-S7LULKPT.js.map → external-run-IJGGZOGI.js.map} +0 -0
  178. /package/dist/{feature-flag-IASHTUGU.js.map → feature-flag-UH6Z7774.js.map} +0 -0
  179. /package/dist/{flow-IBEZFZTU.js.map → flow-MRWAQYZZ.js.map} +0 -0
  180. /package/dist/{github-HAEZRULP.js.map → github-VDJU3PU3.js.map} +0 -0
  181. /package/dist/{init-LT2TZMQY.js.map → init-CROSTPA6.js.map} +0 -0
  182. /package/dist/{install-RTEYH6TK.js.map → install-ECR7OLAY.js.map} +0 -0
  183. /package/dist/{issues-5GE277R2.js.map → issues-K5TL2S4Z.js.map} +0 -0
  184. /package/dist/{knobs-AMIWGYUZ.js.map → knobs-OKQFBQLK.js.map} +0 -0
  185. /package/dist/{landing-3VLVVQL5.js.map → landing-XANIBPVM.js.map} +0 -0
  186. /package/dist/{list-GDJR6UY6.js.map → list-64GX47Q4.js.map} +0 -0
  187. /package/dist/{local-4R6GVW7U.js.map → local-QMQY2ZUX.js.map} +0 -0
  188. /package/dist/{login-IXVAWTLO.js.map → login-I7UQ3GTM.js.map} +0 -0
  189. /package/dist/{metrics-SSIP5CEB.js.map → metrics-KBTQ3KGZ.js.map} +0 -0
  190. /package/dist/{monitor-ESZYHXT2.js.map → monitor-UGWY43IU.js.map} +0 -0
  191. /package/dist/{product-VCGUV5TH.js.map → product-KLG2ZTHC.js.map} +0 -0
  192. /package/dist/{product-map-AXKZNQDM.js.map → product-map-AMEZ76EQ.js.map} +0 -0
  193. /package/dist/{prompts-FVPSON2N.js.map → prompts-ZVQNGL7X.js.map} +0 -0
  194. /package/dist/{src-AEWV5HYH.js.map → proofs-QCNSQAPX.js.map} +0 -0
  195. /package/dist/{property-XEPCC7KQ.js.map → property-PJDQFHAL.js.map} +0 -0
  196. /package/dist/{psql-JKF3OYYJ.js.map → psql-AURAN27C.js.map} +0 -0
  197. /package/dist/{record-REQ37S54.js.map → record-SYWBTUOB.js.map} +0 -0
  198. /package/dist/{redis-WGHT2GJB.js.map → redis-2H752SKO.js.map} +0 -0
  199. /package/dist/{release-SNDIVIA6.js.map → release-OSIYUXSC.js.map} +0 -0
  200. /package/dist/{runbook-XFJG2LFC.js.map → runbook-7LVHF6KV.js.map} +0 -0
  201. /package/dist/{scenarios-PVHCKP55.js.map → scenarios-P2ABU4HK.js.map} +0 -0
  202. /package/dist/{schedule-DGC7CX23.js.map → schedule-XLS56NP5.js.map} +0 -0
  203. /package/dist/{session-ERGUFDDI.js.map → session-ACSFC3UB.js.map} +0 -0
  204. /package/dist/{slack-WE7THLLH.js.map → slack-ZEK4GRND.js.map} +0 -0
  205. /package/dist/{spec-SEOY7N3L.js.map → spec-XPIRRSV6.js.map} +0 -0
  206. /package/dist/{src-CCK3TKBX.js.map → src-N7ZCYCXU.js.map} +0 -0
  207. /package/dist/{tag-GIFEWGBL.js.map → src-XSBBMVGW.js.map} +0 -0
  208. /package/dist/{start-EPGLHTND.js.map → start-DKWLOQI6.js.map} +0 -0
  209. /package/dist/{status-DTE2GXM2.js.map → status-K7KH5DI3.js.map} +0 -0
  210. /package/dist/{trace-RWQDYL4K.js.map → tag-2Y3OMQQC.js.map} +0 -0
  211. /package/dist/{task-VLJCHSFB.js.map → task-4PIOC4QV.js.map} +0 -0
  212. /package/dist/{triage-6UAXLCGU.js.map → trace-QPXHYBLA.js.map} +0 -0
  213. /package/dist/{update-check-QRREAM3N.js.map → triage-EZABRZZM.js.map} +0 -0
  214. /package/dist/{upgrade-5Y655TQ6.js.map → update-check-E3X7UIY6.js.map} +0 -0
  215. /package/dist/{utilities-SUON2KZK.js.map → upgrade-EJGTMW44.js.map} +0 -0
  216. /package/dist/{usage-KCXFY46V.js.map → usage-GZCQDULF.js.map} +0 -0
  217. /package/dist/{workflow-debug-RACNWWOL.js.map → utilities-RIITD53A.js.map} +0 -0
  218. /package/dist/{visual-GJZVJC32.js.map → visual-FH67OKS2.js.map} +0 -0
  219. /package/dist/{workflow-cache-PFBTWLGM.js.map → workflow-cache-YQ3BGJIG.js.map} +0 -0
  220. /package/dist/{workflow-cache-convergence-resolve-UZGRPC3B.js.map → workflow-cache-convergence-resolve-SPLBRSNK.js.map} +0 -0
  221. /package/dist/{workflow-feedback-RJIQ5R3R.js.map → workflow-feedback-6UOTIWIQ.js.map} +0 -0
  222. /package/dist/{workflow-fork-LINDCYJ3.js.map → workflow-fork-IDYHAHCX.js.map} +0 -0
  223. /package/dist/{workflow-proposals-E4EXKAGM.js.map → workflow-proposals-MMVNIQT3.js.map} +0 -0
  224. /package/dist/{workflow-revisions-5M6APN6R.js.map → workflow-revisions-BPUOLCZB.js.map} +0 -0
  225. /package/dist/{workflow-status-7KL73WS2.js.map → workflow-status-RA3IEMEW.js.map} +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/remote-test.ts","../src/remote-test-config.ts","../src/remote-test-format.ts"],"sourcesContent":["/**\n * Remote Test Command\n *\n * Triggers workflow tests on the Canary platform and streams results via SSE.\n * Designed for CI/CD pipelines. Exit codes: 0 every runnable test settled with\n * no failures, 1 one or more hard failures, 2 no failures but at least one\n * workflow was inconclusive (the platform never produced a verdict).\n *\n * Usage:\n * canary test-suite run [options]\n *\n * Options:\n * --token <key> API key (or set CANARY_API_TOKEN env var)\n * --api-url <url> API URL (default: https://api.runcofactor.com)\n * --tag <tag> Filter workflows by tag\n * --name-pattern <pat> Filter workflows by name pattern\n * --external-host-shape <shape> Reserve one exact dedicated host for an external v2 suite\n * --verbose, -v Show all events (not just results)\n */\n\nimport process from 'node:process';\nimport { createParser, type EventSourceMessage } from 'eventsource-parser';\nimport type {\n WorkflowTestEvent,\n WorkflowTestStatus,\n WorkflowTestSuiteEvent,\n} from '@chatsdet/types';\nimport { resolveConfig } from './auth';\nimport { resolveTestConfig, type ResolvedConfig } from './remote-test-config';\nimport { resolveTestSuite, UUID_RE } from './cli-helpers.js';\nimport { writeRawOutput } from './brand.js';\nimport {\n buildQueryParams,\n extractWorkflowName,\n formatFailedTests,\n formatExternalRunSummary,\n formatStreamClosedBeforeCompletion,\n formatStreamReadFailure,\n formatSummary,\n formatTestResultLine,\n getArgValue,\n hasFlag,\n trackFailedTest,\n type FailedTestTracker,\n type SuiteSummaryStats,\n} from './remote-test-format';\n\n/** No suite event seen yet — `formatSummary` reads this as \"no workflows\". */\nconst EMPTY_SUITE_STATS: SuiteSummaryStats = {\n totalWorkflows: 0,\n completedWorkflows: 0,\n successfulWorkflows: 0,\n failedWorkflows: 0,\n inconclusiveWorkflows: 0,\n skippedWorkflows: 0,\n degradedWorkflows: 0,\n waitingWorkflows: 0,\n};\n\n\ntype TriggerResponse = {\n ok: boolean;\n jobId?: string;\n suiteId?: string;\n startedAt?: string;\n appUrl?: string;\n error?: string;\n};\n\n/**\n * The message for a refused trigger: a typed user error's own sentence (e.g. why\n * no workflows matched), otherwise the status and raw body.\n */\nfunction formatTriggerFailure(status: number, body: string): string {\n try {\n const parsed = JSON.parse(body) as { userError?: boolean; error?: unknown };\n if (parsed.userError === true && typeof parsed.error === 'string') {\n return `Error: ${parsed.error}`;\n }\n } catch {\n // Not JSON (a proxy error page, say) — fall through to the raw body.\n }\n return `Failed to start tests: ${status}\\n${body}`;\n}\n\n// Export internals for testing\nexport const __internals = {\n getArgValue,\n hasFlag,\n buildQueryParams,\n extractWorkflowName,\n formatSummary,\n formatTestResultLine,\n trackFailedTest,\n formatExternalRunSummary,\n formatFailedTests,\n formatStreamReadFailure,\n formatStreamClosedBeforeCompletion,\n resolveTestConfig,\n UUID_RE,\n triggerNamedSuiteRun,\n};\n\ntype NamedSuiteTrigger =\n | { kind: 'native'; executionId: string; jobId: string; pinnedCommit: string | null }\n | { kind: 'external'; runId: string; jobId: string; runUrl: string };\n\n/**\n * Resolve a `--suite <name|id>` argument to a suite id and trigger a layered\n * run via POST /test-suites/:id/run. Returns the execution id (used as the SSE\n * stream key) and the orchestrator job id.\n */\nasync function triggerNamedSuiteRun(\n apiUrl: string,\n token: string,\n suiteArg: string,\n environmentId: string | undefined,\n environmentMode: 'mixed' | 'force',\n verbose: boolean,\n externalSelection?: { specPath?: string; grep?: string },\n externalAreas?: string[],\n pinnedCommit?: string,\n externalHostShape?: string\n): Promise<NamedSuiteTrigger> {\n const authHeaders = {\n Authorization: `Bearer ${token}`,\n 'Content-Type': 'application/json',\n };\n\n // Resolve name → id (exact id is accepted as-is; otherwise match by name).\n const match = await resolveTestSuite(suiteArg, apiUrl, token);\n\n // The name came from the database, so it must bypass console branding — a\n // suite really named \"Canary Smoke\" would otherwise print as \"Cofactor\n // Smoke\". See writeRawOutput's contract in brand.ts.\n writeRawOutput(`Starting test suite: ${match.name}`);\n console.log(` Env mode: ${environmentMode}`);\n if (environmentMode === 'force' && environmentId) console.log(` Environment: ${environmentId}`);\n if (pinnedCommit) console.log(` Pinned commit: ${pinnedCommit} (never feeds visual baselines)`);\n console.log('');\n\n const runBody: {\n environmentId?: string;\n environmentMode: 'mixed' | 'force';\n externalSelection?: { specPath?: string; grep?: string };\n externalAreas?: string[];\n externalHostShape?: string;\n at?: string;\n } = {\n environmentMode,\n };\n // `!== undefined`, not truthiness: an empty value must reach the server and be refused, never run unpinned.\n if (pinnedCommit !== undefined) runBody.at = pinnedCommit;\n if (environmentMode === 'force' && environmentId) runBody.environmentId = environmentId;\n if (externalSelection) runBody.externalSelection = externalSelection;\n if (externalAreas) runBody.externalAreas = externalAreas;\n if (externalHostShape !== undefined) runBody.externalHostShape = externalHostShape;\n const runRes = await fetch(`${apiUrl}/test-suites/${match.id}/run`, {\n method: 'POST',\n headers: authHeaders,\n body: JSON.stringify(runBody),\n });\n if (!runRes.ok) {\n const body = await runRes.text();\n // A refused pin says why in `reason`; show it rather than the raw body.\n try {\n const refused = JSON.parse(body) as { userError?: boolean; error?: string; reason?: string };\n if (refused.userError === true && refused.reason) {\n console.error(`Error: ${refused.error ?? 'The suite cannot run'}: ${refused.reason}`);\n process.exit(1);\n }\n } catch {\n // Not JSON (a proxy error page, say): fall through to the raw body.\n }\n console.error(`Failed to start suite: ${runRes.status}`);\n console.error(body);\n process.exit(1);\n }\n const runJson = (await runRes.json()) as {\n ok: boolean;\n data?: NamedSuiteTrigger;\n error?: string;\n };\n if (!runJson.ok || !runJson.data) {\n console.error(`Failed to start suite: ${runJson.error ?? 'Unknown error'}`);\n process.exit(1);\n }\n if (verbose) {\n console.log(\n runJson.data.kind === 'external'\n ? `External run ID: ${runJson.data.runId}`\n : `Execution ID: ${runJson.data.executionId}`\n );\n console.log(`Job ID: ${runJson.data.jobId}`);\n console.log('');\n }\n return runJson.data;\n}\n\nexport async function streamExternalRun(\n apiUrl: string,\n token: string,\n run: Extract<NamedSuiteTrigger, { kind: 'external' }>,\n verbose: boolean\n): Promise<never> {\n const response = await fetch(`${apiUrl}/jobs/${run.runId}/stream`, {\n headers: { Authorization: `Bearer ${token}`, Accept: 'text/event-stream' },\n });\n if (!response.ok || !response.body) {\n console.error(`Failed to connect to external run stream: ${response.status}`);\n process.exit(1);\n }\n\n let sealed = false;\n let exitCode = 1;\n const parser = createParser({\n onEvent: (event: EventSourceMessage) => {\n if (!event.data || !event.event?.startsWith('external-run.')) return;\n try {\n const data = JSON.parse(event.data) as Record<string, unknown>;\n if (verbose) console.log(`[${event.event}] ${JSON.stringify(data)}`);\n if (event.event === 'external-run.shard' && !verbose) {\n console.log(\n ` shard ${String(data.shardIndex)}/${String(data.shardTotal)}: ${String(data.phase)} (attempt ${String(data.attempt)})`\n );\n }\n if (event.event === 'external-run.status' && data.status === 'canceled') {\n sealed = true;\n exitCode = 1;\n console.error('External test run was canceled.');\n }\n if (event.event === 'external-run.sealed') {\n sealed = true;\n const total = Number(data.total ?? 0);\n const passed = Number(data.passed ?? 0);\n const failed = Number(data.failed ?? 0);\n const flaky = Number(data.flaky ?? 0);\n const skipped = Number(data.skipped ?? 0);\n const summary = formatExternalRunSummary({\n total,\n passed,\n failed,\n flaky,\n skipped,\n resultsTruncated: data.resultsTruncated === true,\n });\n console.log('');\n console.log('─'.repeat(50));\n console.log(summary.message);\n console.log(`Run: ${apiUrl.replace(/\\/api\\/?$/, '')}${run.runUrl}`);\n exitCode = summary.exitCode;\n }\n } catch (error) {\n if (verbose) console.error(`Ignored malformed stream event: ${String(error)}`);\n }\n },\n });\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n try {\n while (!sealed) {\n const { done, value } = await reader.read();\n if (done) break;\n parser.feed(decoder.decode(value, { stream: true }));\n }\n } catch (error) {\n console.error(formatStreamReadFailure(error));\n } finally {\n reader.releaseLock();\n }\n if (!sealed) console.error(formatStreamClosedBeforeCompletion());\n process.exit(exitCode);\n}\n\nexport async function runRemoteTest(argv: string[]): Promise<void> {\n if (argv.some((arg) => arg.startsWith('--external-host-shape='))) {\n console.error('Error: use --external-host-shape <shape> with a space; equals syntax is not supported.');\n process.exit(1);\n }\n const { apiUrl, token } = await resolveConfig(argv);\n\n const tag = getArgValue(argv, '--tag');\n const namePattern = getArgValue(argv, '--name-pattern');\n // \"--application\" is the user-facing flag; \"--property\" stays as a backwards-compat alias.\n const propertyArg = getArgValue(argv, '--application') ?? getArgValue(argv, '--property');\n const environmentArg = getArgValue(argv, '--environment');\n const suiteArg = getArgValue(argv, '--suite');\n const externalSpec = getArgValue(argv, '--external-spec');\n const externalGrep = getArgValue(argv, '--external-grep');\n const externalHostShape = getArgValue(argv, '--external-host-shape');\n if (hasFlag(argv, '--external-host-shape') && (!externalHostShape || externalHostShape.startsWith('--'))) {\n console.error('Error: --external-host-shape needs a provider shape.');\n process.exit(1);\n }\n const areaArg = getArgValue(argv, '--area');\n const verbose = hasFlag(argv, '--verbose', '-v');\n const pinnedCommitArg = getArgValue(argv, '--at');\n // A pin that fails open would run an unpinned suite the caller never asked for.\n if (hasFlag(argv, '--at') && !pinnedCommitArg) {\n console.error('Error: --at needs a full commit sha.');\n process.exit(1);\n }\n if (pinnedCommitArg !== undefined && !suiteArg) {\n console.error('Error: --at requires --suite <name|id>.');\n process.exit(1);\n }\n if ((externalSpec || externalGrep || areaArg !== undefined || externalHostShape !== undefined) && !suiteArg) {\n console.error('Error: --area, --external-spec, --external-grep and --external-host-shape require --suite <name|id>.');\n process.exit(1);\n }\n // A spec path already names the tests to run, so it cannot also be narrowed\n // to areas. A title regex can: it is applied inside each area.\n if (areaArg !== undefined && externalSpec) {\n console.error(\n 'Error: --area cannot be combined with --external-spec (a spec path picks its own tests). ' +\n 'Use --external-grep to filter within an area.'\n );\n process.exit(1);\n }\n // One area or several: `--area a,b` fans out one sandbox per area, exactly as\n // a full run does, so the run page still groups by area.\n //\n // Tested for `undefined`, not truthiness: `--area \"\"` parses to the empty\n // string, and treating that as \"no selection\" would quietly run the whole\n // suite when the user plainly asked to narrow it.\n const externalAreas =\n areaArg === undefined\n ? undefined\n : areaArg\n .split(',')\n .map((area) => area.trim())\n .filter((area) => area.length > 0);\n if (externalAreas && externalAreas.length === 0) {\n console.error('Error: --area needs at least one area name.');\n process.exit(1);\n }\n\n // Env mode: 'mixed' (default) runs each flow on its own preferred env; 'force'\n // pins the whole suite to --environment (which then becomes required).\n const envModeArg = getArgValue(argv, '--env-mode');\n if (envModeArg !== undefined && envModeArg !== 'mixed' && envModeArg !== 'force') {\n console.error(`Error: --env-mode must be \"mixed\" or \"force\" (got \"${envModeArg}\").`);\n process.exit(1);\n }\n const environmentMode: 'mixed' | 'force' =\n (envModeArg as 'mixed' | 'force' | undefined) ?? (environmentArg ? 'force' : 'mixed');\n if (environmentMode === 'force' && !environmentArg) {\n console.error('Error: --env-mode force requires --environment <name|id>.');\n process.exit(1);\n }\n\n // 0. Resolve property/environment if specified\n let resolvedConfig: ResolvedConfig = {};\n if (propertyArg || environmentArg) {\n try {\n resolvedConfig = await resolveTestConfig(apiUrl, token, propertyArg, environmentArg);\n } catch (err) {\n console.error(`${err instanceof Error ? err.message : String(err)}`);\n process.exit(1);\n }\n }\n\n console.log('Starting remote workflow tests...');\n if (resolvedConfig.propertyId) console.log(` Application: ${propertyArg}`);\n if (resolvedConfig.environmentId) console.log(` Environment: ${environmentArg}`);\n if (tag) console.log(` Filtering by tag: ${tag}`);\n if (namePattern) console.log(` Filtering by name pattern: ${namePattern}`);\n console.log('');\n\n let suiteId: string;\n let jobId: string | undefined;\n let appUrl: string | undefined;\n let pinnedCommit: string | null = null;\n\n if (suiteArg) {\n // Named, layered suite run. Resolve name→id, then POST /test-suites/:id/run.\n const triggered = await triggerNamedSuiteRun(\n apiUrl,\n token,\n suiteArg,\n resolvedConfig.environmentId,\n environmentMode,\n verbose,\n externalSpec || externalGrep\n ? { ...(externalSpec ? { specPath: externalSpec } : {}), ...(externalGrep ? { grep: externalGrep } : {}) }\n : undefined,\n externalAreas,\n pinnedCommitArg,\n externalHostShape\n );\n if (triggered.kind === 'external') {\n return await streamExternalRun(apiUrl, token, triggered, verbose);\n }\n pinnedCommit = triggered.pinnedCommit;\n suiteId = triggered.executionId;\n jobId = triggered.jobId;\n } else {\n // 1. Trigger test run\n const queryParams = new URLSearchParams();\n if (tag) queryParams.set('tag', tag);\n if (namePattern) queryParams.set('namePattern', namePattern);\n\n const triggerUrl = `${apiUrl}/workflows/test-runs${queryParams.toString() ? `?${queryParams}` : ''}`;\n\n const body: Record<string, string> = { environmentMode };\n if (resolvedConfig.propertyId) body.propertyId = resolvedConfig.propertyId;\n if (environmentMode === 'force' && resolvedConfig.environmentId)\n body.environmentId = resolvedConfig.environmentId;\n\n let triggerRes: Response;\n try {\n triggerRes = await fetch(triggerUrl, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${token}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(body),\n });\n } catch (err) {\n console.error(`Failed to connect to API: ${err}`);\n process.exit(1);\n }\n\n if (!triggerRes.ok) {\n console.error(formatTriggerFailure(triggerRes.status, await triggerRes.text()));\n process.exit(1);\n }\n\n const triggerData = (await triggerRes.json()) as TriggerResponse;\n if (!triggerData.ok || !triggerData.suiteId) {\n console.error(`Failed to start tests: ${triggerData.error ?? 'Unknown error'}`);\n process.exit(1);\n }\n suiteId = triggerData.suiteId;\n jobId = triggerData.jobId;\n appUrl = triggerData.appUrl;\n }\n\n if (verbose) {\n console.log(`Suite ID: ${suiteId}`);\n console.log(`Job ID: ${jobId}`);\n console.log('');\n }\n\n // 2. Subscribe to SSE stream\n const streamUrl = `${apiUrl}/workflows/test-runs/stream?suiteId=${suiteId}`;\n\n let streamRes: Response;\n try {\n streamRes = await fetch(streamUrl, {\n headers: {\n Authorization: `Bearer ${token}`,\n Accept: 'text/event-stream',\n },\n });\n } catch (err) {\n console.error(`Failed to connect to event stream: ${err}`);\n process.exit(1);\n }\n\n if (!streamRes.ok || !streamRes.body) {\n console.error(`Failed to connect to event stream: ${streamRes.status}`);\n process.exit(1);\n }\n\n // Track state\n let hasCompleted = false;\n const workflowNames = new Map<string, string>();\n const failedTests: FailedTestTracker = new Map();\n // The last suite event wins: its counters are recomputed server-side from the\n // deduplicated latest attempt per test, so they are authoritative over\n // anything this process could tally from the individual test events.\n let suiteStats: SuiteSummaryStats = EMPTY_SUITE_STATS;\n let streamInterrupted = false;\n\n // 3. Parse SSE events\n const parser = createParser({\n onEvent: (event: EventSourceMessage) => {\n if (!event.data) return;\n\n try {\n const data = JSON.parse(event.data) as\n | WorkflowTestEvent\n | WorkflowTestSuiteEvent\n | { error?: string };\n\n if (verbose) {\n console.log(`[${event.event}] ${JSON.stringify(data)}`);\n }\n\n if (event.event === 'workflow-test') {\n const testEvent = data as WorkflowTestEvent;\n const { status, workflowId, message } = testEvent;\n const name =\n workflowNames.get(workflowId) ||\n message?.replace(/^Flow \"(.+)\" .*$/, '$1') ||\n workflowId;\n\n if (message?.startsWith('Flow \"')) {\n const match = message.match(/^Flow \"(.+?)\" /);\n if (match) workflowNames.set(workflowId, match[1]);\n }\n\n // Tracked regardless of verbosity: the \"Failed tests\" section at the\n // end is built from this, and a later attempt's pass retracts an\n // earlier failure here.\n trackFailedTest(failedTests, {\n status,\n workflowId,\n name,\n testRunId: testEvent.testRunId,\n });\n\n if (!verbose) {\n const line = formatTestResultLine({\n status,\n name,\n errorMessage: testEvent.errorMessage,\n skipReason: testEvent.skipReason,\n });\n if (line) console.log(line);\n }\n }\n\n if (event.event === 'workflow-test-suite') {\n const suiteEvent = data as WorkflowTestSuiteEvent;\n suiteStats = suiteEvent;\n\n if (suiteEvent.status === 'completed') {\n hasCompleted = true;\n }\n }\n\n if (event.event === 'error') {\n const errorData = data as { error?: string };\n console.error(`Stream error: ${errorData.error ?? 'Unknown error'}`);\n streamInterrupted = true;\n }\n } catch {\n // Ignore parse errors for keepalive, etc.\n }\n },\n });\n\n const reader = streamRes.body.getReader();\n const decoder = new TextDecoder();\n\n try {\n while (!hasCompleted) {\n const { done, value } = await reader.read();\n if (done) {\n if (!hasCompleted) {\n streamInterrupted = true;\n console.error(formatStreamClosedBeforeCompletion());\n }\n break;\n }\n parser.feed(decoder.decode(value, { stream: true }));\n }\n } catch (err) {\n streamInterrupted = true;\n console.error(formatStreamReadFailure(err));\n } finally {\n reader.releaseLock();\n }\n\n // 4. Print summary\n console.log('');\n console.log('─'.repeat(50));\n\n // One rule for the verdict, shared with the unit tests, instead of a second\n // copy of it inline here — the no-workflows case included.\n const summary = formatSummary(suiteStats);\n console.log(summary.message);\n if (pinnedCommit) console.log(`Pinned commit: ${pinnedCommit}`);\n\n // A truncated stream makes every counter above a partial read: a workflow\n // that would have failed may simply not have reported yet. Collapse to a\n // plain failure rather than keeping a verdict the partial read cannot\n // support, and say so, so the printed summary and the exit code agree\n // instead of pairing \"PASSED\" with a non-zero exit.\n let exitCode = summary.exitCode;\n if (streamInterrupted) {\n console.log('Result stream did not complete — the summary above is a partial read.');\n exitCode = 1;\n }\n\n const failedSection = formatFailedTests([...failedTests.values()], appUrl);\n if (failedSection) {\n console.log(failedSection);\n }\n\n if (suiteStats.failedWorkflows > 0) {\n console.log('');\n console.log(`Triage: canary triage wait ${suiteId}`);\n }\n\n process.exit(exitCode);\n}\n","import { UUID_RE } from './cli-helpers.js';\n\ntype TestConfigProperty = {\n id: string;\n name: string;\n baseUrl: string;\n environments: Array<{\n id: string;\n name: string;\n url: string;\n isDefault: boolean;\n environmentKindId?: string | null;\n environmentKindName?: string | null;\n }>;\n};\n\ntype TestConfigResponse = {\n ok: boolean;\n properties: TestConfigProperty[];\n error?: string;\n};\n\nexport type ResolvedConfig = {\n propertyId?: string;\n environmentId?: string;\n};\n\nexport async function resolveTestConfig(\n apiUrl: string,\n token: string,\n propertyArg?: string,\n environmentArg?: string\n): Promise<ResolvedConfig> {\n if (!propertyArg && !environmentArg) return {};\n\n const res = await fetch(`${apiUrl}/workflows/test-config`, {\n headers: { Authorization: `Bearer ${token}` },\n });\n\n if (!res.ok) {\n const text = await res.text();\n throw new Error(`Failed to fetch test config: ${res.status} ${text}`);\n }\n\n const data = (await res.json()) as TestConfigResponse;\n if (!data.ok || !data.properties) {\n throw new Error(`Failed to fetch test config: ${data.error ?? 'Unknown error'}`);\n }\n\n const { properties } = data;\n let property: TestConfigProperty | undefined;\n\n if (propertyArg) {\n if (UUID_RE.test(propertyArg)) {\n property = properties.find((p) => p.id === propertyArg);\n } else {\n property = properties.find((p) => p.name.toLowerCase() === propertyArg.toLowerCase());\n }\n if (!property) {\n const available = properties.map((p) => ` - ${p.name} (${p.id})`).join('\\n');\n throw new Error(\n `Application \"${propertyArg}\" not found.\\nAvailable applications:\\n${available}`\n );\n }\n } else if (environmentArg) {\n // --environment without --application: auto-resolve if single application\n if (properties.length === 0) {\n throw new Error('No applications found in your organization.');\n }\n if (properties.length > 1) {\n const available = properties.map((p) => ` - ${p.name} (${p.id})`).join('\\n');\n throw new Error(\n `--environment requires --application when multiple applications exist.\\nAvailable applications:\\n${available}`\n );\n }\n property = properties[0];\n }\n\n const result: ResolvedConfig = {};\n if (property) {\n result.propertyId = property.id;\n\n if (environmentArg) {\n let env: TestConfigProperty['environments'][number] | undefined;\n if (UUID_RE.test(environmentArg)) {\n env = property.environments.find((e) => e.id === environmentArg);\n } else {\n env = property.environments.find(\n (e) =>\n e.name.toLowerCase() === environmentArg.toLowerCase() ||\n e.environmentKindName?.toLowerCase() === environmentArg.toLowerCase()\n );\n }\n if (!env) {\n const available = property.environments\n .map((e) => ` - ${e.name}${e.isDefault ? ' (default)' : ''} (${e.id})`)\n .join('\\n');\n throw new Error(\n `Environment \"${environmentArg}\" not found for application \"${property.name}\".\\nAvailable environments:\\n${available}`\n );\n }\n result.environmentId = env.id;\n }\n }\n\n return result;\n}\n","import {\n WORKFLOW_TEST_FAILED_STATUSES,\n WORKFLOW_TEST_SUCCESS_STATUSES,\n type WorkflowTestStatus,\n type WorkflowTestSuiteEvent,\n} from '@chatsdet/types';\n\nexport function getArgValue(argv: string[], key: string): string | undefined {\n const index = argv.indexOf(key);\n if (index === -1 || index >= argv.length - 1) return undefined;\n return argv[index + 1];\n}\n\nexport function hasFlag(argv: string[], ...flags: string[]): boolean {\n return flags.some((flag) => argv.includes(flag));\n}\n\nexport function buildQueryParams(tag?: string, namePattern?: string): string {\n const params = new URLSearchParams();\n if (tag) params.set('tag', tag);\n if (namePattern) params.set('namePattern', namePattern);\n return params.toString();\n}\n\nexport function extractWorkflowName(message: string | undefined, workflowId: string): string {\n if (!message) return workflowId;\n const match = message.match(/^Flow \"(.+?)\" /);\n return match ? match[1] : workflowId;\n}\n\nexport type FailedTest = { name: string; testRunId: string };\n\n/**\n * Failed tests keyed by `workflowId` rather than accumulated in a list.\n *\n * A retried test arrives as a `failed` event followed by a `flaky_success`\n * event for the SAME workflow. Keying by workflow is what lets the later pass\n * remove the earlier failure — an append-only list left the run listed under\n * \"Failed tests\" while the summary line directly above it counted the workflow\n * as a pass.\n *\n * KNOWN LIMITATION: suite scenario expansion fans one workflow out to several\n * test runs in the same execution, which share a `workflowId` and differ only\n * by `scenarioId`. A failing scenario followed by a passing one would therefore\n * retract the failure from this list. The CLI cannot key more precisely today —\n * `WorkflowTestEvent` carries no `scenarioId` over SSE — and keying by\n * `testRunId` instead would break the retry case above, which is the one that\n * actually occurs: of 7,847 production test runs in the 30 days to 2026-09-11,\n * zero had a `scenario_id`. Closing this properly means adding `scenarioId` to\n * the SSE payload, not guessing here. Note the summary COUNTS come from the\n * suite event and stay correct either way; only this list is affected.\n */\nexport type FailedTestTracker = Map<string, FailedTest>;\n\n/**\n * Record or clear one workflow's failure state from a single test event.\n *\n * Only hard failures are listed: `inconclusive`, `skipped` and `degraded` are\n * terminal without being failures (see `WORKFLOW_TEST_FAILED_STATUSES`), and\n * listing them under \"Failed tests\" would overstate what actually broke.\n */\nexport function trackFailedTest(\n tracker: FailedTestTracker,\n event: { status: WorkflowTestStatus; workflowId: string; name: string; testRunId: string }\n): void {\n if (WORKFLOW_TEST_SUCCESS_STATUSES.has(event.status)) {\n tracker.delete(event.workflowId);\n return;\n }\n if (WORKFLOW_TEST_FAILED_STATUSES.has(event.status)) {\n tracker.set(event.workflowId, { name: event.name, testRunId: event.testRunId });\n }\n}\n\n/** Keep a failure message from swamping the result line it annotates. */\nconst ERROR_EXCERPT_LIMIT = 200;\n\nconst excerpt = (text: string | null | undefined): string | null => {\n const trimmed = text?.trim();\n if (!trimmed) return null;\n return trimmed.length > ERROR_EXCERPT_LIMIT\n ? `${trimmed.slice(0, ERROR_EXCERPT_LIMIT)}\\u2026`\n : trimmed;\n};\n\n/**\n * Render one test's result line, or null while the test is still in flight.\n *\n * The `switch` is exhaustive over `WorkflowTestStatus` on purpose: the previous\n * if/else chain covered 4 of the 11 statuses and silently printed nothing for\n * the other 7, so a suite could finish having shown the operator fewer results\n * than it ran. The `never` assignment in the default makes adding a status to\n * the enum a typecheck failure here rather than another silent omission.\n */\nexport function formatTestResultLine(event: {\n status: WorkflowTestStatus;\n name: string;\n errorMessage?: string | null;\n skipReason?: string | null;\n}): string | null {\n const { status, name } = event;\n const detail = excerpt(event.errorMessage);\n const reason = excerpt(event.skipReason);\n\n switch (status) {\n case 'queued':\n case 'running':\n // In-flight: the terminal event for this test is still coming.\n return null;\n case 'waiting':\n return ` \\u23F3 ${name} (waiting for scheduled time)`;\n case 'success':\n return ` \\u2713 ${name}`;\n case 'flaky_success':\n return ` \\u2713 ${name} (flaky \\u2014 passed on retry)`;\n case 'failed':\n return detail ? ` \\u2717 ${name}\\n Error: ${detail}` : ` \\u2717 ${name}`;\n case 'timeout':\n return detail\n ? ` \\u2717 ${name} (timed out)\\n Error: ${detail}`\n : ` \\u2717 ${name} (timed out)`;\n case 'canceled':\n return ` \\u2717 ${name} (canceled)`;\n case 'inconclusive':\n return detail\n ? ` ? ${name} (inconclusive \\u2014 no verdict)\\n Reason: ${detail}`\n : ` ? ${name} (inconclusive \\u2014 no verdict)`;\n case 'skipped':\n return reason ? ` \\u2013 ${name} (skipped: ${reason})` : ` \\u2013 ${name} (skipped)`;\n case 'degraded':\n return ` \\u2013 ${name} (degraded \\u2014 not run)`;\n default: {\n // Compile-time exhaustiveness; at runtime a status from a server newer\n // than this CLI reaches here, and a bare status word is a worse line than\n // none. The suite counters still force exit 2 for it (see formatSummary).\n const exhaustive: never = status;\n void exhaustive;\n return null;\n }\n }\n}\n\nexport function formatFailedTests(\n failedTests: FailedTest[],\n appUrl: string | undefined\n): string | null {\n if (failedTests.length === 0 || !appUrl) return null;\n\n const lines = ['', 'Failed tests:'];\n for (const t of failedTests) {\n lines.push(` \\u2717 ${t.name}`);\n lines.push(` ${appUrl}/runs/tests/${t.testRunId}`);\n }\n return lines.join('\\n');\n}\n\n/** A suite ran, but at least one workflow produced no verdict. */\nexport const EXIT_INCONCLUSIVE = 2;\n\n/**\n * Counters the verdict is computed from — read straight off one\n * `workflow-test-suite` event, so this cannot drift from the wire shape.\n */\nexport type SuiteSummaryStats = Pick<\n WorkflowTestSuiteEvent,\n | 'totalWorkflows'\n | 'completedWorkflows'\n | 'successfulWorkflows'\n | 'failedWorkflows'\n | 'inconclusiveWorkflows'\n | 'skippedWorkflows'\n | 'degradedWorkflows'\n | 'waitingWorkflows'\n>;\n\n/**\n * Summarize a finished suite and decide the process exit code.\n *\n * Three outcomes, not two. A run that produced no verdict — the platform\n * errored before it could check the product — is neither a pass nor a failure;\n * calling it a pass is how production suites that ran 9/9, 10/10 and 12/12\n * inconclusive each reported a green CI build having verified nothing. A real\n * failure still outranks one: a suite with both exits 1, because a product bug\n * is the more actionable signal.\n *\n * The green test is deliberately an INVARIANT rather than a list of the\n * statuses we currently know about: a suite is clean only when every workflow\n * is accounted for as passed, skipped, degraded, or still scheduled. Asking\n * instead whether `inconclusiveWorkflows > 0` would reopen this very bug the\n * day a new no-verdict status gets its own counter column — it would land in\n * neither term and exit 0 silently. Stated this way, anything the CLI cannot\n * account for forces exit 2, including a counter an older server never sent.\n *\n * The explicit `inconclusiveWorkflows` check is kept ALONGSIDE the invariant\n * rather than replaced by it, because the two fail in opposite directions and\n * neither can produce a false green alone. 24 of 2,164 production executions\n * in the 60 days to 2026-09-11 report counters summing ABOVE `totalWorkflows`\n * (overlapping counters), which zeroes `noVerdict` — and would hide a known\n * inconclusive behind an arithmetic artifact if the invariant were the only\n * test. No such execution has a non-zero inconclusive count today; this keeps\n * that from being load-bearing.\n */\nexport function formatSummary(stats: SuiteSummaryStats): { message: string; exitCode: number } {\n const {\n totalWorkflows,\n successfulWorkflows,\n failedWorkflows,\n completedWorkflows,\n inconclusiveWorkflows,\n skippedWorkflows,\n degradedWorkflows,\n waitingWorkflows,\n } = stats;\n\n if (totalWorkflows === 0) {\n return { message: 'No workflows found matching the filter criteria.', exitCode: 0 };\n }\n\n // Clamped per counter, not just on the total: production emits a NEGATIVE\n // `waitingWorkflows` on some executions (six in the 60 days to 2026-09-11,\n // e.g. total 31 / successful 31 / waiting -2 — a server-side counter\n // decremented past zero). Summed raw, that corrupt counter drags the total\n // below `totalWorkflows` and fails an all-green suite, which is a far worse\n // outcome than the miscount it comes from.\n const accountedFor =\n Math.max(0, successfulWorkflows) +\n Math.max(0, failedWorkflows) +\n Math.max(0, skippedWorkflows) +\n Math.max(0, degradedWorkflows) +\n Math.max(0, waitingWorkflows);\n const noVerdict = Math.max(0, totalWorkflows - accountedFor);\n\n const passRate = Math.round((successfulWorkflows / totalWorkflows) * 100);\n const stillWaiting = totalWorkflows - completedWorkflows;\n\n let message: string;\n let exitCode: number;\n\n if (failedWorkflows > 0) {\n message = `FAILED: ${failedWorkflows} of ${totalWorkflows} workflows failed (${passRate}% pass rate)`;\n if (inconclusiveWorkflows > 0) {\n message += `, ${inconclusiveWorkflows} inconclusive`;\n }\n exitCode = 1;\n } else if (noVerdict > 0 || inconclusiveWorkflows > 0) {\n // Whichever term actually fired: when overlapping counters zero `noVerdict`,\n // the inconclusive count is the only honest number left to report.\n const withoutVerdict = Math.max(noVerdict, inconclusiveWorkflows);\n message =\n `INCONCLUSIVE: ${withoutVerdict} of ${totalWorkflows} workflows produced no verdict ` +\n `(${successfulWorkflows} passed)`;\n exitCode = EXIT_INCONCLUSIVE;\n } else {\n message = `PASSED: ${successfulWorkflows} of ${totalWorkflows} workflows passed`;\n exitCode = 0;\n }\n\n if (stillWaiting > 0) {\n message += `\\nNote: ${stillWaiting} workflow(s) are still waiting (scheduled for later)`;\n }\n\n // Counters that sum past the total are impossible, so the verdict above rests\n // on numbers that disagree with each other. Surfaced rather than acted on: all\n // 24 such executions in the 60 days to 2026-09-11 also had failures and so\n // exit 1 regardless, and promoting an overcount to a failure on its own would\n // redden builds over a server-side miscount with no evidence anything is\n // actually wrong with the run.\n if (accountedFor > totalWorkflows) {\n message += `\\nNote: the server reported ${accountedFor} outcomes for ${totalWorkflows} workflows — counters disagree.`;\n }\n\n return { message, exitCode };\n}\n\nexport function formatExternalRunSummary(stats: {\n total: number;\n passed: number;\n failed: number;\n flaky: number;\n skipped: number;\n resultsTruncated: boolean;\n}): { message: string; exitCode: number } {\n const { total, passed, failed, flaky, skipped, resultsTruncated } = stats;\n\n if (resultsTruncated) {\n return {\n message: `FAILED: external run ended with incomplete results (${total} collected)`,\n exitCode: 1,\n };\n }\n if (total === 0) {\n return { message: 'FAILED: external run produced no test results', exitCode: 1 };\n }\n if (failed > 0) {\n return { message: `FAILED: ${failed} of ${total} external tests failed`, exitCode: 1 };\n }\n return {\n message: `PASSED: ${passed} passed, ${flaky} flaky, ${skipped} skipped`,\n exitCode: 0,\n };\n}\n\nexport function formatStreamReadFailure(error: unknown): string {\n const detail = error instanceof Error && error.message.trim() ? ` (${error.message})` : '';\n return [\n `Test result stream ended unexpectedly${detail}.`,\n 'The test suite may still be running in Canary.',\n 'Re-run the command or check the test suite in the app for final status.',\n ].join(' ');\n}\n\nexport function formatStreamClosedBeforeCompletion(): string {\n return [\n 'Test result stream ended before the suite completed.',\n 'The test suite may still be running in Canary.',\n 'Re-run the command or check the test suite in the app for final status.',\n ].join(' ');\n}\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/cli-helpers.ts"],"sourcesContent":["/**\n * Shared CLI helpers for superadmin management commands (knobs, feature-flags).\n */\n\nimport { readFileSync } from 'node:fs';\nimport fs from 'node:fs/promises';\nimport path from 'node:path';\nimport process from 'node:process';\nimport { getCanaryTmpDir } from '@chatsdet/tmp';\nimport { VALID_MACRO_NAMES } from '@chatsdet/types';\nimport { getArgValue } from './auth.js';\nimport { apiFetch, exitOnFailure, isTerminalFailure } from './api-fetch.js';\nimport { decodeJwtPayload, isSuperadminToken } from './jwt.js';\n\n/**\n * Canonical hyphenated UUID — the CLI's single copy, matching `isUuid` in\n * `@chatsdet/ids`. The CLI keeps its own only because `@chatsdet/ids` is not\n * currently a workspace dependency here; adding it would work the same way\n * `@chatsdet/tmp` and `@chatsdet/types` do below (devDependencies that tsup\n * bundles). Fold this into `isUuid` if that dependency is ever added.\n */\nexport const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\nexport type LifecycleStage = 'active' | 'baking' | 'internal' | 'deprecated' | 'ready_for_cleanup';\n\n/** Stages knobs may use — `baking` (the oven) and `internal` are flag-only. */\nexport const KNOB_LIFECYCLE_STAGES: LifecycleStage[] = [\n 'active',\n 'deprecated',\n 'ready_for_cleanup',\n];\n\nexport function toLifecycleLabel(stage: LifecycleStage): string {\n switch (stage) {\n case 'baking':\n return 'baking';\n case 'internal':\n return 'internal';\n case 'deprecated':\n return 'deprecated';\n case 'ready_for_cleanup':\n return 'ready_for_cleanup';\n default:\n return 'active';\n }\n}\n\nexport function parseLifecycleStage(\n argv: string[],\n allowedStages: LifecycleStage[] = [\n 'active',\n 'baking',\n 'internal',\n 'deprecated',\n 'ready_for_cleanup',\n ]\n): LifecycleStage {\n const stage = getArgValue(argv, '--stage');\n if (!stage || !allowedStages.includes(stage as LifecycleStage)) {\n console.error(`Error: --stage is required and must be one of: ${allowedStages.join(', ')}`);\n process.exit(1);\n }\n return stage as LifecycleStage;\n}\n\n/**\n * Target-org id for superadmin cross-org commands (feature-flag, product,\n * account, task). `--org` is now the global identity selector — which of *your*\n * logged-in orgs to act as — so cross-org targeting uses `--target-org`.\n *\n * The two flags are mutually exclusive by design: a bare `--org` naming an org\n * you aren't logged into is already rejected by identity resolution (with a hint\n * to use `--target-org`); if it names one you ARE logged into, we stop here so a\n * superadmin action never silently runs under the wrong identity. Either way the\n * fix is the same — use `--target-org`.\n */\nexport function getTargetOrg(argv: string[]): string | undefined {\n const target = getArgValue(argv, '--target-org');\n if (target) return target;\n if (getArgValue(argv, '--org')) {\n console.error(\n 'Error: --org selects which of your logged-in orgs to act as. To target a ' +\n 'different org for this command, use --target-org.'\n );\n process.exit(1);\n }\n return undefined;\n}\n\n/** Drop exactly one trailing `\\n` or `\\r\\n`; any other trailing whitespace is part of the value. */\nexport function stripOneTrailingNewline(raw: string): string {\n if (raw.endsWith('\\r\\n')) return raw.slice(0, -2);\n if (raw.endsWith('\\n')) return raw.slice(0, -1);\n return raw;\n}\n\n/**\n * Resolve a `<text|@file>` flag value: `@path` reads that file, anything else\n * is the text itself. An unreadable file prints `Error reading <path>` and\n * exits 1 rather than surfacing a stack trace.\n */\nexport function readTextOrFile(value: string): string {\n if (!value.startsWith('@')) return value;\n const file = value.slice(1);\n try {\n return readFileSync(file, 'utf8');\n } catch (err) {\n console.error(`Error reading ${file}: ${err instanceof Error ? err.message : String(err)}`);\n process.exit(1);\n }\n}\n\n/**\n * Connecting an integration from the CLI is admin-only (the API answers\n * `ADMIN_REQUIRED` to anyone else), so a member is told before a browser opens\n * or a question is asked. The role is the one `GET /cli-login/orgs` reports for\n * the token's org (as `cofactor orgs list` shows it); a superadmin passes, as on\n * the API. `integration` is the `cofactor` subcommand (`slack`, `github`).\n */\nexport async function requireOrgAdmin(\n apiUrl: string,\n token: string,\n integration: string,\n action: string\n): Promise<void> {\n if (isSuperadminToken(token)) return;\n const orgId = decodeJwtPayload(token)?.org_id;\n const result = await apiRequest<{\n ok: boolean;\n error?: string;\n organizations?: Array<{ id: string; name: string; role: string }>;\n }>(apiUrl, token, 'GET', '/cli-login/orgs');\n const die = (message: string): never => {\n console.error(`Error: ${message}`);\n return process.exit(1);\n };\n if (!result.ok) die(result.error ?? 'failed to read your role in this organization');\n const org = result.organizations?.find((o) => o.id === orgId);\n if (org?.role !== 'admin') {\n const label = integration[0].toUpperCase() + integration.slice(1);\n die(\n `Only organization admins can ${action} ${label}. Ask an admin of ${org?.name ?? 'this organization'} to run \\`cofactor ${integration} ${action}\\`.`\n );\n }\n}\n\n/** All of stdin as UTF-8 text (Node and Bun alike). */\nexport async function readStdin(): Promise<string> {\n const chunks: Buffer[] = [];\n for await (const chunk of process.stdin) chunks.push(Buffer.from(chunk));\n return Buffer.concat(chunks).toString('utf-8');\n}\n\nexport async function apiRequest<T extends { ok: boolean; error?: string }>(\n apiUrl: string,\n token: string,\n method: string,\n path: string,\n body?: Record<string, unknown>,\n options?: { timeoutMs?: number }\n): Promise<T> {\n const result = await apiFetch(`${apiUrl}${path}`, {\n method,\n headers: {\n Authorization: `Bearer ${token}`,\n 'Content-Type': 'application/json',\n 'User-Agent': 'Cofactor-CLI',\n },\n token,\n ...(options?.timeoutMs !== undefined ? { timeoutMs: options.timeoutMs } : {}),\n ...(body ? { body: JSON.stringify(body) } : {}),\n });\n\n if (!result.ok) {\n const { failure } = result;\n // Terminal kinds (auth/proxy/TLS) surface the full diagnostic and exit;\n // http-error falls through so the caller can pretty-print structured\n // user errors (unresolved vars, invalid macros, etc.).\n if (isTerminalFailure(failure)) exitOnFailure(failure);\n const text = failure.body ?? '';\n let errorMessage = failure.message;\n let parsed: Record<string, unknown> | null = null;\n try {\n const data = JSON.parse(text);\n if (data && typeof data === 'object') {\n parsed = data as Record<string, unknown>;\n if (typeof parsed.error === 'string') errorMessage = parsed.error;\n }\n } catch {\n /* keep failure.message */\n }\n return { ...parsed, ok: false, error: errorMessage } as T;\n }\n\n try {\n return (await result.response.json()) as T;\n } catch {\n return { ok: false, error: `Invalid JSON response (${result.response.status})` } as T;\n }\n}\n\n/** One environment option carried by the workflow-detail payload. */\nexport type WorkflowEnvOption = {\n id: string;\n name: string;\n type: string;\n isDefault: boolean;\n environmentKindId?: string | null;\n environmentKindName?: string | null;\n};\n\n/**\n * Fetch the environments available on a workflow's property (from the\n * workflow-detail payload). Shared by `workflow run --environment` and\n * `workflow patch --default-environment` so both resolve names identically.\n */\nexport async function fetchWorkflowEnvironments(\n apiUrl: string,\n token: string,\n workflowId: string\n): Promise<WorkflowEnvOption[]> {\n const res = await apiRequest<{\n ok: boolean;\n error?: string;\n workflow?: { environments?: WorkflowEnvOption[] };\n }>(apiUrl, token, 'GET', `/workflows/${workflowId}`);\n return res.workflow?.environments ?? [];\n}\n\n/**\n * Resolve a `--environment` argument (UUID or case-insensitive name) to an\n * environment id against the flow's property. Exits with an actionable error\n * listing the known environments when no match is found.\n */\nexport function resolveEnvironmentByNameOrId(envs: WorkflowEnvOption[], nameOrId: string): string {\n const match =\n envs.find((e) => e.id === nameOrId) ??\n envs.find((e) => e.name.toLowerCase() === nameOrId.toLowerCase()) ??\n envs.find((e) => e.environmentKindName?.toLowerCase() === nameOrId.toLowerCase());\n if (match) return match.id;\n // A bare UUID that isn't in the (possibly empty) known list may still be a\n // valid env the server will accept — e.g. a login-less flow with no resolved\n // property scope being pointed at a sandbox env. Pass it through and let the\n // server be the authority; only hard-fail on an unresolvable *name*.\n if (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(nameOrId)) {\n return nameOrId;\n }\n console.error(\n `Error: environment \"${nameOrId}\" not found on this flow's property. Known: ${\n envs.map((e) => `${e.name} (${e.type})`).join(', ') || '(none)'\n }`\n );\n process.exit(1);\n}\n\ntype UnresolvedRef = {\n nodeId?: string;\n nodeType?: string;\n field?: string;\n variable: string;\n};\n\n/**\n * Render a structured `userError` response from the workflow API into a\n * multi-line, actionable message. Recognizes `WORKFLOW_UNRESOLVED_VARIABLES`\n * and falls back to `✖ {code}: {message}` for other codes. Returns `null` if\n * the response body isn't a recognized structured error.\n */\nexport function renderWorkflowUserError(result: Record<string, unknown>): string | null {\n const code = typeof result.code === 'string' ? result.code : null;\n const message = typeof result.error === 'string' ? result.error : null;\n if (!code) return null;\n\n if (code === 'WORKFLOW_VALIDATION_FAILED') {\n type ValidationIssue = {\n nodeId?: string | null;\n nodeType?: string | null;\n field?: string;\n message?: string;\n code?: string;\n severity?: 'error' | 'warning';\n };\n const issues = Array.isArray(result.issues) ? (result.issues as ValidationIssue[]) : [];\n const errors = issues.filter((i) => (i.severity ?? 'error') === 'error');\n const warnings = issues.filter((i) => i.severity === 'warning');\n\n const lines: string[] = [\n `✖ Validation failed (${errors.length} error${errors.length === 1 ? '' : 's'}` +\n (warnings.length > 0\n ? `, ${warnings.length} warning${warnings.length === 1 ? '' : 's'}`\n : '') +\n ')',\n ];\n if (message) lines.push(` ${message}`);\n\n // Group by nodeId (null = flow-level).\n const byNode = new Map<string, ValidationIssue[]>();\n for (const issue of issues) {\n const key = issue.nodeId ?? '__flow__';\n const arr = byNode.get(key) ?? [];\n arr.push(issue);\n byNode.set(key, arr);\n }\n for (const [nodeKey, nodeIssues] of byNode) {\n const heading =\n nodeKey === '__flow__'\n ? 'Flow'\n : `Node ${nodeKey}${nodeIssues[0]?.nodeType ? ` (${nodeIssues[0].nodeType})` : ''}`;\n lines.push(` ${heading}:`);\n for (const issue of nodeIssues) {\n const sev = issue.severity === 'warning' ? '⚠' : '•';\n const field = issue.field ? `${issue.field} — ` : '';\n lines.push(` ${sev} ${field}${issue.message ?? ''}`);\n }\n }\n return lines.join('\\n');\n }\n\n if (code === 'WORKFLOW_UNRESOLVED_VARIABLES') {\n const lines: string[] = [`✖ ${code}`];\n if (message) lines.push(` ${message}`);\n const refs = Array.isArray(result.refs) ? (result.refs as UnresolvedRef[]) : [];\n const variables = Array.isArray(result.variables)\n ? (result.variables as string[])\n : refs.map((r) => r.variable);\n\n if (refs.length > 0) {\n lines.push(' Unresolved references:');\n for (const ref of refs) {\n const loc = ref.field && ref.nodeId ? ` (node ${ref.nodeId}, ${ref.field})` : '';\n lines.push(` • $var.${ref.variable}${loc}`);\n }\n } else if (variables.length > 0) {\n lines.push(' Unresolved:');\n for (const v of variables) lines.push(` • $var.${v}`);\n }\n\n const macroHints = variables.filter((v) => VALID_MACRO_NAMES.has(v.toUpperCase()));\n if (macroHints.length > 0) {\n lines.push(' Hint: these look like macro names, not variables:');\n for (const v of macroHints) {\n lines.push(` $var.${v} → $${v.toUpperCase()}()`);\n }\n }\n return lines.join('\\n');\n }\n\n if (code === 'WORKFLOW_INVALID_INPUT_VALUES') {\n const lines: string[] = [`✖ ${code}`];\n if (message) lines.push(` ${message}`);\n const issues = Array.isArray(result.issues)\n ? (result.issues as Array<{\n key?: string;\n value?: string;\n reason?: string;\n variables?: string[];\n }>)\n : [];\n for (const issue of issues) {\n const key = issue.key ?? '?';\n const value = issue.value ?? '';\n if (issue.reason === 'self_referential') {\n lines.push(` • ${key}: self-referential → ${value}`);\n lines.push(` Use a literal or point at a different upstream variable.`);\n } else if (issue.reason === 'macro_name_used_as_var') {\n lines.push(` • ${key}: macro name used as $var → ${value}`);\n for (const v of issue.variables ?? []) {\n lines.push(` $var.${v} → $${v.toUpperCase()}()`);\n }\n } else {\n lines.push(` • ${key}: ${issue.reason ?? 'invalid'} → ${value}`);\n }\n }\n return lines.join('\\n');\n }\n\n if (code === 'WORKFLOW_INVALID_MACRO') {\n const lines: string[] = [`✖ ${code}`];\n if (message) lines.push(` ${message}`);\n const invalid = Array.isArray(result.invalid) ? (result.invalid as string[]) : [];\n const valid = Array.isArray(result.valid) ? (result.valid as string[]) : [];\n if (invalid.length > 0) {\n lines.push(` Invalid macro(s): ${invalid.map((n) => `$${n}()`).join(', ')}`);\n }\n if (valid.length > 0) {\n lines.push(` Valid macros: ${valid.map((n) => `$${n}()`).join(', ')}`);\n }\n return lines.join('\\n');\n }\n\n return `✖ ${code}${message ? `: ${message}` : ''}`;\n}\n\nexport async function downloadStorageState(opts: {\n apiUrl: string;\n token: string;\n propertyId: string;\n credentialId: string;\n prefix?: string;\n}): Promise<string | undefined> {\n const tmpFile = path.join(getCanaryTmpDir(), `${opts.prefix ?? 'canary-ss'}-${Date.now()}.json`);\n try {\n const res = await fetch(\n `${opts.apiUrl}/org/properties/${opts.propertyId}/credentials/${opts.credentialId}/storage-state/download`,\n {\n headers: { Authorization: `Bearer ${opts.token}` },\n redirect: 'follow',\n }\n );\n if (res.ok) {\n const body = await res.text();\n await fs.writeFile(tmpFile, body, 'utf-8');\n return tmpFile;\n }\n } catch {\n // Caller handles missing storage state\n }\n return undefined;\n}\n\n/* ── Credential selection ─────────────────────────────────────────────── */\n\nexport interface CredentialListItem {\n id: string;\n name: string;\n propertyId: string;\n propertyName?: string;\n propertyBaseUrl?: string;\n environmentId?: string | null;\n environmentName?: string | null;\n storageStateS3Key?: string | null;\n isDefault?: boolean;\n isEphemeral?: boolean;\n}\n\n/**\n * Fetch org credentials and let the user pick one interactively.\n *\n * @param credentialArg - Credential name or ID to auto-select, or undefined for interactive\n * @returns The selected credential, or null if none found / invalid selection\n */\nexport async function selectCredential(\n apiUrl: string,\n token: string,\n credentialArg?: string\n): Promise<CredentialListItem | null> {\n const credentials = await fetchList<CredentialListItem>(\n apiUrl,\n token,\n '/org/credentials',\n 'items'\n );\n\n if (credentials.length === 0) {\n console.error('No credentials found. Create one first in the web app.');\n return null;\n }\n\n if (credentialArg) {\n const match = credentials.find(\n (c) => c.id === credentialArg || c.name.toLowerCase() === credentialArg.toLowerCase()\n );\n if (!match) {\n console.error(`Credential \"${credentialArg}\" not found.`);\n console.error('Available credentials:');\n for (const c of credentials) {\n const env = c.environmentName ? ` — ${c.environmentName}` : '';\n const baseUrl = c.propertyBaseUrl ? ` — ${c.propertyBaseUrl}` : '';\n console.error(` - ${c.name} [${c.propertyName ?? c.propertyId}${env}${baseUrl}]`);\n }\n return null;\n }\n return match;\n }\n\n // Interactive selection\n console.log('\\nSelect a credential:\\n');\n const labels = credentials.map((c) => {\n const property = c.propertyName ?? c.propertyId;\n const env = c.environmentName ? ` — ${c.environmentName}` : '';\n const baseUrl = c.propertyBaseUrl ? ` — ${c.propertyBaseUrl}` : '';\n return `${c.name} [${property}${env}${baseUrl}]`;\n });\n\n const readline = await import('node:readline');\n const rl = readline.createInterface({ input: process.stdin, output: process.stdout });\n const answer = await new Promise<string>((resolve) => {\n for (let i = 0; i < labels.length; i++) {\n console.log(` ${i + 1}. ${labels[i]}`);\n }\n rl.question('\\nEnter number: ', (ans) => {\n rl.close();\n resolve(ans.trim());\n });\n });\n\n const idx = parseInt(answer, 10) - 1;\n if (isNaN(idx) || idx < 0 || idx >= credentials.length) {\n console.error('Invalid selection.');\n return null;\n }\n\n return credentials[idx];\n}\n\nexport type CredentialDetail = {\n id: string;\n name: string;\n authProvider?: string;\n /** Resolved login start URL (environment URL + relative login path) */\n loginUrl?: string | null;\n loginInstructions?: string | null;\n sessionVerificationGuidance?: string | null;\n data?: {\n username?: string;\n password?: string;\n email?: string;\n user?: string;\n mfaSecret?: string;\n apiKey?: string;\n customHeaders?: Record<string, string>;\n [key: string]: unknown;\n } | null;\n};\n\nexport async function fetchCredentialDetail(\n apiUrl: string,\n token: string,\n propertyId: string,\n credId: string\n): Promise<CredentialDetail | null> {\n const result = await apiRequest<{ ok: boolean; credential?: CredentialDetail; error?: string }>(\n apiUrl,\n token,\n 'GET',\n `/org/properties/${propertyId}/credentials/${credId}`\n );\n if (!result.ok || !result.credential) return null;\n return result.credential;\n}\n\n/**\n * Non-interactive credential lookup by id or case-insensitive name, narrowed by\n * application (id or case-insensitive name) when several match. Throws on none or ambiguous.\n */\nexport function findCredential(\n list: CredentialListItem[],\n ref: string,\n application?: string\n): CredentialListItem {\n const lower = ref.toLowerCase();\n let matches = list.filter((c) => c.id === ref || c.name.toLowerCase() === lower);\n if (matches.length === 0) throw new Error(`Credential \"${ref}\" not found.`);\n if (matches.length === 1) return matches[0]!;\n if (!application) {\n const options = matches.map((c) => ` - ${c.id} (${c.propertyName ?? c.propertyId})`);\n throw new Error(\n [`Multiple credentials match \"${ref}\". Pass --application to disambiguate.`, ...options].join(\n '\\n'\n )\n );\n }\n const app = application.toLowerCase();\n matches = matches.filter((c) => c.propertyId === application || c.propertyName?.toLowerCase() === app);\n if (matches.length === 0) {\n throw new Error(`Credential \"${ref}\" found, but none match application \"${application}\".`);\n }\n if (matches.length > 1) {\n throw new Error(`Multiple credentials match \"${ref}\" on application \"${application}\".`);\n }\n return matches[0]!;\n}\n\n/** The login identifier of a credential: username, else email, else user. */\nexport function credentialUsername(data: NonNullable<CredentialDetail['data']>): string | undefined {\n return data.username ?? data.email ?? data.user;\n}\n\n/**\n * Resolve a credential name or id to its UUID.\n *\n * The server runs `extractUuid` on `credentialId`, so a bare name is rejected\n * with an opaque invalid-id error. Resolving here is what makes\n * `--credential \"Superadmin User\"` mean what it looks like it means.\n */\nexport async function resolveCredentialId(\n arg: string,\n propertyId: string | undefined,\n apiUrl: string,\n token: string\n): Promise<string> {\n const all = await fetchList<CredentialListItem>(apiUrl, token, '/org/credentials', 'items');\n\n const byId = all.find((c) => c.id === arg);\n if (byId) return byId.id;\n\n // Narrow to the target's application when one is known. The same credential\n // name routinely exists on several applications, and a chat target's\n // credential should belong to the application it is scoped to anyway.\n const credentials = propertyId ? all.filter((c) => c.propertyId === propertyId) : all;\n\n const byName = credentials.filter((c) => c.name.toLowerCase() === arg.toLowerCase());\n if (byName.length === 1) return byName[0]!.id;\n if (byName.length > 1) {\n console.error(\n `Error: Multiple credentials match \"${arg}\"` +\n (propertyId ? ' on this application.' : '. Pass --application to narrow, or use the id.')\n );\n for (const c of byName) console.error(` - ${c.name} (${c.id}) env=${c.environmentName ?? ''}`);\n process.exit(1);\n }\n\n console.error(`Error: Credential \"${arg}\" not found.`);\n if (credentials.length > 0) {\n console.error('Available credentials:');\n for (const c of credentials) console.error(` - ${c.name} (${c.id})`);\n }\n process.exit(1);\n}\n\n/* ── Property selection ──────────────────────────────────────────────── */\n\ninterface PropertyListItem {\n id: string;\n name: string;\n baseUrl: string;\n}\n\nexport async function fetchProperties(apiUrl: string, token: string): Promise<PropertyListItem[]> {\n return fetchList<PropertyListItem>(apiUrl, token, '/org/properties', 'items');\n}\n\n/**\n * Read the application/property selector flag. `--application` is the user-facing\n * name; `--property` is kept as a backwards-compat alias (read second). Internally\n * this is still a \"property\" — the rename is cosmetic.\n */\nexport function getApplicationArg(argv: string[]): string | undefined {\n return getArgValue(argv, '--application') ?? getArgValue(argv, '--property');\n}\n\n/**\n * Return the first positional argument, excluding values consumed by the given\n * `--flag value` pairs.\n *\n * The obvious `argv.find(a => !a.startsWith('-'))` returns the FIRST FLAG VALUE\n * whenever a flag precedes the positional (`--application Acme my-target`),\n * which is the normal shape for every application-scoped command.\n */\nexport function getPositionalArg(\n argv: string[],\n flagsWithValues: string[]\n): string | undefined {\n return getPositionalArgs(argv, flagsWithValues).find((a) => !a.startsWith('-'));\n}\n\n/**\n * Every word that is not a `--flag` or the value of one of the given\n * `--flag value` pairs, in order (a `--flag=value` is one `--` token, so it is\n * skipped whole). Single-dash words are kept, because they can be free text\n * (`-5% latency`). Use this, not `argv.filter(a => !a.startsWith('--'))`, which\n * drops each flag but keeps its value, wherever positional words are joined\n * into free text such as a comment.\n */\nexport function getPositionalArgs(argv: string[], flagsWithValues: string[]): string[] {\n const out: string[] = [];\n for (let i = 0; i < argv.length; i++) {\n const a = argv[i]!;\n if (flagsWithValues.includes(a)) {\n i++;\n continue;\n }\n if (!a.startsWith('--')) out.push(a);\n }\n return out;\n}\n\n/** Global `--flag value` pairs a `guide` subcommand may be given (every command accepts them). */\nconst GUIDE_VALUE_FLAGS = ['--env', '--api-url', '--token', '--org'];\n\n/**\n * The section slug of a `<command> guide [section]` invocation. Skips global\n * flag values, so `guide --env production` prints the whole guide instead of\n * failing on an unknown section named \"production\".\n */\nexport function getGuideSectionArg(argv: string[]): string | undefined {\n return getPositionalArg(argv, GUIDE_VALUE_FLAGS);\n}\n\n/**\n * Read the application/property UUID flag. `--application-id` is the user-facing\n * name; `--property-id` is kept as a backwards-compat alias.\n */\nexport function getApplicationIdArg(argv: string[]): string | undefined {\n return getArgValue(argv, '--application-id') ?? getArgValue(argv, '--property-id');\n}\n\nexport async function selectProperty(\n apiUrl: string,\n token: string,\n hint?: string\n): Promise<PropertyListItem | null> {\n const properties = await fetchProperties(apiUrl, token);\n\n if (properties.length === 0) {\n console.error('No applications found. Create one first in the web app.');\n return null;\n }\n\n if (hint) {\n const match = properties.find(\n (p) => p.id === hint || p.name.toLowerCase() === hint.toLowerCase()\n );\n if (!match) {\n console.error(`Application \"${hint}\" not found.`);\n console.error('Available applications:');\n for (const p of properties) {\n console.error(` - ${p.name} (${p.baseUrl})`);\n }\n return null;\n }\n return match;\n }\n\n if (properties.length === 1) {\n // stderr: stdout may be carrying `--json` output.\n console.error(`Auto-selected application: ${properties[0].name}`);\n return properties[0];\n }\n\n if (!process.stdin.isTTY) {\n console.error('Multiple applications and no --application given. Pass one of:');\n for (const p of properties) {\n console.error(` --application \"${p.name}\" (${p.baseUrl})`);\n }\n return null;\n }\n\n return promptChoice(\n 'Select an application:',\n properties.map((p) => ({ label: `${p.name} — ${p.baseUrl}`, value: p }))\n );\n}\n\n/* ── Interactive prompt helpers ──────────────────────────────────────── */\n\n/**\n * Neither prompt waits on a stdin that is not a terminal: an agent or a script\n * piping the CLI would otherwise hang forever on a question nobody can answer.\n */\nfunction failNonInteractive(question: string): never {\n console.error(`Cannot prompt \"${question}\": stdin is not a terminal. Pass the value explicitly.`);\n process.exit(1);\n}\n\nexport async function promptInput(question: string, defaultValue?: string): Promise<string> {\n if (!process.stdin.isTTY) {\n if (defaultValue !== undefined) return defaultValue;\n failNonInteractive(question);\n }\n const readline = await import('node:readline');\n const rl = readline.createInterface({ input: process.stdin, output: process.stdout });\n const prompt = defaultValue ? `${question} [${defaultValue}]: ` : `${question}: `;\n const answer = await new Promise<string>((resolve) => {\n rl.question(prompt, (ans) => {\n rl.close();\n resolve(ans.trim());\n });\n });\n return answer || defaultValue || '';\n}\n\nexport async function promptChoice<T>(\n question: string,\n options: Array<{ label: string; value: T }>\n): Promise<T | null> {\n if (!process.stdin.isTTY) {\n for (const option of options) console.error(` - ${option.label}`);\n failNonInteractive(question);\n }\n console.log(`\\n${question}\\n`);\n for (let i = 0; i < options.length; i++) {\n console.log(` ${i + 1}. ${options[i].label}`);\n }\n const readline = await import('node:readline');\n const rl = readline.createInterface({ input: process.stdin, output: process.stdout });\n const answer = await new Promise<string>((resolve) => {\n rl.question('\\nEnter number: ', (ans) => {\n rl.close();\n resolve(ans.trim());\n });\n });\n\n const idx = parseInt(answer, 10) - 1;\n if (isNaN(idx) || idx < 0 || idx >= options.length) {\n console.error('Invalid selection.');\n return null;\n }\n return options[idx].value;\n}\n\nexport async function uploadStorageState(opts: {\n apiUrl: string;\n token: string;\n propertyId: string;\n credentialId: string;\n storageState: unknown;\n}): Promise<{ ok: boolean; s3Key?: string; error?: string }> {\n const result = await apiFetch(\n `${opts.apiUrl}/org/properties/${opts.propertyId}/credentials/${opts.credentialId}/storage-state/upload`,\n {\n method: 'PUT',\n headers: {\n Authorization: `Bearer ${opts.token}`,\n 'Content-Type': 'application/json',\n },\n token: opts.token,\n body: JSON.stringify({ storageState: opts.storageState }),\n }\n );\n\n if (!result.ok) exitOnFailure(result.failure);\n\n return (await result.response.json()) as { ok: boolean; s3Key?: string; error?: string };\n}\n\n/* ── Repeatable-flag collection ──────────────────────────────────────── */\n\n/**\n * Collect every value for a repeatable flag (`getArgValue` only returns the\n * first). Accepts both `--key value` and `--key=value` forms.\n *\n * A `--key` with no usable value — missing, looking like the next flag (a\n * bare `-`-leading token), or blank after trimming — exits 1 naming the flag\n * rather than being silently dropped: `--layer A --layer` must not quietly\n * become one layer instead of two, and `--layer \"\"` must not push an empty\n * layer. The single validating implementation for the whole CLI — previously\n * duplicated (leniently, with the dangling-value bug) in `issue-create.ts`,\n * `automation-shared.ts`, and `sandbox.ts`.\n */\nexport function getArgValues(argv: string[], key: string): string[] {\n const out: string[] = [];\n const eqPrefix = `${key}=`;\n for (let i = 0; i < argv.length; i++) {\n const arg = argv[i];\n if (arg === key) {\n const value = argv[i + 1];\n if (value === undefined || value.startsWith('-') || value.trim().length === 0) {\n console.error(`Error: ${key} requires a value.`);\n process.exit(1);\n }\n out.push(value);\n } else if (arg?.startsWith(eqPrefix)) {\n const value = arg.slice(eqPrefix.length);\n if (value.trim().length === 0) {\n console.error(`Error: ${key} requires a value.`);\n process.exit(1);\n }\n out.push(value);\n }\n }\n return out;\n}\n\n/**\n * `getArgValue` (auth.ts) returns whatever token follows the flag, including\n * another flag — `--name --failure-mode continue` would create a suite\n * literally named \"--failure-mode\", and `!body.name.trim()` never catches it\n * because the string isn't blank. Same validation `getArgValues` applies to\n * repeatable flags, scoped to these three scalar write flags rather than\n * changing `getArgValue` itself, which the whole CLI depends on staying\n * permissive.\n *\n * Checks the `--key=value` form first, returning its suffix verbatim — like\n * the hoisted `getArgValues`, that branch skips the leading-`-` check, so\n * `--description=\"- covers the checkout path\"` is the escape hatch for a\n * value that legitimately starts with `-` (the bare `--key value` form\n * rejects a leading `-` to catch a swallowed flag, which otherwise makes such\n * a value unrepresentable).\n *\n * @param opts.allowEmpty Accept a blank value (after trimming) instead of\n * erroring. Use only when blank has domain meaning, such as clearing a\n * description or sending an empty directory-area list.\n */\nexport function getScalarArgValue(\n argv: string[],\n key: string,\n opts?: { allowEmpty: boolean }\n): string | undefined {\n const eqPrefix = `${key}=`;\n const eqToken = argv.find((a) => a.startsWith(eqPrefix));\n if (eqToken !== undefined) {\n const value = eqToken.slice(eqPrefix.length);\n if (value.trim().length === 0 && !opts?.allowEmpty) {\n console.error(`Error: ${key} requires a value.`);\n process.exit(1);\n }\n return value;\n }\n\n const value = getArgValue(argv, key);\n if (value === undefined) return undefined;\n const blank = value.trim().length === 0;\n if (value.startsWith('-') || (blank && !opts?.allowEmpty)) {\n console.error(`Error: ${key} requires a value.`);\n process.exit(1);\n }\n return value;\n}\n\n/* ── Test suite selection ────────────────────────────────────────────── */\n\n/** One suite as returned by `GET /test-suites` — enough to resolve `<name|id>`. */\nexport type SuiteRef = {\n id: string;\n name: string;\n};\n\n/**\n * Resolve a `--suite <name|id>` (or bare `<id|name>` positional) argument\n * against `GET /test-suites`. An exact id match wins; otherwise falls back to\n * a case-insensitive name match. Lists the available suites on a miss.\n *\n * Shared by `test-suite`'s create/update/delete/members/set-members verbs and\n * `remote-test`'s `--suite` run flag, so suite name resolution has exactly one\n * implementation.\n */\nexport async function resolveTestSuite(\n arg: string,\n apiUrl: string,\n token: string\n): Promise<SuiteRef> {\n const res = await apiRequest<{ ok: boolean; error?: string; data?: SuiteRef[] }>(\n apiUrl,\n token,\n 'GET',\n '/test-suites'\n );\n if (!res.ok) {\n console.error(`Error: ${res.error ?? 'Failed to list test suites'}`);\n process.exit(1);\n }\n const suites = res.data ?? [];\n const match =\n suites.find((s) => s.id === arg) ?? suites.find((s) => s.name.toLowerCase() === arg.toLowerCase());\n if (!match) {\n // `arg` echoes the user's own --suite entry and `s.name` is server data —\n // write straight to stderr rather than through console.error, which the\n // `cofactor` binary brand-rewrites (a suite named \"Canary Smoke\" would\n // print as \"Cofactor Smoke\").\n process.stderr.write(`Error: Test suite \"${arg}\" not found.\\n`);\n if (suites.length > 0) {\n console.error('Available suites:');\n for (const s of suites) process.stderr.write(` - ${s.name} (${s.id})\\n`);\n }\n process.exit(1);\n }\n return match;\n}\n\nexport async function fetchList<T>(\n apiUrl: string,\n token: string,\n path: string,\n listKey: string\n): Promise<T[]> {\n const result = await apiFetch(`${apiUrl}${path}`, {\n headers: { Authorization: `Bearer ${token}` },\n token,\n });\n\n if (!result.ok) exitOnFailure(result.failure);\n\n const json = (await result.response.json()) as Record<string, unknown>;\n\n if (!json.ok) {\n console.error(`Error: ${json.error}`);\n process.exit(1);\n }\n\n return (json[listKey] as T[]) ?? [];\n}\n"],"mappings":"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","names":["path","value"]}