@posthog/wizard 2.76.1 → 2.78.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 (176) hide show
  1. package/README.md +30 -15
  2. package/dist/README.md +16 -15
  3. package/dist/{add-mcp-server-to-clients-4yHaRvOo.js → add-mcp-server-to-clients-JMv8LP-O.js} +429 -114
  4. package/dist/add-mcp-server-to-clients-JMv8LP-O.js.map +1 -0
  5. package/dist/agent-bCZ3i3fh.js +3085 -0
  6. package/dist/agent-bCZ3i3fh.js.map +1 -0
  7. package/dist/{agent-interface-DPaPdIjS.js → agent-interface-BAOIHuXS.js} +452 -377
  8. package/dist/agent-interface-BAOIHuXS.js.map +1 -0
  9. package/dist/{agent-prompt-loader-BlhabNM2.js → agent-prompt-loader-7Pd4CfZO.js} +24 -8
  10. package/dist/agent-prompt-loader-7Pd4CfZO.js.map +1 -0
  11. package/dist/analytics-BcV-WCM4.js +2 -0
  12. package/dist/{analytics-DSVyjIVF.js → analytics-CHCZ_qUR.js} +10 -6
  13. package/dist/analytics-CHCZ_qUR.js.map +1 -0
  14. package/dist/{api-Ci0dZWLR.js → api-Diy_2fp8.js} +4 -17
  15. package/dist/api-Diy_2fp8.js.map +1 -0
  16. package/dist/bin.js +410 -68
  17. package/dist/bin.js.map +1 -1
  18. package/dist/{bounded-fs-FbZJ_vJ_.js → bounded-fs-Bx9zTe6m.js} +4 -4
  19. package/dist/bounded-fs-Bx9zTe6m.js.map +1 -0
  20. package/dist/check-screens.tsx +1 -1
  21. package/dist/{ci-install-CEVCqahf.js → ci-install-CDdQYsPe.js} +6 -5
  22. package/dist/ci-install-CDdQYsPe.js.map +1 -0
  23. package/dist/constants-jFI2SaE1.js +260 -0
  24. package/dist/constants-jFI2SaE1.js.map +1 -0
  25. package/dist/debug-Ch4BDOtD.js +171 -0
  26. package/dist/debug-Ch4BDOtD.js.map +1 -0
  27. package/dist/{debug-C6KWXbQi.js → debug-M2gV2iDq.js} +1 -1
  28. package/dist/{defaults-DulzMJpN.js → defaults-C6J7yL4O.js} +26 -3
  29. package/dist/defaults-C6J7yL4O.js.map +1 -0
  30. package/dist/endpoints-DCVbM06L.js +108 -0
  31. package/dist/endpoints-DCVbM06L.js.map +1 -0
  32. package/dist/{env-api-key-DwmkB3fm.js → env-api-key-Qx8NnYHm.js} +3 -3
  33. package/dist/env-api-key-Qx8NnYHm.js.map +1 -0
  34. package/dist/{environment-1wA7uwAS.js → environment-B07Bmxae.js} +6 -6
  35. package/dist/environment-B07Bmxae.js.map +1 -0
  36. package/dist/{errors--OqLxmWR.js → errors-CQ9kttJD.js} +30 -35
  37. package/dist/errors-CQ9kttJD.js.map +1 -0
  38. package/dist/{file-C3HGL1IM.js → file-BcLTjU16.js} +2 -2
  39. package/dist/{file-C3HGL1IM.js.map → file-BcLTjU16.js.map} +1 -1
  40. package/dist/{file-watcher-D8s0b-VT.js → file-watcher-DWc_bTs6.js} +2 -2
  41. package/dist/{file-watcher-D8s0b-VT.js.map → file-watcher-DWc_bTs6.js.map} +1 -1
  42. package/dist/{gateway-session-OqzCMpZ9.js → gateway-CjoShdLa.js} +175 -20
  43. package/dist/gateway-CjoShdLa.js.map +1 -0
  44. package/dist/generate-version.cjs +1 -1
  45. package/dist/{headless-ui-DRGWDg9Z.js → headless-ui-BFFmYC4v.js} +13 -2
  46. package/dist/headless-ui-BFFmYC4v.js.map +1 -0
  47. package/dist/{interactive-B6OlvhSf.js → interactive-D6zV7brJ.js} +2 -2
  48. package/dist/{interactive-B6OlvhSf.js.map → interactive-D6zV7brJ.js.map} +1 -1
  49. package/dist/{local-dev-DPZiIPCK.js → local-dev-McsurOC-.js} +2 -2
  50. package/dist/local-dev-McsurOC-.js.map +1 -0
  51. package/dist/logging-ui-_TwoMNbG.js +226 -0
  52. package/dist/logging-ui-_TwoMNbG.js.map +1 -0
  53. package/dist/mcp-DAgaoGk5.js +97 -0
  54. package/dist/mcp-DAgaoGk5.js.map +1 -0
  55. package/dist/{mcp-prompt-streaming-E1d8DJcX.js → mcp-prompt-streaming-I2U_X7BC.js} +7 -6
  56. package/dist/mcp-prompt-streaming-I2U_X7BC.js.map +1 -0
  57. package/dist/{mint-failure-C82j0RMC.js → mint-failure-D4nd2XmZ.js} +134 -48
  58. package/dist/mint-failure-D4nd2XmZ.js.map +1 -0
  59. package/dist/models-DDdBJKwT.js +83 -0
  60. package/dist/models-DDdBJKwT.js.map +1 -0
  61. package/dist/{non-interactive-DGT2H_n4.js → non-interactive-Dzow7YKY.js} +3 -3
  62. package/dist/non-interactive-Dzow7YKY.js.map +1 -0
  63. package/dist/oauth-session-DJ8h6GFa.js +74 -0
  64. package/dist/oauth-session-DJ8h6GFa.js.map +1 -0
  65. package/dist/{orchestrator-tools-BBBNV1z5.js → orchestrator-tools-szcdscwn.js} +5 -5
  66. package/dist/orchestrator-tools-szcdscwn.js.map +1 -0
  67. package/dist/{package-json-xt3eeyBI.js → package-json-DY70kkNp.js} +2 -2
  68. package/dist/package-json-DY70kkNp.js.map +1 -0
  69. package/dist/{pi-fG5fT50Y.js → pi-D3h5Ok0i.js} +329 -65
  70. package/dist/pi-D3h5Ok0i.js.map +1 -0
  71. package/dist/{playground-UFSHP-Yn.js → playground-5AB4WgNF.js} +18 -10
  72. package/dist/playground-5AB4WgNF.js.map +1 -0
  73. package/dist/{posthog-O9kKcnda.js → posthog-DNn574nW.js} +8 -3
  74. package/dist/posthog-DNn574nW.js.map +1 -0
  75. package/dist/{provisioning-B-tzjG4r.js → provisioning-B8uSqRZk.js} +6 -5
  76. package/dist/provisioning-B8uSqRZk.js.map +1 -0
  77. package/dist/{queue-tools-7Oso-fpt.js → queue-tools-9OOVx5TE.js} +11 -86
  78. package/dist/queue-tools-9OOVx5TE.js.map +1 -0
  79. package/dist/{registry-D1L5e-WQ.js → registry-8YG-kuXk.js} +265 -6
  80. package/dist/registry-8YG-kuXk.js.map +1 -0
  81. package/dist/run-agent-legacy-d7rWlzQR.js +3739 -0
  82. package/dist/run-agent-legacy-d7rWlzQR.js.map +1 -0
  83. package/dist/{security-n3-xdINN.js → security-Cg_4a7vB.js} +6 -6
  84. package/dist/security-Cg_4a7vB.js.map +1 -0
  85. package/dist/{setup-utils-Bj0gejEf.js → setup-utils-CmTCTPkX.js} +46 -18
  86. package/dist/setup-utils-CmTCTPkX.js.map +1 -0
  87. package/dist/smoke-test.sh +1 -1
  88. package/dist/{start-tui-BowrTPXm.js → start-tui-CB2PZs1x.js} +42 -48
  89. package/dist/start-tui-CB2PZs1x.js.map +1 -0
  90. package/dist/{steps-CpNTGu9N.js → steps-BZcXRJNY.js} +7 -7
  91. package/dist/{steps-CpNTGu9N.js.map → steps-BZcXRJNY.js.map} +1 -1
  92. package/dist/{store-CVkJKlny.js → store-UN7c-ASf.js} +18 -55
  93. package/dist/store-UN7c-ASf.js.map +1 -0
  94. package/dist/{subagent-SxmvfDfp.js → subagent-BofIDK-O.js} +8 -3
  95. package/dist/subagent-BofIDK-O.js.map +1 -0
  96. package/dist/{task-nF7Ry8ff.js → task-r9t1UhQP.js} +124 -36
  97. package/dist/task-r9t1UhQP.js.map +1 -0
  98. package/dist/{task-stream-C9BLiAhz.js → task-stream-CMUilu54.js} +45 -15
  99. package/dist/task-stream-CMUilu54.js.map +1 -0
  100. package/dist/{tasks-BEEVIBcT.js → tasks-Bbv_lOBG.js} +17 -13
  101. package/dist/tasks-Bbv_lOBG.js.map +1 -0
  102. package/dist/{telemetry-PD7bhcda.js → telemetry-D6ruCtnv.js} +5 -5
  103. package/dist/telemetry-D6ruCtnv.js.map +1 -0
  104. package/dist/{terminal-NhNl-BIA.js → terminal-DrGyci_a.js} +63 -94
  105. package/dist/terminal-DrGyci_a.js.map +1 -0
  106. package/dist/{tools-BBdpgcIf.js → tools-Dyfh9aCD.js} +11 -11
  107. package/dist/tools-Dyfh9aCD.js.map +1 -0
  108. package/dist/ui-BBVuTM1v.js +15 -0
  109. package/dist/ui-BBVuTM1v.js.map +1 -0
  110. package/dist/version-aXxpgRKx.js +6 -0
  111. package/dist/version-aXxpgRKx.js.map +1 -0
  112. package/dist/warlock-smoke-test.ts +2 -2
  113. package/dist/{wizard-abort-hqHF-9xD.js → wizard-abort-BBkiUxfS.js} +23 -20
  114. package/dist/wizard-abort-BBkiUxfS.js.map +1 -0
  115. package/dist/wizard-abort-BU4aLyvh.js +3 -0
  116. package/dist/{wizard-session-FSJCkTcP.js → wizard-session-2w4Ru-Wz.js} +12 -16
  117. package/dist/wizard-session-2w4Ru-Wz.js.map +1 -0
  118. package/dist/wizard-session-DGaUHOF3.js +2 -0
  119. package/dist/wizard-ui-DoyOZxK8.js +16 -0
  120. package/dist/wizard-ui-DoyOZxK8.js.map +1 -0
  121. package/dist/{yara-hooks-BaPKsm-B.js → yara-hooks-DCeMDd1o.js} +19 -68
  122. package/dist/yara-hooks-DCeMDd1o.js.map +1 -0
  123. package/dist/yara-hooks-DSGuCU9A.js +2 -0
  124. package/package.json +8 -1
  125. package/dist/add-mcp-server-to-clients-4yHaRvOo.js.map +0 -1
  126. package/dist/agent-interface-DPaPdIjS.js.map +0 -1
  127. package/dist/agent-prompt-loader-BlhabNM2.js.map +0 -1
  128. package/dist/agent-runner-JFwQQj2k.js +0 -6100
  129. package/dist/agent-runner-JFwQQj2k.js.map +0 -1
  130. package/dist/analytics-CcHoyhE_.js +0 -2
  131. package/dist/analytics-DSVyjIVF.js.map +0 -1
  132. package/dist/api-Ci0dZWLR.js.map +0 -1
  133. package/dist/bounded-fs-FbZJ_vJ_.js.map +0 -1
  134. package/dist/ci-install-CEVCqahf.js.map +0 -1
  135. package/dist/debug-DWYDVp0B.js +0 -750
  136. package/dist/debug-DWYDVp0B.js.map +0 -1
  137. package/dist/defaults-DulzMJpN.js.map +0 -1
  138. package/dist/env-api-key-DwmkB3fm.js.map +0 -1
  139. package/dist/environment-1wA7uwAS.js.map +0 -1
  140. package/dist/errors--OqLxmWR.js.map +0 -1
  141. package/dist/gateway-session-OqzCMpZ9.js.map +0 -1
  142. package/dist/gateway-session-s1xr8b48.js +0 -2
  143. package/dist/headless-ui-DRGWDg9Z.js.map +0 -1
  144. package/dist/local-dev-DPZiIPCK.js.map +0 -1
  145. package/dist/mcp-BprhJJ2c.js +0 -64
  146. package/dist/mcp-BprhJJ2c.js.map +0 -1
  147. package/dist/mcp-prompt-streaming-E1d8DJcX.js.map +0 -1
  148. package/dist/mint-failure-C82j0RMC.js.map +0 -1
  149. package/dist/non-interactive-DGT2H_n4.js.map +0 -1
  150. package/dist/orchestrator-tools-BBBNV1z5.js.map +0 -1
  151. package/dist/package-json-xt3eeyBI.js.map +0 -1
  152. package/dist/package-manager-DU3aVif4.js +0 -290
  153. package/dist/package-manager-DU3aVif4.js.map +0 -1
  154. package/dist/pi-fG5fT50Y.js.map +0 -1
  155. package/dist/playground-UFSHP-Yn.js.map +0 -1
  156. package/dist/posthog-O9kKcnda.js.map +0 -1
  157. package/dist/provisioning-B-tzjG4r.js.map +0 -1
  158. package/dist/queue-tools-7Oso-fpt.js.map +0 -1
  159. package/dist/registry-D1L5e-WQ.js.map +0 -1
  160. package/dist/security-n3-xdINN.js.map +0 -1
  161. package/dist/setup-utils-Bj0gejEf.js.map +0 -1
  162. package/dist/start-tui-BowrTPXm.js.map +0 -1
  163. package/dist/store-CVkJKlny.js.map +0 -1
  164. package/dist/subagent-SxmvfDfp.js.map +0 -1
  165. package/dist/task-nF7Ry8ff.js.map +0 -1
  166. package/dist/task-stream-C9BLiAhz.js.map +0 -1
  167. package/dist/tasks-BEEVIBcT.js.map +0 -1
  168. package/dist/telemetry-PD7bhcda.js.map +0 -1
  169. package/dist/terminal-NhNl-BIA.js.map +0 -1
  170. package/dist/tools-BBdpgcIf.js.map +0 -1
  171. package/dist/wizard-abort-D3YA1hXX.js +0 -2
  172. package/dist/wizard-abort-hqHF-9xD.js.map +0 -1
  173. package/dist/wizard-session-CYvyYnti.js +0 -2
  174. package/dist/wizard-session-FSJCkTcP.js.map +0 -1
  175. package/dist/yara-hooks-BaPKsm-B.js.map +0 -1
  176. package/dist/yara-hooks-CwfQuEl3.js +0 -2
@@ -4,8 +4,31 @@ import z$1 from "zod";
4
4
  * Failure text comes from CLIs we invoke with the user's personal API key on the
5
5
  * command line (`--header "Authorization: Bearer phx_..."`), so it can carry the
6
6
  * key into the log file, the TUI and exception reports. Mask it first.
7
+ *
8
+ * The plugin stages clone git repositories and read the user's own config, so
9
+ * the text reaching here is no longer only ours: a failing clone echoes the
10
+ * remote URL with any credentials embedded in it, and a config error can quote
11
+ * a foreign provider's key. Each pattern below is a shape we have seen a CLI
12
+ * print, not a guess at every possible secret.
7
13
  */
8
- const redactSecrets = (raw) => raw.replace(/Bearer\s+\S+/gi, "Bearer [redacted]").replace(/ph[xspc]_[A-Za-z0-9_-]+/g, "[redacted]");
14
+ const redactSecrets = (raw) => raw.replace(/Bearer\s+\S+/gi, "Bearer [redacted]").replace(/ph[xspc]_[A-Za-z0-9_-]+/g, "[redacted]").replace(/(\bhttps?:\/\/)[^/\s:@]+:[^/\s@]+@/gi, "$1[redacted]@").replace(/\bgh[pousr]_[A-Za-z0-9]{16,}/g, "[redacted]").replace(/\bgithub_pat_[A-Za-z0-9_]{20,}/g, "[redacted]").replace(/\bsk-[A-Za-z0-9_-]{16,}/g, "[redacted]").replace(/\bAIza[A-Za-z0-9_-]{16,}/g, "[redacted]").replace(/\b([A-Za-z0-9_]*(?:KEY|TOKEN|SECRET|PASSWORD|PASSWD|CREDENTIALS?)[A-Za-z0-9_]*)(\s*[=:]\s*)["']?[A-Za-z0-9_\-./+]{8,}["']?/gi, "$1$2[redacted]");
15
+ /**
16
+ * Replace the user's home directory with `~`. Error tracking fingerprints on
17
+ * the message, so a path that differs per user splits one root cause into one
18
+ * issue per user — and the username itself never needs to leave the machine.
19
+ */
20
+ const scrubHomePaths = (raw) => raw.replace(/\/(?:Users|home)\/[^/\s'"]+/g, "~").replace(/[A-Za-z]:\\Users\\[^\\\s'"]+/gi, "~");
21
+ /**
22
+ * The first matching hint, or undefined when the failure is worth reporting.
23
+ *
24
+ * `stage` is the caller's name for what it was doing. Passing it lets an entry
25
+ * narrow itself; omitting it only considers entries that claim every stage,
26
+ * because an unscoped call cannot honour a scoped entry.
27
+ */
28
+ const expectedFailureHint = (text, table, stage) => {
29
+ if (!text.trim()) return void 0;
30
+ return table.find((f) => (f.stages === void 0 || stage !== void 0 && f.stages.includes(stage)) && f.match.test(text) && !f.unless?.test(text))?.hint;
31
+ };
9
32
  /** First non-empty line of an error, trimmed to something a TUI line can hold. */
10
33
  const summarizeFailure = (raw) => {
11
34
  const line = redactSecrets(raw ?? "").split("\n").map((l) => l.trim()).find((l) => l.length > 0);
@@ -281,6 +304,6 @@ const getDefaultServerConfig = (apiKey, selectedFeatures, local) => {
281
304
  };
282
305
  };
283
306
  //#endregion
284
- export { getNativeHTTPServerConfig as a, namesWithStatus as c, toClientResult as d, getDefaultServerConfig as i, redactSecrets as l, AVAILABLE_FEATURES as n, isAllFeaturesSelected as o, buildMCPUrl as r, isOk as s, ALL_FEATURE_VALUES as t, summarizeFailure as u };
307
+ export { getNativeHTTPServerConfig as a, isOk as c, scrubHomePaths as d, summarizeFailure as f, getDefaultServerConfig as i, namesWithStatus as l, AVAILABLE_FEATURES as n, isAllFeaturesSelected as o, toClientResult as p, buildMCPUrl as r, expectedFailureHint as s, ALL_FEATURE_VALUES as t, redactSecrets as u };
285
308
 
286
- //# sourceMappingURL=defaults-DulzMJpN.js.map
309
+ //# sourceMappingURL=defaults-C6J7yL4O.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"defaults-C6J7yL4O.js","names":["z"],"sources":["../src/steps/add-mcp-server-to-clients/results.ts","../src/steps/add-mcp-server-to-clients/defaults.ts"],"sourcesContent":["/**\n * Per-client outcome of an MCP server / plugin install or removal.\n *\n * \"Nothing to do\" is a first-class outcome rather than a silent no-op:\n * re-running `mcp add` on a machine that's already set up is the common case,\n * and collapsing it into an empty result set is what made the flow report\n * \"Installation skipped.\" with no explanation. Failures carry a short reason\n * for the same reason — an empty list tells the user nothing.\n *\n * The names are action-neutral because both `mcp add` and `mcp remove` report\n * through them: `Changed` is \"installed\" or \"removed\" depending on the flow.\n */\n\nexport enum McpClientStatus {\n /** We made the change — wrote the config, installed or removed the server. */\n Changed = 'changed',\n /** Already in the requested state; nothing was touched. */\n Unchanged = 'unchanged',\n Failed = 'failed',\n}\n\nexport interface McpClientResult {\n name: string;\n status: McpClientStatus;\n /** Short, user-facing explanation. Set for failures. */\n detail?: string;\n}\n\n/** Result shape every client's addServer/removeServer/installPlugin returns. */\nexport interface InstallResult {\n success: boolean;\n /** Already in the requested state — nothing was written or removed. */\n alreadyInstalled?: boolean;\n /** Raw failure text from the underlying CLI or filesystem error. */\n reason?: string;\n}\n\n/**\n * Failure text comes from CLIs we invoke with the user's personal API key on the\n * command line (`--header \"Authorization: Bearer phx_...\"`), so it can carry the\n * key into the log file, the TUI and exception reports. Mask it first.\n *\n * The plugin stages clone git repositories and read the user's own config, so\n * the text reaching here is no longer only ours: a failing clone echoes the\n * remote URL with any credentials embedded in it, and a config error can quote\n * a foreign provider's key. Each pattern below is a shape we have seen a CLI\n * print, not a guess at every possible secret.\n */\nexport const redactSecrets = (raw: string): string =>\n raw\n .replace(/Bearer\\s+\\S+/gi, 'Bearer [redacted]')\n .replace(/ph[xspc]_[A-Za-z0-9_-]+/g, '[redacted]')\n // `https://user:token@github.com/...` — git prints the whole remote back.\n .replace(/(\\bhttps?:\\/\\/)[^/\\s:@]+:[^/\\s@]+@/gi, '$1[redacted]@')\n // GitHub tokens, which a clone failure quotes verbatim.\n .replace(/\\bgh[pousr]_[A-Za-z0-9]{16,}/g, '[redacted]')\n .replace(/\\bgithub_pat_[A-Za-z0-9_]{20,}/g, '[redacted]')\n // OpenAI keys: `OPENAI_API_KEY` is already a failure codex reports on.\n .replace(/\\bsk-[A-Za-z0-9_-]{16,}/g, '[redacted]')\n // Google keys, which a config error quotes the same way.\n .replace(/\\bAIza[A-Za-z0-9_-]{16,}/g, '[redacted]')\n // The catch-all for providers we have no shape for: a config error prints\n // the offending line, and the variable's own name says it holds a secret.\n // The name is kept — `OPENAI_API_KEY` is wording an expected-failure hint\n // matches on, and masking it would turn that hint back into an exception.\n .replace(\n /\\b([A-Za-z0-9_]*(?:KEY|TOKEN|SECRET|PASSWORD|PASSWD|CREDENTIALS?)[A-Za-z0-9_]*)(\\s*[=:]\\s*)[\"']?[A-Za-z0-9_\\-./+]{8,}[\"']?/gi,\n '$1$2[redacted]',\n );\n\n/**\n * Replace the user's home directory with `~`. Error tracking fingerprints on\n * the message, so a path that differs per user splits one root cause into one\n * issue per user — and the username itself never needs to leave the machine.\n */\nexport const scrubHomePaths = (raw: string): string =>\n raw\n .replace(/\\/(?:Users|home)\\/[^/\\s'\"]+/g, '~')\n .replace(/[A-Za-z]:\\\\Users\\\\[^\\\\\\s'\"]+/gi, '~');\n\n/**\n * A failure that lives entirely in the user's environment: a CLI too old, a\n * broken install, an unreadable config. We can't fix these, so we hand back a\n * hint instead of filing an issue nobody can action.\n *\n * Silencing a failure is the expensive direction: a pattern that matches too\n * much retires a real bug of ours into a hint nobody reads. So an entry says\n * where it applies (`stages`) and what disqualifies it (`unless`), and anything\n * it cannot claim confidently goes back to being reported.\n */\nexport interface ExpectedFailure {\n match: RegExp;\n /**\n * Stages this hint is allowed to fire on. Omitted means any stage. Wording\n * that means one thing during `plugin install` can mean something else during\n * `mcp add`, and a hint naming the wrong command is worse than none.\n */\n stages?: string[];\n /**\n * Wording that disqualifies the match even when `match` hits — the tell that\n * the failure is ours, not the environment's.\n */\n unless?: RegExp;\n /** One short line, shown to the user as the failure detail. */\n hint: string;\n}\n\n/**\n * The first matching hint, or undefined when the failure is worth reporting.\n *\n * `stage` is the caller's name for what it was doing. Passing it lets an entry\n * narrow itself; omitting it only considers entries that claim every stage,\n * because an unscoped call cannot honour a scoped entry.\n */\nexport const expectedFailureHint = (\n text: string,\n table: ExpectedFailure[],\n stage?: string,\n): string | undefined => {\n if (!text.trim()) return undefined;\n return table.find(\n (f) =>\n (f.stages === undefined ||\n (stage !== undefined && f.stages.includes(stage))) &&\n f.match.test(text) &&\n !f.unless?.test(text),\n )?.hint;\n};\n\n/** First non-empty line of an error, trimmed to something a TUI line can hold. */\nexport const summarizeFailure = (raw?: string): string | undefined => {\n const line = redactSecrets(raw ?? '')\n .split('\\n')\n .map((l) => l.trim())\n .find((l) => l.length > 0);\n if (!line) return undefined;\n return line.length > 120 ? `${line.slice(0, 117)}...` : line;\n};\n\nexport const toClientResult = (\n name: string,\n result: InstallResult | undefined,\n): McpClientResult => {\n if (!result?.success) {\n return {\n name,\n status: McpClientStatus.Failed,\n detail: summarizeFailure(result?.reason),\n };\n }\n return {\n name,\n status: result.alreadyInstalled\n ? McpClientStatus.Unchanged\n : McpClientStatus.Changed,\n };\n};\n\nexport const isOk = (r: McpClientResult): boolean =>\n r.status !== McpClientStatus.Failed;\n\nexport const namesWithStatus = (\n results: McpClientResult[],\n status: McpClientStatus,\n): string[] => results.filter((r) => r.status === status).map((r) => r.name);\n","import z from 'zod';\n\nexport const DefaultMCPClientConfig = z\n .object({\n mcpServers: z.record(\n z.string(),\n z.union([\n z.object({\n command: z.string().optional(),\n args: z.array(z.string()).optional(),\n env: z.record(z.string(), z.string()).optional(),\n }),\n z.object({\n url: z.string(),\n headers: z.record(z.string(), z.string()).optional(),\n }),\n ]),\n ),\n })\n .passthrough();\n\nexport const AVAILABLE_FEATURES = {\n 'Data & Analytics': [\n {\n value: 'dashboards',\n label: 'Dashboards',\n hint: 'Dashboard creation and management',\n },\n {\n value: 'insights',\n label: 'Insights',\n hint: 'Analytics insights',\n },\n {\n value: 'product_analytics',\n label: 'Product Analytics',\n hint: 'Insight CRUD management',\n },\n {\n value: 'experiments',\n label: 'Experiments',\n hint: 'A/B testing experiments',\n },\n {\n value: 'surveys',\n label: 'Surveys',\n hint: 'Survey management',\n },\n {\n value: 'annotations',\n label: 'Annotations',\n hint: 'Annotation management',\n },\n {\n value: 'replay',\n label: 'Session Replay',\n hint: 'Session recording management',\n },\n {\n value: 'sql',\n label: 'SQL',\n hint: 'SQL query execution',\n },\n {\n value: 'web_analytics',\n label: 'Web Analytics',\n hint: 'Web analytics queries and digests',\n },\n {\n value: 'customer_analytics',\n label: 'Usage metrics',\n hint: 'Customer usage metric tracking',\n },\n {\n value: 'signals',\n label: 'Signals',\n hint: 'Signal reports and source configs',\n },\n ],\n 'AI Engineering': [\n {\n value: 'llm_analytics',\n label: 'AI Observability',\n hint: 'LLM usage and cost tracking',\n },\n ],\n 'Development Tools': [\n {\n value: 'error_tracking',\n label: 'Error Tracking',\n hint: 'Error monitoring and debugging',\n },\n {\n value: 'logs',\n label: 'Logs',\n hint: 'Log querying',\n },\n {\n value: 'flags',\n label: 'Feature Flags',\n hint: 'Feature flag management',\n },\n {\n value: 'early_access_features',\n label: 'Early Access Features',\n hint: 'Early access feature management',\n },\n {\n value: 'cohorts',\n label: 'Cohorts',\n hint: 'Cohort management',\n },\n {\n value: 'sdk_doctor',\n label: 'SDK Doctor',\n hint: 'SDK health diagnostics',\n },\n {\n value: 'tracing',\n label: 'APM Tracing',\n hint: 'Distributed trace and span queries',\n },\n ],\n 'Data Management': [\n {\n value: 'events',\n label: 'Events',\n hint: 'Event and property definitions',\n },\n {\n value: 'persons',\n label: 'Persons',\n hint: 'Person and group management',\n },\n {\n value: 'actions',\n label: 'Actions',\n hint: 'Action definitions',\n },\n {\n value: 'data_warehouse',\n label: 'Data Warehouse',\n hint: 'Data warehouse management',\n },\n {\n value: 'endpoints',\n label: 'Endpoints',\n hint: 'Data warehouse endpoint management',\n },\n {\n value: 'data_schema',\n label: 'Data Schema',\n hint: 'Data schema exploration',\n },\n {\n value: 'batch_exports',\n label: 'Batch Exports',\n hint: 'Scheduled data exports',\n },\n ],\n 'CDP & Automation': [\n {\n value: 'hog_functions',\n label: 'Hog Functions',\n hint: 'CDP function management',\n },\n {\n value: 'hog_function_templates',\n label: 'Hog Function Templates',\n hint: 'CDP function template browsing',\n },\n {\n value: 'workflows',\n label: 'Workflows',\n hint: 'Workflow management',\n },\n ],\n 'Platform & Management': [\n {\n value: 'workspace',\n label: 'Workspace',\n hint: 'Organization and project management',\n },\n {\n value: 'docs',\n label: 'Documentation',\n hint: 'PostHog documentation search',\n },\n {\n value: 'notebooks',\n label: 'Notebooks',\n hint: 'Notebook management',\n },\n {\n value: 'alerts',\n label: 'Alerts',\n hint: 'Alert management',\n },\n {\n value: 'platform_features',\n label: 'Platform Features',\n hint: 'Activity logs, approvals, comments, and roles',\n },\n {\n value: 'integrations',\n label: 'Integrations',\n hint: 'Connected integration management',\n },\n {\n value: 'conversations',\n label: 'Conversations',\n hint: 'Support ticket management',\n },\n {\n value: 'core',\n label: 'Subscriptions',\n hint: 'Scheduled insight and dashboard deliveries',\n },\n {\n value: 'search',\n label: 'Search',\n hint: 'Entity search across the project',\n },\n {\n value: 'reverse_proxy',\n label: 'Reverse Proxy',\n hint: 'Reverse proxy record management',\n },\n {\n value: 'debug',\n label: 'Debug',\n hint: 'Debug and diagnostic tools',\n },\n ],\n};\n\nexport const ALL_FEATURE_VALUES = Object.values(AVAILABLE_FEATURES)\n .flat()\n .map((feature) => feature.value);\n\nexport const isAllFeaturesSelected = (features: string[]): boolean =>\n features.length === ALL_FEATURE_VALUES.length &&\n ALL_FEATURE_VALUES.every((feature) => features.includes(feature));\n\nexport const buildMCPUrl = (selectedFeatures?: string[], local?: boolean) => {\n const host = local ? 'http://localhost:8787' : 'https://mcp.posthog.com';\n const baseUrl = `${host}/mcp`;\n\n const params: string[] = [];\n\n // Add features param if not all features selected\n if (\n selectedFeatures &&\n selectedFeatures.length > 0 &&\n !isAllFeaturesSelected(selectedFeatures)\n ) {\n params.push(`features=${selectedFeatures.join(',')}`);\n }\n\n return params.length > 0 ? `${baseUrl}?${params.join('&')}` : baseUrl;\n};\n\nexport const getNativeHTTPServerConfig = (\n apiKey: string | undefined,\n selectedFeatures?: string[],\n local?: boolean,\n) => {\n const config: Record<string, unknown> = {\n url: buildMCPUrl(selectedFeatures, local),\n };\n\n // Only add auth header if API key is provided (not OAuth mode)\n if (apiKey) {\n config.headers = {\n Authorization: `Bearer ${apiKey}`,\n };\n }\n\n return config;\n};\n\nexport const getDefaultServerConfig = (\n apiKey: string | undefined,\n selectedFeatures?: string[],\n local?: boolean,\n) => {\n const urlWithFeatures = buildMCPUrl(selectedFeatures, local);\n\n // OAuth mode: no auth header, let MCP handle OAuth\n if (!apiKey) {\n return {\n command: 'npx',\n args: ['-y', 'mcp-remote@latest', urlWithFeatures],\n };\n }\n\n // API key mode: include auth header\n return {\n command: 'npx',\n args: [\n '-y',\n 'mcp-remote@latest',\n urlWithFeatures,\n '--header',\n `Authorization:\\${POSTHOG_AUTH_HEADER}`,\n ],\n env: {\n POSTHOG_AUTH_HEADER: `Bearer ${apiKey}`,\n },\n };\n};\n"],"mappings":";;;;;;;;;;;;;AAgDA,MAAa,iBAAiB,QAC5B,IACG,QAAQ,kBAAkB,oBAAoB,CAC9C,QAAQ,4BAA4B,aAAa,CAEjD,QAAQ,wCAAwC,gBAAgB,CAEhE,QAAQ,iCAAiC,aAAa,CACtD,QAAQ,mCAAmC,aAAa,CAExD,QAAQ,4BAA4B,aAAa,CAEjD,QAAQ,6BAA6B,aAAa,CAKlD,QACC,gIACA,iBACD;;;;;;AAOL,MAAa,kBAAkB,QAC7B,IACG,QAAQ,gCAAgC,IAAI,CAC5C,QAAQ,kCAAkC,IAAI;;;;;;;;AAoCnD,MAAa,uBACX,MACA,OACA,UACuB;AACvB,KAAI,CAAC,KAAK,MAAM,CAAE,QAAO,KAAA;AACzB,QAAO,MAAM,MACV,OACE,EAAE,WAAW,KAAA,KACX,UAAU,KAAA,KAAa,EAAE,OAAO,SAAS,MAAM,KAClD,EAAE,MAAM,KAAK,KAAK,IAClB,CAAC,EAAE,QAAQ,KAAK,KAAK,CACxB,EAAE;;;AAIL,MAAa,oBAAoB,QAAqC;CACpE,MAAM,OAAO,cAAc,OAAO,GAAG,CAClC,MAAM,KAAK,CACX,KAAK,MAAM,EAAE,MAAM,CAAC,CACpB,MAAM,MAAM,EAAE,SAAS,EAAE;AAC5B,KAAI,CAAC,KAAM,QAAO,KAAA;AAClB,QAAO,KAAK,SAAS,MAAM,GAAG,KAAK,MAAM,GAAG,IAAI,CAAC,OAAO;;AAG1D,MAAa,kBACX,MACA,WACoB;AACpB,KAAI,CAAC,QAAQ,QACX,QAAO;EACL;EACA,QAAA;EACA,QAAQ,iBAAiB,QAAQ,OAAO;EACzC;AAEH,QAAO;EACL;EACA,QAAQ,OAAO,mBAAA,cAAA;EAGhB;;AAGH,MAAa,QAAQ,MACnB,EAAE,WAAA;AAEJ,MAAa,mBACX,SACA,WACa,QAAQ,QAAQ,MAAM,EAAE,WAAW,OAAO,CAAC,KAAK,MAAM,EAAE,KAAK;AClKtCA,IACnC,OAAO,EACN,YAAYA,IAAE,OACZA,IAAE,QAAQ,EACVA,IAAE,MAAM,CACNA,IAAE,OAAO;CACP,SAASA,IAAE,QAAQ,CAAC,UAAU;CAC9B,MAAMA,IAAE,MAAMA,IAAE,QAAQ,CAAC,CAAC,UAAU;CACpC,KAAKA,IAAE,OAAOA,IAAE,QAAQ,EAAEA,IAAE,QAAQ,CAAC,CAAC,UAAU;CACjD,CAAC,EACFA,IAAE,OAAO;CACP,KAAKA,IAAE,QAAQ;CACf,SAASA,IAAE,OAAOA,IAAE,QAAQ,EAAEA,IAAE,QAAQ,CAAC,CAAC,UAAU;CACrD,CAAC,CACH,CAAC,CACH,EACF,CAAC,CACD,aAAa;AAEhB,MAAa,qBAAqB;CAChC,oBAAoB;EAClB;GACE,OAAO;GACP,OAAO;GACP,MAAM;GACP;EACD;GACE,OAAO;GACP,OAAO;GACP,MAAM;GACP;EACD;GACE,OAAO;GACP,OAAO;GACP,MAAM;GACP;EACD;GACE,OAAO;GACP,OAAO;GACP,MAAM;GACP;EACD;GACE,OAAO;GACP,OAAO;GACP,MAAM;GACP;EACD;GACE,OAAO;GACP,OAAO;GACP,MAAM;GACP;EACD;GACE,OAAO;GACP,OAAO;GACP,MAAM;GACP;EACD;GACE,OAAO;GACP,OAAO;GACP,MAAM;GACP;EACD;GACE,OAAO;GACP,OAAO;GACP,MAAM;GACP;EACD;GACE,OAAO;GACP,OAAO;GACP,MAAM;GACP;EACD;GACE,OAAO;GACP,OAAO;GACP,MAAM;GACP;EACF;CACD,kBAAkB,CAChB;EACE,OAAO;EACP,OAAO;EACP,MAAM;EACP,CACF;CACD,qBAAqB;EACnB;GACE,OAAO;GACP,OAAO;GACP,MAAM;GACP;EACD;GACE,OAAO;GACP,OAAO;GACP,MAAM;GACP;EACD;GACE,OAAO;GACP,OAAO;GACP,MAAM;GACP;EACD;GACE,OAAO;GACP,OAAO;GACP,MAAM;GACP;EACD;GACE,OAAO;GACP,OAAO;GACP,MAAM;GACP;EACD;GACE,OAAO;GACP,OAAO;GACP,MAAM;GACP;EACD;GACE,OAAO;GACP,OAAO;GACP,MAAM;GACP;EACF;CACD,mBAAmB;EACjB;GACE,OAAO;GACP,OAAO;GACP,MAAM;GACP;EACD;GACE,OAAO;GACP,OAAO;GACP,MAAM;GACP;EACD;GACE,OAAO;GACP,OAAO;GACP,MAAM;GACP;EACD;GACE,OAAO;GACP,OAAO;GACP,MAAM;GACP;EACD;GACE,OAAO;GACP,OAAO;GACP,MAAM;GACP;EACD;GACE,OAAO;GACP,OAAO;GACP,MAAM;GACP;EACD;GACE,OAAO;GACP,OAAO;GACP,MAAM;GACP;EACF;CACD,oBAAoB;EAClB;GACE,OAAO;GACP,OAAO;GACP,MAAM;GACP;EACD;GACE,OAAO;GACP,OAAO;GACP,MAAM;GACP;EACD;GACE,OAAO;GACP,OAAO;GACP,MAAM;GACP;EACF;CACD,yBAAyB;EACvB;GACE,OAAO;GACP,OAAO;GACP,MAAM;GACP;EACD;GACE,OAAO;GACP,OAAO;GACP,MAAM;GACP;EACD;GACE,OAAO;GACP,OAAO;GACP,MAAM;GACP;EACD;GACE,OAAO;GACP,OAAO;GACP,MAAM;GACP;EACD;GACE,OAAO;GACP,OAAO;GACP,MAAM;GACP;EACD;GACE,OAAO;GACP,OAAO;GACP,MAAM;GACP;EACD;GACE,OAAO;GACP,OAAO;GACP,MAAM;GACP;EACD;GACE,OAAO;GACP,OAAO;GACP,MAAM;GACP;EACD;GACE,OAAO;GACP,OAAO;GACP,MAAM;GACP;EACD;GACE,OAAO;GACP,OAAO;GACP,MAAM;GACP;EACD;GACE,OAAO;GACP,OAAO;GACP,MAAM;GACP;EACF;CACF;AAED,MAAa,qBAAqB,OAAO,OAAO,mBAAmB,CAChE,MAAM,CACN,KAAK,YAAY,QAAQ,MAAM;AAElC,MAAa,yBAAyB,aACpC,SAAS,WAAW,mBAAmB,UACvC,mBAAmB,OAAO,YAAY,SAAS,SAAS,QAAQ,CAAC;AAEnE,MAAa,eAAe,kBAA6B,UAAoB;CAE3E,MAAM,UAAU,GADH,QAAQ,0BAA0B,0BACvB;CAExB,MAAM,SAAmB,EAAE;AAG3B,KACE,oBACA,iBAAiB,SAAS,KAC1B,CAAC,sBAAsB,iBAAiB,CAExC,QAAO,KAAK,YAAY,iBAAiB,KAAK,IAAI,GAAG;AAGvD,QAAO,OAAO,SAAS,IAAI,GAAG,QAAQ,GAAG,OAAO,KAAK,IAAI,KAAK;;AAGhE,MAAa,6BACX,QACA,kBACA,UACG;CACH,MAAM,SAAkC,EACtC,KAAK,YAAY,kBAAkB,MAAM,EAC1C;AAGD,KAAI,OACF,QAAO,UAAU,EACf,eAAe,UAAU,UAC1B;AAGH,QAAO;;AAGT,MAAa,0BACX,QACA,kBACA,UACG;CACH,MAAM,kBAAkB,YAAY,kBAAkB,MAAM;AAG5D,KAAI,CAAC,OACH,QAAO;EACL,SAAS;EACT,MAAM;GAAC;GAAM;GAAqB;GAAgB;EACnD;AAIH,QAAO;EACL,SAAS;EACT,MAAM;GACJ;GACA;GACA;GACA;GACA;GACD;EACD,KAAK,EACH,qBAAqB,UAAU,UAChC;EACF"}
@@ -0,0 +1,108 @@
1
+ import { s as logToFile } from "./debug-Ch4BDOtD.js";
2
+ import { g as GITHUB_SKILLS_BASE_URL, o as AWS_SKILLS_BASE_URL } from "./constants-jFI2SaE1.js";
3
+ //#region src/shared/health-checks/endpoints.ts
4
+ function noConnectionResult(error, attempts) {
5
+ return {
6
+ status: "no-connection",
7
+ error,
8
+ rawIndicator: attempts > 1 ? `attempts=${attempts}` : void 0
9
+ };
10
+ }
11
+ function downResult(error) {
12
+ return {
13
+ status: "down",
14
+ error
15
+ };
16
+ }
17
+ const RETRY_BACKOFFS_MS = [500, 2e3];
18
+ async function attemptFetch(url, timeoutMs, redirect) {
19
+ const controller = new AbortController();
20
+ const tid = setTimeout(() => controller.abort(), timeoutMs);
21
+ try {
22
+ const res = await fetch(url, {
23
+ signal: controller.signal,
24
+ redirect
25
+ });
26
+ clearTimeout(tid);
27
+ return {
28
+ kind: "response",
29
+ res
30
+ };
31
+ } catch (e) {
32
+ clearTimeout(tid);
33
+ const err = e instanceof Error ? e : /* @__PURE__ */ new Error("Unknown error");
34
+ return {
35
+ kind: "error",
36
+ error: err,
37
+ timedOut: err.name === "AbortError"
38
+ };
39
+ }
40
+ }
41
+ async function fetchEndpointHealth(url, timeoutMs = 5e3, isExpectedStatus = (s) => s === 200, redirect = "follow") {
42
+ let lastHttpStatus = null;
43
+ let lastError = "Unknown error";
44
+ let attempts = 0;
45
+ for (let i = 0; i <= RETRY_BACKOFFS_MS.length; i++) {
46
+ if (i > 0) {
47
+ const wait = RETRY_BACKOFFS_MS[i - 1];
48
+ logToFile(`[health-checks] retry ${i}/${RETRY_BACKOFFS_MS.length} for ${url} in ${wait}ms (last: ${lastError})`);
49
+ await new Promise((r) => setTimeout(r, wait));
50
+ }
51
+ attempts++;
52
+ const outcome = await attemptFetch(url, timeoutMs, redirect);
53
+ if (outcome.kind === "response") {
54
+ const res = outcome.res;
55
+ if (isExpectedStatus(res.status)) {
56
+ const result = {
57
+ status: "healthy",
58
+ rawIndicator: attempts > 1 ? `HTTP ${res.status} (attempts=${attempts})` : `HTTP ${res.status}`
59
+ };
60
+ logToFile(`[health-checks] GET ${url} -> ${result.status} (${result.rawIndicator})`);
61
+ return result;
62
+ }
63
+ lastHttpStatus = res.status;
64
+ lastError = `HTTP ${res.status}`;
65
+ continue;
66
+ }
67
+ lastError = outcome.timedOut ? `Request timed out after ${timeoutMs}ms` : outcome.error.message;
68
+ }
69
+ const result = lastHttpStatus !== null ? downResult(`HTTP ${lastHttpStatus} (attempts=${attempts})`) : noConnectionResult(lastError, attempts);
70
+ logToFile(`[health-checks] GET ${url} -> ${result.status} (attempts=${attempts}, ${result.error})`);
71
+ return result;
72
+ }
73
+ const checkLlmGatewayHealth = (gatewayUrl) => fetchEndpointHealth(new URL("/readyz", gatewayUrl).href);
74
+ /**
75
+ * Skills are published to two origins under the same filenames and
76
+ * `fetchWithRetry` fails over between them, so the run is only blocked when
77
+ * neither answers. Probed in parallel — sequential probes would double the
78
+ * worst case past `READINESS_TIMEOUT_MS`.
79
+ */
80
+ const checkSkillsOriginHealth = async () => {
81
+ const [github, aws] = await Promise.all([fetchEndpointHealth(`${GITHUB_SKILLS_BASE_URL}/skill-menu.json`), fetchEndpointHealth(`${AWS_SKILLS_BASE_URL}/skill-menu.json`)]);
82
+ return combineOriginHealth(github, aws);
83
+ };
84
+ /**
85
+ * Mirrors `fetchWithRetry`: a download tries GitHub, then AWS, so the run is
86
+ * only blocked when neither origin answers. Whichever failure the probes saw,
87
+ * one origin serving means skills are reachable.
88
+ */
89
+ function combineOriginHealth(github, aws) {
90
+ if (github.status === "healthy") return aws.status === "healthy" ? github : withIndicatorSuffix(github, "aws unavailable");
91
+ if (aws.status === "healthy") return withIndicatorSuffix(aws, "via aws, github unavailable");
92
+ const error = `github: ${github.error ?? "unknown"} | aws: ${aws.error ?? "unknown"}`;
93
+ return {
94
+ status: github.status === "down" || aws.status === "down" ? "down" : "no-connection",
95
+ error,
96
+ rawIndicator: github.rawIndicator ?? aws.rawIndicator
97
+ };
98
+ }
99
+ function withIndicatorSuffix(result, suffix) {
100
+ return {
101
+ ...result,
102
+ rawIndicator: result.rawIndicator ? `${result.rawIndicator} (${suffix})` : suffix
103
+ };
104
+ }
105
+ //#endregion
106
+ export { checkSkillsOriginHealth as n, checkLlmGatewayHealth as t };
107
+
108
+ //# sourceMappingURL=endpoints-DCVbM06L.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"endpoints-DCVbM06L.js","names":[],"sources":["../src/shared/health-checks/endpoints.ts"],"sourcesContent":["import { AWS_SKILLS_BASE_URL, GITHUB_SKILLS_BASE_URL } from '@shared/constants';\nimport { logToFile } from '@utils/debug';\nimport { ServiceHealthStatus, type BaseHealthResult } from './types';\n\n// Direct gateway and skill-origin checks distinguish HTTP failures from connection failures.\n\nfunction noConnectionResult(error: string, attempts: number): BaseHealthResult {\n return {\n status: ServiceHealthStatus.NoConnection,\n error,\n rawIndicator: attempts > 1 ? `attempts=${attempts}` : undefined,\n };\n}\n\nfunction downResult(error: string): BaseHealthResult {\n return { status: ServiceHealthStatus.Down, error };\n}\n\n// Backoffs sized to cover typical wifi flakiness — a single dropped\n// packet recovers via the 500ms retry; a wifi access point reconnect\n// or wifi↔LTE handoff (2-5s) is caught by the 2000ms retry. Tighter\n// schedules miss multi-second blips because all retries land in the\n// same dead window.\nconst RETRY_BACKOFFS_MS = [500, 2000];\n\nasync function attemptFetch(\n url: string,\n timeoutMs: number,\n redirect: 'follow' | 'manual' | 'error',\n): Promise<\n | { kind: 'response'; res: Response }\n | { kind: 'error'; error: Error; timedOut: boolean }\n> {\n const controller = new AbortController();\n const tid = setTimeout(() => controller.abort(), timeoutMs);\n try {\n const res = await fetch(url, { signal: controller.signal, redirect });\n clearTimeout(tid);\n return { kind: 'response', res };\n } catch (e) {\n clearTimeout(tid);\n const err = e instanceof Error ? e : new Error('Unknown error');\n return { kind: 'error', error: err, timedOut: err.name === 'AbortError' };\n }\n}\n\n// Exported so tests can pin the retry/taxonomy machinery directly.\nexport async function fetchEndpointHealth(\n url: string,\n timeoutMs = 5000,\n isExpectedStatus: (status: number) => boolean = (s) => s === 200,\n redirect: 'follow' | 'manual' | 'error' = 'follow',\n): Promise<BaseHealthResult> {\n // Total attempts = 1 initial + RETRY_BACKOFFS_MS.length retries. Both\n // unexpected HTTP statuses (4xx/5xx) and network errors trigger a retry:\n // transient 5xx and Cloudflare edge blips often recover on a retry, and\n // even nominally deterministic 4xx can be transient (CDN propagation\n // lag after a release, token rotation, rate-limit window resets). GETs\n // are idempotent so retrying is safe.\n //\n // Final status if every attempt fails:\n // - At least one HTTP response observed → `Down` (server-side evidence)\n // - Only network errors observed → `NoConnection`\n let lastHttpStatus: number | null = null;\n let lastError = 'Unknown error';\n let attempts = 0;\n\n for (let i = 0; i <= RETRY_BACKOFFS_MS.length; i++) {\n if (i > 0) {\n const wait = RETRY_BACKOFFS_MS[i - 1];\n logToFile(\n `[health-checks] retry ${i}/${RETRY_BACKOFFS_MS.length} for ${url} in ${wait}ms (last: ${lastError})`,\n );\n await new Promise((r) => setTimeout(r, wait));\n }\n attempts++;\n\n const outcome = await attemptFetch(url, timeoutMs, redirect);\n\n if (outcome.kind === 'response') {\n const res = outcome.res;\n if (isExpectedStatus(res.status)) {\n const result: BaseHealthResult = {\n status: ServiceHealthStatus.Healthy,\n rawIndicator:\n attempts > 1\n ? `HTTP ${res.status} (attempts=${attempts})`\n : `HTTP ${res.status}`,\n };\n logToFile(\n `[health-checks] GET ${url} -> ${result.status}` +\n ` (${result.rawIndicator})`,\n );\n return result;\n }\n lastHttpStatus = res.status;\n lastError = `HTTP ${res.status}`;\n continue;\n }\n\n lastError = outcome.timedOut\n ? `Request timed out after ${timeoutMs}ms`\n : outcome.error.message;\n }\n\n const result =\n lastHttpStatus !== null\n ? downResult(`HTTP ${lastHttpStatus} (attempts=${attempts})`)\n : noConnectionResult(lastError, attempts);\n logToFile(\n `[health-checks] GET ${url} -> ${result.status}` +\n ` (attempts=${attempts}, ${result.error})`,\n );\n return result;\n}\n\nexport const checkLlmGatewayHealth = (\n gatewayUrl: string,\n): Promise<BaseHealthResult> =>\n fetchEndpointHealth(new URL('/readyz', gatewayUrl).href);\n\n/**\n * Skills are published to two origins under the same filenames and\n * `fetchWithRetry` fails over between them, so the run is only blocked when\n * neither answers. Probed in parallel — sequential probes would double the\n * worst case past `READINESS_TIMEOUT_MS`.\n */\nexport const checkSkillsOriginHealth = async (): Promise<BaseHealthResult> => {\n const [github, aws] = await Promise.all([\n fetchEndpointHealth(`${GITHUB_SKILLS_BASE_URL}/skill-menu.json`),\n fetchEndpointHealth(`${AWS_SKILLS_BASE_URL}/skill-menu.json`),\n ]);\n return combineOriginHealth(github, aws);\n};\n\n/**\n * Mirrors `fetchWithRetry`: a download tries GitHub, then AWS, so the run is\n * only blocked when neither origin answers. Whichever failure the probes saw,\n * one origin serving means skills are reachable.\n */\nfunction combineOriginHealth(\n github: BaseHealthResult,\n aws: BaseHealthResult,\n): BaseHealthResult {\n if (github.status === ServiceHealthStatus.Healthy) {\n // Naming the dead origin makes a one-sided outage legible in the log and\n // in the readiness reasons, where the status alone reads as \"fine\".\n return aws.status === ServiceHealthStatus.Healthy\n ? github\n : withIndicatorSuffix(github, 'aws unavailable');\n }\n\n if (aws.status === ServiceHealthStatus.Healthy) {\n return withIndicatorSuffix(aws, 'via aws, github unavailable');\n }\n\n const error = `github: ${github.error ?? 'unknown'} | aws: ${\n aws.error ?? 'unknown'\n }`;\n const confirmedDown =\n github.status === ServiceHealthStatus.Down ||\n aws.status === ServiceHealthStatus.Down;\n return {\n status: confirmedDown\n ? ServiceHealthStatus.Down\n : ServiceHealthStatus.NoConnection,\n error,\n // Keeps the `attempts=N` the blocked-readiness analytics parses.\n rawIndicator: github.rawIndicator ?? aws.rawIndicator,\n };\n}\n\nfunction withIndicatorSuffix(\n result: BaseHealthResult,\n suffix: string,\n): BaseHealthResult {\n return {\n ...result,\n rawIndicator: result.rawIndicator\n ? `${result.rawIndicator} (${suffix})`\n : suffix,\n };\n}\n"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-B_-DWIq7.js";
2
- import { i as readProjectFile } from "./bounded-fs-FbZJ_vJ_.js";
2
+ import { i as readProjectFile } from "./bounded-fs-Bx9zTe6m.js";
3
3
  import * as path$1 from "path";
4
- //#region src/utils/env-api-key.ts
4
+ //#region src/shared/utils/env-api-key.ts
5
5
  var env_api_key_exports = /* @__PURE__ */ __exportAll({ readApiKeyFromEnv: () => readApiKeyFromEnv });
6
6
  /**
7
7
  * Read POSTHOG_PERSONAL_API_KEY from .env.local or .env in the current
@@ -16,4 +16,4 @@ function readApiKeyFromEnv() {
16
16
  //#endregion
17
17
  export { readApiKeyFromEnv as n, env_api_key_exports as t };
18
18
 
19
- //# sourceMappingURL=env-api-key-DwmkB3fm.js.map
19
+ //# sourceMappingURL=env-api-key-Qx8NnYHm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env-api-key-Qx8NnYHm.js","names":["path"],"sources":["../src/shared/utils/env-api-key.ts"],"sourcesContent":["import * as path from 'path';\nimport { readProjectFile } from './bounded-fs';\n\n/**\n * Read POSTHOG_PERSONAL_API_KEY from .env.local or .env in the current\n * working directory. Returns undefined when no key is found.\n */\nexport function readApiKeyFromEnv(): string | undefined {\n for (const envFile of ['.env.local', '.env']) {\n // null when missing or unreadable, e.g. a `.env` directory (a Python virtualenv).\n const content = readProjectFile(path.join(process.cwd(), envFile));\n const match = content?.match(/^POSTHOG_PERSONAL_API_KEY=(.+)$/m);\n if (match) return match[1].trim();\n }\n return undefined;\n}\n"],"mappings":";;;;;;;;;AAOA,SAAgB,oBAAwC;AACtD,MAAK,MAAM,WAAW,CAAC,cAAc,OAAO,EAAE;EAG5C,MAAM,QADU,gBAAgBA,OAAK,KAAK,QAAQ,KAAK,EAAE,QAAQ,CAAC,EAC3C,MAAM,mCAAmC;AAChE,MAAI,MAAO,QAAO,MAAM,GAAG,MAAM"}
@@ -1,9 +1,9 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-B_-DWIq7.js";
2
- import "./debug-DWYDVp0B.js";
3
- import "./bounded-fs-FbZJ_vJ_.js";
4
- import "./setup-utils-Bj0gejEf.js";
2
+ import "./constants-jFI2SaE1.js";
3
+ import "./bounded-fs-Bx9zTe6m.js";
4
+ import "./setup-utils-CmTCTPkX.js";
5
5
  import readEnvModule from "read-env";
6
- //#region src/utils/environment.ts
6
+ //#region src/shared/utils/environment.ts
7
7
  var environment_exports = /* @__PURE__ */ __exportAll({
8
8
  isNonInteractiveEnvironment: () => isNonInteractiveEnvironment,
9
9
  readEnvironment: () => readEnvironment
@@ -22,7 +22,7 @@ function isNonInteractiveEnvironment() {
22
22
  * so every question would stall for the bridge timeout instead of failing fast
23
23
  * with an actionable error. See `shouldDisableAsk`.
24
24
  */
25
- const NEVER_FROM_ENV = ["e2eAsk"];
25
+ const NEVER_FROM_ENV = ["e2eAsk", "runId"];
26
26
  /**
27
27
  * Session args from the `POSTHOG_WIZARD_*` environment variables.
28
28
  *
@@ -41,4 +41,4 @@ function readEnvironment() {
41
41
  //#endregion
42
42
  export { isNonInteractiveEnvironment as n, environment_exports as t };
43
43
 
44
- //# sourceMappingURL=environment-1wA7uwAS.js.map
44
+ //# sourceMappingURL=environment-B07Bmxae.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"environment-B07Bmxae.js","names":[],"sources":["../src/shared/utils/environment.ts"],"sourcesContent":["import readEnvModule from 'read-env';\n\nconst readEnv =\n typeof readEnvModule === 'function'\n ? readEnvModule\n : (readEnvModule as any).default;\nimport { tryGetPackageJson } from './setup-utils';\nimport type { WizardRunOptions } from './types';\nimport { boundedGlob } from './bounded-fs';\nimport { IS_DEV } from '@shared/constants';\n\nexport function isNonInteractiveEnvironment(): boolean {\n if (IS_DEV) {\n return false;\n }\n\n if (!process.stdout.isTTY || !process.stderr.isTTY) {\n return true;\n }\n\n return false;\n}\n\n/**\n * Session fields the environment must never set, matched case-insensitively\n * against the camel-cased key `read-env` produces.\n *\n * `e2eAsk` re-wires the `wizard_ask` bridge in an otherwise non-interactive\n * run. Only the e2e TUI host may set it: a real `--ci` run has nobody to answer,\n * so every question would stall for the bridge timeout instead of failing fast\n * with an actionable error. See `shouldDisableAsk`.\n */\nconst NEVER_FROM_ENV = ['e2eAsk', 'runId'];\n\n/**\n * Session args from the `POSTHOG_WIZARD_*` environment variables.\n *\n * `read-env` camel-cases every prefixed variable into a key, and the CI runner\n * spreads the whole bag into `buildSession` — so adding a field to the session\n * silently adds an environment variable that sets it. Most of them are meant to\n * work that way (`POSTHOG_WIZARD_DEBUG` → `debug`); the ones in\n * {@link NEVER_FROM_ENV} are not, and are dropped here rather than at the call\n * site, so a second caller cannot reopen the door.\n */\nexport function readEnvironment(): Record<string, unknown> {\n const result = readEnv('POSTHOG_WIZARD') as Record<string, unknown>;\n\n for (const key of Object.keys(result)) {\n if (NEVER_FROM_ENV.some((k) => k.toLowerCase() === key.toLowerCase())) {\n delete result[key];\n }\n }\n\n return result;\n}\n\nexport async function detectEnvVarPrefix(\n options: WizardRunOptions,\n): Promise<string> {\n const packageJson = await tryGetPackageJson(options);\n if (!packageJson) return 'VITE_PUBLIC_';\n\n const deps = { ...packageJson.dependencies, ...packageJson.devDependencies };\n const has = (name: string) => name in deps;\n // O(1) matches per probe: one config file existing anywhere answers it.\n const hasAnyFile = async (patterns: string[]) => {\n const matches = await boundedGlob(patterns, {\n cwd: options.installDir,\n limit: 1,\n });\n return matches.length > 0;\n };\n\n // --- Next.js\n if (has('next') || (await hasAnyFile(['**/next.config.{js,ts,mjs,cjs}']))) {\n return 'NEXT_PUBLIC_';\n }\n\n // --- Create React App\n if (\n has('react-scripts') ||\n has('create-react-app') ||\n (await hasAnyFile(['**/config-overrides.js']))\n ) {\n return 'REACT_APP_';\n }\n\n // --- Vite (vanilla, TanStack, Solid, etc.)\n // Note: Vite does not need PUBLIC_ but we use it to follow the docs, to improve the chances of an LLM getting it right.\n if (has('vite') || (await hasAnyFile(['**/vite.config.{js,ts,mjs,cjs}']))) {\n return 'VITE_PUBLIC_';\n }\n\n // --- SvelteKit\n if (\n has('@sveltejs/kit') ||\n (await hasAnyFile(['**/svelte.config.{js,ts}']))\n ) {\n return 'PUBLIC_';\n }\n\n // --- TanStack Start (uses Vite)\n if (\n has('@tanstack/start') ||\n (await hasAnyFile(['**/tanstack.config.{js,ts}']))\n ) {\n return 'VITE_PUBLIC_';\n }\n\n // --- SolidStart (uses Vite)\n if (has('solid-start') || (await hasAnyFile(['**/solid.config.{js,ts}']))) {\n return 'VITE_PUBLIC_';\n }\n\n // --- Astro\n if (has('astro') || (await hasAnyFile(['**/astro.config.{js,ts,mjs}']))) {\n return 'PUBLIC_';\n }\n\n // We default to Vite if we can't detect a specific framework, since it's the most commonly used.\n return 'VITE_PUBLIC_';\n}\n"],"mappings":";;;;;;;;;;AAEA,MAAM,UACJ,OAAO,kBAAkB,aACrB,gBACC,cAAsB;AAM7B,SAAgB,8BAAuC;AAKrD,KAAI,CAAC,QAAQ,OAAO,SAAS,CAAC,QAAQ,OAAO,MAC3C,QAAO;AAGT,QAAO;;;;;;;;;;;AAYT,MAAM,iBAAiB,CAAC,UAAU,QAAQ;;;;;;;;;;;AAY1C,SAAgB,kBAA2C;CACzD,MAAM,SAAS,QAAQ,iBAAiB;AAExC,MAAK,MAAM,OAAO,OAAO,KAAK,OAAO,CACnC,KAAI,eAAe,MAAM,MAAM,EAAE,aAAa,KAAK,IAAI,aAAa,CAAC,CACnE,QAAO,OAAO;AAIlB,QAAO"}
@@ -1,4 +1,4 @@
1
- //#region src/lib/errors/codes.ts
1
+ //#region src/shared/errors/codes.ts
2
2
  const ErrorCodes = {
3
3
  CliNodeVersion: "PHW_CLI_NODE_VERSION",
4
4
  CliBadArgs: "PHW_CLI_BAD_ARGS",
@@ -33,6 +33,7 @@ const ErrorCodes = {
33
33
  SkillNotFound: "PHW_SKILL_NOT_FOUND",
34
34
  SkillDownloadFailed: "PHW_SKILL_DOWNLOAD_FAILED",
35
35
  AgentAbort: "PHW_AGENT_ABORT",
36
+ AgenticDetectionTimeout: "PHW_AGENTIC_DETECTION_TIMEOUT",
36
37
  AgentMcpMissing: "PHW_AGENT_MCP_MISSING",
37
38
  AgentResourceMissing: "PHW_AGENT_RESOURCE_MISSING",
38
39
  AgentRateLimit: "PHW_AGENT_RATE_LIMIT",
@@ -53,7 +54,7 @@ function isErrorCode(value) {
53
54
  return Object.values(ErrorCodes).includes(value);
54
55
  }
55
56
  //#endregion
56
- //#region src/lib/errors/emit.ts
57
+ //#region src/shared/errors/emit.ts
57
58
  const PHW_ERROR_PREFIX = "phw-error:";
58
59
  function formatWizardErrorLine(line) {
59
60
  return `${PHW_ERROR_PREFIX} ${JSON.stringify(line)}`;
@@ -61,9 +62,26 @@ function formatWizardErrorLine(line) {
61
62
  function emitWizardError(line) {
62
63
  process.stderr.write(`${formatWizardErrorLine(line)}\n`);
63
64
  }
64
- ErrorCodes.CliNodeVersion, ErrorCodes.CliBadArgs, ErrorCodes.CliFlagUnavailable, ErrorCodes.CliInteractiveRequired, ErrorCodes.ArgsMissingApiKey, ErrorCodes.ArgsMissingInstallDir, ErrorCodes.ArgsMissingEmail, ErrorCodes.ArgsSignupProvisionFailed, ErrorCodes.AuthKeyType, ErrorCodes.AuthInvalidOrExpired, ErrorCodes.AuthSessionExpired, ErrorCodes.AuthMissingScope, ErrorCodes.AuthRegionMismatch, ErrorCodes.AuthSettingsConflict, ErrorCodes.AuthStoredLoginConflict, ErrorCodes.AuthProjectFetchFailed, ErrorCodes.EnvLocalServicesDown, ErrorCodes.EnvServiceOutage, ErrorCodes.DetectBadDirectory, ErrorCodes.DetectNoFramework, ErrorCodes.DetectUnsupportedVersion, ErrorCodes.DetectUnsupportedPlatform, ErrorCodes.DetectNoPosthogSdk, ErrorCodes.DetectNoProjectFiles, ErrorCodes.DetectNoSources, ErrorCodes.DetectNoPackageJson, ErrorCodes.DetectNoSdks, ErrorCodes.DetectMissingStripe, ErrorCodes.DetectUnclassified, ErrorCodes.SkillMenuFetchFailed, ErrorCodes.SkillNotFound, ErrorCodes.SkillDownloadFailed, ErrorCodes.AgentAbort, ErrorCodes.AgentMcpMissing, ErrorCodes.AgentResourceMissing, ErrorCodes.AgentRateLimit, ErrorCodes.AgentApiError, ErrorCodes.AgentYaraViolation, ErrorCodes.AgentNoProgress, ErrorCodes.AgentIncompleteTasks, ErrorCodes.AgentOrchestratorSkillVariantMissing, ErrorCodes.AgentOrchestratorTasksFailed, ErrorCodes.AgentOrchestratorHollowRun, ErrorCodes.AgentOrchestratorSinkInvariant, ErrorCodes.GatewayMintRefused, ErrorCodes.GatewayMintFailed, ErrorCodes.SettingsUnfixableConflict, ErrorCodes.InternalUnhandled;
65
+ ErrorCodes.CliNodeVersion, ErrorCodes.CliBadArgs, ErrorCodes.CliFlagUnavailable, ErrorCodes.CliInteractiveRequired, ErrorCodes.ArgsMissingApiKey, ErrorCodes.ArgsMissingInstallDir, ErrorCodes.ArgsMissingEmail, ErrorCodes.ArgsSignupProvisionFailed, ErrorCodes.AuthKeyType, ErrorCodes.AuthInvalidOrExpired, ErrorCodes.AuthSessionExpired, ErrorCodes.AuthMissingScope, ErrorCodes.AuthRegionMismatch, ErrorCodes.AuthSettingsConflict, ErrorCodes.AuthStoredLoginConflict, ErrorCodes.AuthProjectFetchFailed, ErrorCodes.EnvLocalServicesDown, ErrorCodes.EnvServiceOutage, ErrorCodes.DetectBadDirectory, ErrorCodes.DetectNoFramework, ErrorCodes.DetectUnsupportedVersion, ErrorCodes.DetectUnsupportedPlatform, ErrorCodes.DetectNoPosthogSdk, ErrorCodes.DetectNoProjectFiles, ErrorCodes.DetectNoSources, ErrorCodes.DetectNoPackageJson, ErrorCodes.DetectNoSdks, ErrorCodes.DetectMissingStripe, ErrorCodes.DetectUnclassified, ErrorCodes.SkillMenuFetchFailed, ErrorCodes.SkillNotFound, ErrorCodes.SkillDownloadFailed, ErrorCodes.AgentAbort, ErrorCodes.AgenticDetectionTimeout, ErrorCodes.AgentMcpMissing, ErrorCodes.AgentResourceMissing, ErrorCodes.AgentRateLimit, ErrorCodes.AgentApiError, ErrorCodes.AgentYaraViolation, ErrorCodes.AgentNoProgress, ErrorCodes.AgentIncompleteTasks, ErrorCodes.AgentOrchestratorSkillVariantMissing, ErrorCodes.AgentOrchestratorTasksFailed, ErrorCodes.AgentOrchestratorHollowRun, ErrorCodes.AgentOrchestratorSinkInvariant, ErrorCodes.GatewayMintRefused, ErrorCodes.GatewayMintFailed, ErrorCodes.SettingsUnfixableConflict, ErrorCodes.InternalUnhandled;
65
66
  //#endregion
66
- //#region src/lib/errors/auth.ts
67
+ //#region src/shared/errors/wizard-error.ts
68
+ /**
69
+ * Structured error data for analytics and the machine-readable error line.
70
+ *
71
+ * A data carrier: the agent returns it inside a failure and the legacy adapter
72
+ * hands it to `wizardAbort()`, which captures it. Never thrown by the wizard.
73
+ */
74
+ var WizardError = class extends Error {
75
+ code;
76
+ constructor(message, context, code) {
77
+ super(message);
78
+ this.context = context;
79
+ this.name = "WizardError";
80
+ this.code = code;
81
+ }
82
+ };
83
+ //#endregion
84
+ //#region src/shared/errors/auth.ts
67
85
  function classifyAuthFailure(input) {
68
86
  if (input.sessionExpired) return ErrorCodes.AuthSessionExpired;
69
87
  if (input.usingManagedLogin) return ErrorCodes.AuthStoredLoginConflict;
@@ -74,7 +92,7 @@ function classifyAuthFailure(input) {
74
92
  return ErrorCodes.AuthInvalidOrExpired;
75
93
  }
76
94
  //#endregion
77
- //#region src/lib/agent/signals.ts
95
+ //#region src/agent/signals.ts
78
96
  /**
79
97
  * Agent signal vocabulary — the marker strings the agent emits and the error
80
98
  * taxonomy the runner returns. Kept as a dependency-free leaf module so both
@@ -104,8 +122,9 @@ const REMARK_INSTRUCTION = `Reply with a single line that starts with ${AgentSig
104
122
  */
105
123
  const RESUME_INSTRUCTION = "Your previous request failed with a transient gateway authentication error that has since been fixed. Continue the task from where you left off.";
106
124
  //#endregion
107
- //#region src/lib/errors/agent-map.ts
125
+ //#region src/shared/errors/agent-map.ts
108
126
  const AGENT_ERROR_CODE = {
127
+ ["WIZARD_AGENTIC_DETECTION_TIMEOUT"]: ErrorCodes.AgenticDetectionTimeout,
109
128
  ["WIZARD_MCP_MISSING"]: ErrorCodes.AgentMcpMissing,
110
129
  ["WIZARD_RESOURCE_MISSING"]: ErrorCodes.AgentResourceMissing,
111
130
  ["WIZARD_RATE_LIMIT"]: ErrorCodes.AgentRateLimit,
@@ -116,31 +135,7 @@ const AGENT_ERROR_CODE = {
116
135
  ["WIZARD_INCOMPLETE_TASKS"]: ErrorCodes.AgentIncompleteTasks
117
136
  };
118
137
  //#endregion
119
- //#region src/lib/errors/detect-map.ts
120
- const DETECT_CODES = {
121
- "bad-directory": ErrorCodes.DetectBadDirectory,
122
- "unsupported-platform": ErrorCodes.DetectUnsupportedPlatform,
123
- "no-project-files": ErrorCodes.DetectNoProjectFiles,
124
- "no-sources": ErrorCodes.DetectNoSources,
125
- "no-package-json": ErrorCodes.DetectNoPackageJson,
126
- "no-sdks": ErrorCodes.DetectNoSdks,
127
- "missing-stripe": ErrorCodes.DetectMissingStripe,
128
- "no-posthog-sdk": ErrorCodes.DetectNoPosthogSdk,
129
- "no-posthog": ErrorCodes.DetectNoPosthogSdk,
130
- "missing-posthog": ErrorCodes.DetectNoPosthogSdk
131
- };
132
- /**
133
- * `kind` arrives as a bare string — `frameworkContext.detectError` is untyped
134
- * storage — so the lookup can still miss. It falls back to a detect-group code
135
- * with `retry: 'no'`, never to `InternalUnhandled`: an unrecognized precondition
136
- * failure is still a precondition failure, and telling a sandbox to retry one
137
- * costs it the whole budget.
138
- */
139
- function detectErrorCode(kind) {
140
- return DETECT_CODES[kind] ?? ErrorCodes.DetectUnclassified;
141
- }
142
- //#endregion
143
- //#region src/lib/errors/skill-map.ts
138
+ //#region src/shared/errors/skill-map.ts
144
139
  const SKILL_CODES = {
145
140
  "menu-fetch-failed": ErrorCodes.SkillMenuFetchFailed,
146
141
  "skill-not-found": ErrorCodes.SkillNotFound,
@@ -151,7 +146,7 @@ function skillErrorCode(result) {
151
146
  return SKILL_CODES[result.kind];
152
147
  }
153
148
  //#endregion
154
- //#region src/lib/errors/sanitize.ts
149
+ //#region src/shared/errors/sanitize.ts
155
150
  const DETAIL_ALLOWLIST = [
156
151
  "reason",
157
152
  "detected",
@@ -164,7 +159,7 @@ function sanitizeErrorDetail(detail) {
164
159
  return Object.keys(safe).length > 0 ? safe : void 0;
165
160
  }
166
161
  //#endregion
167
- //#region src/lib/errors/run-failure.ts
162
+ //#region src/shared/errors/run-failure.ts
168
163
  /**
169
164
  * How a run's terminal error is reported. A coded WizardError (a mint refusal,
170
165
  * say) keeps its own message and code; anything else is unhandled and gets the
@@ -185,6 +180,6 @@ function classifyRunFailure(err) {
185
180
  };
186
181
  }
187
182
  //#endregion
188
- export { AGENT_ERROR_CODE as a, RESUME_INSTRUCTION as c, ErrorCodes as d, detectErrorCode as i, classifyAuthFailure as l, sanitizeErrorDetail as n, AgentSignals as o, skillErrorCode as r, REMARK_INSTRUCTION as s, classifyRunFailure as t, emitWizardError as u };
183
+ export { AgentSignals as a, classifyAuthFailure as c, ErrorCodes as d, AGENT_ERROR_CODE as i, WizardError as l, sanitizeErrorDetail as n, REMARK_INSTRUCTION as o, skillErrorCode as r, RESUME_INSTRUCTION as s, classifyRunFailure as t, emitWizardError as u };
189
184
 
190
- //# sourceMappingURL=errors--OqLxmWR.js.map
185
+ //# sourceMappingURL=errors-CQ9kttJD.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors-CQ9kttJD.js","names":[],"sources":["../src/shared/errors/codes.ts","../src/shared/errors/emit.ts","../src/shared/errors/catalog.ts","../src/shared/errors/wizard-error.ts","../src/shared/errors/auth.ts","../src/agent/signals.ts","../src/shared/errors/agent-map.ts","../src/shared/errors/skill-map.ts","../src/shared/errors/sanitize.ts","../src/shared/errors/run-failure.ts"],"sourcesContent":["export const ErrorCodes = {\n CliNodeVersion: 'PHW_CLI_NODE_VERSION',\n CliBadArgs: 'PHW_CLI_BAD_ARGS',\n CliFlagUnavailable: 'PHW_CLI_FLAG_UNAVAILABLE',\n CliInteractiveRequired: 'PHW_CLI_INTERACTIVE_REQUIRED',\n ArgsMissingApiKey: 'PHW_ARGS_MISSING_API_KEY',\n ArgsMissingInstallDir: 'PHW_ARGS_MISSING_INSTALL_DIR',\n ArgsMissingEmail: 'PHW_ARGS_MISSING_EMAIL',\n ArgsSignupProvisionFailed: 'PHW_ARGS_SIGNUP_PROVISION_FAILED',\n AuthKeyType: 'PHW_AUTH_KEY_TYPE',\n AuthMissingScope: 'PHW_AUTH_MISSING_SCOPE',\n AuthRegionMismatch: 'PHW_AUTH_REGION_MISMATCH',\n AuthInvalidOrExpired: 'PHW_AUTH_INVALID_OR_EXPIRED',\n /** The OAuth grant itself is gone — distinct from a merely expired token. */\n AuthSessionExpired: 'PHW_AUTH_SESSION_EXPIRED',\n AuthSettingsConflict: 'PHW_AUTH_SETTINGS_CONFLICT',\n AuthStoredLoginConflict: 'PHW_AUTH_STORED_LOGIN_CONFLICT',\n AuthProjectFetchFailed: 'PHW_AUTH_PROJECT_FETCH_FAILED',\n EnvLocalServicesDown: 'PHW_ENV_LOCAL_SERVICES_DOWN',\n EnvServiceOutage: 'PHW_ENV_SERVICE_OUTAGE',\n DetectBadDirectory: 'PHW_DETECT_BAD_DIRECTORY',\n DetectNoFramework: 'PHW_DETECT_NO_FRAMEWORK',\n DetectUnsupportedVersion: 'PHW_DETECT_UNSUPPORTED_VERSION',\n DetectUnsupportedPlatform: 'PHW_DETECT_UNSUPPORTED_PLATFORM',\n DetectNoPosthogSdk: 'PHW_DETECT_NO_POSTHOG_SDK',\n DetectNoProjectFiles: 'PHW_DETECT_NO_PROJECT_FILES',\n DetectNoSources: 'PHW_DETECT_NO_SOURCES',\n DetectNoPackageJson: 'PHW_DETECT_NO_PACKAGE_JSON',\n DetectNoSdks: 'PHW_DETECT_NO_SDKS',\n DetectMissingStripe: 'PHW_DETECT_MISSING_STRIPE',\n DetectUnclassified: 'PHW_DETECT_UNCLASSIFIED',\n SkillMenuFetchFailed: 'PHW_SKILL_MENU_FETCH_FAILED',\n SkillNotFound: 'PHW_SKILL_NOT_FOUND',\n SkillDownloadFailed: 'PHW_SKILL_DOWNLOAD_FAILED',\n AgentAbort: 'PHW_AGENT_ABORT',\n AgenticDetectionTimeout: 'PHW_AGENTIC_DETECTION_TIMEOUT',\n AgentMcpMissing: 'PHW_AGENT_MCP_MISSING',\n AgentResourceMissing: 'PHW_AGENT_RESOURCE_MISSING',\n AgentRateLimit: 'PHW_AGENT_RATE_LIMIT',\n AgentApiError: 'PHW_AGENT_API_ERROR',\n AgentYaraViolation: 'PHW_AGENT_YARA_VIOLATION',\n AgentNoProgress: 'PHW_AGENT_NO_PROGRESS',\n AgentIncompleteTasks: 'PHW_AGENT_INCOMPLETE_TASKS',\n AgentOrchestratorSkillVariantMissing:\n 'PHW_AGENT_ORCHESTRATOR_SKILL_VARIANT_MISSING',\n AgentOrchestratorTasksFailed: 'PHW_AGENT_ORCHESTRATOR_TASKS_FAILED',\n AgentOrchestratorHollowRun: 'PHW_AGENT_ORCHESTRATOR_HOLLOW_RUN',\n AgentOrchestratorSinkInvariant: 'PHW_AGENT_ORCHESTRATOR_SINK_INVARIANT',\n /** The backend answered the mint with a deliberate refusal. */\n GatewayMintRefused: 'PHW_GATEWAY_MINT_REFUSED',\n /** The mint was unreachable, errored, or returned an unusable token. */\n GatewayMintFailed: 'PHW_GATEWAY_MINT_FAILED',\n SettingsUnfixableConflict: 'PHW_SETTINGS_UNFIXABLE_CONFLICT',\n InternalUnhandled: 'PHW_INTERNAL_UNHANDLED',\n} as const;\n\nexport type ErrorCode = (typeof ErrorCodes)[keyof typeof ErrorCodes];\n\nexport const ERROR_CODE_PATTERN = /^PHW_[A-Z][A-Z0-9_]*$/;\n\nexport function isErrorCode(value: string): value is ErrorCode {\n return (Object.values(ErrorCodes) as string[]).includes(value);\n}\n","import type { ErrorCode } from './codes';\n\nexport const PHW_ERROR_PREFIX = 'phw-error:';\n\nexport interface WizardErrorLine {\n code: ErrorCode;\n message: string;\n detail?: Record<string, unknown>;\n}\n\nexport function formatWizardErrorLine(line: WizardErrorLine): string {\n return `${PHW_ERROR_PREFIX} ${JSON.stringify(line)}`;\n}\n\nexport function emitWizardError(line: WizardErrorLine): void {\n process.stderr.write(`${formatWizardErrorLine(line)}\\n`);\n}\n","import { ErrorCodes, type ErrorCode } from './codes';\nimport type { ErrorCatalogEntry } from './types';\n\nexport const ERROR_CATALOG: Record<ErrorCode, ErrorCatalogEntry> = {\n [ErrorCodes.CliNodeVersion]: {\n group: 'cli',\n retry: 'no',\n description: 'Node.js runtime is older than the required range.',\n },\n [ErrorCodes.CliBadArgs]: {\n group: 'cli',\n retry: 'no',\n description: 'Unrecognized command or option.',\n },\n [ErrorCodes.CliFlagUnavailable]: {\n group: 'cli',\n retry: 'no',\n description: 'A dev-only flag or env var was passed to a published build.',\n },\n [ErrorCodes.CliInteractiveRequired]: {\n group: 'cli',\n retry: 'no',\n description:\n 'The command requires an interactive terminal and has no non-interactive fallback.',\n },\n [ErrorCodes.ArgsMissingApiKey]: {\n group: 'args',\n retry: 'no',\n description: 'Non-interactive run without --api-key.',\n },\n [ErrorCodes.ArgsMissingInstallDir]: {\n group: 'args',\n retry: 'no',\n description: 'Non-interactive run without --install-dir.',\n },\n [ErrorCodes.ArgsMissingEmail]: {\n group: 'args',\n retry: 'no',\n description: 'Signup run without --email.',\n },\n [ErrorCodes.ArgsSignupProvisionFailed]: {\n group: 'args',\n retry: 'yes',\n description: 'Account provisioning for --signup failed.',\n },\n [ErrorCodes.AuthKeyType]: {\n group: 'auth',\n retry: 'no',\n description: 'The API key has the wrong type or prefix for this mode.',\n },\n [ErrorCodes.AuthInvalidOrExpired]: {\n group: 'auth',\n retry: 'no',\n description: 'The credential was rejected by the LLM gateway or API.',\n },\n [ErrorCodes.AuthSessionExpired]: {\n group: 'auth',\n // Retryable, unlike its neighbours: a new run means a new login.\n retry: 'yes',\n description: 'The OAuth grant expired or was revoked mid-run.',\n },\n [ErrorCodes.AuthMissingScope]: {\n group: 'auth',\n retry: 'no',\n description: 'The personal API key lacks a scope the run requires.',\n },\n [ErrorCodes.AuthRegionMismatch]: {\n group: 'auth',\n retry: 'no',\n description: 'The resolved region does not match the credential.',\n },\n [ErrorCodes.AuthSettingsConflict]: {\n group: 'auth',\n retry: 'no',\n description: 'A Claude settings file overrides the gateway credential.',\n },\n [ErrorCodes.AuthStoredLoginConflict]: {\n group: 'auth',\n retry: 'no',\n description:\n 'The agent SDK used a stored Claude login instead of the gateway token.',\n },\n [ErrorCodes.AuthProjectFetchFailed]: {\n group: 'auth',\n retry: 'yes',\n description: 'Fetching user or project data from the PostHog API failed.',\n },\n [ErrorCodes.EnvLocalServicesDown]: {\n group: 'env',\n retry: 'yes',\n description: 'A local dev target service is not running.',\n },\n [ErrorCodes.EnvServiceOutage]: {\n group: 'env',\n retry: 'yes',\n description: 'Blocking external services are down.',\n },\n [ErrorCodes.DetectBadDirectory]: {\n group: 'detect',\n retry: 'no',\n description:\n 'The install directory is missing, not a directory, or unreadable.',\n },\n [ErrorCodes.DetectNoFramework]: {\n group: 'detect',\n retry: 'no',\n description: 'Framework auto-detection found no supported framework.',\n },\n [ErrorCodes.DetectUnsupportedVersion]: {\n group: 'detect',\n retry: 'no',\n description:\n 'The detected framework version is below the supported minimum.',\n },\n [ErrorCodes.DetectUnsupportedPlatform]: {\n group: 'detect',\n retry: 'no',\n description: 'The project platform has no matching skill variant.',\n },\n [ErrorCodes.DetectNoPosthogSdk]: {\n group: 'detect',\n retry: 'no',\n description:\n 'The program requires an installed PostHog SDK and none was found.',\n },\n [ErrorCodes.DetectNoProjectFiles]: {\n group: 'detect',\n retry: 'no',\n description: 'The project has no files for the program to work on.',\n },\n [ErrorCodes.DetectNoSources]: {\n group: 'detect',\n retry: 'no',\n description: 'No data warehouse sources were found.',\n },\n [ErrorCodes.DetectNoPackageJson]: {\n group: 'detect',\n retry: 'no',\n description: 'The project has no package.json for the program to scan.',\n },\n [ErrorCodes.DetectNoSdks]: {\n group: 'detect',\n retry: 'no',\n description: 'None of the SDKs the program needs are installed.',\n },\n [ErrorCodes.DetectMissingStripe]: {\n group: 'detect',\n retry: 'no',\n description: 'Revenue analytics requires a Stripe SDK and none was found.',\n },\n [ErrorCodes.DetectUnclassified]: {\n group: 'detect',\n // A detect step reported a precondition failure we have no code for yet.\n // Every known cause is a property of the user's project, so retrying can\n // only burn budget — fail closed rather than inheriting `internal`'s 'yes'.\n retry: 'no',\n description: 'A detect step failed with a kind absent from the catalog.',\n },\n [ErrorCodes.SkillMenuFetchFailed]: {\n group: 'skill',\n retry: 'yes',\n description: 'The context-mill skill menu could not be fetched.',\n },\n [ErrorCodes.SkillNotFound]: {\n group: 'skill',\n retry: 'no',\n description: 'The skill id is absent from the context-mill menu.',\n },\n [ErrorCodes.SkillDownloadFailed]: {\n group: 'skill',\n retry: 'yes',\n description: 'The skill download or extraction failed.',\n },\n [ErrorCodes.AgentAbort]: {\n group: 'agent',\n retry: 'case-by-case',\n description: 'The agent emitted an [ABORT] signal with a reason.',\n },\n [ErrorCodes.AgenticDetectionTimeout]: {\n group: 'agent',\n retry: 'yes',\n description: 'The agent run exceeded its configured time limit.',\n },\n [ErrorCodes.AgentMcpMissing]: {\n group: 'agent',\n retry: 'yes',\n description: 'The agent could not reach the PostHog MCP server.',\n },\n [ErrorCodes.AgentResourceMissing]: {\n group: 'agent',\n retry: 'yes',\n description: 'The agent could not access a setup resource.',\n },\n [ErrorCodes.AgentRateLimit]: {\n group: 'agent',\n retry: 'yes',\n description: 'The LLM gateway rate-limited the run.',\n },\n [ErrorCodes.AgentApiError]: {\n group: 'agent',\n retry: 'yes',\n description: 'The agent hit an API error other than a rate limit.',\n },\n [ErrorCodes.AgentYaraViolation]: {\n group: 'agent',\n retry: 'no',\n description: 'The security scanner terminated the run.',\n },\n [ErrorCodes.AgentNoProgress]: {\n group: 'agent',\n retry: 'case-by-case',\n description: 'The agent ended without a single tool call.',\n },\n [ErrorCodes.AgentIncompleteTasks]: {\n group: 'agent',\n retry: 'case-by-case',\n description: 'The agent stopped with planned tasks still open.',\n },\n [ErrorCodes.AgentOrchestratorSkillVariantMissing]: {\n group: 'agent',\n retry: 'yes',\n description: 'The orchestrator could not download a needed skill variant.',\n },\n [ErrorCodes.AgentOrchestratorTasksFailed]: {\n group: 'agent',\n retry: 'case-by-case',\n description: 'The orchestrator queue drained with failed or blocked tasks.',\n },\n [ErrorCodes.AgentOrchestratorHollowRun]: {\n group: 'agent',\n retry: 'yes',\n description:\n 'The orchestrator drained with zero tasks — the seed step got no usable model output.',\n },\n [ErrorCodes.AgentOrchestratorSinkInvariant]: {\n group: 'agent',\n retry: 'no',\n description: 'The orchestrator plan failed the sink coverage invariant.',\n },\n [ErrorCodes.GatewayMintRefused]: {\n group: 'gateway',\n // 429 clears with the daily window, 401 with a fresh login, 403 never.\n retry: 'case-by-case',\n description:\n 'The PostHog backend refused to mint a gateway token for this run.',\n },\n [ErrorCodes.GatewayMintFailed]: {\n group: 'gateway',\n retry: 'yes',\n description:\n 'The PostHog backend could not mint a gateway token: unreachable, a 5xx, or an unusable response.',\n },\n [ErrorCodes.SettingsUnfixableConflict]: {\n group: 'settings',\n retry: 'no',\n description:\n 'A Claude settings conflict cannot be neutralized automatically.',\n },\n [ErrorCodes.InternalUnhandled]: {\n group: 'internal',\n retry: 'yes',\n description: 'An unexpected error escaped the pipeline.',\n },\n};\n","import type { ErrorCode } from './codes';\n\n/**\n * Structured error data for analytics and the machine-readable error line.\n *\n * A data carrier: the agent returns it inside a failure and the legacy adapter\n * hands it to `wizardAbort()`, which captures it. Never thrown by the wizard.\n */\nexport class WizardError extends Error {\n readonly code?: ErrorCode;\n\n constructor(\n message: string,\n public readonly context?: Record<string, unknown>,\n code?: ErrorCode,\n ) {\n super(message);\n this.name = 'WizardError';\n this.code = code;\n }\n}\n","import { ErrorCodes, type ErrorCode } from './codes';\n\nexport interface AuthFailureInput {\n hasSettingsConflict?: boolean;\n usingManagedLogin?: boolean;\n /** A pre-run token refresh already failed on a dead grant this run. */\n sessionExpired?: boolean;\n apiKey?: string;\n missingScopes?: readonly string[];\n gatewayRegion?: 'us' | 'eu' | 'local';\n sessionRegion?: 'us' | 'eu';\n}\n\nexport function classifyAuthFailure(input: AuthFailureInput): ErrorCode {\n // First: the only branch backed by a server verdict rather than inference.\n // The token endpoint already told us the grant is dead, so every heuristic\n // below would be guessing at a cause we know.\n if (input.sessionExpired) return ErrorCodes.AuthSessionExpired;\n if (input.usingManagedLogin) return ErrorCodes.AuthStoredLoginConflict;\n if (input.hasSettingsConflict) return ErrorCodes.AuthSettingsConflict;\n if (\n input.apiKey &&\n !input.apiKey.startsWith('phx_') &&\n !input.apiKey.startsWith('pha_')\n ) {\n return ErrorCodes.AuthKeyType;\n }\n if (input.missingScopes && input.missingScopes.length > 0) {\n return ErrorCodes.AuthMissingScope;\n }\n if (\n input.gatewayRegion &&\n input.sessionRegion &&\n input.gatewayRegion !== 'local' &&\n input.gatewayRegion !== input.sessionRegion\n ) {\n return ErrorCodes.AuthRegionMismatch;\n }\n return ErrorCodes.AuthInvalidOrExpired;\n}\n","/**\n * Agent signal vocabulary — the marker strings the agent emits and the error\n * taxonomy the runner returns. Kept as a dependency-free leaf module so both\n * `agent-interface.ts` and `output-signals.ts` can import it without a cycle.\n */\n\nexport const AgentSignals = {\n /** Signal emitted when the agent reports progress to the user */\n STATUS: '[STATUS]',\n /** Signal emitted when the agent cannot access the PostHog MCP server */\n ERROR_MCP_MISSING: '[ERROR-MCP-MISSING]',\n /** Signal emitted when the agent cannot access the setup resource */\n ERROR_RESOURCE_MISSING: '[ERROR-RESOURCE-MISSING]',\n /**\n * Signal emitted when `install_skill` failed and the agent is continuing\n * the integration WITHOUT the skill (best-effort from its own knowledge).\n * Format: \"[SKILL-INSTALL-FAILED] <skill id — reason>\". Non-fatal by\n * design: a freestyled integration beats an outright failure, but the run\n * must be measurable — the runner captures it as an analytics event.\n */\n SKILL_INSTALL_FAILED: '[SKILL-INSTALL-FAILED]',\n /**\n * Signal emitted when the agent cannot complete the program and is\n * aborting intentionally (distinct from errors). Format: \"[ABORT] <reason>\".\n * Programs can declare an onAbort handler to render a custom screen.\n */\n ABORT: '[ABORT]',\n /** Signal emitted when the agent provides a remark about its run */\n WIZARD_REMARK: '[WIZARD-REMARK]',\n /** Signal prefix for benchmark logging */\n BENCHMARK: '[BENCHMARK]',\n /**\n * Signal emitted when the agent has created a PostHog dashboard for the\n * user. Format: `[DASHBOARD_URL] <full https url>`. The URL is captured\n * onto `session.dashboardUrl` and surfaced by programs in their outro.\n */\n DASHBOARD_URL: '[DASHBOARD_URL]',\n /**\n * Signal emitted when the agent has uploaded a report to a PostHog\n * notebook. Format: `[NOTEBOOK_URL] <full https url>`. The URL is captured\n * onto `session.notebookUrl` and surfaced by programs in their outro.\n */\n NOTEBOOK_URL: '[NOTEBOOK_URL]',\n} as const;\n\nexport type AgentSignal = (typeof AgentSignals)[keyof typeof AgentSignals];\n\n/**\n * End-of-run reflection ask, shared by both harnesses so remarks are\n * comparable. The format clause comes FIRST and nothing trails the marker —\n * literal models (gpt-5-mini) echo whatever text follows it. The parser also\n * drops remarks that quote this instruction (see AgentOutputSignals.remark).\n */\nexport const REMARK_INSTRUCTION = `Reply with a single line that starts with ${AgentSignals.WIZARD_REMARK} and no other lines. In that line, state briefly what information or guidance would have been useful to have in the integration prompt or documentation for this run — specifically anything that would have prevented tool failures, erroneous edits, or other wasted turns.`;\n\n/**\n * First prompt of a session resumed after a mid-run re-mint. The transcript\n * carries the work so far; the model only needs to pick it up.\n */\nexport const RESUME_INSTRUCTION =\n 'Your previous request failed with a transient gateway authentication error that has since been fixed. Continue the task from where you left off.';\n\n/**\n * Error types that can be returned from agent execution.\n * These correspond to the error signals that the agent emits.\n */\nexport enum AgentErrorType {\n /** The configured run deadline elapsed and the SDK query was aborted */\n AGENTIC_DETECTION_TIMEOUT = 'WIZARD_AGENTIC_DETECTION_TIMEOUT',\n /** Agent could not access the PostHog MCP server */\n MCP_MISSING = 'WIZARD_MCP_MISSING',\n /** Agent could not access the setup resource */\n RESOURCE_MISSING = 'WIZARD_RESOURCE_MISSING',\n /** API rate limit exceeded */\n RATE_LIMIT = 'WIZARD_RATE_LIMIT',\n /** Generic API error */\n API_ERROR = 'WIZARD_API_ERROR',\n /** YARA scanner detected a security violation */\n YARA_VIOLATION = 'WIZARD_YARA_VIOLATION',\n /** Agent intentionally aborted the program (emitted [ABORT] <reason>) */\n ABORT = 'WIZARD_ABORT',\n /** Agent ended without making a single tool call — a no-op run that did no work */\n NO_PROGRESS = 'WIZARD_NO_PROGRESS',\n /** Agent acted but stopped short — planned tasks left open and/or no skill installed */\n INCOMPLETE_TASKS = 'WIZARD_INCOMPLETE_TASKS',\n}\n","// Leaf module on purpose: every module imports the errors, so the agent entry\n// would form a module cycle here.\n// eslint-disable-next-line @typescript-eslint/no-restricted-imports -- B1: error codes move next to their owners\nimport { AgentErrorType } from '@agent/signals';\nimport { ErrorCodes, type ErrorCode } from './codes';\n\nexport const AGENT_ERROR_CODE: Record<AgentErrorType, ErrorCode> = {\n [AgentErrorType.AGENTIC_DETECTION_TIMEOUT]:\n ErrorCodes.AgenticDetectionTimeout,\n [AgentErrorType.MCP_MISSING]: ErrorCodes.AgentMcpMissing,\n [AgentErrorType.RESOURCE_MISSING]: ErrorCodes.AgentResourceMissing,\n [AgentErrorType.RATE_LIMIT]: ErrorCodes.AgentRateLimit,\n [AgentErrorType.API_ERROR]: ErrorCodes.AgentApiError,\n [AgentErrorType.YARA_VIOLATION]: ErrorCodes.AgentYaraViolation,\n [AgentErrorType.ABORT]: ErrorCodes.AgentAbort,\n [AgentErrorType.NO_PROGRESS]: ErrorCodes.AgentNoProgress,\n [AgentErrorType.INCOMPLETE_TASKS]: ErrorCodes.AgentIncompleteTasks,\n};\n","import { ErrorCodes, type ErrorCode } from './codes';\nimport type { InstallSkillResult } from '@agent/types';\n\nconst SKILL_CODES: Record<\n Exclude<InstallSkillResult['kind'], 'ok'>,\n ErrorCode\n> = {\n 'menu-fetch-failed': ErrorCodes.SkillMenuFetchFailed,\n 'skill-not-found': ErrorCodes.SkillNotFound,\n 'download-failed': ErrorCodes.SkillDownloadFailed,\n};\n\nexport function skillErrorCode(result: InstallSkillResult): ErrorCode | null {\n if (result.kind === 'ok') return null;\n return SKILL_CODES[result.kind];\n}\n","const DETAIL_ALLOWLIST = ['reason', 'detected', 'platform'] as const;\n\nexport function sanitizeErrorDetail(\n detail: Record<string, unknown> | undefined,\n): Record<string, unknown> | undefined {\n if (!detail) return undefined;\n const safe: Record<string, unknown> = {};\n for (const key of DETAIL_ALLOWLIST) {\n if (detail[key] !== undefined) safe[key] = detail[key];\n }\n return Object.keys(safe).length > 0 ? safe : undefined;\n}\n","import { ErrorCodes, isErrorCode, type ErrorCode } from './codes';\n\nexport interface RunFailure {\n code: ErrorCode;\n message: string;\n /** True when the error carried its own code: a decision, not a crash. */\n coded: boolean;\n}\n\n/**\n * How a run's terminal error is reported. A coded WizardError (a mint refusal,\n * say) keeps its own message and code; anything else is unhandled and gets the\n * generic framing. Duck-typed on `code` so callers need not load wizard-abort.\n */\nexport function classifyRunFailure(err: unknown): RunFailure {\n const message = err instanceof Error ? err.message : String(err);\n const code =\n err instanceof Error ? (err as { code?: unknown }).code : undefined;\n if (typeof code === 'string' && isErrorCode(code)) {\n return { code, message, coded: true };\n }\n return { code: ErrorCodes.InternalUnhandled, message, coded: false };\n}\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { s as logToFile, u as WIZARD_TASK_STREAM_FILE } from "./debug-DWYDVp0B.js";
1
+ import { d as WIZARD_TASK_STREAM_FILE, s as logToFile } from "./debug-Ch4BDOtD.js";
2
2
  import { dirname, isAbsolute, resolve } from "node:path";
3
3
  import { appendFileSync, mkdirSync, writeFileSync } from "node:fs";
4
4
  //#region src/lib/task-stream/destinations/file.ts
@@ -57,4 +57,4 @@ function createFileDestination(value, opts = {}) {
57
57
  //#endregion
58
58
  export { FileDestination, createFileDestination };
59
59
 
60
- //# sourceMappingURL=file-C3HGL1IM.js.map
60
+ //# sourceMappingURL=file-BcLTjU16.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"file-C3HGL1IM.js","names":[],"sources":["../src/lib/task-stream/destinations/file.ts"],"sourcesContent":["/**\n * Dumps every *attempted* sync to a local JSONL file, one line per push. A line\n * means the run published that payload, not that a backend accepted it — `--ci`\n * swaps the PostHog destination for this one, so a synthetic run never creates a\n * session row in a real project.\n *\n * Dev builds only: the payload holds the run's handoff report.\n */\n\nimport { appendFileSync, mkdirSync, writeFileSync } from 'node:fs';\nimport { dirname, isAbsolute, resolve } from 'node:path';\nimport { IS_PRODUCTION_BUILD } from '@env';\nimport { WIZARD_TASK_STREAM_FILE } from '@utils/paths';\nimport { logToFile } from '@utils/debug';\nimport type {\n StreamEvent,\n TaskStreamDestination,\n TaskStreamUpdate,\n} from '@lib/task-stream/types';\n\nexport interface FileDestinationOptions {\n path: string;\n /** Override for tests. */\n now?: () => string;\n}\n\nexport class FileDestination implements TaskStreamDestination {\n readonly name = 'file';\n readonly path: string;\n\n private readonly now: () => string;\n private disabled = false;\n\n constructor(opts: FileDestinationOptions) {\n this.path = opts.path;\n this.now = opts.now ?? (() => new Date().toISOString());\n // Truncate up front: one run, one file.\n this.write(() => writeFileSync(this.path, '', { mode: 0o600 }));\n }\n\n send(event: StreamEvent, payload: TaskStreamUpdate): Promise<void> {\n if (!this.disabled) {\n const line = `${JSON.stringify({ at: this.now(), event, payload })}\\n`;\n this.write(() => appendFileSync(this.path, line, { mode: 0o600 }));\n }\n return Promise.resolve();\n }\n\n /** Never throws, never writes to stdio; the first failure ends the dump. */\n private write(op: () => void): void {\n try {\n mkdirSync(dirname(this.path), { recursive: true });\n op();\n } catch (err) {\n this.disabled = true;\n logToFile(\n `[task-stream] file destination disabled (${\n err instanceof Error ? err.message : String(err)\n }): ${this.path}`,\n );\n }\n }\n}\n\n/**\n * Resolve a `--task-stream-log` value, or null when the run did not ask for a\n * dump. `''` and `true` mean the default path — how `--ci` asks for one.\n */\nexport function createFileDestination(\n value: unknown,\n opts: { productionBuild?: boolean; cwd?: string } = {},\n): FileDestination | null {\n if (opts.productionBuild ?? IS_PRODUCTION_BUILD) return null;\n if (value === undefined || value === null || value === false) return null;\n\n const raw = typeof value === 'string' ? value.trim() : '';\n const path =\n raw === ''\n ? WIZARD_TASK_STREAM_FILE\n : isAbsolute(raw)\n ? raw\n : resolve(opts.cwd ?? process.cwd(), raw);\n\n logToFile(`[task-stream] file destination enabled: ${path}`);\n return new FileDestination({ path });\n}\n"],"mappings":";;;;;;;;;;;;AA0BA,IAAa,kBAAb,MAA8D;CAC5D,OAAgB;CAChB;CAEA;CACA,WAAmB;CAEnB,YAAY,MAA8B;AACxC,OAAK,OAAO,KAAK;AACjB,OAAK,MAAM,KAAK,+BAAc,IAAI,MAAM,EAAC,aAAa;AAEtD,OAAK,YAAY,cAAc,KAAK,MAAM,IAAI,EAAE,MAAM,KAAO,CAAC,CAAC;;CAGjE,KAAK,OAAoB,SAA0C;AACjE,MAAI,CAAC,KAAK,UAAU;GAClB,MAAM,OAAO,GAAG,KAAK,UAAU;IAAE,IAAI,KAAK,KAAK;IAAE;IAAO;IAAS,CAAC,CAAC;AACnE,QAAK,YAAY,eAAe,KAAK,MAAM,MAAM,EAAE,MAAM,KAAO,CAAC,CAAC;;AAEpE,SAAO,QAAQ,SAAS;;;CAI1B,MAAc,IAAsB;AAClC,MAAI;AACF,aAAU,QAAQ,KAAK,KAAK,EAAE,EAAE,WAAW,MAAM,CAAC;AAClD,OAAI;WACG,KAAK;AACZ,QAAK,WAAW;AAChB,aACE,4CACE,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI,CACjD,KAAK,KAAK,OACZ;;;;;;;;AASP,SAAgB,sBACd,OACA,OAAoD,EAAE,EAC9B;AACxB,KAAI,KAAK,mBAAA,KAAwC,QAAO;AACxD,KAAI,UAAU,KAAA,KAAa,UAAU,QAAQ,UAAU,MAAO,QAAO;CAErE,MAAM,MAAM,OAAO,UAAU,WAAW,MAAM,MAAM,GAAG;CACvD,MAAM,OACJ,QAAQ,KACJ,0BACA,WAAW,IAAI,GACf,MACA,QAAQ,KAAK,OAAO,QAAQ,KAAK,EAAE,IAAI;AAE7C,WAAU,2CAA2C,OAAO;AAC5D,QAAO,IAAI,gBAAgB,EAAE,MAAM,CAAC"}
1
+ {"version":3,"file":"file-BcLTjU16.js","names":[],"sources":["../src/lib/task-stream/destinations/file.ts"],"sourcesContent":["/**\n * Dumps every *attempted* sync to a local JSONL file, one line per push. A line\n * means the run published that payload, not that a backend accepted it — `--ci`\n * swaps the PostHog destination for this one, so a synthetic run never creates a\n * session row in a real project.\n *\n * Dev builds only: the payload holds the run's handoff report.\n */\n\nimport { appendFileSync, mkdirSync, writeFileSync } from 'node:fs';\nimport { dirname, isAbsolute, resolve } from 'node:path';\nimport { IS_PRODUCTION_BUILD } from '@env';\nimport { WIZARD_TASK_STREAM_FILE } from '@utils/paths';\nimport { logToFile } from '@utils/debug';\nimport type {\n StreamEvent,\n TaskStreamDestination,\n TaskStreamUpdate,\n} from '@lib/task-stream/types';\n\nexport interface FileDestinationOptions {\n path: string;\n /** Override for tests. */\n now?: () => string;\n}\n\nexport class FileDestination implements TaskStreamDestination {\n readonly name = 'file';\n readonly path: string;\n\n private readonly now: () => string;\n private disabled = false;\n\n constructor(opts: FileDestinationOptions) {\n this.path = opts.path;\n this.now = opts.now ?? (() => new Date().toISOString());\n // Truncate up front: one run, one file.\n this.write(() => writeFileSync(this.path, '', { mode: 0o600 }));\n }\n\n send(event: StreamEvent, payload: TaskStreamUpdate): Promise<void> {\n if (!this.disabled) {\n const line = `${JSON.stringify({ at: this.now(), event, payload })}\\n`;\n this.write(() => appendFileSync(this.path, line, { mode: 0o600 }));\n }\n return Promise.resolve();\n }\n\n /** Never throws, never writes to stdio; the first failure ends the dump. */\n private write(op: () => void): void {\n try {\n mkdirSync(dirname(this.path), { recursive: true });\n op();\n } catch (err) {\n this.disabled = true;\n logToFile(\n `[task-stream] file destination disabled (${\n err instanceof Error ? err.message : String(err)\n }): ${this.path}`,\n );\n }\n }\n}\n\n/**\n * Resolve a `--task-stream-log` value, or null when the run did not ask for a\n * dump. `''` and `true` mean the default path — how `--ci` asks for one.\n */\nexport function createFileDestination(\n value: unknown,\n opts: { productionBuild?: boolean; cwd?: string } = {},\n): FileDestination | null {\n if (opts.productionBuild ?? IS_PRODUCTION_BUILD) return null;\n if (value === undefined || value === null || value === false) return null;\n\n const raw = typeof value === 'string' ? value.trim() : '';\n const path =\n raw === ''\n ? WIZARD_TASK_STREAM_FILE\n : isAbsolute(raw)\n ? raw\n : resolve(opts.cwd ?? process.cwd(), raw);\n\n logToFile(`[task-stream] file destination enabled: ${path}`);\n return new FileDestination({ path });\n}\n"],"mappings":";;;;;;;;;;;;AA0BA,IAAa,kBAAb,MAA8D;CAC5D,OAAgB;CAChB;CAEA;CACA,WAAmB;CAEnB,YAAY,MAA8B;AACxC,OAAK,OAAO,KAAK;AACjB,OAAK,MAAM,KAAK,+BAAc,IAAI,MAAM,EAAC,aAAa;AAEtD,OAAK,YAAY,cAAc,KAAK,MAAM,IAAI,EAAE,MAAM,KAAO,CAAC,CAAC;;CAGjE,KAAK,OAAoB,SAA0C;AACjE,MAAI,CAAC,KAAK,UAAU;GAClB,MAAM,OAAO,GAAG,KAAK,UAAU;IAAE,IAAI,KAAK,KAAK;IAAE;IAAO;IAAS,CAAC,CAAC;AACnE,QAAK,YAAY,eAAe,KAAK,MAAM,MAAM,EAAE,MAAM,KAAO,CAAC,CAAC;;AAEpE,SAAO,QAAQ,SAAS;;;CAI1B,MAAc,IAAsB;AAClC,MAAI;AACF,aAAU,QAAQ,KAAK,KAAK,EAAE,EAAE,WAAW,MAAM,CAAC;AAClD,OAAI;WACG,KAAK;AACZ,QAAK,WAAW;AAChB,aACE,4CACE,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI,CACjD,KAAK,KAAK,OACZ;;;;;;;;AASP,SAAgB,sBACd,OACA,OAAoD,EAAE,EAC9B;AACxB,KAAI,KAAK,mBAAA,KAAwC,QAAO;AACxD,KAAI,UAAU,KAAA,KAAa,UAAU,QAAQ,UAAU,MAAO,QAAO;CAErE,MAAM,MAAM,OAAO,UAAU,WAAW,MAAM,MAAM,GAAG;CACvD,MAAM,OACJ,QAAQ,KACJ,0BACA,WAAW,IAAI,GACf,MACA,QAAQ,KAAK,OAAO,QAAQ,KAAK,EAAE,IAAI;AAE7C,WAAU,2CAA2C,OAAO;AAC5D,QAAO,IAAI,gBAAgB,EAAE,MAAM,CAAC"}
@@ -1,4 +1,4 @@
1
- import { s as logToFile } from "./debug-DWYDVp0B.js";
1
+ import { s as logToFile } from "./debug-Ch4BDOtD.js";
2
2
  import * as fs$1 from "fs";
3
3
  import { basename, dirname } from "node:path";
4
4
  //#region src/lib/file-watcher.ts
@@ -120,4 +120,4 @@ function startFileWatcher(path, onUpdate, options = {}) {
120
120
  //#endregion
121
121
  export { startFileWatcher as t };
122
122
 
123
- //# sourceMappingURL=file-watcher-D8s0b-VT.js.map
123
+ //# sourceMappingURL=file-watcher-DWc_bTs6.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"file-watcher-D8s0b-VT.js","names":["fs"],"sources":["../src/lib/file-watcher.ts"],"sourcesContent":["/**\n * JSON file watcher shared by runner and UI machinery.\n *\n * `fs.watch` alone is unreliable for atomic-rename writes, so the watcher\n * pairs it with a continuous mtime-polled re-read. The poll catches missed\n * events; the watch keeps latency low when it does fire.\n */\n\nimport * as fs from 'fs';\nimport { basename, dirname } from 'node:path';\nimport { logToFile } from '@utils/debug';\n\nconst DEFAULT_POLL_INTERVAL_MS = 5000;\nconst DEFAULT_ATTACH_RETRY_INTERVAL_MS = 1000;\nconst DEFAULT_WATCH_DEBOUNCE_MS = 25;\n\nexport interface FileWatcherHandle {\n refresh(): void;\n stop(): void;\n}\n\nexport interface FileWatcherOptions {\n /** ms between mtime checks once the file exists. */\n pollIntervalMs?: number;\n /** ms between attach attempts while waiting for the file to appear. */\n attachRetryIntervalMs?: number;\n /** ms to coalesce duplicate filesystem events. */\n watchDebounceMs?: number;\n /** Ignore the file that exists when the watcher starts until it changes. */\n ignoreInitialFile?: boolean;\n /** Refuse to read files larger than this many bytes. */\n maxFileSizeBytes?: number;\n}\n\n/** Watch `path` for JSON updates and call `onUpdate(parsed)` whenever the\n * file's mtime changes and the contents are valid JSON. Caller must invoke\n * `handle.stop()` to release the watcher. */\nexport function startFileWatcher(\n path: string,\n onUpdate: (parsed: unknown) => void,\n options: FileWatcherOptions = {},\n): FileWatcherHandle {\n const pollIntervalMs = options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;\n const attachRetryIntervalMs =\n options.attachRetryIntervalMs ?? DEFAULT_ATTACH_RETRY_INTERVAL_MS;\n const watchDebounceMs = options.watchDebounceMs ?? DEFAULT_WATCH_DEBOUNCE_MS;\n\n const watchers: fs.FSWatcher[] = [];\n const intervals: Array<ReturnType<typeof setInterval>> = [];\n const targetDir = dirname(path);\n const targetName = basename(path);\n let lastMtimeMs = 0;\n let ignoredInitialSignature: string | null = null;\n let watchDebounceTimer: ReturnType<typeof setTimeout> | null = null;\n let lastReadErrorSignature: string | null = null;\n let stopped = false;\n\n const signature = (stat: fs.Stats): string =>\n `${stat.dev}:${stat.ino}:${stat.size}:${stat.mtimeMs}:${stat.ctimeMs}`;\n\n if (options.ignoreInitialFile) {\n try {\n const stat = fs.lstatSync(path);\n if (stat.isFile() && !stat.isSymbolicLink()) {\n ignoredInitialSignature = signature(stat);\n }\n } catch {\n // No initial file to ignore.\n }\n }\n\n const logReadError = (errorSignature: string, message: string) => {\n if (lastReadErrorSignature === errorSignature) return;\n lastReadErrorSignature = errorSignature;\n logToFile(`[file-watcher] ${message}: ${path}`);\n };\n\n const read = (force = false) => {\n let stat: fs.Stats;\n try {\n stat = fs.lstatSync(path);\n } catch {\n return;\n }\n\n if (!stat.isFile() || stat.isSymbolicLink()) return;\n const fileSignature = signature(stat);\n\n try {\n if (ignoredInitialSignature) {\n if (fileSignature === ignoredInitialSignature) return;\n ignoredInitialSignature = null;\n }\n if (\n options.maxFileSizeBytes !== undefined &&\n stat.size > options.maxFileSizeBytes\n ) {\n logReadError(\n `oversized:${fileSignature}`,\n `refusing oversized JSON file (${stat.size} bytes, limit ${options.maxFileSizeBytes} bytes)`,\n );\n return;\n }\n if (!force && stat.mtimeMs === lastMtimeMs) return;\n lastMtimeMs = stat.mtimeMs;\n const parsed: unknown = JSON.parse(fs.readFileSync(path, 'utf-8'));\n lastReadErrorSignature = null;\n onUpdate(parsed);\n } catch (error) {\n logReadError(\n `invalid:${fileSignature}:${String(error)}`,\n `could not read valid JSON (${String(error)})`,\n );\n }\n };\n\n const scheduleRead = () => {\n if (stopped) return;\n if (watchDebounceTimer) clearTimeout(watchDebounceTimer);\n watchDebounceTimer = setTimeout(() => {\n watchDebounceTimer = null;\n read(true);\n }, watchDebounceMs);\n };\n\n const attachWatch = () => {\n watchers.push(\n fs.watch(targetDir, (_eventType, filename) => {\n if (filename == null || filename.toString() === targetName) {\n scheduleRead();\n }\n }),\n );\n };\n\n intervals.push(setInterval(() => read(), pollIntervalMs));\n\n try {\n attachWatch();\n // Defer the initial callback until the caller has received the handle, so\n // callbacks that stop their watcher cannot race handle assignment.\n queueMicrotask(() => {\n if (!stopped) read(true);\n });\n } catch {\n // Parent directory does not exist yet. Polling still covers a later file;\n // retry attaching the low-latency directory watcher until it appears.\n const attachInterval = setInterval(() => {\n try {\n fs.accessSync(targetDir);\n clearInterval(attachInterval);\n const idx = intervals.indexOf(attachInterval);\n if (idx >= 0) intervals.splice(idx, 1);\n attachWatch();\n read(true);\n } catch {\n // Still waiting.\n }\n }, attachRetryIntervalMs);\n intervals.push(attachInterval);\n }\n\n return {\n refresh() {\n if (stopped) return;\n if (watchDebounceTimer) {\n clearTimeout(watchDebounceTimer);\n watchDebounceTimer = null;\n }\n read(true);\n },\n stop() {\n stopped = true;\n if (watchDebounceTimer) {\n clearTimeout(watchDebounceTimer);\n watchDebounceTimer = null;\n }\n for (const watcher of watchers) watcher.close();\n for (const interval of intervals) clearInterval(interval);\n },\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"file-watcher-DWc_bTs6.js","names":["fs"],"sources":["../src/lib/file-watcher.ts"],"sourcesContent":["/**\n * JSON file watcher shared by runner and UI machinery.\n *\n * `fs.watch` alone is unreliable for atomic-rename writes, so the watcher\n * pairs it with a continuous mtime-polled re-read. The poll catches missed\n * events; the watch keeps latency low when it does fire.\n */\n\nimport * as fs from 'fs';\nimport { basename, dirname } from 'node:path';\nimport { logToFile } from '@utils/debug';\n\nconst DEFAULT_POLL_INTERVAL_MS = 5000;\nconst DEFAULT_ATTACH_RETRY_INTERVAL_MS = 1000;\nconst DEFAULT_WATCH_DEBOUNCE_MS = 25;\n\nexport interface FileWatcherHandle {\n refresh(): void;\n stop(): void;\n}\n\nexport interface FileWatcherOptions {\n /** ms between mtime checks once the file exists. */\n pollIntervalMs?: number;\n /** ms between attach attempts while waiting for the file to appear. */\n attachRetryIntervalMs?: number;\n /** ms to coalesce duplicate filesystem events. */\n watchDebounceMs?: number;\n /** Ignore the file that exists when the watcher starts until it changes. */\n ignoreInitialFile?: boolean;\n /** Refuse to read files larger than this many bytes. */\n maxFileSizeBytes?: number;\n}\n\n/** Watch `path` for JSON updates and call `onUpdate(parsed)` whenever the\n * file's mtime changes and the contents are valid JSON. Caller must invoke\n * `handle.stop()` to release the watcher. */\nexport function startFileWatcher(\n path: string,\n onUpdate: (parsed: unknown) => void,\n options: FileWatcherOptions = {},\n): FileWatcherHandle {\n const pollIntervalMs = options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;\n const attachRetryIntervalMs =\n options.attachRetryIntervalMs ?? DEFAULT_ATTACH_RETRY_INTERVAL_MS;\n const watchDebounceMs = options.watchDebounceMs ?? DEFAULT_WATCH_DEBOUNCE_MS;\n\n const watchers: fs.FSWatcher[] = [];\n const intervals: Array<ReturnType<typeof setInterval>> = [];\n const targetDir = dirname(path);\n const targetName = basename(path);\n let lastMtimeMs = 0;\n let ignoredInitialSignature: string | null = null;\n let watchDebounceTimer: ReturnType<typeof setTimeout> | null = null;\n let lastReadErrorSignature: string | null = null;\n let stopped = false;\n\n const signature = (stat: fs.Stats): string =>\n `${stat.dev}:${stat.ino}:${stat.size}:${stat.mtimeMs}:${stat.ctimeMs}`;\n\n if (options.ignoreInitialFile) {\n try {\n const stat = fs.lstatSync(path);\n if (stat.isFile() && !stat.isSymbolicLink()) {\n ignoredInitialSignature = signature(stat);\n }\n } catch {\n // No initial file to ignore.\n }\n }\n\n const logReadError = (errorSignature: string, message: string) => {\n if (lastReadErrorSignature === errorSignature) return;\n lastReadErrorSignature = errorSignature;\n logToFile(`[file-watcher] ${message}: ${path}`);\n };\n\n const read = (force = false) => {\n let stat: fs.Stats;\n try {\n stat = fs.lstatSync(path);\n } catch {\n return;\n }\n\n if (!stat.isFile() || stat.isSymbolicLink()) return;\n const fileSignature = signature(stat);\n\n try {\n if (ignoredInitialSignature) {\n if (fileSignature === ignoredInitialSignature) return;\n ignoredInitialSignature = null;\n }\n if (\n options.maxFileSizeBytes !== undefined &&\n stat.size > options.maxFileSizeBytes\n ) {\n logReadError(\n `oversized:${fileSignature}`,\n `refusing oversized JSON file (${stat.size} bytes, limit ${options.maxFileSizeBytes} bytes)`,\n );\n return;\n }\n if (!force && stat.mtimeMs === lastMtimeMs) return;\n lastMtimeMs = stat.mtimeMs;\n const parsed: unknown = JSON.parse(fs.readFileSync(path, 'utf-8'));\n lastReadErrorSignature = null;\n onUpdate(parsed);\n } catch (error) {\n logReadError(\n `invalid:${fileSignature}:${String(error)}`,\n `could not read valid JSON (${String(error)})`,\n );\n }\n };\n\n const scheduleRead = () => {\n if (stopped) return;\n if (watchDebounceTimer) clearTimeout(watchDebounceTimer);\n watchDebounceTimer = setTimeout(() => {\n watchDebounceTimer = null;\n read(true);\n }, watchDebounceMs);\n };\n\n const attachWatch = () => {\n watchers.push(\n fs.watch(targetDir, (_eventType, filename) => {\n if (filename == null || filename.toString() === targetName) {\n scheduleRead();\n }\n }),\n );\n };\n\n intervals.push(setInterval(() => read(), pollIntervalMs));\n\n try {\n attachWatch();\n // Defer the initial callback until the caller has received the handle, so\n // callbacks that stop their watcher cannot race handle assignment.\n queueMicrotask(() => {\n if (!stopped) read(true);\n });\n } catch {\n // Parent directory does not exist yet. Polling still covers a later file;\n // retry attaching the low-latency directory watcher until it appears.\n const attachInterval = setInterval(() => {\n try {\n fs.accessSync(targetDir);\n clearInterval(attachInterval);\n const idx = intervals.indexOf(attachInterval);\n if (idx >= 0) intervals.splice(idx, 1);\n attachWatch();\n read(true);\n } catch {\n // Still waiting.\n }\n }, attachRetryIntervalMs);\n intervals.push(attachInterval);\n }\n\n return {\n refresh() {\n if (stopped) return;\n if (watchDebounceTimer) {\n clearTimeout(watchDebounceTimer);\n watchDebounceTimer = null;\n }\n read(true);\n },\n stop() {\n stopped = true;\n if (watchDebounceTimer) {\n clearTimeout(watchDebounceTimer);\n watchDebounceTimer = null;\n }\n for (const watcher of watchers) watcher.close();\n for (const interval of intervals) clearInterval(interval);\n },\n };\n}\n"],"mappings":"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"}