@posthog/wizard 2.79.0 → 2.80.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 (186) hide show
  1. package/dist/PickerMenu-m4CCPX4f.js +758 -0
  2. package/dist/PickerMenu-m4CCPX4f.js.map +1 -0
  3. package/dist/{agent-CNrEC-9v.js → agent-BkBbVuNq.js} +50 -45
  4. package/dist/agent-BkBbVuNq.js.map +1 -0
  5. package/dist/agent-QGK2YzlX.js +6 -0
  6. package/dist/{agent-interface-BXvmAm4c.js → agent-interface-CzyCmp7q.js} +87 -38
  7. package/dist/agent-interface-CzyCmp7q.js.map +1 -0
  8. package/dist/{agent-prompt-loader-DiKUbhi_.js → agent-prompt-loader-4wMxlYSX.js} +5 -5
  9. package/dist/{agent-prompt-loader-DiKUbhi_.js.map → agent-prompt-loader-4wMxlYSX.js.map} +1 -1
  10. package/dist/{analytics-VRYdebXW.js → analytics-B6q5PN7a.js} +5 -5
  11. package/dist/analytics-B6q5PN7a.js.map +1 -0
  12. package/dist/analytics-BXtfK9o7.js +2 -0
  13. package/dist/{api-DGuJTzjr.js → api-C3Dc4LFl.js} +4 -16
  14. package/dist/{api-DGuJTzjr.js.map → api-C3Dc4LFl.js.map} +1 -1
  15. package/dist/api-yNjrfZs2.js +2 -0
  16. package/dist/bin.js +5 -2960
  17. package/dist/bin.js.map +1 -1
  18. package/dist/{bounded-fs-Cpn129A4.js → bounded-fs-CC_NDtoP.js} +3 -3
  19. package/dist/{bounded-fs-Cpn129A4.js.map → bounded-fs-CC_NDtoP.js.map} +1 -1
  20. package/dist/check-screens.tsx +5 -5
  21. package/dist/{ci-install-BPA6gzEq.js → ci-install-DfcAMhlu.js} +9 -8
  22. package/dist/ci-install-DfcAMhlu.js.map +1 -0
  23. package/dist/{constants-CuJfQEcC.js → constants-DO4X70po.js} +3 -3
  24. package/dist/{constants-CuJfQEcC.js.map → constants-DO4X70po.js.map} +1 -1
  25. package/dist/{debug-B9k4ideV.js → debug-BueK5mRR.js} +1 -1
  26. package/dist/{debug-Cj-8vfXY.js → debug-DyARf5TF.js} +3 -3
  27. package/dist/debug-DyARf5TF.js.map +1 -0
  28. package/dist/{defaults-C6J7yL4O.js → defaults-DpfMnH2q.js} +2 -2
  29. package/dist/defaults-DpfMnH2q.js.map +1 -0
  30. package/dist/emit-BSetXzDV.js +69 -0
  31. package/dist/emit-BSetXzDV.js.map +1 -0
  32. package/dist/{endpoints-DnIX0pZH.js → endpoints-D-4KxZiB.js} +3 -3
  33. package/dist/{endpoints-DnIX0pZH.js.map → endpoints-D-4KxZiB.js.map} +1 -1
  34. package/dist/env-api-key-CHGLe0iz.js +2 -0
  35. package/dist/{env-api-key-BWdKpGEr.js → env-api-key-COf0U-Au.js} +3 -5
  36. package/dist/{env-api-key-BWdKpGEr.js.map → env-api-key-COf0U-Au.js.map} +1 -1
  37. package/dist/{environment-DZYUSCpV.js → environment-DrLaSsA8.js} +5 -10
  38. package/dist/{environment-DZYUSCpV.js.map → environment-DrLaSsA8.js.map} +1 -1
  39. package/dist/environment-hBj7ngmO.js +2 -0
  40. package/dist/{errors-ddDE6MbA.js → errors-C2domSkK.js} +3 -67
  41. package/dist/errors-C2domSkK.js.map +1 -0
  42. package/dist/{file-dEhr-l3n.js → file-CVA9ocIC.js} +4 -4
  43. package/dist/file-CVA9ocIC.js.map +1 -0
  44. package/dist/file-CmoKK_Pp.js +2 -0
  45. package/dist/{file-watcher-CPM-Nn9L.js → file-watcher-C3Su8vXm.js} +3 -3
  46. package/dist/file-watcher-C3Su8vXm.js.map +1 -0
  47. package/dist/{gateway-CVU1G0H3.js → gateway-BmjYUSua.js} +8 -7
  48. package/dist/{gateway-CVU1G0H3.js.map → gateway-BmjYUSua.js.map} +1 -1
  49. package/dist/{headless-ui-pY2xB5ug.js → headless-ui-CUbv12Yx.js} +3 -3
  50. package/dist/headless-ui-CUbv12Yx.js.map +1 -0
  51. package/dist/host-resolution-DVSb7kXb.js +114 -0
  52. package/dist/host-resolution-DVSb7kXb.js.map +1 -0
  53. package/dist/{add-mcp-server-to-clients-amlJWW7W.js → install-Bk5TNk27.js} +16 -26
  54. package/dist/install-Bk5TNk27.js.map +1 -0
  55. package/dist/install-F4wm3RSl.js +2 -0
  56. package/dist/interactive-C5LNvDj6.js +11 -0
  57. package/dist/interactive-C5LNvDj6.js.map +1 -0
  58. package/dist/{telemetry-Bpv-Mh5B.js → links-9LWZPSCq.js} +4 -13
  59. package/dist/links-9LWZPSCq.js.map +1 -0
  60. package/dist/{logging-ui-CUxNYF2t.js → logging-ui-p6XKVmTx.js} +6 -6
  61. package/dist/logging-ui-p6XKVmTx.js.map +1 -0
  62. package/dist/main-DKPjFHEV.js +1404 -0
  63. package/dist/main-DKPjFHEV.js.map +1 -0
  64. package/dist/{mcp-JcsBBnWv.js → mcp-Co8VRF62.js} +4 -4
  65. package/dist/{mcp-JcsBBnWv.js.map → mcp-Co8VRF62.js.map} +1 -1
  66. package/dist/mcp-install-smoke-test.ts +1 -1
  67. package/dist/{mcp-prompt-streaming-B-p_Pvb2.js → mcp-prompt-streaming-ljCVoQ9I.js} +6 -6
  68. package/dist/{mcp-prompt-streaming-B-p_Pvb2.js.map → mcp-prompt-streaming-ljCVoQ9I.js.map} +1 -1
  69. package/dist/{mint-failure-CZFZpHpW.js → mint-failure-C0HyvEwO.js} +105 -93
  70. package/dist/mint-failure-C0HyvEwO.js.map +1 -0
  71. package/dist/{models-DnAY67Lg.js → models-Dba8ITeo.js} +2 -2
  72. package/dist/{models-DnAY67Lg.js.map → models-Dba8ITeo.js.map} +1 -1
  73. package/dist/{non-interactive-DR1CWXY1.js → non-interactive-CLMJrj_7.js} +5 -4
  74. package/dist/non-interactive-CLMJrj_7.js.map +1 -0
  75. package/dist/{oauth-session-CQTFOk-I.js → oauth-session-D-RfcVmr.js} +2 -2
  76. package/dist/{oauth-session-CQTFOk-I.js.map → oauth-session-D-RfcVmr.js.map} +1 -1
  77. package/dist/{orchestrator-tools-CBdt-SXO.js → orchestrator-tools-CVJ1F9Ub.js} +3 -3
  78. package/dist/{orchestrator-tools-CBdt-SXO.js.map → orchestrator-tools-CVJ1F9Ub.js.map} +1 -1
  79. package/dist/package-json--ppioh2F.js +2 -0
  80. package/dist/{package-json-DY70kkNp.js → package-json-Cowya9-3.js} +2 -8
  81. package/dist/{package-json-DY70kkNp.js.map → package-json-Cowya9-3.js.map} +1 -1
  82. package/dist/{pi-BtqcLX3y.js → pi-B5YTBRDv.js} +20 -18
  83. package/dist/pi-B5YTBRDv.js.map +1 -0
  84. package/dist/{playground-Dz-ZVjBk.js → playground-BLsCfHmQ.js} +33 -33
  85. package/dist/playground-BLsCfHmQ.js.map +1 -0
  86. package/dist/posthog-B2E0hpZt.js +2 -0
  87. package/dist/{posthog-6Uqw_Eux.js → posthog-B42rt_1S.js} +4 -9
  88. package/dist/posthog-B42rt_1S.js.map +1 -0
  89. package/dist/project-data--L-WZyvj.js +2 -0
  90. package/dist/{setup-utils-dCViCC6s.js → project-data-BO9IXn9y.js} +566 -957
  91. package/dist/project-data-BO9IXn9y.js.map +1 -0
  92. package/dist/provisioning-C01REIU2.js +2 -0
  93. package/dist/{provisioning-Zf7yPcEs.js → provisioning-CLhuKqXH.js} +6 -73
  94. package/dist/provisioning-CLhuKqXH.js.map +1 -0
  95. package/dist/{queue-tools-B7_aCyd-.js → queue-tools-knnPQP0e.js} +4 -4
  96. package/dist/{queue-tools-B7_aCyd-.js.map → queue-tools-knnPQP0e.js.map} +1 -1
  97. package/dist/{registry-B6iLGorH.js → registry-B3rC5U4b.js} +7 -9
  98. package/dist/{registry-B6iLGorH.js.map → registry-B3rC5U4b.js.map} +1 -1
  99. package/dist/registry-BamsM7J9.js +2 -0
  100. package/dist/run-agent-legacy-CwUmPfQF.js +2 -0
  101. package/dist/{run-agent-legacy-6WcYMaR9.js → run-agent-legacy-DWBajsMh.js} +40 -114
  102. package/dist/run-agent-legacy-DWBajsMh.js.map +1 -0
  103. package/dist/runners-D1PqqpjA.js +834 -0
  104. package/dist/runners-D1PqqpjA.js.map +1 -0
  105. package/dist/{security-lkBwWbrK.js → security-BvdSFDuM.js} +5 -5
  106. package/dist/{security-lkBwWbrK.js.map → security-BvdSFDuM.js.map} +1 -1
  107. package/dist/setup-utils-ChXXTi1m.js +301 -0
  108. package/dist/setup-utils-ChXXTi1m.js.map +1 -0
  109. package/dist/{start-tui-oCWMgsDP.js → start-tui-DicLfgsj.js} +95 -91
  110. package/dist/start-tui-DicLfgsj.js.map +1 -0
  111. package/dist/{steps-gx0Pq4I4.js → steps-Hc28zVc_.js} +10 -10
  112. package/dist/steps-Hc28zVc_.js.map +1 -0
  113. package/dist/store-COCO6U0P.js +5 -0
  114. package/dist/{store-xYy_6Ruv.js → store-D3509G87.js} +67 -35
  115. package/dist/store-D3509G87.js.map +1 -0
  116. package/dist/{subagent-Bfy0sfhU.js → subagent-D_Sqak94.js} +3 -3
  117. package/dist/{subagent-Bfy0sfhU.js.map → subagent-D_Sqak94.js.map} +1 -1
  118. package/dist/{task-BOfw7GPc.js → task-DiTI6Sak.js} +19 -17
  119. package/dist/task-DiTI6Sak.js.map +1 -0
  120. package/dist/{wizard-ui-DoyOZxK8.js → task-status-CmrrR7zo.js} +2 -2
  121. package/dist/task-status-CmrrR7zo.js.map +1 -0
  122. package/dist/{task-stream-kYlm9qd0.js → task-stream-BckhG_cB.js} +11 -11
  123. package/dist/task-stream-BckhG_cB.js.map +1 -0
  124. package/dist/{tasks-Bbv_lOBG.js → tasks-ChZ_F1YP.js} +1 -1
  125. package/dist/{tasks-Bbv_lOBG.js.map → tasks-ChZ_F1YP.js.map} +1 -1
  126. package/dist/telemetry-BNKsDxNS.js +13 -0
  127. package/dist/telemetry-BNKsDxNS.js.map +1 -0
  128. package/dist/{terminal-D07EQ2y0.js → terminal-PK9bCLRi.js} +123 -97
  129. package/dist/terminal-PK9bCLRi.js.map +1 -0
  130. package/dist/{tools-Cc41af5W.js → tools-C8LQ8QFz.js} +9 -9
  131. package/dist/tools-C8LQ8QFz.js.map +1 -0
  132. package/dist/{ui-CGXtC7Vn.js → ui-tPNpM6vj.js} +3 -3
  133. package/dist/ui-tPNpM6vj.js.map +1 -0
  134. package/dist/urls-BI4kJ4qQ.js +67 -0
  135. package/dist/urls-BI4kJ4qQ.js.map +1 -0
  136. package/dist/{version-Cadof0Z_.js → version-5_ydHkM6.js} +2 -2
  137. package/dist/{version-Cadof0Z_.js.map → version-5_ydHkM6.js.map} +1 -1
  138. package/dist/wizard-abort-BVjqLyeA.js +3 -0
  139. package/dist/{wizard-abort-HDbP3YcS.js → wizard-abort-DBKtfnBR.js} +20 -17
  140. package/dist/wizard-abort-DBKtfnBR.js.map +1 -0
  141. package/dist/wizard-session-B7BHnay0.js +2 -0
  142. package/dist/{wizard-session-2w4Ru-Wz.js → wizard-session-BYw6LzpS.js} +30 -28
  143. package/dist/wizard-session-BYw6LzpS.js.map +1 -0
  144. package/dist/{yara-hooks-DCfROpZt.js → yara-hooks-C3L6lBij.js} +5 -5
  145. package/dist/{yara-hooks-DCfROpZt.js.map → yara-hooks-C3L6lBij.js.map} +1 -1
  146. package/dist/yara-hooks-CF_IsoaA.js +2 -0
  147. package/package.json +2 -3
  148. package/dist/add-mcp-server-to-clients-amlJWW7W.js.map +0 -1
  149. package/dist/agent-CNrEC-9v.js.map +0 -1
  150. package/dist/agent-interface-BXvmAm4c.js.map +0 -1
  151. package/dist/analytics-DxwRi2Hf.js +0 -2
  152. package/dist/analytics-VRYdebXW.js.map +0 -1
  153. package/dist/ci-install-BPA6gzEq.js.map +0 -1
  154. package/dist/debug-Cj-8vfXY.js.map +0 -1
  155. package/dist/defaults-C6J7yL4O.js.map +0 -1
  156. package/dist/errors-ddDE6MbA.js.map +0 -1
  157. package/dist/file-dEhr-l3n.js.map +0 -1
  158. package/dist/file-watcher-CPM-Nn9L.js.map +0 -1
  159. package/dist/headless-ui-pY2xB5ug.js.map +0 -1
  160. package/dist/interactive-BOGlinZe.js +0 -11
  161. package/dist/interactive-BOGlinZe.js.map +0 -1
  162. package/dist/logging-ui-CUxNYF2t.js.map +0 -1
  163. package/dist/mint-failure-CZFZpHpW.js.map +0 -1
  164. package/dist/non-interactive-DR1CWXY1.js.map +0 -1
  165. package/dist/pi-BtqcLX3y.js.map +0 -1
  166. package/dist/playground-Dz-ZVjBk.js.map +0 -1
  167. package/dist/posthog-6Uqw_Eux.js.map +0 -1
  168. package/dist/provisioning-Zf7yPcEs.js.map +0 -1
  169. package/dist/rolldown-runtime-B_-DWIq7.js +0 -15
  170. package/dist/run-agent-legacy-6WcYMaR9.js.map +0 -1
  171. package/dist/setup-utils-dCViCC6s.js.map +0 -1
  172. package/dist/start-tui-oCWMgsDP.js.map +0 -1
  173. package/dist/steps-gx0Pq4I4.js.map +0 -1
  174. package/dist/store-xYy_6Ruv.js.map +0 -1
  175. package/dist/task-BOfw7GPc.js.map +0 -1
  176. package/dist/task-stream-kYlm9qd0.js.map +0 -1
  177. package/dist/telemetry-Bpv-Mh5B.js.map +0 -1
  178. package/dist/terminal-D07EQ2y0.js.map +0 -1
  179. package/dist/tools-Cc41af5W.js.map +0 -1
  180. package/dist/ui-CGXtC7Vn.js.map +0 -1
  181. package/dist/wizard-abort-HDbP3YcS.js.map +0 -1
  182. package/dist/wizard-abort-Y-Ip2K4z.js +0 -3
  183. package/dist/wizard-session-2w4Ru-Wz.js.map +0 -1
  184. package/dist/wizard-session-DGaUHOF3.js +0 -2
  185. package/dist/wizard-ui-DoyOZxK8.js.map +0 -1
  186. package/dist/yara-hooks-B49RIk5d.js +0 -2
@@ -1,603 +1,113 @@
1
- import { t as __exportAll } from "./rolldown-runtime-B_-DWIq7.js";
2
- import { s as logToFile } from "./debug-Cj-8vfXY.js";
3
- import { n as MCP_LOCAL_URL } from "./local-dev-McsurOC-.js";
4
- import { B as WIZARD_CONTACT_EMAIL, C as ISSUES_URL, E as OAUTH_TIMEOUT_MS, J as WIZARD_PROVISIONING_SCOPES, N as POSTHOG_PROXY_CLIENT_ID, O as POSTHOG_DEV_CLIENT_ID, P as POSTHOG_STATUS_PAGE_URL, Q as WIZARD_USER_AGENT, T as OAUTH_PORTS, W as WIZARD_OAUTH_SCOPES, d as DUMMY_PROJECT_API_KEY } from "./constants-CuJfQEcC.js";
5
- import "./wizard-session-2w4Ru-Wz.js";
6
- import { n as analytics } from "./analytics-VRYdebXW.js";
7
- import { i as withUtm, n as openTrackedLink, t as withProgress } from "./telemetry-Bpv-Mh5B.js";
8
- import { r as readFileHead } from "./bounded-fs-Cpn129A4.js";
9
- import { t as getUI } from "./ui-CGXtC7Vn.js";
10
- import { a as detectRegion, d as resolveBaseUrl, l as getOAuthUrl, n as provisionNewAccount, o as getCloudUrl, s as getHost, t as ProvisionedAccountUnreadableError, u as getUiHostFromHost } from "./provisioning-Zf7yPcEs.js";
11
- import { i as fetchProjectData, o as fetchUserData } from "./api-DGuJTzjr.js";
12
- import { i as wizardAbort } from "./wizard-abort-HDbP3YcS.js";
13
- import { major, minVersion } from "semver";
14
- import * as fs$2 from "fs";
15
- import * as path$2 from "path";
16
- import { basename, join as join$1 } from "node:path";
17
- import * as childProcess from "node:child_process";
1
+ import { s as logToFile } from "./debug-DyARf5TF.js";
2
+ import { B as WIZARD_CONTACT_EMAIL, C as ISSUES_URL, E as OAUTH_TIMEOUT_MS, J as WIZARD_PROVISIONING_SCOPES, N as POSTHOG_PROXY_CLIENT_ID, O as POSTHOG_DEV_CLIENT_ID, P as POSTHOG_STATUS_PAGE_URL, Q as WIZARD_USER_AGENT, T as OAUTH_PORTS, W as WIZARD_OAUTH_SCOPES, d as DUMMY_PROJECT_API_KEY } from "./constants-DO4X70po.js";
3
+ import "./wizard-session-BYw6LzpS.js";
4
+ import { n as analytics } from "./analytics-B6q5PN7a.js";
5
+ import { r as withUtm, t as openTrackedLink } from "./links-9LWZPSCq.js";
6
+ import { t as withProgress } from "./telemetry-BNKsDxNS.js";
7
+ import { n as detectOrgAndProject, t as abort } from "./setup-utils-ChXXTi1m.js";
8
+ import { t as getUI } from "./ui-tPNpM6vj.js";
9
+ import { r as wizardAbort } from "./wizard-abort-DBKtfnBR.js";
10
+ import { a as getOAuthUrl, s as resolveBaseUrl } from "./urls-BI4kJ4qQ.js";
11
+ import { t as HostResolution } from "./host-resolution-DVSb7kXb.js";
12
+ import { n as provisionNewAccount, t as ProvisionedAccountUnreadableError } from "./provisioning-CLhuKqXH.js";
13
+ import { c as fetchUserData, o as fetchProjectData } from "./api-C3Dc4LFl.js";
18
14
  import { execSync } from "node:child_process";
19
- import * as fs$1 from "node:fs";
20
15
  import axios from "axios";
21
16
  import * as crypto from "node:crypto";
22
- import * as http from "node:http";
23
17
  import { z } from "zod";
24
- //#region src/shared/utils/package-manager.ts
25
- function hasLockfile(installDir, file) {
26
- return fs$2.existsSync(path$2.join(installDir, file));
27
- }
28
- function lockfileHeaderContains(installDir, file, needle) {
29
- return readFileHead(path$2.join(installDir, file), 500)?.includes(needle) ?? false;
30
- }
31
- async function writeOverride(slot, pkgName, pkgVersion, { installDir }) {
32
- const pkg = await getPackageDotJson({ installDir });
33
- let next;
34
- if (slot === "yarn") next = {
35
- ...pkg,
36
- resolutions: {
37
- ...pkg.resolutions ?? {},
38
- [pkgName]: pkgVersion
39
- }
40
- };
41
- else if (slot === "pnpm") next = {
42
- ...pkg,
43
- pnpm: {
44
- ...pkg.pnpm ?? {},
45
- overrides: {
46
- ...pkg.pnpm?.overrides ?? {},
47
- [pkgName]: pkgVersion
48
- }
49
- }
50
- };
51
- else next = {
52
- ...pkg,
53
- overrides: {
54
- ...pkg.overrides ?? {},
55
- [pkgName]: pkgVersion
56
- }
57
- };
58
- await updatePackageDotJson(next, { installDir });
59
- }
60
- const packageManagers = [
61
- {
62
- name: "bun",
63
- label: "Bun",
64
- installCommand: "bun add",
65
- buildCommand: "bun run build",
66
- runScriptCommand: "bun run",
67
- flags: "",
68
- detect: ({ installDir }) => hasLockfile(installDir, "bun.lockb") || hasLockfile(installDir, "bun.lock"),
69
- addOverride: (pkgName, pkgVersion, opts) => writeOverride("npm", pkgName, pkgVersion, opts)
70
- },
71
- {
72
- name: "yarn",
73
- label: "Yarn V1",
74
- installCommand: "yarn add",
75
- buildCommand: "yarn build",
76
- runScriptCommand: "yarn",
77
- flags: "--ignore-workspace-root-check",
78
- detect: ({ installDir }) => lockfileHeaderContains(installDir, "yarn.lock", "yarn lockfile v1"),
79
- addOverride: (pkgName, pkgVersion, opts) => writeOverride("yarn", pkgName, pkgVersion, opts)
80
- },
81
- {
82
- name: "yarn",
83
- label: "Yarn V2/3/4",
84
- installCommand: "yarn add",
85
- buildCommand: "yarn build",
86
- runScriptCommand: "yarn",
87
- flags: "",
88
- detect: ({ installDir }) => lockfileHeaderContains(installDir, "yarn.lock", "__metadata"),
89
- addOverride: (pkgName, pkgVersion, opts) => writeOverride("yarn", pkgName, pkgVersion, opts)
90
- },
91
- {
92
- name: "pnpm",
93
- label: "pnpm",
94
- installCommand: "pnpm add",
95
- buildCommand: "pnpm build",
96
- runScriptCommand: "pnpm",
97
- flags: "--ignore-workspace-root-check",
98
- detect: ({ installDir }) => hasLockfile(installDir, "pnpm-lock.yaml"),
99
- addOverride: (pkgName, pkgVersion, opts) => writeOverride("pnpm", pkgName, pkgVersion, opts)
100
- },
101
- {
102
- name: "npm",
103
- label: "npm",
104
- installCommand: "npm add",
105
- buildCommand: "npm run build",
106
- runScriptCommand: "npm run",
107
- flags: "",
108
- detect: ({ installDir }) => hasLockfile(installDir, "package-lock.json"),
109
- addOverride: (pkgName, pkgVersion, opts) => writeOverride("npm", pkgName, pkgVersion, opts)
110
- },
111
- {
112
- name: "expo",
113
- label: "Expo",
114
- installCommand: "npx expo install",
115
- buildCommand: "npx expo build",
116
- runScriptCommand: "npx expo run",
117
- flags: "",
118
- detect: () => false,
119
- addOverride: (pkgName, pkgVersion, opts) => writeOverride("npm", pkgName, pkgVersion, opts)
120
- }
121
- ];
122
- function detectAllPackageManagers({ installDir }) {
123
- return withProgress("detect-package-manager", () => {
124
- const matches = packageManagers.filter((pm) => pm.detect({ installDir }));
125
- if (matches.length === 0) analytics.setTag("package-manager", "not-detected");
126
- return matches;
127
- });
128
- }
129
- function serializeNodePM(pm) {
130
- return {
131
- name: pm.name,
132
- label: pm.label,
133
- installCommand: pm.installCommand,
134
- runCommand: pm.runScriptCommand
135
- };
136
- }
18
+ import * as http from "node:http";
19
+ //#region src/shared/utils/oauth-errors.ts
137
20
  /**
138
- * Detect Node.js package managers via lockfiles.
139
- * Wraps detectAllPackageManagers() above.
21
+ * OAuth failure presentation — the structured error the flow throws and the
22
+ * user-facing messaging built from it.
23
+ *
24
+ * `oauth.ts` owns the flow machinery (callback server, token exchange, port
25
+ * retries); this module owns what a failure *means*: the RFC 6749 error
26
+ * fields (`error`, `error_description`, `error_uri`), per-code remediation
27
+ * copy, and safe rendering for the browser callback page. Everything here is
28
+ * pure so the copy can be unit-tested without the HTTP machinery.
140
29
  */
141
- function detectNodePackageManagers(installDir) {
142
- const detected = detectAllPackageManagers({ installDir }).map(serializeNodePM);
143
- if (detected.length === 0) return Promise.resolve({
144
- detected: [],
145
- primary: null,
146
- recommendation: "No lockfile found. Default to npm (npm add, npm run)."
147
- });
148
- const primary = detected[0];
149
- return Promise.resolve({
150
- detected,
151
- primary,
152
- recommendation: detected.length === 1 ? `Use ${primary.label} (${primary.installCommand}).` : `Multiple package managers detected. Prefer ${primary.label} (${primary.installCommand}).`
153
- });
154
- }
155
- //#endregion
156
- //#region src/programs/oauth/program-scopes.ts
157
30
  /**
158
- * Extra scopes the MCP tutorial needs on top of `WIZARD_OAUTH_SCOPES`.
159
- *
160
- * Every scope requested here must stay within the wizard OAuth app's
161
- * ceiling on the PostHog side (`OAuthApplication.scopes`) — the full
162
- * list lives in the README under "OAuth app scope ceiling". The
163
- * tutorial's prompts and follow-ups touch most of the read surface,
164
- * plus annotation write for the "PostHog wizard install" verify-prompt.
31
+ * A structured OAuth failure (RFC 6749 §4.1.2.1 authorize errors, §5.2 token
32
+ * errors). Thrown by both the authorize callback and the token exchange so
33
+ * the flow's error handler keys remediation copy and analytics fingerprints
34
+ * off `code` instead of re-parsing `message`.
165
35
  *
166
- * Already in the base `WIZARD_OAUTH_SCOPES` (and therefore not
167
- * repeated here):
168
- * • user:read, project:read, llm_gateway:read — auth + gateway
169
- * • query:read — HogQL
170
- * • dashboard:write, insight:write, notebook:write — Phase-5 persist
36
+ * The message stays exactly `OAuth error: <code>` — the analytics fingerprint
37
+ * scheme (`wizard_oauth_<code>`) and the legacy message-prefix parsing both
38
+ * depend on that shape. The description/uri ride along as properties.
171
39
  *
172
- * Deliberately omitted (writes on read-only product surfaces):
173
- * • feature_flag:write, experiment:write, survey:write,
174
- * cohort:write, session_recording:write, error_tracking:write,
175
- * alert:write, subscription:write
40
+ * NAMING: never bind an instance to a variable or property whose name
41
+ * contains "oauth" (use `flowError`, `callbackError`, ...). CodeQL's
42
+ * clear-text-logging heuristic classifies any `*oauth*`-named variable read
43
+ * as password data, and these errors are deliberately shown to the user, so
44
+ * such a binding flags every log statement downstream of it (CWE-312).
176
45
  */
177
- const MCP_TUTORIAL_SCOPE_ADDITIONS = [
178
- "dashboard:read",
179
- "insight:read",
180
- "notebook:read",
181
- "feature_flag:read",
182
- "experiment:read",
183
- "experiment_saved_metric:read",
184
- "survey:read",
185
- "session_recording:read",
186
- "error_tracking:read",
187
- "web_analytics:read",
188
- "llm_analytics:read",
189
- "cohort:read",
190
- "person:read",
191
- "annotation:read",
192
- "annotation:write",
193
- "activity_log:read",
194
- "property_definition:read",
195
- "event_definition:read",
196
- "action:read",
197
- "warehouse_table:read",
198
- "warehouse_view:read",
199
- "alert:read",
200
- "subscription:read",
201
- "integration:read"
202
- ];
46
+ var OAuthError = class extends Error {
47
+ code;
48
+ /** The server's human-readable explanation (`error_description`), if sent. */
49
+ description;
50
+ /** The server's "more info" link (`error_uri`), if sent. */
51
+ uri;
52
+ constructor(code, description, uri) {
53
+ super(`OAuth error: ${code}`);
54
+ this.name = "OAuthError";
55
+ this.code = code;
56
+ this.description = description;
57
+ this.uri = uri;
58
+ }
59
+ };
203
60
  /**
204
- * Extra scopes the agent-skill program needs on top of `WIZARD_OAUTH_SCOPES`.
205
- *
206
- * Skills under this program (e.g. `creating-product-tours`) create feature
207
- * flags during the install flow. PostHog's consent grants exactly the scope
208
- * strings requested — `:write` does not imply `:read` — so listing existing
209
- * flags to avoid key collisions needs `feature_flag:read` explicitly.
210
- * `property_definition:read` lets the agent discover person properties when
211
- * building flag rollout filters instead of having to ask the user verbatim.
61
+ * Sanitize an OAuth error param before logging or rendering it: these arrive
62
+ * on a query string (or a token-endpoint body) that anything able to hit the
63
+ * local callback port can populate. Strips non-printable/non-ASCII characters
64
+ * and caps the length. Returns undefined when nothing printable remains.
212
65
  */
213
- const AGENT_SKILL_SCOPE_ADDITIONS = [
214
- "feature_flag:read",
215
- "feature_flag:write",
216
- "property_definition:read"
217
- ];
66
+ function sanitizeOAuthParam(value, maxLength = 200) {
67
+ if (!value) return void 0;
68
+ return value.replace(/[^\x20-\x7e]/g, "").slice(0, maxLength).trim() || void 0;
69
+ }
70
+ /** Description sentences get more room than bare codes/URIs. */
71
+ const MAX_DESCRIPTION_LENGTH = 500;
72
+ function escapeHtml(value) {
73
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
74
+ }
75
+ const OAuthErrorBodySchema = z.object({
76
+ error: z.string().min(1),
77
+ error_description: z.string().optional(),
78
+ error_uri: z.string().optional()
79
+ });
218
80
  /**
219
- * Extra scopes the self-driving program needs on top of
220
- * `WIZARD_OAUTH_SCOPES`. All consumed by the PostHog MCP tools the
221
- * agent drives during the run:
222
- * • task:read / task:write — the signal source config API
223
- * (`inbox-source-configs-*`) is permissioned under the generic
224
- * `task` scope object, NOT a signals-specific one. Unrelated to
225
- * the Tasks product.
226
- * • integration:read — `integrations-list`, to check whether the
227
- * team already has a GitHub integration and to verify the connect
228
- * flow completed.
229
- * • signal_scout:read / signal_scout:write — list, sync, and tune
230
- * the Signals scout troop (`signals-scout-config-*`).
231
- * • session_recording:read / survey:read / error_tracking:read —
232
- * server-side product-usage probes (`query-session-recordings-list`,
233
- * `survey-list`, `error-issue-list`). Product usage is a
234
- * project-level fact (often instrumented in another repo or via
235
- * the snippet), so the agent asks the server instead of inferring
236
- * only from the local setup report. All three are read-only and
237
- * already in the wizard OAuth app's production scope ceiling (the
238
- * mcp-tutorial program requests them).
239
- * • external_data_source:read / external_data_source:write — the
240
- * connected-tools step creates the GitHub Issues / Linear warehouse
241
- * sources directly (`external-data-sources-create`) and verifies
242
- * what's actually connected (`external-data-sources-list`) instead
243
- * of taking the user's word for it.
244
- * • llm_skill:read / llm_skill:write — the custom-scouts step
245
- * (skill step 6b): read the seeded `authoring-signals-scouts`
246
- * guide and canonical scout bodies (`llma-skill-get` /
247
- * `llma-skill-file-get`) and author the user-approved custom
248
- * `signals-scout-*` skills (`llma-skill-create`). Canonical scout
249
- * bodies are never edited.
250
- * • product_enablement:write — the "Enable products" step turns on
251
- * Session Replay / Error Tracking / Support so their sources have
252
- * data to read (`products-enable`). A purpose-built scope: the
253
- * server owns each enable recipe, so this can flip the product
254
- * toggles without the far broader `project:write`.
255
- * • replay_scanner:read / replay_scanner:write — the Replay Vision
256
- * scanners step (skill step 6c) lists the team's existing scanners
257
- * and creates the `emits_signals` ones whose findings land in the
258
- * inbox (`vision-scanners-list` / `-create` / `-update`, and the
259
- * advisory `vision-scanners-estimate-create` / `vision-quota-retrieve`).
260
- * The scope OBJECT is `replay_scanner` — the `vision-scanners-*`
261
- * names are MCP tool names, not scopes. Configuring a scanner also
262
- * requires `session_recording:read` (the API pairs the two, since a
263
- * scanner's config indirectly exposes recording contents); that one
264
- * is already in this list for the step-2 usage probes.
265
- *
266
- * No OAuth-ceiling edit is needed for any scope here: they are all normal
267
- * public (unprivileged, non-internal, non-hidden) scope objects, and the
268
- * live wizard apps' ceiling is the `@default` sentinel, which resolves to
269
- * every such scope (`UNPRIVILEGED_SCOPES`) and auto-tracks new ones. Only a
270
- * privileged/internal/hidden object (e.g. `llm_gateway:*`) would need a
271
- * manual per-app edit. See README → "OAuth app scope ceiling".
81
+ * Build an `OAuthError` from an authorize-callback's query params.
82
+ * Sanitizes every field — the callback URL is attacker-reachable input.
272
83
  */
273
- const SELF_DRIVING_SCOPE_ADDITIONS = [
274
- "task:read",
275
- "task:write",
276
- "integration:read",
277
- "signal_scout:read",
278
- "signal_scout:write",
279
- "session_recording:read",
280
- "survey:read",
281
- "error_tracking:read",
282
- "external_data_source:read",
283
- "external_data_source:write",
284
- "llm_skill:read",
285
- "llm_skill:write",
286
- "product_enablement:write",
287
- "replay_scanner:read",
288
- "replay_scanner:write"
289
- ];
84
+ function oauthErrorFromCallbackParams(params) {
85
+ return new OAuthError(sanitizeOAuthParam(params.get("error")) ?? "unknown", sanitizeOAuthParam(params.get("error_description"), MAX_DESCRIPTION_LENGTH), sanitizeOAuthParam(params.get("error_uri")));
86
+ }
290
87
  /**
291
- * Extra scopes the warehouse-source program needs on top of
292
- * `WIZARD_OAUTH_SCOPES`. The agent creates data warehouse sources directly
293
- * (`external-data-sources-create`) and lists what's connected
294
- * (`external-data-sources-list`) to verify the result. Both are already within
295
- * the wizard OAuth app's scope ceiling — the self-driving program requests the
296
- * same pair.
88
+ * Build an `OAuthError` from a token-endpoint JSON error body (RFC 6749
89
+ * §5.2). Returns null when the body isn't an OAuth error shape (HTML error
90
+ * pages, proxy bodies, network failures) — the caller rethrows the raw
91
+ * error in that case.
297
92
  */
298
- const WAREHOUSE_SOURCE_SCOPE_ADDITIONS = ["external_data_source:read", "external_data_source:write"];
93
+ function oauthErrorFromTokenBody(data) {
94
+ const parsed = OAuthErrorBodySchema.safeParse(data);
95
+ if (!parsed.success) return null;
96
+ return new OAuthError(sanitizeOAuthParam(parsed.data.error) ?? "unknown", sanitizeOAuthParam(parsed.data.error_description, MAX_DESCRIPTION_LENGTH), sanitizeOAuthParam(parsed.data.error_uri));
97
+ }
299
98
  /**
300
- * Extra scope the Connect-Slack step needs on top of `WIZARD_OAUTH_SCOPES`.
301
- *
302
- * The step polls `/api/projects/:id/integrations/` (`fetchSlackConnected`)
303
- * to render the already-connected variant and to flip live once the user
304
- * completes the Slack OAuth step in the browser. Without `integration:read`
305
- * the first poll 403s, the screen stops polling, and an already-connected
306
- * project is nagged with the connect nudge. Used by the default integration
307
- * run (the step ends the run) and by the standalone `wizard slack` flow
308
- * (the step is the whole program).
99
+ * The error paragraphs for the browser callback page. The constant framing
100
+ * (title, styles, "return to your terminal") stays in `oauth.ts`; this owns
101
+ * the part that varies by error, with everything HTML-escaped since the
102
+ * values come straight off the callback query string.
309
103
  */
310
- const CONNECT_SLACK_SCOPE_ADDITIONS = ["integration:read"];
311
- /**
312
- * Extra scopes the replay-vision program needs on top of `WIZARD_OAUTH_SCOPES`.
313
- * The same set self-driving's step 6c uses, narrowed to just this flow:
314
- * • replay_scanner:read / replay_scanner:write — the scanner tasks list the
315
- * team's existing scanners and create the ones scoped to the product's key
316
- * flows (`vision-scanners-list` / `-create`, plus the advisory
317
- * `vision-scanners-estimate-create` / `vision-quota-retrieve`). The scope
318
- * OBJECT is `replay_scanner`; `vision-scanners-*` are MCP tool names.
319
- * • session_recording:read — the scanner API pairs it with
320
- * `replay_scanner:*`, since a scanner's config indirectly exposes
321
- * recording contents. Configuring a scanner fails without it.
322
- * • product_enablement:write — the enable-replay task's server half turns on
323
- * Session Replay (`products-enable`) so there are recordings to scan.
324
- *
325
- * Without these the PostHog MCP omits the tools from the catalog it serves
326
- * this token, every scanner task takes its "tool unknown" skip path, and the
327
- * run reports success having created nothing (run 69afc6f8).
328
- *
329
- * No OAuth-ceiling edit needed — all are unprivileged public scope objects
330
- * covered by the apps' `@default` sentinel, and self-driving already requests
331
- * every one of them.
332
- */
333
- const REPLAY_VISION_SCOPE_ADDITIONS = [
334
- "session_recording:read",
335
- "product_enablement:write",
336
- "replay_scanner:read",
337
- "replay_scanner:write"
338
- ];
339
- /**
340
- * Per-program scope additions, layered on top of `WIZARD_OAUTH_SCOPES`.
341
- *
342
- * Programs not listed here request the unchanged base set. Use this
343
- * map only for programs that need *more* than the base — never for
344
- * narrowing, since narrowing risks breaking shared infrastructure
345
- * (e.g. dropping `llm_gateway:read` would 401 every agent call).
346
- *
347
- * Keyed by `ProgramId` so TypeScript catches stale entries when a
348
- * program is renamed or removed.
349
- */
350
- const PROGRAM_SCOPE_ADDITIONS = {
351
- "mcp-tutorial": MCP_TUTORIAL_SCOPE_ADDITIONS,
352
- "agent-skill": AGENT_SKILL_SCOPE_ADDITIONS,
353
- "self-driving": SELF_DRIVING_SCOPE_ADDITIONS,
354
- "warehouse-source": WAREHOUSE_SOURCE_SCOPE_ADDITIONS,
355
- "posthog-integration": [...CONNECT_SLACK_SCOPE_ADDITIONS, ...WAREHOUSE_SOURCE_SCOPE_ADDITIONS],
356
- slack: CONNECT_SLACK_SCOPE_ADDITIONS,
357
- "replay-vision": REPLAY_VISION_SCOPE_ADDITIONS
358
- };
359
- /**
360
- * Resolve the OAuth scope list to request for a given program. Returns
361
- * `WIZARD_OAUTH_SCOPES` for programs without an addition entry; for
362
- * programs that do have one, returns the union of base + additions
363
- * with duplicates dropped (declaration order preserved, base first).
364
- *
365
- * `null` / `undefined` programId falls through to the default — same
366
- * behavior as the historical hardcoded `WIZARD_OAUTH_SCOPES` reference
367
- * in `askForWizardLogin`, so call sites that haven't been updated to
368
- * pass a programId continue to work unchanged.
369
- */
370
- function getOAuthScopesForProgram(programId) {
371
- const additions = programId && PROGRAM_SCOPE_ADDITIONS[programId] || [];
372
- if (additions.length === 0) return WIZARD_OAUTH_SCOPES;
373
- const seen = /* @__PURE__ */ new Set();
374
- const merged = [];
375
- for (const s of [...WIZARD_OAUTH_SCOPES, ...additions]) {
376
- if (seen.has(s)) continue;
377
- seen.add(s);
378
- merged.push(s);
379
- }
380
- return merged;
381
- }
382
- /**
383
- * Resolve the scope list for the signup provisioning path. Same
384
- * base-plus-additions shape as `getOAuthScopesForProgram`, but layered on
385
- * `WIZARD_PROVISIONING_SCOPES` so a program's extra scopes only reach
386
- * tokens provisioned for that program.
387
- */
388
- function getProvisioningScopesForProgram(programId) {
389
- const additions = programId && PROGRAM_SCOPE_ADDITIONS[programId] || [];
390
- if (additions.length === 0) return WIZARD_PROVISIONING_SCOPES;
391
- const seen = /* @__PURE__ */ new Set();
392
- const merged = [];
393
- for (const s of [...WIZARD_PROVISIONING_SCOPES, ...additions]) {
394
- if (seen.has(s)) continue;
395
- seen.add(s);
396
- merged.push(s);
397
- }
398
- return merged;
399
- }
400
- //#endregion
401
- //#region src/shared/host-resolution.ts
402
- /**
403
- * HostResolution — the single, immutable snapshot of where the wizard talks to
404
- * PostHog for one run.
405
- *
406
- * One cloud region (or a pinned `--base-url`) implies a whole family of hosts:
407
- * the event-ingestion/API host, the user-facing web app host, the CDN asset
408
- * host, the LLM gateway, and the MCP server. Rather than re-deriving each of
409
- * these at every call site and threading `region` + `baseUrl` around as loose
410
- * params, resolve once at auth time and pass this frozen object around — read
411
- * the property you need.
412
- *
413
- * The actual region→URL math (and the `--base-url` override) lives in
414
- * `@utils/urls`; this class is a thin, immutable façade over those resolvers so
415
- * the override flows through every field without callers having to know about
416
- * it. The OAuth-server URL stays in `@utils/urls` (`getOAuthUrl`) because it is
417
- * needed *before* a region is known, so it can't come from this post-auth object.
418
- */
419
- const MCP_PROD_URL = "https://mcp.posthog.com/mcp";
420
- function assetHostFor(region, baseUrl) {
421
- const override = resolveBaseUrl(baseUrl);
422
- if (override) return override;
423
- return region === "eu" ? "https://eu-assets.i.posthog.com" : "https://us-assets.i.posthog.com";
424
- }
425
- function assetHostFromApiHost(apiHost) {
426
- if (apiHost.includes("us.i.posthog.com")) return "https://us-assets.i.posthog.com";
427
- if (apiHost.includes("eu.i.posthog.com")) return "https://eu-assets.i.posthog.com";
428
- return apiHost;
429
- }
430
- /**
431
- * Dev/CI only: the env override (see env.ts) wins even under --local-mcp;
432
- * published builds strip the read. Don't name that env var in comments here —
433
- * smoke-test.sh greps dist/*.js for it, and JSDoc survives bundling.
434
- */
435
- function mcpUrlFor(localMcp) {
436
- return localMcp ? MCP_LOCAL_URL : MCP_PROD_URL;
437
- }
438
- var HostResolution = class HostResolution {
439
- /** The resolved cloud region. `'us'` when a base URL is pinned. */
440
- region;
441
- /**
442
- * Event-ingestion / REST API host (e.g. `https://us.i.posthog.com`, or the
443
- * pinned `--base-url`). The SDK `host` written into the user's `.env`, the
444
- * base for the wizard-session REST calls, and the host shown to the agent.
445
- */
446
- apiHost;
447
- /**
448
- * User-facing web app host (e.g. `https://us.posthog.com`). Use for any link
449
- * we hand to the user or open in their browser (dashboards, settings, inbox,
450
- * deep-link base).
451
- */
452
- appHost;
453
- /** CDN asset host (e.g. `https://us-assets.i.posthog.com`). */
454
- assetHost;
455
- /**
456
- * PostHog MCP server URL the agent connects to. Region-independent — the
457
- * server resolves the user's region from the bearer token — so this is driven
458
- * only by `--local-mcp` / `--local-dev` and the dev env override, not by
459
- * region/base-url.
460
- */
461
- mcpUrl;
462
- constructor(fields) {
463
- this.region = fields.region;
464
- this.apiHost = fields.apiHost;
465
- this.appHost = fields.appHost;
466
- this.assetHost = fields.assetHost;
467
- this.mcpUrl = fields.mcpUrl;
468
- Object.freeze(this);
469
- }
470
- /**
471
- * Canonical path: build the host family from a resolved region. A pinned
472
- * `--base-url` (via `opts.baseUrl`) wins for every origin; otherwise the
473
- * region's standard hosts are used. Honors IS_DEV through `resolveBaseUrl`.
474
- */
475
- static fromRegion(region, opts = {}) {
476
- return new HostResolution({
477
- region,
478
- apiHost: getHost(region, opts.baseUrl),
479
- appHost: getCloudUrl(region, opts.baseUrl),
480
- assetHost: assetHostFor(region, opts.baseUrl),
481
- mcpUrl: mcpUrlFor(opts.localMcp ?? false)
482
- });
483
- }
484
- /**
485
- * Build from an ingestion host string (the provisioning API returns one). The
486
- * given host is preserved verbatim as `apiHost`; the region and the derived
487
- * app/asset/gateway hosts are inferred from it.
488
- */
489
- static fromApiHost(apiHost, opts = {}) {
490
- return new HostResolution({
491
- region: apiHost.includes("eu.") ? "eu" : "us",
492
- apiHost,
493
- appHost: getUiHostFromHost(apiHost),
494
- assetHost: assetHostFromApiHost(apiHost),
495
- mcpUrl: mcpUrlFor(opts.localMcp ?? false)
496
- });
497
- }
498
- /**
499
- * Resolve the region from an access token (the us/eu probe, skipped when a
500
- * base URL is pinned), then build the host family. Used after OAuth and after
501
- * CI-mode API-key auth.
502
- */
503
- static async fromAccessToken(accessToken, opts = {}) {
504
- const region = opts.region ?? await detectRegion(accessToken, opts.baseUrl);
505
- return HostResolution.fromRegion(region, opts);
506
- }
507
- };
508
- //#endregion
509
- //#region src/shared/utils/oauth-errors.ts
510
- /**
511
- * OAuth failure presentation — the structured error the flow throws and the
512
- * user-facing messaging built from it.
513
- *
514
- * `oauth.ts` owns the flow machinery (callback server, token exchange, port
515
- * retries); this module owns what a failure *means*: the RFC 6749 error
516
- * fields (`error`, `error_description`, `error_uri`), per-code remediation
517
- * copy, and safe rendering for the browser callback page. Everything here is
518
- * pure so the copy can be unit-tested without the HTTP machinery.
519
- */
520
- /**
521
- * A structured OAuth failure (RFC 6749 §4.1.2.1 authorize errors, §5.2 token
522
- * errors). Thrown by both the authorize callback and the token exchange so
523
- * the flow's error handler keys remediation copy and analytics fingerprints
524
- * off `code` instead of re-parsing `message`.
525
- *
526
- * The message stays exactly `OAuth error: <code>` — the analytics fingerprint
527
- * scheme (`wizard_oauth_<code>`) and the legacy message-prefix parsing both
528
- * depend on that shape. The description/uri ride along as properties.
529
- *
530
- * NAMING: never bind an instance to a variable or property whose name
531
- * contains "oauth" (use `flowError`, `callbackError`, ...). CodeQL's
532
- * clear-text-logging heuristic classifies any `*oauth*`-named variable read
533
- * as password data, and these errors are deliberately shown to the user, so
534
- * such a binding flags every log statement downstream of it (CWE-312).
535
- */
536
- var OAuthError = class extends Error {
537
- code;
538
- /** The server's human-readable explanation (`error_description`), if sent. */
539
- description;
540
- /** The server's "more info" link (`error_uri`), if sent. */
541
- uri;
542
- constructor(code, description, uri) {
543
- super(`OAuth error: ${code}`);
544
- this.name = "OAuthError";
545
- this.code = code;
546
- this.description = description;
547
- this.uri = uri;
548
- }
549
- };
550
- /**
551
- * Sanitize an OAuth error param before logging or rendering it: these arrive
552
- * on a query string (or a token-endpoint body) that anything able to hit the
553
- * local callback port can populate. Strips non-printable/non-ASCII characters
554
- * and caps the length. Returns undefined when nothing printable remains.
555
- */
556
- function sanitizeOAuthParam(value, maxLength = 200) {
557
- if (!value) return void 0;
558
- return value.replace(/[^\x20-\x7e]/g, "").slice(0, maxLength).trim() || void 0;
559
- }
560
- /** Description sentences get more room than bare codes/URIs. */
561
- const MAX_DESCRIPTION_LENGTH = 500;
562
- function escapeHtml(value) {
563
- return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
564
- }
565
- const OAuthErrorBodySchema = z.object({
566
- error: z.string().min(1),
567
- error_description: z.string().optional(),
568
- error_uri: z.string().optional()
569
- });
570
- /**
571
- * Build an `OAuthError` from an authorize-callback's query params.
572
- * Sanitizes every field — the callback URL is attacker-reachable input.
573
- */
574
- function oauthErrorFromCallbackParams(params) {
575
- return new OAuthError(sanitizeOAuthParam(params.get("error")) ?? "unknown", sanitizeOAuthParam(params.get("error_description"), MAX_DESCRIPTION_LENGTH), sanitizeOAuthParam(params.get("error_uri")));
576
- }
577
- /**
578
- * Build an `OAuthError` from a token-endpoint JSON error body (RFC 6749
579
- * §5.2). Returns null when the body isn't an OAuth error shape (HTML error
580
- * pages, proxy bodies, network failures) — the caller rethrows the raw
581
- * error in that case.
582
- */
583
- function oauthErrorFromTokenBody(data) {
584
- const parsed = OAuthErrorBodySchema.safeParse(data);
585
- if (!parsed.success) return null;
586
- return new OAuthError(sanitizeOAuthParam(parsed.data.error) ?? "unknown", sanitizeOAuthParam(parsed.data.error_description, MAX_DESCRIPTION_LENGTH), sanitizeOAuthParam(parsed.data.error_uri));
587
- }
588
- /**
589
- * The error paragraphs for the browser callback page. The constant framing
590
- * (title, styles, "return to your terminal") stays in `oauth.ts`; this owns
591
- * the part that varies by error, with everything HTML-escaped since the
592
- * values come straight off the callback query string.
593
- */
594
- function buildCallbackErrorHtml(error) {
595
- if (error.code === "access_denied") return "<p>Authorization cancelled.</p>";
596
- const paragraphs = [`<p>Authorization failed (${escapeHtml(error.code)}).</p>`];
597
- if (error.description) paragraphs.push(`<p>${escapeHtml(error.description)}</p>`);
598
- if (error.uri) paragraphs.push(`<p>More info: ${escapeHtml(error.uri)}</p>`);
599
- return paragraphs.join("\n");
600
- }
104
+ function buildCallbackErrorHtml(error) {
105
+ if (error.code === "access_denied") return "<p>Authorization cancelled.</p>";
106
+ const paragraphs = [`<p>Authorization failed (${escapeHtml(error.code)}).</p>`];
107
+ if (error.description) paragraphs.push(`<p>${escapeHtml(error.description)}</p>`);
108
+ if (error.uri) paragraphs.push(`<p>More info: ${escapeHtml(error.uri)}</p>`);
109
+ return paragraphs.join("\n");
110
+ }
601
111
  /**
602
112
  * Per-code headline + "what to do" remediation for the terminal failure
603
113
  * message. Codes not listed fall through to a generic rendering of the
@@ -658,29 +168,7 @@ function buildOAuthFailureMessage(params) {
658
168
  return sections.join("\n\n");
659
169
  }
660
170
  //#endregion
661
- //#region src/shared/utils/oauth.ts
662
- const OAUTH_CALLBACK_STYLES = `
663
- <style>
664
- * {
665
- font-family: monospace;
666
- background-color: #1b0a00;
667
- color: #F7A502;
668
- font-weight: medium;
669
- font-size: 24px;
670
- margin: .25rem;
671
- }
672
-
673
- .blink {
674
- animation: blink-animation 1s steps(2, start) infinite;
675
- }
676
-
677
- @keyframes blink-animation {
678
- to {
679
- opacity: 0;
680
- }
681
- }
682
- </style>
683
- `;
171
+ //#region src/programs/oauth/tokens.ts
684
172
  const OAuthTokenResponseSchema = z.object({
685
173
  access_token: z.string(),
686
174
  expires_in: z.number(),
@@ -692,7 +180,6 @@ const OAuthTokenResponseSchema = z.object({
692
180
  posthog_region: z.enum(["us", "eu"]).optional().catch(void 0),
693
181
  posthog_base_url: z.string().optional().catch(void 0)
694
182
  });
695
- const WIZARD_COMPLETION_SCOPE = "event_definition:write";
696
183
  function parseOAuthScopes(scope) {
697
184
  return scope.split(/\s+/).filter(Boolean);
698
185
  }
@@ -710,14 +197,6 @@ function missingOAuthScopes(requested, grantedScope) {
710
197
  const granted = new Set(parseOAuthScopes(grantedScope));
711
198
  return requested.filter((scope) => !granted.has(scope));
712
199
  }
713
- function assertWizardCompletionScope(scope) {
714
- if (parseOAuthScopes(scope).includes("event_definition:write")) return;
715
- throw new Error(`This run was authorized without the ${WIZARD_COMPLETION_SCOPE} permission, which the wizard needs to finish setup. Please try again, approving all permissions on the PostHog authorization screen. If that screen does not reappear, revoke the existing PostHog Wizard authorization in your PostHog settings first.`);
716
- }
717
- const AUTHORIZATION_TIMEOUT_MESSAGE = "Authorization timed out";
718
- function isAuthorizationTimeout(error) {
719
- return error.message === AUTHORIZATION_TIMEOUT_MESSAGE;
720
- }
721
200
  /**
722
201
  * OAuth client ID for the current target. A pinned base URL (`--base-url`, or
723
202
  * IS_DEV's implicit localhost) means we're talking to a dev-seeded stack, which
@@ -731,42 +210,98 @@ function isAuthorizationTimeout(error) {
731
210
  function getOAuthClientId(baseUrl) {
732
211
  return resolveBaseUrl(baseUrl) ? POSTHOG_DEV_CLIENT_ID : POSTHOG_PROXY_CLIENT_ID;
733
212
  }
734
- function getLocalOAuthOrigin(port) {
735
- return `http://localhost:${port}`;
736
- }
737
- function getCallbackUrl(port) {
738
- return `${getLocalOAuthOrigin(port)}/callback`;
739
- }
740
- function getLocalLoginUrl(port) {
741
- return `${getLocalOAuthOrigin(port)}/authorize`;
742
- }
743
- function getLocalSignupUrl(port) {
744
- return `${getLocalLoginUrl(port)}?signup=true`;
745
- }
746
- /**
747
- * Extract an OAuth authorization code from raw user input. Accepts either the
748
- * bare code, the full callback URL the browser was redirected to
749
- * (`http://localhost:8239/callback?code=abc123&...`), or just the query
750
- * string. Returns null when no code can be found.
751
- *
752
- * This backs the manual-entry fallback: in headless/remote environments the
753
- * browser can't reach the wizard's local callback server, so the user copies
754
- * the failed callback URL (or the code from it) back into the terminal.
755
- */
756
- function extractOAuthCode(input) {
757
- const trimmed = input.trim();
758
- if (!trimmed) return null;
759
- let looksLikeUrl = false;
213
+ async function refreshAccessToken(refreshToken, baseUrl, clientId) {
214
+ const oauthUrl = getOAuthUrl(baseUrl);
215
+ logToFile(`[oauth] refreshing access token at ${oauthUrl}/oauth/token`);
760
216
  try {
761
- const url = new URL(trimmed);
762
- looksLikeUrl = true;
763
- const code = url.searchParams.get("code");
764
- if (code) return code;
765
- } catch {}
766
- const match = trimmed.match(/[?&]?code=([^&\s]+)/);
767
- if (match) return decodeURIComponent(match[1]);
768
- if (looksLikeUrl) return null;
769
- if (!/\s/.test(trimmed)) return trimmed;
217
+ const response = await axios.post(`${oauthUrl}/oauth/token`, {
218
+ grant_type: "refresh_token",
219
+ refresh_token: refreshToken,
220
+ client_id: clientId ?? getOAuthClientId(baseUrl)
221
+ }, {
222
+ headers: {
223
+ "Content-Type": "application/json",
224
+ "User-Agent": WIZARD_USER_AGENT
225
+ },
226
+ timeout: 3e4
227
+ });
228
+ const token = OAuthTokenResponseSchema.parse(response.data);
229
+ logToFile("[oauth] access token refreshed");
230
+ return token;
231
+ } catch (e) {
232
+ logToFile("[oauth] token refresh failed:", e instanceof Error ? e.message : e);
233
+ throw (axios.isAxiosError(e) ? oauthErrorFromTokenBody(e.response?.data) : null) ?? e;
234
+ }
235
+ }
236
+ //#endregion
237
+ //#region src/tui/auth/oauth.ts
238
+ const OAUTH_CALLBACK_STYLES = `
239
+ <style>
240
+ * {
241
+ font-family: monospace;
242
+ background-color: #1b0a00;
243
+ color: #F7A502;
244
+ font-weight: medium;
245
+ font-size: 24px;
246
+ margin: .25rem;
247
+ }
248
+
249
+ .blink {
250
+ animation: blink-animation 1s steps(2, start) infinite;
251
+ }
252
+
253
+ @keyframes blink-animation {
254
+ to {
255
+ opacity: 0;
256
+ }
257
+ }
258
+ </style>
259
+ `;
260
+ const WIZARD_COMPLETION_SCOPE = "event_definition:write";
261
+ function assertWizardCompletionScope(scope) {
262
+ if (parseOAuthScopes(scope).includes("event_definition:write")) return;
263
+ throw new Error(`This run was authorized without the ${WIZARD_COMPLETION_SCOPE} permission, which the wizard needs to finish setup. Please try again, approving all permissions on the PostHog authorization screen. If that screen does not reappear, revoke the existing PostHog Wizard authorization in your PostHog settings first.`);
264
+ }
265
+ const AUTHORIZATION_TIMEOUT_MESSAGE = "Authorization timed out";
266
+ function isAuthorizationTimeout(error) {
267
+ return error.message === AUTHORIZATION_TIMEOUT_MESSAGE;
268
+ }
269
+ function getLocalOAuthOrigin(port) {
270
+ return `http://localhost:${port}`;
271
+ }
272
+ function getCallbackUrl(port) {
273
+ return `${getLocalOAuthOrigin(port)}/callback`;
274
+ }
275
+ function getLocalLoginUrl(port) {
276
+ return `${getLocalOAuthOrigin(port)}/authorize`;
277
+ }
278
+ function getLocalSignupUrl(port) {
279
+ return `${getLocalLoginUrl(port)}?signup=true`;
280
+ }
281
+ /**
282
+ * Extract an OAuth authorization code from raw user input. Accepts either the
283
+ * bare code, the full callback URL the browser was redirected to
284
+ * (`http://localhost:8239/callback?code=abc123&...`), or just the query
285
+ * string. Returns null when no code can be found.
286
+ *
287
+ * This backs the manual-entry fallback: in headless/remote environments the
288
+ * browser can't reach the wizard's local callback server, so the user copies
289
+ * the failed callback URL (or the code from it) back into the terminal.
290
+ */
291
+ function extractOAuthCode(input) {
292
+ const trimmed = input.trim();
293
+ if (!trimmed) return null;
294
+ let looksLikeUrl = false;
295
+ try {
296
+ const url = new URL(trimmed);
297
+ looksLikeUrl = true;
298
+ const code = url.searchParams.get("code");
299
+ if (code) return code;
300
+ } catch {}
301
+ const match = trimmed.match(/[?&]?code=([^&\s]+)/);
302
+ if (match) return decodeURIComponent(match[1]);
303
+ if (looksLikeUrl) return null;
304
+ if (!/\s/.test(trimmed)) return trimmed;
770
305
  return null;
771
306
  }
772
307
  function generateCodeVerifier() {
@@ -964,142 +499,374 @@ async function exchangeCodeForToken(code, codeVerifier, callbackUrl, baseUrl) {
964
499
  logToFile(`[oauth] token exchange succeeded, granted scopes: ${token.scope}${token.posthog_region ? `, region: ${token.posthog_region}` : ""}${token.scoped_teams ? `, scoped_teams: [${token.scoped_teams.join(", ")}]` : ""}${token.scoped_organizations ? `, scoped_organizations: ${token.scoped_organizations.length}` : ""}`);
965
500
  return token;
966
501
  }
967
- async function refreshAccessToken(refreshToken, baseUrl, clientId) {
968
- const oauthUrl = getOAuthUrl(baseUrl);
969
- logToFile(`[oauth] refreshing access token at ${oauthUrl}/oauth/token`);
970
- try {
971
- const response = await axios.post(`${oauthUrl}/oauth/token`, {
972
- grant_type: "refresh_token",
973
- refresh_token: refreshToken,
974
- client_id: clientId ?? getOAuthClientId(baseUrl)
975
- }, {
976
- headers: {
977
- "Content-Type": "application/json",
978
- "User-Agent": WIZARD_USER_AGENT
979
- },
980
- timeout: 3e4
981
- });
982
- const token = OAuthTokenResponseSchema.parse(response.data);
983
- logToFile("[oauth] access token refreshed");
984
- return token;
985
- } catch (e) {
986
- logToFile("[oauth] token refresh failed:", e instanceof Error ? e.message : e);
987
- throw (axios.isAxiosError(e) ? oauthErrorFromTokenBody(e.response?.data) : null) ?? e;
502
+ /**
503
+ * Warn — at login, while the user is still watching — when the grant came back
504
+ * narrower than the request, and record the gap so narrowed runs are countable.
505
+ * Non-fatal by design: deselecting an optional scope is the user's call, and
506
+ * most flows survive it. The one scope the wizard cannot run without has its
507
+ * own hard check (`assertWizardCompletionScope`).
508
+ */
509
+ function reportNarrowedGrant(requestedScopes, grantedScope) {
510
+ const missing = missingOAuthScopes(requestedScopes, grantedScope);
511
+ if (missing.length === 0) return;
512
+ logToFile(`[oauth] grant narrower than request, missing: ${missing.join(" ")}`);
513
+ analytics.wizardCapture("oauth grant narrowed", {
514
+ requested_scopes: [...requestedScopes].sort().join(" "),
515
+ granted_scopes: parseOAuthScopes(grantedScope).sort().join(" "),
516
+ missing_scopes: missing.join(" "),
517
+ missing_scope_count: missing.length
518
+ });
519
+ const plural = missing.length > 1;
520
+ getUI().log.warn(`Your PostHog authorization is missing ${plural ? `${missing.length} permissions` : "a permission"} the wizard asked for: ${missing.join(", ")}. Setup will continue, but steps that need ${plural ? "them" : "it"} may fail. To grant ${plural ? "them" : "it"}, re-run the wizard and approve all permissions on the authorization screen. If that screen does not reappear, revoke the existing PostHog Wizard authorization in your PostHog settings first.`);
521
+ }
522
+ async function performOAuthFlow(config) {
523
+ const clientId = getOAuthClientId(config.baseUrl);
524
+ const oauthUrl = getOAuthUrl(config.baseUrl);
525
+ const codeVerifier = generateCodeVerifier();
526
+ const codeChallenge = generateCodeChallenge(codeVerifier);
527
+ let shouldRetry = false;
528
+ logToFile(`[oauth] starting flow against ${oauthUrl}, requested scopes: ${config.scopes.join(" ")}`);
529
+ do {
530
+ shouldRetry = false;
531
+ let lastProcessInfo = null;
532
+ for (const port of OAUTH_PORTS) {
533
+ const callbackUrl = getCallbackUrl(port);
534
+ const authUrl = new URL(`${oauthUrl}/oauth/authorize`);
535
+ authUrl.searchParams.set("client_id", clientId);
536
+ authUrl.searchParams.set("redirect_uri", callbackUrl);
537
+ authUrl.searchParams.set("response_type", "code");
538
+ authUrl.searchParams.set("code_challenge", codeChallenge);
539
+ authUrl.searchParams.set("code_challenge_method", "S256");
540
+ authUrl.searchParams.set("scope", config.scopes.join(" "));
541
+ authUrl.searchParams.set("required_access_level", "project");
542
+ if (config.projectId !== void 0) authUrl.searchParams.set("team_id", String(config.projectId));
543
+ const taggedAuthUrl = withUtm(authUrl.toString(), "oauth-authorize");
544
+ const signupUrl = new URL(withUtm(`${oauthUrl}/signup?next=${encodeURIComponent(taggedAuthUrl)}`, "oauth-signup"));
545
+ const localSignupUrl = getLocalSignupUrl(port);
546
+ const localLoginUrl = getLocalLoginUrl(port);
547
+ const urlToOpen = config.signup ? localSignupUrl : localLoginUrl;
548
+ logToFile(`[oauth] attempting callback server on port ${port}`);
549
+ let server;
550
+ let waitForCallback;
551
+ try {
552
+ ({server, waitForCallback} = await startCallbackServer(taggedAuthUrl, signupUrl.toString(), port));
553
+ } catch (e) {
554
+ if (!isPortInUseError(e)) throw e;
555
+ lastProcessInfo = getPortProcessInfo(port);
556
+ continue;
557
+ }
558
+ logToFile("[oauth] callback server ready, showing login URL");
559
+ getUI().setLoginUrl(urlToOpen);
560
+ getUI().setAuthorizeUrl(config.signup ? signupUrl.toString() : taggedAuthUrl);
561
+ openTrackedLink(urlToOpen, "oauth", {
562
+ auto: true,
563
+ skipUtm: true
564
+ });
565
+ const loginSpinner = getUI().spinner();
566
+ loginSpinner.start("Waiting for authorization...");
567
+ try {
568
+ const token = await exchangeCodeForToken(await Promise.race([
569
+ waitForCallback(),
570
+ getUI().waitForManualAuthCode(),
571
+ new Promise((_, reject) => setTimeout(() => reject(new Error(AUTHORIZATION_TIMEOUT_MESSAGE)), OAUTH_TIMEOUT_MS))
572
+ ]), codeVerifier, callbackUrl, config.baseUrl);
573
+ server.close();
574
+ getUI().setLoginUrl(null);
575
+ getUI().setAuthorizeUrl(null);
576
+ loginSpinner.stop("Authorization complete!");
577
+ reportNarrowedGrant(config.scopes, token.scope);
578
+ return token;
579
+ } catch (e) {
580
+ const error = e instanceof Error ? e : /* @__PURE__ */ new Error("Unknown error");
581
+ const timedOut = isAuthorizationTimeout(error);
582
+ const flowError = error instanceof OAuthError ? error : null;
583
+ loginSpinner.stop(timedOut ? "Session timed out." : "Authorization failed.");
584
+ server.close();
585
+ logToFile("[oauth] flow failed:", error);
586
+ if (flowError?.description) logToFile(`[oauth] server error_description: ${flowError.description}`);
587
+ const accessDenied = flowError ? flowError.code === "access_denied" : error.message.includes("access_denied");
588
+ if (timedOut) getUI().showSessionTimeout();
589
+ else if (accessDenied) getUI().log.info(`Authorization was cancelled.\n\nYou denied access to PostHog. To use the wizard, you need to authorize access to your PostHog account.\n\nYou can try again by re-running the wizard.`);
590
+ else getUI().log.error(buildOAuthFailureMessage({
591
+ error,
592
+ requestedScopes: config.scopes,
593
+ clientId,
594
+ oauthUrl,
595
+ isDevStack: resolveBaseUrl(config.baseUrl) !== void 0
596
+ }));
597
+ const oauthErrorCode = flowError ? flowError.code : error.message.startsWith("OAuth error: ") ? error.message.slice(13) : timedOut ? "timeout" : "unknown";
598
+ analytics.captureException(error, {
599
+ step: "oauth_flow",
600
+ oauth_error_code: oauthErrorCode,
601
+ oauth_error_description: flowError?.description,
602
+ client_id: clientId,
603
+ requested_scopes: config.scopes.join(" "),
604
+ $exception_fingerprint: `wizard_oauth_${oauthErrorCode}`
605
+ });
606
+ await abort();
607
+ throw error;
608
+ }
609
+ }
610
+ if (!lastProcessInfo) throw new Error("No OAuth callback ports configured");
611
+ await getUI().showPortConflict(lastProcessInfo);
612
+ shouldRetry = true;
613
+ } while (shouldRetry);
614
+ throw new Error("OAuth port retry loop exited unexpectedly");
615
+ }
616
+ //#endregion
617
+ //#region src/programs/agent-skill/scopes.ts
618
+ /**
619
+ * Extra scopes the agent-skill program needs on top of `WIZARD_OAUTH_SCOPES`.
620
+ *
621
+ * Skills under this program (e.g. `creating-product-tours`) create feature
622
+ * flags during the install flow. PostHog's consent grants exactly the scope
623
+ * strings requested — `:write` does not imply `:read` — so listing existing
624
+ * flags to avoid key collisions needs `feature_flag:read` explicitly.
625
+ * `property_definition:read` lets the agent discover person properties when
626
+ * building flag rollout filters instead of having to ask the user verbatim.
627
+ */
628
+ const AGENT_SKILL_SCOPE_ADDITIONS = [
629
+ "feature_flag:read",
630
+ "feature_flag:write",
631
+ "property_definition:read"
632
+ ];
633
+ //#endregion
634
+ //#region src/programs/replay-vision/scopes.ts
635
+ /**
636
+ * Extra scopes the replay-vision program needs on top of `WIZARD_OAUTH_SCOPES`.
637
+ * The same set self-driving's step 6c uses, narrowed to just this flow:
638
+ * • replay_scanner:read / replay_scanner:write — the scanner tasks list the
639
+ * team's existing scanners and create the ones scoped to the product's key
640
+ * flows (`vision-scanners-list` / `-create`, plus the advisory
641
+ * `vision-scanners-estimate-create` / `vision-quota-retrieve`). The scope
642
+ * OBJECT is `replay_scanner`; `vision-scanners-*` are MCP tool names.
643
+ * • session_recording:read — the scanner API pairs it with
644
+ * `replay_scanner:*`, since a scanner's config indirectly exposes
645
+ * recording contents. Configuring a scanner fails without it.
646
+ * • product_enablement:write — the enable-replay task's server half turns on
647
+ * Session Replay (`products-enable`) so there are recordings to scan.
648
+ *
649
+ * Without these the PostHog MCP omits the tools from the catalog it serves
650
+ * this token, every scanner task takes its "tool unknown" skip path, and the
651
+ * run reports success having created nothing (run 69afc6f8).
652
+ *
653
+ * No OAuth-ceiling edit needed — all are unprivileged public scope objects
654
+ * covered by the apps' `@default` sentinel, and self-driving already requests
655
+ * every one of them.
656
+ */
657
+ const REPLAY_VISION_SCOPE_ADDITIONS = [
658
+ "session_recording:read",
659
+ "product_enablement:write",
660
+ "replay_scanner:read",
661
+ "replay_scanner:write"
662
+ ];
663
+ //#endregion
664
+ //#region src/programs/self-driving/scopes.ts
665
+ /**
666
+ * Extra scopes the self-driving program needs on top of
667
+ * `WIZARD_OAUTH_SCOPES`. All consumed by the PostHog MCP tools the
668
+ * agent drives during the run:
669
+ * • task:read / task:write — the signal source config API
670
+ * (`inbox-source-configs-*`) is permissioned under the generic
671
+ * `task` scope object, NOT a signals-specific one. Unrelated to
672
+ * the Tasks product.
673
+ * • integration:read — `integrations-list`, to check whether the
674
+ * team already has a GitHub integration and to verify the connect
675
+ * flow completed.
676
+ * • signal_scout:read / signal_scout:write — list, sync, and tune
677
+ * the Signals scout troop (`signals-scout-config-*`).
678
+ * • session_recording:read / survey:read / error_tracking:read —
679
+ * server-side product-usage probes (`query-session-recordings-list`,
680
+ * `survey-list`, `error-issue-list`). Product usage is a
681
+ * project-level fact (often instrumented in another repo or via
682
+ * the snippet), so the agent asks the server instead of inferring
683
+ * only from the local setup report. All three are read-only and
684
+ * already in the wizard OAuth app's production scope ceiling (the
685
+ * mcp-tutorial program requests them).
686
+ * • external_data_source:read / external_data_source:write — the
687
+ * connected-tools step creates the GitHub Issues / Linear warehouse
688
+ * sources directly (`external-data-sources-create`) and verifies
689
+ * what's actually connected (`external-data-sources-list`) instead
690
+ * of taking the user's word for it.
691
+ * • llm_skill:read / llm_skill:write — the custom-scouts step
692
+ * (skill step 6b): read the seeded `authoring-signals-scouts`
693
+ * guide and canonical scout bodies (`llma-skill-get` /
694
+ * `llma-skill-file-get`) and author the user-approved custom
695
+ * `signals-scout-*` skills (`llma-skill-create`). Canonical scout
696
+ * bodies are never edited.
697
+ * • product_enablement:write — the "Enable products" step turns on
698
+ * Session Replay / Error Tracking / Support so their sources have
699
+ * data to read (`products-enable`). A purpose-built scope: the
700
+ * server owns each enable recipe, so this can flip the product
701
+ * toggles without the far broader `project:write`.
702
+ * • replay_scanner:read / replay_scanner:write — the Replay Vision
703
+ * scanners step (skill step 6c) lists the team's existing scanners
704
+ * and creates the `emits_signals` ones whose findings land in the
705
+ * inbox (`vision-scanners-list` / `-create` / `-update`, and the
706
+ * advisory `vision-scanners-estimate-create` / `vision-quota-retrieve`).
707
+ * The scope OBJECT is `replay_scanner` — the `vision-scanners-*`
708
+ * names are MCP tool names, not scopes. Configuring a scanner also
709
+ * requires `session_recording:read` (the API pairs the two, since a
710
+ * scanner's config indirectly exposes recording contents); that one
711
+ * is already in this list for the step-2 usage probes.
712
+ *
713
+ * No OAuth-ceiling edit is needed for any scope here: they are all normal
714
+ * public (unprivileged, non-internal, non-hidden) scope objects, and the
715
+ * live wizard apps' ceiling is the `@default` sentinel, which resolves to
716
+ * every such scope (`UNPRIVILEGED_SCOPES`) and auto-tracks new ones. Only a
717
+ * privileged/internal/hidden object (e.g. `llm_gateway:*`) would need a
718
+ * manual per-app edit. See README → "OAuth app scope ceiling".
719
+ */
720
+ const SELF_DRIVING_SCOPE_ADDITIONS = [
721
+ "task:read",
722
+ "task:write",
723
+ "integration:read",
724
+ "signal_scout:read",
725
+ "signal_scout:write",
726
+ "session_recording:read",
727
+ "survey:read",
728
+ "error_tracking:read",
729
+ "external_data_source:read",
730
+ "external_data_source:write",
731
+ "llm_skill:read",
732
+ "llm_skill:write",
733
+ "product_enablement:write",
734
+ "replay_scanner:read",
735
+ "replay_scanner:write"
736
+ ];
737
+ //#endregion
738
+ //#region src/shared/oauth-scopes.ts
739
+ /**
740
+ * Extra scope the Connect-Slack step needs on top of `WIZARD_OAUTH_SCOPES`.
741
+ *
742
+ * The step polls `/api/projects/:id/integrations/` (`fetchSlackConnected`)
743
+ * to render the already-connected variant and to flip live once the user
744
+ * completes the Slack OAuth step in the browser. Without `integration:read`
745
+ * the first poll 403s, the screen stops polling, and an already-connected
746
+ * project is nagged with the connect nudge. Used by the default integration
747
+ * run (the step ends the run) and by the standalone `wizard slack` flow
748
+ * (the step is the whole program).
749
+ */
750
+ const CONNECT_SLACK_SCOPE_ADDITIONS = ["integration:read"];
751
+ //#endregion
752
+ //#region src/tools/mcp/scopes.ts
753
+ /**
754
+ * Extra scopes the MCP tutorial needs on top of `WIZARD_OAUTH_SCOPES`.
755
+ *
756
+ * Every scope requested here must stay within the wizard OAuth app's
757
+ * ceiling on the PostHog side (`OAuthApplication.scopes`) — the full
758
+ * list lives in the README under "OAuth app scope ceiling". The
759
+ * tutorial's prompts and follow-ups touch most of the read surface,
760
+ * plus annotation write for the "PostHog wizard install" verify-prompt.
761
+ *
762
+ * Already in the base `WIZARD_OAUTH_SCOPES` (and therefore not
763
+ * repeated here):
764
+ * • user:read, project:read, llm_gateway:read — auth + gateway
765
+ * • query:read — HogQL
766
+ * • dashboard:write, insight:write, notebook:write — Phase-5 persist
767
+ *
768
+ * Deliberately omitted (writes on read-only product surfaces):
769
+ * • feature_flag:write, experiment:write, survey:write,
770
+ * cohort:write, session_recording:write, error_tracking:write,
771
+ * alert:write, subscription:write
772
+ */
773
+ const MCP_TUTORIAL_SCOPE_ADDITIONS = [
774
+ "dashboard:read",
775
+ "insight:read",
776
+ "notebook:read",
777
+ "feature_flag:read",
778
+ "experiment:read",
779
+ "experiment_saved_metric:read",
780
+ "survey:read",
781
+ "session_recording:read",
782
+ "error_tracking:read",
783
+ "web_analytics:read",
784
+ "llm_analytics:read",
785
+ "cohort:read",
786
+ "person:read",
787
+ "annotation:read",
788
+ "annotation:write",
789
+ "activity_log:read",
790
+ "property_definition:read",
791
+ "event_definition:read",
792
+ "action:read",
793
+ "warehouse_table:read",
794
+ "warehouse_view:read",
795
+ "alert:read",
796
+ "subscription:read",
797
+ "integration:read"
798
+ ];
799
+ //#endregion
800
+ //#region src/programs/oauth/program-scopes.ts
801
+ /**
802
+ * Extra scopes the warehouse-source program needs on top of
803
+ * `WIZARD_OAUTH_SCOPES`. The agent creates data warehouse sources directly
804
+ * (`external-data-sources-create`) and lists what's connected
805
+ * (`external-data-sources-list`) to verify the result. Both are already within
806
+ * the wizard OAuth app's scope ceiling — the self-driving program requests the
807
+ * same pair.
808
+ */
809
+ const WAREHOUSE_SOURCE_SCOPE_ADDITIONS = ["external_data_source:read", "external_data_source:write"];
810
+ /**
811
+ * Per-program scope additions, layered on top of `WIZARD_OAUTH_SCOPES`.
812
+ *
813
+ * Programs not listed here request the unchanged base set. Use this
814
+ * map only for programs that need *more* than the base — never for
815
+ * narrowing, since narrowing risks breaking shared infrastructure
816
+ * (e.g. dropping `llm_gateway:read` would 401 every agent call).
817
+ *
818
+ * Keyed by `ProgramId` so TypeScript catches stale entries when a
819
+ * program is renamed or removed.
820
+ */
821
+ const PROGRAM_SCOPE_ADDITIONS = {
822
+ "mcp-tutorial": MCP_TUTORIAL_SCOPE_ADDITIONS,
823
+ "agent-skill": AGENT_SKILL_SCOPE_ADDITIONS,
824
+ "self-driving": SELF_DRIVING_SCOPE_ADDITIONS,
825
+ "warehouse-source": WAREHOUSE_SOURCE_SCOPE_ADDITIONS,
826
+ "posthog-integration": [...CONNECT_SLACK_SCOPE_ADDITIONS, ...WAREHOUSE_SOURCE_SCOPE_ADDITIONS],
827
+ slack: CONNECT_SLACK_SCOPE_ADDITIONS,
828
+ "replay-vision": REPLAY_VISION_SCOPE_ADDITIONS
829
+ };
830
+ /**
831
+ * Resolve the OAuth scope list to request for a given program. Returns
832
+ * `WIZARD_OAUTH_SCOPES` for programs without an addition entry; for
833
+ * programs that do have one, returns the union of base + additions
834
+ * with duplicates dropped (declaration order preserved, base first).
835
+ *
836
+ * `null` / `undefined` programId falls through to the default — same
837
+ * behavior as the historical hardcoded `WIZARD_OAUTH_SCOPES` reference
838
+ * in `askForWizardLogin`, so call sites that haven't been updated to
839
+ * pass a programId continue to work unchanged.
840
+ */
841
+ function getOAuthScopesForProgram(programId) {
842
+ const additions = programId && PROGRAM_SCOPE_ADDITIONS[programId] || [];
843
+ if (additions.length === 0) return WIZARD_OAUTH_SCOPES;
844
+ const seen = /* @__PURE__ */ new Set();
845
+ const merged = [];
846
+ for (const s of [...WIZARD_OAUTH_SCOPES, ...additions]) {
847
+ if (seen.has(s)) continue;
848
+ seen.add(s);
849
+ merged.push(s);
988
850
  }
851
+ return merged;
989
852
  }
990
853
  /**
991
- * Warn — at login, while the user is still watching — when the grant came back
992
- * narrower than the request, and record the gap so narrowed runs are countable.
993
- * Non-fatal by design: deselecting an optional scope is the user's call, and
994
- * most flows survive it. The one scope the wizard cannot run without has its
995
- * own hard check (`assertWizardCompletionScope`).
854
+ * Resolve the scope list for the signup provisioning path. Same
855
+ * base-plus-additions shape as `getOAuthScopesForProgram`, but layered on
856
+ * `WIZARD_PROVISIONING_SCOPES` so a program's extra scopes only reach
857
+ * tokens provisioned for that program.
996
858
  */
997
- function reportNarrowedGrant(requestedScopes, grantedScope) {
998
- const missing = missingOAuthScopes(requestedScopes, grantedScope);
999
- if (missing.length === 0) return;
1000
- logToFile(`[oauth] grant narrower than request, missing: ${missing.join(" ")}`);
1001
- analytics.wizardCapture("oauth grant narrowed", {
1002
- requested_scopes: [...requestedScopes].sort().join(" "),
1003
- granted_scopes: parseOAuthScopes(grantedScope).sort().join(" "),
1004
- missing_scopes: missing.join(" "),
1005
- missing_scope_count: missing.length
1006
- });
1007
- const plural = missing.length > 1;
1008
- getUI().log.warn(`Your PostHog authorization is missing ${plural ? `${missing.length} permissions` : "a permission"} the wizard asked for: ${missing.join(", ")}. Setup will continue, but steps that need ${plural ? "them" : "it"} may fail. To grant ${plural ? "them" : "it"}, re-run the wizard and approve all permissions on the authorization screen. If that screen does not reappear, revoke the existing PostHog Wizard authorization in your PostHog settings first.`);
1009
- }
1010
- async function performOAuthFlow(config) {
1011
- const clientId = getOAuthClientId(config.baseUrl);
1012
- const oauthUrl = getOAuthUrl(config.baseUrl);
1013
- const codeVerifier = generateCodeVerifier();
1014
- const codeChallenge = generateCodeChallenge(codeVerifier);
1015
- let shouldRetry = false;
1016
- logToFile(`[oauth] starting flow against ${oauthUrl}, requested scopes: ${config.scopes.join(" ")}`);
1017
- do {
1018
- shouldRetry = false;
1019
- let lastProcessInfo = null;
1020
- for (const port of OAUTH_PORTS) {
1021
- const callbackUrl = getCallbackUrl(port);
1022
- const authUrl = new URL(`${oauthUrl}/oauth/authorize`);
1023
- authUrl.searchParams.set("client_id", clientId);
1024
- authUrl.searchParams.set("redirect_uri", callbackUrl);
1025
- authUrl.searchParams.set("response_type", "code");
1026
- authUrl.searchParams.set("code_challenge", codeChallenge);
1027
- authUrl.searchParams.set("code_challenge_method", "S256");
1028
- authUrl.searchParams.set("scope", config.scopes.join(" "));
1029
- authUrl.searchParams.set("required_access_level", "project");
1030
- if (config.projectId !== void 0) authUrl.searchParams.set("team_id", String(config.projectId));
1031
- const taggedAuthUrl = withUtm(authUrl.toString(), "oauth-authorize");
1032
- const signupUrl = new URL(withUtm(`${oauthUrl}/signup?next=${encodeURIComponent(taggedAuthUrl)}`, "oauth-signup"));
1033
- const localSignupUrl = getLocalSignupUrl(port);
1034
- const localLoginUrl = getLocalLoginUrl(port);
1035
- const urlToOpen = config.signup ? localSignupUrl : localLoginUrl;
1036
- logToFile(`[oauth] attempting callback server on port ${port}`);
1037
- let server;
1038
- let waitForCallback;
1039
- try {
1040
- ({server, waitForCallback} = await startCallbackServer(taggedAuthUrl, signupUrl.toString(), port));
1041
- } catch (e) {
1042
- if (!isPortInUseError(e)) throw e;
1043
- lastProcessInfo = getPortProcessInfo(port);
1044
- continue;
1045
- }
1046
- logToFile("[oauth] callback server ready, showing login URL");
1047
- getUI().setLoginUrl(urlToOpen);
1048
- getUI().setAuthorizeUrl(config.signup ? signupUrl.toString() : taggedAuthUrl);
1049
- openTrackedLink(urlToOpen, "oauth", {
1050
- auto: true,
1051
- skipUtm: true
1052
- });
1053
- const loginSpinner = getUI().spinner();
1054
- loginSpinner.start("Waiting for authorization...");
1055
- try {
1056
- const token = await exchangeCodeForToken(await Promise.race([
1057
- waitForCallback(),
1058
- getUI().waitForManualAuthCode(),
1059
- new Promise((_, reject) => setTimeout(() => reject(new Error(AUTHORIZATION_TIMEOUT_MESSAGE)), OAUTH_TIMEOUT_MS))
1060
- ]), codeVerifier, callbackUrl, config.baseUrl);
1061
- server.close();
1062
- getUI().setLoginUrl(null);
1063
- getUI().setAuthorizeUrl(null);
1064
- loginSpinner.stop("Authorization complete!");
1065
- reportNarrowedGrant(config.scopes, token.scope);
1066
- return token;
1067
- } catch (e) {
1068
- const error = e instanceof Error ? e : /* @__PURE__ */ new Error("Unknown error");
1069
- const timedOut = isAuthorizationTimeout(error);
1070
- const flowError = error instanceof OAuthError ? error : null;
1071
- loginSpinner.stop(timedOut ? "Session timed out." : "Authorization failed.");
1072
- server.close();
1073
- logToFile("[oauth] flow failed:", error);
1074
- if (flowError?.description) logToFile(`[oauth] server error_description: ${flowError.description}`);
1075
- const accessDenied = flowError ? flowError.code === "access_denied" : error.message.includes("access_denied");
1076
- if (timedOut) getUI().showSessionTimeout();
1077
- else if (accessDenied) getUI().log.info(`Authorization was cancelled.\n\nYou denied access to PostHog. To use the wizard, you need to authorize access to your PostHog account.\n\nYou can try again by re-running the wizard.`);
1078
- else getUI().log.error(buildOAuthFailureMessage({
1079
- error,
1080
- requestedScopes: config.scopes,
1081
- clientId,
1082
- oauthUrl,
1083
- isDevStack: resolveBaseUrl(config.baseUrl) !== void 0
1084
- }));
1085
- const oauthErrorCode = flowError ? flowError.code : error.message.startsWith("OAuth error: ") ? error.message.slice(13) : timedOut ? "timeout" : "unknown";
1086
- analytics.captureException(error, {
1087
- step: "oauth_flow",
1088
- oauth_error_code: oauthErrorCode,
1089
- oauth_error_description: flowError?.description,
1090
- client_id: clientId,
1091
- requested_scopes: config.scopes.join(" "),
1092
- $exception_fingerprint: `wizard_oauth_${oauthErrorCode}`
1093
- });
1094
- await abort();
1095
- throw error;
1096
- }
1097
- }
1098
- if (!lastProcessInfo) throw new Error("No OAuth callback ports configured");
1099
- await getUI().showPortConflict(lastProcessInfo);
1100
- shouldRetry = true;
1101
- } while (shouldRetry);
1102
- throw new Error("OAuth port retry loop exited unexpectedly");
859
+ function getProvisioningScopesForProgram(programId) {
860
+ const additions = programId && PROGRAM_SCOPE_ADDITIONS[programId] || [];
861
+ if (additions.length === 0) return WIZARD_PROVISIONING_SCOPES;
862
+ const seen = /* @__PURE__ */ new Set();
863
+ const merged = [];
864
+ for (const s of [...WIZARD_PROVISIONING_SCOPES, ...additions]) {
865
+ if (seen.has(s)) continue;
866
+ seen.add(s);
867
+ merged.push(s);
868
+ }
869
+ return merged;
1103
870
  }
1104
871
  //#endregion
1105
872
  //#region src/shared/utils/project-resolution.ts
@@ -1130,167 +897,27 @@ function resolveGrantedProject(requestedProjectId, scopedTeams) {
1130
897
  };
1131
898
  }
1132
899
  //#endregion
1133
- //#region src/shared/utils/semver.ts
1134
- /**
1135
- * Version strings from package.json that are not semver ranges.
1136
- * URLs, git refs, dist-tags, local paths, workspace protocol, npm aliases, etc.
1137
- * These should be rejected early — we can't determine a clear version from them.
1138
- */
1139
- function isNonSemverVersion(version) {
1140
- const v = version.trim();
1141
- return v === "" || v.startsWith("http://") || v.startsWith("https://") || v.startsWith("git+") || v.startsWith("git://") || v.startsWith("file:") || v.startsWith("npm:") || v.startsWith("workspace:") || v.startsWith("/") || v.includes("/");
1142
- }
1143
- /**
1144
- * Creates a version bucket function for analytics.
1145
- * Converts versions like "1.2.3" to "1.x" for grouping in analytics.
1146
- *
1147
- * @param minMajorVersion - Optional minimum major version threshold.
1148
- * Versions below this will be bucketed as "<{min}.0.0"
1149
- *
1150
- * @example
1151
- * const getVersionBucket = createVersionBucket(); // no minimum
1152
- * getVersionBucket("1.2.3") // "1.x"
1153
- *
1154
- * const getVersionBucket = createVersionBucket(11);
1155
- * getVersionBucket("15.3.0") // "15.x"
1156
- * getVersionBucket("10.0.0") // "<11.0.0"
1157
- */
1158
- function createVersionBucket(minMajorVersion) {
1159
- return (version) => {
1160
- if (!version) return "none";
1161
- if (isNonSemverVersion(version)) return "unknown";
1162
- try {
1163
- const minVer = minVersion(version);
1164
- if (!minVer) return "invalid";
1165
- const majorVersion = major(minVer);
1166
- if (minMajorVersion !== void 0 && majorVersion < minMajorVersion) return `<${minMajorVersion}.0.0`;
1167
- return `${majorVersion}.x`;
1168
- } catch {
1169
- return "unknown";
1170
- }
900
+ //#region src/shared/api-key-login.ts
901
+ async function fetchProjectDataWithApiKey(apiKey, cloudUrl) {
902
+ const projectId = (await fetchUserData(apiKey, cloudUrl)).team?.id;
903
+ if (!projectId) throw new Error("Could not determine project ID from API key. Please ensure your API key has access to a project in this cloud region.");
904
+ const projectData = await fetchProjectData(apiKey, projectId, cloudUrl);
905
+ return {
906
+ api_token: projectData.api_token,
907
+ id: projectId,
908
+ project: projectData
1171
909
  };
1172
910
  }
1173
- //#endregion
1174
- //#region src/shared/utils/setup-utils.ts
1175
- var setup_utils_exports = /* @__PURE__ */ __exportAll({
1176
- abort: () => abort,
1177
- detectOrgAndProject: () => detectOrgAndProject,
1178
- getOrAskForProjectData: () => getOrAskForProjectData,
1179
- getPackageDotJson: () => getPackageDotJson,
1180
- isUsingTypeScript: () => isUsingTypeScript,
1181
- tryGetPackageJson: () => tryGetPackageJson,
1182
- updatePackageDotJson: () => updatePackageDotJson
1183
- });
1184
- /** @deprecated Use wizardAbort() directly for new code. */
1185
- async function abort(message, status) {
1186
- return wizardAbort({
1187
- message,
1188
- exitCode: status
1189
- });
1190
- }
1191
- const FREEMAIL_DOMAINS = new Set([
1192
- "gmail.com",
1193
- "googlemail.com",
1194
- "hotmail.com",
1195
- "outlook.com",
1196
- "yahoo.com",
1197
- "icloud.com",
1198
- "me.com",
1199
- "mail.com",
1200
- "protonmail.com",
1201
- "proton.me",
1202
- "live.com",
1203
- "aol.com",
1204
- "yandex.com",
1205
- "zoho.com",
1206
- "gmx.com",
1207
- "fastmail.com"
1208
- ]);
1209
- function parseGitRemote() {
1210
- try {
1211
- const match = childProcess.execSync("git remote get-url origin", { stdio: [
1212
- "ignore",
1213
- "pipe",
1214
- "ignore"
1215
- ] }).toString().trim().match(/[/:]([\w.-]+)\/([\w.-]+?)(?:\.git)?$/);
1216
- if (match) return {
1217
- org: match[1],
1218
- repo: match[2]
1219
- };
1220
- } catch {}
1221
- return null;
1222
- }
1223
- function detectOrgAndProject(email) {
1224
- const remote = parseGitRemote();
1225
- const projectName = remote?.repo || basename(process.cwd()) || void 0;
1226
- let orgName;
1227
- if (remote?.org) orgName = remote.org;
1228
- else {
1229
- const domain = email.split("@")[1]?.toLowerCase();
1230
- if (domain && !FREEMAIL_DOMAINS.has(domain)) orgName = domain.split(".")[0];
1231
- }
911
+ async function fetchProjectDataById(apiKey, projectId, cloudUrl) {
912
+ const projectData = await fetchProjectData(apiKey, projectId, cloudUrl);
1232
913
  return {
1233
- orgName,
1234
- projectName
914
+ api_token: projectData.api_token,
915
+ id: projectId,
916
+ project: projectData
1235
917
  };
1236
918
  }
1237
- /**
1238
- * Get package.json or abort the wizard if not found.
1239
- * Only use where package.json is required (e.g., package install, overrides).
1240
- * For detection/version-checks, use tryGetPackageJson() instead.
1241
- */
1242
- async function getPackageDotJson({ installDir }) {
1243
- const pkgPath = join$1(installDir, "package.json");
1244
- let raw;
1245
- try {
1246
- raw = await fs$1.promises.readFile(pkgPath, "utf8");
1247
- } catch {
1248
- getUI().log.error("Could not find package.json. Make sure to run the wizard in the root of your app!");
1249
- await abort();
1250
- return {};
1251
- }
1252
- try {
1253
- return JSON.parse(raw) ?? {};
1254
- } catch {
1255
- getUI().log.error(`Unable to parse your package.json. Make sure it has a valid format!`);
1256
- await abort();
1257
- return {};
1258
- }
1259
- }
1260
- /**
1261
- * Try to get package.json, returning null if it doesn't exist.
1262
- * Use this for detection purposes where missing package.json is expected (e.g., Python projects).
1263
- */
1264
- async function tryGetPackageJson({ installDir }) {
1265
- try {
1266
- const packageJsonFileContents = await fs$1.promises.readFile(join$1(installDir, "package.json"), "utf8");
1267
- return JSON.parse(packageJsonFileContents);
1268
- } catch {
1269
- return null;
1270
- }
1271
- }
1272
- async function updatePackageDotJson(packageDotJson, { installDir }) {
1273
- const pkgPath = join$1(installDir, "package.json");
1274
- const serialized = JSON.stringify(packageDotJson, null, 2);
1275
- try {
1276
- await fs$1.promises.writeFile(pkgPath, serialized, {
1277
- encoding: "utf8",
1278
- flag: "w"
1279
- });
1280
- return;
1281
- } catch {
1282
- getUI().log.error(`Unable to update your package.json.`);
1283
- await abort();
1284
- }
1285
- }
1286
- function isUsingTypeScript({ installDir }) {
1287
- try {
1288
- fs$1.accessSync(join$1(installDir, "tsconfig.json"));
1289
- return true;
1290
- } catch {
1291
- return false;
1292
- }
1293
- }
919
+ //#endregion
920
+ //#region src/tui/auth/project-data.ts
1294
921
  /**
1295
922
  * Get project data for the wizard via OAuth or CI API key.
1296
923
  */
@@ -1360,24 +987,6 @@ ${cloudUrl}/settings/project#variables`);
1360
987
  missingScopes: missingScopes ?? []
1361
988
  };
1362
989
  }
1363
- async function fetchProjectDataWithApiKey(apiKey, cloudUrl) {
1364
- const projectId = (await fetchUserData(apiKey, cloudUrl)).team?.id;
1365
- if (!projectId) throw new Error("Could not determine project ID from API key. Please ensure your API key has access to a project in this cloud region.");
1366
- const projectData = await fetchProjectData(apiKey, projectId, cloudUrl);
1367
- return {
1368
- api_token: projectData.api_token,
1369
- id: projectId,
1370
- project: projectData
1371
- };
1372
- }
1373
- async function fetchProjectDataById(apiKey, projectId, cloudUrl) {
1374
- const projectData = await fetchProjectData(apiKey, projectId, cloudUrl);
1375
- return {
1376
- api_token: projectData.api_token,
1377
- id: projectId,
1378
- project: projectData
1379
- };
1380
- }
1381
990
  async function askForWizardLogin(options) {
1382
991
  if (options.signup) return askForProvisioningSignup(options.email, options.region, options.baseUrl, options.localMcp, options.programId);
1383
992
  const requestedScopes = [...getOAuthScopesForProgram(options.programId)];
@@ -1517,6 +1126,6 @@ async function askForProvisioningSignup(email, region, baseUrl, localMcp, progra
1517
1126
  }
1518
1127
  }
1519
1128
  //#endregion
1520
- export { createVersionBucket as a, refreshAccessToken as c, mcpUrlFor as d, detectAllPackageManagers as f, tryGetPackageJson as i, OAuthError as l, isUsingTypeScript as n, extractOAuthCode as o, detectNodePackageManagers as p, setup_utils_exports as r, missingOAuthScopes as s, getOrAskForProjectData as t, HostResolution as u };
1129
+ export { OAuthError as a, refreshAccessToken as i, extractOAuthCode as n, missingOAuthScopes as r, getOrAskForProjectData as t };
1521
1130
 
1522
- //# sourceMappingURL=setup-utils-dCViCC6s.js.map
1131
+ //# sourceMappingURL=project-data-BO9IXn9y.js.map