workos 0.18.0 → 0.19.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 (333) hide show
  1. package/README.md +8 -145
  2. package/dist/bin.js +129 -50
  3. package/dist/bin.js.map +1 -1
  4. package/dist/commands/api/index.js +18 -10
  5. package/dist/commands/api/index.js.map +1 -1
  6. package/dist/commands/api/interactive.js +11 -11
  7. package/dist/commands/api/interactive.js.map +1 -1
  8. package/dist/commands/claim.js +28 -16
  9. package/dist/commands/claim.js.map +1 -1
  10. package/dist/commands/connection.js +3 -3
  11. package/dist/commands/connection.js.map +1 -1
  12. package/dist/commands/debug.js +6 -6
  13. package/dist/commands/debug.js.map +1 -1
  14. package/dist/commands/dev.d.ts +7 -8
  15. package/dist/commands/dev.js +14 -14
  16. package/dist/commands/dev.js.map +1 -1
  17. package/dist/commands/directory.js +3 -3
  18. package/dist/commands/directory.js.map +1 -1
  19. package/dist/commands/doctor.js +2 -2
  20. package/dist/commands/doctor.js.map +1 -1
  21. package/dist/commands/emulate.d.ts +1 -0
  22. package/dist/commands/emulate.js +3 -2
  23. package/dist/commands/emulate.js.map +1 -1
  24. package/dist/commands/env.d.ts +10 -0
  25. package/dist/commands/env.js +94 -18
  26. package/dist/commands/env.js.map +1 -1
  27. package/dist/commands/install.js +8 -13
  28. package/dist/commands/install.js.map +1 -1
  29. package/dist/commands/login.d.ts +25 -15
  30. package/dist/commands/login.js +126 -71
  31. package/dist/commands/login.js.map +1 -1
  32. package/dist/commands/logout.js +4 -4
  33. package/dist/commands/logout.js.map +1 -1
  34. package/dist/commands/mcp.js +7 -16
  35. package/dist/commands/mcp.js.map +1 -1
  36. package/dist/commands/migrations.js +2 -1
  37. package/dist/commands/migrations.js.map +1 -1
  38. package/dist/commands/seed.js +5 -4
  39. package/dist/commands/seed.js.map +1 -1
  40. package/dist/commands/setup.d.ts +50 -0
  41. package/dist/commands/setup.js +237 -0
  42. package/dist/commands/setup.js.map +1 -0
  43. package/dist/commands/vault-run.js +3 -2
  44. package/dist/commands/vault-run.js.map +1 -1
  45. package/dist/commands/verify-login.d.ts +25 -0
  46. package/dist/commands/verify-login.js +158 -0
  47. package/dist/commands/verify-login.js.map +1 -0
  48. package/dist/doctor/issues.js +3 -2
  49. package/dist/doctor/issues.js.map +1 -1
  50. package/dist/doctor/output.js +2 -1
  51. package/dist/doctor/output.js.map +1 -1
  52. package/dist/integrations/dotnet/index.js +1 -1
  53. package/dist/integrations/dotnet/index.js.map +1 -1
  54. package/dist/integrations/elixir/index.js +1 -1
  55. package/dist/integrations/elixir/index.js.map +1 -1
  56. package/dist/integrations/go/index.js +1 -1
  57. package/dist/integrations/go/index.js.map +1 -1
  58. package/dist/integrations/nextjs/index.js +8 -5
  59. package/dist/integrations/nextjs/index.js.map +1 -1
  60. package/dist/integrations/nextjs/utils.d.ts +1 -1
  61. package/dist/integrations/nextjs/utils.js +21 -6
  62. package/dist/integrations/nextjs/utils.js.map +1 -1
  63. package/dist/integrations/react-router/index.js +5 -5
  64. package/dist/integrations/react-router/index.js.map +1 -1
  65. package/dist/integrations/react-router/utils.js +28 -12
  66. package/dist/integrations/react-router/utils.js.map +1 -1
  67. package/dist/integrations/ruby/index.js +1 -1
  68. package/dist/integrations/ruby/index.js.map +1 -1
  69. package/dist/lib/adapters/cli-adapter.d.ts +33 -4
  70. package/dist/lib/adapters/cli-adapter.js +251 -135
  71. package/dist/lib/adapters/cli-adapter.js.map +1 -1
  72. package/dist/lib/adapters/dashboard-adapter.js +3 -3
  73. package/dist/lib/adapters/dashboard-adapter.js.map +1 -1
  74. package/dist/lib/adapters/headless-adapter.d.ts +7 -0
  75. package/dist/lib/adapters/headless-adapter.js +42 -5
  76. package/dist/lib/adapters/headless-adapter.js.map +1 -1
  77. package/dist/lib/agent-interface.js +15 -1
  78. package/dist/lib/agent-interface.js.map +1 -1
  79. package/dist/lib/agent-runner.js +1 -1
  80. package/dist/lib/agent-runner.js.map +1 -1
  81. package/dist/lib/api-key.js +2 -1
  82. package/dist/lib/api-key.js.map +1 -1
  83. package/dist/lib/completion-data.d.ts +33 -0
  84. package/dist/lib/completion-data.js +33 -0
  85. package/dist/lib/completion-data.js.map +1 -0
  86. package/dist/lib/config-store.d.ts +13 -0
  87. package/dist/lib/config-store.js +24 -0
  88. package/dist/lib/config-store.js.map +1 -1
  89. package/dist/lib/config.d.ts +1 -1
  90. package/dist/lib/config.js +1 -1
  91. package/dist/lib/config.js.map +1 -1
  92. package/dist/lib/constants.d.ts +8 -0
  93. package/dist/lib/constants.js +8 -0
  94. package/dist/lib/constants.js.map +1 -1
  95. package/dist/lib/events.d.ts +34 -1
  96. package/dist/lib/events.js.map +1 -1
  97. package/dist/lib/framework-config.d.ts +6 -0
  98. package/dist/lib/framework-config.js.map +1 -1
  99. package/dist/lib/installer-core.d.ts +14 -1
  100. package/dist/lib/installer-core.js +24 -5
  101. package/dist/lib/installer-core.js.map +1 -1
  102. package/dist/lib/installer-core.types.d.ts +3 -1
  103. package/dist/lib/installer-core.types.js.map +1 -1
  104. package/dist/lib/mcp-clients.d.ts +6 -0
  105. package/dist/lib/mcp-clients.js +13 -0
  106. package/dist/lib/mcp-clients.js.map +1 -1
  107. package/dist/lib/port-detection.js +19 -13
  108. package/dist/lib/port-detection.js.map +1 -1
  109. package/dist/lib/preferences.d.ts +30 -2
  110. package/dist/lib/preferences.js +34 -8
  111. package/dist/lib/preferences.js.map +1 -1
  112. package/dist/lib/run-with-core.js +40 -3
  113. package/dist/lib/run-with-core.js.map +1 -1
  114. package/dist/lib/telemetry-notice.d.ts +5 -5
  115. package/dist/lib/telemetry-notice.js +7 -8
  116. package/dist/lib/telemetry-notice.js.map +1 -1
  117. package/dist/lib/unclaimed-env-provision.js +15 -12
  118. package/dist/lib/unclaimed-env-provision.js.map +1 -1
  119. package/dist/lib/unclaimed-warning.js +4 -3
  120. package/dist/lib/unclaimed-warning.js.map +1 -1
  121. package/dist/lib/validation/security-checks.js +2 -1
  122. package/dist/lib/validation/security-checks.js.map +1 -1
  123. package/dist/lib/validation/validator.js +30 -3
  124. package/dist/lib/validation/validator.js.map +1 -1
  125. package/dist/lib/workos-management.js +26 -19
  126. package/dist/lib/workos-management.js.map +1 -1
  127. package/dist/run.d.ts +1 -0
  128. package/dist/run.js +5 -1
  129. package/dist/run.js.map +1 -1
  130. package/dist/steps/add-or-update-environment-variables.js +10 -10
  131. package/dist/steps/add-or-update-environment-variables.js.map +1 -1
  132. package/dist/steps/run-prettier.js +3 -3
  133. package/dist/steps/run-prettier.js.map +1 -1
  134. package/dist/steps/upload-environment-variables/index.js +15 -3
  135. package/dist/steps/upload-environment-variables/index.js.map +1 -1
  136. package/dist/steps/upload-environment-variables/providers/vercel.js +2 -2
  137. package/dist/steps/upload-environment-variables/providers/vercel.js.map +1 -1
  138. package/dist/utils/box.d.ts +6 -30
  139. package/dist/utils/box.js +8 -95
  140. package/dist/utils/box.js.map +1 -1
  141. package/dist/utils/cli-symbols.d.ts +14 -2
  142. package/dist/utils/cli-symbols.js +12 -0
  143. package/dist/utils/cli-symbols.js.map +1 -1
  144. package/dist/utils/debug.js +3 -3
  145. package/dist/utils/debug.js.map +1 -1
  146. package/dist/utils/environment.js +1 -1
  147. package/dist/utils/environment.js.map +1 -1
  148. package/dist/utils/help-json.js +115 -4
  149. package/dist/utils/help-json.js.map +1 -1
  150. package/dist/utils/package-manager.js +1 -1
  151. package/dist/utils/package-manager.js.map +1 -1
  152. package/dist/utils/recovery-hints.d.ts +5 -0
  153. package/dist/utils/recovery-hints.js +12 -0
  154. package/dist/utils/recovery-hints.js.map +1 -1
  155. package/dist/utils/summary-box.d.ts +8 -1
  156. package/dist/utils/summary-box.js +67 -4
  157. package/dist/utils/summary-box.js.map +1 -1
  158. package/dist/utils/types.d.ts +10 -0
  159. package/dist/utils/types.js.map +1 -1
  160. package/dist/utils/{clack-utils.d.ts → ui-utils.d.ts} +1 -1
  161. package/dist/utils/{clack-utils.js → ui-utils.js} +64 -37
  162. package/dist/utils/ui-utils.js.map +1 -0
  163. package/dist/utils/ui.d.ts +148 -0
  164. package/dist/utils/ui.js +368 -0
  165. package/dist/utils/ui.js.map +1 -0
  166. package/package.json +5 -13
  167. package/dist/check-coverage.ts +0 -237
  168. package/dist/emulate/core/id.d.ts +0 -48
  169. package/dist/emulate/core/id.js +0 -73
  170. package/dist/emulate/core/id.js.map +0 -1
  171. package/dist/emulate/core/index.d.ts +0 -8
  172. package/dist/emulate/core/index.js +0 -8
  173. package/dist/emulate/core/index.js.map +0 -1
  174. package/dist/emulate/core/jwt.d.ts +0 -28
  175. package/dist/emulate/core/jwt.js +0 -78
  176. package/dist/emulate/core/jwt.js.map +0 -1
  177. package/dist/emulate/core/middleware/auth.d.ts +0 -15
  178. package/dist/emulate/core/middleware/auth.js +0 -17
  179. package/dist/emulate/core/middleware/auth.js.map +0 -1
  180. package/dist/emulate/core/middleware/error-handler.d.ts +0 -22
  181. package/dist/emulate/core/middleware/error-handler.js +0 -72
  182. package/dist/emulate/core/middleware/error-handler.js.map +0 -1
  183. package/dist/emulate/core/pagination.d.ts +0 -27
  184. package/dist/emulate/core/pagination.js +0 -43
  185. package/dist/emulate/core/pagination.js.map +0 -1
  186. package/dist/emulate/core/plugin.d.ts +0 -15
  187. package/dist/emulate/core/plugin.js +0 -2
  188. package/dist/emulate/core/plugin.js.map +0 -1
  189. package/dist/emulate/core/server.d.ts +0 -17
  190. package/dist/emulate/core/server.js +0 -90
  191. package/dist/emulate/core/server.js.map +0 -1
  192. package/dist/emulate/core/store.d.ts +0 -44
  193. package/dist/emulate/core/store.js +0 -169
  194. package/dist/emulate/core/store.js.map +0 -1
  195. package/dist/emulate/index.d.ts +0 -25
  196. package/dist/emulate/index.js +0 -47
  197. package/dist/emulate/index.js.map +0 -1
  198. package/dist/emulate/workos/constants.d.ts +0 -56
  199. package/dist/emulate/workos/constants.js +0 -56
  200. package/dist/emulate/workos/constants.js.map +0 -1
  201. package/dist/emulate/workos/entities.d.ts +0 -360
  202. package/dist/emulate/workos/entities.js +0 -2
  203. package/dist/emulate/workos/entities.js.map +0 -1
  204. package/dist/emulate/workos/event-bus.d.ts +0 -17
  205. package/dist/emulate/workos/event-bus.js +0 -70
  206. package/dist/emulate/workos/event-bus.js.map +0 -1
  207. package/dist/emulate/workos/helpers.d.ts +0 -72
  208. package/dist/emulate/workos/helpers.js +0 -211
  209. package/dist/emulate/workos/helpers.js.map +0 -1
  210. package/dist/emulate/workos/index.d.ts +0 -93
  211. package/dist/emulate/workos/index.js +0 -326
  212. package/dist/emulate/workos/index.js.map +0 -1
  213. package/dist/emulate/workos/role-helpers.d.ts +0 -21
  214. package/dist/emulate/workos/role-helpers.js +0 -130
  215. package/dist/emulate/workos/role-helpers.js.map +0 -1
  216. package/dist/emulate/workos/routes/api-keys.d.ts +0 -2
  217. package/dist/emulate/workos/routes/api-keys.js +0 -32
  218. package/dist/emulate/workos/routes/api-keys.js.map +0 -1
  219. package/dist/emulate/workos/routes/audit-logs.d.ts +0 -2
  220. package/dist/emulate/workos/routes/audit-logs.js +0 -104
  221. package/dist/emulate/workos/routes/audit-logs.js.map +0 -1
  222. package/dist/emulate/workos/routes/auth-challenges.d.ts +0 -2
  223. package/dist/emulate/workos/routes/auth-challenges.js +0 -51
  224. package/dist/emulate/workos/routes/auth-challenges.js.map +0 -1
  225. package/dist/emulate/workos/routes/auth-factors.d.ts +0 -2
  226. package/dist/emulate/workos/routes/auth-factors.js +0 -51
  227. package/dist/emulate/workos/routes/auth-factors.js.map +0 -1
  228. package/dist/emulate/workos/routes/auth.d.ts +0 -2
  229. package/dist/emulate/workos/routes/auth.js +0 -350
  230. package/dist/emulate/workos/routes/auth.js.map +0 -1
  231. package/dist/emulate/workos/routes/authorization-checks.d.ts +0 -10
  232. package/dist/emulate/workos/routes/authorization-checks.js +0 -123
  233. package/dist/emulate/workos/routes/authorization-checks.js.map +0 -1
  234. package/dist/emulate/workos/routes/authorization-org-roles.d.ts +0 -2
  235. package/dist/emulate/workos/routes/authorization-org-roles.js +0 -64
  236. package/dist/emulate/workos/routes/authorization-org-roles.js.map +0 -1
  237. package/dist/emulate/workos/routes/authorization-permissions.d.ts +0 -2
  238. package/dist/emulate/workos/routes/authorization-permissions.js +0 -67
  239. package/dist/emulate/workos/routes/authorization-permissions.js.map +0 -1
  240. package/dist/emulate/workos/routes/authorization-resources.d.ts +0 -2
  241. package/dist/emulate/workos/routes/authorization-resources.js +0 -117
  242. package/dist/emulate/workos/routes/authorization-resources.js.map +0 -1
  243. package/dist/emulate/workos/routes/authorization-roles.d.ts +0 -2
  244. package/dist/emulate/workos/routes/authorization-roles.js +0 -13
  245. package/dist/emulate/workos/routes/authorization-roles.js.map +0 -1
  246. package/dist/emulate/workos/routes/config.d.ts +0 -2
  247. package/dist/emulate/workos/routes/config.js +0 -57
  248. package/dist/emulate/workos/routes/config.js.map +0 -1
  249. package/dist/emulate/workos/routes/connect.d.ts +0 -2
  250. package/dist/emulate/workos/routes/connect.js +0 -65
  251. package/dist/emulate/workos/routes/connect.js.map +0 -1
  252. package/dist/emulate/workos/routes/connections.d.ts +0 -2
  253. package/dist/emulate/workos/routes/connections.js +0 -73
  254. package/dist/emulate/workos/routes/connections.js.map +0 -1
  255. package/dist/emulate/workos/routes/data-integrations.d.ts +0 -2
  256. package/dist/emulate/workos/routes/data-integrations.js +0 -55
  257. package/dist/emulate/workos/routes/data-integrations.js.map +0 -1
  258. package/dist/emulate/workos/routes/directories.d.ts +0 -2
  259. package/dist/emulate/workos/routes/directories.js +0 -90
  260. package/dist/emulate/workos/routes/directories.js.map +0 -1
  261. package/dist/emulate/workos/routes/email-verification.d.ts +0 -2
  262. package/dist/emulate/workos/routes/email-verification.js +0 -49
  263. package/dist/emulate/workos/routes/email-verification.js.map +0 -1
  264. package/dist/emulate/workos/routes/events.d.ts +0 -2
  265. package/dist/emulate/workos/routes/events.js +0 -18
  266. package/dist/emulate/workos/routes/events.js.map +0 -1
  267. package/dist/emulate/workos/routes/feature-flags.d.ts +0 -2
  268. package/dist/emulate/workos/routes/feature-flags.js +0 -103
  269. package/dist/emulate/workos/routes/feature-flags.js.map +0 -1
  270. package/dist/emulate/workos/routes/invitations.d.ts +0 -2
  271. package/dist/emulate/workos/routes/invitations.js +0 -122
  272. package/dist/emulate/workos/routes/invitations.js.map +0 -1
  273. package/dist/emulate/workos/routes/legacy-mfa.d.ts +0 -2
  274. package/dist/emulate/workos/routes/legacy-mfa.js +0 -75
  275. package/dist/emulate/workos/routes/legacy-mfa.js.map +0 -1
  276. package/dist/emulate/workos/routes/magic-auth.d.ts +0 -2
  277. package/dist/emulate/workos/routes/magic-auth.js +0 -32
  278. package/dist/emulate/workos/routes/magic-auth.js.map +0 -1
  279. package/dist/emulate/workos/routes/memberships.d.ts +0 -2
  280. package/dist/emulate/workos/routes/memberships.js +0 -114
  281. package/dist/emulate/workos/routes/memberships.js.map +0 -1
  282. package/dist/emulate/workos/routes/organization-domains.d.ts +0 -2
  283. package/dist/emulate/workos/routes/organization-domains.js +0 -58
  284. package/dist/emulate/workos/routes/organization-domains.js.map +0 -1
  285. package/dist/emulate/workos/routes/organizations.d.ts +0 -2
  286. package/dist/emulate/workos/routes/organizations.js +0 -131
  287. package/dist/emulate/workos/routes/organizations.js.map +0 -1
  288. package/dist/emulate/workos/routes/password-reset.d.ts +0 -2
  289. package/dist/emulate/workos/routes/password-reset.js +0 -61
  290. package/dist/emulate/workos/routes/password-reset.js.map +0 -1
  291. package/dist/emulate/workos/routes/pipes.d.ts +0 -2
  292. package/dist/emulate/workos/routes/pipes.js +0 -82
  293. package/dist/emulate/workos/routes/pipes.js.map +0 -1
  294. package/dist/emulate/workos/routes/portal.d.ts +0 -2
  295. package/dist/emulate/workos/routes/portal.js +0 -18
  296. package/dist/emulate/workos/routes/portal.js.map +0 -1
  297. package/dist/emulate/workos/routes/radar.d.ts +0 -2
  298. package/dist/emulate/workos/routes/radar.js +0 -41
  299. package/dist/emulate/workos/routes/radar.js.map +0 -1
  300. package/dist/emulate/workos/routes/sessions.d.ts +0 -2
  301. package/dist/emulate/workos/routes/sessions.js +0 -51
  302. package/dist/emulate/workos/routes/sessions.js.map +0 -1
  303. package/dist/emulate/workos/routes/sso.d.ts +0 -2
  304. package/dist/emulate/workos/routes/sso.js +0 -161
  305. package/dist/emulate/workos/routes/sso.js.map +0 -1
  306. package/dist/emulate/workos/routes/user-features.d.ts +0 -2
  307. package/dist/emulate/workos/routes/user-features.js +0 -50
  308. package/dist/emulate/workos/routes/user-features.js.map +0 -1
  309. package/dist/emulate/workos/routes/users.d.ts +0 -2
  310. package/dist/emulate/workos/routes/users.js +0 -129
  311. package/dist/emulate/workos/routes/users.js.map +0 -1
  312. package/dist/emulate/workos/routes/webhook-endpoints.d.ts +0 -2
  313. package/dist/emulate/workos/routes/webhook-endpoints.js +0 -67
  314. package/dist/emulate/workos/routes/webhook-endpoints.js.map +0 -1
  315. package/dist/emulate/workos/routes/widgets.d.ts +0 -2
  316. package/dist/emulate/workos/routes/widgets.js +0 -27
  317. package/dist/emulate/workos/routes/widgets.js.map +0 -1
  318. package/dist/emulate/workos/store.d.ts +0 -48
  319. package/dist/emulate/workos/store.js +0 -102
  320. package/dist/emulate/workos/store.js.map +0 -1
  321. package/dist/emulate/workos/webhook-signer.d.ts +0 -1
  322. package/dist/emulate/workos/webhook-signer.js +0 -8
  323. package/dist/emulate/workos/webhook-signer.js.map +0 -1
  324. package/dist/gen-routes-lib.spec.ts +0 -659
  325. package/dist/gen-routes-lib.ts +0 -647
  326. package/dist/gen-routes.ts +0 -96
  327. package/dist/lib/mcp-notice.d.ts +0 -72
  328. package/dist/lib/mcp-notice.js +0 -275
  329. package/dist/lib/mcp-notice.js.map +0 -1
  330. package/dist/utils/clack-utils.js.map +0 -1
  331. package/dist/utils/clack.d.ts +0 -5
  332. package/dist/utils/clack.js +0 -34
  333. package/dist/utils/clack.js.map +0 -1
@@ -1,14 +1,15 @@
1
- import clack from '../../utils/clack.js';
1
+ import { relative } from 'node:path';
2
+ import ui, { PromptUnavailableError } from '../../utils/ui.js';
2
3
  import chalk from 'chalk';
3
4
  import { getConfig } from '../settings.js';
4
5
  import { ProgressTracker } from '../progress-tracker.js';
5
- import { renderCompletionSummary } from '../../utils/summary-box.js';
6
+ import { renderCompletionSummary, renderBrandMark } from '../../utils/summary-box.js';
6
7
  import { formatWorkOSCommand } from '../../utils/command-invocation.js';
7
8
  /**
8
- * CLI adapter that renders wizard events via clack.
9
+ * CLI adapter that renders wizard events via ui.
9
10
  *
10
11
  * Subscribes to InstallerEventEmitter and translates events into
11
- * clack UI operations (logs, spinners, prompts).
12
+ * UI facade operations (logs, spinners, prompts).
12
13
  */
13
14
  export class CLIAdapter {
14
15
  emitter;
@@ -27,8 +28,15 @@ export class CLIAdapter {
27
28
  pendingLogs = [];
28
29
  // SIGINT handler for cleanup
29
30
  sigIntHandler = null;
30
- // Long-running agent update interval
31
- agentUpdateInterval = null;
31
+ // Aborts in-flight/queued prompts when the run ends (cancellation, ctrl-c).
32
+ // The installer's `preparing` state can queue a second prompt behind a live
33
+ // one (git-dirty + protected-branch); if the first is cancelled, this signal
34
+ // stops the queued sibling from opening a now-moot question.
35
+ promptAbort = null;
36
+ // Last phase message shown on the agent spinner, restored after logging above it.
37
+ lastAgentMessage = 'Running AI agent...';
38
+ // Last file path rendered as a step line, to dedupe consecutive same-path ops.
39
+ lastFileOp = null;
32
40
  constructor(config) {
33
41
  this.emitter = config.emitter;
34
42
  this.sendEvent = config.sendEvent;
@@ -52,29 +60,47 @@ export class CLIAdapter {
52
60
  const logs = this.pendingLogs.splice(0);
53
61
  logs.forEach((fn) => fn());
54
62
  }
63
+ /**
64
+ * Run a prompt with the log queue engaged: mark a prompt active so async log
65
+ * events (detection:complete, branch:created, …) buffer instead of scribbling
66
+ * over the question, then release and flush once it resolves. Wraps every
67
+ * prompt handler so the active/flush lifecycle lives in one place.
68
+ */
69
+ async withPromptActive(run) {
70
+ this.isPromptActive = true;
71
+ try {
72
+ return await run();
73
+ }
74
+ finally {
75
+ this.isPromptActive = false;
76
+ this.flushPendingLogs();
77
+ }
78
+ }
55
79
  async start() {
56
80
  if (this.isStarted)
57
81
  return;
58
82
  this.isStarted = true;
83
+ this.promptAbort = new AbortController();
59
84
  // Show intro
60
85
  const config = getConfig();
61
86
  if (config.branding.showAsciiArt) {
62
- const art = config.branding.useCompact ? config.branding.compactAsciiArt : config.branding.asciiArt;
63
- console.log(chalk.cyan(art));
64
- console.log();
87
+ // Compact brand mark (the lock + wordmark) instead of the full block banner.
88
+ console.log('');
89
+ console.log(renderBrandMark('AuthKit installer'));
90
+ console.log('');
65
91
  }
66
92
  else {
67
- clack.intro('Welcome to the WorkOS AuthKit installer');
93
+ ui.intro('WorkOS', 'AuthKit installer');
68
94
  }
69
95
  // Handle Ctrl+C gracefully
70
96
  const handleSigInt = () => {
97
+ this.promptAbort?.abort();
71
98
  if (this.spinner) {
72
99
  this.spinner.stop('Cancelled');
73
100
  this.spinner = null;
74
101
  }
75
- this.stopAgentUpdates();
76
- clack.log.warn('Installer cancelled');
77
- clack.outro('Your project was not modified');
102
+ ui.log.warn('Installer cancelled');
103
+ ui.outro('Your project was not modified');
78
104
  process.exit(0);
79
105
  };
80
106
  process.on('SIGINT', handleSigInt);
@@ -93,12 +119,19 @@ export class CLIAdapter {
93
119
  this.subscribe('credentials:env:prompt', this.handleEnvScanPrompt);
94
120
  this.subscribe('device:started', this.handleDeviceStarted);
95
121
  this.subscribe('device:success', this.handleDeviceSuccess);
122
+ this.subscribe('device:error', this.handleDeviceError);
123
+ this.subscribe('device:timeout', this.handleDeviceTimeout);
96
124
  this.subscribe('staging:fetching', this.handleStagingFetching);
97
125
  this.subscribe('staging:success', this.handleStagingSuccess);
126
+ this.subscribe('staging:error', this.handleStagingError);
98
127
  this.subscribe('credentials:env:found', this.handleEnvCredentialsFound);
99
128
  this.subscribe('config:complete', this.handleConfigComplete);
100
129
  this.subscribe('agent:start', this.handleAgentStart);
101
130
  this.subscribe('agent:progress', this.handleAgentProgress);
131
+ // Persistent, append-only log of file operations + tool calls above the spinner.
132
+ this.subscribe('file:write', this.handleFileWrite);
133
+ this.subscribe('file:edit', this.handleFileEdit);
134
+ this.subscribe('agent:tool', this.handleAgentTool);
102
135
  this.subscribe('validation:start', this.handleValidationStart);
103
136
  this.subscribe('validation:issues', this.handleValidationIssues);
104
137
  this.subscribe('validation:complete', this.handleValidationComplete);
@@ -130,13 +163,15 @@ export class CLIAdapter {
130
163
  async stop() {
131
164
  if (!this.isStarted)
132
165
  return;
166
+ // Abort any in-flight/queued prompt so a cancelled run can't leave a
167
+ // now-moot sibling question open (e.g. the branch prompt after git-cancel).
168
+ this.promptAbort?.abort();
169
+ this.promptAbort = null;
133
170
  // Remove SIGINT handler
134
171
  if (this.sigIntHandler) {
135
172
  process.off('SIGINT', this.sigIntHandler);
136
173
  this.sigIntHandler = null;
137
174
  }
138
- // Stop agent updates
139
- this.stopAgentUpdates();
140
175
  // Unsubscribe from all events
141
176
  for (const [event, handler] of this.handlers) {
142
177
  this.emitter.off(event, handler);
@@ -147,15 +182,9 @@ export class CLIAdapter {
147
182
  this.spinner = null;
148
183
  this.isStarted = false;
149
184
  }
150
- stopAgentUpdates = () => {
151
- if (this.agentUpdateInterval) {
152
- clearInterval(this.agentUpdateInterval);
153
- this.agentUpdateInterval = null;
154
- }
155
- };
156
- stopSpinner(message) {
185
+ stopSpinner(message, code = 0) {
157
186
  if (this.spinner) {
158
- this.spinner.stop(message);
187
+ this.spinner.stop(message, code);
159
188
  this.spinner = null;
160
189
  }
161
190
  }
@@ -170,9 +199,43 @@ export class CLIAdapter {
170
199
  */
171
200
  subscribe(event, handler) {
172
201
  const boundHandler = handler.bind(this);
173
- this.handlers.set(event, boundHandler);
174
- this.emitter.on(event, boundHandler);
202
+ // Handlers are invoked fire-and-forget by a plain EventEmitter, so an async
203
+ // rejection would become an unhandledRejection (silent crash). Route a
204
+ // PromptUnavailableError (prompt attempted where the user can't answer) to a
205
+ // clean fail-fast; re-surface anything else unchanged so real bugs still crash.
206
+ const safeHandler = (payload) => {
207
+ try {
208
+ const result = boundHandler(payload);
209
+ if (result instanceof Promise)
210
+ result.catch((err) => this.onHandlerError(err));
211
+ }
212
+ catch (err) {
213
+ this.onHandlerError(err);
214
+ }
215
+ };
216
+ this.handlers.set(event, safeHandler);
217
+ this.emitter.on(event, safeHandler);
175
218
  }
219
+ /**
220
+ * Terminal handler failure. A PromptUnavailableError means we reached a prompt
221
+ * in a context that can't answer it (non-TTY stdin) — the CLIAdapter only runs
222
+ * in human, non-JSON output, so this is always a real human at a broken input,
223
+ * never a JSON stream. Surface a clear message and exit before anything is
224
+ * written, rather than hanging or crashing. Re-throw anything else.
225
+ */
226
+ onHandlerError = (error) => {
227
+ if (error instanceof PromptUnavailableError) {
228
+ this.spinner?.clear();
229
+ this.spinner = null;
230
+ ui.log.error(error.message);
231
+ ui.log.hint('Re-run in an interactive terminal, or pass the flags that answer these prompts.');
232
+ // Exit 1 (general error), matching the direct-command classification of
233
+ // this same error in bin.ts. NOT 4 — that's "auth required" (gh
234
+ // convention), which would send scripts into an auth-retry loop.
235
+ process.exit(1);
236
+ }
237
+ throw error;
238
+ };
176
239
  // ===== Event Handlers =====
177
240
  handleStateEnter = ({ state }) => {
178
241
  this.progress.enterPhase(state);
@@ -181,39 +244,36 @@ export class CLIAdapter {
181
244
  this.progress.exitPhase(state);
182
245
  };
183
246
  handleAuthSuccess = () => {
184
- clack.log.success('Authenticated');
247
+ ui.log.success('Authenticated');
185
248
  };
186
249
  handleAuthFailure = ({ message }) => {
187
- clack.log.error(`Auth failed: ${message}`);
188
- clack.log.info('Visit https://dashboard.workos.com to verify your account');
250
+ ui.log.error(`Auth failed: ${message}`);
251
+ ui.log.info('Visit https://dashboard.workos.com to verify your account');
189
252
  };
190
253
  handleDetectionComplete = ({ integration }) => {
191
- this.queueableLog(() => clack.log.success(`Detected ${chalk.bold(integration)}`));
254
+ this.queueableLog(() => ui.log.success(`Detected ${chalk.bold(integration)}`));
192
255
  };
193
256
  handleDetectionNone = () => {
194
- this.queueableLog(() => clack.log.warn('Could not detect framework automatically'));
257
+ this.queueableLog(() => ui.log.warn('Could not detect framework automatically'));
195
258
  };
196
259
  handleCredentialsFound = () => {
197
- clack.log.success('Found existing WorkOS credentials in .env.local');
260
+ ui.log.success('Found existing WorkOS credentials in .env.local');
198
261
  };
199
262
  handleEnvScanPrompt = async ({ files }) => {
200
- this.isPromptActive = true;
201
263
  const fileList = files.length === 1 ? files[0] : files.slice(0, 2).join(', ');
202
- const confirmed = await clack.confirm({
264
+ const confirmed = await this.withPromptActive(() => ui.confirm({
203
265
  message: `Found ${fileList}. Check for existing WorkOS credentials?`,
204
266
  initialValue: true,
205
- });
206
- this.isPromptActive = false;
207
- this.flushPendingLogs();
267
+ }));
208
268
  this.sendEvent({
209
- type: clack.isCancel(confirmed) || !confirmed ? 'ENV_SCAN_DECLINED' : 'ENV_SCAN_APPROVED',
269
+ type: ui.isCancel(confirmed) || !confirmed ? 'ENV_SCAN_DECLINED' : 'ENV_SCAN_APPROVED',
210
270
  });
211
271
  };
212
272
  handleDeviceStarted = ({ verificationUri, userCode }) => {
213
- clack.log.info(`\nOpen this URL in your browser:\n`);
273
+ ui.log.info(`\nOpen this URL in your browser:\n`);
214
274
  console.log(` ${chalk.cyan(verificationUri)}`);
215
275
  console.log(`\nEnter code: ${chalk.bold(userCode)}\n`);
216
- this.spinner = clack.spinner();
276
+ this.spinner = ui.spinner();
217
277
  this.spinner.start('Waiting for authentication...');
218
278
  };
219
279
  handleDeviceSuccess = () => {
@@ -223,36 +283,65 @@ export class CLIAdapter {
223
283
  if (this.spinner) {
224
284
  this.spinner.stop('Authenticated');
225
285
  }
226
- this.spinner = clack.spinner();
286
+ this.spinner = ui.spinner();
227
287
  this.spinner.start('Fetching your WorkOS credentials...');
228
288
  };
229
- handleStagingSuccess = () => {
230
- this.stopSpinner('Credentials fetched');
231
- clack.log.success('WorkOS credentials retrieved automatically');
289
+ handleStagingSuccess = ({ source }) => {
290
+ if (source === 'device') {
291
+ this.stopSpinner('Environment ready');
292
+ ui.log.success('Set up a WorkOS environment for this install');
293
+ }
294
+ else if (source === 'stored') {
295
+ this.stopSpinner('Using active environment');
296
+ ui.log.success('Using your active WorkOS environment');
297
+ }
298
+ else {
299
+ this.stopSpinner('Environment ready');
300
+ ui.log.success('Using your WorkOS environment');
301
+ }
232
302
  };
233
303
  handleEnvCredentialsFound = ({ sourcePath }) => {
234
- clack.log.success(`Found existing WorkOS credentials in ${sourcePath}`);
304
+ ui.log.success(`Found existing WorkOS credentials in ${sourcePath}`);
305
+ };
306
+ // Automatic auth / credential fetch can fail and fall back to manual entry.
307
+ // These finalize the spinner with a failure glyph so it isn't left spinning
308
+ // (orphaned) over the manual-credentials prompt that follows.
309
+ handleDeviceError = ({ message }) => {
310
+ this.stopSpinner('Automatic sign-in failed', 1);
311
+ if (this.debug)
312
+ this.debugLog(`[device:error] ${message}`);
313
+ };
314
+ handleDeviceTimeout = () => {
315
+ this.stopSpinner('Sign-in timed out', 1);
316
+ };
317
+ handleStagingError = ({ message }) => {
318
+ this.stopSpinner('Could not fetch WorkOS credentials', 1);
319
+ if (this.debug)
320
+ this.debugLog(`[staging:error] ${message}`);
235
321
  };
236
322
  handleGitDirty = async ({ files }) => {
237
- clack.log.warn('You have uncommitted or untracked files:');
238
- files.slice(0, 5).forEach((f) => clack.log.info(chalk.dim(` ${f}`)));
323
+ ui.log.warn('You have uncommitted or untracked files:');
324
+ files.slice(0, 5).forEach((f) => ui.log.info(chalk.dim(` ${f}`)));
239
325
  if (files.length > 5) {
240
- clack.log.info(chalk.dim(` ... and ${files.length - 5} more`));
326
+ ui.log.info(chalk.dim(` ... and ${files.length - 5} more`));
241
327
  }
242
- this.isPromptActive = true;
243
- const confirmed = await clack.confirm({
328
+ const confirmed = await this.withPromptActive(() => ui.confirm({
244
329
  message: 'Continue anyway?',
245
330
  initialValue: false,
246
- });
247
- this.isPromptActive = false;
248
- this.flushPendingLogs();
331
+ signal: this.promptAbort?.signal,
332
+ }));
249
333
  this.sendEvent({
250
- type: clack.isCancel(confirmed) || !confirmed ? 'GIT_CANCELLED' : 'GIT_CONFIRMED',
334
+ type: ui.isCancel(confirmed) || !confirmed ? 'GIT_CANCELLED' : 'GIT_CONFIRMED',
251
335
  });
252
336
  };
253
337
  handleCredentialsRequest = async ({ requiresApiKey, }) => {
254
- clack.log.step(`Get your credentials from ${chalk.cyan('https://dashboard.workos.com')}`);
255
- const clientId = await clack.text({
338
+ // Guaranteed chokepoint: promptingManual is the fallback for any auth path,
339
+ // so clear any still-running spinner before the prompt opens (defense in
340
+ // depth on top of the device/staging error handlers and withPrompt's pause).
341
+ this.spinner?.clear();
342
+ this.spinner = null;
343
+ ui.log.step(`Get your credentials from ${chalk.cyan('https://dashboard.workos.com')}`);
344
+ const clientId = await ui.text({
256
345
  message: 'Enter your WorkOS Client ID:',
257
346
  placeholder: 'client_...',
258
347
  validate: (value) => {
@@ -265,14 +354,14 @@ export class CLIAdapter {
265
354
  return undefined;
266
355
  },
267
356
  });
268
- if (clack.isCancel(clientId)) {
357
+ if (ui.isCancel(clientId)) {
269
358
  this.sendEvent({ type: 'CANCEL' });
270
359
  return;
271
360
  }
272
361
  let apiKey = '';
273
362
  if (requiresApiKey) {
274
- clack.log.info(chalk.dim('ℹ️ Your API key will be hidden for security and saved to .env.local'));
275
- const apiKeyResult = await clack.password({
363
+ ui.log.info(chalk.dim('ℹ️ Your API key will be hidden for security and saved to .env.local'));
364
+ const apiKeyResult = await ui.password({
276
365
  message: 'Enter your WorkOS API Key:',
277
366
  validate: (value) => {
278
367
  if (!value || value.trim().length === 0) {
@@ -284,14 +373,14 @@ export class CLIAdapter {
284
373
  return undefined;
285
374
  },
286
375
  });
287
- if (clack.isCancel(apiKeyResult)) {
376
+ if (ui.isCancel(apiKeyResult)) {
288
377
  this.sendEvent({ type: 'CANCEL' });
289
378
  return;
290
379
  }
291
380
  apiKey = apiKeyResult;
292
381
  }
293
382
  else {
294
- clack.log.info(chalk.dim('ℹ️ Client-only SDK - API key not required'));
383
+ ui.log.info(chalk.dim('ℹ️ Client-only SDK - API key not required'));
295
384
  }
296
385
  this.sendEvent({
297
386
  type: 'CREDENTIALS_SUBMITTED',
@@ -300,93 +389,131 @@ export class CLIAdapter {
300
389
  });
301
390
  };
302
391
  handleConfigComplete = () => {
303
- clack.log.success('Environment configured');
392
+ ui.log.success('Environment configured');
304
393
  };
305
394
  handleAgentStart = () => {
306
- this.spinner = clack.spinner();
307
- this.spinner.start('Running AI agent...');
308
- // Periodic status updates for long-running operations
309
- let dots = 0;
310
- this.agentUpdateInterval = setInterval(() => {
311
- dots = (dots + 1) % 4;
312
- const dotStr = '.'.repeat(dots + 1);
313
- this.spinner?.message(`Running AI agent${dotStr}`);
314
- }, 2000);
395
+ this.spinner = ui.spinner();
396
+ this.spinner.start(this.lastAgentMessage);
397
+ // No setInterval: ui animates its own frames, and the old 2s reset
398
+ // clobbered the current phase text set by handleAgentProgress.
315
399
  };
316
400
  handleAgentProgress = ({ step, detail }) => {
317
401
  const message = detail ? `${step}: ${detail}` : step;
402
+ this.lastAgentMessage = message;
318
403
  this.spinner?.message(message);
319
404
  };
405
+ /**
406
+ * Render a persistent line above the running spinner: stop the spinner to
407
+ * finalize its line, emit the log, then restart it on the last phase message.
408
+ * Mirrors the existing stop→log and stop→start-new-spinner precedents.
409
+ */
410
+ logAboveSpinner(render) {
411
+ const wasRunning = this.spinner !== null;
412
+ this.spinner?.stop();
413
+ this.spinner = null;
414
+ render();
415
+ if (wasRunning) {
416
+ this.spinner = ui.spinner();
417
+ this.spinner.start(this.lastAgentMessage);
418
+ }
419
+ }
420
+ logFileOp(verb, path) {
421
+ if (path === this.lastFileOp)
422
+ return; // dedupe consecutive same-path ops
423
+ this.lastFileOp = path;
424
+ const rel = relative(process.cwd(), path);
425
+ this.logAboveSpinner(() => ui.log.step(`${verb} ${chalk.dim(rel)}`));
426
+ }
427
+ handleFileWrite = ({ path }) => {
428
+ this.logFileOp('Creating', path);
429
+ };
430
+ handleFileEdit = ({ path }) => {
431
+ this.logFileOp('Editing', path);
432
+ };
433
+ handleAgentTool = ({ detail }) => {
434
+ const cmd = detail.length > 80 ? `${detail.slice(0, 77)}…` : detail;
435
+ this.logAboveSpinner(() => ui.log.step(`Running ${chalk.dim(cmd)}`));
436
+ };
320
437
  handleValidationStart = () => {
321
- this.stopAgentUpdates();
322
438
  this.stopSpinner('Agent completed');
323
439
  };
324
440
  handleValidationIssues = ({ issues }) => {
325
441
  for (const issue of issues) {
326
442
  if (issue.severity === 'error') {
327
- clack.log.error(issue.message);
443
+ ui.log.error(issue.message);
328
444
  }
329
445
  else {
330
- clack.log.warn(issue.message);
446
+ ui.log.warn(issue.message);
331
447
  }
332
448
  if (issue.hint) {
333
- clack.log.info(`Hint: ${issue.hint}`);
449
+ ui.log.info(`Hint: ${issue.hint}`);
334
450
  }
335
451
  }
336
452
  };
337
453
  handleValidationComplete = ({ passed, issueCount }) => {
338
454
  if (passed) {
339
- clack.log.success('Validation passed');
455
+ ui.log.success('Validation passed');
340
456
  }
341
457
  else {
342
- clack.log.warn(`Validation found ${issueCount} issue(s)`);
458
+ ui.log.warn(`Validation found ${issueCount} issue(s)`);
343
459
  }
344
460
  };
345
- handleComplete = ({ success, summary }) => {
346
- this.stopAgentUpdates();
461
+ handleComplete = ({ success, summary, completion }) => {
462
+ // Fires synchronously during the cancelled-state transition (emitCancelled),
463
+ // BEFORE a queued sibling prompt's microtask runs — so aborting here makes a
464
+ // cancelled run's still-queued prompt (e.g. the branch select after a
465
+ // git-dirty "No") open with an already-aborted signal and resolve to CANCEL
466
+ // without ever rendering a now-moot question.
467
+ this.promptAbort?.abort();
347
468
  this.stopSpinner(success ? 'Done' : 'Failed');
348
469
  console.log('');
349
- console.log(renderCompletionSummary(success, summary));
470
+ console.log(renderCompletionSummary(success, summary, completion));
350
471
  console.log('');
351
472
  // When we scaffolded a fresh app, the install ran in the current dir, so
352
473
  // point the user straight at the dev server.
353
474
  if (success && this.scaffolded) {
354
- clack.log.info(`Start your app: ${chalk.cyan(`${this.scaffoldPackageManager} run dev`)}`);
475
+ ui.log.info(`Start your app: ${chalk.cyan(`${this.scaffoldPackageManager} run dev`)}`);
355
476
  }
356
477
  };
357
478
  handleError = ({ message, stack }) => {
358
- this.stopSpinner('Error');
359
- this.stopAgentUpdates();
360
- // Rewrite raw API/SDK errors into user-friendly messages
479
+ this.stopSpinner('Failed', 1);
480
+ // Rewrite raw API/SDK errors into user-friendly messages with a next step.
481
+ // Matching is word-boundary / code-based so 'author' doesn't read as 'auth'
482
+ // and 'Module not found' doesn't read as a missing-directory error.
361
483
  const isServiceError = /\b50[0-9]\b/.test(message) || /server_error|internal_error|overloaded|service.*unavailable/i.test(message);
362
- const isRateLimit = /\b429\b/.test(message) || /rate.limit/i.test(message);
484
+ const isRateLimit = /\b429\b/.test(message) || /\brate.?limit/i.test(message);
363
485
  const isNetworkError = /ECONNREFUSED|ETIMEDOUT|ENOTFOUND|fetch failed/i.test(message);
364
486
  const isProcessExit = /process exited with code/i.test(message);
487
+ const isAuthError = /\b(401|403|unauthorized|forbidden|authentication|authorization)\b/i.test(message);
488
+ const isMissingPath = /\bENOENT\b/.test(message);
365
489
  if (isServiceError) {
366
- clack.log.error('The AI service is temporarily unavailable.');
367
- clack.log.info('This is usually resolved within a few minutes. Please try again shortly.');
490
+ ui.log.error('The AI service is temporarily unavailable.');
491
+ ui.log.info('This is usually resolved within a few minutes. Please try again shortly.');
368
492
  }
369
493
  else if (isRateLimit) {
370
- clack.log.error('The AI service is currently rate-limited.');
371
- clack.log.info('Please wait a minute and try again.');
494
+ ui.log.error('The AI service is currently rate-limited.');
495
+ ui.log.info('Please wait a minute and try again.');
372
496
  }
373
497
  else if (isNetworkError) {
374
- clack.log.error('Could not connect to the AI service.');
375
- clack.log.info('Check your internet connection and try again.');
498
+ ui.log.error('Could not connect to the AI service.');
499
+ ui.log.info('Check your internet connection and try again.');
376
500
  }
377
501
  else if (isProcessExit) {
378
- clack.log.error('The AI agent process exited unexpectedly.');
379
- clack.log.info('Try running again. If this persists, run with --debug for details.');
502
+ ui.log.error('The AI agent process exited unexpectedly.');
503
+ ui.log.info('Try running again. If this persists, run with --debug for details.');
380
504
  }
381
- else {
382
- clack.log.error(message);
505
+ else if (isAuthError) {
506
+ ui.log.error('Authentication failed.');
507
+ ui.log.info(`Try running: ${formatWorkOSCommand('auth logout')} && ${formatWorkOSCommand('install')}`);
383
508
  }
384
- // Add actionable hints for common errors
385
- if (message.includes('authentication') || message.includes('auth')) {
386
- clack.log.info(`Try running: ${formatWorkOSCommand('auth logout')} && ${formatWorkOSCommand('install')}`);
509
+ else if (isMissingPath) {
510
+ ui.log.error(message);
511
+ ui.log.info('Make sure you are running this in your project directory.');
387
512
  }
388
- if (message.includes('ENOENT') || message.includes('not found')) {
389
- clack.log.info('Ensure you are in a project directory');
513
+ else {
514
+ // Unknown error: still give the user somewhere to go next.
515
+ ui.log.error(message);
516
+ ui.log.info(`Re-run with ${chalk.cyan('--debug')} for details, or report it at ${chalk.cyan('https://github.com/workos/cli/issues')}`);
390
517
  }
391
518
  if (stack && this.debug) {
392
519
  this.debugLog(stack);
@@ -395,20 +522,17 @@ export class CLIAdapter {
395
522
  // ===== Scaffold Event Handlers =====
396
523
  handleScaffoldPrompt = async ({ packageManager }) => {
397
524
  this.scaffoldPackageManager = packageManager;
398
- this.isPromptActive = true;
399
- const confirmed = await clack.confirm({
525
+ const confirmed = await this.withPromptActive(() => ui.confirm({
400
526
  message: 'This directory is empty. Scaffold a new Next.js app with AuthKit here?',
401
527
  initialValue: true,
402
- });
403
- this.isPromptActive = false;
404
- this.flushPendingLogs();
528
+ }));
405
529
  this.sendEvent({
406
- type: clack.isCancel(confirmed) || !confirmed ? 'SCAFFOLD_CANCELLED' : 'SCAFFOLD_CONFIRMED',
530
+ type: ui.isCancel(confirmed) || !confirmed ? 'SCAFFOLD_CANCELLED' : 'SCAFFOLD_CONFIRMED',
407
531
  });
408
532
  };
409
533
  handleScaffoldStart = ({ packageManager }) => {
410
534
  this.scaffoldPackageManager = packageManager;
411
- this.spinner = clack.spinner();
535
+ this.spinner = ui.spinner();
412
536
  this.spinner.start(`Scaffolding a new Next.js app with ${packageManager} (this can take a minute)...`);
413
537
  };
414
538
  // create-next-app output is verbose; surface it only under --debug and keep
@@ -425,21 +549,19 @@ export class CLIAdapter {
425
549
  };
426
550
  handleScaffoldFailed = ({ error }) => {
427
551
  this.stopSpinner('Scaffold failed');
428
- clack.log.error(`Could not scaffold the app: ${error}`);
552
+ ui.log.error(`Could not scaffold the app: ${error}`);
429
553
  };
430
554
  handleBranchPrompt = async ({ branch }) => {
431
- this.isPromptActive = true;
432
- const choice = await clack.select({
555
+ const choice = await this.withPromptActive(() => ui.select({
433
556
  message: `You are on ${chalk.bold(branch)}. Create a feature branch?`,
434
557
  options: [
435
558
  { value: 'create', label: 'Create feat/add-workos-authkit' },
436
559
  { value: 'continue', label: 'Continue on current branch' },
437
560
  { value: 'cancel', label: 'Cancel' },
438
561
  ],
439
- });
440
- this.isPromptActive = false;
441
- this.flushPendingLogs();
442
- if (clack.isCancel(choice) || choice === 'cancel') {
562
+ signal: this.promptAbort?.signal,
563
+ }));
564
+ if (ui.isCancel(choice) || choice === 'cancel') {
443
565
  this.sendEvent({ type: 'BRANCH_CANCEL' });
444
566
  }
445
567
  else if (choice === 'create') {
@@ -450,50 +572,44 @@ export class CLIAdapter {
450
572
  }
451
573
  };
452
574
  handleBranchCreated = ({ branch }) => {
453
- this.queueableLog(() => clack.log.success(`Created branch ${chalk.bold(branch)}`));
575
+ this.queueableLog(() => ui.log.success(`Created branch ${chalk.bold(branch)}`));
454
576
  };
455
577
  // ===== Post-install Event Handlers =====
456
578
  handlePostInstallChanges = ({ files }) => {
457
579
  this.debugLog(`Post-install: ${files.length} changed files detected`);
458
580
  };
459
581
  handleCommitPrompt = async () => {
460
- this.isPromptActive = true;
461
- const confirmed = await clack.confirm({
582
+ const confirmed = await this.withPromptActive(() => ui.confirm({
462
583
  message: 'Commit the changes?',
463
584
  initialValue: true,
464
- });
465
- this.isPromptActive = false;
466
- this.flushPendingLogs();
585
+ }));
467
586
  this.sendEvent({
468
- type: clack.isCancel(confirmed) || !confirmed ? 'COMMIT_DECLINED' : 'COMMIT_APPROVED',
587
+ type: ui.isCancel(confirmed) || !confirmed ? 'COMMIT_DECLINED' : 'COMMIT_APPROVED',
469
588
  });
470
589
  };
471
590
  handleCommitGenerating = () => {
472
- this.spinner = clack.spinner();
591
+ this.spinner = ui.spinner();
473
592
  this.spinner.start('Generating commit message...');
474
593
  };
475
594
  handleCommitSuccess = ({ message }) => {
476
595
  this.stopSpinner('Committed');
477
- clack.log.success(`Committed: ${chalk.dim(message)}`);
596
+ ui.log.success(`Committed: ${chalk.dim(message)}`);
478
597
  };
479
598
  handleCommitFailed = ({ error }) => {
480
599
  this.stopSpinner('Commit failed');
481
- clack.log.error(`Commit failed: ${error}`);
600
+ ui.log.error(`Commit failed: ${error}`);
482
601
  };
483
602
  handlePrPrompt = async () => {
484
- this.isPromptActive = true;
485
- const confirmed = await clack.confirm({
603
+ const confirmed = await this.withPromptActive(() => ui.confirm({
486
604
  message: 'Create a pull request?',
487
605
  initialValue: true,
488
- });
489
- this.isPromptActive = false;
490
- this.flushPendingLogs();
606
+ }));
491
607
  this.sendEvent({
492
- type: clack.isCancel(confirmed) || !confirmed ? 'PR_DECLINED' : 'PR_APPROVED',
608
+ type: ui.isCancel(confirmed) || !confirmed ? 'PR_DECLINED' : 'PR_APPROVED',
493
609
  });
494
610
  };
495
611
  handlePrGenerating = () => {
496
- this.spinner = clack.spinner();
612
+ this.spinner = ui.spinner();
497
613
  this.spinner.start('Generating PR description...');
498
614
  };
499
615
  handlePrPushing = () => {
@@ -501,24 +617,24 @@ export class CLIAdapter {
501
617
  this.spinner.message('Pushing to remote...');
502
618
  }
503
619
  else {
504
- this.spinner = clack.spinner();
620
+ this.spinner = ui.spinner();
505
621
  this.spinner.start('Pushing to remote...');
506
622
  }
507
623
  };
508
624
  handlePrSuccess = ({ url }) => {
509
625
  this.stopSpinner('PR created');
510
- clack.log.success(`Pull request created: ${chalk.cyan(url)}`);
626
+ ui.log.success(`Pull request created: ${chalk.cyan(url)}`);
511
627
  };
512
628
  handlePrFailed = ({ error }) => {
513
629
  this.stopSpinner('PR creation failed');
514
- clack.log.error(`PR creation failed: ${error}`);
630
+ ui.log.error(`PR creation failed: ${error}`);
515
631
  };
516
632
  handlePushFailed = ({ error }) => {
517
633
  this.stopSpinner('Push failed');
518
- clack.log.error(`Push failed: ${error}`);
634
+ ui.log.error(`Push failed: ${error}`);
519
635
  };
520
636
  handleManualInstructions = ({ instructions }) => {
521
- clack.log.info('GitHub CLI not found. Manual steps:');
637
+ ui.log.info('GitHub CLI not found. Manual steps:');
522
638
  console.log(chalk.dim(instructions));
523
639
  };
524
640
  }