checkly 9.5.0 → 10.0.0-rc.1

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 (372) hide show
  1. package/dist/ai-context/context.js +1 -0
  2. package/dist/ai-context/context.js.map +1 -1
  3. package/dist/ai-context/onboarding-boilerplate/checkly-config-template.ts +2 -0
  4. package/dist/ai-context/public-skills/checkly/SKILL.md +18 -5
  5. package/dist/ai-context/skills-command/references/communicate.md +6 -3
  6. package/dist/ai-context/skills-command/references/configure-api-checks.md +1 -9
  7. package/dist/ai-context/skills-command/references/configure-browser-checks.md +1 -8
  8. package/dist/ai-context/skills-command/references/configure-check-groups.md +1 -0
  9. package/dist/ai-context/skills-command/references/configure-dns-monitors.md +1 -9
  10. package/dist/ai-context/skills-command/references/configure-grpc-monitors.md +24 -0
  11. package/dist/ai-context/skills-command/references/configure-heartbeat-monitors.md +1 -9
  12. package/dist/ai-context/skills-command/references/configure-icmp-monitors.md +1 -9
  13. package/dist/ai-context/skills-command/references/configure-multistep-checks.md +1 -9
  14. package/dist/ai-context/skills-command/references/configure-playwright-checks.md +1 -1
  15. package/dist/ai-context/skills-command/references/configure-supporting-constructs.md +6 -2
  16. package/dist/ai-context/skills-command/references/configure-tcp-monitors.md +1 -10
  17. package/dist/ai-context/skills-command/references/configure-url-monitors.md +1 -10
  18. package/dist/ai-context/skills-command/references/configure.md +16 -5
  19. package/dist/ai-context/skills-command/references/initialize.md +11 -3
  20. package/dist/ai-context/skills-command/references/investigate-checks.md +1 -1
  21. package/dist/auth/api-key.d.ts +24 -0
  22. package/dist/auth/api-key.js +104 -0
  23. package/dist/auth/api-key.js.map +1 -0
  24. package/dist/auth/device-flow.d.ts +57 -0
  25. package/dist/auth/device-flow.js +221 -0
  26. package/dist/auth/device-flow.js.map +1 -0
  27. package/dist/auth/index.d.ts +6 -6
  28. package/dist/auth/index.js +64 -64
  29. package/dist/auth/index.js.map +1 -1
  30. package/dist/commands/api.js +43 -10
  31. package/dist/commands/api.js.map +1 -1
  32. package/dist/commands/authCommand.d.ts +2 -0
  33. package/dist/commands/authCommand.js +77 -7
  34. package/dist/commands/authCommand.js.map +1 -1
  35. package/dist/commands/debug/parse-project.d.ts +1 -0
  36. package/dist/commands/debug/parse-project.js +5 -2
  37. package/dist/commands/debug/parse-project.js.map +1 -1
  38. package/dist/commands/deploy.d.ts +7 -1
  39. package/dist/commands/deploy.js +627 -271
  40. package/dist/commands/deploy.js.map +1 -1
  41. package/dist/commands/import/plan.js +18 -4
  42. package/dist/commands/import/plan.js.map +1 -1
  43. package/dist/commands/init.js +29 -13
  44. package/dist/commands/init.js.map +1 -1
  45. package/dist/commands/login.d.ts +18 -2
  46. package/dist/commands/login.js +681 -48
  47. package/dist/commands/login.js.map +1 -1
  48. package/dist/commands/logout.js +7 -1
  49. package/dist/commands/logout.js.map +1 -1
  50. package/dist/commands/pw-test.js +3 -2
  51. package/dist/commands/pw-test.js.map +1 -1
  52. package/dist/commands/switch.js +23 -10
  53. package/dist/commands/switch.js.map +1 -1
  54. package/dist/commands/test.js +16 -17
  55. package/dist/commands/test.js.map +1 -1
  56. package/dist/commands/trigger.js +4 -3
  57. package/dist/commands/trigger.js.map +1 -1
  58. package/dist/commands/validate.js +1 -0
  59. package/dist/commands/validate.js.map +1 -1
  60. package/dist/constants.d.ts +1 -2
  61. package/dist/constants.js +0 -1
  62. package/dist/constants.js.map +1 -1
  63. package/dist/constructs/alert-channel-codegen.d.ts +6 -1
  64. package/dist/constructs/alert-channel-codegen.js +12 -6
  65. package/dist/constructs/alert-channel-codegen.js.map +1 -1
  66. package/dist/constructs/alert-escalation-policy-codegen.d.ts +7 -0
  67. package/dist/constructs/alert-escalation-policy-codegen.js +25 -8
  68. package/dist/constructs/alert-escalation-policy-codegen.js.map +1 -1
  69. package/dist/constructs/api-assertion.d.ts +0 -2
  70. package/dist/constructs/api-assertion.js +0 -4
  71. package/dist/constructs/api-assertion.js.map +1 -1
  72. package/dist/constructs/api-check-codegen.js +1 -1
  73. package/dist/constructs/api-check-codegen.js.map +1 -1
  74. package/dist/constructs/api-check.d.ts +4 -22
  75. package/dist/constructs/api-check.js +15 -17
  76. package/dist/constructs/api-check.js.map +1 -1
  77. package/dist/constructs/api-request-codegen.js +10 -8
  78. package/dist/constructs/api-request-codegen.js.map +1 -1
  79. package/dist/constructs/browser-check-bundle.d.ts +19 -13
  80. package/dist/constructs/browser-check-bundle.js +5 -1
  81. package/dist/constructs/browser-check-bundle.js.map +1 -1
  82. package/dist/constructs/browser-check.d.ts +1 -4
  83. package/dist/constructs/browser-check.js +1 -1
  84. package/dist/constructs/browser-check.js.map +1 -1
  85. package/dist/constructs/check-codegen.d.ts +39 -14
  86. package/dist/constructs/check-codegen.js +101 -23
  87. package/dist/constructs/check-codegen.js.map +1 -1
  88. package/dist/constructs/check-group-codegen.js +23 -9
  89. package/dist/constructs/check-group-codegen.js.map +1 -1
  90. package/dist/constructs/check-group-v1.d.ts +19 -14
  91. package/dist/constructs/check-group-v1.js +39 -14
  92. package/dist/constructs/check-group-v1.js.map +1 -1
  93. package/dist/constructs/check-group-v2.d.ts +0 -20
  94. package/dist/constructs/check-group-v2.js +0 -4
  95. package/dist/constructs/check-group-v2.js.map +1 -1
  96. package/dist/constructs/check.d.ts +45 -32
  97. package/dist/constructs/check.js +78 -17
  98. package/dist/constructs/check.js.map +1 -1
  99. package/dist/constructs/construct-codegen.d.ts +1 -2
  100. package/dist/constructs/construct-diagnostics.d.ts +3 -0
  101. package/dist/constructs/construct-diagnostics.js +11 -0
  102. package/dist/constructs/construct-diagnostics.js.map +1 -1
  103. package/dist/constructs/construct.d.ts +25 -4
  104. package/dist/constructs/construct.js +41 -5
  105. package/dist/constructs/construct.js.map +1 -1
  106. package/dist/constructs/dns-request-codegen.js +1 -1
  107. package/dist/constructs/dns-request-codegen.js.map +1 -1
  108. package/dist/constructs/email-alert-channel-codegen.js +1 -1
  109. package/dist/constructs/email-alert-channel-codegen.js.map +1 -1
  110. package/dist/constructs/frequency-codegen.js +10 -2
  111. package/dist/constructs/frequency-codegen.js.map +1 -1
  112. package/dist/constructs/frequency.d.ts +8 -3
  113. package/dist/constructs/frequency.js +7 -2
  114. package/dist/constructs/frequency.js.map +1 -1
  115. package/dist/constructs/grpc-monitor.js +19 -0
  116. package/dist/constructs/grpc-monitor.js.map +1 -1
  117. package/dist/constructs/grpc-request-codegen.js +13 -4
  118. package/dist/constructs/grpc-request-codegen.js.map +1 -1
  119. package/dist/constructs/grpc-request.d.ts +23 -0
  120. package/dist/constructs/heartbeat-monitor.d.ts +2 -1
  121. package/dist/constructs/heartbeat-monitor.js +2 -2
  122. package/dist/constructs/heartbeat-monitor.js.map +1 -1
  123. package/dist/constructs/icmp-request-codegen.js +1 -1
  124. package/dist/constructs/icmp-request-codegen.js.map +1 -1
  125. package/dist/constructs/incidentio-alert-channel-codegen.d.ts +1 -1
  126. package/dist/constructs/incidentio-alert-channel-codegen.js +14 -8
  127. package/dist/constructs/incidentio-alert-channel-codegen.js.map +1 -1
  128. package/dist/constructs/index.d.ts +1 -1
  129. package/dist/constructs/index.js +1 -1
  130. package/dist/constructs/index.js.map +1 -1
  131. package/dist/constructs/internal/assertion-validation.js +1 -2
  132. package/dist/constructs/internal/assertion-validation.js.map +1 -1
  133. package/dist/constructs/internal/codegen/context.d.ts +73 -6
  134. package/dist/constructs/internal/codegen/context.js +78 -6
  135. package/dist/constructs/internal/codegen/context.js.map +1 -1
  136. package/dist/constructs/internal/codegen/index.d.ts +3 -1
  137. package/dist/constructs/internal/codegen/index.js +2 -1
  138. package/dist/constructs/internal/codegen/index.js.map +1 -1
  139. package/dist/constructs/internal/codegen/render.d.ts +68 -0
  140. package/dist/constructs/internal/codegen/render.js +75 -0
  141. package/dist/constructs/internal/codegen/render.js.map +1 -0
  142. package/dist/constructs/internal/common-diagnostics.d.ts +1 -2
  143. package/dist/constructs/internal/common-diagnostics.js +6 -14
  144. package/dist/constructs/internal/common-diagnostics.js.map +1 -1
  145. package/dist/constructs/internal/declaring-file.d.ts +39 -0
  146. package/dist/constructs/internal/declaring-file.js +139 -0
  147. package/dist/constructs/internal/declaring-file.js.map +1 -0
  148. package/dist/constructs/internal/retry-strategy-defaults.d.ts +13 -0
  149. package/dist/constructs/internal/retry-strategy-defaults.js +14 -0
  150. package/dist/constructs/internal/retry-strategy-defaults.js.map +1 -0
  151. package/dist/constructs/key-value-pair-codegen.d.ts +6 -1
  152. package/dist/constructs/key-value-pair-codegen.js +24 -3
  153. package/dist/constructs/key-value-pair-codegen.js.map +1 -1
  154. package/dist/constructs/maintenance-window-codegen.d.ts +7 -0
  155. package/dist/constructs/maintenance-window-codegen.js +36 -19
  156. package/dist/constructs/maintenance-window-codegen.js.map +1 -1
  157. package/dist/constructs/maintenance-window.d.ts +35 -3
  158. package/dist/constructs/maintenance-window.js +35 -1
  159. package/dist/constructs/maintenance-window.js.map +1 -1
  160. package/dist/constructs/monitor.d.ts +1 -1
  161. package/dist/constructs/msteams-alert-channel-codegen.d.ts +1 -1
  162. package/dist/constructs/msteams-alert-channel-codegen.js +8 -18
  163. package/dist/constructs/msteams-alert-channel-codegen.js.map +1 -1
  164. package/dist/constructs/opsgenie-alert-channel-codegen.js +1 -1
  165. package/dist/constructs/opsgenie-alert-channel-codegen.js.map +1 -1
  166. package/dist/constructs/pagerduty-alert-channel-codegen.js +1 -1
  167. package/dist/constructs/pagerduty-alert-channel-codegen.js.map +1 -1
  168. package/dist/constructs/phone-call-alert-channel-codegen.js +1 -1
  169. package/dist/constructs/phone-call-alert-channel-codegen.js.map +1 -1
  170. package/dist/constructs/playwright-check-bundle.d.ts +7 -1
  171. package/dist/constructs/playwright-check-bundle.js +3 -0
  172. package/dist/constructs/playwright-check-bundle.js.map +1 -1
  173. package/dist/constructs/playwright-check-codegen.d.ts +85 -0
  174. package/dist/constructs/playwright-check-codegen.js +212 -0
  175. package/dist/constructs/playwright-check-codegen.js.map +1 -0
  176. package/dist/constructs/playwright-check.d.ts +14 -24
  177. package/dist/constructs/playwright-check.js +16 -52
  178. package/dist/constructs/playwright-check.js.map +1 -1
  179. package/dist/constructs/retry-strategy-codegen.d.ts +7 -1
  180. package/dist/constructs/retry-strategy-codegen.js +41 -32
  181. package/dist/constructs/retry-strategy-codegen.js.map +1 -1
  182. package/dist/constructs/retry-strategy.d.ts +0 -4
  183. package/dist/constructs/retry-strategy.js +5 -8
  184. package/dist/constructs/retry-strategy.js.map +1 -1
  185. package/dist/constructs/session.d.ts +26 -1
  186. package/dist/constructs/session.js +33 -2
  187. package/dist/constructs/session.js.map +1 -1
  188. package/dist/constructs/slack-alert-channel-codegen.js +1 -1
  189. package/dist/constructs/slack-alert-channel-codegen.js.map +1 -1
  190. package/dist/constructs/slack-app-alert-channel-codegen.js +1 -1
  191. package/dist/constructs/slack-app-alert-channel-codegen.js.map +1 -1
  192. package/dist/constructs/sms-alert-channel-codegen.js +1 -1
  193. package/dist/constructs/sms-alert-channel-codegen.js.map +1 -1
  194. package/dist/constructs/ssl-request-codegen.js +4 -3
  195. package/dist/constructs/ssl-request-codegen.js.map +1 -1
  196. package/dist/constructs/status-page-v3-automation-rule-codegen.js +5 -4
  197. package/dist/constructs/status-page-v3-automation-rule-codegen.js.map +1 -1
  198. package/dist/constructs/status-page-v3-codegen.js +3 -2
  199. package/dist/constructs/status-page-v3-codegen.js.map +1 -1
  200. package/dist/constructs/status-page-v3-component-codegen.js +8 -7
  201. package/dist/constructs/status-page-v3-component-codegen.js.map +1 -1
  202. package/dist/constructs/tcp-monitor-codegen.js +3 -2
  203. package/dist/constructs/tcp-monitor-codegen.js.map +1 -1
  204. package/dist/constructs/tcp-monitor.d.ts +4 -3
  205. package/dist/constructs/tcp-monitor.js +3 -2
  206. package/dist/constructs/tcp-monitor.js.map +1 -1
  207. package/dist/constructs/telegram-alert-channel-codegen.d.ts +1 -1
  208. package/dist/constructs/telegram-alert-channel-codegen.js +14 -8
  209. package/dist/constructs/telegram-alert-channel-codegen.js.map +1 -1
  210. package/dist/constructs/time-zone.d.ts +24 -0
  211. package/dist/constructs/time-zone.js +455 -0
  212. package/dist/constructs/time-zone.js.map +1 -0
  213. package/dist/constructs/traceroute-request-codegen.js +2 -2
  214. package/dist/constructs/traceroute-request-codegen.js.map +1 -1
  215. package/dist/constructs/url-monitor-codegen.js +1 -1
  216. package/dist/constructs/url-monitor-codegen.js.map +1 -1
  217. package/dist/constructs/url-monitor.d.ts +3 -2
  218. package/dist/constructs/url-monitor.js +3 -0
  219. package/dist/constructs/url-monitor.js.map +1 -1
  220. package/dist/constructs/url-request-codegen.js +4 -3
  221. package/dist/constructs/url-request-codegen.js.map +1 -1
  222. package/dist/constructs/webhook-alert-channel-codegen.js +3 -3
  223. package/dist/constructs/webhook-alert-channel-codegen.js.map +1 -1
  224. package/dist/formatters/check-result-detail.js +0 -185
  225. package/dist/formatters/check-result-detail.js.map +1 -1
  226. package/dist/helpers/activate-account.d.ts +14 -0
  227. package/dist/helpers/activate-account.js +25 -0
  228. package/dist/helpers/activate-account.js.map +1 -0
  229. package/dist/helpers/cli-mode.d.ts +18 -0
  230. package/dist/helpers/cli-mode.js +41 -2
  231. package/dist/helpers/cli-mode.js.map +1 -1
  232. package/dist/helpers/command-preview.d.ts +44 -0
  233. package/dist/helpers/command-preview.js +29 -9
  234. package/dist/helpers/command-preview.js.map +1 -1
  235. package/dist/helpers/flags.d.ts +10 -0
  236. package/dist/helpers/flags.js +17 -0
  237. package/dist/helpers/flags.js.map +1 -1
  238. package/dist/helpers/onboarding/boilerplate.js +13 -1
  239. package/dist/helpers/onboarding/boilerplate.js.map +1 -1
  240. package/dist/helpers/onboarding/index.d.ts +1 -1
  241. package/dist/helpers/onboarding/index.js +1 -1
  242. package/dist/helpers/onboarding/index.js.map +1 -1
  243. package/dist/helpers/onboarding/messages.d.ts +6 -0
  244. package/dist/helpers/onboarding/messages.js +14 -0
  245. package/dist/helpers/onboarding/messages.js.map +1 -1
  246. package/dist/helpers/onboarding/skill-install.js +2 -2
  247. package/dist/helpers/onboarding/skill-install.js.map +1 -1
  248. package/dist/helpers/wrap.js +1 -1
  249. package/dist/helpers/wrap.js.map +1 -1
  250. package/dist/reporters/abstract-list.js +11 -2
  251. package/dist/reporters/abstract-list.js.map +1 -1
  252. package/dist/reporters/github.js +3 -2
  253. package/dist/reporters/github.js.map +1 -1
  254. package/dist/reporters/util.d.ts +1 -0
  255. package/dist/reporters/util.js +5 -58
  256. package/dist/reporters/util.js.map +1 -1
  257. package/dist/rest/analytics.js +0 -4
  258. package/dist/rest/analytics.js.map +1 -1
  259. package/dist/rest/api.d.ts +8 -1
  260. package/dist/rest/api.js +21 -3
  261. package/dist/rest/api.js.map +1 -1
  262. package/dist/rest/check-results.d.ts +1 -50
  263. package/dist/rest/check-results.js.map +1 -1
  264. package/dist/rest/checkly-storage.d.ts +6 -1
  265. package/dist/rest/checkly-storage.js +13 -2
  266. package/dist/rest/checkly-storage.js.map +1 -1
  267. package/dist/rest/errors.d.ts +13 -0
  268. package/dist/rest/errors.js +5 -1
  269. package/dist/rest/errors.js.map +1 -1
  270. package/dist/rest/projects.d.ts +254 -4
  271. package/dist/rest/projects.js +184 -12
  272. package/dist/rest/projects.js.map +1 -1
  273. package/dist/services/abstract-check-runner.d.ts +11 -0
  274. package/dist/services/abstract-check-runner.js +98 -46
  275. package/dist/services/abstract-check-runner.js.map +1 -1
  276. package/dist/services/check-parser/bundler.d.ts +26 -0
  277. package/dist/services/check-parser/bundler.js +81 -7
  278. package/dist/services/check-parser/bundler.js.map +1 -1
  279. package/dist/services/check-parser/cache-hash.d.ts +1 -4
  280. package/dist/services/check-parser/cache-hash.js.map +1 -1
  281. package/dist/services/checkly-config-codegen.js +1 -4
  282. package/dist/services/checkly-config-codegen.js.map +1 -1
  283. package/dist/services/checkly-config-loader.d.ts +7 -33
  284. package/dist/services/checkly-config-loader.js +50 -47
  285. package/dist/services/checkly-config-loader.js.map +1 -1
  286. package/dist/services/config.d.ts +2 -0
  287. package/dist/services/config.js +14 -0
  288. package/dist/services/config.js.map +1 -1
  289. package/dist/services/content-hash.d.ts +14 -0
  290. package/dist/services/content-hash.js +22 -0
  291. package/dist/services/content-hash.js.map +1 -0
  292. package/dist/services/deploy-diff/diff-lines.d.ts +38 -0
  293. package/dist/services/deploy-diff/diff-lines.js +191 -0
  294. package/dist/services/deploy-diff/diff-lines.js.map +1 -0
  295. package/dist/services/deploy-diff/import-shape.d.ts +171 -0
  296. package/dist/services/deploy-diff/import-shape.js +633 -0
  297. package/dist/services/deploy-diff/import-shape.js.map +1 -0
  298. package/dist/services/deploy-diff/legacy-payload.d.ts +22 -0
  299. package/dist/services/deploy-diff/legacy-payload.js +51 -0
  300. package/dist/services/deploy-diff/legacy-payload.js.map +1 -0
  301. package/dist/services/deploy-diff/plan-summary.d.ts +37 -0
  302. package/dist/services/deploy-diff/plan-summary.js +154 -0
  303. package/dist/services/deploy-diff/plan-summary.js.map +1 -0
  304. package/dist/services/deploy-diff/preview-output.d.ts +61 -0
  305. package/dist/services/deploy-diff/preview-output.js +394 -0
  306. package/dist/services/deploy-diff/preview-output.js.map +1 -0
  307. package/dist/services/deploy-diff/render.d.ts +53 -0
  308. package/dist/services/deploy-diff/render.js +317 -0
  309. package/dist/services/deploy-diff/render.js.map +1 -0
  310. package/dist/services/deploy-diff/shape-changes.d.ts +2 -0
  311. package/dist/services/deploy-diff/shape-changes.js +35 -0
  312. package/dist/services/deploy-diff/shape-changes.js.map +1 -0
  313. package/dist/services/deploy-diff/source-index.d.ts +18 -0
  314. package/dist/services/deploy-diff/source-index.js +48 -0
  315. package/dist/services/deploy-diff/source-index.js.map +1 -0
  316. package/dist/services/deploy-diff/variable-names.d.ts +30 -0
  317. package/dist/services/deploy-diff/variable-names.js +64 -0
  318. package/dist/services/deploy-diff/variable-names.js.map +1 -0
  319. package/dist/services/project-parser.d.ts +5 -0
  320. package/dist/services/project-parser.js +25 -13
  321. package/dist/services/project-parser.js.map +1 -1
  322. package/dist/services/snapshot-service.d.ts +25 -8
  323. package/dist/services/snapshot-service.js +46 -4
  324. package/dist/services/snapshot-service.js.map +1 -1
  325. package/dist/services/test-filters.d.ts +18 -0
  326. package/dist/services/test-filters.js +10 -0
  327. package/dist/services/test-filters.js.map +1 -1
  328. package/dist/services/test-runner.js +7 -3
  329. package/dist/services/test-runner.js.map +1 -1
  330. package/dist/services/test-session-cancel.js +1 -1
  331. package/dist/services/test-session-cancel.js.map +1 -1
  332. package/dist/services/util.d.ts +17 -2
  333. package/dist/services/util.js +118 -11
  334. package/dist/services/util.js.map +1 -1
  335. package/dist/services/write-back/apply-edits.d.ts +29 -0
  336. package/dist/services/write-back/apply-edits.js +255 -0
  337. package/dist/services/write-back/apply-edits.js.map +1 -0
  338. package/dist/services/write-back/helper-edit.d.ts +135 -0
  339. package/dist/services/write-back/helper-edit.js +343 -0
  340. package/dist/services/write-back/helper-edit.js.map +1 -0
  341. package/dist/services/write-back/imports.d.ts +30 -0
  342. package/dist/services/write-back/imports.js +82 -0
  343. package/dist/services/write-back/imports.js.map +1 -0
  344. package/dist/services/write-back/literal-edit.d.ts +183 -0
  345. package/dist/services/write-back/literal-edit.js +359 -0
  346. package/dist/services/write-back/literal-edit.js.map +1 -0
  347. package/dist/services/write-back/output.d.ts +26 -0
  348. package/dist/services/write-back/output.js +110 -0
  349. package/dist/services/write-back/output.js.map +1 -0
  350. package/dist/services/write-back/plan.d.ts +173 -0
  351. package/dist/services/write-back/plan.js +968 -0
  352. package/dist/services/write-back/plan.js.map +1 -0
  353. package/dist/services/write-back/source-file.d.ts +118 -0
  354. package/dist/services/write-back/source-file.js +432 -0
  355. package/dist/services/write-back/source-file.js.map +1 -0
  356. package/dist/sourcegen/index.d.ts +1 -1
  357. package/dist/sourcegen/index.js +1 -1
  358. package/dist/sourcegen/index.js.map +1 -1
  359. package/dist/sourcegen/program.d.ts +19 -1
  360. package/dist/sourcegen/program.js +32 -7
  361. package/dist/sourcegen/program.js.map +1 -1
  362. package/oclif.manifest.json +676 -635
  363. package/package.json +13 -13
  364. package/dist/commands/rules.d.ts +0 -9
  365. package/dist/commands/rules.js +0 -13
  366. package/dist/commands/rules.js.map +0 -1
  367. package/dist/constructs/agentic-check-codegen.d.ts +0 -22
  368. package/dist/constructs/agentic-check-codegen.js +0 -59
  369. package/dist/constructs/agentic-check-codegen.js.map +0 -1
  370. package/dist/constructs/agentic-check.d.ts +0 -237
  371. package/dist/constructs/agentic-check.js +0 -100
  372. package/dist/constructs/agentic-check.js.map +0 -1
@@ -1,11 +1,21 @@
1
+ var _a;
1
2
  import open from 'open';
2
3
  import chalk from 'chalk';
3
- import { BaseCommand } from './baseCommand.js';
4
+ import { Flags } from '@oclif/core';
4
5
  import prompts from 'prompts';
6
+ import { BaseCommand } from './baseCommand.js';
5
7
  import config from '../services/config.js';
6
8
  import * as api from '../rest/api.js';
9
+ import { ApiError, ForbiddenError, MissingResponseError, NotFoundError, ProxyConnectionError, UnauthorizedError, } from '../rest/errors.js';
7
10
  import { AuthContext } from '../auth/index.js';
11
+ import { DeviceFlow, DeviceFlowError, DeviceFlowNotAllowedError, } from '../auth/device-flow.js';
12
+ import { credentialsFromTokens } from '../auth/api-key.js';
13
+ import { isPersonAtTerminal, detectCliMode, isEnvFlagSet } from '../helpers/cli-mode.js';
14
+ import { activateAccount } from '../helpers/activate-account.js';
8
15
  import commonMessages from '../messages/common-messages.js';
16
+ import { detectPackageManager } from '../services/check-parser/package-files/package-manager.js';
17
+ import { getChecklyConfigFile } from '../services/checkly-config-loader.js';
18
+ import { shellQuote } from '../services/shell.js';
9
19
  export const selectAccount = async (accounts, { onCancel }) => {
10
20
  const { selectedAccount } = await prompts({
11
21
  name: 'selectedAccount',
@@ -15,67 +25,645 @@ export const selectAccount = async (accounts, { onCancel }) => {
15
25
  }, { onCancel });
16
26
  return selectedAccount;
17
27
  };
18
- export default class Login extends BaseCommand {
28
+ /** A login failure with the `reason` code agent-mode output reports for it. */
29
+ class LoginError extends Error {
30
+ reason;
31
+ constructor(reason, message) {
32
+ super(message);
33
+ this.reason = reason;
34
+ this.name = 'LoginError';
35
+ }
36
+ }
37
+ /**
38
+ * Classifies a login failure: the device-flow outcomes (`network_error` when
39
+ * the login server cannot be reached), the account problems raised by the
40
+ * login itself, `api_error` for a failed or unreachable Checkly API and
41
+ * `login_failed` for anything else. (`env_credentials` is
42
+ * reported before any of these can happen.)
43
+ */
44
+ function errorReason(error) {
45
+ if (error instanceof LoginError) {
46
+ return error.reason;
47
+ }
48
+ if (error instanceof DeviceFlowError) {
49
+ switch (error.code) {
50
+ case 'access_denied':
51
+ case 'expired_token':
52
+ case 'invalid_response':
53
+ case 'network_error':
54
+ return error.code;
55
+ case 'invalid_grant':
56
+ return 'code_used';
57
+ }
58
+ return 'login_failed';
59
+ }
60
+ // validateAuthentication() rewords rejected stored credentials into a plain
61
+ // Error that keeps the API error as its cause.
62
+ const apiError = error instanceof Error && error.cause instanceof ApiError ? error.cause : error;
63
+ if (apiError instanceof ApiError || apiError instanceof MissingResponseError
64
+ || apiError instanceof ProxyConnectionError) {
65
+ return 'api_error';
66
+ }
67
+ return 'login_failed';
68
+ }
69
+ /**
70
+ * What to do after logging in, in the current directory's package manager:
71
+ * run the checks of a Checkly project, or set one up. Undefined when that
72
+ * cannot be worked out; the hint is never worth failing a login over.
73
+ */
74
+ async function nextStepHint() {
75
+ try {
76
+ const packageManager = await detectPackageManager(process.cwd());
77
+ const command = (subcommand) => packageManager.execCommand(['checkly', subcommand]).unsafeDisplayCommand;
78
+ return await getChecklyConfigFile()
79
+ ? `To run your checks, run \`${command('test')}\`.`
80
+ : `To create checks for a project, run \`${command('init')}\`.`;
81
+ }
82
+ catch {
83
+ return undefined;
84
+ }
85
+ }
86
+ /** `https://auth.checklyhq.com/activate` as `auth.checklyhq.com/activate`. */
87
+ function withoutScheme(url) {
88
+ return url.replace(/^https?:\/\//, '');
89
+ }
90
+ /** What Auth0 says when the user cancels on the activation page. */
91
+ const AUTH0_CANCELLED_MESSAGE = 'User did not confirm their request';
92
+ /**
93
+ * The message of a login failure, in our own words for a login cancelled in
94
+ * the browser. Other denials (e.g. by a tenant rule) keep the login
95
+ * server's message, the only place their reason appears.
96
+ */
97
+ function errorMessage(error) {
98
+ if (error instanceof DeviceFlowError && error.code === 'access_denied'
99
+ && error.message === AUTH0_CANCELLED_MESSAGE) {
100
+ return 'The login was cancelled in the browser.';
101
+ }
102
+ // An OAuth error without a reason of its own: name its code, the only
103
+ // thing that identifies it for support.
104
+ if (error instanceof DeviceFlowError && errorReason(error) === 'login_failed'
105
+ && !error.message.includes(`(${error.code})`)) {
106
+ return `The login server refused the login: ${error.message} (${error.code})`;
107
+ }
108
+ return error?.message || String(error);
109
+ }
110
+ // An agent relays a stored code again only while there is still time to
111
+ // approve it.
112
+ const MIN_RELAY_TIME_MS = 60_000;
113
+ class Login extends BaseCommand {
19
114
  static hidden = false;
20
115
  static idempotent = true;
21
116
  static description = 'Login to your Checkly account or create a new one.';
22
- _checkExistingCredentials = async () => {
117
+ static examples = [
118
+ '$ npx checkly login',
119
+ '$ npx checkly login --account-id <id>',
120
+ ];
121
+ static flags = {
122
+ 'account-id': Flags.string({
123
+ description: 'Use this account instead of asking which one. When already logged in, switch to it '
124
+ + 'without logging in again, like `checkly switch --account-id`.',
125
+ }),
126
+ 'no-browser': Flags.boolean({
127
+ description: 'Only print the login URL and code; do not try to open a browser. '
128
+ + 'Also honoured through CHECKLY_NO_BROWSER=1, e.g. on headless hosts.',
129
+ default: false,
130
+ }),
131
+ };
132
+ #mode = 'interactive';
133
+ #openBrowser = true;
134
+ #inline = false;
135
+ // Why a stored login stopped working, for the agent to pass on with the
136
+ // next step (a new code, account selection or success).
137
+ #notice;
138
+ // Whether the interactive output so far ends with an empty line, so the
139
+ // next block is separated by exactly one.
140
+ #atBlankLine = false;
141
+ // `--account-id` as given, so the command to run after approval keeps it.
142
+ #requestedAccountId;
143
+ async run() {
144
+ const { flags } = await this.parse(_a);
145
+ const ok = await this.login({ accountId: flags['account-id'], openBrowser: !flags['no-browser'] });
146
+ return this.exit(ok ? 0 : 1);
147
+ }
148
+ /**
149
+ * Runs the whole login flow for the detected CLI mode and stores the
150
+ * credentials. Returns false when the flow did not complete in agent mode
151
+ * (the JSON error line has already been printed); throws otherwise.
152
+ * Other commands call this with `inline: true` to log the user in before
153
+ * they run; the login then writes to stderr so the command's own stdout
154
+ * (e.g. `--output json`) stays clean.
155
+ */
156
+ async login(options = {}) {
157
+ this.#mode = detectCliMode();
158
+ this.#inline = options.inline ?? false;
159
+ this.#notice = undefined;
160
+ this.#requestedAccountId = options.accountId;
161
+ this.#openBrowser = (options.openBrowser ?? true) && !isEnvFlagSet(process.env.CHECKLY_NO_BROWSER);
23
162
  if (config.hasEnvVarsConfigured()) {
24
- this.warn(`${commonMessages.envCredentialsConfigured} You must delete them to use \`npx checkly login\`.`);
25
- this.exit(0);
163
+ const message = `${commonMessages.envCredentialsConfigured} You must delete them to use \`npx checkly login\`.`;
164
+ if (this.#mode === 'agent') {
165
+ // Not a success: the variables may be incomplete or wrong, and only
166
+ // the user can remove them.
167
+ const reason = 'env_credentials';
168
+ this.#print(JSON.stringify({ status: 'error', reason, userActionRequired: true, message }));
169
+ return false;
170
+ }
171
+ this.warn(message);
172
+ return true;
26
173
  }
27
- const hasValidCredentials = config.hasValidCredentials();
28
- if (hasValidCredentials) {
29
- const { setNewkey } = await prompts({
30
- name: 'setNewkey',
31
- type: 'confirm',
32
- message: `You are currently logged in to "${config.data.get('accountName')}". Do you want to log out and log in to a different account?`,
33
- });
34
- if (!setNewkey) {
35
- this.exit(0);
174
+ // `--account-id` naming a different account switches to it with the
175
+ // stored key; it is not a request to log in again.
176
+ const switchingAccount = config.hasValidCredentials()
177
+ && Boolean(options.accountId) && options.accountId !== config.getAccountId();
178
+ try {
179
+ // A stored login is only kept as it is while it still works. Dropping an
180
+ // unusable account sends the flow below into the stored-key resume path:
181
+ // account selection with a working key, a new login otherwise.
182
+ if (config.hasValidCredentials() && !switchingAccount) {
183
+ const storedAccount = await this.#checkStoredAccount();
184
+ if (storedAccount.usable && !await this.#wantsToReplaceLogin(storedAccount.name)) {
185
+ return true;
186
+ }
187
+ if (!storedAccount.usable) {
188
+ this.#notice = `Account "${storedAccount.name}" is no longer available with the stored login.`;
189
+ if (this.#mode !== 'agent') {
190
+ this.#print(this.#notice);
191
+ }
192
+ }
193
+ }
194
+ // The stored key is reused when switching accounts, and when a previous
195
+ // login stored it but stopped before an account was chosen (agent mode
196
+ // with several accounts, or a cancelled account prompt).
197
+ const reuseStoredKey = switchingAccount || (Boolean(config.getApiKey()) && !config.getAccountId());
198
+ let userName = reuseStoredKey ? await this.#storedKeyUserName() : undefined;
199
+ const usingStoredKey = userName !== undefined;
200
+ if (userName === undefined) {
201
+ if (this.#mode === 'ci') {
202
+ this.error((reuseStoredKey ? 'The stored login is no longer valid and was removed. ' : '')
203
+ + '`npx checkly login` needs a browser. In CI, set `CHECKLY_API_KEY` and `CHECKLY_ACCOUNT_ID` '
204
+ + 'in the environment instead.', { exit: 1 });
205
+ }
206
+ if (this.#mode === 'interactive' && !isPersonAtTerminal()) {
207
+ this.error((reuseStoredKey ? 'The stored login is no longer valid and was removed. ' : '')
208
+ + '`npx checkly login` needs a terminal to show the login code and wait for it. '
209
+ + 'Run it in a terminal, or set `CHECKLY_API_KEY` and `CHECKLY_ACCOUNT_ID` in the environment. '
210
+ + 'If you are at a terminal that is not detected as one, set `CHECKLY_CLI_MODE=interactive`; '
211
+ + 'AI agents set `CHECKLY_CLI_MODE=agent` instead.', { exit: 1 });
212
+ }
213
+ if (reuseStoredKey && this.#mode === 'interactive') {
214
+ this.#print('The stored login is no longer valid. Logging in again.');
215
+ }
216
+ const result = await this.#authenticate();
217
+ if (result === 'pending') {
218
+ return false;
219
+ }
220
+ if (result === 'stored') {
221
+ userName = await this.#storedKeyUserName();
222
+ if (userName === undefined) {
223
+ throw new LoginError('code_used', 'The login code was already used. Please run the login again.');
224
+ }
225
+ }
226
+ else {
227
+ this.#storeNewKey(result.key);
228
+ userName = result.name;
229
+ }
230
+ }
231
+ else if (this.#mode === 'interactive' && !switchingAccount && !this.#notice) {
232
+ this.#print(`Continuing the login as ${chalk.bold(userName)}. `
233
+ + 'Run `npx checkly logout` first to log in as someone else.');
234
+ }
235
+ const { data: accounts } = await api.accounts.getAll();
236
+ const accountSummaries = accounts.map(({ id, name }) => ({ id, name }));
237
+ const account = await this.#pickAccount(accounts, options.accountId, usingStoredKey);
238
+ if (!account) {
239
+ // Agent mode, several accounts, none requested: do not guess.
240
+ // (CI never gets here; #pickAccount throws instead.)
241
+ // A person has to choose; the agent must not pick an account itself.
242
+ this.#print(JSON.stringify({
243
+ status: 'action_required',
244
+ reason: 'select_account',
245
+ userActionRequired: true,
246
+ message: (this.#notice ? `${this.#notice} ` : '')
247
+ + 'Logged in, but this user belongs to several accounts. Ask the user which one to use, then run '
248
+ + '`npx checkly login --account-id <id>`'
249
+ + (this.#inline ? ' and run the original command again' : '')
250
+ + '. To log in as someone else instead, run `npx checkly logout` first.',
251
+ user: userName,
252
+ choices: accountSummaries,
253
+ next: [{ command: 'npx checkly login --account-id <id>' }],
254
+ }));
255
+ return false;
256
+ }
257
+ await activateAccount(account);
258
+ const switched = switchingAccount && usingStoredKey;
259
+ if (this.#mode === 'agent') {
260
+ this.#print(JSON.stringify({
261
+ status: 'success',
262
+ reason: switched ? 'account_switched' : 'logged_in',
263
+ message: (this.#notice ? `${this.#notice} ` : '') + (switched
264
+ ? `Switched to account "${account.name}".`
265
+ : `Logged in as ${userName} to account "${account.name}".`),
266
+ user: userName,
267
+ accountId: account.id,
268
+ accountName: account.name,
269
+ }));
36
270
  }
271
+ else if (switched) {
272
+ this.#print(`Switched to account ${chalk.cyan.bold(account.name)} (${account.id})`);
273
+ }
274
+ else {
275
+ if (this.#mode === 'interactive' && !this.#atBlankLine) {
276
+ this.#print('');
277
+ }
278
+ this.#print(`Logged in as ${chalk.cyan.bold(userName)} to ${chalk.cyan.bold(account.name)}.`);
279
+ // A login another command started goes straight on to that command.
280
+ const hint = this.#mode === 'interactive' && !this.#inline ? await nextStepHint() : undefined;
281
+ if (hint) {
282
+ this.#print('');
283
+ this.#print(hint);
284
+ }
285
+ }
286
+ return true;
37
287
  }
38
- };
39
- _isLoginSuccess = async () => {
40
- await api.validateAuthentication();
41
- this.log('Welcome to the Checkly CLI');
42
- };
43
- async run() {
44
- await this._checkExistingCredentials();
45
- const mode = await this.#promptForLoginOrSignUp();
46
- const onCancel = () => {
47
- this.error('Command cancelled.\n');
48
- };
49
- const authContext = new AuthContext(mode);
50
- const { openUrl } = await prompts({
51
- name: 'openUrl',
288
+ catch (error) {
289
+ if (this.#mode !== 'agent') {
290
+ // Expected login failures, including any answer from the login
291
+ // server, get a plain message; anything else keeps its stack for
292
+ // debugging.
293
+ if (error instanceof DeviceFlowError || errorReason(error) !== 'login_failed') {
294
+ this.error(errorMessage(error), { exit: 1 });
295
+ }
296
+ throw error;
297
+ }
298
+ this.#print(JSON.stringify({ status: 'error', reason: errorReason(error), message: errorMessage(error) }));
299
+ return false;
300
+ }
301
+ }
302
+ /**
303
+ * Stores the key of a fresh login and forgets any device code an agent
304
+ * login left behind. The previous account belongs to the
305
+ * previous key, possibly another user, so it is dropped first: if choosing
306
+ * an account fails afterwards, the login is left unfinished (key without
307
+ * account, which the next login resumes) instead of pairing the new key
308
+ * with the old account.
309
+ */
310
+ #storeNewKey(key) {
311
+ config.data.delete('accountId');
312
+ config.data.delete('accountName');
313
+ config.auth.delete('pendingDeviceAuthorization');
314
+ config.auth.set('apiKey', key);
315
+ }
316
+ /**
317
+ * The name of the user the stored key belongs to, or undefined when the
318
+ * key is no longer accepted (revoked or expired). A rejected key is
319
+ * deleted so the caller authenticates again instead of reusing it.
320
+ */
321
+ async #storedKeyUserName() {
322
+ try {
323
+ return (await api.user.get()).data.name;
324
+ }
325
+ catch (error) {
326
+ if (error instanceof UnauthorizedError) {
327
+ config.auth.delete('apiKey');
328
+ return undefined;
329
+ }
330
+ throw error;
331
+ }
332
+ }
333
+ #print(line) {
334
+ this.#atBlankLine = line === '';
335
+ if (this.#inline) {
336
+ this.logToStderr(line);
337
+ }
338
+ else {
339
+ this.log(line);
340
+ }
341
+ }
342
+ // ─── LOGIN STATE ────────────────────────────────────────────
343
+ async #wantsToReplaceLogin(accountName) {
344
+ const accountId = config.data.get('accountId');
345
+ if (this.#mode !== 'interactive') {
346
+ if (this.#mode === 'agent') {
347
+ this.#print(JSON.stringify({
348
+ status: 'success',
349
+ reason: 'already_logged_in',
350
+ message: `Already logged in to account "${accountName}".`,
351
+ accountId,
352
+ accountName,
353
+ }));
354
+ }
355
+ else {
356
+ this.#print(`Already logged in to "${accountName}".`);
357
+ }
358
+ return false;
359
+ }
360
+ const { setNewkey } = await prompts({
361
+ name: 'setNewkey',
52
362
  type: 'confirm',
53
- message: `Do you want to open a browser window to continue with ${mode === 'login' ? 'login' : 'sign up'}?`,
54
- initial: true,
363
+ message: `You are currently logged in to "${accountName}". Do you want to log out and log in to a different account?`,
55
364
  });
56
- if (!openUrl) {
57
- this.log(`Please open the following URL in your browser: \n\n${chalk.cyan(authContext.authenticationUrl)}`);
365
+ return Boolean(setNewkey);
366
+ }
367
+ /**
368
+ * Checks the stored key against the stored account. If it no longer works
369
+ * (key revoked, access removed, account deleted), drops the stored account,
370
+ * so the stored key is then checked like that of an unfinished login. Other
371
+ * failures, such as the API being unreachable, are thrown. Returns the
372
+ * account's name either way.
373
+ */
374
+ async #checkStoredAccount() {
375
+ const accountId = config.getAccountId();
376
+ try {
377
+ const { data: account } = await api.accounts.get(accountId);
378
+ // Older CLI versions' `checkly switch` stored only the new account's
379
+ // id, so the stored name can belong to another account.
380
+ if (account.name !== config.data.get('accountName')) {
381
+ config.data.set('accountName', account.name);
382
+ }
383
+ return { usable: true, name: account.name };
384
+ }
385
+ catch (error) {
386
+ const unusable = error instanceof UnauthorizedError
387
+ || error instanceof ForbiddenError
388
+ || error instanceof NotFoundError;
389
+ if (!unusable) {
390
+ throw error;
391
+ }
392
+ const accountName = config.data.get('accountName');
393
+ config.data.delete('accountId');
394
+ config.data.delete('accountName');
395
+ return { usable: false, name: accountName || accountId };
396
+ }
397
+ }
398
+ // ─── AUTHENTICATION ─────────────────────────────────────────
399
+ /**
400
+ * Returns the new credentials, or:
401
+ * - 'pending' when an agent-mode login has shown the code and returned
402
+ * without waiting for the user (the next run collects it), or has sent
403
+ * the agent to `npx checkly login` (nothing stored to collect);
404
+ * - 'stored' when another process completed this login with the same code
405
+ * and stored its key.
406
+ */
407
+ async #authenticate() {
408
+ const deviceFlow = new DeviceFlow();
409
+ let authorization;
410
+ try {
411
+ const pending = this.#mode === 'agent' ? this.#pendingAuthorization() : undefined;
412
+ if (pending) {
413
+ const collected = await this.#collectPendingAuthorization(deviceFlow, pending);
414
+ if (collected !== 'expiring') {
415
+ return collected;
416
+ }
417
+ }
418
+ authorization = await deviceFlow.requestAuthorization();
419
+ }
420
+ catch (error) {
421
+ // The device grant is not enabled, or was turned off (rolled back)
422
+ // after a stored code was issued; that code is gone either way.
423
+ if (error instanceof DeviceFlowNotAllowedError) {
424
+ return this.#authenticateWithBrowserCallback();
425
+ }
426
+ throw error;
427
+ }
428
+ if (this.#mode === 'agent') {
429
+ // Agents usually show a command's output only once it exits, so
430
+ // waiting here would hide the code until it expired. Keep the code and
431
+ // return; the next login (or authenticated command) collects it.
432
+ // Commands an agent runs in parallel before it knows it is logged out
433
+ // each get here; the first code stored wins, so the user approves the
434
+ // one code every later run collects.
435
+ const storedMeanwhile = this.#pendingAuthorization();
436
+ if (storedMeanwhile) {
437
+ authorization = storedMeanwhile;
438
+ }
439
+ else {
440
+ const stored = { ...authorization, authUrl: config.getAuthUrl() };
441
+ config.auth.set('pendingDeviceAuthorization', stored);
442
+ }
443
+ }
444
+ // A login an agent's command starts on its own (often just a `whoami`
445
+ // check) must not pop up a browser tab the user did not ask for; the
446
+ // agent relays the URL instead. An explicit `checkly login` opens it for
447
+ // a code this run shows for the first time, whether it requested it or
448
+ // took it over from a parallel run (not when it shows a stored one again).
449
+ const browserOpened = !(this.#mode === 'agent' && this.#inline)
450
+ && await this.#tryOpenBrowser(authorization.verificationUriComplete);
451
+ this.#announceDeviceCode(authorization, undefined, browserOpened);
452
+ if (this.#mode === 'agent') {
453
+ return 'pending';
454
+ }
455
+ const tokens = await this.#whileWaiting('Waiting for you to finish in the browser', report => deviceFlow.pollForTokens(authorization, {
456
+ onUnexpectedAnswers: failure => report(`Still waiting: the last answer was ${failure}.`),
457
+ }));
458
+ return credentialsFromTokens(tokens);
459
+ }
460
+ /** The stored device code, if it is still usable against this login server. */
461
+ #pendingAuthorization() {
462
+ const stored = config.auth.get('pendingDeviceAuthorization');
463
+ if (!stored) {
464
+ return undefined;
465
+ }
466
+ if (stored.authUrl !== config.getAuthUrl() || !(stored.expiresAt > Date.now())) {
467
+ config.auth.delete('pendingDeviceAuthorization');
468
+ return undefined;
469
+ }
470
+ return stored;
471
+ }
472
+ /**
473
+ * Checks once whether the user has approved the stored code. Still
474
+ * waiting: show the same code again and return 'pending', or return
475
+ * 'expiring' when it is about to expire.
476
+ */
477
+ async #collectPendingAuthorization(deviceFlow, pending) {
478
+ let result;
479
+ try {
480
+ result = await deviceFlow.pollOnce(pending);
481
+ }
482
+ catch (error) {
483
+ // Denied, expired or already used: the code is done either way.
484
+ config.auth.delete('pendingDeviceAuthorization');
485
+ if (error instanceof DeviceFlowError && config.getApiKey()) {
486
+ return 'stored';
487
+ }
488
+ throw error;
489
+ }
490
+ if (!result.tokens) {
491
+ // The login server says it is not approved, and too little time is left
492
+ // for the agent to relay it and the user to approve: the caller requests
493
+ // a new code. After a failed request the code is kept, since it may
494
+ // have been approved.
495
+ if (!result.failure && pending.expiresAt - Date.now() < MIN_RELAY_TIME_MS) {
496
+ config.auth.delete('pendingDeviceAuthorization');
497
+ return 'expiring';
498
+ }
499
+ // Without this, an agent whose user has already approved would be told
500
+ // to run the command again with nothing to suggest that it was the
501
+ // login server, not the user, that has not answered yet.
502
+ const problem = result.failure
503
+ ? `The last request to the login server failed (${result.failure}), so an approval may not have been seen yet; `
504
+ + 'run this command again in a moment.'
505
+ : undefined;
506
+ this.#announceDeviceCode(pending, problem);
507
+ return 'pending';
508
+ }
509
+ config.auth.delete('pendingDeviceAuthorization');
510
+ return credentialsFromTokens(result.tokens);
511
+ }
512
+ // The login pages also offer sign-up, and logging in with a new identity
513
+ // creates the Checkly user (see exchangeAccessTokenForApiKey()).
514
+ static #signUpHint = 'New to Checkly? You can sign up on the same page.';
515
+ /**
516
+ * `browserOpened` only shapes the interactive lines: the pre-filled link is
517
+ * shown when no browser was opened, for the user to open themselves.
518
+ */
519
+ #announceDeviceCode(authorization, problem, browserOpened = false) {
520
+ this.#announce({
521
+ status: 'action_required',
522
+ reason: 'login_required',
523
+ userActionRequired: true,
524
+ message: (this.#notice ? `${this.#notice} ` : '')
525
+ // A command that started a login on its own (e.g. `whoami`) prints
526
+ // this instead of what was asked for; say so, or the line reads as a
527
+ // plain "not logged in" error and the code goes unused.
528
+ + (this.#inline ? 'Not logged in to Checkly, so this command needs a login first. ' : '')
529
+ + `Open ${authorization.verificationUri} in a browser on any device and enter the code `
530
+ + `${authorization.userCode}. Once the user has approved, run this command again.`
531
+ + (problem ? ` ${problem}` : '') + ` ${_a.#signUpHint}`,
532
+ verification_uri: authorization.verificationUri,
533
+ verification_uri_complete: authorization.verificationUriComplete,
534
+ user_code: authorization.userCode,
535
+ expires_in: Math.max(0, Math.round((authorization.expiresAt - Date.now()) / 1000)),
536
+ // The step after the user approves, spelled out: no other CLI logs in
537
+ // over two runs, so agents won't expect it. Left out when another
538
+ // command started the login: running that command again is the step,
539
+ // and its arguments aren't known here.
540
+ next: this.#inline
541
+ ? undefined
542
+ : [{
543
+ command: 'npx checkly login'
544
+ + (this.#requestedAccountId ? ` --account-id ${shellQuote(this.#requestedAccountId)}` : ''),
545
+ when: 'after the user has approved in the browser',
546
+ }],
547
+ }, [
548
+ `Visit ${chalk.bold.underline(withoutScheme(authorization.verificationUri))} and enter `
549
+ + chalk.bold(authorization.userCode),
550
+ // Keeps its scheme: this is the link to click or copy.
551
+ ...browserOpened ? [] : [chalk.dim(`Or open ${authorization.verificationUriComplete}`)],
552
+ '',
553
+ ]);
554
+ }
555
+ /**
556
+ * Authorization-code flow with a callback server on localhost. Used while
557
+ * the device grant is not enabled for the CLI client. Only works when the
558
+ * browser runs on the same machine as the CLI.
559
+ */
560
+ async #authenticateWithBrowserCallback() {
561
+ // This login only completes while the command waits for the browser on
562
+ // this machine. A command an agent started for something else (often
563
+ // just a `whoami` check) would sit there unseen, since agents usually
564
+ // show output only once a command exits; send the agent to the login
565
+ // command instead.
566
+ if (this.#mode === 'agent' && this.#inline) {
567
+ this.#announce({
568
+ status: 'action_required',
569
+ reason: 'login_required',
570
+ userActionRequired: true,
571
+ message: 'Run `npx checkly login` in the background to log in (it waits for the browser on this machine '
572
+ + 'to finish), relay what it prints, then run the original command again.',
573
+ next: [{ command: 'npx checkly login' }],
574
+ }, []);
575
+ return 'pending';
576
+ }
577
+ const mode = this.#mode === 'interactive'
578
+ ? await this.#promptForLoginOrSignUp()
579
+ : 'any';
580
+ const authContext = new AuthContext(mode);
581
+ if (this.#mode === 'interactive') {
582
+ // --no-browser / CHECKLY_NO_BROWSER: don't offer to open one.
583
+ const openUrl = this.#openBrowser && (await prompts({
584
+ name: 'openUrl',
585
+ type: 'confirm',
586
+ message: `Do you want to open a browser window to continue with ${mode === 'signup' ? 'sign up' : 'login'}?`,
587
+ initial: true,
588
+ })).openUrl;
589
+ if (openUrl) {
590
+ await open(authContext.authenticationUrl);
591
+ }
592
+ else {
593
+ // The login completes through a callback to localhost, so a browser
594
+ // elsewhere (e.g. on the laptop an SSH session comes from) won't do.
595
+ this.#print('Please open the following URL in a browser on this machine: '
596
+ + `\n\n${chalk.cyan(authContext.authenticationUrl)}`);
597
+ }
58
598
  }
59
599
  else {
60
- await open(authContext.authenticationUrl);
600
+ this.#announce({
601
+ status: 'action_required',
602
+ reason: 'login_required',
603
+ userActionRequired: true,
604
+ message: 'Ask the user to open the URL in a browser on the same machine as this CLI '
605
+ + `(it completes through a local callback). ${_a.#signUpHint}`,
606
+ verification_uri: authContext.authenticationUrl,
607
+ }, []);
608
+ await this.#tryOpenBrowser(authContext.authenticationUrl);
61
609
  }
62
- const { key, name } = await authContext.getAuth0Credentials();
63
- config.auth.set('apiKey', key);
64
- const { data: accounts } = await api.accounts.getAll();
65
- // A single-account login needs no selection; prompt only when there's a real choice.
66
- const selectedAccount = accounts.length === 1
67
- ? accounts[0]
68
- : await selectAccount(accounts, { onCancel });
69
- if (!selectedAccount) {
70
- this.warn('You must select a valid Checkly account name.');
71
- this.exit(1);
610
+ return authContext.getAuth0Credentials();
611
+ }
612
+ #announce(payload, interactiveLines) {
613
+ if (this.#mode === 'agent') {
614
+ this.#print(JSON.stringify(payload));
72
615
  return;
73
616
  }
74
- config.data.set('accountId', selectedAccount.id);
75
- config.data.set('accountName', selectedAccount.name);
76
- this.log(`Successfully logged in as ${chalk.cyan.bold(name)}`);
77
- await this._isLoginSuccess();
78
- this.exit(0);
617
+ for (const line of interactiveLines) {
618
+ this.#print(line);
619
+ }
620
+ }
621
+ /**
622
+ * Opens the URL in a browser as a best effort. Returns whether one can be
623
+ * assumed to have opened on the user's screen: `open` resolves as soon as
624
+ * it starts a launcher, also in a remote session or on a Linux machine
625
+ * without a desktop, where nothing appears.
626
+ */
627
+ async #tryOpenBrowser(url) {
628
+ if (!this.#openBrowser) {
629
+ return false;
630
+ }
631
+ try {
632
+ await open(url);
633
+ }
634
+ catch {
635
+ return false;
636
+ }
637
+ const remote = Boolean(process.env.SSH_CONNECTION || process.env.SSH_TTY);
638
+ const noDesktop = process.platform === 'linux' && !process.env.DISPLAY && !process.env.WAYLAND_DISPLAY;
639
+ return !remote && !noDesktop;
640
+ }
641
+ /**
642
+ * Runs `wait` behind the CLI's usual spinner when a person watches the
643
+ * command's own output on a terminal; otherwise prints `message` as a plain
644
+ * line, so piped output gets no animation frames and a login another
645
+ * command started keeps its output on stderr (the spinner writes to
646
+ * stdout). `report` shows a note while waiting.
647
+ */
648
+ async #whileWaiting(message, wait) {
649
+ if (this.#mode !== 'interactive' || this.#inline || !this.fancy || !process.stdout.isTTY) {
650
+ if (this.#mode === 'interactive') {
651
+ this.#print(chalk.dim(`${message}…`));
652
+ }
653
+ return wait(note => this.#print(note));
654
+ }
655
+ this.style.actionStart(message);
656
+ try {
657
+ const result = await wait(note => this.style.actionStatus(note));
658
+ this.style.actionSuccess();
659
+ // actionSuccess ends with an empty line.
660
+ this.#atBlankLine = true;
661
+ return result;
662
+ }
663
+ catch (error) {
664
+ this.style.actionFailure();
665
+ throw error;
666
+ }
79
667
  }
80
668
  async #promptForLoginOrSignUp() {
81
669
  const { mode } = await prompts({
@@ -92,5 +680,50 @@ export default class Login extends BaseCommand {
92
680
  });
93
681
  return mode;
94
682
  }
683
+ // ─── ACCOUNT SELECTION ──────────────────────────────────────
684
+ /**
685
+ * Returns undefined only in agent mode when several accounts are available
686
+ * and none was requested: the caller reports the choice instead of guessing.
687
+ */
688
+ async #pickAccount(accounts, requestedId, usingStoredKey) {
689
+ const available = accounts.map(a => `${a.name} (${a.id})`).join(', ');
690
+ if (requestedId) {
691
+ const match = accounts.find(account => account.id === requestedId);
692
+ if (!match) {
693
+ // With the stored key the account list is that user's; the requested
694
+ // account may belong to another identity the user also logs in with.
695
+ throw new LoginError('account_not_found', `No account with id "${requestedId}" is available to this user. `
696
+ + `Available: ${available}`
697
+ + (usingStoredKey ? '. To log in as a different user, run `npx checkly logout` first.' : ''));
698
+ }
699
+ return match;
700
+ }
701
+ if (accounts.length === 0) {
702
+ throw new LoginError('no_accounts', 'This user has no Checkly accounts. '
703
+ + 'To log in as a different user, run `npx checkly logout` first.');
704
+ }
705
+ if (accounts.length === 1) {
706
+ return accounts[0];
707
+ }
708
+ if (this.#mode === 'ci') {
709
+ throw new Error('This user belongs to several accounts: '
710
+ + `${available}. `
711
+ + 'Choose one with `npx checkly login --account-id <id>`, or set `CHECKLY_API_KEY` and '
712
+ + '`CHECKLY_ACCOUNT_ID` in the environment.');
713
+ }
714
+ if (this.#mode === 'agent') {
715
+ return undefined;
716
+ }
717
+ this.#atBlankLine = false;
718
+ const selected = await selectAccount(accounts, {
719
+ onCancel: () => this.error('Command cancelled.\n'),
720
+ });
721
+ if (!selected) {
722
+ throw new Error('You must select a valid Checkly account name.');
723
+ }
724
+ return selected;
725
+ }
95
726
  }
727
+ _a = Login;
728
+ export default Login;
96
729
  //# sourceMappingURL=login.js.map