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,13 +1,13 @@
1
1
  /**
2
2
  * First-run telemetry notice.
3
3
  *
4
- * Prints a one-time, stderr-only box telling the user that anonymous CLI usage
5
- * telemetry is being collected and how to turn it off. Shown at most once ever
6
- * (backed by the persisted `noticeShownAt` timestamp in preferences.json), only
7
- * in interactive human mode, and never on the machine-readable path.
4
+ * Prints a one-time, stderr-only notice telling the user that anonymous CLI
5
+ * usage telemetry is being collected and how to turn it off. Shown at most once
6
+ * ever (backed by the persisted `noticeShownAt` timestamp in preferences.json),
7
+ * only in interactive human mode, and never on the machine-readable path.
8
8
  *
9
9
  * Mirrors the structural pattern of unclaimed-warning.ts: a per-session guard,
10
- * a `!isJsonMode()` gate, the shared renderStderrBox helper, and a never-throws
10
+ * a `!isJsonMode()` gate, the shared renderStderrNotice helper, and a never-throws
11
11
  * contract so it can never block command execution. The one structural
12
12
  * difference is persistence — this notice writes `noticeShownAt` the first time
13
13
  * it actually displays so it never re-shows across runs.
@@ -1,20 +1,20 @@
1
1
  /**
2
2
  * First-run telemetry notice.
3
3
  *
4
- * Prints a one-time, stderr-only box telling the user that anonymous CLI usage
5
- * telemetry is being collected and how to turn it off. Shown at most once ever
6
- * (backed by the persisted `noticeShownAt` timestamp in preferences.json), only
7
- * in interactive human mode, and never on the machine-readable path.
4
+ * Prints a one-time, stderr-only notice telling the user that anonymous CLI
5
+ * usage telemetry is being collected and how to turn it off. Shown at most once
6
+ * ever (backed by the persisted `noticeShownAt` timestamp in preferences.json),
7
+ * only in interactive human mode, and never on the machine-readable path.
8
8
  *
9
9
  * Mirrors the structural pattern of unclaimed-warning.ts: a per-session guard,
10
- * a `!isJsonMode()` gate, the shared renderStderrBox helper, and a never-throws
10
+ * a `!isJsonMode()` gate, the shared renderStderrNotice helper, and a never-throws
11
11
  * contract so it can never block command execution. The one structural
12
12
  * difference is persistence — this notice writes `noticeShownAt` the first time
13
13
  * it actually displays so it never re-shows across runs.
14
14
  */
15
15
  import chalk from 'chalk';
16
16
  import { isJsonMode } from '../utils/output.js';
17
- import { renderStderrBox } from '../utils/box.js';
17
+ import { renderStderrNotice } from '../utils/box.js';
18
18
  import { formatWorkOSCommand } from '../utils/command-invocation.js';
19
19
  import { isNoticeShown, markNoticeShown, isTelemetryOptedOut } from './preferences.js';
20
20
  import { markStartupNoticeShown } from './startup-notice-gate.js';
@@ -38,8 +38,7 @@ export function maybeShowTelemetryNotice() {
38
38
  if (isNoticeShown())
39
39
  return; // already shown once, ever
40
40
  const optOut = chalk.cyan(formatWorkOSCommand('telemetry opt-out'));
41
- const inner = ` ${chalk.cyan('ℹ')} WorkOS collects anonymous CLI usage telemetry. Run ${optOut} to disable it. `;
42
- renderStderrBox(inner, chalk.cyan);
41
+ renderStderrNotice(`${chalk.cyan('ℹ')} ${chalk.dim('Anonymous CLI usage telemetry is on.')} ${chalk.dim('Disable:')} ${optOut}`);
43
42
  // Set the per-session guard and persist ONLY after a successful render, so a
44
43
  // render failure (caught below) lets a later command in this process retry
45
44
  // rather than silently suppressing the notice for the rest of the session.
@@ -1 +1 @@
1
- {"version":3,"file":"telemetry-notice.js","sourceRoot":"","sources":["../../src/lib/telemetry-notice.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AACrE,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AACvF,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAElE,IAAI,gBAAgB,GAAG,KAAK,CAAC;AAE7B;;;;;;;GAOG;AACH,MAAM,UAAU,wBAAwB;IACtC,IAAI,CAAC;QACH,IAAI,gBAAgB;YAAE,OAAO;QAC7B,IAAI,UAAU,EAAE;YAAE,OAAO,CAAC,mEAAmE;QAC7F,IAAI,mBAAmB,EAAE;YAAE,OAAO,CAAC,wCAAwC;QAC3E,IAAI,aAAa,EAAE;YAAE,OAAO,CAAC,2BAA2B;QAExD,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,mBAAmB,CAAC,mBAAmB,CAAC,CAAC,CAAC;QACpE,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,uDAAuD,MAAM,kBAAkB,CAAC;QACjH,eAAe,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QACnC,6EAA6E;QAC7E,2EAA2E;QAC3E,2EAA2E;QAC3E,gBAAgB,GAAG,IAAI,CAAC;QACxB,eAAe,EAAE,CAAC;QAClB,6EAA6E;QAC7E,sBAAsB,EAAE,CAAC;IAC3B,CAAC;IAAC,MAAM,CAAC;QACP,iCAAiC;IACnC,CAAC;AACH,CAAC;AAED,yCAAyC;AACzC,MAAM,UAAU,yBAAyB;IACvC,gBAAgB,GAAG,KAAK,CAAC;AAC3B,CAAC","sourcesContent":["/**\n * First-run telemetry notice.\n *\n * Prints a one-time, stderr-only box telling the user that anonymous CLI usage\n * telemetry is being collected and how to turn it off. Shown at most once ever\n * (backed by the persisted `noticeShownAt` timestamp in preferences.json), only\n * in interactive human mode, and never on the machine-readable path.\n *\n * Mirrors the structural pattern of unclaimed-warning.ts: a per-session guard,\n * a `!isJsonMode()` gate, the shared renderStderrBox helper, and a never-throws\n * contract so it can never block command execution. The one structural\n * difference is persistence — this notice writes `noticeShownAt` the first time\n * it actually displays so it never re-shows across runs.\n */\n\nimport chalk from 'chalk';\nimport { isJsonMode } from '../utils/output.js';\nimport { renderStderrBox } from '../utils/box.js';\nimport { formatWorkOSCommand } from '../utils/command-invocation.js';\nimport { isNoticeShown, markNoticeShown, isTelemetryOptedOut } from './preferences.js';\nimport { markStartupNoticeShown } from './startup-notice-gate.js';\n\nlet shownThisSession = false;\n\n/**\n * Show the first-run telemetry notice if it has never been displayed.\n *\n * Gate order is load-bearing: every suppression check runs BEFORE\n * markNoticeShown(), so a non-human first run (--json / piped / CI) never\n * consumes the one-time display. The flag is set only when the box is actually\n * rendered, so a real human eventually sees it. Never throws.\n */\nexport function maybeShowTelemetryNotice(): void {\n try {\n if (shownThisSession) return;\n if (isJsonMode()) return; // suppress in --json / non-TTY / CI (output auto-switches to json)\n if (isTelemetryOptedOut()) return; // already opted out — nothing to inform\n if (isNoticeShown()) return; // already shown once, ever\n\n const optOut = chalk.cyan(formatWorkOSCommand('telemetry opt-out'));\n const inner = ` ${chalk.cyan('ℹ')} WorkOS collects anonymous CLI usage telemetry. Run ${optOut} to disable it. `;\n renderStderrBox(inner, chalk.cyan);\n // Set the per-session guard and persist ONLY after a successful render, so a\n // render failure (caught below) lets a later command in this process retry\n // rather than silently suppressing the notice for the rest of the session.\n shownThisSession = true;\n markNoticeShown();\n // Claim the one-notice-per-run slot so the lower-priority MCP banner defers.\n markStartupNoticeShown();\n } catch {\n // Never block command execution.\n }\n}\n\n/** Reset session state (for testing). */\nexport function resetTelemetryNoticeState(): void {\n shownThisSession = false;\n}\n"]}
1
+ {"version":3,"file":"telemetry-notice.js","sourceRoot":"","sources":["../../src/lib/telemetry-notice.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AACrE,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AACvF,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAElE,IAAI,gBAAgB,GAAG,KAAK,CAAC;AAE7B;;;;;;;GAOG;AACH,MAAM,UAAU,wBAAwB;IACtC,IAAI,CAAC;QACH,IAAI,gBAAgB;YAAE,OAAO;QAC7B,IAAI,UAAU,EAAE;YAAE,OAAO,CAAC,mEAAmE;QAC7F,IAAI,mBAAmB,EAAE;YAAE,OAAO,CAAC,wCAAwC;QAC3E,IAAI,aAAa,EAAE;YAAE,OAAO,CAAC,2BAA2B;QAExD,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,mBAAmB,CAAC,mBAAmB,CAAC,CAAC,CAAC;QACpE,kBAAkB,CAChB,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,GAAG,CAAC,sCAAsC,CAAC,IAAI,KAAK,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,MAAM,EAAE,CAC7G,CAAC;QACF,6EAA6E;QAC7E,2EAA2E;QAC3E,2EAA2E;QAC3E,gBAAgB,GAAG,IAAI,CAAC;QACxB,eAAe,EAAE,CAAC;QAClB,6EAA6E;QAC7E,sBAAsB,EAAE,CAAC;IAC3B,CAAC;IAAC,MAAM,CAAC;QACP,iCAAiC;IACnC,CAAC;AACH,CAAC;AAED,yCAAyC;AACzC,MAAM,UAAU,yBAAyB;IACvC,gBAAgB,GAAG,KAAK,CAAC;AAC3B,CAAC","sourcesContent":["/**\n * First-run telemetry notice.\n *\n * Prints a one-time, stderr-only notice telling the user that anonymous CLI\n * usage telemetry is being collected and how to turn it off. Shown at most once\n * ever (backed by the persisted `noticeShownAt` timestamp in preferences.json),\n * only in interactive human mode, and never on the machine-readable path.\n *\n * Mirrors the structural pattern of unclaimed-warning.ts: a per-session guard,\n * a `!isJsonMode()` gate, the shared renderStderrNotice helper, and a never-throws\n * contract so it can never block command execution. The one structural\n * difference is persistence — this notice writes `noticeShownAt` the first time\n * it actually displays so it never re-shows across runs.\n */\n\nimport chalk from 'chalk';\nimport { isJsonMode } from '../utils/output.js';\nimport { renderStderrNotice } from '../utils/box.js';\nimport { formatWorkOSCommand } from '../utils/command-invocation.js';\nimport { isNoticeShown, markNoticeShown, isTelemetryOptedOut } from './preferences.js';\nimport { markStartupNoticeShown } from './startup-notice-gate.js';\n\nlet shownThisSession = false;\n\n/**\n * Show the first-run telemetry notice if it has never been displayed.\n *\n * Gate order is load-bearing: every suppression check runs BEFORE\n * markNoticeShown(), so a non-human first run (--json / piped / CI) never\n * consumes the one-time display. The flag is set only when the box is actually\n * rendered, so a real human eventually sees it. Never throws.\n */\nexport function maybeShowTelemetryNotice(): void {\n try {\n if (shownThisSession) return;\n if (isJsonMode()) return; // suppress in --json / non-TTY / CI (output auto-switches to json)\n if (isTelemetryOptedOut()) return; // already opted out — nothing to inform\n if (isNoticeShown()) return; // already shown once, ever\n\n const optOut = chalk.cyan(formatWorkOSCommand('telemetry opt-out'));\n renderStderrNotice(\n `${chalk.cyan('ℹ')} ${chalk.dim('Anonymous CLI usage telemetry is on.')} ${chalk.dim('Disable:')} ${optOut}`,\n );\n // Set the per-session guard and persist ONLY after a successful render, so a\n // render failure (caught below) lets a later command in this process retry\n // rather than silently suppressing the notice for the rest of the session.\n shownThisSession = true;\n markNoticeShown();\n // Claim the one-notice-per-run slot so the lower-priority MCP banner defers.\n markStartupNoticeShown();\n } catch {\n // Never block command execution.\n }\n}\n\n/** Reset session state (for testing). */\nexport function resetTelemetryNoticeState(): void {\n shownThisSession = false;\n}\n"]}
@@ -7,11 +7,12 @@
7
7
  */
8
8
  import chalk from 'chalk';
9
9
  import { provisionUnclaimedEnvironment, UnclaimedEnvApiError } from './unclaimed-env-api.js';
10
- import { getConfig, saveConfig, getActiveEnvironment } from './config-store.js';
10
+ import { getConfig, saveConfig, getActiveEnvironment, freshEnvKey } from './config-store.js';
11
11
  import { writeCredentialsEnv } from './env-writer.js';
12
12
  import { logInfo, logError } from '../utils/debug.js';
13
- import { renderStderrBox } from '../utils/box.js';
14
- import clack from '../utils/clack.js';
13
+ import { renderStderrNotice } from '../utils/box.js';
14
+ import ui from '../utils/ui.js';
15
+ import { formatWorkOSCommand } from '../utils/command-invocation.js';
15
16
  /**
16
17
  * Try to provision an unclaimed environment. Non-fatal — returns true on success,
17
18
  * false on any failure.
@@ -36,27 +37,29 @@ export async function tryProvisionUnclaimedEnv(options) {
36
37
  envVars[key] = options.redirectUri;
37
38
  }
38
39
  writeCredentialsEnv(options.installDir, envVars);
39
- // Save to config store (after .env.local succeeds)
40
+ // Save to config store (after .env.local succeeds). A fresh key so a repeated
41
+ // provision never clobbers an earlier env's claim token (unrecoverable — it
42
+ // lives only in this config).
40
43
  const config = getConfig() ?? { environments: {} };
41
- config.environments['unclaimed'] = {
42
- name: 'unclaimed',
44
+ const key = freshEnvKey(config, 'unclaimed');
45
+ config.environments[key] = {
46
+ name: key,
43
47
  type: 'unclaimed',
44
48
  apiKey: result.apiKey,
45
49
  clientId: result.clientId,
46
50
  claimToken: result.claimToken,
47
51
  };
48
- config.activeEnvironment = 'unclaimed';
52
+ config.activeEnvironment = key;
49
53
  saveConfig(config);
50
54
  // Verify config persisted — critical for `workos env claim` in a later process
51
55
  const readBack = getActiveEnvironment();
52
56
  if (!readBack || readBack.type !== 'unclaimed') {
53
57
  logError('[unclaimed-env-provision] Config read-back failed after save — claim token may not persist');
54
- clack.log.warn('Environment provisioned but config storage may be unreliable. Falling back to login...');
58
+ ui.log.warn('Environment provisioned but config storage may be unreliable. Falling back to login...');
55
59
  return false;
56
60
  }
57
61
  logInfo('[unclaimed-env-provision] Unclaimed environment provisioned and saved');
58
- const inner = ` ✓ ${chalk.green('Environment provisioned')} — Run ${chalk.cyan('workos env claim')} to keep it. `;
59
- renderStderrBox(inner, chalk.green);
62
+ renderStderrNotice(`${chalk.green('')} ${chalk.bold('Environment provisioned')} ${chalk.dim('credentials saved to your project')}`, `${chalk.dim('Run')} ${chalk.bold.cyan(formatWorkOSCommand('env claim'))} ${chalk.dim('to link it to your account.')}`);
60
63
  return true;
61
64
  }
62
65
  catch (error) {
@@ -64,12 +67,12 @@ export async function tryProvisionUnclaimedEnv(options) {
64
67
  logError('[unclaimed-env-provision] Failed:', message);
65
68
  if (error instanceof UnclaimedEnvApiError) {
66
69
  if (error.statusCode === 429) {
67
- clack.log.warn('WorkOS is busy, falling back to login...');
70
+ ui.log.warn('WorkOS is busy, falling back to login...');
68
71
  }
69
72
  }
70
73
  else {
71
74
  // Non-API errors (filesystem, keyring) are unexpected — surface to user
72
- clack.log.warn(`Could not set up environment: ${message}. Falling back to login...`);
75
+ ui.log.warn(`Could not set up environment: ${message}. Falling back to login...`);
73
76
  }
74
77
  return false;
75
78
  }
@@ -1 +1 @@
1
- {"version":3,"file":"unclaimed-env-provision.js","sourceRoot":"","sources":["../../src/lib/unclaimed-env-provision.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Unclaimed environment provisioning helper.\n *\n * Calls the unclaimed env API, saves credentials to config store as type 'unclaimed',\n * and returns whether provisioning succeeded. Non-fatal — wraps everything in\n * try/catch so install flow can fall back to login.\n */\n\nimport chalk from 'chalk';\nimport { provisionUnclaimedEnvironment, UnclaimedEnvApiError } from './unclaimed-env-api.js';\nimport { getConfig, saveConfig, getActiveEnvironment } from './config-store.js';\nimport type { CliConfig } from './config-store.js';\nimport { writeCredentialsEnv } from './env-writer.js';\nimport { logInfo, logError } from '../utils/debug.js';\nimport { renderStderrBox } from '../utils/box.js';\nimport clack from '../utils/clack.js';\n\nexport interface UnclaimedEnvProvisionOptions {\n installDir: string;\n /** Redirect URI key name varies by framework */\n redirectUriKey?: string;\n /** Redirect URI value */\n redirectUri?: string;\n}\n\n/**\n * Try to provision an unclaimed environment. Non-fatal — returns true on success,\n * false on any failure.\n *\n * On success:\n * - Saves environment to config store as type 'unclaimed'\n * - Sets it as active environment\n * - Writes credentials (including cookie password and claim token) to .env.local\n */\nexport async function tryProvisionUnclaimedEnv(options: UnclaimedEnvProvisionOptions): Promise<boolean> {\n try {\n logInfo('[unclaimed-env-provision] Attempting unclaimed environment provisioning');\n\n const result = await provisionUnclaimedEnvironment();\n\n // Write .env.local first — if this fails, config stays clean (no orphan entries)\n const envVars: Record<string, string> = {\n WORKOS_API_KEY: result.apiKey,\n WORKOS_CLIENT_ID: result.clientId,\n WORKOS_CLAIM_TOKEN: result.claimToken,\n };\n\n if (options.redirectUri) {\n const key = options.redirectUriKey ?? 'WORKOS_REDIRECT_URI';\n envVars[key] = options.redirectUri;\n }\n\n writeCredentialsEnv(options.installDir, envVars);\n\n // Save to config store (after .env.local succeeds)\n const config: CliConfig = getConfig() ?? { environments: {} };\n config.environments['unclaimed'] = {\n name: 'unclaimed',\n type: 'unclaimed',\n apiKey: result.apiKey,\n clientId: result.clientId,\n claimToken: result.claimToken,\n };\n config.activeEnvironment = 'unclaimed';\n saveConfig(config);\n\n // Verify config persisted — critical for `workos env claim` in a later process\n const readBack = getActiveEnvironment();\n if (!readBack || readBack.type !== 'unclaimed') {\n logError('[unclaimed-env-provision] Config read-back failed after save — claim token may not persist');\n clack.log.warn('Environment provisioned but config storage may be unreliable. Falling back to login...');\n return false;\n }\n\n logInfo('[unclaimed-env-provision] Unclaimed environment provisioned and saved');\n const inner = ` ✓ ${chalk.green('Environment provisioned')} — Run ${chalk.cyan('workos env claim')} to keep it. `;\n renderStderrBox(inner, chalk.green);\n\n return true;\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error';\n logError('[unclaimed-env-provision] Failed:', message);\n\n if (error instanceof UnclaimedEnvApiError) {\n if (error.statusCode === 429) {\n clack.log.warn('WorkOS is busy, falling back to login...');\n }\n } else {\n // Non-API errors (filesystem, keyring) are unexpected — surface to user\n clack.log.warn(`Could not set up environment: ${message}. Falling back to login...`);\n }\n\n return false;\n }\n}\n"]}
1
+ {"version":3,"file":"unclaimed-env-provision.js","sourceRoot":"","sources":["../../src/lib/unclaimed-env-provision.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Unclaimed environment provisioning helper.\n *\n * Calls the unclaimed env API, saves credentials to config store as type 'unclaimed',\n * and returns whether provisioning succeeded. Non-fatal — wraps everything in\n * try/catch so install flow can fall back to login.\n */\n\nimport chalk from 'chalk';\nimport { provisionUnclaimedEnvironment, UnclaimedEnvApiError } from './unclaimed-env-api.js';\nimport { getConfig, saveConfig, getActiveEnvironment, freshEnvKey } from './config-store.js';\nimport type { CliConfig } from './config-store.js';\nimport { writeCredentialsEnv } from './env-writer.js';\nimport { logInfo, logError } from '../utils/debug.js';\nimport { renderStderrNotice } from '../utils/box.js';\nimport ui from '../utils/ui.js';\nimport { formatWorkOSCommand } from '../utils/command-invocation.js';\n\nexport interface UnclaimedEnvProvisionOptions {\n installDir: string;\n /** Redirect URI key name varies by framework */\n redirectUriKey?: string;\n /** Redirect URI value */\n redirectUri?: string;\n}\n\n/**\n * Try to provision an unclaimed environment. Non-fatal — returns true on success,\n * false on any failure.\n *\n * On success:\n * - Saves environment to config store as type 'unclaimed'\n * - Sets it as active environment\n * - Writes credentials (including cookie password and claim token) to .env.local\n */\nexport async function tryProvisionUnclaimedEnv(options: UnclaimedEnvProvisionOptions): Promise<boolean> {\n try {\n logInfo('[unclaimed-env-provision] Attempting unclaimed environment provisioning');\n\n const result = await provisionUnclaimedEnvironment();\n\n // Write .env.local first — if this fails, config stays clean (no orphan entries)\n const envVars: Record<string, string> = {\n WORKOS_API_KEY: result.apiKey,\n WORKOS_CLIENT_ID: result.clientId,\n WORKOS_CLAIM_TOKEN: result.claimToken,\n };\n\n if (options.redirectUri) {\n const key = options.redirectUriKey ?? 'WORKOS_REDIRECT_URI';\n envVars[key] = options.redirectUri;\n }\n\n writeCredentialsEnv(options.installDir, envVars);\n\n // Save to config store (after .env.local succeeds). A fresh key so a repeated\n // provision never clobbers an earlier env's claim token (unrecoverable — it\n // lives only in this config).\n const config: CliConfig = getConfig() ?? { environments: {} };\n const key = freshEnvKey(config, 'unclaimed');\n config.environments[key] = {\n name: key,\n type: 'unclaimed',\n apiKey: result.apiKey,\n clientId: result.clientId,\n claimToken: result.claimToken,\n };\n config.activeEnvironment = key;\n saveConfig(config);\n\n // Verify config persisted — critical for `workos env claim` in a later process\n const readBack = getActiveEnvironment();\n if (!readBack || readBack.type !== 'unclaimed') {\n logError('[unclaimed-env-provision] Config read-back failed after save — claim token may not persist');\n ui.log.warn('Environment provisioned but config storage may be unreliable. Falling back to login...');\n return false;\n }\n\n logInfo('[unclaimed-env-provision] Unclaimed environment provisioned and saved');\n renderStderrNotice(\n `${chalk.green('')} ${chalk.bold('Environment provisioned')} ${chalk.dim('credentials saved to your project')}`,\n `${chalk.dim('Run')} ${chalk.bold.cyan(formatWorkOSCommand('env claim'))} ${chalk.dim('to link it to your account.')}`,\n );\n\n return true;\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error';\n logError('[unclaimed-env-provision] Failed:', message);\n\n if (error instanceof UnclaimedEnvApiError) {\n if (error.statusCode === 429) {\n ui.log.warn('WorkOS is busy, falling back to login...');\n }\n } else {\n // Non-API errors (filesystem, keyring) are unexpected — surface to user\n ui.log.warn(`Could not set up environment: ${message}. Falling back to login...`);\n }\n\n return false;\n }\n}\n"]}
@@ -11,8 +11,10 @@ import { getActiveEnvironment, isUnclaimedEnvironment, markEnvironmentClaimed }
11
11
  import { createClaimNonce, UnclaimedEnvApiError } from './unclaimed-env-api.js';
12
12
  import { logError, logInfo } from '../utils/debug.js';
13
13
  import { isJsonMode } from '../utils/output.js';
14
- import { renderStderrBox } from '../utils/box.js';
14
+ import { renderStderrNotice } from '../utils/box.js';
15
+ import { pill } from '../utils/ui.js';
15
16
  import { markStartupNoticeShown } from './startup-notice-gate.js';
17
+ import { formatWorkOSCommand } from '../utils/command-invocation.js';
16
18
  let warningShownThisSession = false;
17
19
  let claimCheckDoneThisSession = false;
18
20
  /**
@@ -58,8 +60,7 @@ export async function warnIfUnclaimed() {
58
60
  return;
59
61
  warningShownThisSession = true;
60
62
  if (!isJsonMode()) {
61
- const inner = ` ${chalk.yellow('Unclaimed environment')} — Run ${chalk.cyan('workos env claim')} to keep your data. `;
62
- renderStderrBox(inner, chalk.yellow);
63
+ renderStderrNotice(`${pill('WARN', 'warn')} ${chalk.bold('Unclaimed environment')} ${chalk.dim('not linked to your account')}`, `${chalk.dim('Run')} ${chalk.bold.cyan(formatWorkOSCommand('env claim'))} ${chalk.dim('to save it — its credentials live only on this machine.')}`);
63
64
  // Claim the one-notice-per-run slot so the lower-priority MCP banner defers.
64
65
  markStartupNoticeShown();
65
66
  }
@@ -1 +1 @@
1
- {"version":3,"file":"unclaimed-warning.js","sourceRoot":"","sources":["../../src/lib/unclaimed-warning.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,oBAAoB,EAAE,sBAAsB,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AACzG,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAChF,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAElE,IAAI,uBAAuB,GAAG,KAAK,CAAC;AACpC,IAAI,yBAAyB,GAAG,KAAK,CAAC;AAEtC;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe;IACnC,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,oBAAoB,EAAE,CAAC;QACnC,IAAI,CAAC,GAAG,IAAI,CAAC,sBAAsB,CAAC,GAAG,CAAC;YAAE,OAAO;QAEjD,2DAA2D;QAC3D,2EAA2E;QAC3E,IAAI,CAAC,yBAAyB,EAAE,CAAC;YAC/B,yBAAyB,GAAG,IAAI,CAAC;YACjC,IAAI,CAAC;gBACH,MAAM,MAAM,GAAG,MAAM,gBAAgB,CAAC,GAAG,CAAC,QAAQ,EAAE,GAAG,CAAC,UAAU,CAAC,CAAC;gBACpE,IAAI,MAAM,CAAC,cAAc,EAAE,CAAC;oBAC1B,sBAAsB,EAAE,CAAC;oBACzB,OAAO,CAAC,wEAAwE,CAAC,CAAC;oBAClF,OAAO;gBACT,CAAC;YACH,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,IAAI,KAAK,YAAY,oBAAoB,IAAI,KAAK,CAAC,UAAU,KAAK,GAAG,EAAE,CAAC;oBACtE,yEAAyE;oBACzE,yDAAyD;oBACzD,sBAAsB,EAAE,CAAC;oBACzB,OAAO,CAAC,0DAA0D,CAAC,CAAC;oBACpE,OAAO;gBACT,CAAC;gBACD,wEAAwE;gBACxE,IAAI,KAAK,YAAY,oBAAoB,EAAE,CAAC;oBAC1C,QAAQ,CAAC,yCAAyC,EAAE,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;gBACvF,CAAC;qBAAM,CAAC;oBACN,QAAQ,CAAC,yCAAyC,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;gBAC9G,CAAC;YACH,CAAC;QACH,CAAC;QAED,gCAAgC;QAChC,IAAI,uBAAuB;YAAE,OAAO;QACpC,uBAAuB,GAAG,IAAI,CAAC;QAE/B,IAAI,CAAC,UAAU,EAAE,EAAE,CAAC;YAClB,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,MAAM,CAAC,yBAAyB,CAAC,UAAU,KAAK,CAAC,IAAI,CAAC,kBAAkB,CAAC,sBAAsB,CAAC;YACxH,eAAe,CAAC,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;YACrC,6EAA6E;YAC7E,sBAAsB,EAAE,CAAC;QAC3B,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,yDAAyD;QACzD,QAAQ,CAAC,uCAAuC,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;IAC5G,CAAC;AACH,CAAC;AAED,wCAAwC;AACxC,MAAM,UAAU,0BAA0B;IACxC,uBAAuB,GAAG,KAAK,CAAC;IAChC,yBAAyB,GAAG,KAAK,CAAC;AACpC,CAAC","sourcesContent":["/**\n * Unclaimed environment warning module.\n *\n * Shows a one-line stderr warning when the active environment is unclaimed.\n * On first run, checks if the environment was claimed externally (e.g. via\n * browser) and updates the local config if so.\n * Never throws — all errors are caught to avoid blocking management commands.\n */\n\nimport chalk from 'chalk';\nimport { getActiveEnvironment, isUnclaimedEnvironment, markEnvironmentClaimed } from './config-store.js';\nimport { createClaimNonce, UnclaimedEnvApiError } from './unclaimed-env-api.js';\nimport { logError, logInfo } from '../utils/debug.js';\nimport { isJsonMode } from '../utils/output.js';\nimport { renderStderrBox } from '../utils/box.js';\nimport { markStartupNoticeShown } from './startup-notice-gate.js';\n\nlet warningShownThisSession = false;\nlet claimCheckDoneThisSession = false;\n\n/**\n * Show a warning if the active environment is unclaimed.\n * Non-blocking — never throws.\n */\nexport async function warnIfUnclaimed(): Promise<void> {\n try {\n const env = getActiveEnvironment();\n if (!env || !isUnclaimedEnvironment(env)) return;\n\n // Check once per session if the env was claimed externally\n // claimToken and clientId guaranteed present by UnclaimedEnvironmentConfig\n if (!claimCheckDoneThisSession) {\n claimCheckDoneThisSession = true;\n try {\n const result = await createClaimNonce(env.clientId, env.claimToken);\n if (result.alreadyClaimed) {\n markEnvironmentClaimed();\n logInfo('[unclaimed-warning] Environment was claimed externally, config updated');\n return;\n }\n } catch (error) {\n if (error instanceof UnclaimedEnvApiError && error.statusCode === 401) {\n // 401 likely means the claim token was invalidated after the environment\n // was claimed. We assume claimed and promote to sandbox.\n markEnvironmentClaimed();\n logInfo('[unclaimed-warning] Claim token invalid/expired, removed');\n return;\n }\n // Log non-401 errors for diagnostics, then fall through to show warning\n if (error instanceof UnclaimedEnvApiError) {\n logError('[unclaimed-warning] Claim check failed:', error.statusCode, error.message);\n } else {\n logError('[unclaimed-warning] Claim check failed:', error instanceof Error ? error.message : String(error));\n }\n }\n }\n\n // Show warning once per session\n if (warningShownThisSession) return;\n warningShownThisSession = true;\n\n if (!isJsonMode()) {\n const inner = ` ${chalk.yellow('Unclaimed environment')} — Run ${chalk.cyan('workos env claim')} to keep your data. `;\n renderStderrBox(inner, chalk.yellow);\n // Claim the one-notice-per-run slot so the lower-priority MCP banner defers.\n markStartupNoticeShown();\n }\n } catch (error) {\n // Never block command execution, but log for diagnostics\n logError('[unclaimed-warning] Unexpected error:', error instanceof Error ? error.message : String(error));\n }\n}\n\n/** Reset session state (for testing) */\nexport function resetUnclaimedWarningState(): void {\n warningShownThisSession = false;\n claimCheckDoneThisSession = false;\n}\n"]}
1
+ {"version":3,"file":"unclaimed-warning.js","sourceRoot":"","sources":["../../src/lib/unclaimed-warning.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Unclaimed environment warning module.\n *\n * Shows a one-line stderr warning when the active environment is unclaimed.\n * On first run, checks if the environment was claimed externally (e.g. via\n * browser) and updates the local config if so.\n * Never throws — all errors are caught to avoid blocking management commands.\n */\n\nimport chalk from 'chalk';\nimport { getActiveEnvironment, isUnclaimedEnvironment, markEnvironmentClaimed } from './config-store.js';\nimport { createClaimNonce, UnclaimedEnvApiError } from './unclaimed-env-api.js';\nimport { logError, logInfo } from '../utils/debug.js';\nimport { isJsonMode } from '../utils/output.js';\nimport { renderStderrNotice } from '../utils/box.js';\nimport { pill } from '../utils/ui.js';\nimport { markStartupNoticeShown } from './startup-notice-gate.js';\nimport { formatWorkOSCommand } from '../utils/command-invocation.js';\n\nlet warningShownThisSession = false;\nlet claimCheckDoneThisSession = false;\n\n/**\n * Show a warning if the active environment is unclaimed.\n * Non-blocking — never throws.\n */\nexport async function warnIfUnclaimed(): Promise<void> {\n try {\n const env = getActiveEnvironment();\n if (!env || !isUnclaimedEnvironment(env)) return;\n\n // Check once per session if the env was claimed externally\n // claimToken and clientId guaranteed present by UnclaimedEnvironmentConfig\n if (!claimCheckDoneThisSession) {\n claimCheckDoneThisSession = true;\n try {\n const result = await createClaimNonce(env.clientId, env.claimToken);\n if (result.alreadyClaimed) {\n markEnvironmentClaimed();\n logInfo('[unclaimed-warning] Environment was claimed externally, config updated');\n return;\n }\n } catch (error) {\n if (error instanceof UnclaimedEnvApiError && error.statusCode === 401) {\n // 401 likely means the claim token was invalidated after the environment\n // was claimed. We assume claimed and promote to sandbox.\n markEnvironmentClaimed();\n logInfo('[unclaimed-warning] Claim token invalid/expired, removed');\n return;\n }\n // Log non-401 errors for diagnostics, then fall through to show warning\n if (error instanceof UnclaimedEnvApiError) {\n logError('[unclaimed-warning] Claim check failed:', error.statusCode, error.message);\n } else {\n logError('[unclaimed-warning] Claim check failed:', error instanceof Error ? error.message : String(error));\n }\n }\n }\n\n // Show warning once per session\n if (warningShownThisSession) return;\n warningShownThisSession = true;\n\n if (!isJsonMode()) {\n renderStderrNotice(\n `${pill('WARN', 'warn')} ${chalk.bold('Unclaimed environment')} ${chalk.dim('not linked to your account')}`,\n `${chalk.dim('Run')} ${chalk.bold.cyan(formatWorkOSCommand('env claim'))} ${chalk.dim('to save it its credentials live only on this machine.')}`,\n );\n // Claim the one-notice-per-run slot so the lower-priority MCP banner defers.\n markStartupNoticeShown();\n }\n } catch (error) {\n // Never block command execution, but log for diagnostics\n logError('[unclaimed-warning] Unexpected error:', error instanceof Error ? error.message : String(error));\n }\n}\n\n/** Reset session state (for testing) */\nexport function resetUnclaimedWarningState(): void {\n warningShownThisSession = false;\n claimCheckDoneThisSession = false;\n}\n"]}
@@ -1,4 +1,5 @@
1
1
  import { checkAuthPatterns } from '../../doctor/checks/auth-patterns.js';
2
+ import { formatWorkOSCommand } from '../../utils/command-invocation.js';
2
3
  /**
3
4
  * The "security subset" of `workos doctor`'s auth-pattern checks that the
4
5
  * installer enforces. These are the patterns that are *unsafe* or *leak secrets*
@@ -99,7 +100,7 @@ export function formatBlockingSecurityError(blocking) {
99
100
  '',
100
101
  ...lines,
101
102
  '',
102
- 'Fix the issues above (or run `workos doctor` for details and remediation) and re-run the installer.',
103
+ `Fix the issues above (or run \`${formatWorkOSCommand('doctor')}\` for details and remediation) and re-run the installer.`,
103
104
  ].join('\n');
104
105
  }
105
106
  //# sourceMappingURL=security-checks.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"security-checks.js","sourceRoot":"","sources":["../../../src/lib/validation/security-checks.ts"],"names":[],"mappings":"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","sourcesContent":["import { checkAuthPatterns } from '../../doctor/checks/auth-patterns.js';\nimport type { AuthPatternFinding, FrameworkInfo, EnvironmentInfo, SdkInfo } from '../../doctor/types.js';\nimport type { ValidationIssue } from './types.js';\n\n/**\n * The \"security subset\" of `workos doctor`'s auth-pattern checks that the\n * installer enforces. These are the patterns that are *unsafe* or *leak secrets*\n * (an unsafe GET sign-out, an API key in client env/source, an ungitignored\n * .env). Completeness checks (missing middleware/callback/provider) are\n * intentionally excluded here — `validateInstallation` already covers those, and\n * they carry higher false-positive risk than we want gating install success.\n *\n * Keep in sync with the codes emitted by `src/doctor/checks/auth-patterns.ts`.\n */\nconst SECURITY_FINDING_CODES = new Set<string>([\n 'SIGNOUT_GET_HANDLER',\n 'SIGNOUT_LINK_PREFETCH',\n 'API_KEY_LEAKED_TO_CLIENT',\n 'API_KEY_IN_SOURCE',\n 'ENV_FILE_NOT_GITIGNORED',\n 'MIXED_ENVIRONMENT',\n]);\n\n/** Map an installer integration id to the framework name doctor's checks expect. */\nconst INTEGRATION_FRAMEWORK_NAME: Record<string, string> = {\n nextjs: 'Next.js',\n 'react-router': 'React Router',\n 'tanstack-start': 'TanStack Start',\n};\n\nexport interface SecurityCheckResult {\n /** All security-class findings for this install (errors + warnings). */\n findings: AuthPatternFinding[];\n /**\n * Error-severity findings that must block a successful install. Empty when the\n * install is secure; a non-empty list means install should not report success.\n */\n blocking: AuthPatternFinding[];\n}\n\n/**\n * Run the security subset of doctor's auth-pattern checks against an install\n * directory. Pure file inspection — no network — so it is safe to call both\n * inside the installer's self-correction loop and as the final pre-success gate.\n *\n * This closes the install-validate ↔ doctor gap: previously install could report\n * `success: true` while `workos doctor` immediately found a security hole,\n * because neither the retry loop nor `validateInstallation` ran these checks.\n */\nexport async function runInstallSecurityChecks(integration: string, installDir: string): Promise<SecurityCheckResult> {\n const framework: FrameworkInfo = {\n name: INTEGRATION_FRAMEWORK_NAME[integration] ?? null,\n version: null,\n };\n // checkAuthPatterns loads .env files itself; these structs only satisfy the\n // shape its non-Next.js checks read from.\n const environment: EnvironmentInfo = {\n apiKeyConfigured: false,\n apiKeyType: null,\n clientId: null,\n redirectUri: null,\n cookieDomain: null,\n baseUrl: null,\n };\n const sdk: SdkInfo = {\n name: null,\n version: null,\n latest: null,\n outdated: false,\n isAuthKit: false,\n language: 'javascript',\n };\n\n const result = await checkAuthPatterns({ installDir }, framework, environment, sdk);\n const findings = result.findings.filter((f) => SECURITY_FINDING_CODES.has(f.code));\n const blocking = findings.filter((f) => f.severity === 'error');\n return { findings, blocking };\n}\n\n/** Convert security findings into ValidationIssues for the emitter/report surfaces. */\nexport function securityFindingsToIssues(findings: AuthPatternFinding[]): ValidationIssue[] {\n return findings.map((f) => ({\n type: 'pattern',\n severity: f.severity,\n message: f.filePath ? `${f.message} (${f.filePath})` : f.message,\n hint: f.remediation,\n }));\n}\n\n/**\n * Build an agent correction prompt from security findings so the installer's\n * self-correction loop fixes them before declaring success. Returns an empty\n * string when there is nothing to correct.\n */\nexport function formatSecurityFindingsForAgent(findings: AuthPatternFinding[]): string {\n if (findings.length === 0) return '';\n const lines = findings.map((f) => {\n const loc = f.filePath ? ` in ${f.filePath}` : '';\n const fix = f.remediation ? ` Fix: ${f.remediation}` : '';\n return `- [${f.severity}] ${f.message}${loc}.${fix}`;\n });\n return `Security checks found issues that must be fixed:\\n\\n${lines.join('\\n')}\\n\\nApply the fixes above, then make sure the project still builds.`;\n}\n\n/**\n * Build the error message thrown when error-severity security findings survive\n * the installer's retries — the message that turns a silent insecure \"success\"\n * into a visible failure.\n */\nexport function formatBlockingSecurityError(blocking: AuthPatternFinding[]): string {\n const lines = blocking.map((f) => {\n const loc = f.filePath ? ` (${f.filePath})` : '';\n return ` • ${f.code}: ${f.message}${loc}`;\n });\n return [\n 'Installation produced insecure code that could not be auto-corrected:',\n '',\n ...lines,\n '',\n 'Fix the issues above (or run `workos doctor` for details and remediation) and re-run the installer.',\n ].join('\\n');\n}\n"]}
1
+ {"version":3,"file":"security-checks.js","sourceRoot":"","sources":["../../../src/lib/validation/security-checks.ts"],"names":[],"mappings":"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","sourcesContent":["import { checkAuthPatterns } from '../../doctor/checks/auth-patterns.js';\nimport type { AuthPatternFinding, FrameworkInfo, EnvironmentInfo, SdkInfo } from '../../doctor/types.js';\nimport type { ValidationIssue } from './types.js';\nimport { formatWorkOSCommand } from '../../utils/command-invocation.js';\n\n/**\n * The \"security subset\" of `workos doctor`'s auth-pattern checks that the\n * installer enforces. These are the patterns that are *unsafe* or *leak secrets*\n * (an unsafe GET sign-out, an API key in client env/source, an ungitignored\n * .env). Completeness checks (missing middleware/callback/provider) are\n * intentionally excluded here — `validateInstallation` already covers those, and\n * they carry higher false-positive risk than we want gating install success.\n *\n * Keep in sync with the codes emitted by `src/doctor/checks/auth-patterns.ts`.\n */\nconst SECURITY_FINDING_CODES = new Set<string>([\n 'SIGNOUT_GET_HANDLER',\n 'SIGNOUT_LINK_PREFETCH',\n 'API_KEY_LEAKED_TO_CLIENT',\n 'API_KEY_IN_SOURCE',\n 'ENV_FILE_NOT_GITIGNORED',\n 'MIXED_ENVIRONMENT',\n]);\n\n/** Map an installer integration id to the framework name doctor's checks expect. */\nconst INTEGRATION_FRAMEWORK_NAME: Record<string, string> = {\n nextjs: 'Next.js',\n 'react-router': 'React Router',\n 'tanstack-start': 'TanStack Start',\n};\n\nexport interface SecurityCheckResult {\n /** All security-class findings for this install (errors + warnings). */\n findings: AuthPatternFinding[];\n /**\n * Error-severity findings that must block a successful install. Empty when the\n * install is secure; a non-empty list means install should not report success.\n */\n blocking: AuthPatternFinding[];\n}\n\n/**\n * Run the security subset of doctor's auth-pattern checks against an install\n * directory. Pure file inspection — no network — so it is safe to call both\n * inside the installer's self-correction loop and as the final pre-success gate.\n *\n * This closes the install-validate ↔ doctor gap: previously install could report\n * `success: true` while `workos doctor` immediately found a security hole,\n * because neither the retry loop nor `validateInstallation` ran these checks.\n */\nexport async function runInstallSecurityChecks(integration: string, installDir: string): Promise<SecurityCheckResult> {\n const framework: FrameworkInfo = {\n name: INTEGRATION_FRAMEWORK_NAME[integration] ?? null,\n version: null,\n };\n // checkAuthPatterns loads .env files itself; these structs only satisfy the\n // shape its non-Next.js checks read from.\n const environment: EnvironmentInfo = {\n apiKeyConfigured: false,\n apiKeyType: null,\n clientId: null,\n redirectUri: null,\n cookieDomain: null,\n baseUrl: null,\n };\n const sdk: SdkInfo = {\n name: null,\n version: null,\n latest: null,\n outdated: false,\n isAuthKit: false,\n language: 'javascript',\n };\n\n const result = await checkAuthPatterns({ installDir }, framework, environment, sdk);\n const findings = result.findings.filter((f) => SECURITY_FINDING_CODES.has(f.code));\n const blocking = findings.filter((f) => f.severity === 'error');\n return { findings, blocking };\n}\n\n/** Convert security findings into ValidationIssues for the emitter/report surfaces. */\nexport function securityFindingsToIssues(findings: AuthPatternFinding[]): ValidationIssue[] {\n return findings.map((f) => ({\n type: 'pattern',\n severity: f.severity,\n message: f.filePath ? `${f.message} (${f.filePath})` : f.message,\n hint: f.remediation,\n }));\n}\n\n/**\n * Build an agent correction prompt from security findings so the installer's\n * self-correction loop fixes them before declaring success. Returns an empty\n * string when there is nothing to correct.\n */\nexport function formatSecurityFindingsForAgent(findings: AuthPatternFinding[]): string {\n if (findings.length === 0) return '';\n const lines = findings.map((f) => {\n const loc = f.filePath ? ` in ${f.filePath}` : '';\n const fix = f.remediation ? ` Fix: ${f.remediation}` : '';\n return `- [${f.severity}] ${f.message}${loc}.${fix}`;\n });\n return `Security checks found issues that must be fixed:\\n\\n${lines.join('\\n')}\\n\\nApply the fixes above, then make sure the project still builds.`;\n}\n\n/**\n * Build the error message thrown when error-severity security findings survive\n * the installer's retries — the message that turns a silent insecure \"success\"\n * into a visible failure.\n */\nexport function formatBlockingSecurityError(blocking: AuthPatternFinding[]): string {\n const lines = blocking.map((f) => {\n const loc = f.filePath ? ` (${f.filePath})` : '';\n return ` • ${f.code}: ${f.message}${loc}`;\n });\n return [\n 'Installation produced insecure code that could not be auto-corrected:',\n '',\n ...lines,\n '',\n `Fix the issues above (or run \\`${formatWorkOSCommand('doctor')}\\` for details and remediation) and re-run the installer.`,\n ].join('\\n');\n}\n"]}
@@ -3,6 +3,7 @@ import { existsSync } from 'fs';
3
3
  import { join } from 'path';
4
4
  import fg from 'fast-glob';
5
5
  import { runBuildValidation } from './build-validator.js';
6
+ import { detectPort } from '../port-detection.js';
6
7
  export async function validateInstallation(framework, projectDir, options = {}) {
7
8
  const startTime = Date.now();
8
9
  const issues = [];
@@ -213,6 +214,29 @@ export async function validateFrameworkSpecific(framework, projectDir) {
213
214
  }
214
215
  return issues;
215
216
  }
217
+ /**
218
+ * Compares the redirect URI's effective port to the detected dev server port,
219
+ * for local dev hosts only, and records an error-severity issue on mismatch.
220
+ * Skips production URIs and placeholder hosts like http://test.
221
+ */
222
+ function validateRedirectUriPort(url, framework, projectDir, issues) {
223
+ // Only enforce for local dev hosts; skip production URIs and placeholder
224
+ // hosts like http://test. Node parses IPv6 hostnames with brackets: [::1].
225
+ const localHosts = new Set(['localhost', '127.0.0.1', '[::1]', '::1']);
226
+ if (!localHosts.has(url.hostname))
227
+ return;
228
+ const effectivePort = url.port || (url.protocol === 'https:' ? '443' : '80');
229
+ const detected = detectPort(framework, projectDir);
230
+ if (Number(effectivePort) !== detected) {
231
+ issues.push({
232
+ type: 'env',
233
+ severity: 'error',
234
+ message: `Redirect URI port :${effectivePort} does not match the detected dev server port :${detected}`,
235
+ hint: `Update the redirect URI to use port :${detected} in .env.local and the WorkOS dashboard ` +
236
+ `(redirect URI, CORS origin, homepage URL), or change your dev server to run on port ${effectivePort}.`,
237
+ });
238
+ }
239
+ }
216
240
  /**
217
241
  * Validates that the Next.js redirect URI matches an existing callback route.
218
242
  *
@@ -237,6 +261,7 @@ async function validateNextjsRedirectUri(projectDir, issues) {
237
261
  try {
238
262
  const url = new URL(redirectUri);
239
263
  callbackPath = url.pathname;
264
+ validateRedirectUriPort(url, 'nextjs', projectDir, issues);
240
265
  }
241
266
  catch {
242
267
  issues.push({
@@ -272,7 +297,7 @@ async function validateNextjsRedirectUri(projectDir, issues) {
272
297
  const actualPath = '/' + existingRoutes[0].replace(/^(src\/)?app\//, '').replace(/\/route\.(ts|tsx|js|jsx)$/, '');
273
298
  hint =
274
299
  `Found callback route at ${existingRoutes[0]} but redirect URI points to ${callbackPath}. Either:\n` +
275
- ` 1. Change NEXT_PUBLIC_WORKOS_REDIRECT_URI to http://localhost:3000${actualPath}\n` +
300
+ ` 1. Change NEXT_PUBLIC_WORKOS_REDIRECT_URI to ${new URL(redirectUri).origin}${actualPath}\n` +
276
301
  ` 2. Move the route to app/${routePath}/route.ts`;
277
302
  }
278
303
  issues.push({
@@ -308,6 +333,7 @@ async function validateReactRouterRedirectUri(projectDir, issues) {
308
333
  try {
309
334
  const url = new URL(redirectUri);
310
335
  callbackPath = url.pathname;
336
+ validateRedirectUriPort(url, 'react-router', projectDir, issues);
311
337
  }
312
338
  catch {
313
339
  issues.push({
@@ -361,7 +387,7 @@ async function validateReactRouterRedirectUri(projectDir, issues) {
361
387
  .replace(/\./g, '/');
362
388
  hint =
363
389
  `Found callback route at ${actualFile} but redirect URI points to ${callbackPath}. Either:\n` +
364
- ` 1. Change WORKOS_REDIRECT_URI to http://localhost:3000${actualPath}\n` +
390
+ ` 1. Change WORKOS_REDIRECT_URI to ${new URL(redirectUri).origin}${actualPath}\n` +
365
391
  ` 2. Move the route to app/routes/${dotPath}.tsx`;
366
392
  }
367
393
  issues.push({
@@ -396,6 +422,7 @@ async function validateTanstackStartRedirectUri(projectDir, issues) {
396
422
  try {
397
423
  const url = new URL(redirectUri);
398
424
  callbackPath = url.pathname;
425
+ validateRedirectUriPort(url, 'tanstack-start', projectDir, issues);
399
426
  }
400
427
  catch {
401
428
  issues.push({
@@ -433,7 +460,7 @@ async function validateTanstackStartRedirectUri(projectDir, issues) {
433
460
  .replace(/\/index$/, '');
434
461
  hint =
435
462
  `Found callback route at ${actualFile} but redirect URI points to ${callbackPath}. Either:\n` +
436
- ` 1. Change WORKOS_REDIRECT_URI to http://localhost:3000${actualPath}\n` +
463
+ ` 1. Change WORKOS_REDIRECT_URI to ${new URL(redirectUri).origin}${actualPath}\n` +
437
464
  ` 2. Move the route to app/routes/${routePath}.tsx`;
438
465
  }
439
466
  issues.push({