@posthog/wizard 2.81.0 → 2.82.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 (120) hide show
  1. package/dist/{agent-interface-BFzBnz10.js → agent-interface-3YfIWZmY.js} +211 -46
  2. package/dist/agent-interface-3YfIWZmY.js.map +1 -0
  3. package/dist/{agent-prompt-loader-CbCW6YbP.js → agent-prompt-loader-hOHONY7u.js} +6 -6
  4. package/dist/{agent-prompt-loader-CbCW6YbP.js.map → agent-prompt-loader-hOHONY7u.js.map} +1 -1
  5. package/dist/{analytics-CGGE7OSa.js → analytics-B_xFshPY.js} +4 -3
  6. package/dist/analytics-B_xFshPY.js.map +1 -0
  7. package/dist/analytics-DGDr6-iD.js +2 -0
  8. package/dist/{api-ZZhiRtbb.js → api-C_9KsN-7.js} +71 -3
  9. package/dist/{api-ZZhiRtbb.js.map → api-C_9KsN-7.js.map} +1 -1
  10. package/dist/api-DhLSO3ai.js +2 -0
  11. package/dist/api-key-login-DiRuZ8Lm.js +2 -0
  12. package/dist/{api-key-login-D0KBr1dx.js → api-key-login-oUJQDmOV.js} +5 -5
  13. package/dist/{api-key-login-D0KBr1dx.js.map → api-key-login-oUJQDmOV.js.map} +1 -1
  14. package/dist/bin.js +1 -1
  15. package/dist/{bounded-fs-DwL2Tasp.js → bounded-fs-iRaQcKYL.js} +6 -4
  16. package/dist/bounded-fs-iRaQcKYL.js.map +1 -0
  17. package/dist/{ci-install-DKXrt_ow.js → ci-install-BGlijoTo.js} +4 -4
  18. package/dist/{ci-install-DKXrt_ow.js.map → ci-install-BGlijoTo.js.map} +1 -1
  19. package/dist/{debug-sB20p-Ek.js → debug-BvEbemym.js} +3 -3
  20. package/dist/{debug-sB20p-Ek.js.map → debug-BvEbemym.js.map} +1 -1
  21. package/dist/{gateway-Dg4Fm-nC.js → gateway-CWoYlPz7.js} +5 -5
  22. package/dist/{gateway-Dg4Fm-nC.js.map → gateway-CWoYlPz7.js.map} +1 -1
  23. package/dist/{headless-DhTDC9ga.js → headless-Bfi950Qh.js} +2 -2
  24. package/dist/{headless-DhTDC9ga.js.map → headless-Bfi950Qh.js.map} +1 -1
  25. package/dist/{headless-B3Ol1nkU.js → headless-Gisv3faN.js} +1 -1
  26. package/dist/{host-resolution-DDYJXiAQ.js → host-resolution-BxnNn8Jm.js} +4 -4
  27. package/dist/{host-resolution-DDYJXiAQ.js.map → host-resolution-BxnNn8Jm.js.map} +1 -1
  28. package/dist/{install-Dh8Z5R15.js → install-CDVeUT_5.js} +5 -5
  29. package/dist/{install-Dh8Z5R15.js.map → install-CDVeUT_5.js.map} +1 -1
  30. package/dist/{install-CZeR38cD.js → install-CX65IxGa.js} +1 -1
  31. package/dist/{interactive-hqHk6kz4.js → interactive-5-Numwwb.js} +3 -3
  32. package/dist/{interactive-hqHk6kz4.js.map → interactive-5-Numwwb.js.map} +1 -1
  33. package/dist/{links-rMot83W2.js → links-Dmj3Wj9X.js} +3 -3
  34. package/dist/{links-rMot83W2.js.map → links-Dmj3Wj9X.js.map} +1 -1
  35. package/dist/{main-DNIeasXx.js → main-5KNn80Vt.js} +20 -20
  36. package/dist/{main-DNIeasXx.js.map → main-5KNn80Vt.js.map} +1 -1
  37. package/dist/{mcp-SZcGfaOz.js → mcp-Buf9sfFm.js} +3 -3
  38. package/dist/{mcp-SZcGfaOz.js.map → mcp-Buf9sfFm.js.map} +1 -1
  39. package/dist/{mcp-prompt-streaming-BuRMBgbo.js → mcp-prompt-streaming-DHPxJK-F.js} +5 -5
  40. package/dist/{mcp-prompt-streaming-BuRMBgbo.js.map → mcp-prompt-streaming-DHPxJK-F.js.map} +1 -1
  41. package/dist/{models-G1j-wgwe.js → models-C9rjXPZ6.js} +2 -2
  42. package/dist/{models-G1j-wgwe.js.map → models-C9rjXPZ6.js.map} +1 -1
  43. package/dist/{oauth-session-C2fVSGfr.js → oauth-session-Bi1MLRCC.js} +45 -3
  44. package/dist/oauth-session-Bi1MLRCC.js.map +1 -0
  45. package/dist/{orchestrator-tools-BchqG7Qi.js → orchestrator-tools-DyKws92o.js} +3 -3
  46. package/dist/{orchestrator-tools-BchqG7Qi.js.map → orchestrator-tools-DyKws92o.js.map} +1 -1
  47. package/dist/{package-manager-B3_qSybg.js → package-manager-DpCdmMmB.js} +4 -4
  48. package/dist/{package-manager-B3_qSybg.js.map → package-manager-DpCdmMmB.js.map} +1 -1
  49. package/dist/{pi-Cw7CPXSl.js → pi-DTj1hsJe.js} +32 -15
  50. package/dist/pi-DTj1hsJe.js.map +1 -0
  51. package/dist/{playground-CPNrQfb3.js → playground-CiH6zZPb.js} +4 -4
  52. package/dist/{playground-CPNrQfb3.js.map → playground-CiH6zZPb.js.map} +1 -1
  53. package/dist/{posthog-integration-FAWfClH3.js → posthog-integration-CpinLWNO.js} +78 -35
  54. package/dist/posthog-integration-CpinLWNO.js.map +1 -0
  55. package/dist/{programs-Ml6OLybj.js → programs-Bq9Ju9xk.js} +28 -23
  56. package/dist/programs-Bq9Ju9xk.js.map +1 -0
  57. package/dist/programs-CnOSdQo1.js +3 -0
  58. package/dist/{provisioning-BTVL49jD.js → provisioning-D-AM_7DG.js} +4 -4
  59. package/dist/{provisioning-BTVL49jD.js.map → provisioning-D-AM_7DG.js.map} +1 -1
  60. package/dist/provisioning-D6Cqbc36.js +2 -0
  61. package/dist/{queue-tools-BQfRI_uD.js → queue-tools-BoIITT7w.js} +4 -4
  62. package/dist/{queue-tools-BQfRI_uD.js.map → queue-tools-BoIITT7w.js.map} +1 -1
  63. package/dist/{results-ycm_Tf31.js → results-CDqlHogZ.js} +2 -2
  64. package/dist/{results-ycm_Tf31.js.map → results-CDqlHogZ.js.map} +1 -1
  65. package/dist/{run-C6Ei6vvb.js → run-CfMArkpV.js} +7 -7
  66. package/dist/{run-C6Ei6vvb.js.map → run-CfMArkpV.js.map} +1 -1
  67. package/dist/{run-Bgg-ctbA.js → run-DcaVJSeI.js} +10 -10
  68. package/dist/{run-Bgg-ctbA.js.map → run-DcaVJSeI.js.map} +1 -1
  69. package/dist/{run-tool-CrN2xWzU.js → run-tool-DJZWnnR9.js} +18 -18
  70. package/dist/{run-tool-CrN2xWzU.js.map → run-tool-DJZWnnR9.js.map} +1 -1
  71. package/dist/run-tool-zMLQroVh.js +2 -0
  72. package/dist/{runners-GGYJs0Ny.js → runners-Cvnk9BcA.js} +8 -8
  73. package/dist/{runners-GGYJs0Ny.js.map → runners-Cvnk9BcA.js.map} +1 -1
  74. package/dist/{security-C36p8lu6.js → security-Cz3a-d7r.js} +5 -5
  75. package/dist/{security-C36p8lu6.js.map → security-Cz3a-d7r.js.map} +1 -1
  76. package/dist/{setup-utils-Dfgse6pY.js → setup-utils-C7kaDP5X.js} +3 -3
  77. package/dist/{setup-utils-Dfgse6pY.js.map → setup-utils-C7kaDP5X.js.map} +1 -1
  78. package/dist/{start-playground-D0TrdLmc.js → start-playground-B62_pYfu.js} +13 -13
  79. package/dist/{start-playground-D0TrdLmc.js.map → start-playground-B62_pYfu.js.map} +1 -1
  80. package/dist/{subagent-DR5caRzL.js → subagent-BBfETG17.js} +3 -3
  81. package/dist/{subagent-DR5caRzL.js.map → subagent-BBfETG17.js.map} +1 -1
  82. package/dist/{task-C1qKgsAf.js → task-CmNe3xeg.js} +28 -16
  83. package/dist/task-CmNe3xeg.js.map +1 -0
  84. package/dist/{telemetry-Ch9ZJvm7.js → telemetry-DKM7VGuJ.js} +2 -2
  85. package/dist/{telemetry-Ch9ZJvm7.js.map → telemetry-DKM7VGuJ.js.map} +1 -1
  86. package/dist/{terminal-D20JFN3J.js → terminal-Bkxwzkhn.js} +118 -93
  87. package/dist/terminal-Bkxwzkhn.js.map +1 -0
  88. package/dist/{tools-CmSMclOD.js → tools-Bm0FDT6f.js} +15 -11
  89. package/dist/tools-Bm0FDT6f.js.map +1 -0
  90. package/dist/{tools-BZseGChD.js → tools-DZCEvDv9.js} +13 -13
  91. package/dist/{tools-BZseGChD.js.map → tools-DZCEvDv9.js.map} +1 -1
  92. package/dist/{tui-BXe1D0tc.js → tui-B56jvkbi.js} +4 -4
  93. package/dist/{tui-BXe1D0tc.js.map → tui-B56jvkbi.js.map} +1 -1
  94. package/dist/{tui-DJf3ZPRh.js → tui-tKI95MSr.js} +1 -1
  95. package/dist/{upload-step-BlAN7MC5.js → upload-step-DqTgn6UB.js} +3 -3
  96. package/dist/{upload-step-BlAN7MC5.js.map → upload-step-DqTgn6UB.js.map} +1 -1
  97. package/dist/{urls-eFaChZ9d.js → urls-B82v7gfO.js} +2 -2
  98. package/dist/{urls-eFaChZ9d.js.map → urls-B82v7gfO.js.map} +1 -1
  99. package/dist/{wizard-abort-BX3O4Hg4.js → wizard-abort-Cd-7sfCw.js} +4 -4
  100. package/dist/{wizard-abort-BX3O4Hg4.js.map → wizard-abort-Cd-7sfCw.js.map} +1 -1
  101. package/dist/{yara-hooks-C4HOysrM.js → yara-hooks-BS0n3ekf.js} +4 -4
  102. package/dist/{yara-hooks-C4HOysrM.js.map → yara-hooks-BS0n3ekf.js.map} +1 -1
  103. package/dist/{yara-hooks-DV4EkkZY.js → yara-hooks-CxksH-GZ.js} +1 -1
  104. package/package.json +1 -1
  105. package/dist/agent-interface-BFzBnz10.js.map +0 -1
  106. package/dist/analytics-CGGE7OSa.js.map +0 -1
  107. package/dist/analytics-D6NAIJxP.js +0 -2
  108. package/dist/api-DM4uMJ00.js +0 -2
  109. package/dist/api-key-login-C30LRht2.js +0 -2
  110. package/dist/bounded-fs-DwL2Tasp.js.map +0 -1
  111. package/dist/oauth-session-C2fVSGfr.js.map +0 -1
  112. package/dist/pi-Cw7CPXSl.js.map +0 -1
  113. package/dist/posthog-integration-FAWfClH3.js.map +0 -1
  114. package/dist/programs-DjCmEcxb.js +0 -3
  115. package/dist/programs-Ml6OLybj.js.map +0 -1
  116. package/dist/provisioning-DL0xCJXH.js +0 -2
  117. package/dist/run-tool-tNjm8S-E.js +0 -2
  118. package/dist/task-C1qKgsAf.js.map +0 -1
  119. package/dist/terminal-D20JFN3J.js.map +0 -1
  120. package/dist/tools-CmSMclOD.js.map +0 -1
@@ -1,5 +1,5 @@
1
- import { o as logToFile } from "./debug-sB20p-Ek.js";
2
- import { n as analytics } from "./analytics-CGGE7OSa.js";
1
+ import { o as logToFile } from "./debug-BvEbemym.js";
2
+ import { n as analytics } from "./analytics-B_xFshPY.js";
3
3
  import fs from "fs";
4
4
  import path from "path";
5
5
  import fg from "fast-glob";
@@ -24,7 +24,9 @@ const BENIGN_FS_ERROR_CODES = new Set([
24
24
  "ELOOP",
25
25
  "ENAMETOOLONG",
26
26
  "EMFILE",
27
- "ENFILE"
27
+ "ENFILE",
28
+ "EBUSY",
29
+ "UNKNOWN"
28
30
  ]);
29
31
  /**
30
32
  * Report a swallowed filesystem error. Traversal stays best-effort — the caller
@@ -239,4 +241,4 @@ function readFileHead(filePath, bytes) {
239
241
  //#endregion
240
242
  export { safeReadFile as a, readProjectFile as i, boundedGlob as n, walkProjectFiles as o, readFileHead as r, IGNORED_DIRS as t };
241
243
 
242
- //# sourceMappingURL=bounded-fs-DwL2Tasp.js.map
244
+ //# sourceMappingURL=bounded-fs-iRaQcKYL.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bounded-fs-iRaQcKYL.js","names":[],"sources":["../src/shared/utils/bounded-fs.ts"],"sourcesContent":["/**\n * Bounded filesystem primitives for scanning USER project trees. Every\n * detection-time glob, walk, and project-file read goes through here so the\n * worst case is a constant, never a function of the user's repo.\n */\nimport path from 'path';\nimport fs from 'fs';\nimport type { Dirent } from 'fs';\nimport fg from 'fast-glob';\nimport { analytics } from './analytics';\nimport { logToFile } from './debug';\nimport type { WizardRunOptions } from './types';\n\n/**\n * Errno codes that are expected while walking an arbitrary tree and mean\n * \"skip this entry\", not \"something is broken\". Scanning from a broad dir\n * (e.g. `~`) routinely hits OS-protected paths (macOS `Library/Group\n * Containers/group.com.apple.*`) that throw EPERM/EACCES per entry — capturing\n * each as an exception buried real errors under thousands of noise events.\n */\nconst BENIGN_FS_ERROR_CODES = new Set<string>([\n 'EACCES', // permission denied\n 'EPERM', // operation not permitted (macOS-protected dirs)\n 'ENOENT', // entry vanished mid-walk\n 'ENOTDIR', // path component isn't a directory\n 'ELOOP', // symlink loop\n 'ENAMETOOLONG', // path too long\n 'EMFILE', // too many open files (environmental, not a wizard bug)\n 'ENFILE',\n 'EBUSY', // file locked by another process (Windows)\n 'UNKNOWN', // libuv could not map the OS error (Windows locked/placeholder files)\n]);\n\n/**\n * Report a swallowed filesystem error. Traversal stays best-effort — the caller\n * still skips the failing entry. Expected errno codes (permission denied, entry\n * vanished, symlink loops…) are logged to the debug file only; anything else is\n * genuinely unexpected and gets captured to error tracking. Preserves the\n * original Error (and its `code`, e.g. EACCES / ENOENT) when available.\n */\nfunction reportFsError(step: string, filePath: string, error: unknown): void {\n const code = (error as NodeJS.ErrnoException | null)?.code;\n if (code && BENIGN_FS_ERROR_CODES.has(code)) {\n logToFile(`[bounded-fs] skipped ${step} (${code}): ${filePath}`);\n return;\n }\n analytics.captureException(\n error instanceof Error ? error : new Error(String(error)),\n { step, path: filePath },\n );\n}\n\nexport function getDotGitignore({\n installDir,\n}: Pick<WizardRunOptions, 'installDir'>) {\n const gitignorePath = path.join(installDir, '.gitignore');\n const gitignoreExists = fs.existsSync(gitignorePath);\n\n if (gitignoreExists) {\n return gitignorePath;\n }\n\n return undefined;\n}\n\n/**\n * Directory names to skip when scanning a project tree — dependency\n * directories, build output, virtual environments, vendored trees.\n * Used verbatim by walkProjectFiles and as glob patterns by boundedGlob.\n */\nexport const IGNORED_DIRS = new Set<string>([\n 'node_modules',\n '.git',\n '.next',\n '.nuxt',\n '.svelte-kit',\n '.turbo',\n '.cache',\n '.parcel-cache',\n 'dist',\n 'build',\n 'out',\n 'coverage',\n '.coverage',\n 'venv',\n '.venv',\n '__pycache__',\n '.pytest_cache',\n 'vendor',\n 'target',\n '.gradle',\n '.idea',\n '.vscode',\n 'site-packages',\n 'DerivedData',\n 'Pods',\n]);\n\n/** IGNORED_DIRS as fast-glob ignore patterns. */\nexport const PROJECT_IGNORE_GLOBS: readonly string[] = [...IGNORED_DIRS].map(\n (dir) => `**/${dir}/**`,\n);\n\n// Files larger than this are skipped, never materialized.\nexport const MAX_PROJECT_FILE_BYTES = 2 * 1024 * 1024;\n\n// A glob returns at most this many entries.\nexport const MAX_GLOB_MATCHES = 500;\n\n// A glob's traversal is destroyed after this long, even when abandoned.\nexport const GLOB_DEADLINE_MS = 8_000;\n\n// A directory contributes at most this many entries to a walk.\nexport const MAX_DIR_ENTRIES = 10_000;\n\n// A walk fires at most this many onFile callbacks.\nexport const MAX_WALK_FILES = 100_000;\n\nexport interface BoundedGlobOptions {\n cwd: string;\n /** Appended to PROJECT_IGNORE_GLOBS. */\n extraIgnore?: readonly string[];\n /** Stop after this many matches. Defaults to MAX_GLOB_MATCHES. */\n limit?: number;\n /** Match dotfiles. Traversal of dot-DIRECTORIES stays blocked by the ignore set. */\n dot?: boolean;\n /** fast-glob `deep` — maximum directory depth. */\n deep?: number;\n}\n\n/**\n * Streams matches, stops at `limit`, destroys the crawl at GLOB_DEADLINE_MS.\n * Returns paths relative to `cwd`, possibly truncated.\n */\nexport async function boundedGlob(\n patterns: string | string[],\n options: BoundedGlobOptions,\n): Promise<string[]> {\n const limit = options.limit ?? MAX_GLOB_MATCHES;\n const stream = fg.stream(patterns, {\n cwd: options.cwd,\n dot: options.dot ?? false,\n onlyFiles: true,\n deep: options.deep,\n ignore: [...PROJECT_IGNORE_GLOBS, ...(options.extraIgnore ?? [])],\n suppressErrors: true,\n followSymbolicLinks: false,\n }) as NodeJS.ReadableStream & { destroy(): void };\n\n const matches: string[] = [];\n return new Promise((resolve) => {\n let settled = false;\n const finish = (): void => {\n if (settled) return;\n settled = true;\n clearTimeout(timer);\n resolve(matches);\n };\n const timer = setTimeout(() => {\n stream.destroy();\n finish();\n }, GLOB_DEADLINE_MS);\n stream.on('data', (entry: string | Buffer) => {\n matches.push(String(entry));\n if (matches.length >= limit) {\n stream.destroy();\n finish();\n }\n });\n stream.once('error', finish);\n stream.once('end', finish);\n stream.once('close', finish);\n });\n}\n\n/**\n * Walk a project tree, invoking `onFile(name, fullPath)` for every regular\n * file — including dotfiles like `.env` (the caller decides what it cares\n * about). Skips `IGNORED_DIRS` and hidden directories, follows symlinked\n * directories with realpath-based loop protection, descends at most\n * `maxDepth` levels below `rootDir`, and stops at the per-directory and\n * whole-walk caps. Filesystem errors are reported and skipped (best-effort).\n */\nexport function walkProjectFiles(\n rootDir: string,\n onFile: (name: string, fullPath: string) => void,\n maxDepth = 3,\n): void {\n const visited = new Set<string>();\n let filesSeen = 0;\n // Explicit stack — no call-stack recursion.\n const stack: Array<{ dir: string; depth: number }> = [\n { dir: rootDir, depth: 0 },\n ];\n\n while (stack.length > 0 && filesSeen < MAX_WALK_FILES) {\n const { dir, depth } = stack.pop()!;\n\n // realpath both resolves symlinked dirs and gives us a stable key to\n // detect loops (symlink cycles, e.g. a -> ../a).\n let realDir: string;\n try {\n realDir = fs.realpathSync(dir);\n } catch (error) {\n reportFsError('walkProjectFiles.realpath', dir, error);\n continue;\n }\n if (visited.has(realDir)) continue;\n visited.add(realDir);\n\n let handle: fs.Dir;\n try {\n handle = fs.opendirSync(dir);\n } catch (error) {\n reportFsError('walkProjectFiles.opendir', dir, error);\n continue;\n }\n try {\n let entriesRead = 0;\n let entry: Dirent | null;\n while ((entry = handle.readSync()) !== null) {\n if (++entriesRead > MAX_DIR_ENTRIES || filesSeen >= MAX_WALK_FILES) {\n logToFile(\n `[bounded-fs] walk capped in ${dir} (entries=${entriesRead}, files=${filesSeen})`,\n );\n break;\n }\n if (IGNORED_DIRS.has(entry.name)) continue;\n\n const fullPath = path.join(dir, entry.name);\n\n // Symlinks report isDirectory()/isFile() as false under withFileTypes,\n // so resolve the target before deciding how to handle the entry.\n let isDir = entry.isDirectory();\n let isFile = entry.isFile();\n if (entry.isSymbolicLink()) {\n try {\n const st = fs.statSync(fullPath);\n isDir = st.isDirectory();\n isFile = st.isFile();\n } catch (error) {\n reportFsError('walkProjectFiles.stat', fullPath, error);\n continue;\n }\n }\n\n if (isDir) {\n if (entry.name.startsWith('.')) continue; // skip hidden directories\n if (depth < maxDepth) stack.push({ dir: fullPath, depth: depth + 1 });\n } else if (isFile) {\n filesSeen += 1;\n onFile(entry.name, fullPath);\n }\n }\n } finally {\n try {\n handle.closeSync();\n } catch {\n /* already closed */\n }\n }\n }\n}\n\n/**\n * Read a project file as UTF-8, or null when missing, unreadable, or over\n * `maxBytes` (stat-gated: an oversized file is never materialized).\n */\nexport function readProjectFile(\n filePath: string,\n maxBytes = MAX_PROJECT_FILE_BYTES,\n): string | null {\n try {\n if (fs.statSync(filePath).size > maxBytes) return null;\n return fs.readFileSync(filePath, 'utf-8');\n } catch {\n return null;\n }\n}\n\n/**\n * `readProjectFile` that also reports unexpected errors to error tracking.\n * For walk callbacks where a failing read is worth a telemetry breadcrumb.\n */\nexport function safeReadFile(fullPath: string): string | null {\n try {\n if (fs.statSync(fullPath).size > MAX_PROJECT_FILE_BYTES) {\n logToFile(`[bounded-fs] skipped oversized file: ${fullPath}`);\n return null;\n }\n return fs.readFileSync(fullPath, 'utf-8');\n } catch (error) {\n reportFsError('safeReadFile', fullPath, error);\n return null;\n }\n}\n\n/**\n * The first `bytes` of a file without materializing the rest.\n */\nexport function readFileHead(filePath: string, bytes: number): string | null {\n try {\n const fd = fs.openSync(filePath, 'r');\n try {\n const buffer = Buffer.alloc(bytes);\n const read = fs.readSync(fd, buffer, 0, bytes, 0);\n return buffer.toString('utf-8', 0, read);\n } finally {\n fs.closeSync(fd);\n }\n } catch {\n return null;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAoBA,MAAM,wBAAwB,IAAI,IAAY;CAC5C;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD,CAAC;;;;;;;;AASF,SAAS,cAAc,MAAc,UAAkB,OAAsB;CAC3E,MAAM,OAAQ,OAAwC;AACtD,KAAI,QAAQ,sBAAsB,IAAI,KAAK,EAAE;AAC3C,YAAU,wBAAwB,KAAK,IAAI,KAAK,KAAK,WAAW;AAChE;;AAEF,WAAU,iBACR,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,MAAM,CAAC,EACzD;EAAE;EAAM,MAAM;EAAU,CACzB;;;;;;;AAqBH,MAAa,eAAe,IAAI,IAAY;CAC1C;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD,CAAC;;AAGF,MAAa,uBAA0C,CAAC,GAAG,aAAa,CAAC,KACtE,QAAQ,MAAM,IAAI,KACpB;AAGD,MAAa,yBAAyB,IAAI,OAAO;AAMjD,MAAa,mBAAmB;;;;;AAwBhC,eAAsB,YACpB,UACA,SACmB;CACnB,MAAM,QAAQ,QAAQ,SAAA;CACtB,MAAM,SAAS,GAAG,OAAO,UAAU;EACjC,KAAK,QAAQ;EACb,KAAK,QAAQ,OAAO;EACpB,WAAW;EACX,MAAM,QAAQ;EACd,QAAQ,CAAC,GAAG,sBAAsB,GAAI,QAAQ,eAAe,EAAE,CAAE;EACjE,gBAAgB;EAChB,qBAAqB;EACtB,CAAC;CAEF,MAAM,UAAoB,EAAE;AAC5B,QAAO,IAAI,SAAS,YAAY;EAC9B,IAAI,UAAU;EACd,MAAM,eAAqB;AACzB,OAAI,QAAS;AACb,aAAU;AACV,gBAAa,MAAM;AACnB,WAAQ,QAAQ;;EAElB,MAAM,QAAQ,iBAAiB;AAC7B,UAAO,SAAS;AAChB,WAAQ;KACP,iBAAiB;AACpB,SAAO,GAAG,SAAS,UAA2B;AAC5C,WAAQ,KAAK,OAAO,MAAM,CAAC;AAC3B,OAAI,QAAQ,UAAU,OAAO;AAC3B,WAAO,SAAS;AAChB,YAAQ;;IAEV;AACF,SAAO,KAAK,SAAS,OAAO;AAC5B,SAAO,KAAK,OAAO,OAAO;AAC1B,SAAO,KAAK,SAAS,OAAO;GAC5B;;;;;;;;;;AAWJ,SAAgB,iBACd,SACA,QACA,WAAW,GACL;CACN,MAAM,0BAAU,IAAI,KAAa;CACjC,IAAI,YAAY;CAEhB,MAAM,QAA+C,CACnD;EAAE,KAAK;EAAS,OAAO;EAAG,CAC3B;AAED,QAAO,MAAM,SAAS,KAAK,YAAA,KAA4B;EACrD,MAAM,EAAE,KAAK,UAAU,MAAM,KAAK;EAIlC,IAAI;AACJ,MAAI;AACF,aAAU,GAAG,aAAa,IAAI;WACvB,OAAO;AACd,iBAAc,6BAA6B,KAAK,MAAM;AACtD;;AAEF,MAAI,QAAQ,IAAI,QAAQ,CAAE;AAC1B,UAAQ,IAAI,QAAQ;EAEpB,IAAI;AACJ,MAAI;AACF,YAAS,GAAG,YAAY,IAAI;WACrB,OAAO;AACd,iBAAc,4BAA4B,KAAK,MAAM;AACrD;;AAEF,MAAI;GACF,IAAI,cAAc;GAClB,IAAI;AACJ,WAAQ,QAAQ,OAAO,UAAU,MAAM,MAAM;AAC3C,QAAI,EAAE,cAAA,OAAiC,aAAA,KAA6B;AAClE,eACE,+BAA+B,IAAI,YAAY,YAAY,UAAU,UAAU,GAChF;AACD;;AAEF,QAAI,aAAa,IAAI,MAAM,KAAK,CAAE;IAElC,MAAM,WAAW,KAAK,KAAK,KAAK,MAAM,KAAK;IAI3C,IAAI,QAAQ,MAAM,aAAa;IAC/B,IAAI,SAAS,MAAM,QAAQ;AAC3B,QAAI,MAAM,gBAAgB,CACxB,KAAI;KACF,MAAM,KAAK,GAAG,SAAS,SAAS;AAChC,aAAQ,GAAG,aAAa;AACxB,cAAS,GAAG,QAAQ;aACb,OAAO;AACd,mBAAc,yBAAyB,UAAU,MAAM;AACvD;;AAIJ,QAAI,OAAO;AACT,SAAI,MAAM,KAAK,WAAW,IAAI,CAAE;AAChC,SAAI,QAAQ,SAAU,OAAM,KAAK;MAAE,KAAK;MAAU,OAAO,QAAQ;MAAG,CAAC;eAC5D,QAAQ;AACjB,kBAAa;AACb,YAAO,MAAM,MAAM,SAAS;;;YAGxB;AACR,OAAI;AACF,WAAO,WAAW;WACZ;;;;;;;;AAWd,SAAgB,gBACd,UACA,WAAW,wBACI;AACf,KAAI;AACF,MAAI,GAAG,SAAS,SAAS,CAAC,OAAO,SAAU,QAAO;AAClD,SAAO,GAAG,aAAa,UAAU,QAAQ;SACnC;AACN,SAAO;;;;;;;AAQX,SAAgB,aAAa,UAAiC;AAC5D,KAAI;AACF,MAAI,GAAG,SAAS,SAAS,CAAC,OAAA,SAA+B;AACvD,aAAU,wCAAwC,WAAW;AAC7D,UAAO;;AAET,SAAO,GAAG,aAAa,UAAU,QAAQ;UAClC,OAAO;AACd,gBAAc,gBAAgB,UAAU,MAAM;AAC9C,SAAO;;;;;;AAOX,SAAgB,aAAa,UAAkB,OAA8B;AAC3E,KAAI;EACF,MAAM,KAAK,GAAG,SAAS,UAAU,IAAI;AACrC,MAAI;GACF,MAAM,SAAS,OAAO,MAAM,MAAM;GAClC,MAAM,OAAO,GAAG,SAAS,IAAI,QAAQ,GAAG,OAAO,EAAE;AACjD,UAAO,OAAO,SAAS,SAAS,GAAG,KAAK;YAChC;AACR,MAAG,UAAU,GAAG;;SAEZ;AACN,SAAO"}
@@ -1,9 +1,9 @@
1
1
  import { n as ErrorCodes, t as emitWizardError } from "./emit-BqLCdV8Y.js";
2
2
  import "./errors-DhFeR8Sp.js";
3
- import { n as runWizardCI, r as API_KEY_HINT, t as runWizardHeadless } from "./runners-GGYJs0Ny.js";
3
+ import { n as runWizardCI, r as API_KEY_HINT, t as runWizardHeadless } from "./runners-Cvnk9BcA.js";
4
4
  import { t as consoleLog } from "./console-log-DJiTqYT2.js";
5
- import { t as config } from "./posthog-integration-FAWfClH3.js";
6
- import { n as provisionNewAccount } from "./provisioning-BTVL49jD.js";
5
+ import { t as config } from "./posthog-integration-CpinLWNO.js";
6
+ import { n as provisionNewAccount } from "./provisioning-D-AM_7DG.js";
7
7
  //#region src/cli/commands/basic-integration/ci-install.ts
8
8
  /**
9
9
  * CI-mode install entry point (`--ci`, dev/test builds). Thin shell over the
@@ -114,4 +114,4 @@ async function provisionForSignup(options) {
114
114
  //#endregion
115
115
  export { runCIInstall, runHeadlessInstall };
116
116
 
117
- //# sourceMappingURL=ci-install-DKXrt_ow.js.map
117
+ //# sourceMappingURL=ci-install-BGlijoTo.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ci-install-DKXrt_ow.js","names":["posthogIntegrationConfig"],"sources":["../src/cli/commands/basic-integration/ci-install.ts"],"sourcesContent":["import type { Arguments } from 'yargs';\nimport { consoleLog } from '@shared/console-log';\nimport { API_KEY_HINT, runWizardCI, runWizardHeadless } from '@cli/runners';\nimport type { NonInteractiveMode } from '@headless';\nimport { provisionNewAccount } from '@utils/provisioning';\nimport { config as posthogIntegrationConfig } from '@programs/posthog-integration';\nimport { ErrorCodes, type ErrorCode } from '@shared/errors';\nimport { emitWizardError } from '@shared/errors';\n\ntype Options = Arguments & {\n region?: string;\n baseUrl?: string;\n installDir?: string;\n apiKey?: string;\n signup?: boolean;\n email?: string;\n name?: string;\n projectId?: string;\n};\n\n/**\n * CI-mode install entry point (`--ci`, dev/test builds). Thin shell over the\n * shared non-interactive install — see `runNonInteractiveInstall`.\n */\nexport function runCIInstall(argv: Arguments): void {\n runNonInteractiveInstall(argv, 'ci');\n}\n\n/**\n * Headless install entry point (the experimental published-build run path; see\n * @shared/headless-mode). Thin shell over the shared non-interactive install.\n * Today it behaves exactly like `runCIInstall`; it is a separate function so\n * headless can diverge later (auth, prompts, …) without touching the CI path.\n */\nexport function runHeadlessInstall(argv: Arguments): void {\n runNonInteractiveInstall(argv, 'headless');\n}\n\n/**\n * Non-interactive install shared by CI and headless. Validates signup flags,\n * optionally provisions an account, then installs. `mode` only changes\n * user-facing labels and which runner is invoked; the accepted keys and the\n * install itself are identical (see runNonInteractive).\n */\nfunction runNonInteractiveInstall(\n argv: Arguments,\n mode: NonInteractiveMode,\n): void {\n const options = { ...argv } as Options;\n const headless = mode === 'headless';\n const label = headless ? 'Headless' : 'CI';\n const runWizard = headless ? runWizardHeadless : runWizardCI;\n\n // Base validation (region/install-dir/api-key) is owned by the runner.\n // This layer only adds the signup branch on top.\n if (!options.apiKey && !options.signup) {\n return failCI(\n `${label} mode requires --api-key (${API_KEY_HINT}). ` +\n 'To create a new account instead, use --signup --email you@example.com.',\n ErrorCodes.ArgsMissingApiKey,\n );\n }\n if (!options.apiKey && options.signup && !options.email) {\n return failCI(\n `${label} --signup requires --email to create a new account.`,\n ErrorCodes.ArgsMissingEmail,\n );\n }\n warnOnUnexpectedKeyPrefix(options.apiKey);\n\n void (async () => {\n if (!options.apiKey && options.signup) {\n // Fail before the irreversible provisioning step rather than after it.\n if (!options.installDir) {\n return failCI(\n `${label} mode requires --install-dir (directory to install in)`,\n ErrorCodes.ArgsMissingInstallDir,\n );\n }\n const provisioned = await provisionForSignup(options);\n options.apiKey = provisioned.personalApiKey;\n if (options.projectId == null) options.projectId = provisioned.projectId;\n }\n runWizard(posthogIntegrationConfig, options);\n })().catch((error: unknown) => {\n emitWizardError({\n code: ErrorCodes.ArgsSignupProvisionFailed,\n message: error instanceof Error ? error.message : String(error),\n });\n process.exit(1);\n });\n}\n\nfunction failCI(message: string, code?: ErrorCode): void {\n consoleLog.intro('PostHog Wizard');\n consoleLog.log.error(message);\n if (code) emitWizardError({ code, message });\n process.exit(1);\n}\n\n/**\n * Decide whether to warn about an unexpected `--api-key` prefix, and with what\n * message. Returns `null` when the key is acceptable.\n *\n * CI and headless accept the same two credentials: a personal API key (`phx_`)\n * and a `pha_` OAuth access token minted under the wizard's own OAuth\n * application (cloud runs, and the CI bot). Both authenticate the mint the\n * same way. Anything else is unexpected and warns.\n *\n * Extracted as a pure predicate so it can be unit-tested without a UI.\n */\nexport function keyPrefixWarning(apiKey: string | undefined): string | null {\n if (!apiKey || apiKey.startsWith('phx_') || apiKey.startsWith('pha_')) {\n return null;\n }\n const hint = apiKey.startsWith('phc_')\n ? ' (phc_ is a project/client key; expected a personal API key or a wizard-app token)'\n : '';\n return `--api-key does not start with \"phx_\" or \"pha_\"${hint}. Continuing anyway, but the LLM Gateway may reject it with a 401.`;\n}\n\nfunction warnOnUnexpectedKeyPrefix(apiKey: string | undefined): void {\n const message = keyPrefixWarning(apiKey);\n if (!message) return;\n consoleLog.intro('PostHog Wizard');\n consoleLog.log.warn(message);\n}\n\n/**\n * Provision a new account and return its credentials. Throws on any failure\n * (after logging a user-facing message); the caller's `.catch` turns that\n * into a non-zero exit. The return type carries no failure sentinel.\n */\nasync function provisionForSignup(\n options: Options,\n): Promise<{ personalApiKey: string; projectId: string }> {\n consoleLog.intro('PostHog Wizard');\n const signupRegion = ((options.region as string) || 'us').toUpperCase() as\n | 'US'\n | 'EU';\n consoleLog.log.info(\n `Provisioning new PostHog account for ${String(\n options.email,\n )} in ${signupRegion}...`,\n );\n\n let result;\n try {\n result = await provisionNewAccount(\n options.email as string,\n options.name ?? '',\n signupRegion,\n { baseUrl: options.baseUrl },\n );\n } catch (error) {\n const msg = error instanceof Error ? error.message : String(error);\n consoleLog.log.error(`Provisioning failed: ${msg}`);\n throw error;\n }\n\n if (!result.personalApiKey) {\n consoleLog.log.error(\n 'Provisioning succeeded but no personal API key was returned — cannot continue install.',\n );\n throw new Error('provisioning returned no personal API key');\n }\n\n consoleLog.log.success('Account ready.');\n consoleLog.log.info(` Project API Key: ${result.projectApiKey}`);\n consoleLog.log.info(` Personal API Key: ${result.personalApiKey}`);\n consoleLog.log.info(` Host: ${result.host}`);\n return {\n personalApiKey: result.personalApiKey,\n projectId: result.projectId,\n };\n}\n"],"mappings":";;;;;;;;;;;AAwBA,SAAgB,aAAa,MAAuB;AAClD,0BAAyB,MAAM,KAAK;;;;;;;;AAStC,SAAgB,mBAAmB,MAAuB;AACxD,0BAAyB,MAAM,WAAW;;;;;;;;AAS5C,SAAS,yBACP,MACA,MACM;CACN,MAAM,UAAU,EAAE,GAAG,MAAM;CAC3B,MAAM,WAAW,SAAS;CAC1B,MAAM,QAAQ,WAAW,aAAa;CACtC,MAAM,YAAY,WAAW,oBAAoB;AAIjD,KAAI,CAAC,QAAQ,UAAU,CAAC,QAAQ,OAC9B,QAAO,OACL,GAAG,MAAM,4BAA4B,aAAa,4EAElD,WAAW,kBACZ;AAEH,KAAI,CAAC,QAAQ,UAAU,QAAQ,UAAU,CAAC,QAAQ,MAChD,QAAO,OACL,GAAG,MAAM,sDACT,WAAW,iBACZ;AAEH,2BAA0B,QAAQ,OAAO;AAEzC,EAAM,YAAY;AAChB,MAAI,CAAC,QAAQ,UAAU,QAAQ,QAAQ;AAErC,OAAI,CAAC,QAAQ,WACX,QAAO,OACL,GAAG,MAAM,yDACT,WAAW,sBACZ;GAEH,MAAM,cAAc,MAAM,mBAAmB,QAAQ;AACrD,WAAQ,SAAS,YAAY;AAC7B,OAAI,QAAQ,aAAa,KAAM,SAAQ,YAAY,YAAY;;AAEjE,YAAUA,QAA0B,QAAQ;KAC1C,CAAC,OAAO,UAAmB;AAC7B,kBAAgB;GACd,MAAM,WAAW;GACjB,SAAS,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM;GAChE,CAAC;AACF,UAAQ,KAAK,EAAE;GACf;;AAGJ,SAAS,OAAO,SAAiB,MAAwB;AACvD,YAAW,MAAM,iBAAiB;AAClC,YAAW,IAAI,MAAM,QAAQ;AAC7B,KAAI,KAAM,iBAAgB;EAAE;EAAM;EAAS,CAAC;AAC5C,SAAQ,KAAK,EAAE;;;;;;;;;;;;;AAcjB,SAAgB,iBAAiB,QAA2C;AAC1E,KAAI,CAAC,UAAU,OAAO,WAAW,OAAO,IAAI,OAAO,WAAW,OAAO,CACnE,QAAO;AAKT,QAAO,iDAHM,OAAO,WAAW,OAAO,GAClC,uFACA,GACyD;;AAG/D,SAAS,0BAA0B,QAAkC;CACnE,MAAM,UAAU,iBAAiB,OAAO;AACxC,KAAI,CAAC,QAAS;AACd,YAAW,MAAM,iBAAiB;AAClC,YAAW,IAAI,KAAK,QAAQ;;;;;;;AAQ9B,eAAe,mBACb,SACwD;AACxD,YAAW,MAAM,iBAAiB;CAClC,MAAM,gBAAiB,QAAQ,UAAqB,MAAM,aAAa;AAGvE,YAAW,IAAI,KACb,wCAAwC,OACtC,QAAQ,MACT,CAAC,MAAM,aAAa,KACtB;CAED,IAAI;AACJ,KAAI;AACF,WAAS,MAAM,oBACb,QAAQ,OACR,QAAQ,QAAQ,IAChB,cACA,EAAE,SAAS,QAAQ,SAAS,CAC7B;UACM,OAAO;EACd,MAAM,MAAM,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM;AAClE,aAAW,IAAI,MAAM,wBAAwB,MAAM;AACnD,QAAM;;AAGR,KAAI,CAAC,OAAO,gBAAgB;AAC1B,aAAW,IAAI,MACb,yFACD;AACD,QAAM,IAAI,MAAM,4CAA4C;;AAG9D,YAAW,IAAI,QAAQ,iBAAiB;AACxC,YAAW,IAAI,KAAK,uBAAuB,OAAO,gBAAgB;AAClE,YAAW,IAAI,KAAK,uBAAuB,OAAO,iBAAiB;AACnE,YAAW,IAAI,KAAK,uBAAuB,OAAO,OAAO;AACzD,QAAO;EACL,gBAAgB,OAAO;EACvB,WAAW,OAAO;EACnB"}
1
+ {"version":3,"file":"ci-install-BGlijoTo.js","names":["posthogIntegrationConfig"],"sources":["../src/cli/commands/basic-integration/ci-install.ts"],"sourcesContent":["import type { Arguments } from 'yargs';\nimport { consoleLog } from '@shared/console-log';\nimport { API_KEY_HINT, runWizardCI, runWizardHeadless } from '@cli/runners';\nimport type { NonInteractiveMode } from '@headless';\nimport { provisionNewAccount } from '@utils/provisioning';\nimport { config as posthogIntegrationConfig } from '@programs/posthog-integration';\nimport { ErrorCodes, type ErrorCode } from '@shared/errors';\nimport { emitWizardError } from '@shared/errors';\n\ntype Options = Arguments & {\n region?: string;\n baseUrl?: string;\n installDir?: string;\n apiKey?: string;\n signup?: boolean;\n email?: string;\n name?: string;\n projectId?: string;\n};\n\n/**\n * CI-mode install entry point (`--ci`, dev/test builds). Thin shell over the\n * shared non-interactive install — see `runNonInteractiveInstall`.\n */\nexport function runCIInstall(argv: Arguments): void {\n runNonInteractiveInstall(argv, 'ci');\n}\n\n/**\n * Headless install entry point (the experimental published-build run path; see\n * @shared/headless-mode). Thin shell over the shared non-interactive install.\n * Today it behaves exactly like `runCIInstall`; it is a separate function so\n * headless can diverge later (auth, prompts, …) without touching the CI path.\n */\nexport function runHeadlessInstall(argv: Arguments): void {\n runNonInteractiveInstall(argv, 'headless');\n}\n\n/**\n * Non-interactive install shared by CI and headless. Validates signup flags,\n * optionally provisions an account, then installs. `mode` only changes\n * user-facing labels and which runner is invoked; the accepted keys and the\n * install itself are identical (see runNonInteractive).\n */\nfunction runNonInteractiveInstall(\n argv: Arguments,\n mode: NonInteractiveMode,\n): void {\n const options = { ...argv } as Options;\n const headless = mode === 'headless';\n const label = headless ? 'Headless' : 'CI';\n const runWizard = headless ? runWizardHeadless : runWizardCI;\n\n // Base validation (region/install-dir/api-key) is owned by the runner.\n // This layer only adds the signup branch on top.\n if (!options.apiKey && !options.signup) {\n return failCI(\n `${label} mode requires --api-key (${API_KEY_HINT}). ` +\n 'To create a new account instead, use --signup --email you@example.com.',\n ErrorCodes.ArgsMissingApiKey,\n );\n }\n if (!options.apiKey && options.signup && !options.email) {\n return failCI(\n `${label} --signup requires --email to create a new account.`,\n ErrorCodes.ArgsMissingEmail,\n );\n }\n warnOnUnexpectedKeyPrefix(options.apiKey);\n\n void (async () => {\n if (!options.apiKey && options.signup) {\n // Fail before the irreversible provisioning step rather than after it.\n if (!options.installDir) {\n return failCI(\n `${label} mode requires --install-dir (directory to install in)`,\n ErrorCodes.ArgsMissingInstallDir,\n );\n }\n const provisioned = await provisionForSignup(options);\n options.apiKey = provisioned.personalApiKey;\n if (options.projectId == null) options.projectId = provisioned.projectId;\n }\n runWizard(posthogIntegrationConfig, options);\n })().catch((error: unknown) => {\n emitWizardError({\n code: ErrorCodes.ArgsSignupProvisionFailed,\n message: error instanceof Error ? error.message : String(error),\n });\n process.exit(1);\n });\n}\n\nfunction failCI(message: string, code?: ErrorCode): void {\n consoleLog.intro('PostHog Wizard');\n consoleLog.log.error(message);\n if (code) emitWizardError({ code, message });\n process.exit(1);\n}\n\n/**\n * Decide whether to warn about an unexpected `--api-key` prefix, and with what\n * message. Returns `null` when the key is acceptable.\n *\n * CI and headless accept the same two credentials: a personal API key (`phx_`)\n * and a `pha_` OAuth access token minted under the wizard's own OAuth\n * application (cloud runs, and the CI bot). Both authenticate the mint the\n * same way. Anything else is unexpected and warns.\n *\n * Extracted as a pure predicate so it can be unit-tested without a UI.\n */\nexport function keyPrefixWarning(apiKey: string | undefined): string | null {\n if (!apiKey || apiKey.startsWith('phx_') || apiKey.startsWith('pha_')) {\n return null;\n }\n const hint = apiKey.startsWith('phc_')\n ? ' (phc_ is a project/client key; expected a personal API key or a wizard-app token)'\n : '';\n return `--api-key does not start with \"phx_\" or \"pha_\"${hint}. Continuing anyway, but the LLM Gateway may reject it with a 401.`;\n}\n\nfunction warnOnUnexpectedKeyPrefix(apiKey: string | undefined): void {\n const message = keyPrefixWarning(apiKey);\n if (!message) return;\n consoleLog.intro('PostHog Wizard');\n consoleLog.log.warn(message);\n}\n\n/**\n * Provision a new account and return its credentials. Throws on any failure\n * (after logging a user-facing message); the caller's `.catch` turns that\n * into a non-zero exit. The return type carries no failure sentinel.\n */\nasync function provisionForSignup(\n options: Options,\n): Promise<{ personalApiKey: string; projectId: string }> {\n consoleLog.intro('PostHog Wizard');\n const signupRegion = ((options.region as string) || 'us').toUpperCase() as\n | 'US'\n | 'EU';\n consoleLog.log.info(\n `Provisioning new PostHog account for ${String(\n options.email,\n )} in ${signupRegion}...`,\n );\n\n let result;\n try {\n result = await provisionNewAccount(\n options.email as string,\n options.name ?? '',\n signupRegion,\n { baseUrl: options.baseUrl },\n );\n } catch (error) {\n const msg = error instanceof Error ? error.message : String(error);\n consoleLog.log.error(`Provisioning failed: ${msg}`);\n throw error;\n }\n\n if (!result.personalApiKey) {\n consoleLog.log.error(\n 'Provisioning succeeded but no personal API key was returned — cannot continue install.',\n );\n throw new Error('provisioning returned no personal API key');\n }\n\n consoleLog.log.success('Account ready.');\n consoleLog.log.info(` Project API Key: ${result.projectApiKey}`);\n consoleLog.log.info(` Personal API Key: ${result.personalApiKey}`);\n consoleLog.log.info(` Host: ${result.host}`);\n return {\n personalApiKey: result.personalApiKey,\n projectId: result.projectId,\n };\n}\n"],"mappings":";;;;;;;;;;;AAwBA,SAAgB,aAAa,MAAuB;AAClD,0BAAyB,MAAM,KAAK;;;;;;;;AAStC,SAAgB,mBAAmB,MAAuB;AACxD,0BAAyB,MAAM,WAAW;;;;;;;;AAS5C,SAAS,yBACP,MACA,MACM;CACN,MAAM,UAAU,EAAE,GAAG,MAAM;CAC3B,MAAM,WAAW,SAAS;CAC1B,MAAM,QAAQ,WAAW,aAAa;CACtC,MAAM,YAAY,WAAW,oBAAoB;AAIjD,KAAI,CAAC,QAAQ,UAAU,CAAC,QAAQ,OAC9B,QAAO,OACL,GAAG,MAAM,4BAA4B,aAAa,4EAElD,WAAW,kBACZ;AAEH,KAAI,CAAC,QAAQ,UAAU,QAAQ,UAAU,CAAC,QAAQ,MAChD,QAAO,OACL,GAAG,MAAM,sDACT,WAAW,iBACZ;AAEH,2BAA0B,QAAQ,OAAO;AAEzC,EAAM,YAAY;AAChB,MAAI,CAAC,QAAQ,UAAU,QAAQ,QAAQ;AAErC,OAAI,CAAC,QAAQ,WACX,QAAO,OACL,GAAG,MAAM,yDACT,WAAW,sBACZ;GAEH,MAAM,cAAc,MAAM,mBAAmB,QAAQ;AACrD,WAAQ,SAAS,YAAY;AAC7B,OAAI,QAAQ,aAAa,KAAM,SAAQ,YAAY,YAAY;;AAEjE,YAAUA,QAA0B,QAAQ;KAC1C,CAAC,OAAO,UAAmB;AAC7B,kBAAgB;GACd,MAAM,WAAW;GACjB,SAAS,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM;GAChE,CAAC;AACF,UAAQ,KAAK,EAAE;GACf;;AAGJ,SAAS,OAAO,SAAiB,MAAwB;AACvD,YAAW,MAAM,iBAAiB;AAClC,YAAW,IAAI,MAAM,QAAQ;AAC7B,KAAI,KAAM,iBAAgB;EAAE;EAAM;EAAS,CAAC;AAC5C,SAAQ,KAAK,EAAE;;;;;;;;;;;;;AAcjB,SAAgB,iBAAiB,QAA2C;AAC1E,KAAI,CAAC,UAAU,OAAO,WAAW,OAAO,IAAI,OAAO,WAAW,OAAO,CACnE,QAAO;AAKT,QAAO,iDAHM,OAAO,WAAW,OAAO,GAClC,uFACA,GACyD;;AAG/D,SAAS,0BAA0B,QAAkC;CACnE,MAAM,UAAU,iBAAiB,OAAO;AACxC,KAAI,CAAC,QAAS;AACd,YAAW,MAAM,iBAAiB;AAClC,YAAW,IAAI,KAAK,QAAQ;;;;;;;AAQ9B,eAAe,mBACb,SACwD;AACxD,YAAW,MAAM,iBAAiB;CAClC,MAAM,gBAAiB,QAAQ,UAAqB,MAAM,aAAa;AAGvE,YAAW,IAAI,KACb,wCAAwC,OACtC,QAAQ,MACT,CAAC,MAAM,aAAa,KACtB;CAED,IAAI;AACJ,KAAI;AACF,WAAS,MAAM,oBACb,QAAQ,OACR,QAAQ,QAAQ,IAChB,cACA,EAAE,SAAS,QAAQ,SAAS,CAC7B;UACM,OAAO;EACd,MAAM,MAAM,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM;AAClE,aAAW,IAAI,MAAM,wBAAwB,MAAM;AACnD,QAAM;;AAGR,KAAI,CAAC,OAAO,gBAAgB;AAC1B,aAAW,IAAI,MACb,yFACD;AACD,QAAM,IAAI,MAAM,4CAA4C;;AAG9D,YAAW,IAAI,QAAQ,iBAAiB;AACxC,YAAW,IAAI,KAAK,uBAAuB,OAAO,gBAAgB;AAClE,YAAW,IAAI,KAAK,uBAAuB,OAAO,iBAAiB;AACnE,YAAW,IAAI,KAAK,uBAAuB,OAAO,OAAO;AACzD,QAAO;EACL,gBAAgB,OAAO;EACvB,WAAW,OAAO;EACnB"}
@@ -41,7 +41,7 @@ const TASK_RUN_ID = runtimeEnv("POSTHOG_TASK_RUN_ID") || void 0;
41
41
  const TASK_ID = runtimeEnv("POSTHOG_TASK_ID") || void 0;
42
42
  //#endregion
43
43
  //#region src/shared/version.ts
44
- const VERSION = "2.81.0";
44
+ const VERSION = "2.82.0";
45
45
  //#endregion
46
46
  //#region src/shared/utils/paths.ts
47
47
  const TMP = process.platform === "win32" ? tmpdir() : "/tmp";
@@ -95,7 +95,7 @@ let reportedLogFailure = false;
95
95
  function reportLogFailureOnce(err) {
96
96
  if (reportedLogFailure) return;
97
97
  reportedLogFailure = true;
98
- import("./analytics-D6NAIJxP.js").then(({ analytics }) => analytics.captureException(err instanceof Error ? err : new Error(String(err)), {
98
+ import("./analytics-DGDr6-iD.js").then(({ analytics }) => analytics.captureException(err instanceof Error ? err : new Error(String(err)), {
99
99
  source: "log-file-write",
100
100
  log_path: logFilePath,
101
101
  platform: process.platform
@@ -134,4 +134,4 @@ function logToFile(...args) {
134
134
  //#endregion
135
135
  export { TASK_RUN_ID as _, initLogFile as a, WIZARD_LOG_FILE as c, relativeToInstallDir as d, VERSION as f, TASK_ID as g, RUN_SURFACE as h, getLogFilePath as i, WIZARD_TASK_STREAM_FILE as l, NODE_ENV as m, configureLogFileFromEnvironment as n, logToFile as o, HEADLESS_FLAG as p, formatLogLine as r, WIZARD_BENCHMARK_FILE as s, configureLogFile as t, WIZARD_YARA_REPORT_FILE as u, runtimeEnv as v };
136
136
 
137
- //# sourceMappingURL=debug-sB20p-Ek.js.map
137
+ //# sourceMappingURL=debug-BvEbemym.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"debug-sB20p-Ek.js","names":["join","sep"],"sources":["../src/env.ts","../src/shared/version.ts","../src/shared/utils/paths.ts","../src/shared/utils/debug.ts"],"sourcesContent":["/**\n * Central environment variable access for the PostHog wizard.\n *\n * ── Build-time constants ────────────────────────────────────────────\n * Inlined by tsdown's `env` option at compile time. After build, the\n * runtime value of these env vars has zero effect on the wizard.\n *\n * ── Runtime variables ───────────────────────────────────────────────\n * Read through `runtimeEnv()` with a typed allowlist. This makes every\n * runtime dependency on the environment explicit and grep-able.\n *\n * ── Direct process.env access ───────────────────────────────────────\n * Reserved for subprocess environment configuration (writes) and\n * vendored code. Production source outside those cases should use\n * this module instead.\n */\n\n/**\n * The on-CLI flag name. Intentionally ugly + undocumented; do not surface it in\n * `--help`, the README, or user-facing error messages.\n */\nexport const HEADLESS_FLAG = 'headless-DONOTUSE-EXPERIMENTAL';\n\n// ── Build-time constants ─────────────────────────────────────────────\n// tsdown replaces `process.env.NODE_ENV` with a string literal.\n// After build these are just `\"production\"`, `false`, etc.\n\nexport const NODE_ENV = process.env.NODE_ENV as string;\nexport const IS_DEV =\n process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'test';\n\n/**\n * True only in published/production builds. tsdown inlines\n * `process.env.NODE_ENV` as the literal `\"production\"` at build time, so this\n * collapses to `true` in `dist/` and stays `false` for `tsx`/dev/test runs\n * (where NODE_ENV is unset, `development`, or `test`). Used to gate features\n * that aren't supported in the shipped package — e.g. `--ci` mode.\n */\nexport const IS_PRODUCTION_BUILD = process.env.NODE_ENV === 'production';\n\n/** 'cloud' when launched with the experimental headless flag, else 'local' — resolved once from argv at launch. */\nexport const RUN_SURFACE: 'cloud' | 'local' = process.argv.some(\n (a) => a === `--${HEADLESS_FLAG}` || a.startsWith(`--${HEADLESS_FLAG}=`),\n)\n ? 'cloud'\n : 'local';\n\n// ── Runtime environment ──────────────────────────────────────────────\n\n/**\n * Exhaustive allowlist of env vars the wizard reads at runtime.\n * Add new keys here when a new runtime dependency is needed.\n */\ntype RuntimeEnvKey =\n // CI-build-only flag overrides (see utils/ci-flag-overrides.ts).\n // Deliberately NOT POSTHOG_WIZARD_-prefixed: yargs .env('POSTHOG_WIZARD')\n // would claim it as an unknown CLI option and strict-reject the run.\n | 'WIZARD_CI_FLAG_OVERRIDES'\n | 'WIZARD_CI_EXCLUDE_TASKS'\n | 'WIZARD_CI_GATEWAY_TOKEN_FILE'\n | 'WIZARD_CI_GATEWAY_URL'\n // Wizard CLI configuration (yargs POSTHOG_WIZARD_ prefix)\n | 'POSTHOG_WIZARD_BENCHMARK_CONFIG'\n | 'POSTHOG_WIZARD_BENCHMARK_FILE'\n | 'POSTHOG_WIZARD_LOG_DIR'\n | 'POSTHOG_WIZARD_DEBUG'\n // Identity of the PostHog task run whose sandbox launched this wizard. Set by\n // the sandbox for every run it starts, agent or wizard. Deliberately NOT\n // POSTHOG_WIZARD_-prefixed, for the same reason as the two keys above:\n // yargs .env('POSTHOG_WIZARD') would claim them as CLI options and\n // .strictOptions() would reject the run unless the wizard also declared them,\n // which would tie every sandbox deploy to an npm release of this package.\n | 'POSTHOG_WIZARD_RUN_ID'\n | 'POSTHOG_TASK_RUN_ID'\n | 'POSTHOG_TASK_ID'\n | 'POSTHOG_HANDOFF_OUTPUT_PATH'\n // Local/CI escape hatch to disable Warlock scanning without the PostHog flag.\n | 'POSTHOG_WIZARD_WARLOCK_DISABLED'\n | 'DEBUG'\n // Agent / MCP\n | 'MCP_URL'\n | 'POSTHOG_API_KEY'\n // Platform: terminal detection\n | 'TERM'\n | 'TERM_PROGRAM'\n | 'TERMINAL_EMULATOR'\n | 'CI'\n | 'WT_SESSION'\n | 'TERMINUS_SUBLIME'\n | 'ConEmuTask'\n // Platform: paths\n | 'APPDATA'\n | 'OPENCODE_CONFIG_DIR'\n | 'XDG_CONFIG_HOME';\n\n/** Read a runtime environment variable. Only allowlisted keys compile. */\nexport function runtimeEnv(key: RuntimeEnvKey): string | undefined {\n return process.env[key];\n}\n\n/**\n * The PostHog task run that launched this wizard, when one did. A cloud run's\n * sandbox exports both ids into the wizard's environment; every local run\n * leaves them undefined. Resolved once at launch, like RUN_SURFACE above.\n */\nexport const TASK_RUN_ID: string | undefined =\n runtimeEnv('POSTHOG_TASK_RUN_ID') || undefined;\nexport const TASK_ID: string | undefined =\n runtimeEnv('POSTHOG_TASK_ID') || undefined;\n","// Auto-generated by scripts/generate-version.js — do not edit\nexport const VERSION = '2.81.0';\n","import { tmpdir } from 'node:os';\nimport { join, sep } from 'node:path';\n\n// /tmp is stable and discoverable on macOS/Linux; Windows needs os.tmpdir()\nconst TMP = process.platform === 'win32' ? tmpdir() : '/tmp';\n\nexport const WIZARD_LOG_FILE = join(TMP, 'posthog-wizard.log');\nexport const WIZARD_BENCHMARK_FILE = join(TMP, 'posthog-wizard-benchmark.json');\n/** Default target of `--task-stream-log`. Dev builds only. */\nexport const WIZARD_TASK_STREAM_FILE = join(\n TMP,\n 'posthog-wizard-task-stream.jsonl',\n);\nexport const WIZARD_YARA_REPORT_FILE = join(\n TMP,\n 'posthog-wizard-yara-report.json',\n);\n/**\n * Strip an absolute installDir prefix off a project file path so the UI\n * renders `index.js:12` instead of `/Users/.../index.js:12`. Defends\n * against false matches like `/Users/foo` ⊂ `/Users/foobar/x.js` by\n * normalizing to a trailing path separator before the prefix check.\n */\nexport function relativeToInstallDir(file: string, installDir: string): string {\n const prefix = installDir.endsWith(sep) ? installDir : installDir + sep;\n return file.startsWith(prefix) ? file.slice(prefix.length) : file;\n}\n","import { appendFileSync, mkdirSync } from 'fs';\nimport path from 'path';\nimport { inspect } from 'node:util';\nimport { IS_DEV, runtimeEnv } from '@env';\nimport { WIZARD_LOG_FILE } from './paths';\n\n// Dev builds may redirect the log so concurrent runs don't interleave one file.\nlet logFilePath =\n (IS_DEV && process.env.POSTHOG_WIZARD_LOG_FILE) || WIZARD_LOG_FILE;\nlet fileLoggingEnabled = true;\n// An explicit --log-file wins over every later path change while pinned.\nlet logFilePinned = false;\n\nfunction stringify(value: unknown): string {\n if (typeof value === 'string') return value;\n if (value instanceof Error) return value.stack ?? String(value);\n try {\n // JSON.stringify throws on cycles and skips some values — fall back to\n // inspect so a crash log line is never dropped.\n return JSON.stringify(value, null, 2) ?? inspect(value, { depth: 3 });\n } catch {\n return inspect(value, { depth: 3 });\n }\n}\n\n/** One log line from `logToFile`-style arguments: strings as they are, errors as their stack, the rest as JSON. */\nexport function formatLogLine(...args: readonly unknown[]): string {\n return args.map(stringify).join(' ');\n}\n\nexport function getLogFilePath(): string {\n return logFilePath;\n}\n\nexport function configureLogFile(opts: {\n path?: string;\n enabled?: boolean;\n pin?: boolean;\n}): void {\n if (opts.pin === false) logFilePinned = false;\n if (opts.path !== undefined && !logFilePinned) {\n logFilePath = opts.path;\n ensuredLogDir = false;\n }\n if (opts.pin === true) logFilePinned = true;\n if (opts.enabled !== undefined) fileLoggingEnabled = opts.enabled;\n}\n\nlet ensuredLogDir = false;\nlet reportedLogFailure = false;\n\n// Failed log writes go to error tracking, once per process. Dynamic import:\n// analytics logs through this module, so a static import would be a cycle.\nfunction reportLogFailureOnce(err: unknown): void {\n if (reportedLogFailure) return;\n reportedLogFailure = true;\n void import('./analytics')\n .then(({ analytics }) =>\n analytics.captureException(\n err instanceof Error ? err : new Error(String(err)),\n {\n source: 'log-file-write',\n log_path: logFilePath,\n platform: process.platform,\n },\n ),\n )\n .catch(() => {\n // Reporting must never crash the wizard either.\n });\n}\n\n// The log's directory isn't guaranteed to exist (Windows %TEMP%,\n// POSTHOG_WIZARD_LOG_DIR) — create it on first failure.\nfunction appendLine(text: string): void {\n try {\n appendFileSync(logFilePath, text);\n } catch (err) {\n if (ensuredLogDir) {\n reportLogFailureOnce(err);\n return;\n }\n ensuredLogDir = true;\n try {\n mkdirSync(path.dirname(logFilePath), { recursive: true });\n appendFileSync(logFilePath, text);\n } catch (retryErr) {\n reportLogFailureOnce(retryErr);\n }\n }\n}\n\nexport function configureLogFileFromEnvironment(): void {\n const dir = runtimeEnv('POSTHOG_WIZARD_LOG_DIR');\n if (dir) {\n configureLogFile({ path: path.join(dir, 'posthog-wizard.log') });\n }\n}\n\nexport function initLogFile(): void {\n if (!fileLoggingEnabled) return;\n const divider = '='.repeat(60);\n appendLine(\n `\\n${divider}\\nPostHog Wizard Run: ${new Date().toISOString()}\\n${divider}\\n`,\n );\n}\n\nexport function logToFile(...args: unknown[]): void {\n if (!fileLoggingEnabled) return;\n const ts = new Date().toISOString();\n appendLine(`[${ts}] ${formatLogLine(...args)}\\n`);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"debug-BvEbemym.js","names":["join","sep"],"sources":["../src/env.ts","../src/shared/version.ts","../src/shared/utils/paths.ts","../src/shared/utils/debug.ts"],"sourcesContent":["/**\n * Central environment variable access for the PostHog wizard.\n *\n * ── Build-time constants ────────────────────────────────────────────\n * Inlined by tsdown's `env` option at compile time. After build, the\n * runtime value of these env vars has zero effect on the wizard.\n *\n * ── Runtime variables ───────────────────────────────────────────────\n * Read through `runtimeEnv()` with a typed allowlist. This makes every\n * runtime dependency on the environment explicit and grep-able.\n *\n * ── Direct process.env access ───────────────────────────────────────\n * Reserved for subprocess environment configuration (writes) and\n * vendored code. Production source outside those cases should use\n * this module instead.\n */\n\n/**\n * The on-CLI flag name. Intentionally ugly + undocumented; do not surface it in\n * `--help`, the README, or user-facing error messages.\n */\nexport const HEADLESS_FLAG = 'headless-DONOTUSE-EXPERIMENTAL';\n\n// ── Build-time constants ─────────────────────────────────────────────\n// tsdown replaces `process.env.NODE_ENV` with a string literal.\n// After build these are just `\"production\"`, `false`, etc.\n\nexport const NODE_ENV = process.env.NODE_ENV as string;\nexport const IS_DEV =\n process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'test';\n\n/**\n * True only in published/production builds. tsdown inlines\n * `process.env.NODE_ENV` as the literal `\"production\"` at build time, so this\n * collapses to `true` in `dist/` and stays `false` for `tsx`/dev/test runs\n * (where NODE_ENV is unset, `development`, or `test`). Used to gate features\n * that aren't supported in the shipped package — e.g. `--ci` mode.\n */\nexport const IS_PRODUCTION_BUILD = process.env.NODE_ENV === 'production';\n\n/** 'cloud' when launched with the experimental headless flag, else 'local' — resolved once from argv at launch. */\nexport const RUN_SURFACE: 'cloud' | 'local' = process.argv.some(\n (a) => a === `--${HEADLESS_FLAG}` || a.startsWith(`--${HEADLESS_FLAG}=`),\n)\n ? 'cloud'\n : 'local';\n\n// ── Runtime environment ──────────────────────────────────────────────\n\n/**\n * Exhaustive allowlist of env vars the wizard reads at runtime.\n * Add new keys here when a new runtime dependency is needed.\n */\ntype RuntimeEnvKey =\n // CI-build-only flag overrides (see utils/ci-flag-overrides.ts).\n // Deliberately NOT POSTHOG_WIZARD_-prefixed: yargs .env('POSTHOG_WIZARD')\n // would claim it as an unknown CLI option and strict-reject the run.\n | 'WIZARD_CI_FLAG_OVERRIDES'\n | 'WIZARD_CI_EXCLUDE_TASKS'\n | 'WIZARD_CI_GATEWAY_TOKEN_FILE'\n | 'WIZARD_CI_GATEWAY_URL'\n // Wizard CLI configuration (yargs POSTHOG_WIZARD_ prefix)\n | 'POSTHOG_WIZARD_BENCHMARK_CONFIG'\n | 'POSTHOG_WIZARD_BENCHMARK_FILE'\n | 'POSTHOG_WIZARD_LOG_DIR'\n | 'POSTHOG_WIZARD_DEBUG'\n // Identity of the PostHog task run whose sandbox launched this wizard. Set by\n // the sandbox for every run it starts, agent or wizard. Deliberately NOT\n // POSTHOG_WIZARD_-prefixed, for the same reason as the two keys above:\n // yargs .env('POSTHOG_WIZARD') would claim them as CLI options and\n // .strictOptions() would reject the run unless the wizard also declared them,\n // which would tie every sandbox deploy to an npm release of this package.\n | 'POSTHOG_WIZARD_RUN_ID'\n | 'POSTHOG_TASK_RUN_ID'\n | 'POSTHOG_TASK_ID'\n | 'POSTHOG_HANDOFF_OUTPUT_PATH'\n // Local/CI escape hatch to disable Warlock scanning without the PostHog flag.\n | 'POSTHOG_WIZARD_WARLOCK_DISABLED'\n | 'DEBUG'\n // Agent / MCP\n | 'MCP_URL'\n | 'POSTHOG_API_KEY'\n // Platform: terminal detection\n | 'TERM'\n | 'TERM_PROGRAM'\n | 'TERMINAL_EMULATOR'\n | 'CI'\n | 'WT_SESSION'\n | 'TERMINUS_SUBLIME'\n | 'ConEmuTask'\n // Platform: paths\n | 'APPDATA'\n | 'OPENCODE_CONFIG_DIR'\n | 'XDG_CONFIG_HOME';\n\n/** Read a runtime environment variable. Only allowlisted keys compile. */\nexport function runtimeEnv(key: RuntimeEnvKey): string | undefined {\n return process.env[key];\n}\n\n/**\n * The PostHog task run that launched this wizard, when one did. A cloud run's\n * sandbox exports both ids into the wizard's environment; every local run\n * leaves them undefined. Resolved once at launch, like RUN_SURFACE above.\n */\nexport const TASK_RUN_ID: string | undefined =\n runtimeEnv('POSTHOG_TASK_RUN_ID') || undefined;\nexport const TASK_ID: string | undefined =\n runtimeEnv('POSTHOG_TASK_ID') || undefined;\n","// Auto-generated by scripts/generate-version.js — do not edit\nexport const VERSION = '2.82.0';\n","import { tmpdir } from 'node:os';\nimport { join, sep } from 'node:path';\n\n// /tmp is stable and discoverable on macOS/Linux; Windows needs os.tmpdir()\nconst TMP = process.platform === 'win32' ? tmpdir() : '/tmp';\n\nexport const WIZARD_LOG_FILE = join(TMP, 'posthog-wizard.log');\nexport const WIZARD_BENCHMARK_FILE = join(TMP, 'posthog-wizard-benchmark.json');\n/** Default target of `--task-stream-log`. Dev builds only. */\nexport const WIZARD_TASK_STREAM_FILE = join(\n TMP,\n 'posthog-wizard-task-stream.jsonl',\n);\nexport const WIZARD_YARA_REPORT_FILE = join(\n TMP,\n 'posthog-wizard-yara-report.json',\n);\n/**\n * Strip an absolute installDir prefix off a project file path so the UI\n * renders `index.js:12` instead of `/Users/.../index.js:12`. Defends\n * against false matches like `/Users/foo` ⊂ `/Users/foobar/x.js` by\n * normalizing to a trailing path separator before the prefix check.\n */\nexport function relativeToInstallDir(file: string, installDir: string): string {\n const prefix = installDir.endsWith(sep) ? installDir : installDir + sep;\n return file.startsWith(prefix) ? file.slice(prefix.length) : file;\n}\n","import { appendFileSync, mkdirSync } from 'fs';\nimport path from 'path';\nimport { inspect } from 'node:util';\nimport { IS_DEV, runtimeEnv } from '@env';\nimport { WIZARD_LOG_FILE } from './paths';\n\n// Dev builds may redirect the log so concurrent runs don't interleave one file.\nlet logFilePath =\n (IS_DEV && process.env.POSTHOG_WIZARD_LOG_FILE) || WIZARD_LOG_FILE;\nlet fileLoggingEnabled = true;\n// An explicit --log-file wins over every later path change while pinned.\nlet logFilePinned = false;\n\nfunction stringify(value: unknown): string {\n if (typeof value === 'string') return value;\n if (value instanceof Error) return value.stack ?? String(value);\n try {\n // JSON.stringify throws on cycles and skips some values — fall back to\n // inspect so a crash log line is never dropped.\n return JSON.stringify(value, null, 2) ?? inspect(value, { depth: 3 });\n } catch {\n return inspect(value, { depth: 3 });\n }\n}\n\n/** One log line from `logToFile`-style arguments: strings as they are, errors as their stack, the rest as JSON. */\nexport function formatLogLine(...args: readonly unknown[]): string {\n return args.map(stringify).join(' ');\n}\n\nexport function getLogFilePath(): string {\n return logFilePath;\n}\n\nexport function configureLogFile(opts: {\n path?: string;\n enabled?: boolean;\n pin?: boolean;\n}): void {\n if (opts.pin === false) logFilePinned = false;\n if (opts.path !== undefined && !logFilePinned) {\n logFilePath = opts.path;\n ensuredLogDir = false;\n }\n if (opts.pin === true) logFilePinned = true;\n if (opts.enabled !== undefined) fileLoggingEnabled = opts.enabled;\n}\n\nlet ensuredLogDir = false;\nlet reportedLogFailure = false;\n\n// Failed log writes go to error tracking, once per process. Dynamic import:\n// analytics logs through this module, so a static import would be a cycle.\nfunction reportLogFailureOnce(err: unknown): void {\n if (reportedLogFailure) return;\n reportedLogFailure = true;\n void import('./analytics')\n .then(({ analytics }) =>\n analytics.captureException(\n err instanceof Error ? err : new Error(String(err)),\n {\n source: 'log-file-write',\n log_path: logFilePath,\n platform: process.platform,\n },\n ),\n )\n .catch(() => {\n // Reporting must never crash the wizard either.\n });\n}\n\n// The log's directory isn't guaranteed to exist (Windows %TEMP%,\n// POSTHOG_WIZARD_LOG_DIR) — create it on first failure.\nfunction appendLine(text: string): void {\n try {\n appendFileSync(logFilePath, text);\n } catch (err) {\n if (ensuredLogDir) {\n reportLogFailureOnce(err);\n return;\n }\n ensuredLogDir = true;\n try {\n mkdirSync(path.dirname(logFilePath), { recursive: true });\n appendFileSync(logFilePath, text);\n } catch (retryErr) {\n reportLogFailureOnce(retryErr);\n }\n }\n}\n\nexport function configureLogFileFromEnvironment(): void {\n const dir = runtimeEnv('POSTHOG_WIZARD_LOG_DIR');\n if (dir) {\n configureLogFile({ path: path.join(dir, 'posthog-wizard.log') });\n }\n}\n\nexport function initLogFile(): void {\n if (!fileLoggingEnabled) return;\n const divider = '='.repeat(60);\n appendLine(\n `\\n${divider}\\nPostHog Wizard Run: ${new Date().toISOString()}\\n${divider}\\n`,\n );\n}\n\nexport function logToFile(...args: unknown[]): void {\n if (!fileLoggingEnabled) return;\n const ts = new Date().toISOString();\n appendLine(`[${ts}] ${formatLogLine(...args)}\\n`);\n}\n"],"mappings":"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"}
@@ -1,9 +1,9 @@
1
1
  import { n as ErrorCodes } from "./emit-BqLCdV8Y.js";
2
- import { o as logToFile } from "./debug-sB20p-Ek.js";
3
- import { l as AWS_SKILLS_BASE_URL, n as analytics, y as GITHUB_SKILLS_BASE_URL } from "./analytics-CGGE7OSa.js";
2
+ import { o as logToFile } from "./debug-BvEbemym.js";
3
+ import { l as AWS_SKILLS_BASE_URL, n as analytics, y as GITHUB_SKILLS_BASE_URL } from "./analytics-B_xFshPY.js";
4
4
  import { a as WizardError } from "./errors-DhFeR8Sp.js";
5
- import { i as oauthLoginKey } from "./oauth-session-C2fVSGfr.js";
6
- import { i as modelCapabilities } from "./models-G1j-wgwe.js";
5
+ import { a as oauthLoginKey } from "./oauth-session-Bi1MLRCC.js";
6
+ import { i as modelCapabilities } from "./models-C9rjXPZ6.js";
7
7
  //#region src/shared/health-checks/endpoints.ts
8
8
  function noConnectionResult(error, attempts) {
9
9
  return {
@@ -565,4 +565,4 @@ function withGatewayRemint(opts) {
565
565
  //#endregion
566
566
  export { gatewayTerminalFailure as a, REMARK_INSTRUCTION as c, gatewayAuth as d, isPastRefresh as f, gatewayApiFor as i, RESUME_INSTRUCTION as l, checkSkillsOriginHealth as m, buildGatewayModel as n, withGatewayRemint as o, useRunGatewayCredential as p, buildGatewayProvider as r, AgentSignals as s, GATEWAY_PROVIDER as t, buildWizardPropertiesBlob as u };
567
567
 
568
- //# sourceMappingURL=gateway-Dg4Fm-nC.js.map
568
+ //# sourceMappingURL=gateway-CWoYlPz7.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"gateway-Dg4Fm-nC.js","names":[],"sources":["../src/shared/health-checks/endpoints.ts","../src/agent/gateway-session.ts","../src/agent/signals.ts","../src/agent/runner/harness/pi/gateway.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","/**\n * Gateway auth for a wizard run: a `phe_` scoped token the backend mints, with\n * pinned attribution, a spend cap and an expiry.\n *\n * Every mint failure throws: a silent downgrade would spend uncapped,\n * unattributed money to hide an outage.\n */\n\nimport { logToFile } from '@utils/debug';\nimport { analytics } from '@utils/analytics';\nimport { ErrorCodes, WizardError } from '@shared/errors';\nimport type { GatewayCredential } from '@shared/api';\nimport type { HostResolution } from '@shared/host-resolution';\nimport { oauthLoginKey } from '@shared/oauth-session';\nimport { checkLlmGatewayHealth } from '@shared/health-checks/endpoints';\nimport { ServiceHealthStatus } from '@shared/health-checks/types';\nimport { IS_PRODUCTION_BUILD } from '@env';\n\nexport interface GatewayAuth {\n /** Base URL for model calls (no `/v1`; transports append their route). */\n gatewayUrl: string;\n /** Gateway bearer, minted normally or supplied directly by CI. */\n token: string;\n /** Team verified by the mint, or explicitly supplied for CI attribution. */\n teamId?: number;\n /**\n * Instant past which a 401 on this bearer is age rather than a bad\n * credential: the cache re-mints past it, and a session still holding the\n * old bearer may re-mint once. Before it the mint has to be trusted.\n */\n refreshAtMs: number;\n}\n\ninterface CachedAuth {\n key: string;\n auth: GatewayAuth;\n /** Re-resolve once past this instant. */\n staleAtMs: number;\n}\n\nlet cached: CachedAuth | null = null;\n/**\n * Shared so concurrent callers mint once. The orchestrator starts every runnable\n * task at once, and each would otherwise take its own token and its own cap.\n */\nlet inFlight: { key: string; promise: Promise<GatewayAuth> } | null = null;\nlet ciAuth: GatewayAuth | null = null;\n\n// Snapshot CI supplies a gateway bearer without minting or re-minting.\nexport function configureGatewayCredentialsForCI(\n token: string,\n projectId: number,\n gatewayUrl: string,\n): void {\n if (IS_PRODUCTION_BUILD)\n throw new Error('CI gateway auth requires a non-production build');\n if (!token.trim() || !Number.isSafeInteger(projectId) || projectId <= 0) {\n throw new Error('CI gateway auth requires a token and valid project ID');\n }\n if (\n !/^https?:\\/\\//.test(gatewayUrl) ||\n !isTrustedGatewayUrl(gatewayUrl, '')\n ) {\n throw new Error('CI gateway auth requires a trusted gateway origin');\n }\n resetGatewaySession();\n ciAuth = {\n token: token.trim(),\n teamId: projectId,\n gatewayUrl: gatewayUrl.replace(/\\/+$/, ''),\n refreshAtMs: Infinity,\n };\n}\n\n/** Use this run's pre-issued gateway token, or mint when it has none; the keyed mint cache stays. */\nexport function useRunGatewayCredential(\n gateway: GatewayCredential | undefined,\n projectId: number,\n): void {\n if (gateway) {\n configureGatewayCredentialsForCI(gateway.token, projectId, gateway.url);\n return;\n }\n ciAuth = null;\n}\n\n/**\n * Adoption floor. The anthropic subprocess holds its credential until a 401\n * forces a re-mint, so a token below this would churn mints.\n */\nconst MIN_USABLE_TTL_MS = 2 * 60 * 1000;\n/** Re-resolve at this fraction of the token's life, leaving a usable remainder. */\nconst REFRESH_AT_FRACTION = 0.8;\n// Exceeds the backend's own 10s gateway timeout: a slow mint that lands after the\n// CLI hangs up spends a daily mint and orphans a live token.\nconst MINT_TIMEOUT_MS = 20_000;\n/** Longer than any refusal the mint writes; a body past this is not a message. */\nconst MAX_REFUSAL_DETAIL_LENGTH = 500;\n/** Outcomes are short snake_case labels; anything longer is not one. */\nconst MAX_REFUSAL_OUTCOME_LENGTH = 64;\n\n/** Resolve this run's gateway auth, minting and re-minting near expiry. */\nexport async function gatewayAuth(\n host: HostResolution,\n accessToken: string,\n program: string | undefined,\n): Promise<GatewayAuth> {\n if (ciAuth) return ciAuth;\n // Keyed by program: a token pins `wizard:<program>`, so reusing one across\n // programs bills the wrong budget. Keyed by login rather than OAuth token:\n // the token rotates mid-run, and each new one would spend a weekly mint.\n const key = `${host.apiHost}\\n${oauthLoginKey(accessToken)}\\n${\n program ?? ''\n }`;\n if (cached && cached.key === key && Date.now() < cached.staleAtMs) {\n return cached.auth;\n }\n if (inFlight && inFlight.key === key) return inFlight.promise;\n const promise = resolveGatewayAuth(host, accessToken, key, program);\n inFlight = { key, promise };\n try {\n return await promise;\n } finally {\n if (inFlight?.promise === promise) inFlight = null;\n }\n}\n\nasync function resolveGatewayAuth(\n host: HostResolution,\n accessToken: string,\n key: string,\n program: string | undefined,\n): Promise<GatewayAuth> {\n if (!program) {\n // Every run has one; absent means a caller was not wired, and the spend would\n // be unattributable.\n logToFile('[gateway] run has no program id; failing the run');\n throw new GatewayMintFailed(\n 'this run has no program to attribute its spend to',\n );\n }\n const minted = await mintGatewayToken(host, accessToken, program);\n const health = await checkLlmGatewayHealth(minted.gatewayUrl);\n if (health.status !== ServiceHealthStatus.Healthy) {\n throw new WizardError(\n 'The PostHog AI gateway is unavailable. Please try again later.',\n undefined,\n ErrorCodes.EnvServiceOutage,\n );\n }\n const expiresAtMs = Date.parse(minted.expiresAt);\n const ttlMs = expiresAtMs - Date.now();\n if (!Number.isFinite(expiresAtMs) || ttlMs < MIN_USABLE_TTL_MS) {\n // Expired, unreadable, or too short to serve a session. Adopting it would\n // 401 mid-run, and downgrading would spend the rest of the run uncapped.\n logToFile(\n `[gateway] mint returned a token with ${ttlMs}ms of life; failing the run`,\n );\n throw new GatewayMintFailed(\n `the PostHog gateway issued a token with ${ttlMs}ms of life`,\n );\n }\n const staleAtMs = Date.now() + ttlMs * REFRESH_AT_FRACTION;\n // Only failures are logged otherwise, so a successful run leaves no\n // local trace. Never log the token itself.\n logToFile(\n `[gateway] minted a scoped token: program=${program} team=${\n minted.teamId ?? 'unknown'\n } ttl=${Math.round(ttlMs / 1000)}s url=${minted.gatewayUrl}`,\n );\n const auth: GatewayAuth = {\n gatewayUrl: minted.gatewayUrl,\n token: minted.token,\n teamId: minted.teamId,\n refreshAtMs: staleAtMs,\n };\n cached = { key, auth, staleAtMs };\n return auth;\n}\n\n/** Test hook: drop the cached auth so the next call re-resolves. */\nexport function resetGatewaySession(): void {\n cached = null;\n inFlight = null;\n ciAuth = null;\n}\n\n/** Whether a 401 on this bearer may be age (past its refresh instant) rather than a bad credential. */\nexport function isPastRefresh(auth: GatewayAuth, now = Date.now()): boolean {\n return now >= auth.refreshAtMs;\n}\n\n/**\n * Whether a server-supplied origin may receive a bearer and prompt content:\n * https (loopback excepted), and either a current cloud gateway or the host the run\n * authenticated against.\n */\nexport function isTrustedGatewayUrl(value: string, apiHost: string): boolean {\n let url: URL;\n try {\n url = new URL(value);\n } catch {\n return false;\n }\n // Consumers append routes to this value, so anything beyond an origin\n // (path, query, fragment, userinfo) would build a malformed endpoint.\n if (\n url.pathname !== '/' ||\n url.search ||\n url.hash ||\n url.username ||\n url.password\n ) {\n return false;\n }\n const localhost =\n url.hostname === 'localhost' ||\n url.hostname === '127.0.0.1' ||\n url.hostname === 'host.docker.internal';\n // Loopback is the dev gateway, and is the one case allowed over http.\n if (localhost) return true;\n if (url.protocol !== 'https:') return false;\n if (url.hostname.endsWith('.posthog.com')) {\n return (\n url.origin === 'https://ai-gateway.us.posthog.com' ||\n url.origin === 'https://ai-gateway.eu.posthog.com'\n );\n }\n try {\n return url.hostname === new URL(apiHost).hostname;\n } catch {\n return false;\n }\n}\n\ninterface MintedToken {\n token: string;\n expiresAt: string;\n gatewayUrl: string;\n teamId?: number;\n}\n\n/**\n * A deliberate refusal from the mint endpoint, as opposed to the mint being\n * unavailable. Thrown so the run stops instead of proceeding without the\n * controls the refusal was enforcing. A WizardError, so the runners print its\n * message as-is and `wizardAbort` resolves its code.\n */\nexport class GatewayMintRefused extends WizardError {\n readonly status: number;\n /** The backend's refusal outcome (`blocked`, `throttled`, ...), when it sent one. */\n readonly outcome?: string;\n\n constructor(status: number, message: string, outcome?: string) {\n super(message, { status, outcome }, ErrorCodes.GatewayMintRefused);\n this.name = 'GatewayMintRefused';\n this.status = status;\n this.outcome = outcome;\n }\n}\n\n/**\n * The mint could not produce a usable credential: unreachable, a 5xx, or a\n * response the client cannot use.\n */\nexport class GatewayMintFailed extends WizardError {\n constructor(message: string) {\n super(message, undefined, ErrorCodes.GatewayMintFailed);\n this.name = 'GatewayMintFailed';\n }\n}\n\n/**\n * Whether a mint status means \"refused this run\" rather than \"not available\".\n * 429 the daily run limit, 403 revoked project access, 400 a login covering\n * more than one project, 401 a credential the mint does not accept, 404 an\n * instance without the mint endpoint.\n */\nfunction isMintRefusal(status: number): boolean {\n return (\n status === 400 ||\n status === 401 ||\n status === 403 ||\n status === 404 ||\n status === 429\n );\n}\n\ninterface MintRefusal {\n detail?: string;\n outcome?: string;\n}\n\n/**\n * The server's own reason for a refusal, when it sent one. DRF answers every\n * refusal as `{\"detail\": \"...\", \"code\": \"<outcome>\"}`; the blocklist's detail\n * names the contact address, which the fixed messages below cannot. `code` is\n * the backend's own label for the refusal (`outcome` on older backends) and\n * rides the client event.\n */\nfunction cleanRefusalText(value: unknown): string {\n if (typeof value !== 'string') return '';\n // Not because the server sends escapes, but because this string is printed\n // straight to a terminal: sanitizing at the boundary means no later message\n // can move the cursor or repaint the screen.\n // eslint-disable-next-line no-control-regex\n return value.replace(/[\\u0000-\\u001f\\u007f-\\u009f]/g, ' ').trim();\n}\n\nasync function readRefusal(resp: Response): Promise<MintRefusal> {\n try {\n const body = (await resp.json()) as {\n detail?: unknown;\n code?: unknown;\n outcome?: unknown;\n };\n const detail = cleanRefusalText(body?.detail);\n // The DRF handler flattens a dict detail, so the outcome rides as `code`.\n // A `code` that cleans to nothing does not shadow a usable `outcome`.\n const outcome =\n cleanRefusalText(body?.code) || cleanRefusalText(body?.outcome);\n return {\n detail:\n detail.length > 0 && detail.length <= MAX_REFUSAL_DETAIL_LENGTH\n ? detail\n : undefined,\n outcome:\n outcome.length > 0 && outcome.length <= MAX_REFUSAL_OUTCOME_LENGTH\n ? outcome\n : undefined,\n };\n } catch {\n return {};\n }\n}\n\nfunction mintRefusalMessage(status: number, detail?: string): string {\n if (detail) return detail;\n switch (status) {\n case 429:\n return 'This wizard program has used its daily run limit. Try again tomorrow.';\n case 403:\n return 'Your access to this project has changed. Re-authenticate and try again.';\n case 400:\n // The only 400 the mint answers is the exactly-one-project check.\n return 'Your PostHog login must cover exactly one project. Re-authenticate and try again.';\n case 401:\n return 'PostHog did not accept this login. Re-authenticate with `npx @posthog/wizard@latest`.';\n case 404:\n return 'This PostHog instance does not issue gateway tokens. Upgrade with `npx @posthog/wizard@latest` and try again.';\n default:\n return 'The PostHog gateway refused this run.';\n }\n}\n\nasync function mintGatewayToken(\n host: HostResolution,\n accessToken: string,\n program: string,\n): Promise<MintedToken> {\n try {\n const resp = await fetch(`${host.apiHost}/api/wizard/gateway_token/`, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${accessToken}`,\n 'Content-Type': 'application/json',\n },\n // The flag tells the server this build reads a refusal, so it may answer\n // with the reason. A build that omits it gets a 404, which is its signal\n // to fall back to the legacy gateway.\n body: JSON.stringify({ program, reads_refusal_reason: true }),\n signal: AbortSignal.timeout(MINT_TIMEOUT_MS),\n });\n if (!resp.ok) {\n if (isMintRefusal(resp.status)) {\n const refusal = await readRefusal(resp);\n logToFile(\n `[gateway] mint refused with HTTP ${resp.status} (${\n refusal.outcome ?? 'no outcome'\n }); failing the run`,\n );\n // The terminal denial event for this run. The backend's own event has\n // no run id, so this is what joins a refusal to the session.\n analytics.wizardCapture('gateway mint refused', {\n status: resp.status,\n outcome: refusal.outcome,\n program,\n });\n throw new GatewayMintRefused(\n resp.status,\n mintRefusalMessage(resp.status, refusal.detail),\n refusal.outcome,\n );\n }\n logToFile(\n `[gateway] mint failed with HTTP ${resp.status}; failing the run`,\n );\n throw new GatewayMintFailed(\n `the PostHog gateway could not issue a token (HTTP ${resp.status})`,\n );\n }\n const body = (await resp.json()) as {\n token?: string;\n expires_at?: string;\n gateway_url?: string;\n team_id?: number;\n };\n // Checked one at a time, not in a loop, so each clause narrows the optional\n // field for the return below and each names itself in the failure.\n if (!body.token) {\n logToFile('[gateway] mint response omitted token; failing the run');\n throw new GatewayMintFailed('mint response omitted token');\n }\n if (!body.expires_at) {\n logToFile('[gateway] mint response omitted expires_at; failing the run');\n throw new GatewayMintFailed('mint response omitted expires_at');\n }\n if (!body.gateway_url) {\n logToFile('[gateway] mint response omitted gateway_url; failing the run');\n throw new GatewayMintFailed('mint response omitted gateway_url');\n }\n if (!isTrustedGatewayUrl(body.gateway_url, host.apiHost)) {\n logToFile(\n '[gateway] mint returned an untrusted gateway url; failing the run',\n );\n throw new GatewayMintFailed('mint returned an untrusted gateway url');\n }\n const gatewayUrl = new URL(body.gateway_url);\n if (\n ['localhost', '127.0.0.1'].includes(new URL(host.apiHost).hostname) &&\n gatewayUrl.hostname === 'host.docker.internal'\n ) {\n gatewayUrl.hostname = 'localhost';\n }\n return {\n token: body.token,\n expiresAt: body.expires_at,\n gatewayUrl: gatewayUrl.origin,\n teamId: body.team_id,\n };\n } catch (e) {\n // Decisions and failures both pass through: this catch exists for transport\n // errors, and folding the others into it would lose the reason.\n if (e instanceof GatewayMintRefused || e instanceof GatewayMintFailed)\n throw e;\n logToFile(\n `[gateway] mint transport failure (${String(e)}); failing the run`,\n );\n throw new GatewayMintFailed(\n `could not reach the PostHog gateway (${String(e)})`,\n );\n }\n}\n\n/**\n * The v2 run-metadata carrier: one JSON blob for the `X-PostHog-Properties`\n * header. Plain keys only, since the gateway strips `$`-prefixed keys as reserved,\n * so feature-flag variants land as `wizard_flag_<key>` instead of the legacy\n * `$feature/<key>` (dashboards keying on `$feature/wizard-*` read the new key\n * post-cutover).\n */\nexport function buildWizardPropertiesBlob(\n wizardMetadata: Record<string, string>,\n wizardFlags: Record<string, string>,\n teamId?: number,\n): string {\n // The gateway pins `$ai_product` to `wizard:<program>`, and rejects a legacy\n // product override on a scoped token, so the unprefixed key every cost and\n // error consumer reads is only present if this blob declares it.\n const props: Record<string, string | number> = { ai_product: 'wizard' };\n if (teamId !== undefined) props.team_id = teamId;\n for (const [key, value] of Object.entries(wizardMetadata)) {\n props[stripPropertyPrefix(key)] = value;\n }\n for (const [flagKey, variant] of Object.entries(wizardFlags)) {\n if (!flagKey.toLowerCase().startsWith('wizard')) continue;\n props[`wizard_flag_${flagKey.toLowerCase()}`] = variant;\n }\n return JSON.stringify(props);\n}\n\nconst LEGACY_PROPERTY_PREFIX = 'X-POSTHOG-PROPERTY-';\n\nfunction stripPropertyPrefix(key: string): string {\n return key.toUpperCase().startsWith(LEGACY_PROPERTY_PREFIX)\n ? key.slice(LEGACY_PROPERTY_PREFIX.length).toLowerCase()\n : key;\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 /** A schema-bound run ended without output matching its schema */\n INVALID_STRUCTURED_OUTPUT = 'WIZARD_INVALID_STRUCTURED_OUTPUT',\n /** Agent acted but stopped short — planned tasks left open and/or no skill installed */\n INCOMPLETE_TASKS = 'WIZARD_INCOMPLETE_TASKS',\n}\n","/**\n * PostHog AI gateway provider spec for pi sessions, shared by the linear run\n * and the orchestrator's per-task runs so both speak to the gateway\n * identically: bearer auth, the wizard properties header, transport shape\n * inferred from the model id. The caller registers the spec on its own\n * (lazily imported, properly typed) pi ModelRegistry.\n */\n\nimport {\n buildWizardPropertiesBlob,\n isPastRefresh,\n type GatewayAuth,\n} from '../../../gateway-session';\nimport {\n modelCapabilities,\n type ThinkingLevel,\n} from '../../switchboard/models';\nimport { AgentErrorType } from '../../../signals';\n\n/** Provider registered on the in-memory registry for this run. */\nexport const GATEWAY_PROVIDER = 'posthog-gateway';\n\n/** The pi transports the gateway serves. */\nexport type GatewayApi =\n | 'anthropic-messages'\n | 'openai-completions'\n | 'openai-responses';\n\n/**\n * The gateway speaks two shapes on two endpoints: Anthropic models over\n * `anthropic-messages` (the SDK appends `/v1/messages`, so the base URL has no\n * `/v1`), and OpenAI-class models (`openai/gpt-5`, …) over an OpenAI shape at\n * `/v1/responses` or `/v1/chat/completions` (base URL keeps `/v1`). Infer the\n * shape from the model id so a pair's model selects the right transport.\n *\n * OpenAI models take the Responses API because OpenAI rejects function tools\n * combined with `reasoning_effort` on chat completions and every task sends both.\n */\nexport function gatewayApiFor(modelId: string): GatewayApi {\n return modelId.startsWith('openai/')\n ? 'openai-responses'\n : 'anthropic-messages';\n}\n\n/**\n * Gateway HTTP headers, mirroring `buildAgentEnv` on the anthropic path: one\n * `X-PostHog-Properties` JSON blob (Bedrock fallback is native, so no opt-in)\n * plus the 1M context beta, since pi otherwise runs at 200k and overflows on\n * larger projects (the post-run compaction failures).\n */\nexport function buildGatewayHeaders(\n wizardMetadata: Record<string, string>,\n wizardFlags: Record<string, string>,\n teamId?: number,\n): Record<string, string> {\n return {\n 'anthropic-beta': 'context-1m-2025-08-07',\n 'X-PostHog-Properties': buildWizardPropertiesBlob(\n wizardMetadata,\n wizardFlags,\n teamId,\n ),\n };\n}\n\nexport interface GatewayProviderInputs {\n gatewayUrl: string;\n accessToken: string;\n /** Customer team for the properties blob (from the mint response). */\n teamId?: number;\n wizardMetadata: Record<string, string>;\n wizardFlags: Record<string, string>;\n modelId: string;\n // Resolved effort override — the switchboard's flag/payload pick on linear\n // runs, the prompt-frontmatter effort on per-task agents. Overrides the\n // table default for a reasoning model when set.\n effort?: ThinkingLevel;\n}\n\n/**\n * One gateway model spec — the single description of how to reach a model on\n * the gateway. The provider spec below wraps it for pi's registry; one-shot\n * callers (scan triage) hand it straight to `completeSimple`.\n */\nexport function buildGatewayModel(inputs: GatewayProviderInputs) {\n const { gatewayUrl, wizardMetadata, wizardFlags, modelId, teamId } = inputs;\n const api = gatewayApiFor(modelId);\n return {\n id: modelId,\n name: `${modelId} (PostHog Gateway)`,\n api,\n provider: GATEWAY_PROVIDER,\n // Anthropic drops /v1 (the SDK appends /v1/messages); the OpenAI shape keeps it.\n baseUrl: api === 'anthropic-messages' ? gatewayUrl : `${gatewayUrl}/v1`,\n // A model trait resolved by the switchboard, not a harness guess:\n // non-reasoning openai models reject `reasoning_effort` (gpt-4o → gateway\n // UnsupportedParamsError → the run no-ops).\n reasoning: modelCapabilities(modelId).reasoning,\n input: ['text' as const],\n cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },\n contextWindow: 1_000_000,\n maxTokens: 64_000,\n headers: buildGatewayHeaders(wizardMetadata, wizardFlags, teamId),\n };\n}\n\n/**\n * The provider object for `registry.registerProvider(GATEWAY_PROVIDER, …)`,\n * plus the derived traits the session setup needs (`caps.thinkingLevel`).\n */\nexport function buildGatewayProvider(inputs: GatewayProviderInputs): {\n provider: Record<string, unknown>;\n api: GatewayApi;\n caps: ReturnType<typeof modelCapabilities>;\n gatewayUrl: string;\n baseUrl: string;\n} {\n const { gatewayUrl, accessToken, modelId, effort } = inputs;\n const api = gatewayApiFor(modelId);\n // One resolution point for the model's traits and the run's effort override.\n // pi clamps whatever comes out of here against the levels this spec declares,\n // so a level the spec doesn't carry is silently reduced by the session.\n const tableCaps = modelCapabilities(modelId);\n const caps =\n effort && effort !== 'off' && tableCaps.reasoning\n ? modelCapabilities(modelId, effort)\n : effort === 'off' && tableCaps.reasoning\n ? { ...tableCaps, thinkingLevel: 'off' as const }\n : tableCaps;\n\n const model = buildGatewayModel(inputs);\n const provider = {\n name: 'PostHog Gateway',\n baseUrl: model.baseUrl,\n apiKey: accessToken,\n authHeader: true,\n api,\n headers: model.headers,\n models: [model],\n };\n return { provider, api, caps, gatewayUrl, baseUrl: model.baseUrl };\n}\n\n/** The part of a pi assistant turn that says why it failed. */\nexport interface GatewayTurnError {\n errorMessage?: string;\n diagnostics?: { error?: { name?: string; code?: string | number } }[];\n}\n\nexport type GatewayTerminalFailure = {\n classification:\n | AgentErrorType.API_ERROR\n | AgentErrorType.RATE_LIMIT\n | AgentErrorType.ABORT;\n message: string;\n status?: number;\n};\n\nexport function gatewayTerminalFailure(\n turn: ({ stopReason?: string } & GatewayTurnError) | undefined,\n): GatewayTerminalFailure | undefined {\n if (!turn || (turn.stopReason !== 'error' && turn.stopReason !== 'aborted'))\n return undefined;\n const message =\n turn.errorMessage ||\n (turn.stopReason === 'aborted'\n ? 'Agent turn aborted'\n : 'Gateway request failed');\n if (turn.stopReason === 'aborted') {\n return { classification: AgentErrorType.ABORT, message };\n }\n const codes =\n turn.diagnostics\n ?.map((d) => d.error?.code)\n .filter((code) => code !== undefined) ?? [];\n const structuredStatus = codes\n .map(Number)\n .find((code) => Number.isInteger(code) && code >= 400 && code < 600);\n const rateLimited =\n structuredStatus !== undefined\n ? structuredStatus === 429\n : /\\b429\\b|rate limit/i.test(message);\n return {\n classification: rateLimited\n ? AgentErrorType.RATE_LIMIT\n : AgentErrorType.API_ERROR,\n message,\n status:\n structuredStatus ?? (isGatewayAuthRejection(turn) ? 401 : undefined),\n };\n}\n\n/**\n * Whether a turn's error is the gateway rejecting the bearer. pi attaches the\n * SDK's own error to `diagnostics`, so its code decides when one is present.\n * The message match is the fallback for a turn that failed before pi built a\n * diagnostic, where the status survives only as prose.\n */\nexport function isGatewayAuthRejection(\n turn: GatewayTurnError | string | undefined,\n): boolean {\n const { errorMessage, diagnostics } =\n typeof turn === 'string'\n ? { errorMessage: turn, diagnostics: undefined }\n : turn ?? {};\n const structuredStatus = (diagnostics ?? [])\n .map((diagnostic) => Number(diagnostic.error?.code))\n .find((code) => Number.isInteger(code) && code >= 400 && code < 600);\n if (structuredStatus !== undefined) return structuredStatus === 401;\n for (const diagnostic of diagnostics ?? []) {\n const code = diagnostic.error?.code;\n if (code === 401 || code === '401') return true;\n if (/^authentication_?error$/i.test(diagnostic.error?.name ?? ''))\n return true;\n }\n return /\\b401\\b|authentication_error|unauthorized/i.test(errorMessage ?? '');\n}\n\nexport interface GatewayRemintOptions {\n signal?: AbortSignal;\n session: { prompt(text: string): Promise<void> };\n registry: { registerProvider(providerName: string, config: never): void };\n auth: GatewayAuth;\n /** The cache: the same token while fresh, a new mint past the refresh point. */\n refreshAuth: () => Promise<GatewayAuth>;\n providerInputs: (auth: GatewayAuth) => GatewayProviderInputs;\n /** The prompt that resumes the work after a re-mint. */\n continueText: string | (() => string);\n onRemint?: () => void;\n}\n\n/**\n * Wraps a pi session's prompt(): when a turn ends on a 401 from a bearer past\n * its refresh instant, mint once, re-register the provider with the new\n * bearer (pi resolves the apiKey per request), and continue. A 401 on a fresh\n * bearer, or a second one, is left to the harness's normal failure path.\n */\nexport function withGatewayRemint(opts: GatewayRemintOptions): {\n prompt(text: string): Promise<void>;\n /** Feed every assistant `message_end`; the last turn decides. */\n noteAssistantTurn(message: unknown): void;\n terminalFailure(): GatewayTerminalFailure | undefined;\n} {\n let auth = opts.auth;\n let rejected = false;\n let reminted = false;\n let lastTurn: ({ stopReason?: string } & GatewayTurnError) | undefined;\n return {\n noteAssistantTurn(message) {\n lastTurn = message as\n | ({ stopReason?: string } & GatewayTurnError)\n | undefined;\n rejected =\n lastTurn?.stopReason === 'error' && isGatewayAuthRejection(lastTurn);\n },\n terminalFailure: () => gatewayTerminalFailure(lastTurn),\n async prompt(text) {\n if (opts.signal?.aborted) return;\n rejected = false;\n lastTurn = undefined;\n await opts.session.prompt(text);\n if (opts.signal?.aborted || !rejected || reminted || !isPastRefresh(auth))\n return;\n reminted = true;\n auth = await opts.refreshAuth();\n if (opts.signal?.aborted) return;\n opts.registry.registerProvider(\n GATEWAY_PROVIDER,\n buildGatewayProvider(opts.providerInputs(auth)).provider as never,\n );\n opts.onRemint?.();\n rejected = false;\n lastTurn = undefined;\n const next = opts.continueText;\n if (opts.signal?.aborted) return;\n await opts.session.prompt(typeof next === 'function' ? next() : next);\n },\n };\n}\n"],"mappings":";;;;;;;AAMA,SAAS,mBAAmB,OAAe,UAAoC;AAC7E,QAAO;EACL,QAAA;EACA;EACA,cAAc,WAAW,IAAI,YAAY,aAAa,KAAA;EACvD;;AAGH,SAAS,WAAW,OAAiC;AACnD,QAAO;EAAE,QAAA;EAAkC;EAAO;;AAQpD,MAAM,oBAAoB,CAAC,KAAK,IAAK;AAErC,eAAe,aACb,KACA,WACA,UAIA;CACA,MAAM,aAAa,IAAI,iBAAiB;CACxC,MAAM,MAAM,iBAAiB,WAAW,OAAO,EAAE,UAAU;AAC3D,KAAI;EACF,MAAM,MAAM,MAAM,MAAM,KAAK;GAAE,QAAQ,WAAW;GAAQ;GAAU,CAAC;AACrE,eAAa,IAAI;AACjB,SAAO;GAAE,MAAM;GAAY;GAAK;UACzB,GAAG;AACV,eAAa,IAAI;EACjB,MAAM,MAAM,aAAa,QAAQ,oBAAI,IAAI,MAAM,gBAAgB;AAC/D,SAAO;GAAE,MAAM;GAAS,OAAO;GAAK,UAAU,IAAI,SAAS;GAAc;;;AAK7E,eAAsB,oBACpB,KACA,YAAY,KACZ,oBAAiD,MAAM,MAAM,KAC7D,WAA0C,UACf;CAW3B,IAAI,iBAAgC;CACpC,IAAI,YAAY;CAChB,IAAI,WAAW;AAEf,MAAK,IAAI,IAAI,GAAG,KAAK,kBAAkB,QAAQ,KAAK;AAClD,MAAI,IAAI,GAAG;GACT,MAAM,OAAO,kBAAkB,IAAI;AACnC,aACE,yBAAyB,EAAE,GAAG,kBAAkB,OAAO,OAAO,IAAI,MAAM,KAAK,YAAY,UAAU,GACpG;AACD,SAAM,IAAI,SAAS,MAAM,WAAW,GAAG,KAAK,CAAC;;AAE/C;EAEA,MAAM,UAAU,MAAM,aAAa,KAAK,WAAW,SAAS;AAE5D,MAAI,QAAQ,SAAS,YAAY;GAC/B,MAAM,MAAM,QAAQ;AACpB,OAAI,iBAAiB,IAAI,OAAO,EAAE;IAChC,MAAM,SAA2B;KAC/B,QAAA;KACA,cACE,WAAW,IACP,QAAQ,IAAI,OAAO,aAAa,SAAS,KACzC,QAAQ,IAAI;KACnB;AACD,cACE,uBAAuB,IAAI,MAAM,OAAO,OAAA,IACjC,OAAO,aAAa,GAC5B;AACD,WAAO;;AAET,oBAAiB,IAAI;AACrB,eAAY,QAAQ,IAAI;AACxB;;AAGF,cAAY,QAAQ,WAChB,2BAA2B,UAAU,MACrC,QAAQ,MAAM;;CAGpB,MAAM,SACJ,mBAAmB,OACf,WAAW,QAAQ,eAAe,aAAa,SAAS,GAAG,GAC3D,mBAAmB,WAAW,SAAS;AAC7C,WACE,uBAAuB,IAAI,MAAM,OAAO,OAAA,aACxB,SAAS,IAAI,OAAO,MAAM,GAC3C;AACD,QAAO;;AAGT,MAAa,yBACX,eAEA,oBAAoB,IAAI,IAAI,WAAW,WAAW,CAAC,KAAK;;;;;;;AAQ1D,MAAa,0BAA0B,YAAuC;CAC5E,MAAM,CAAC,QAAQ,OAAO,MAAM,QAAQ,IAAI,CACtC,oBAAoB,GAAG,uBAAuB,kBAAkB,EAChE,oBAAoB,GAAG,oBAAoB,kBAAkB,CAC9D,CAAC;AACF,QAAO,oBAAoB,QAAQ,IAAI;;;;;;;AAQzC,SAAS,oBACP,QACA,KACkB;AAClB,KAAI,OAAO,WAAA,UAGT,QAAO,IAAI,WAAA,YACP,SACA,oBAAoB,QAAQ,kBAAkB;AAGpD,KAAI,IAAI,WAAA,UACN,QAAO,oBAAoB,KAAK,8BAA8B;CAGhE,MAAM,QAAQ,WAAW,OAAO,SAAS,UAAU,UACjD,IAAI,SAAS;AAKf,QAAO;EACL,QAHA,OAAO,WAAA,UACP,IAAI,WAAA,SAAA,SAAA;EAKJ;EAEA,cAAc,OAAO,gBAAgB,IAAI;EAC1C;;AAGH,SAAS,oBACP,QACA,QACkB;AAClB,QAAO;EACL,GAAG;EACH,cAAc,OAAO,eACjB,GAAG,OAAO,aAAa,IAAI,OAAO,KAClC;EACL;;;;;;;;;;;AC7IH,IAAI,SAA4B;;;;;AAKhC,IAAI,WAAkE;AACtE,IAAI,SAA6B;AAGjC,SAAgB,iCACd,OACA,WACA,YACM;AAEJ,OAAM,IAAI,MAAM,kDAAkD;;;AAoBtE,SAAgB,wBACd,SACA,WACM;AACN,KAAI,SAAS;AACX,mCAAiC,QAAQ,OAAO,WAAW,QAAQ,IAAI;AACvE;;AAEF,UAAS;;;;;;AAOX,MAAM,oBAAoB,MAAS;;AAEnC,MAAM,sBAAsB;AAG5B,MAAM,kBAAkB;;AAExB,MAAM,4BAA4B;;AAElC,MAAM,6BAA6B;;AAGnC,eAAsB,YACpB,MACA,aACA,SACsB;AACtB,KAAI,OAAQ,QAAO;CAInB,MAAM,MAAM,GAAG,KAAK,QAAQ,IAAI,cAAc,YAAY,CAAC,IACzD,WAAW;AAEb,KAAI,UAAU,OAAO,QAAQ,OAAO,KAAK,KAAK,GAAG,OAAO,UACtD,QAAO,OAAO;AAEhB,KAAI,YAAY,SAAS,QAAQ,IAAK,QAAO,SAAS;CACtD,MAAM,UAAU,mBAAmB,MAAM,aAAa,KAAK,QAAQ;AACnE,YAAW;EAAE;EAAK;EAAS;AAC3B,KAAI;AACF,SAAO,MAAM;WACL;AACR,MAAI,UAAU,YAAY,QAAS,YAAW;;;AAIlD,eAAe,mBACb,MACA,aACA,KACA,SACsB;AACtB,KAAI,CAAC,SAAS;AAGZ,YAAU,mDAAmD;AAC7D,QAAM,IAAI,kBACR,oDACD;;CAEH,MAAM,SAAS,MAAM,iBAAiB,MAAM,aAAa,QAAQ;AAEjE,MADe,MAAM,sBAAsB,OAAO,WAAW,EAClD,WAAA,UACT,OAAM,IAAI,YACR,kEACA,KAAA,GACA,WAAW,iBACZ;CAEH,MAAM,cAAc,KAAK,MAAM,OAAO,UAAU;CAChD,MAAM,QAAQ,cAAc,KAAK,KAAK;AACtC,KAAI,CAAC,OAAO,SAAS,YAAY,IAAI,QAAQ,mBAAmB;AAG9D,YACE,wCAAwC,MAAM,6BAC/C;AACD,QAAM,IAAI,kBACR,2CAA2C,MAAM,YAClD;;CAEH,MAAM,YAAY,KAAK,KAAK,GAAG,QAAQ;AAGvC,WACE,4CAA4C,QAAQ,QAClD,OAAO,UAAU,UAClB,OAAO,KAAK,MAAM,QAAQ,IAAK,CAAC,QAAQ,OAAO,aACjD;CACD,MAAM,OAAoB;EACxB,YAAY,OAAO;EACnB,OAAO,OAAO;EACd,QAAQ,OAAO;EACf,aAAa;EACd;AACD,UAAS;EAAE;EAAK;EAAM;EAAW;AACjC,QAAO;;;AAWT,SAAgB,cAAc,MAAmB,MAAM,KAAK,KAAK,EAAW;AAC1E,QAAO,OAAO,KAAK;;;;;;;AAQrB,SAAgB,oBAAoB,OAAe,SAA0B;CAC3E,IAAI;AACJ,KAAI;AACF,QAAM,IAAI,IAAI,MAAM;SACd;AACN,SAAO;;AAIT,KACE,IAAI,aAAa,OACjB,IAAI,UACJ,IAAI,QACJ,IAAI,YACJ,IAAI,SAEJ,QAAO;AAOT,KAJE,IAAI,aAAa,eACjB,IAAI,aAAa,eACjB,IAAI,aAAa,uBAEJ,QAAO;AACtB,KAAI,IAAI,aAAa,SAAU,QAAO;AACtC,KAAI,IAAI,SAAS,SAAS,eAAe,CACvC,QACE,IAAI,WAAW,uCACf,IAAI,WAAW;AAGnB,KAAI;AACF,SAAO,IAAI,aAAa,IAAI,IAAI,QAAQ,CAAC;SACnC;AACN,SAAO;;;;;;;;;AAiBX,IAAa,qBAAb,cAAwC,YAAY;CAClD;;CAEA;CAEA,YAAY,QAAgB,SAAiB,SAAkB;AAC7D,QAAM,SAAS;GAAE;GAAQ;GAAS,EAAE,WAAW,mBAAmB;AAClE,OAAK,OAAO;AACZ,OAAK,SAAS;AACd,OAAK,UAAU;;;;;;;AAQnB,IAAa,oBAAb,cAAuC,YAAY;CACjD,YAAY,SAAiB;AAC3B,QAAM,SAAS,KAAA,GAAW,WAAW,kBAAkB;AACvD,OAAK,OAAO;;;;;;;;;AAUhB,SAAS,cAAc,QAAyB;AAC9C,QACE,WAAW,OACX,WAAW,OACX,WAAW,OACX,WAAW,OACX,WAAW;;;;;;;;;AAgBf,SAAS,iBAAiB,OAAwB;AAChD,KAAI,OAAO,UAAU,SAAU,QAAO;AAKtC,QAAO,MAAM,QAAQ,iCAAiC,IAAI,CAAC,MAAM;;AAGnE,eAAe,YAAY,MAAsC;AAC/D,KAAI;EACF,MAAM,OAAQ,MAAM,KAAK,MAAM;EAK/B,MAAM,SAAS,iBAAiB,MAAM,OAAO;EAG7C,MAAM,UACJ,iBAAiB,MAAM,KAAK,IAAI,iBAAiB,MAAM,QAAQ;AACjE,SAAO;GACL,QACE,OAAO,SAAS,KAAK,OAAO,UAAU,4BAClC,SACA,KAAA;GACN,SACE,QAAQ,SAAS,KAAK,QAAQ,UAAU,6BACpC,UACA,KAAA;GACP;SACK;AACN,SAAO,EAAE;;;AAIb,SAAS,mBAAmB,QAAgB,QAAyB;AACnE,KAAI,OAAQ,QAAO;AACnB,SAAQ,QAAR;EACE,KAAK,IACH,QAAO;EACT,KAAK,IACH,QAAO;EACT,KAAK,IAEH,QAAO;EACT,KAAK,IACH,QAAO;EACT,KAAK,IACH,QAAO;EACT,QACE,QAAO;;;AAIb,eAAe,iBACb,MACA,aACA,SACsB;AACtB,KAAI;EACF,MAAM,OAAO,MAAM,MAAM,GAAG,KAAK,QAAQ,6BAA6B;GACpE,QAAQ;GACR,SAAS;IACP,eAAe,UAAU;IACzB,gBAAgB;IACjB;GAID,MAAM,KAAK,UAAU;IAAE;IAAS,sBAAsB;IAAM,CAAC;GAC7D,QAAQ,YAAY,QAAQ,gBAAgB;GAC7C,CAAC;AACF,MAAI,CAAC,KAAK,IAAI;AACZ,OAAI,cAAc,KAAK,OAAO,EAAE;IAC9B,MAAM,UAAU,MAAM,YAAY,KAAK;AACvC,cACE,oCAAoC,KAAK,OAAO,IAC9C,QAAQ,WAAW,aACpB,oBACF;AAGD,cAAU,cAAc,wBAAwB;KAC9C,QAAQ,KAAK;KACb,SAAS,QAAQ;KACjB;KACD,CAAC;AACF,UAAM,IAAI,mBACR,KAAK,QACL,mBAAmB,KAAK,QAAQ,QAAQ,OAAO,EAC/C,QAAQ,QACT;;AAEH,aACE,mCAAmC,KAAK,OAAO,mBAChD;AACD,SAAM,IAAI,kBACR,qDAAqD,KAAK,OAAO,GAClE;;EAEH,MAAM,OAAQ,MAAM,KAAK,MAAM;AAQ/B,MAAI,CAAC,KAAK,OAAO;AACf,aAAU,yDAAyD;AACnE,SAAM,IAAI,kBAAkB,8BAA8B;;AAE5D,MAAI,CAAC,KAAK,YAAY;AACpB,aAAU,8DAA8D;AACxE,SAAM,IAAI,kBAAkB,mCAAmC;;AAEjE,MAAI,CAAC,KAAK,aAAa;AACrB,aAAU,+DAA+D;AACzE,SAAM,IAAI,kBAAkB,oCAAoC;;AAElE,MAAI,CAAC,oBAAoB,KAAK,aAAa,KAAK,QAAQ,EAAE;AACxD,aACE,oEACD;AACD,SAAM,IAAI,kBAAkB,yCAAyC;;EAEvE,MAAM,aAAa,IAAI,IAAI,KAAK,YAAY;AAC5C,MACE,CAAC,aAAa,YAAY,CAAC,SAAS,IAAI,IAAI,KAAK,QAAQ,CAAC,SAAS,IACnE,WAAW,aAAa,uBAExB,YAAW,WAAW;AAExB,SAAO;GACL,OAAO,KAAK;GACZ,WAAW,KAAK;GAChB,YAAY,WAAW;GACvB,QAAQ,KAAK;GACd;UACM,GAAG;AAGV,MAAI,aAAa,sBAAsB,aAAa,kBAClD,OAAM;AACR,YACE,qCAAqC,OAAO,EAAE,CAAC,oBAChD;AACD,QAAM,IAAI,kBACR,wCAAwC,OAAO,EAAE,CAAC,GACnD;;;;;;;;;;AAWL,SAAgB,0BACd,gBACA,aACA,QACQ;CAIR,MAAM,QAAyC,EAAE,YAAY,UAAU;AACvE,KAAI,WAAW,KAAA,EAAW,OAAM,UAAU;AAC1C,MAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,eAAe,CACvD,OAAM,oBAAoB,IAAI,IAAI;AAEpC,MAAK,MAAM,CAAC,SAAS,YAAY,OAAO,QAAQ,YAAY,EAAE;AAC5D,MAAI,CAAC,QAAQ,aAAa,CAAC,WAAW,SAAS,CAAE;AACjD,QAAM,eAAe,QAAQ,aAAa,MAAM;;AAElD,QAAO,KAAK,UAAU,MAAM;;AAG9B,MAAM,yBAAyB;AAE/B,SAAS,oBAAoB,KAAqB;AAChD,QAAO,IAAI,aAAa,CAAC,WAAW,uBAAuB,GACvD,IAAI,MAAM,GAA8B,CAAC,aAAa,GACtD;;;;;;;;;ACheN,MAAa,eAAe;CAE1B,QAAQ;CAER,mBAAmB;CAEnB,wBAAwB;CAQxB,sBAAsB;CAMtB,OAAO;CAEP,eAAe;CAEf,WAAW;CAMX,eAAe;CAMf,cAAc;CACf;;;;;;;AAUD,MAAa,qBAAqB,6CAA6C,aAAa,cAAc;;;;;AAM1G,MAAa,qBACX;;;;;;;;;;;ACxCF,MAAa,mBAAmB;;;;;;;;;;;AAkBhC,SAAgB,cAAc,SAA6B;AACzD,QAAO,QAAQ,WAAW,UAAU,GAChC,qBACA;;;;;;;;AASN,SAAgB,oBACd,gBACA,aACA,QACwB;AACxB,QAAO;EACL,kBAAkB;EAClB,wBAAwB,0BACtB,gBACA,aACA,OACD;EACF;;;;;;;AAsBH,SAAgB,kBAAkB,QAA+B;CAC/D,MAAM,EAAE,YAAY,gBAAgB,aAAa,SAAS,WAAW;CACrE,MAAM,MAAM,cAAc,QAAQ;AAClC,QAAO;EACL,IAAI;EACJ,MAAM,GAAG,QAAQ;EACjB;EACA,UAAU;EAEV,SAAS,QAAQ,uBAAuB,aAAa,GAAG,WAAW;EAInE,WAAW,kBAAkB,QAAQ,CAAC;EACtC,OAAO,CAAC,OAAgB;EACxB,MAAM;GAAE,OAAO;GAAG,QAAQ;GAAG,WAAW;GAAG,YAAY;GAAG;EAC1D,eAAe;EACf,WAAW;EACX,SAAS,oBAAoB,gBAAgB,aAAa,OAAO;EAClE;;;;;;AAOH,SAAgB,qBAAqB,QAMnC;CACA,MAAM,EAAE,YAAY,aAAa,SAAS,WAAW;CACrD,MAAM,MAAM,cAAc,QAAQ;CAIlC,MAAM,YAAY,kBAAkB,QAAQ;CAC5C,MAAM,OACJ,UAAU,WAAW,SAAS,UAAU,YACpC,kBAAkB,SAAS,OAAO,GAClC,WAAW,SAAS,UAAU,YAC9B;EAAE,GAAG;EAAW,eAAe;EAAgB,GAC/C;CAEN,MAAM,QAAQ,kBAAkB,OAAO;AAUvC,QAAO;EAAE,UATQ;GACf,MAAM;GACN,SAAS,MAAM;GACf,QAAQ;GACR,YAAY;GACZ;GACA,SAAS,MAAM;GACf,QAAQ,CAAC,MAAM;GAChB;EACkB;EAAK;EAAM;EAAY,SAAS,MAAM;EAAS;;AAkBpE,SAAgB,uBACd,MACoC;AACpC,KAAI,CAAC,QAAS,KAAK,eAAe,WAAW,KAAK,eAAe,UAC/D,QAAO,KAAA;CACT,MAAM,UACJ,KAAK,iBACJ,KAAK,eAAe,YACjB,uBACA;AACN,KAAI,KAAK,eAAe,UACtB,QAAO;EAAE,gBAAA;EAAsC;EAAS;CAM1D,MAAM,oBAHJ,KAAK,aACD,KAAK,MAAM,EAAE,OAAO,KAAK,CAC1B,QAAQ,SAAS,SAAS,KAAA,EAAU,IAAI,EAAE,EAE5C,IAAI,OAAO,CACX,MAAM,SAAS,OAAO,UAAU,KAAK,IAAI,QAAQ,OAAO,OAAO,IAAI;AAKtE,QAAO;EACL,iBAJA,qBAAqB,KAAA,IACjB,qBAAqB,MACrB,sBAAsB,KAAK,QAAQ,IAAA,sBAAA;EAKvC;EACA,QACE,qBAAqB,uBAAuB,KAAK,GAAG,MAAM,KAAA;EAC7D;;;;;;;;AASH,SAAgB,uBACd,MACS;CACT,MAAM,EAAE,cAAc,gBACpB,OAAO,SAAS,WACZ;EAAE,cAAc;EAAM,aAAa,KAAA;EAAW,GAC9C,QAAQ,EAAE;CAChB,MAAM,oBAAoB,eAAe,EAAE,EACxC,KAAK,eAAe,OAAO,WAAW,OAAO,KAAK,CAAC,CACnD,MAAM,SAAS,OAAO,UAAU,KAAK,IAAI,QAAQ,OAAO,OAAO,IAAI;AACtE,KAAI,qBAAqB,KAAA,EAAW,QAAO,qBAAqB;AAChE,MAAK,MAAM,cAAc,eAAe,EAAE,EAAE;EAC1C,MAAM,OAAO,WAAW,OAAO;AAC/B,MAAI,SAAS,OAAO,SAAS,MAAO,QAAO;AAC3C,MAAI,2BAA2B,KAAK,WAAW,OAAO,QAAQ,GAAG,CAC/D,QAAO;;AAEX,QAAO,6CAA6C,KAAK,gBAAgB,GAAG;;;;;;;;AAsB9E,SAAgB,kBAAkB,MAKhC;CACA,IAAI,OAAO,KAAK;CAChB,IAAI,WAAW;CACf,IAAI,WAAW;CACf,IAAI;AACJ,QAAO;EACL,kBAAkB,SAAS;AACzB,cAAW;AAGX,cACE,UAAU,eAAe,WAAW,uBAAuB,SAAS;;EAExE,uBAAuB,uBAAuB,SAAS;EACvD,MAAM,OAAO,MAAM;AACjB,OAAI,KAAK,QAAQ,QAAS;AAC1B,cAAW;AACX,cAAW,KAAA;AACX,SAAM,KAAK,QAAQ,OAAO,KAAK;AAC/B,OAAI,KAAK,QAAQ,WAAW,CAAC,YAAY,YAAY,CAAC,cAAc,KAAK,CACvE;AACF,cAAW;AACX,UAAO,MAAM,KAAK,aAAa;AAC/B,OAAI,KAAK,QAAQ,QAAS;AAC1B,QAAK,SAAS,iBACZ,kBACA,qBAAqB,KAAK,eAAe,KAAK,CAAC,CAAC,SACjD;AACD,QAAK,YAAY;AACjB,cAAW;AACX,cAAW,KAAA;GACX,MAAM,OAAO,KAAK;AAClB,OAAI,KAAK,QAAQ,QAAS;AAC1B,SAAM,KAAK,QAAQ,OAAO,OAAO,SAAS,aAAa,MAAM,GAAG,KAAK;;EAExE"}
1
+ {"version":3,"file":"gateway-CWoYlPz7.js","names":[],"sources":["../src/shared/health-checks/endpoints.ts","../src/agent/gateway-session.ts","../src/agent/signals.ts","../src/agent/runner/harness/pi/gateway.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","/**\n * Gateway auth for a wizard run: a `phe_` scoped token the backend mints, with\n * pinned attribution, a spend cap and an expiry.\n *\n * Every mint failure throws: a silent downgrade would spend uncapped,\n * unattributed money to hide an outage.\n */\n\nimport { logToFile } from '@utils/debug';\nimport { analytics } from '@utils/analytics';\nimport { ErrorCodes, WizardError } from '@shared/errors';\nimport type { GatewayCredential } from '@shared/api';\nimport type { HostResolution } from '@shared/host-resolution';\nimport { oauthLoginKey } from '@shared/oauth-session';\nimport { checkLlmGatewayHealth } from '@shared/health-checks/endpoints';\nimport { ServiceHealthStatus } from '@shared/health-checks/types';\nimport { IS_PRODUCTION_BUILD } from '@env';\n\nexport interface GatewayAuth {\n /** Base URL for model calls (no `/v1`; transports append their route). */\n gatewayUrl: string;\n /** Gateway bearer, minted normally or supplied directly by CI. */\n token: string;\n /** Team verified by the mint, or explicitly supplied for CI attribution. */\n teamId?: number;\n /**\n * Instant past which a 401 on this bearer is age rather than a bad\n * credential: the cache re-mints past it, and a session still holding the\n * old bearer may re-mint once. Before it the mint has to be trusted.\n */\n refreshAtMs: number;\n}\n\ninterface CachedAuth {\n key: string;\n auth: GatewayAuth;\n /** Re-resolve once past this instant. */\n staleAtMs: number;\n}\n\nlet cached: CachedAuth | null = null;\n/**\n * Shared so concurrent callers mint once. The orchestrator starts every runnable\n * task at once, and each would otherwise take its own token and its own cap.\n */\nlet inFlight: { key: string; promise: Promise<GatewayAuth> } | null = null;\nlet ciAuth: GatewayAuth | null = null;\n\n// Snapshot CI supplies a gateway bearer without minting or re-minting.\nexport function configureGatewayCredentialsForCI(\n token: string,\n projectId: number,\n gatewayUrl: string,\n): void {\n if (IS_PRODUCTION_BUILD)\n throw new Error('CI gateway auth requires a non-production build');\n if (!token.trim() || !Number.isSafeInteger(projectId) || projectId <= 0) {\n throw new Error('CI gateway auth requires a token and valid project ID');\n }\n if (\n !/^https?:\\/\\//.test(gatewayUrl) ||\n !isTrustedGatewayUrl(gatewayUrl, '')\n ) {\n throw new Error('CI gateway auth requires a trusted gateway origin');\n }\n resetGatewaySession();\n ciAuth = {\n token: token.trim(),\n teamId: projectId,\n gatewayUrl: gatewayUrl.replace(/\\/+$/, ''),\n refreshAtMs: Infinity,\n };\n}\n\n/** Use this run's pre-issued gateway token, or mint when it has none; the keyed mint cache stays. */\nexport function useRunGatewayCredential(\n gateway: GatewayCredential | undefined,\n projectId: number,\n): void {\n if (gateway) {\n configureGatewayCredentialsForCI(gateway.token, projectId, gateway.url);\n return;\n }\n ciAuth = null;\n}\n\n/**\n * Adoption floor. The anthropic subprocess holds its credential until a 401\n * forces a re-mint, so a token below this would churn mints.\n */\nconst MIN_USABLE_TTL_MS = 2 * 60 * 1000;\n/** Re-resolve at this fraction of the token's life, leaving a usable remainder. */\nconst REFRESH_AT_FRACTION = 0.8;\n// Exceeds the backend's own 10s gateway timeout: a slow mint that lands after the\n// CLI hangs up spends a daily mint and orphans a live token.\nconst MINT_TIMEOUT_MS = 20_000;\n/** Longer than any refusal the mint writes; a body past this is not a message. */\nconst MAX_REFUSAL_DETAIL_LENGTH = 500;\n/** Outcomes are short snake_case labels; anything longer is not one. */\nconst MAX_REFUSAL_OUTCOME_LENGTH = 64;\n\n/** Resolve this run's gateway auth, minting and re-minting near expiry. */\nexport async function gatewayAuth(\n host: HostResolution,\n accessToken: string,\n program: string | undefined,\n): Promise<GatewayAuth> {\n if (ciAuth) return ciAuth;\n // Keyed by program: a token pins `wizard:<program>`, so reusing one across\n // programs bills the wrong budget. Keyed by login rather than OAuth token:\n // the token rotates mid-run, and each new one would spend a weekly mint.\n const key = `${host.apiHost}\\n${oauthLoginKey(accessToken)}\\n${\n program ?? ''\n }`;\n if (cached && cached.key === key && Date.now() < cached.staleAtMs) {\n return cached.auth;\n }\n if (inFlight && inFlight.key === key) return inFlight.promise;\n const promise = resolveGatewayAuth(host, accessToken, key, program);\n inFlight = { key, promise };\n try {\n return await promise;\n } finally {\n if (inFlight?.promise === promise) inFlight = null;\n }\n}\n\nasync function resolveGatewayAuth(\n host: HostResolution,\n accessToken: string,\n key: string,\n program: string | undefined,\n): Promise<GatewayAuth> {\n if (!program) {\n // Every run has one; absent means a caller was not wired, and the spend would\n // be unattributable.\n logToFile('[gateway] run has no program id; failing the run');\n throw new GatewayMintFailed(\n 'this run has no program to attribute its spend to',\n );\n }\n const minted = await mintGatewayToken(host, accessToken, program);\n const health = await checkLlmGatewayHealth(minted.gatewayUrl);\n if (health.status !== ServiceHealthStatus.Healthy) {\n throw new WizardError(\n 'The PostHog AI gateway is unavailable. Please try again later.',\n undefined,\n ErrorCodes.EnvServiceOutage,\n );\n }\n const expiresAtMs = Date.parse(minted.expiresAt);\n const ttlMs = expiresAtMs - Date.now();\n if (!Number.isFinite(expiresAtMs) || ttlMs < MIN_USABLE_TTL_MS) {\n // Expired, unreadable, or too short to serve a session. Adopting it would\n // 401 mid-run, and downgrading would spend the rest of the run uncapped.\n logToFile(\n `[gateway] mint returned a token with ${ttlMs}ms of life; failing the run`,\n );\n throw new GatewayMintFailed(\n `the PostHog gateway issued a token with ${ttlMs}ms of life`,\n );\n }\n const staleAtMs = Date.now() + ttlMs * REFRESH_AT_FRACTION;\n // Only failures are logged otherwise, so a successful run leaves no\n // local trace. Never log the token itself.\n logToFile(\n `[gateway] minted a scoped token: program=${program} team=${\n minted.teamId ?? 'unknown'\n } ttl=${Math.round(ttlMs / 1000)}s url=${minted.gatewayUrl}`,\n );\n const auth: GatewayAuth = {\n gatewayUrl: minted.gatewayUrl,\n token: minted.token,\n teamId: minted.teamId,\n refreshAtMs: staleAtMs,\n };\n cached = { key, auth, staleAtMs };\n return auth;\n}\n\n/** Test hook: drop the cached auth so the next call re-resolves. */\nexport function resetGatewaySession(): void {\n cached = null;\n inFlight = null;\n ciAuth = null;\n}\n\n/** Whether a 401 on this bearer may be age (past its refresh instant) rather than a bad credential. */\nexport function isPastRefresh(auth: GatewayAuth, now = Date.now()): boolean {\n return now >= auth.refreshAtMs;\n}\n\n/**\n * Whether a server-supplied origin may receive a bearer and prompt content:\n * https (loopback excepted), and either a current cloud gateway or the host the run\n * authenticated against.\n */\nexport function isTrustedGatewayUrl(value: string, apiHost: string): boolean {\n let url: URL;\n try {\n url = new URL(value);\n } catch {\n return false;\n }\n // Consumers append routes to this value, so anything beyond an origin\n // (path, query, fragment, userinfo) would build a malformed endpoint.\n if (\n url.pathname !== '/' ||\n url.search ||\n url.hash ||\n url.username ||\n url.password\n ) {\n return false;\n }\n const localhost =\n url.hostname === 'localhost' ||\n url.hostname === '127.0.0.1' ||\n url.hostname === 'host.docker.internal';\n // Loopback is the dev gateway, and is the one case allowed over http.\n if (localhost) return true;\n if (url.protocol !== 'https:') return false;\n if (url.hostname.endsWith('.posthog.com')) {\n return (\n url.origin === 'https://ai-gateway.us.posthog.com' ||\n url.origin === 'https://ai-gateway.eu.posthog.com'\n );\n }\n try {\n return url.hostname === new URL(apiHost).hostname;\n } catch {\n return false;\n }\n}\n\ninterface MintedToken {\n token: string;\n expiresAt: string;\n gatewayUrl: string;\n teamId?: number;\n}\n\n/**\n * A deliberate refusal from the mint endpoint, as opposed to the mint being\n * unavailable. Thrown so the run stops instead of proceeding without the\n * controls the refusal was enforcing. A WizardError, so the runners print its\n * message as-is and `wizardAbort` resolves its code.\n */\nexport class GatewayMintRefused extends WizardError {\n readonly status: number;\n /** The backend's refusal outcome (`blocked`, `throttled`, ...), when it sent one. */\n readonly outcome?: string;\n\n constructor(status: number, message: string, outcome?: string) {\n super(message, { status, outcome }, ErrorCodes.GatewayMintRefused);\n this.name = 'GatewayMintRefused';\n this.status = status;\n this.outcome = outcome;\n }\n}\n\n/**\n * The mint could not produce a usable credential: unreachable, a 5xx, or a\n * response the client cannot use.\n */\nexport class GatewayMintFailed extends WizardError {\n constructor(message: string) {\n super(message, undefined, ErrorCodes.GatewayMintFailed);\n this.name = 'GatewayMintFailed';\n }\n}\n\n/**\n * Whether a mint status means \"refused this run\" rather than \"not available\".\n * 429 the daily run limit, 403 revoked project access, 400 a login covering\n * more than one project, 401 a credential the mint does not accept, 404 an\n * instance without the mint endpoint.\n */\nfunction isMintRefusal(status: number): boolean {\n return (\n status === 400 ||\n status === 401 ||\n status === 403 ||\n status === 404 ||\n status === 429\n );\n}\n\ninterface MintRefusal {\n detail?: string;\n outcome?: string;\n}\n\n/**\n * The server's own reason for a refusal, when it sent one. DRF answers every\n * refusal as `{\"detail\": \"...\", \"code\": \"<outcome>\"}`; the blocklist's detail\n * names the contact address, which the fixed messages below cannot. `code` is\n * the backend's own label for the refusal (`outcome` on older backends) and\n * rides the client event.\n */\nfunction cleanRefusalText(value: unknown): string {\n if (typeof value !== 'string') return '';\n // Not because the server sends escapes, but because this string is printed\n // straight to a terminal: sanitizing at the boundary means no later message\n // can move the cursor or repaint the screen.\n // eslint-disable-next-line no-control-regex\n return value.replace(/[\\u0000-\\u001f\\u007f-\\u009f]/g, ' ').trim();\n}\n\nasync function readRefusal(resp: Response): Promise<MintRefusal> {\n try {\n const body = (await resp.json()) as {\n detail?: unknown;\n code?: unknown;\n outcome?: unknown;\n };\n const detail = cleanRefusalText(body?.detail);\n // The DRF handler flattens a dict detail, so the outcome rides as `code`.\n // A `code` that cleans to nothing does not shadow a usable `outcome`.\n const outcome =\n cleanRefusalText(body?.code) || cleanRefusalText(body?.outcome);\n return {\n detail:\n detail.length > 0 && detail.length <= MAX_REFUSAL_DETAIL_LENGTH\n ? detail\n : undefined,\n outcome:\n outcome.length > 0 && outcome.length <= MAX_REFUSAL_OUTCOME_LENGTH\n ? outcome\n : undefined,\n };\n } catch {\n return {};\n }\n}\n\nfunction mintRefusalMessage(status: number, detail?: string): string {\n if (detail) return detail;\n switch (status) {\n case 429:\n return 'This wizard program has used its daily run limit. Try again tomorrow.';\n case 403:\n return 'Your access to this project has changed. Re-authenticate and try again.';\n case 400:\n // The only 400 the mint answers is the exactly-one-project check.\n return 'Your PostHog login must cover exactly one project. Re-authenticate and try again.';\n case 401:\n return 'PostHog did not accept this login. Re-authenticate with `npx @posthog/wizard@latest`.';\n case 404:\n return 'This PostHog instance does not issue gateway tokens. Upgrade with `npx @posthog/wizard@latest` and try again.';\n default:\n return 'The PostHog gateway refused this run.';\n }\n}\n\nasync function mintGatewayToken(\n host: HostResolution,\n accessToken: string,\n program: string,\n): Promise<MintedToken> {\n try {\n const resp = await fetch(`${host.apiHost}/api/wizard/gateway_token/`, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${accessToken}`,\n 'Content-Type': 'application/json',\n },\n // The flag tells the server this build reads a refusal, so it may answer\n // with the reason. A build that omits it gets a 404, which is its signal\n // to fall back to the legacy gateway.\n body: JSON.stringify({ program, reads_refusal_reason: true }),\n signal: AbortSignal.timeout(MINT_TIMEOUT_MS),\n });\n if (!resp.ok) {\n if (isMintRefusal(resp.status)) {\n const refusal = await readRefusal(resp);\n logToFile(\n `[gateway] mint refused with HTTP ${resp.status} (${\n refusal.outcome ?? 'no outcome'\n }); failing the run`,\n );\n // The terminal denial event for this run. The backend's own event has\n // no run id, so this is what joins a refusal to the session.\n analytics.wizardCapture('gateway mint refused', {\n status: resp.status,\n outcome: refusal.outcome,\n program,\n });\n throw new GatewayMintRefused(\n resp.status,\n mintRefusalMessage(resp.status, refusal.detail),\n refusal.outcome,\n );\n }\n logToFile(\n `[gateway] mint failed with HTTP ${resp.status}; failing the run`,\n );\n throw new GatewayMintFailed(\n `the PostHog gateway could not issue a token (HTTP ${resp.status})`,\n );\n }\n const body = (await resp.json()) as {\n token?: string;\n expires_at?: string;\n gateway_url?: string;\n team_id?: number;\n };\n // Checked one at a time, not in a loop, so each clause narrows the optional\n // field for the return below and each names itself in the failure.\n if (!body.token) {\n logToFile('[gateway] mint response omitted token; failing the run');\n throw new GatewayMintFailed('mint response omitted token');\n }\n if (!body.expires_at) {\n logToFile('[gateway] mint response omitted expires_at; failing the run');\n throw new GatewayMintFailed('mint response omitted expires_at');\n }\n if (!body.gateway_url) {\n logToFile('[gateway] mint response omitted gateway_url; failing the run');\n throw new GatewayMintFailed('mint response omitted gateway_url');\n }\n if (!isTrustedGatewayUrl(body.gateway_url, host.apiHost)) {\n logToFile(\n '[gateway] mint returned an untrusted gateway url; failing the run',\n );\n throw new GatewayMintFailed('mint returned an untrusted gateway url');\n }\n const gatewayUrl = new URL(body.gateway_url);\n if (\n ['localhost', '127.0.0.1'].includes(new URL(host.apiHost).hostname) &&\n gatewayUrl.hostname === 'host.docker.internal'\n ) {\n gatewayUrl.hostname = 'localhost';\n }\n return {\n token: body.token,\n expiresAt: body.expires_at,\n gatewayUrl: gatewayUrl.origin,\n teamId: body.team_id,\n };\n } catch (e) {\n // Decisions and failures both pass through: this catch exists for transport\n // errors, and folding the others into it would lose the reason.\n if (e instanceof GatewayMintRefused || e instanceof GatewayMintFailed)\n throw e;\n logToFile(\n `[gateway] mint transport failure (${String(e)}); failing the run`,\n );\n throw new GatewayMintFailed(\n `could not reach the PostHog gateway (${String(e)})`,\n );\n }\n}\n\n/**\n * The v2 run-metadata carrier: one JSON blob for the `X-PostHog-Properties`\n * header. Plain keys only, since the gateway strips `$`-prefixed keys as reserved,\n * so feature-flag variants land as `wizard_flag_<key>` instead of the legacy\n * `$feature/<key>` (dashboards keying on `$feature/wizard-*` read the new key\n * post-cutover).\n */\nexport function buildWizardPropertiesBlob(\n wizardMetadata: Record<string, string>,\n wizardFlags: Record<string, string>,\n teamId?: number,\n): string {\n // The gateway pins `$ai_product` to `wizard:<program>`, and rejects a legacy\n // product override on a scoped token, so the unprefixed key every cost and\n // error consumer reads is only present if this blob declares it.\n const props: Record<string, string | number> = { ai_product: 'wizard' };\n if (teamId !== undefined) props.team_id = teamId;\n for (const [key, value] of Object.entries(wizardMetadata)) {\n props[stripPropertyPrefix(key)] = value;\n }\n for (const [flagKey, variant] of Object.entries(wizardFlags)) {\n if (!flagKey.toLowerCase().startsWith('wizard')) continue;\n props[`wizard_flag_${flagKey.toLowerCase()}`] = variant;\n }\n return JSON.stringify(props);\n}\n\nconst LEGACY_PROPERTY_PREFIX = 'X-POSTHOG-PROPERTY-';\n\nfunction stripPropertyPrefix(key: string): string {\n return key.toUpperCase().startsWith(LEGACY_PROPERTY_PREFIX)\n ? key.slice(LEGACY_PROPERTY_PREFIX.length).toLowerCase()\n : key;\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 /** A schema-bound run ended without output matching its schema */\n INVALID_STRUCTURED_OUTPUT = 'WIZARD_INVALID_STRUCTURED_OUTPUT',\n /** Agent acted but stopped short — planned tasks left open and/or no skill installed */\n INCOMPLETE_TASKS = 'WIZARD_INCOMPLETE_TASKS',\n}\n","/**\n * PostHog AI gateway provider spec for pi sessions, shared by the linear run\n * and the orchestrator's per-task runs so both speak to the gateway\n * identically: bearer auth, the wizard properties header, transport shape\n * inferred from the model id. The caller registers the spec on its own\n * (lazily imported, properly typed) pi ModelRegistry.\n */\n\nimport {\n buildWizardPropertiesBlob,\n isPastRefresh,\n type GatewayAuth,\n} from '../../../gateway-session';\nimport {\n modelCapabilities,\n type ThinkingLevel,\n} from '../../switchboard/models';\nimport { AgentErrorType } from '../../../signals';\n\n/** Provider registered on the in-memory registry for this run. */\nexport const GATEWAY_PROVIDER = 'posthog-gateway';\n\n/** The pi transports the gateway serves. */\nexport type GatewayApi =\n | 'anthropic-messages'\n | 'openai-completions'\n | 'openai-responses';\n\n/**\n * The gateway speaks two shapes on two endpoints: Anthropic models over\n * `anthropic-messages` (the SDK appends `/v1/messages`, so the base URL has no\n * `/v1`), and OpenAI-class models (`openai/gpt-5`, …) over an OpenAI shape at\n * `/v1/responses` or `/v1/chat/completions` (base URL keeps `/v1`). Infer the\n * shape from the model id so a pair's model selects the right transport.\n *\n * OpenAI models take the Responses API because OpenAI rejects function tools\n * combined with `reasoning_effort` on chat completions and every task sends both.\n */\nexport function gatewayApiFor(modelId: string): GatewayApi {\n return modelId.startsWith('openai/')\n ? 'openai-responses'\n : 'anthropic-messages';\n}\n\n/**\n * Gateway HTTP headers, mirroring `buildAgentEnv` on the anthropic path: one\n * `X-PostHog-Properties` JSON blob (Bedrock fallback is native, so no opt-in)\n * plus the 1M context beta, since pi otherwise runs at 200k and overflows on\n * larger projects (the post-run compaction failures).\n */\nexport function buildGatewayHeaders(\n wizardMetadata: Record<string, string>,\n wizardFlags: Record<string, string>,\n teamId?: number,\n): Record<string, string> {\n return {\n 'anthropic-beta': 'context-1m-2025-08-07',\n 'X-PostHog-Properties': buildWizardPropertiesBlob(\n wizardMetadata,\n wizardFlags,\n teamId,\n ),\n };\n}\n\nexport interface GatewayProviderInputs {\n gatewayUrl: string;\n accessToken: string;\n /** Customer team for the properties blob (from the mint response). */\n teamId?: number;\n wizardMetadata: Record<string, string>;\n wizardFlags: Record<string, string>;\n modelId: string;\n // Resolved effort override — the switchboard's flag/payload pick on linear\n // runs, the prompt-frontmatter effort on per-task agents. Overrides the\n // table default for a reasoning model when set.\n effort?: ThinkingLevel;\n}\n\n/**\n * One gateway model spec — the single description of how to reach a model on\n * the gateway. The provider spec below wraps it for pi's registry; one-shot\n * callers (scan triage) hand it straight to `completeSimple`.\n */\nexport function buildGatewayModel(inputs: GatewayProviderInputs) {\n const { gatewayUrl, wizardMetadata, wizardFlags, modelId, teamId } = inputs;\n const api = gatewayApiFor(modelId);\n return {\n id: modelId,\n name: `${modelId} (PostHog Gateway)`,\n api,\n provider: GATEWAY_PROVIDER,\n // Anthropic drops /v1 (the SDK appends /v1/messages); the OpenAI shape keeps it.\n baseUrl: api === 'anthropic-messages' ? gatewayUrl : `${gatewayUrl}/v1`,\n // A model trait resolved by the switchboard, not a harness guess:\n // non-reasoning openai models reject `reasoning_effort` (gpt-4o → gateway\n // UnsupportedParamsError → the run no-ops).\n reasoning: modelCapabilities(modelId).reasoning,\n input: ['text' as const],\n cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },\n contextWindow: 1_000_000,\n maxTokens: 64_000,\n headers: buildGatewayHeaders(wizardMetadata, wizardFlags, teamId),\n };\n}\n\n/**\n * The provider object for `registry.registerProvider(GATEWAY_PROVIDER, …)`,\n * plus the derived traits the session setup needs (`caps.thinkingLevel`).\n */\nexport function buildGatewayProvider(inputs: GatewayProviderInputs): {\n provider: Record<string, unknown>;\n api: GatewayApi;\n caps: ReturnType<typeof modelCapabilities>;\n gatewayUrl: string;\n baseUrl: string;\n} {\n const { gatewayUrl, accessToken, modelId, effort } = inputs;\n const api = gatewayApiFor(modelId);\n // One resolution point for the model's traits and the run's effort override.\n // pi clamps whatever comes out of here against the levels this spec declares,\n // so a level the spec doesn't carry is silently reduced by the session.\n const tableCaps = modelCapabilities(modelId);\n const caps =\n effort && effort !== 'off' && tableCaps.reasoning\n ? modelCapabilities(modelId, effort)\n : effort === 'off' && tableCaps.reasoning\n ? { ...tableCaps, thinkingLevel: 'off' as const }\n : tableCaps;\n\n const model = buildGatewayModel(inputs);\n const provider = {\n name: 'PostHog Gateway',\n baseUrl: model.baseUrl,\n apiKey: accessToken,\n authHeader: true,\n api,\n headers: model.headers,\n models: [model],\n };\n return { provider, api, caps, gatewayUrl, baseUrl: model.baseUrl };\n}\n\n/** The part of a pi assistant turn that says why it failed. */\nexport interface GatewayTurnError {\n errorMessage?: string;\n diagnostics?: { error?: { name?: string; code?: string | number } }[];\n}\n\nexport type GatewayTerminalFailure = {\n classification:\n | AgentErrorType.API_ERROR\n | AgentErrorType.RATE_LIMIT\n | AgentErrorType.ABORT;\n message: string;\n status?: number;\n};\n\nexport function gatewayTerminalFailure(\n turn: ({ stopReason?: string } & GatewayTurnError) | undefined,\n): GatewayTerminalFailure | undefined {\n if (!turn || (turn.stopReason !== 'error' && turn.stopReason !== 'aborted'))\n return undefined;\n const message =\n turn.errorMessage ||\n (turn.stopReason === 'aborted'\n ? 'Agent turn aborted'\n : 'Gateway request failed');\n if (turn.stopReason === 'aborted') {\n return { classification: AgentErrorType.ABORT, message };\n }\n const codes =\n turn.diagnostics\n ?.map((d) => d.error?.code)\n .filter((code) => code !== undefined) ?? [];\n const structuredStatus = codes\n .map(Number)\n .find((code) => Number.isInteger(code) && code >= 400 && code < 600);\n const rateLimited =\n structuredStatus !== undefined\n ? structuredStatus === 429\n : /\\b429\\b|rate limit/i.test(message);\n return {\n classification: rateLimited\n ? AgentErrorType.RATE_LIMIT\n : AgentErrorType.API_ERROR,\n message,\n status:\n structuredStatus ?? (isGatewayAuthRejection(turn) ? 401 : undefined),\n };\n}\n\n/**\n * Whether a turn's error is the gateway rejecting the bearer. pi attaches the\n * SDK's own error to `diagnostics`, so its code decides when one is present.\n * The message match is the fallback for a turn that failed before pi built a\n * diagnostic, where the status survives only as prose.\n */\nexport function isGatewayAuthRejection(\n turn: GatewayTurnError | string | undefined,\n): boolean {\n const { errorMessage, diagnostics } =\n typeof turn === 'string'\n ? { errorMessage: turn, diagnostics: undefined }\n : turn ?? {};\n const structuredStatus = (diagnostics ?? [])\n .map((diagnostic) => Number(diagnostic.error?.code))\n .find((code) => Number.isInteger(code) && code >= 400 && code < 600);\n if (structuredStatus !== undefined) return structuredStatus === 401;\n for (const diagnostic of diagnostics ?? []) {\n const code = diagnostic.error?.code;\n if (code === 401 || code === '401') return true;\n if (/^authentication_?error$/i.test(diagnostic.error?.name ?? ''))\n return true;\n }\n return /\\b401\\b|authentication_error|unauthorized/i.test(errorMessage ?? '');\n}\n\nexport interface GatewayRemintOptions {\n signal?: AbortSignal;\n session: { prompt(text: string): Promise<void> };\n registry: { registerProvider(providerName: string, config: never): void };\n auth: GatewayAuth;\n /** The cache: the same token while fresh, a new mint past the refresh point. */\n refreshAuth: () => Promise<GatewayAuth>;\n providerInputs: (auth: GatewayAuth) => GatewayProviderInputs;\n /** The prompt that resumes the work after a re-mint. */\n continueText: string | (() => string);\n onRemint?: () => void;\n}\n\n/**\n * Wraps a pi session's prompt(): when a turn ends on a 401 from a bearer past\n * its refresh instant, mint once, re-register the provider with the new\n * bearer (pi resolves the apiKey per request), and continue. A 401 on a fresh\n * bearer, or a second one, is left to the harness's normal failure path.\n */\nexport function withGatewayRemint(opts: GatewayRemintOptions): {\n prompt(text: string): Promise<void>;\n /** Feed every assistant `message_end`; the last turn decides. */\n noteAssistantTurn(message: unknown): void;\n terminalFailure(): GatewayTerminalFailure | undefined;\n} {\n let auth = opts.auth;\n let rejected = false;\n let reminted = false;\n let lastTurn: ({ stopReason?: string } & GatewayTurnError) | undefined;\n return {\n noteAssistantTurn(message) {\n lastTurn = message as\n | ({ stopReason?: string } & GatewayTurnError)\n | undefined;\n rejected =\n lastTurn?.stopReason === 'error' && isGatewayAuthRejection(lastTurn);\n },\n terminalFailure: () => gatewayTerminalFailure(lastTurn),\n async prompt(text) {\n if (opts.signal?.aborted) return;\n rejected = false;\n lastTurn = undefined;\n await opts.session.prompt(text);\n if (opts.signal?.aborted || !rejected || reminted || !isPastRefresh(auth))\n return;\n reminted = true;\n auth = await opts.refreshAuth();\n if (opts.signal?.aborted) return;\n opts.registry.registerProvider(\n GATEWAY_PROVIDER,\n buildGatewayProvider(opts.providerInputs(auth)).provider as never,\n );\n opts.onRemint?.();\n rejected = false;\n lastTurn = undefined;\n const next = opts.continueText;\n if (opts.signal?.aborted) return;\n await opts.session.prompt(typeof next === 'function' ? next() : next);\n },\n };\n}\n"],"mappings":";;;;;;;AAMA,SAAS,mBAAmB,OAAe,UAAoC;AAC7E,QAAO;EACL,QAAA;EACA;EACA,cAAc,WAAW,IAAI,YAAY,aAAa,KAAA;EACvD;;AAGH,SAAS,WAAW,OAAiC;AACnD,QAAO;EAAE,QAAA;EAAkC;EAAO;;AAQpD,MAAM,oBAAoB,CAAC,KAAK,IAAK;AAErC,eAAe,aACb,KACA,WACA,UAIA;CACA,MAAM,aAAa,IAAI,iBAAiB;CACxC,MAAM,MAAM,iBAAiB,WAAW,OAAO,EAAE,UAAU;AAC3D,KAAI;EACF,MAAM,MAAM,MAAM,MAAM,KAAK;GAAE,QAAQ,WAAW;GAAQ;GAAU,CAAC;AACrE,eAAa,IAAI;AACjB,SAAO;GAAE,MAAM;GAAY;GAAK;UACzB,GAAG;AACV,eAAa,IAAI;EACjB,MAAM,MAAM,aAAa,QAAQ,oBAAI,IAAI,MAAM,gBAAgB;AAC/D,SAAO;GAAE,MAAM;GAAS,OAAO;GAAK,UAAU,IAAI,SAAS;GAAc;;;AAK7E,eAAsB,oBACpB,KACA,YAAY,KACZ,oBAAiD,MAAM,MAAM,KAC7D,WAA0C,UACf;CAW3B,IAAI,iBAAgC;CACpC,IAAI,YAAY;CAChB,IAAI,WAAW;AAEf,MAAK,IAAI,IAAI,GAAG,KAAK,kBAAkB,QAAQ,KAAK;AAClD,MAAI,IAAI,GAAG;GACT,MAAM,OAAO,kBAAkB,IAAI;AACnC,aACE,yBAAyB,EAAE,GAAG,kBAAkB,OAAO,OAAO,IAAI,MAAM,KAAK,YAAY,UAAU,GACpG;AACD,SAAM,IAAI,SAAS,MAAM,WAAW,GAAG,KAAK,CAAC;;AAE/C;EAEA,MAAM,UAAU,MAAM,aAAa,KAAK,WAAW,SAAS;AAE5D,MAAI,QAAQ,SAAS,YAAY;GAC/B,MAAM,MAAM,QAAQ;AACpB,OAAI,iBAAiB,IAAI,OAAO,EAAE;IAChC,MAAM,SAA2B;KAC/B,QAAA;KACA,cACE,WAAW,IACP,QAAQ,IAAI,OAAO,aAAa,SAAS,KACzC,QAAQ,IAAI;KACnB;AACD,cACE,uBAAuB,IAAI,MAAM,OAAO,OAAA,IACjC,OAAO,aAAa,GAC5B;AACD,WAAO;;AAET,oBAAiB,IAAI;AACrB,eAAY,QAAQ,IAAI;AACxB;;AAGF,cAAY,QAAQ,WAChB,2BAA2B,UAAU,MACrC,QAAQ,MAAM;;CAGpB,MAAM,SACJ,mBAAmB,OACf,WAAW,QAAQ,eAAe,aAAa,SAAS,GAAG,GAC3D,mBAAmB,WAAW,SAAS;AAC7C,WACE,uBAAuB,IAAI,MAAM,OAAO,OAAA,aACxB,SAAS,IAAI,OAAO,MAAM,GAC3C;AACD,QAAO;;AAGT,MAAa,yBACX,eAEA,oBAAoB,IAAI,IAAI,WAAW,WAAW,CAAC,KAAK;;;;;;;AAQ1D,MAAa,0BAA0B,YAAuC;CAC5E,MAAM,CAAC,QAAQ,OAAO,MAAM,QAAQ,IAAI,CACtC,oBAAoB,GAAG,uBAAuB,kBAAkB,EAChE,oBAAoB,GAAG,oBAAoB,kBAAkB,CAC9D,CAAC;AACF,QAAO,oBAAoB,QAAQ,IAAI;;;;;;;AAQzC,SAAS,oBACP,QACA,KACkB;AAClB,KAAI,OAAO,WAAA,UAGT,QAAO,IAAI,WAAA,YACP,SACA,oBAAoB,QAAQ,kBAAkB;AAGpD,KAAI,IAAI,WAAA,UACN,QAAO,oBAAoB,KAAK,8BAA8B;CAGhE,MAAM,QAAQ,WAAW,OAAO,SAAS,UAAU,UACjD,IAAI,SAAS;AAKf,QAAO;EACL,QAHA,OAAO,WAAA,UACP,IAAI,WAAA,SAAA,SAAA;EAKJ;EAEA,cAAc,OAAO,gBAAgB,IAAI;EAC1C;;AAGH,SAAS,oBACP,QACA,QACkB;AAClB,QAAO;EACL,GAAG;EACH,cAAc,OAAO,eACjB,GAAG,OAAO,aAAa,IAAI,OAAO,KAClC;EACL;;;;;;;;;;;AC7IH,IAAI,SAA4B;;;;;AAKhC,IAAI,WAAkE;AACtE,IAAI,SAA6B;AAGjC,SAAgB,iCACd,OACA,WACA,YACM;AAEJ,OAAM,IAAI,MAAM,kDAAkD;;;AAoBtE,SAAgB,wBACd,SACA,WACM;AACN,KAAI,SAAS;AACX,mCAAiC,QAAQ,OAAO,WAAW,QAAQ,IAAI;AACvE;;AAEF,UAAS;;;;;;AAOX,MAAM,oBAAoB,MAAS;;AAEnC,MAAM,sBAAsB;AAG5B,MAAM,kBAAkB;;AAExB,MAAM,4BAA4B;;AAElC,MAAM,6BAA6B;;AAGnC,eAAsB,YACpB,MACA,aACA,SACsB;AACtB,KAAI,OAAQ,QAAO;CAInB,MAAM,MAAM,GAAG,KAAK,QAAQ,IAAI,cAAc,YAAY,CAAC,IACzD,WAAW;AAEb,KAAI,UAAU,OAAO,QAAQ,OAAO,KAAK,KAAK,GAAG,OAAO,UACtD,QAAO,OAAO;AAEhB,KAAI,YAAY,SAAS,QAAQ,IAAK,QAAO,SAAS;CACtD,MAAM,UAAU,mBAAmB,MAAM,aAAa,KAAK,QAAQ;AACnE,YAAW;EAAE;EAAK;EAAS;AAC3B,KAAI;AACF,SAAO,MAAM;WACL;AACR,MAAI,UAAU,YAAY,QAAS,YAAW;;;AAIlD,eAAe,mBACb,MACA,aACA,KACA,SACsB;AACtB,KAAI,CAAC,SAAS;AAGZ,YAAU,mDAAmD;AAC7D,QAAM,IAAI,kBACR,oDACD;;CAEH,MAAM,SAAS,MAAM,iBAAiB,MAAM,aAAa,QAAQ;AAEjE,MADe,MAAM,sBAAsB,OAAO,WAAW,EAClD,WAAA,UACT,OAAM,IAAI,YACR,kEACA,KAAA,GACA,WAAW,iBACZ;CAEH,MAAM,cAAc,KAAK,MAAM,OAAO,UAAU;CAChD,MAAM,QAAQ,cAAc,KAAK,KAAK;AACtC,KAAI,CAAC,OAAO,SAAS,YAAY,IAAI,QAAQ,mBAAmB;AAG9D,YACE,wCAAwC,MAAM,6BAC/C;AACD,QAAM,IAAI,kBACR,2CAA2C,MAAM,YAClD;;CAEH,MAAM,YAAY,KAAK,KAAK,GAAG,QAAQ;AAGvC,WACE,4CAA4C,QAAQ,QAClD,OAAO,UAAU,UAClB,OAAO,KAAK,MAAM,QAAQ,IAAK,CAAC,QAAQ,OAAO,aACjD;CACD,MAAM,OAAoB;EACxB,YAAY,OAAO;EACnB,OAAO,OAAO;EACd,QAAQ,OAAO;EACf,aAAa;EACd;AACD,UAAS;EAAE;EAAK;EAAM;EAAW;AACjC,QAAO;;;AAWT,SAAgB,cAAc,MAAmB,MAAM,KAAK,KAAK,EAAW;AAC1E,QAAO,OAAO,KAAK;;;;;;;AAQrB,SAAgB,oBAAoB,OAAe,SAA0B;CAC3E,IAAI;AACJ,KAAI;AACF,QAAM,IAAI,IAAI,MAAM;SACd;AACN,SAAO;;AAIT,KACE,IAAI,aAAa,OACjB,IAAI,UACJ,IAAI,QACJ,IAAI,YACJ,IAAI,SAEJ,QAAO;AAOT,KAJE,IAAI,aAAa,eACjB,IAAI,aAAa,eACjB,IAAI,aAAa,uBAEJ,QAAO;AACtB,KAAI,IAAI,aAAa,SAAU,QAAO;AACtC,KAAI,IAAI,SAAS,SAAS,eAAe,CACvC,QACE,IAAI,WAAW,uCACf,IAAI,WAAW;AAGnB,KAAI;AACF,SAAO,IAAI,aAAa,IAAI,IAAI,QAAQ,CAAC;SACnC;AACN,SAAO;;;;;;;;;AAiBX,IAAa,qBAAb,cAAwC,YAAY;CAClD;;CAEA;CAEA,YAAY,QAAgB,SAAiB,SAAkB;AAC7D,QAAM,SAAS;GAAE;GAAQ;GAAS,EAAE,WAAW,mBAAmB;AAClE,OAAK,OAAO;AACZ,OAAK,SAAS;AACd,OAAK,UAAU;;;;;;;AAQnB,IAAa,oBAAb,cAAuC,YAAY;CACjD,YAAY,SAAiB;AAC3B,QAAM,SAAS,KAAA,GAAW,WAAW,kBAAkB;AACvD,OAAK,OAAO;;;;;;;;;AAUhB,SAAS,cAAc,QAAyB;AAC9C,QACE,WAAW,OACX,WAAW,OACX,WAAW,OACX,WAAW,OACX,WAAW;;;;;;;;;AAgBf,SAAS,iBAAiB,OAAwB;AAChD,KAAI,OAAO,UAAU,SAAU,QAAO;AAKtC,QAAO,MAAM,QAAQ,iCAAiC,IAAI,CAAC,MAAM;;AAGnE,eAAe,YAAY,MAAsC;AAC/D,KAAI;EACF,MAAM,OAAQ,MAAM,KAAK,MAAM;EAK/B,MAAM,SAAS,iBAAiB,MAAM,OAAO;EAG7C,MAAM,UACJ,iBAAiB,MAAM,KAAK,IAAI,iBAAiB,MAAM,QAAQ;AACjE,SAAO;GACL,QACE,OAAO,SAAS,KAAK,OAAO,UAAU,4BAClC,SACA,KAAA;GACN,SACE,QAAQ,SAAS,KAAK,QAAQ,UAAU,6BACpC,UACA,KAAA;GACP;SACK;AACN,SAAO,EAAE;;;AAIb,SAAS,mBAAmB,QAAgB,QAAyB;AACnE,KAAI,OAAQ,QAAO;AACnB,SAAQ,QAAR;EACE,KAAK,IACH,QAAO;EACT,KAAK,IACH,QAAO;EACT,KAAK,IAEH,QAAO;EACT,KAAK,IACH,QAAO;EACT,KAAK,IACH,QAAO;EACT,QACE,QAAO;;;AAIb,eAAe,iBACb,MACA,aACA,SACsB;AACtB,KAAI;EACF,MAAM,OAAO,MAAM,MAAM,GAAG,KAAK,QAAQ,6BAA6B;GACpE,QAAQ;GACR,SAAS;IACP,eAAe,UAAU;IACzB,gBAAgB;IACjB;GAID,MAAM,KAAK,UAAU;IAAE;IAAS,sBAAsB;IAAM,CAAC;GAC7D,QAAQ,YAAY,QAAQ,gBAAgB;GAC7C,CAAC;AACF,MAAI,CAAC,KAAK,IAAI;AACZ,OAAI,cAAc,KAAK,OAAO,EAAE;IAC9B,MAAM,UAAU,MAAM,YAAY,KAAK;AACvC,cACE,oCAAoC,KAAK,OAAO,IAC9C,QAAQ,WAAW,aACpB,oBACF;AAGD,cAAU,cAAc,wBAAwB;KAC9C,QAAQ,KAAK;KACb,SAAS,QAAQ;KACjB;KACD,CAAC;AACF,UAAM,IAAI,mBACR,KAAK,QACL,mBAAmB,KAAK,QAAQ,QAAQ,OAAO,EAC/C,QAAQ,QACT;;AAEH,aACE,mCAAmC,KAAK,OAAO,mBAChD;AACD,SAAM,IAAI,kBACR,qDAAqD,KAAK,OAAO,GAClE;;EAEH,MAAM,OAAQ,MAAM,KAAK,MAAM;AAQ/B,MAAI,CAAC,KAAK,OAAO;AACf,aAAU,yDAAyD;AACnE,SAAM,IAAI,kBAAkB,8BAA8B;;AAE5D,MAAI,CAAC,KAAK,YAAY;AACpB,aAAU,8DAA8D;AACxE,SAAM,IAAI,kBAAkB,mCAAmC;;AAEjE,MAAI,CAAC,KAAK,aAAa;AACrB,aAAU,+DAA+D;AACzE,SAAM,IAAI,kBAAkB,oCAAoC;;AAElE,MAAI,CAAC,oBAAoB,KAAK,aAAa,KAAK,QAAQ,EAAE;AACxD,aACE,oEACD;AACD,SAAM,IAAI,kBAAkB,yCAAyC;;EAEvE,MAAM,aAAa,IAAI,IAAI,KAAK,YAAY;AAC5C,MACE,CAAC,aAAa,YAAY,CAAC,SAAS,IAAI,IAAI,KAAK,QAAQ,CAAC,SAAS,IACnE,WAAW,aAAa,uBAExB,YAAW,WAAW;AAExB,SAAO;GACL,OAAO,KAAK;GACZ,WAAW,KAAK;GAChB,YAAY,WAAW;GACvB,QAAQ,KAAK;GACd;UACM,GAAG;AAGV,MAAI,aAAa,sBAAsB,aAAa,kBAClD,OAAM;AACR,YACE,qCAAqC,OAAO,EAAE,CAAC,oBAChD;AACD,QAAM,IAAI,kBACR,wCAAwC,OAAO,EAAE,CAAC,GACnD;;;;;;;;;;AAWL,SAAgB,0BACd,gBACA,aACA,QACQ;CAIR,MAAM,QAAyC,EAAE,YAAY,UAAU;AACvE,KAAI,WAAW,KAAA,EAAW,OAAM,UAAU;AAC1C,MAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,eAAe,CACvD,OAAM,oBAAoB,IAAI,IAAI;AAEpC,MAAK,MAAM,CAAC,SAAS,YAAY,OAAO,QAAQ,YAAY,EAAE;AAC5D,MAAI,CAAC,QAAQ,aAAa,CAAC,WAAW,SAAS,CAAE;AACjD,QAAM,eAAe,QAAQ,aAAa,MAAM;;AAElD,QAAO,KAAK,UAAU,MAAM;;AAG9B,MAAM,yBAAyB;AAE/B,SAAS,oBAAoB,KAAqB;AAChD,QAAO,IAAI,aAAa,CAAC,WAAW,uBAAuB,GACvD,IAAI,MAAM,GAA8B,CAAC,aAAa,GACtD;;;;;;;;;ACheN,MAAa,eAAe;CAE1B,QAAQ;CAER,mBAAmB;CAEnB,wBAAwB;CAQxB,sBAAsB;CAMtB,OAAO;CAEP,eAAe;CAEf,WAAW;CAMX,eAAe;CAMf,cAAc;CACf;;;;;;;AAUD,MAAa,qBAAqB,6CAA6C,aAAa,cAAc;;;;;AAM1G,MAAa,qBACX;;;;;;;;;;;ACxCF,MAAa,mBAAmB;;;;;;;;;;;AAkBhC,SAAgB,cAAc,SAA6B;AACzD,QAAO,QAAQ,WAAW,UAAU,GAChC,qBACA;;;;;;;;AASN,SAAgB,oBACd,gBACA,aACA,QACwB;AACxB,QAAO;EACL,kBAAkB;EAClB,wBAAwB,0BACtB,gBACA,aACA,OACD;EACF;;;;;;;AAsBH,SAAgB,kBAAkB,QAA+B;CAC/D,MAAM,EAAE,YAAY,gBAAgB,aAAa,SAAS,WAAW;CACrE,MAAM,MAAM,cAAc,QAAQ;AAClC,QAAO;EACL,IAAI;EACJ,MAAM,GAAG,QAAQ;EACjB;EACA,UAAU;EAEV,SAAS,QAAQ,uBAAuB,aAAa,GAAG,WAAW;EAInE,WAAW,kBAAkB,QAAQ,CAAC;EACtC,OAAO,CAAC,OAAgB;EACxB,MAAM;GAAE,OAAO;GAAG,QAAQ;GAAG,WAAW;GAAG,YAAY;GAAG;EAC1D,eAAe;EACf,WAAW;EACX,SAAS,oBAAoB,gBAAgB,aAAa,OAAO;EAClE;;;;;;AAOH,SAAgB,qBAAqB,QAMnC;CACA,MAAM,EAAE,YAAY,aAAa,SAAS,WAAW;CACrD,MAAM,MAAM,cAAc,QAAQ;CAIlC,MAAM,YAAY,kBAAkB,QAAQ;CAC5C,MAAM,OACJ,UAAU,WAAW,SAAS,UAAU,YACpC,kBAAkB,SAAS,OAAO,GAClC,WAAW,SAAS,UAAU,YAC9B;EAAE,GAAG;EAAW,eAAe;EAAgB,GAC/C;CAEN,MAAM,QAAQ,kBAAkB,OAAO;AAUvC,QAAO;EAAE,UATQ;GACf,MAAM;GACN,SAAS,MAAM;GACf,QAAQ;GACR,YAAY;GACZ;GACA,SAAS,MAAM;GACf,QAAQ,CAAC,MAAM;GAChB;EACkB;EAAK;EAAM;EAAY,SAAS,MAAM;EAAS;;AAkBpE,SAAgB,uBACd,MACoC;AACpC,KAAI,CAAC,QAAS,KAAK,eAAe,WAAW,KAAK,eAAe,UAC/D,QAAO,KAAA;CACT,MAAM,UACJ,KAAK,iBACJ,KAAK,eAAe,YACjB,uBACA;AACN,KAAI,KAAK,eAAe,UACtB,QAAO;EAAE,gBAAA;EAAsC;EAAS;CAM1D,MAAM,oBAHJ,KAAK,aACD,KAAK,MAAM,EAAE,OAAO,KAAK,CAC1B,QAAQ,SAAS,SAAS,KAAA,EAAU,IAAI,EAAE,EAE5C,IAAI,OAAO,CACX,MAAM,SAAS,OAAO,UAAU,KAAK,IAAI,QAAQ,OAAO,OAAO,IAAI;AAKtE,QAAO;EACL,iBAJA,qBAAqB,KAAA,IACjB,qBAAqB,MACrB,sBAAsB,KAAK,QAAQ,IAAA,sBAAA;EAKvC;EACA,QACE,qBAAqB,uBAAuB,KAAK,GAAG,MAAM,KAAA;EAC7D;;;;;;;;AASH,SAAgB,uBACd,MACS;CACT,MAAM,EAAE,cAAc,gBACpB,OAAO,SAAS,WACZ;EAAE,cAAc;EAAM,aAAa,KAAA;EAAW,GAC9C,QAAQ,EAAE;CAChB,MAAM,oBAAoB,eAAe,EAAE,EACxC,KAAK,eAAe,OAAO,WAAW,OAAO,KAAK,CAAC,CACnD,MAAM,SAAS,OAAO,UAAU,KAAK,IAAI,QAAQ,OAAO,OAAO,IAAI;AACtE,KAAI,qBAAqB,KAAA,EAAW,QAAO,qBAAqB;AAChE,MAAK,MAAM,cAAc,eAAe,EAAE,EAAE;EAC1C,MAAM,OAAO,WAAW,OAAO;AAC/B,MAAI,SAAS,OAAO,SAAS,MAAO,QAAO;AAC3C,MAAI,2BAA2B,KAAK,WAAW,OAAO,QAAQ,GAAG,CAC/D,QAAO;;AAEX,QAAO,6CAA6C,KAAK,gBAAgB,GAAG;;;;;;;;AAsB9E,SAAgB,kBAAkB,MAKhC;CACA,IAAI,OAAO,KAAK;CAChB,IAAI,WAAW;CACf,IAAI,WAAW;CACf,IAAI;AACJ,QAAO;EACL,kBAAkB,SAAS;AACzB,cAAW;AAGX,cACE,UAAU,eAAe,WAAW,uBAAuB,SAAS;;EAExE,uBAAuB,uBAAuB,SAAS;EACvD,MAAM,OAAO,MAAM;AACjB,OAAI,KAAK,QAAQ,QAAS;AAC1B,cAAW;AACX,cAAW,KAAA;AACX,SAAM,KAAK,QAAQ,OAAO,KAAK;AAC/B,OAAI,KAAK,QAAQ,WAAW,CAAC,YAAY,YAAY,CAAC,cAAc,KAAK,CACvE;AACF,cAAW;AACX,UAAO,MAAM,KAAK,aAAa;AAC/B,OAAI,KAAK,QAAQ,QAAS;AAC1B,QAAK,SAAS,iBACZ,kBACA,qBAAqB,KAAK,eAAe,KAAK,CAAC,CAAC,SACjD;AACD,QAAK,YAAY;AACjB,cAAW;AACX,cAAW,KAAA;GACX,MAAM,OAAO,KAAK;AAClB,OAAI,KAAK,QAAQ,QAAS;AAC1B,SAAM,KAAK,QAAQ,OAAO,OAAO,SAAS,aAAa,MAAM,GAAG,KAAK;;EAExE"}
@@ -1,10 +1,10 @@
1
1
  //#region src/headless/index.ts
2
2
  /** Run `config` headlessly; resolves with the exit code. */
3
3
  async function runHeadless(config, launch) {
4
- const { runHeadless: run } = await import("./run-C6Ei6vvb.js");
4
+ const { runHeadless: run } = await import("./run-CfMArkpV.js");
5
5
  return run(config, launch);
6
6
  }
7
7
  //#endregion
8
8
  export { runHeadless as t };
9
9
 
10
- //# sourceMappingURL=headless-DhTDC9ga.js.map
10
+ //# sourceMappingURL=headless-Bfi950Qh.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"headless-DhTDC9ga.js","names":[],"sources":["../src/headless/index.ts"],"sourcesContent":["/** Headless's one entry for other layers; `runHeadless` loads the host on first call. */\nimport type { ProgramConfig } from '@programs/types';\nimport type { HeadlessLaunch } from './run';\n\nexport type { HeadlessLaunch, NonInteractiveMode } from './run';\nexport { modeLabel } from './mode-label';\n\n/** Run `config` headlessly; resolves with the exit code. */\nexport async function runHeadless(\n config: ProgramConfig,\n launch: HeadlessLaunch,\n): Promise<number> {\n const { runHeadless: run } = await import('./run');\n return run(config, launch);\n}\n"],"mappings":";;AAQA,eAAsB,YACpB,QACA,QACiB;CACjB,MAAM,EAAE,aAAa,QAAQ,MAAM,OAAO;AAC1C,QAAO,IAAI,QAAQ,OAAO"}
1
+ {"version":3,"file":"headless-Bfi950Qh.js","names":[],"sources":["../src/headless/index.ts"],"sourcesContent":["/** Headless's one entry for other layers; `runHeadless` loads the host on first call. */\nimport type { ProgramConfig } from '@programs/types';\nimport type { HeadlessLaunch } from './run';\n\nexport type { HeadlessLaunch, NonInteractiveMode } from './run';\nexport { modeLabel } from './mode-label';\n\n/** Run `config` headlessly; resolves with the exit code. */\nexport async function runHeadless(\n config: ProgramConfig,\n launch: HeadlessLaunch,\n): Promise<number> {\n const { runHeadless: run } = await import('./run');\n return run(config, launch);\n}\n"],"mappings":";;AAQA,eAAsB,YACpB,QACA,QACiB;CACjB,MAAM,EAAE,aAAa,QAAQ,MAAM,OAAO;AAC1C,QAAO,IAAI,QAAQ,OAAO"}
@@ -1,3 +1,3 @@
1
1
  import "./mode-label-DB3ol_sj.js";
2
- import { t as runHeadless } from "./headless-DhTDC9ga.js";
2
+ import { t as runHeadless } from "./headless-Bfi950Qh.js";
3
3
  export { runHeadless };
@@ -1,6 +1,6 @@
1
- import "./debug-sB20p-Ek.js";
2
- import { at as MCP_LOCAL_URL } from "./analytics-CGGE7OSa.js";
3
- import { n as getCloudUrl, o as getUiHostFromHost, r as getHost, s as resolveBaseUrl, t as detectRegion } from "./urls-eFaChZ9d.js";
1
+ import "./debug-BvEbemym.js";
2
+ import { at as MCP_LOCAL_URL } from "./analytics-B_xFshPY.js";
3
+ import { n as getCloudUrl, o as getUiHostFromHost, r as getHost, s as resolveBaseUrl, t as detectRegion } from "./urls-B82v7gfO.js";
4
4
  //#region src/shared/host-resolution.ts
5
5
  /**
6
6
  * HostResolution — the single, immutable snapshot of where the wizard talks to
@@ -111,4 +111,4 @@ var HostResolution = class HostResolution {
111
111
  //#endregion
112
112
  export { mcpUrlFor as n, HostResolution as t };
113
113
 
114
- //# sourceMappingURL=host-resolution-DDYJXiAQ.js.map
114
+ //# sourceMappingURL=host-resolution-BxnNn8Jm.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"host-resolution-DDYJXiAQ.js","names":[],"sources":["../src/shared/host-resolution.ts"],"sourcesContent":["/**\n * HostResolution — the single, immutable snapshot of where the wizard talks to\n * PostHog for one run.\n *\n * One cloud region (or a pinned `--base-url`) implies a whole family of hosts:\n * the event-ingestion/API host, the user-facing web app host, the CDN asset\n * host, the LLM gateway, and the MCP server. Rather than re-deriving each of\n * these at every call site and threading `region` + `baseUrl` around as loose\n * params, resolve once at auth time and pass this frozen object around — read\n * the property you need.\n *\n * The actual region→URL math (and the `--base-url` override) lives in\n * `@utils/urls`; this class is a thin, immutable façade over those resolvers so\n * the override flows through every field without callers having to know about\n * it. The OAuth-server URL stays in `@utils/urls` (`getOAuthUrl`) because it is\n * needed *before* a region is known, so it can't come from this post-auth object.\n */\n\nimport {\n getHost,\n getCloudUrl,\n getUiHostFromHost,\n detectRegion,\n resolveBaseUrl,\n} from '@utils/urls';\nimport { IS_PRODUCTION_BUILD, runtimeEnv } from '@env';\nimport { MCP_LOCAL_URL } from '@shared/local-dev';\nimport type { CloudRegion } from '@utils/types';\n\n// The wizard's client gets CLI mode (a single `exec` tool) by server default,\n// so no mode is pinned; the dev env override (see env.ts) is taken verbatim.\nconst MCP_PROD_URL = 'https://mcp.posthog.com/mcp';\n\n/** Construction-time inputs that aren't implied by the region. */\nexport interface HostResolutionOptions {\n /** `--local-mcp`: the MCP url only; skills follow `--local-context-mill`. */\n localMcp?: boolean;\n /** `--base-url`: pin every PostHog origin to one URL, bypassing region resolution. */\n baseUrl?: string;\n /** `--region`: trust the explicitly-provided region. */\n region?: CloudRegion;\n}\n\nfunction assetHostFor(region: CloudRegion, baseUrl?: string): string {\n const override = resolveBaseUrl(baseUrl);\n if (override) return override;\n return region === 'eu'\n ? 'https://eu-assets.i.posthog.com'\n : 'https://us-assets.i.posthog.com';\n}\n\nfunction assetHostFromApiHost(apiHost: string): string {\n if (apiHost.includes('us.i.posthog.com')) {\n return 'https://us-assets.i.posthog.com';\n }\n if (apiHost.includes('eu.i.posthog.com')) {\n return 'https://eu-assets.i.posthog.com';\n }\n return apiHost;\n}\n\n/**\n * Dev/CI only: the env override (see env.ts) wins even under --local-mcp;\n * published builds strip the read. Don't name that env var in comments here —\n * smoke-test.sh greps dist/*.js for it, and JSDoc survives bundling.\n */\nexport function mcpUrlFor(localMcp: boolean): string {\n const override = IS_PRODUCTION_BUILD ? undefined : runtimeEnv('MCP_URL');\n if (override) return override;\n return localMcp ? MCP_LOCAL_URL : MCP_PROD_URL;\n}\n\nexport class HostResolution {\n /** The resolved cloud region. `'us'` when a base URL is pinned. */\n readonly region: CloudRegion;\n /**\n * Event-ingestion / REST API host (e.g. `https://us.i.posthog.com`, or the\n * pinned `--base-url`). The SDK `host` written into the user's `.env`, the\n * base for the wizard-session REST calls, and the host shown to the agent.\n */\n readonly apiHost: string;\n /**\n * User-facing web app host (e.g. `https://us.posthog.com`). Use for any link\n * we hand to the user or open in their browser (dashboards, settings, inbox,\n * deep-link base).\n */\n readonly appHost: string;\n /** CDN asset host (e.g. `https://us-assets.i.posthog.com`). */\n readonly assetHost: string;\n /**\n * PostHog MCP server URL the agent connects to. Region-independent — the\n * server resolves the user's region from the bearer token — so this is driven\n * only by `--local-mcp` / `--local-dev` and the dev env override, not by\n * region/base-url.\n */\n readonly mcpUrl: string;\n\n private constructor(fields: {\n region: CloudRegion;\n apiHost: string;\n appHost: string;\n assetHost: string;\n mcpUrl: string;\n }) {\n this.region = fields.region;\n this.apiHost = fields.apiHost;\n this.appHost = fields.appHost;\n this.assetHost = fields.assetHost;\n this.mcpUrl = fields.mcpUrl;\n Object.freeze(this);\n }\n\n /**\n * Canonical path: build the host family from a resolved region. A pinned\n * `--base-url` (via `opts.baseUrl`) wins for every origin; otherwise the\n * region's standard hosts are used. Honors IS_DEV through `resolveBaseUrl`.\n */\n static fromRegion(\n region: CloudRegion,\n opts: HostResolutionOptions = {},\n ): HostResolution {\n const apiHost = getHost(region, opts.baseUrl);\n return new HostResolution({\n region,\n apiHost,\n appHost: getCloudUrl(region, opts.baseUrl),\n assetHost: assetHostFor(region, opts.baseUrl),\n mcpUrl: mcpUrlFor(opts.localMcp ?? false),\n });\n }\n\n /**\n * Build from an ingestion host string (the provisioning API returns one). The\n * given host is preserved verbatim as `apiHost`; the region and the derived\n * app/asset/gateway hosts are inferred from it.\n */\n static fromApiHost(\n apiHost: string,\n opts: Pick<HostResolutionOptions, 'localMcp'> = {},\n ): HostResolution {\n const region: CloudRegion = apiHost.includes('eu.') ? 'eu' : 'us';\n return new HostResolution({\n region,\n apiHost,\n appHost: getUiHostFromHost(apiHost),\n assetHost: assetHostFromApiHost(apiHost),\n mcpUrl: mcpUrlFor(opts.localMcp ?? false),\n });\n }\n\n /**\n * Resolve the region from an access token (the us/eu probe, skipped when a\n * base URL is pinned), then build the host family. Used after OAuth and after\n * CI-mode API-key auth.\n */\n static async fromAccessToken(\n accessToken: string,\n opts: HostResolutionOptions = {},\n ): Promise<HostResolution> {\n const region =\n opts.region ?? (await detectRegion(accessToken, opts.baseUrl));\n return HostResolution.fromRegion(region, opts);\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AA+BA,MAAM,eAAe;AAYrB,SAAS,aAAa,QAAqB,SAA0B;CACnE,MAAM,WAAW,eAAe,QAAQ;AACxC,KAAI,SAAU,QAAO;AACrB,QAAO,WAAW,OACd,oCACA;;AAGN,SAAS,qBAAqB,SAAyB;AACrD,KAAI,QAAQ,SAAS,mBAAmB,CACtC,QAAO;AAET,KAAI,QAAQ,SAAS,mBAAmB,CACtC,QAAO;AAET,QAAO;;;;;;;AAQT,SAAgB,UAAU,UAA2B;AAGnD,QAAO,WAAW,gBAAgB;;AAGpC,IAAa,iBAAb,MAAa,eAAe;;CAE1B;;;;;;CAMA;;;;;;CAMA;;CAEA;;;;;;;CAOA;CAEA,YAAoB,QAMjB;AACD,OAAK,SAAS,OAAO;AACrB,OAAK,UAAU,OAAO;AACtB,OAAK,UAAU,OAAO;AACtB,OAAK,YAAY,OAAO;AACxB,OAAK,SAAS,OAAO;AACrB,SAAO,OAAO,KAAK;;;;;;;CAQrB,OAAO,WACL,QACA,OAA8B,EAAE,EAChB;AAEhB,SAAO,IAAI,eAAe;GACxB;GACA,SAHc,QAAQ,QAAQ,KAAK,QAAQ;GAI3C,SAAS,YAAY,QAAQ,KAAK,QAAQ;GAC1C,WAAW,aAAa,QAAQ,KAAK,QAAQ;GAC7C,QAAQ,UAAU,KAAK,YAAY,MAAM;GAC1C,CAAC;;;;;;;CAQJ,OAAO,YACL,SACA,OAAgD,EAAE,EAClC;AAEhB,SAAO,IAAI,eAAe;GACxB,QAF0B,QAAQ,SAAS,MAAM,GAAG,OAAO;GAG3D;GACA,SAAS,kBAAkB,QAAQ;GACnC,WAAW,qBAAqB,QAAQ;GACxC,QAAQ,UAAU,KAAK,YAAY,MAAM;GAC1C,CAAC;;;;;;;CAQJ,aAAa,gBACX,aACA,OAA8B,EAAE,EACP;EACzB,MAAM,SACJ,KAAK,UAAW,MAAM,aAAa,aAAa,KAAK,QAAQ;AAC/D,SAAO,eAAe,WAAW,QAAQ,KAAK"}
1
+ {"version":3,"file":"host-resolution-BxnNn8Jm.js","names":[],"sources":["../src/shared/host-resolution.ts"],"sourcesContent":["/**\n * HostResolution — the single, immutable snapshot of where the wizard talks to\n * PostHog for one run.\n *\n * One cloud region (or a pinned `--base-url`) implies a whole family of hosts:\n * the event-ingestion/API host, the user-facing web app host, the CDN asset\n * host, the LLM gateway, and the MCP server. Rather than re-deriving each of\n * these at every call site and threading `region` + `baseUrl` around as loose\n * params, resolve once at auth time and pass this frozen object around — read\n * the property you need.\n *\n * The actual region→URL math (and the `--base-url` override) lives in\n * `@utils/urls`; this class is a thin, immutable façade over those resolvers so\n * the override flows through every field without callers having to know about\n * it. The OAuth-server URL stays in `@utils/urls` (`getOAuthUrl`) because it is\n * needed *before* a region is known, so it can't come from this post-auth object.\n */\n\nimport {\n getHost,\n getCloudUrl,\n getUiHostFromHost,\n detectRegion,\n resolveBaseUrl,\n} from '@utils/urls';\nimport { IS_PRODUCTION_BUILD, runtimeEnv } from '@env';\nimport { MCP_LOCAL_URL } from '@shared/local-dev';\nimport type { CloudRegion } from '@utils/types';\n\n// The wizard's client gets CLI mode (a single `exec` tool) by server default,\n// so no mode is pinned; the dev env override (see env.ts) is taken verbatim.\nconst MCP_PROD_URL = 'https://mcp.posthog.com/mcp';\n\n/** Construction-time inputs that aren't implied by the region. */\nexport interface HostResolutionOptions {\n /** `--local-mcp`: the MCP url only; skills follow `--local-context-mill`. */\n localMcp?: boolean;\n /** `--base-url`: pin every PostHog origin to one URL, bypassing region resolution. */\n baseUrl?: string;\n /** `--region`: trust the explicitly-provided region. */\n region?: CloudRegion;\n}\n\nfunction assetHostFor(region: CloudRegion, baseUrl?: string): string {\n const override = resolveBaseUrl(baseUrl);\n if (override) return override;\n return region === 'eu'\n ? 'https://eu-assets.i.posthog.com'\n : 'https://us-assets.i.posthog.com';\n}\n\nfunction assetHostFromApiHost(apiHost: string): string {\n if (apiHost.includes('us.i.posthog.com')) {\n return 'https://us-assets.i.posthog.com';\n }\n if (apiHost.includes('eu.i.posthog.com')) {\n return 'https://eu-assets.i.posthog.com';\n }\n return apiHost;\n}\n\n/**\n * Dev/CI only: the env override (see env.ts) wins even under --local-mcp;\n * published builds strip the read. Don't name that env var in comments here —\n * smoke-test.sh greps dist/*.js for it, and JSDoc survives bundling.\n */\nexport function mcpUrlFor(localMcp: boolean): string {\n const override = IS_PRODUCTION_BUILD ? undefined : runtimeEnv('MCP_URL');\n if (override) return override;\n return localMcp ? MCP_LOCAL_URL : MCP_PROD_URL;\n}\n\nexport class HostResolution {\n /** The resolved cloud region. `'us'` when a base URL is pinned. */\n readonly region: CloudRegion;\n /**\n * Event-ingestion / REST API host (e.g. `https://us.i.posthog.com`, or the\n * pinned `--base-url`). The SDK `host` written into the user's `.env`, the\n * base for the wizard-session REST calls, and the host shown to the agent.\n */\n readonly apiHost: string;\n /**\n * User-facing web app host (e.g. `https://us.posthog.com`). Use for any link\n * we hand to the user or open in their browser (dashboards, settings, inbox,\n * deep-link base).\n */\n readonly appHost: string;\n /** CDN asset host (e.g. `https://us-assets.i.posthog.com`). */\n readonly assetHost: string;\n /**\n * PostHog MCP server URL the agent connects to. Region-independent — the\n * server resolves the user's region from the bearer token — so this is driven\n * only by `--local-mcp` / `--local-dev` and the dev env override, not by\n * region/base-url.\n */\n readonly mcpUrl: string;\n\n private constructor(fields: {\n region: CloudRegion;\n apiHost: string;\n appHost: string;\n assetHost: string;\n mcpUrl: string;\n }) {\n this.region = fields.region;\n this.apiHost = fields.apiHost;\n this.appHost = fields.appHost;\n this.assetHost = fields.assetHost;\n this.mcpUrl = fields.mcpUrl;\n Object.freeze(this);\n }\n\n /**\n * Canonical path: build the host family from a resolved region. A pinned\n * `--base-url` (via `opts.baseUrl`) wins for every origin; otherwise the\n * region's standard hosts are used. Honors IS_DEV through `resolveBaseUrl`.\n */\n static fromRegion(\n region: CloudRegion,\n opts: HostResolutionOptions = {},\n ): HostResolution {\n const apiHost = getHost(region, opts.baseUrl);\n return new HostResolution({\n region,\n apiHost,\n appHost: getCloudUrl(region, opts.baseUrl),\n assetHost: assetHostFor(region, opts.baseUrl),\n mcpUrl: mcpUrlFor(opts.localMcp ?? false),\n });\n }\n\n /**\n * Build from an ingestion host string (the provisioning API returns one). The\n * given host is preserved verbatim as `apiHost`; the region and the derived\n * app/asset/gateway hosts are inferred from it.\n */\n static fromApiHost(\n apiHost: string,\n opts: Pick<HostResolutionOptions, 'localMcp'> = {},\n ): HostResolution {\n const region: CloudRegion = apiHost.includes('eu.') ? 'eu' : 'us';\n return new HostResolution({\n region,\n apiHost,\n appHost: getUiHostFromHost(apiHost),\n assetHost: assetHostFromApiHost(apiHost),\n mcpUrl: mcpUrlFor(opts.localMcp ?? false),\n });\n }\n\n /**\n * Resolve the region from an access token (the us/eu probe, skipped when a\n * base URL is pinned), then build the host family. Used after OAuth and after\n * CI-mode API-key auth.\n */\n static async fromAccessToken(\n accessToken: string,\n opts: HostResolutionOptions = {},\n ): Promise<HostResolution> {\n const region =\n opts.region ?? (await detectRegion(accessToken, opts.baseUrl));\n return HostResolution.fromRegion(region, opts);\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AA+BA,MAAM,eAAe;AAYrB,SAAS,aAAa,QAAqB,SAA0B;CACnE,MAAM,WAAW,eAAe,QAAQ;AACxC,KAAI,SAAU,QAAO;AACrB,QAAO,WAAW,OACd,oCACA;;AAGN,SAAS,qBAAqB,SAAyB;AACrD,KAAI,QAAQ,SAAS,mBAAmB,CACtC,QAAO;AAET,KAAI,QAAQ,SAAS,mBAAmB,CACtC,QAAO;AAET,QAAO;;;;;;;AAQT,SAAgB,UAAU,UAA2B;AAGnD,QAAO,WAAW,gBAAgB;;AAGpC,IAAa,iBAAb,MAAa,eAAe;;CAE1B;;;;;;CAMA;;;;;;CAMA;;CAEA;;;;;;;CAOA;CAEA,YAAoB,QAMjB;AACD,OAAK,SAAS,OAAO;AACrB,OAAK,UAAU,OAAO;AACtB,OAAK,UAAU,OAAO;AACtB,OAAK,YAAY,OAAO;AACxB,OAAK,SAAS,OAAO;AACrB,SAAO,OAAO,KAAK;;;;;;;CAQrB,OAAO,WACL,QACA,OAA8B,EAAE,EAChB;AAEhB,SAAO,IAAI,eAAe;GACxB;GACA,SAHc,QAAQ,QAAQ,KAAK,QAAQ;GAI3C,SAAS,YAAY,QAAQ,KAAK,QAAQ;GAC1C,WAAW,aAAa,QAAQ,KAAK,QAAQ;GAC7C,QAAQ,UAAU,KAAK,YAAY,MAAM;GAC1C,CAAC;;;;;;;CAQJ,OAAO,YACL,SACA,OAAgD,EAAE,EAClC;AAEhB,SAAO,IAAI,eAAe;GACxB,QAF0B,QAAQ,SAAS,MAAM,GAAG,OAAO;GAG3D;GACA,SAAS,kBAAkB,QAAQ;GACnC,WAAW,qBAAqB,QAAQ;GACxC,QAAQ,UAAU,KAAK,YAAY,MAAM;GAC1C,CAAC;;;;;;;CAQJ,aAAa,gBACX,aACA,OAA8B,EAAE,EACP;EACzB,MAAM,SACJ,KAAK,UAAW,MAAM,aAAa,aAAa,KAAK,QAAQ;AAC/D,SAAO,eAAe,WAAW,QAAQ,KAAK"}
@@ -1,7 +1,7 @@
1
- import { o as logToFile, v as runtimeEnv } from "./debug-sB20p-Ek.js";
2
- import { n as analytics } from "./analytics-CGGE7OSa.js";
3
- import { t as openTrackedLink } from "./links-rMot83W2.js";
4
- import { a as scrubHomePaths, i as reportSpawnFailure$1, r as redactSecrets, s as toClientResult } from "./results-ycm_Tf31.js";
1
+ import { o as logToFile, v as runtimeEnv } from "./debug-BvEbemym.js";
2
+ import { n as analytics } from "./analytics-B_xFshPY.js";
3
+ import { t as openTrackedLink } from "./links-Dmj3Wj9X.js";
4
+ import { a as scrubHomePaths, i as reportSpawnFailure$1, r as redactSecrets, s as toClientResult } from "./results-CDqlHogZ.js";
5
5
  import { a as getNativeHTTPServerConfig, i as getDefaultServerConfig, r as buildMCPUrl } from "./defaults-DDVl3tKN.js";
6
6
  import * as fs$2 from "fs";
7
7
  import * as path$3 from "path";
@@ -1124,4 +1124,4 @@ const removeMCPServer = async (clients, local) => {
1124
1124
  //#endregion
1125
1125
  export { installPlugins as a, getSupportedPluginClients as i, getInstalledClients as n, removeMCPServer as o, getSupportedClients as r, isPluginCapable as s, addMCPServer as t };
1126
1126
 
1127
- //# sourceMappingURL=install-Dh8Z5R15.js.map
1127
+ //# sourceMappingURL=install-CDVeUT_5.js.map