@posthog/wizard 2.81.1 → 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.
- package/dist/{agent-interface-DMP3DZRd.js → agent-interface-3YfIWZmY.js} +211 -46
- package/dist/agent-interface-3YfIWZmY.js.map +1 -0
- package/dist/{agent-prompt-loader-BrivWIna.js → agent-prompt-loader-hOHONY7u.js} +6 -6
- package/dist/{agent-prompt-loader-BrivWIna.js.map → agent-prompt-loader-hOHONY7u.js.map} +1 -1
- package/dist/{analytics-X7JfAmh6.js → analytics-B_xFshPY.js} +4 -3
- package/dist/analytics-B_xFshPY.js.map +1 -0
- package/dist/analytics-DGDr6-iD.js +2 -0
- package/dist/{api-BkDwZoL4.js → api-C_9KsN-7.js} +71 -3
- package/dist/{api-BkDwZoL4.js.map → api-C_9KsN-7.js.map} +1 -1
- package/dist/api-DhLSO3ai.js +2 -0
- package/dist/api-key-login-DiRuZ8Lm.js +2 -0
- package/dist/{api-key-login-DE0yKkP4.js → api-key-login-oUJQDmOV.js} +5 -5
- package/dist/{api-key-login-DE0yKkP4.js.map → api-key-login-oUJQDmOV.js.map} +1 -1
- package/dist/bin.js +1 -1
- package/dist/{bounded-fs-iDiqDQ9v.js → bounded-fs-iRaQcKYL.js} +6 -4
- package/dist/bounded-fs-iRaQcKYL.js.map +1 -0
- package/dist/{ci-install-BAftKh2Z.js → ci-install-BGlijoTo.js} +4 -4
- package/dist/{ci-install-BAftKh2Z.js.map → ci-install-BGlijoTo.js.map} +1 -1
- package/dist/{debug-C3tLP-ZW.js → debug-BvEbemym.js} +3 -3
- package/dist/{debug-C3tLP-ZW.js.map → debug-BvEbemym.js.map} +1 -1
- package/dist/{gateway-IQjQ582o.js → gateway-CWoYlPz7.js} +5 -5
- package/dist/{gateway-IQjQ582o.js.map → gateway-CWoYlPz7.js.map} +1 -1
- package/dist/{headless-DOhxEmw5.js → headless-Bfi950Qh.js} +2 -2
- package/dist/{headless-DOhxEmw5.js.map → headless-Bfi950Qh.js.map} +1 -1
- package/dist/{headless-XNvdWVEp.js → headless-Gisv3faN.js} +1 -1
- package/dist/{host-resolution-DI2oucSK.js → host-resolution-BxnNn8Jm.js} +4 -4
- package/dist/{host-resolution-DI2oucSK.js.map → host-resolution-BxnNn8Jm.js.map} +1 -1
- package/dist/{install-BixbBDLR.js → install-CDVeUT_5.js} +5 -5
- package/dist/{install-BixbBDLR.js.map → install-CDVeUT_5.js.map} +1 -1
- package/dist/{install-DaZ6pvEl.js → install-CX65IxGa.js} +1 -1
- package/dist/{interactive-WLNQWF0D.js → interactive-5-Numwwb.js} +3 -3
- package/dist/{interactive-WLNQWF0D.js.map → interactive-5-Numwwb.js.map} +1 -1
- package/dist/{links-Bye4fsrj.js → links-Dmj3Wj9X.js} +3 -3
- package/dist/{links-Bye4fsrj.js.map → links-Dmj3Wj9X.js.map} +1 -1
- package/dist/{main-CN8nbuZO.js → main-5KNn80Vt.js} +20 -20
- package/dist/{main-CN8nbuZO.js.map → main-5KNn80Vt.js.map} +1 -1
- package/dist/{mcp-B2o4d9DS.js → mcp-Buf9sfFm.js} +3 -3
- package/dist/{mcp-B2o4d9DS.js.map → mcp-Buf9sfFm.js.map} +1 -1
- package/dist/{mcp-prompt-streaming-COs1DfAm.js → mcp-prompt-streaming-DHPxJK-F.js} +5 -5
- package/dist/{mcp-prompt-streaming-COs1DfAm.js.map → mcp-prompt-streaming-DHPxJK-F.js.map} +1 -1
- package/dist/{models-Cow22jET.js → models-C9rjXPZ6.js} +2 -2
- package/dist/{models-Cow22jET.js.map → models-C9rjXPZ6.js.map} +1 -1
- package/dist/{oauth-session-Bj_-BY7o.js → oauth-session-Bi1MLRCC.js} +45 -3
- package/dist/oauth-session-Bi1MLRCC.js.map +1 -0
- package/dist/{orchestrator-tools-lhFIBGnp.js → orchestrator-tools-DyKws92o.js} +3 -3
- package/dist/{orchestrator-tools-lhFIBGnp.js.map → orchestrator-tools-DyKws92o.js.map} +1 -1
- package/dist/{package-manager-DZ4wcTz3.js → package-manager-DpCdmMmB.js} +4 -4
- package/dist/{package-manager-DZ4wcTz3.js.map → package-manager-DpCdmMmB.js.map} +1 -1
- package/dist/{pi-DREdQRPP.js → pi-DTj1hsJe.js} +32 -15
- package/dist/pi-DTj1hsJe.js.map +1 -0
- package/dist/{playground-CJx3lwTH.js → playground-CiH6zZPb.js} +4 -4
- package/dist/{playground-CJx3lwTH.js.map → playground-CiH6zZPb.js.map} +1 -1
- package/dist/{posthog-integration-DDSPdbpw.js → posthog-integration-CpinLWNO.js} +78 -35
- package/dist/posthog-integration-CpinLWNO.js.map +1 -0
- package/dist/{programs-6SFfUBmm.js → programs-Bq9Ju9xk.js} +28 -23
- package/dist/programs-Bq9Ju9xk.js.map +1 -0
- package/dist/programs-CnOSdQo1.js +3 -0
- package/dist/{provisioning-tRDPH7EQ.js → provisioning-D-AM_7DG.js} +4 -4
- package/dist/{provisioning-tRDPH7EQ.js.map → provisioning-D-AM_7DG.js.map} +1 -1
- package/dist/provisioning-D6Cqbc36.js +2 -0
- package/dist/{queue-tools-DrJKvFHZ.js → queue-tools-BoIITT7w.js} +4 -4
- package/dist/{queue-tools-DrJKvFHZ.js.map → queue-tools-BoIITT7w.js.map} +1 -1
- package/dist/{results-Crcubw0Q.js → results-CDqlHogZ.js} +2 -2
- package/dist/{results-Crcubw0Q.js.map → results-CDqlHogZ.js.map} +1 -1
- package/dist/{run-BrIGaPy7.js → run-CfMArkpV.js} +7 -7
- package/dist/{run-BrIGaPy7.js.map → run-CfMArkpV.js.map} +1 -1
- package/dist/{run-DVFTMbxf.js → run-DcaVJSeI.js} +10 -10
- package/dist/{run-DVFTMbxf.js.map → run-DcaVJSeI.js.map} +1 -1
- package/dist/{run-tool-CxdPXmlY.js → run-tool-DJZWnnR9.js} +13 -13
- package/dist/{run-tool-CxdPXmlY.js.map → run-tool-DJZWnnR9.js.map} +1 -1
- package/dist/run-tool-zMLQroVh.js +2 -0
- package/dist/{runners-loOOmHyc.js → runners-Cvnk9BcA.js} +8 -8
- package/dist/{runners-loOOmHyc.js.map → runners-Cvnk9BcA.js.map} +1 -1
- package/dist/{security-C_grSgIM.js → security-Cz3a-d7r.js} +5 -5
- package/dist/{security-C_grSgIM.js.map → security-Cz3a-d7r.js.map} +1 -1
- package/dist/{setup-utils-BYXbb9NM.js → setup-utils-C7kaDP5X.js} +3 -3
- package/dist/{setup-utils-BYXbb9NM.js.map → setup-utils-C7kaDP5X.js.map} +1 -1
- package/dist/{start-playground-Blh0Swil.js → start-playground-B62_pYfu.js} +7 -7
- package/dist/{start-playground-Blh0Swil.js.map → start-playground-B62_pYfu.js.map} +1 -1
- package/dist/{subagent-DQ_enM8C.js → subagent-BBfETG17.js} +3 -3
- package/dist/{subagent-DQ_enM8C.js.map → subagent-BBfETG17.js.map} +1 -1
- package/dist/{task-Dm9wZbVW.js → task-CmNe3xeg.js} +28 -16
- package/dist/task-CmNe3xeg.js.map +1 -0
- package/dist/{telemetry-NeZhrBeW.js → telemetry-DKM7VGuJ.js} +2 -2
- package/dist/{telemetry-NeZhrBeW.js.map → telemetry-DKM7VGuJ.js.map} +1 -1
- package/dist/{terminal-Z4hCXfie.js → terminal-Bkxwzkhn.js} +22 -23
- package/dist/terminal-Bkxwzkhn.js.map +1 -0
- package/dist/{tools-1aM6bI9Y.js → tools-Bm0FDT6f.js} +15 -11
- package/dist/tools-Bm0FDT6f.js.map +1 -0
- package/dist/{tools-dUJHRo6X.js → tools-DZCEvDv9.js} +13 -13
- package/dist/{tools-dUJHRo6X.js.map → tools-DZCEvDv9.js.map} +1 -1
- package/dist/{tui-Cmi_ckGg.js → tui-B56jvkbi.js} +4 -4
- package/dist/{tui-Cmi_ckGg.js.map → tui-B56jvkbi.js.map} +1 -1
- package/dist/{tui-CCO7VVTA.js → tui-tKI95MSr.js} +1 -1
- package/dist/{upload-step-e2Jp2fcr.js → upload-step-DqTgn6UB.js} +3 -3
- package/dist/{upload-step-e2Jp2fcr.js.map → upload-step-DqTgn6UB.js.map} +1 -1
- package/dist/{urls-Cu4RO30x.js → urls-B82v7gfO.js} +2 -2
- package/dist/{urls-Cu4RO30x.js.map → urls-B82v7gfO.js.map} +1 -1
- package/dist/{wizard-abort-BPEiPCXk.js → wizard-abort-Cd-7sfCw.js} +4 -4
- package/dist/{wizard-abort-BPEiPCXk.js.map → wizard-abort-Cd-7sfCw.js.map} +1 -1
- package/dist/{yara-hooks-canFIYTc.js → yara-hooks-BS0n3ekf.js} +4 -4
- package/dist/{yara-hooks-canFIYTc.js.map → yara-hooks-BS0n3ekf.js.map} +1 -1
- package/dist/{yara-hooks-CFD60h78.js → yara-hooks-CxksH-GZ.js} +1 -1
- package/package.json +1 -1
- package/dist/agent-interface-DMP3DZRd.js.map +0 -1
- package/dist/analytics-X7JfAmh6.js.map +0 -1
- package/dist/analytics-dcL9EQ4r.js +0 -2
- package/dist/api-BrMGAI-b.js +0 -2
- package/dist/api-key-login-CZEgx2jb.js +0 -2
- package/dist/bounded-fs-iDiqDQ9v.js.map +0 -1
- package/dist/oauth-session-Bj_-BY7o.js.map +0 -1
- package/dist/pi-DREdQRPP.js.map +0 -1
- package/dist/posthog-integration-DDSPdbpw.js.map +0 -1
- package/dist/programs-6SFfUBmm.js.map +0 -1
- package/dist/programs-DvlhdeM_.js +0 -3
- package/dist/provisioning-CPGWgv7p.js +0 -2
- package/dist/run-tool-GfISLgEs.js +0 -2
- package/dist/task-Dm9wZbVW.js.map +0 -1
- package/dist/terminal-Z4hCXfie.js.map +0 -1
- package/dist/tools-1aM6bI9Y.js.map +0 -1
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { o as logToFile } from "./debug-
|
|
2
|
-
import { n as analytics } from "./analytics-
|
|
3
|
-
import { n as isThinkingLevel } from "./models-
|
|
4
|
-
import {
|
|
5
|
-
import { b as fetchWithRetry } from "./yara-hooks-
|
|
1
|
+
import { o as logToFile } from "./debug-BvEbemym.js";
|
|
2
|
+
import { n as analytics } from "./analytics-B_xFshPY.js";
|
|
3
|
+
import { n as isThinkingLevel } from "./models-C9rjXPZ6.js";
|
|
4
|
+
import { R as WIZARD_TOOL_NAMES } from "./agent-interface-3YfIWZmY.js";
|
|
5
|
+
import { b as fetchWithRetry } from "./yara-hooks-BS0n3ekf.js";
|
|
6
6
|
//#region src/agent/agent-prompt-loader.ts
|
|
7
7
|
function projectContext(ctx) {
|
|
8
8
|
return `You have access to the PostHog MCP server and the wizard tools.
|
|
@@ -328,4 +328,4 @@ const EMPTY_PROMPT = {};
|
|
|
328
328
|
//#endregion
|
|
329
329
|
export { assembleTaskPrompt as a, queueTools as c, taskModelSpec as d, assembleSeedPrompt as i, renderToolInventory as l, agentRunTools as n, loadAgentRegistry as o, allowsPostHogMcp as r, promptModelFor as s, ASK_TOOL as t, resolveTask as u };
|
|
330
330
|
|
|
331
|
-
//# sourceMappingURL=agent-prompt-loader-
|
|
331
|
+
//# sourceMappingURL=agent-prompt-loader-hOHONY7u.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-prompt-loader-BrivWIna.js","names":[],"sources":["../src/agent/agent-prompt-loader.ts"],"sourcesContent":["/**\n * Agent-prompt loader + registry.\n *\n * Agent prompts are the WHAT of a task: a markdown file per type, served from\n * context-mill as the `agents` content type (parallel to skills). The frontmatter\n * carries the artifacts the executor needs — model, the mini-skills to load (the\n * HOW), the tools the task may use, and its dependencies — and the body is the\n * instruction the agent reads.\n *\n * The registry is fetched once at startup and scoped to one flow — agents\n * declare `flow` and (for the planner) `seed: true` in frontmatter, so each\n * program (integration, audit, migration, ...) ships its own agent set and the\n * loader stays generic. Every prompt is downloaded and parsed up front, so\n * resolving a task to its run config is synchronous and adds no mid-drain\n * network latency. The registry's type list also drives `enqueue_task`\n * validation.\n */\nimport type {\n QueueStore,\n QueuedTask,\n} from './runner/sequence/orchestrator/queue';\nimport type { ResolvedTask } from './runner/sequence/orchestrator/executor';\nimport type { HostResolution } from '@shared/host-resolution';\nimport {\n isThinkingLevel,\n type ThinkingLevel,\n} from './runner/switchboard/models';\nimport { logToFile } from '@utils/debug';\nimport { analytics } from '@utils/analytics';\nimport { fetchWithRetry } from '@shared/fetch-retry';\nimport { WIZARD_TOOL_NAMES } from './tools/tools';\n\n/**\n * The basics the client injects around every agent-prompt body. The `/agents/`\n * files carry intent only (goal, success criteria); the wizard owns the I/O\n * contract — who the agent is, how it reports, how it surfaces progress — so the\n * authored prompts never restate it.\n */\nexport interface OrchestratorPromptContext {\n projectId: number;\n projectApiKey: string;\n host: HostResolution;\n /** Path to the framework's reference implementation (EXAMPLE.md), if available. */\n examplePath?: string;\n /** Path to the framework's rules (COMMANDMENTS.md), if available. */\n commandmentsPath?: string;\n}\n\nfunction projectContext(ctx: OrchestratorPromptContext): string {\n // Both hosts, distinctly labelled: `apiHost` is the ingestion/API origin the\n // SDK talks to, `appHost` the user-facing web app. A task that hands the user\n // a link (e.g. the data-warehouse new-source fallback) must build it from the\n // app host — the ingestion host does not serve those pages.\n return `You have access to the PostHog MCP server and the wizard tools.\n\nProject context:\n- PostHog Project ID: ${ctx.projectId}\n- PostHog public token: ${ctx.projectApiKey}\n- PostHog Host: ${ctx.host.apiHost}\n- PostHog app URL (base for any link you show the user in a browser): ${ctx.host.appHost}`;\n}\n\n/** Points the agent at the framework's reference integration to learn patterns from. */\nfunction exampleReference(ctx: OrchestratorPromptContext): string | null {\n if (!ctx.examplePath) return null;\n return `A reference PostHog integration for this framework is at \\`${ctx.examplePath}\\`. It shows the target implementation pattern. Reference its patterns and conventions, adapting them to this codebase.`;\n}\n\n/** The framework's rules ship with the reference skill; every task follows them. */\nfunction commandmentsReference(ctx: OrchestratorPromptContext): string | null {\n if (!ctx.commandmentsPath) return null;\n return `Framework rules for this integration are at \\`${ctx.commandmentsPath}\\`. Read them before you edit and follow them.`;\n}\n\n/**\n * The task's own tool list, injected so an agent never has to discover a tool's\n * absence by trying it — and never reports that absence as a finding. Later\n * tasks hold tools this one does not, so work it cannot do is handed on rather\n * than attempted.\n */\n/** Renders the inventory from the tool names a harness actually registered for\n * the run — the one complete list, in the vocabulary the agent will call. */\nexport function renderToolInventory(toolNames: readonly string[]): string {\n if (toolNames.length === 0) return '';\n return `Your tools for this task: ${toolNames.join(\n ', ',\n )}. Do not look for a tool that is not listed or treat its absence as a problem to report. Later tasks in this run hold tools you do not: when your task needs one, hand that work off in your handoff for the task that can do it, or note it for the final report.`;\n}\n\nconst TASK_BASICS = `You are one step in a larger PostHog workflow made of several tasks, run as a fresh agent with no memory of the other tasks beyond the context you are given. Other tasks — before and after you — own the rest of the work, so stay strictly on your own task: do not do a neighbouring step's job, redo what an upstream handoff already did, or reach beyond what you were asked. Do only your task, then report exactly once by calling complete_task with a structured handoff: what your goal was, what you did, and what the next agent should know. When you are given context from previous steps, trust it — those agents already did their work, so do not re-verify or re-read what their handoffs tell you. Build on it and move fast. Read a file before you edit it, so your own changes do not duplicate what is already there. Work only inside this project's own directory: never read, list, or search (find, ls, grep, glob) outside it — not the OS, not other projects, not global package caches. If your task seems to need something outside this directory, it does not — skip that part and say so in your handoff rather than hunting across the filesystem. If your task does not apply to this project — there is genuinely nothing for it to do — report it with status \\`not needed\\` and say why, rather than marking it done.`;\n\nconst SEED_BASICS = `You are the orchestrator. Plan the work and seed the queue with enqueue_task — each call names the task kind in its \\`type\\` field (the tool has no \\`task\\` field) and returns an id you can pass as a dependency to a later task. Give each task a short label for the UI — the action in a few words, not file names, class names, or other specifics. The last task you queue, the one that reports on the run, must depend on every other task in the queue — directly, or through a task it already depends on. You are not a task yourself: do not call complete_task and do not edit the project.`;\n\n/**\n * Injected only when the run excludes task types (a flag or CI gate), so the\n * seed prompt of an ordinary run is byte-identical to one with no exclusions.\n * Naming the types beats a \"check the tool's list\" rule: the planner has\n * nothing to infer, and the pre-queued (runner-seeded) tasks — absent from the\n * enqueue list by design — cannot be mistaken for exclusions.\n */\nfunction excludedTypesNote(types: readonly string[]): string | null {\n if (types.length === 0) return null;\n return `The plan below may mention these task types, but this run excludes them: ${types.join(\n ', ',\n )}. enqueue_task will not accept them — do not queue them, do not retry them, and hang nothing off them.`;\n}\n\n/**\n * Tasks the wizard queued before the planner ran. It has to see them: they are\n * part of the run it is planning around, and the task that reports last has to\n * depend on them. Their ids are real, so it can wire the edge directly.\n *\n * The one-way rule matters as much as the sink edge. These tasks are deferred to\n * the end of the drain after planning (`seeded-deps.ts`) and may block on a\n * person, so a task the planner hangs off one would wait on the user too — which\n * is the interruption deferring them removed. The resolver drops such a task\n * from the seeded task's own dependencies to stay acyclic, so the damage is\n * silent; saying so here is what prevents it.\n */\nfunction preQueuedTasks(\n tasks: readonly { id: string; type: string }[],\n): string | null {\n if (tasks.length === 0) return null;\n const lines = tasks.map((t) => `- ${t.type} (id: ${t.id})`);\n return `The queue already holds these tasks, placed by the wizard from what it found in this project. Do not queue them again. They run late — after every task you queue — and one may stop to ask the user for input, so make the reporting task depend on each one and hang nothing else off them: any other task you made depend on one would end up waiting on a person.\\n${lines.join(\n '\\n',\n )}`;\n}\n\n/**\n * Points the agent at its installed task instructions (the HOW). They live under\n * the wizard's run dir, not `.claude/skills/`, so the SDK does not auto-load\n * them — the prompt has to name them.\n */\nfunction skillReference(paths: readonly string[]): string | null {\n if (paths.length === 0) return null;\n const list = paths.map((p) => `\\`${p}\\``).join(', ');\n return `Your task instructions are at ${list}. Read them before you start and follow them. They are wizard scaffolding, not part of the project.`;\n}\n\n/** A task agent's full prompt: injected basics, then the authored intent. */\nexport function assembleTaskPrompt(\n ctx: OrchestratorPromptContext,\n body: string,\n skillPaths: readonly string[] = [],\n): string {\n return [\n projectContext(ctx),\n exampleReference(ctx),\n commandmentsReference(ctx),\n skillReference(skillPaths),\n TASK_BASICS,\n body,\n ]\n .filter(Boolean)\n .join('\\n\\n');\n}\n\n/** The seed agent's full prompt: injected basics, then the authored intent. */\nexport function assembleSeedPrompt(\n ctx: OrchestratorPromptContext,\n body: string,\n preQueued: readonly { id: string; type: string }[] = [],\n excludedTypes: readonly string[] = [],\n): string {\n return [\n projectContext(ctx),\n SEED_BASICS,\n excludedTypesNote(excludedTypes),\n preQueuedTasks(preQueued),\n body,\n ]\n .filter(Boolean)\n .join('\\n\\n');\n}\n\n/** Orchestrator tools are MCP tools under the `posthog-wizard` server. Frontmatter\n * names them short (e.g. `enqueue_task`); the SDK gates on the full name. */\nconst ORCHESTRATOR_TOOL_PREFIX = 'mcp__posthog-wizard__';\nconst ORCHESTRATOR_TOOLS = new Set([\n 'enqueue_task',\n 'complete_task',\n 'read_handoffs',\n]);\n\n/** The one tool that stops a task until a person answers. Named short in frontmatter. */\nexport const ASK_TOOL = 'wizard_ask';\n\n/** The skill-menu tools, as agents ask for them in frontmatter. */\nconst SKILL_MENU_TOOL = 'load_skill_menu';\nconst INSTALL_SKILL_TOOL = 'install_skill';\n\n/**\n * The PostHog MCP, as an agent asks for it in frontmatter. Every tool a task\n * gets is granted by its own prompt, this one included: a task that never names\n * it never has the server wired in, and one that does gets it under this name\n * on both harnesses.\n */\nexport const POSTHOG_MCP_TOOL = 'posthog_exec';\n/** The same tool as the anthropic SDK names it, on the hosted PostHog server. */\nconst POSTHOG_MCP_SDK_TOOL = 'mcp__posthog-wizard__exec';\n\n/** Whether a task asked for the PostHog MCP — accepts either spelling. */\nexport function allowsPostHogMcp(\n allowedTools: readonly string[] | undefined,\n): boolean {\n return (allowedTools ?? []).some(\n (name) => name === POSTHOG_MCP_TOOL || name === POSTHOG_MCP_SDK_TOOL,\n );\n}\n\n/** The queue tools a task holds — all of them minus its disallows. Short names.\n * Single source for both the injected inventory and the harness's tool grant. */\nexport function queueTools(disallowedTools: readonly string[]): string[] {\n const disallowed = new Set(\n disallowedTools.map((n) => n.replace(ORCHESTRATOR_TOOL_PREFIX, '')),\n );\n return [...ORCHESTRATOR_TOOLS].filter((t) => !disallowed.has(t));\n}\n\n/** A parsed agent prompt. The frontmatter fields plus the markdown body. */\nexport interface AgentPrompt {\n type: string;\n /** Human-readable title for the TUI; falls back to `type` when absent. */\n label?: string;\n /** The flow this agent belongs to (the program id, e.g. \\`posthog-integration\\`). */\n flow?: string;\n /** Marks the flow's planner: it seeds the queue and is not an enqueueable task. */\n seed: boolean;\n /** Marks a task that runs last: it must depend on every other task in the queue. */\n sink: boolean;\n /**\n * Marks a task only the wizard may queue. The planner never sees the type, so\n * whether it runs is a decision the client makes from what it detected, not\n * one an agent can reach — it can neither invent the task nor forget it.\n */\n runnerSeeded: boolean;\n /** Marks a supplementary task: terminal failure unblocks dependents and never fails the run. */\n optional: boolean;\n /** Per-harness model/effort; both profiles also require gateway admission and prompt compatibility. */\n modelPi?: string;\n effortPi?: ThinkingLevel;\n modelSdk?: string;\n effortSdk?: ThinkingLevel;\n skills: string[];\n allowedTools: string[];\n disallowedTools: string[];\n /**\n * Task types this one runs after. Read differently by the two kinds of task:\n *\n * - **Enqueueable types**: advisory. The planner authors the real graph with\n * `enqueue_task`, passing ids, and its seed prompt describes the shape it\n * should build. Nothing resolves this field for them.\n * - **`runnerSeeded` types**: authoritative. Such a task is queued before the\n * planner runs, so it cannot name ids; the runner resolves these types to\n * the ids the planner produced and applies them (see `seeded-deps.ts`).\n * Empty means \"after everything that is not the sink\".\n */\n dependsOn: string[];\n body: string;\n}\n\n/** Pi selects the pi profile; the legacy Anthropic SDK selects the sdk profile. */\nexport function promptModelFor(\n prompt: AgentPrompt,\n harness: string,\n): { model?: string; effort?: ThinkingLevel } {\n const pi = harness === 'pi';\n return {\n model: pi ? prompt.modelPi : prompt.modelSdk,\n effort: pi ? prompt.effortPi : prompt.effortSdk,\n };\n}\n\nexport interface AgentRegistry {\n /** The flow's task types — every prompt except the seed. */\n readonly types: string[];\n /** The types an agent may enqueue: `types` minus the runner-seeded ones. */\n readonly enqueueableTypes: string[];\n /** The types that run last and must depend on the whole queue. */\n readonly sinkTypes: string[];\n /** The types only the wizard queues, from what it detected before the run. */\n readonly runnerSeededTypes: string[];\n /** The types whose terminal failure must not block dependents or fail the run. */\n readonly optionalTypes: string[];\n /**\n * The excluded types this flow actually had, deduped — the honest list for\n * the seed prompt's exclusion note. An exclusion naming a type the flow\n * never carried is dropped: telling the planner it was \"excluded\" would\n * describe work that was never available.\n */\n readonly excludedTypes: string[];\n /** The flow's planner, the one prompt marked `seed: true` in its frontmatter. */\n readonly seed?: AgentPrompt;\n get(type: string): AgentPrompt | undefined;\n}\n\n/** The registry for one flow's prompts. Pure; the loader feeds it the fetched set. */\nexport function buildRegistry(\n prompts: readonly AgentPrompt[],\n flow: string,\n opts?: {\n exclude?: readonly string[];\n /** Per-stage pi model/effort overlays, keyed by task type ('seed' for the planner). Applied here — the one place prompts enter the wizard — so every downstream read sees the effective spec. */\n overrides?: Record<\n string,\n { model?: string; effort?: ThinkingLevel } | undefined\n >;\n },\n): AgentRegistry {\n // The harness can exclude task types (CI excludes dashboards). An excluded\n // type does not exist for the run: the seed cannot enqueue it and no agent\n // is ever spun up for it.\n const excluded = new Set(opts?.exclude ?? []);\n const inFlow = prompts\n .filter((p) => p.flow === flow && !excluded.has(p.type))\n .map((p) => {\n const o = opts?.overrides?.[p.seed ? 'seed' : p.type];\n if (!o) return p;\n return {\n ...p,\n modelPi: o.model ?? p.modelPi,\n effortPi: o.effort ?? p.effortPi,\n };\n });\n const byType = new Map(inFlow.map((p) => [p.type, p]));\n const tasks = inFlow.filter((p) => !p.seed);\n const flowTypes = new Set(\n prompts.filter((p) => p.flow === flow && !p.seed).map((p) => p.type),\n );\n return {\n types: tasks.map((p) => p.type),\n enqueueableTypes: tasks.filter((p) => !p.runnerSeeded).map((p) => p.type),\n sinkTypes: tasks.filter((p) => p.sink).map((p) => p.type),\n runnerSeededTypes: tasks.filter((p) => p.runnerSeeded).map((p) => p.type),\n optionalTypes: tasks.filter((p) => p.optional).map((p) => p.type),\n excludedTypes: [...excluded].filter((t) => flowTypes.has(t)),\n seed: inFlow.find((p) => p.seed),\n get: (type) => byType.get(type),\n };\n}\n\ninterface AgentMenu {\n /** `flow` arrived with context-mill's flow-scoped agents folder; older menus omit it. */\n agents: { id: string; flow?: string; downloadUrl: string }[];\n}\n\n/** A native tool passes through; an MCP tool gets its fully-qualified name. */\nfunction expandToolName(name: string): string {\n if (name === ASK_TOOL) return WIZARD_TOOL_NAMES.wizardAsk;\n if (name === SKILL_MENU_TOOL) return WIZARD_TOOL_NAMES.loadSkillMenu;\n if (name === INSTALL_SKILL_TOOL) return WIZARD_TOOL_NAMES.installSkill;\n if (name === POSTHOG_MCP_TOOL) return POSTHOG_MCP_SDK_TOOL;\n return ORCHESTRATOR_TOOLS.has(name)\n ? `${ORCHESTRATOR_TOOL_PREFIX}${name}`\n : name;\n}\n\n/** A prompt's allow/disallow lists with orchestrator tool names MCP-qualified. */\nexport function agentRunTools(prompt: AgentPrompt): {\n allowedTools: string[];\n disallowedTools: string[];\n} {\n return {\n allowedTools: prompt.allowedTools.map(expandToolName),\n disallowedTools: prompt.disallowedTools.map(expandToolName),\n };\n}\n\nfunction toStringArray(value: unknown): string[] {\n if (!Array.isArray(value)) return [];\n return value.filter((v): v is string => typeof v === 'string');\n}\n\n/**\n * Parse the leading `---` frontmatter block and the markdown body. The\n * frontmatter is a small, known schema (scalars and inline `[a, b]` arrays), so\n * a tiny parser covers it without a YAML dependency. Inline `# comments` after a\n * value are stripped. `fallbackType` (the menu id) and `fallbackFlow` (the\n * menu entry's flow) apply when the frontmatter omits `type:`/`flow:`.\n */\nexport function parseAgentPrompt(\n text: string,\n fallbackType: string,\n fallbackFlow?: string,\n): AgentPrompt {\n const match = text.match(/^---\\r?\\n([\\s\\S]*?)\\r?\\n---\\r?\\n?([\\s\\S]*)$/);\n const frontmatter = match ? match[1] : '';\n const body = (match ? match[2] : text).trim();\n\n const fields: Record<string, unknown> = {};\n for (const rawLine of frontmatter.split(/\\r?\\n/)) {\n const line = rawLine.replace(/\\s+#.*$/, '').trim();\n if (!line || line.startsWith('#')) continue;\n const kv = line.match(/^([\\w-]+):\\s*(.*)$/);\n if (!kv) continue;\n const [, key, raw] = kv;\n if (raw.startsWith('[') && raw.endsWith(']')) {\n fields[key] = raw\n .slice(1, -1)\n .split(',')\n .map((s) => s.trim().replace(/^['\"]|['\"]$/g, ''))\n .filter(Boolean);\n } else {\n fields[key] = raw.replace(/^['\"]|['\"]$/g, '');\n }\n }\n\n const str = (v: unknown) => (typeof v === 'string' ? v : undefined);\n // Effort is remote data — reject typos here so downstream carries ThinkingLevel.\n const effort = (v: unknown, key: string): ThinkingLevel | undefined => {\n if (v === undefined) return undefined;\n if (isThinkingLevel(v)) return v;\n logToFile(\n `[agent-prompt] ${fallbackType}: ignoring invalid ${key} \"${String(v)}\"`,\n );\n analytics.wizardCapture('agent prompt invalid effort', {\n task_type: fallbackType,\n key,\n value: String(v),\n });\n return undefined;\n };\n return {\n type: typeof fields.type === 'string' ? fields.type : fallbackType,\n label: typeof fields.label === 'string' ? fields.label : undefined,\n flow: typeof fields.flow === 'string' ? fields.flow : fallbackFlow,\n seed: fields.seed === 'true',\n sink: fields.sink === 'true',\n runnerSeeded: fields.runnerSeeded === 'true',\n optional: fields.optional === 'true',\n modelPi: str(fields.model_pi),\n effortPi: effort(fields.effort_pi, 'effort_pi'),\n modelSdk: str(fields.model_sdk),\n effortSdk: effort(fields.effort_sdk, 'effort_sdk'),\n skills: toStringArray(fields.skills),\n allowedTools: toStringArray(fields.allowedTools),\n disallowedTools: toStringArray(fields.disallowedTools),\n dependsOn: toStringArray(fields.dependsOn),\n body,\n };\n}\n\nasync function fetchText(url: string): Promise<string> {\n const res = await fetchWithRetry(url);\n return res.text();\n}\n\n/**\n * Fetch the agent menu and every agent prompt it lists, parse them, and build\n * the registry for one flow. Throws if the menu cannot be fetched — the\n * orchestrator cannot run without its prompts.\n */\nexport async function loadAgentRegistry(\n skillsBaseUrl: string,\n flow: string,\n opts?: Parameters<typeof buildRegistry>[2],\n): Promise<AgentRegistry> {\n const menuRaw = await fetchText(`${skillsBaseUrl}/agent-menu.json`);\n const menu = JSON.parse(menuRaw) as AgentMenu;\n\n // Menus that carry a flow per entry let us skip other flows' prompts before\n // fetching them; entries without one are fetched and filtered by their\n // frontmatter in buildRegistry, as before.\n const entries = (menu.agents ?? []).filter(\n (entry) => !entry.flow || entry.flow === flow,\n );\n const prompts = await Promise.all(\n entries.map(async (entry) => {\n const text = await fetchText(entry.downloadUrl);\n return parseAgentPrompt(text, entry.id, entry.flow);\n }),\n );\n\n return buildRegistry(prompts, flow, opts);\n}\n\n/**\n * Render a task's own inputs into a section, so a fanned-out task (e.g. one\n * `capture` per event) sees the specific thing it owns. Empty when there are none.\n */\nfunction renderInputs(task: QueuedTask): string {\n const entries = Object.entries(task.inputs ?? {});\n if (entries.length === 0) return '';\n const lines = entries.map(([k, v]) => `- ${k}: ${formatInputValue(v)}`);\n return `## Your task input\\n\\n${lines.join('\\n')}`;\n}\n\nfunction formatInputValue(value: unknown): string {\n if (typeof value === 'string') return value;\n return JSON.stringify(value);\n}\n\n/**\n * The ids of every task `task` transitively depends on — the full upstream\n * chain, not just direct dependencies — ordered roots-first, each once. A `seen`\n * set dedupes diamonds and guards against cycles.\n */\nfunction ancestorIds(task: QueuedTask, store: QueueStore): string[] {\n const seen = new Set<string>();\n const ordered: string[] = [];\n const visit = (id: string): void => {\n if (seen.has(id)) return;\n seen.add(id);\n const t = store.get(id);\n if (!t) return;\n for (const dep of t.dependsOn) visit(dep); // ancestors before dependents\n ordered.push(id);\n };\n for (const dep of task.dependsOn) visit(dep);\n return ordered;\n}\n\n/**\n * Render the handoffs of every step `task` transitively depends on into a context\n * section, so a fresh agent sees the whole upstream chain — not just its direct\n * dependencies. Reliability over token economy: a step must never have to\n * re-discover what any ancestor already established just because an intermediate\n * handoff happened to omit it. Empty when there are no completed ancestors.\n */\nfunction renderHandoffContext(task: QueuedTask, store: QueueStore): string {\n const lines: string[] = [];\n for (const id of ancestorIds(task, store)) {\n const dep = store.get(id);\n const handoff = store.readHandoff(id);\n if (!dep || !handoff) continue;\n lines.push(`### ${dep.type}`);\n lines.push(`- did: ${handoff.did}`);\n lines.push(`- for you: ${handoff.forNextAgent}`);\n if (handoff.filesTouched?.length) {\n lines.push(`- files: ${handoff.filesTouched.join(', ')}`);\n }\n if (handoff.evidence) {\n lines.push(`- evidence: ${handoff.evidence}`);\n }\n if (handoff.assumptions) {\n lines.push(`- assumed: ${handoff.assumptions}`);\n }\n if (handoff.reportSection) {\n lines.push(`- report section:\\n${handoff.reportSection}`);\n }\n lines.push('');\n }\n if (lines.length === 0) return '';\n return `## Context from previous steps\\n\\n${lines.join('\\n')}`.trim();\n}\n\n/**\n * Resolve a queued task to its run config: the prompt body (with upstream\n * handoffs appended), the model, and the tool lists with orchestrator tool names\n * MCP-qualified. The model precedence is enqueue override, then prompt, then\n * default. Throws if no prompt is registered for the task's type.\n */\nexport function resolveTask(\n registry: AgentRegistry,\n task: QueuedTask,\n store: QueueStore,\n): ResolvedTask {\n const prompt = registry.get(task.type);\n if (!prompt) {\n throw new Error(`No agent prompt registered for task type \"${task.type}\"`);\n }\n\n const body = [\n renderInputs(task),\n prompt.body,\n renderHandoffContext(task, store),\n ]\n .filter(Boolean)\n .join('\\n\\n');\n\n return {\n ...agentRunTools(prompt),\n prompt: body,\n skills: prompt.skills,\n };\n}\n\n/** The model + effort a task runs on for a harness: enqueue override, then the\n * prompt's per-profile frontmatter; the caller's switchboard pick is the fallback. */\nexport function taskModelSpec(\n registry: AgentRegistry,\n task: QueuedTask,\n harness: string,\n): { model?: string; effort?: ThinkingLevel } {\n const picked = promptModelFor(\n registry.get(task.type) ?? EMPTY_PROMPT,\n harness,\n );\n return {\n model: task.model ?? picked.model,\n effort: picked.effort,\n };\n}\n\nconst EMPTY_PROMPT = {} as AgentPrompt;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"agent-prompt-loader-hOHONY7u.js","names":[],"sources":["../src/agent/agent-prompt-loader.ts"],"sourcesContent":["/**\n * Agent-prompt loader + registry.\n *\n * Agent prompts are the WHAT of a task: a markdown file per type, served from\n * context-mill as the `agents` content type (parallel to skills). The frontmatter\n * carries the artifacts the executor needs — model, the mini-skills to load (the\n * HOW), the tools the task may use, and its dependencies — and the body is the\n * instruction the agent reads.\n *\n * The registry is fetched once at startup and scoped to one flow — agents\n * declare `flow` and (for the planner) `seed: true` in frontmatter, so each\n * program (integration, audit, migration, ...) ships its own agent set and the\n * loader stays generic. Every prompt is downloaded and parsed up front, so\n * resolving a task to its run config is synchronous and adds no mid-drain\n * network latency. The registry's type list also drives `enqueue_task`\n * validation.\n */\nimport type {\n QueueStore,\n QueuedTask,\n} from './runner/sequence/orchestrator/queue';\nimport type { ResolvedTask } from './runner/sequence/orchestrator/executor';\nimport type { HostResolution } from '@shared/host-resolution';\nimport {\n isThinkingLevel,\n type ThinkingLevel,\n} from './runner/switchboard/models';\nimport { logToFile } from '@utils/debug';\nimport { analytics } from '@utils/analytics';\nimport { fetchWithRetry } from '@shared/fetch-retry';\nimport { WIZARD_TOOL_NAMES } from './tools/tools';\n\n/**\n * The basics the client injects around every agent-prompt body. The `/agents/`\n * files carry intent only (goal, success criteria); the wizard owns the I/O\n * contract — who the agent is, how it reports, how it surfaces progress — so the\n * authored prompts never restate it.\n */\nexport interface OrchestratorPromptContext {\n projectId: number;\n projectApiKey: string;\n host: HostResolution;\n /** Path to the framework's reference implementation (EXAMPLE.md), if available. */\n examplePath?: string;\n /** Path to the framework's rules (COMMANDMENTS.md), if available. */\n commandmentsPath?: string;\n}\n\nfunction projectContext(ctx: OrchestratorPromptContext): string {\n // Both hosts, distinctly labelled: `apiHost` is the ingestion/API origin the\n // SDK talks to, `appHost` the user-facing web app. A task that hands the user\n // a link (e.g. the data-warehouse new-source fallback) must build it from the\n // app host — the ingestion host does not serve those pages.\n return `You have access to the PostHog MCP server and the wizard tools.\n\nProject context:\n- PostHog Project ID: ${ctx.projectId}\n- PostHog public token: ${ctx.projectApiKey}\n- PostHog Host: ${ctx.host.apiHost}\n- PostHog app URL (base for any link you show the user in a browser): ${ctx.host.appHost}`;\n}\n\n/** Points the agent at the framework's reference integration to learn patterns from. */\nfunction exampleReference(ctx: OrchestratorPromptContext): string | null {\n if (!ctx.examplePath) return null;\n return `A reference PostHog integration for this framework is at \\`${ctx.examplePath}\\`. It shows the target implementation pattern. Reference its patterns and conventions, adapting them to this codebase.`;\n}\n\n/** The framework's rules ship with the reference skill; every task follows them. */\nfunction commandmentsReference(ctx: OrchestratorPromptContext): string | null {\n if (!ctx.commandmentsPath) return null;\n return `Framework rules for this integration are at \\`${ctx.commandmentsPath}\\`. Read them before you edit and follow them.`;\n}\n\n/**\n * The task's own tool list, injected so an agent never has to discover a tool's\n * absence by trying it — and never reports that absence as a finding. Later\n * tasks hold tools this one does not, so work it cannot do is handed on rather\n * than attempted.\n */\n/** Renders the inventory from the tool names a harness actually registered for\n * the run — the one complete list, in the vocabulary the agent will call. */\nexport function renderToolInventory(toolNames: readonly string[]): string {\n if (toolNames.length === 0) return '';\n return `Your tools for this task: ${toolNames.join(\n ', ',\n )}. Do not look for a tool that is not listed or treat its absence as a problem to report. Later tasks in this run hold tools you do not: when your task needs one, hand that work off in your handoff for the task that can do it, or note it for the final report.`;\n}\n\nconst TASK_BASICS = `You are one step in a larger PostHog workflow made of several tasks, run as a fresh agent with no memory of the other tasks beyond the context you are given. Other tasks — before and after you — own the rest of the work, so stay strictly on your own task: do not do a neighbouring step's job, redo what an upstream handoff already did, or reach beyond what you were asked. Do only your task, then report exactly once by calling complete_task with a structured handoff: what your goal was, what you did, and what the next agent should know. When you are given context from previous steps, trust it — those agents already did their work, so do not re-verify or re-read what their handoffs tell you. Build on it and move fast. Read a file before you edit it, so your own changes do not duplicate what is already there. Work only inside this project's own directory: never read, list, or search (find, ls, grep, glob) outside it — not the OS, not other projects, not global package caches. If your task seems to need something outside this directory, it does not — skip that part and say so in your handoff rather than hunting across the filesystem. If your task does not apply to this project — there is genuinely nothing for it to do — report it with status \\`not needed\\` and say why, rather than marking it done.`;\n\nconst SEED_BASICS = `You are the orchestrator. Plan the work and seed the queue with enqueue_task — each call names the task kind in its \\`type\\` field (the tool has no \\`task\\` field) and returns an id you can pass as a dependency to a later task. Give each task a short label for the UI — the action in a few words, not file names, class names, or other specifics. The last task you queue, the one that reports on the run, must depend on every other task in the queue — directly, or through a task it already depends on. You are not a task yourself: do not call complete_task and do not edit the project.`;\n\n/**\n * Injected only when the run excludes task types (a flag or CI gate), so the\n * seed prompt of an ordinary run is byte-identical to one with no exclusions.\n * Naming the types beats a \"check the tool's list\" rule: the planner has\n * nothing to infer, and the pre-queued (runner-seeded) tasks — absent from the\n * enqueue list by design — cannot be mistaken for exclusions.\n */\nfunction excludedTypesNote(types: readonly string[]): string | null {\n if (types.length === 0) return null;\n return `The plan below may mention these task types, but this run excludes them: ${types.join(\n ', ',\n )}. enqueue_task will not accept them — do not queue them, do not retry them, and hang nothing off them.`;\n}\n\n/**\n * Tasks the wizard queued before the planner ran. It has to see them: they are\n * part of the run it is planning around, and the task that reports last has to\n * depend on them. Their ids are real, so it can wire the edge directly.\n *\n * The one-way rule matters as much as the sink edge. These tasks are deferred to\n * the end of the drain after planning (`seeded-deps.ts`) and may block on a\n * person, so a task the planner hangs off one would wait on the user too — which\n * is the interruption deferring them removed. The resolver drops such a task\n * from the seeded task's own dependencies to stay acyclic, so the damage is\n * silent; saying so here is what prevents it.\n */\nfunction preQueuedTasks(\n tasks: readonly { id: string; type: string }[],\n): string | null {\n if (tasks.length === 0) return null;\n const lines = tasks.map((t) => `- ${t.type} (id: ${t.id})`);\n return `The queue already holds these tasks, placed by the wizard from what it found in this project. Do not queue them again. They run late — after every task you queue — and one may stop to ask the user for input, so make the reporting task depend on each one and hang nothing else off them: any other task you made depend on one would end up waiting on a person.\\n${lines.join(\n '\\n',\n )}`;\n}\n\n/**\n * Points the agent at its installed task instructions (the HOW). They live under\n * the wizard's run dir, not `.claude/skills/`, so the SDK does not auto-load\n * them — the prompt has to name them.\n */\nfunction skillReference(paths: readonly string[]): string | null {\n if (paths.length === 0) return null;\n const list = paths.map((p) => `\\`${p}\\``).join(', ');\n return `Your task instructions are at ${list}. Read them before you start and follow them. They are wizard scaffolding, not part of the project.`;\n}\n\n/** A task agent's full prompt: injected basics, then the authored intent. */\nexport function assembleTaskPrompt(\n ctx: OrchestratorPromptContext,\n body: string,\n skillPaths: readonly string[] = [],\n): string {\n return [\n projectContext(ctx),\n exampleReference(ctx),\n commandmentsReference(ctx),\n skillReference(skillPaths),\n TASK_BASICS,\n body,\n ]\n .filter(Boolean)\n .join('\\n\\n');\n}\n\n/** The seed agent's full prompt: injected basics, then the authored intent. */\nexport function assembleSeedPrompt(\n ctx: OrchestratorPromptContext,\n body: string,\n preQueued: readonly { id: string; type: string }[] = [],\n excludedTypes: readonly string[] = [],\n): string {\n return [\n projectContext(ctx),\n SEED_BASICS,\n excludedTypesNote(excludedTypes),\n preQueuedTasks(preQueued),\n body,\n ]\n .filter(Boolean)\n .join('\\n\\n');\n}\n\n/** Orchestrator tools are MCP tools under the `posthog-wizard` server. Frontmatter\n * names them short (e.g. `enqueue_task`); the SDK gates on the full name. */\nconst ORCHESTRATOR_TOOL_PREFIX = 'mcp__posthog-wizard__';\nconst ORCHESTRATOR_TOOLS = new Set([\n 'enqueue_task',\n 'complete_task',\n 'read_handoffs',\n]);\n\n/** The one tool that stops a task until a person answers. Named short in frontmatter. */\nexport const ASK_TOOL = 'wizard_ask';\n\n/** The skill-menu tools, as agents ask for them in frontmatter. */\nconst SKILL_MENU_TOOL = 'load_skill_menu';\nconst INSTALL_SKILL_TOOL = 'install_skill';\n\n/**\n * The PostHog MCP, as an agent asks for it in frontmatter. Every tool a task\n * gets is granted by its own prompt, this one included: a task that never names\n * it never has the server wired in, and one that does gets it under this name\n * on both harnesses.\n */\nexport const POSTHOG_MCP_TOOL = 'posthog_exec';\n/** The same tool as the anthropic SDK names it, on the hosted PostHog server. */\nconst POSTHOG_MCP_SDK_TOOL = 'mcp__posthog-wizard__exec';\n\n/** Whether a task asked for the PostHog MCP — accepts either spelling. */\nexport function allowsPostHogMcp(\n allowedTools: readonly string[] | undefined,\n): boolean {\n return (allowedTools ?? []).some(\n (name) => name === POSTHOG_MCP_TOOL || name === POSTHOG_MCP_SDK_TOOL,\n );\n}\n\n/** The queue tools a task holds — all of them minus its disallows. Short names.\n * Single source for both the injected inventory and the harness's tool grant. */\nexport function queueTools(disallowedTools: readonly string[]): string[] {\n const disallowed = new Set(\n disallowedTools.map((n) => n.replace(ORCHESTRATOR_TOOL_PREFIX, '')),\n );\n return [...ORCHESTRATOR_TOOLS].filter((t) => !disallowed.has(t));\n}\n\n/** A parsed agent prompt. The frontmatter fields plus the markdown body. */\nexport interface AgentPrompt {\n type: string;\n /** Human-readable title for the TUI; falls back to `type` when absent. */\n label?: string;\n /** The flow this agent belongs to (the program id, e.g. \\`posthog-integration\\`). */\n flow?: string;\n /** Marks the flow's planner: it seeds the queue and is not an enqueueable task. */\n seed: boolean;\n /** Marks a task that runs last: it must depend on every other task in the queue. */\n sink: boolean;\n /**\n * Marks a task only the wizard may queue. The planner never sees the type, so\n * whether it runs is a decision the client makes from what it detected, not\n * one an agent can reach — it can neither invent the task nor forget it.\n */\n runnerSeeded: boolean;\n /** Marks a supplementary task: terminal failure unblocks dependents and never fails the run. */\n optional: boolean;\n /** Per-harness model/effort; both profiles also require gateway admission and prompt compatibility. */\n modelPi?: string;\n effortPi?: ThinkingLevel;\n modelSdk?: string;\n effortSdk?: ThinkingLevel;\n skills: string[];\n allowedTools: string[];\n disallowedTools: string[];\n /**\n * Task types this one runs after. Read differently by the two kinds of task:\n *\n * - **Enqueueable types**: advisory. The planner authors the real graph with\n * `enqueue_task`, passing ids, and its seed prompt describes the shape it\n * should build. Nothing resolves this field for them.\n * - **`runnerSeeded` types**: authoritative. Such a task is queued before the\n * planner runs, so it cannot name ids; the runner resolves these types to\n * the ids the planner produced and applies them (see `seeded-deps.ts`).\n * Empty means \"after everything that is not the sink\".\n */\n dependsOn: string[];\n body: string;\n}\n\n/** Pi selects the pi profile; the legacy Anthropic SDK selects the sdk profile. */\nexport function promptModelFor(\n prompt: AgentPrompt,\n harness: string,\n): { model?: string; effort?: ThinkingLevel } {\n const pi = harness === 'pi';\n return {\n model: pi ? prompt.modelPi : prompt.modelSdk,\n effort: pi ? prompt.effortPi : prompt.effortSdk,\n };\n}\n\nexport interface AgentRegistry {\n /** The flow's task types — every prompt except the seed. */\n readonly types: string[];\n /** The types an agent may enqueue: `types` minus the runner-seeded ones. */\n readonly enqueueableTypes: string[];\n /** The types that run last and must depend on the whole queue. */\n readonly sinkTypes: string[];\n /** The types only the wizard queues, from what it detected before the run. */\n readonly runnerSeededTypes: string[];\n /** The types whose terminal failure must not block dependents or fail the run. */\n readonly optionalTypes: string[];\n /**\n * The excluded types this flow actually had, deduped — the honest list for\n * the seed prompt's exclusion note. An exclusion naming a type the flow\n * never carried is dropped: telling the planner it was \"excluded\" would\n * describe work that was never available.\n */\n readonly excludedTypes: string[];\n /** The flow's planner, the one prompt marked `seed: true` in its frontmatter. */\n readonly seed?: AgentPrompt;\n get(type: string): AgentPrompt | undefined;\n}\n\n/** The registry for one flow's prompts. Pure; the loader feeds it the fetched set. */\nexport function buildRegistry(\n prompts: readonly AgentPrompt[],\n flow: string,\n opts?: {\n exclude?: readonly string[];\n /** Per-stage pi model/effort overlays, keyed by task type ('seed' for the planner). Applied here — the one place prompts enter the wizard — so every downstream read sees the effective spec. */\n overrides?: Record<\n string,\n { model?: string; effort?: ThinkingLevel } | undefined\n >;\n },\n): AgentRegistry {\n // The harness can exclude task types (CI excludes dashboards). An excluded\n // type does not exist for the run: the seed cannot enqueue it and no agent\n // is ever spun up for it.\n const excluded = new Set(opts?.exclude ?? []);\n const inFlow = prompts\n .filter((p) => p.flow === flow && !excluded.has(p.type))\n .map((p) => {\n const o = opts?.overrides?.[p.seed ? 'seed' : p.type];\n if (!o) return p;\n return {\n ...p,\n modelPi: o.model ?? p.modelPi,\n effortPi: o.effort ?? p.effortPi,\n };\n });\n const byType = new Map(inFlow.map((p) => [p.type, p]));\n const tasks = inFlow.filter((p) => !p.seed);\n const flowTypes = new Set(\n prompts.filter((p) => p.flow === flow && !p.seed).map((p) => p.type),\n );\n return {\n types: tasks.map((p) => p.type),\n enqueueableTypes: tasks.filter((p) => !p.runnerSeeded).map((p) => p.type),\n sinkTypes: tasks.filter((p) => p.sink).map((p) => p.type),\n runnerSeededTypes: tasks.filter((p) => p.runnerSeeded).map((p) => p.type),\n optionalTypes: tasks.filter((p) => p.optional).map((p) => p.type),\n excludedTypes: [...excluded].filter((t) => flowTypes.has(t)),\n seed: inFlow.find((p) => p.seed),\n get: (type) => byType.get(type),\n };\n}\n\ninterface AgentMenu {\n /** `flow` arrived with context-mill's flow-scoped agents folder; older menus omit it. */\n agents: { id: string; flow?: string; downloadUrl: string }[];\n}\n\n/** A native tool passes through; an MCP tool gets its fully-qualified name. */\nfunction expandToolName(name: string): string {\n if (name === ASK_TOOL) return WIZARD_TOOL_NAMES.wizardAsk;\n if (name === SKILL_MENU_TOOL) return WIZARD_TOOL_NAMES.loadSkillMenu;\n if (name === INSTALL_SKILL_TOOL) return WIZARD_TOOL_NAMES.installSkill;\n if (name === POSTHOG_MCP_TOOL) return POSTHOG_MCP_SDK_TOOL;\n return ORCHESTRATOR_TOOLS.has(name)\n ? `${ORCHESTRATOR_TOOL_PREFIX}${name}`\n : name;\n}\n\n/** A prompt's allow/disallow lists with orchestrator tool names MCP-qualified. */\nexport function agentRunTools(prompt: AgentPrompt): {\n allowedTools: string[];\n disallowedTools: string[];\n} {\n return {\n allowedTools: prompt.allowedTools.map(expandToolName),\n disallowedTools: prompt.disallowedTools.map(expandToolName),\n };\n}\n\nfunction toStringArray(value: unknown): string[] {\n if (!Array.isArray(value)) return [];\n return value.filter((v): v is string => typeof v === 'string');\n}\n\n/**\n * Parse the leading `---` frontmatter block and the markdown body. The\n * frontmatter is a small, known schema (scalars and inline `[a, b]` arrays), so\n * a tiny parser covers it without a YAML dependency. Inline `# comments` after a\n * value are stripped. `fallbackType` (the menu id) and `fallbackFlow` (the\n * menu entry's flow) apply when the frontmatter omits `type:`/`flow:`.\n */\nexport function parseAgentPrompt(\n text: string,\n fallbackType: string,\n fallbackFlow?: string,\n): AgentPrompt {\n const match = text.match(/^---\\r?\\n([\\s\\S]*?)\\r?\\n---\\r?\\n?([\\s\\S]*)$/);\n const frontmatter = match ? match[1] : '';\n const body = (match ? match[2] : text).trim();\n\n const fields: Record<string, unknown> = {};\n for (const rawLine of frontmatter.split(/\\r?\\n/)) {\n const line = rawLine.replace(/\\s+#.*$/, '').trim();\n if (!line || line.startsWith('#')) continue;\n const kv = line.match(/^([\\w-]+):\\s*(.*)$/);\n if (!kv) continue;\n const [, key, raw] = kv;\n if (raw.startsWith('[') && raw.endsWith(']')) {\n fields[key] = raw\n .slice(1, -1)\n .split(',')\n .map((s) => s.trim().replace(/^['\"]|['\"]$/g, ''))\n .filter(Boolean);\n } else {\n fields[key] = raw.replace(/^['\"]|['\"]$/g, '');\n }\n }\n\n const str = (v: unknown) => (typeof v === 'string' ? v : undefined);\n // Effort is remote data — reject typos here so downstream carries ThinkingLevel.\n const effort = (v: unknown, key: string): ThinkingLevel | undefined => {\n if (v === undefined) return undefined;\n if (isThinkingLevel(v)) return v;\n logToFile(\n `[agent-prompt] ${fallbackType}: ignoring invalid ${key} \"${String(v)}\"`,\n );\n analytics.wizardCapture('agent prompt invalid effort', {\n task_type: fallbackType,\n key,\n value: String(v),\n });\n return undefined;\n };\n return {\n type: typeof fields.type === 'string' ? fields.type : fallbackType,\n label: typeof fields.label === 'string' ? fields.label : undefined,\n flow: typeof fields.flow === 'string' ? fields.flow : fallbackFlow,\n seed: fields.seed === 'true',\n sink: fields.sink === 'true',\n runnerSeeded: fields.runnerSeeded === 'true',\n optional: fields.optional === 'true',\n modelPi: str(fields.model_pi),\n effortPi: effort(fields.effort_pi, 'effort_pi'),\n modelSdk: str(fields.model_sdk),\n effortSdk: effort(fields.effort_sdk, 'effort_sdk'),\n skills: toStringArray(fields.skills),\n allowedTools: toStringArray(fields.allowedTools),\n disallowedTools: toStringArray(fields.disallowedTools),\n dependsOn: toStringArray(fields.dependsOn),\n body,\n };\n}\n\nasync function fetchText(url: string): Promise<string> {\n const res = await fetchWithRetry(url);\n return res.text();\n}\n\n/**\n * Fetch the agent menu and every agent prompt it lists, parse them, and build\n * the registry for one flow. Throws if the menu cannot be fetched — the\n * orchestrator cannot run without its prompts.\n */\nexport async function loadAgentRegistry(\n skillsBaseUrl: string,\n flow: string,\n opts?: Parameters<typeof buildRegistry>[2],\n): Promise<AgentRegistry> {\n const menuRaw = await fetchText(`${skillsBaseUrl}/agent-menu.json`);\n const menu = JSON.parse(menuRaw) as AgentMenu;\n\n // Menus that carry a flow per entry let us skip other flows' prompts before\n // fetching them; entries without one are fetched and filtered by their\n // frontmatter in buildRegistry, as before.\n const entries = (menu.agents ?? []).filter(\n (entry) => !entry.flow || entry.flow === flow,\n );\n const prompts = await Promise.all(\n entries.map(async (entry) => {\n const text = await fetchText(entry.downloadUrl);\n return parseAgentPrompt(text, entry.id, entry.flow);\n }),\n );\n\n return buildRegistry(prompts, flow, opts);\n}\n\n/**\n * Render a task's own inputs into a section, so a fanned-out task (e.g. one\n * `capture` per event) sees the specific thing it owns. Empty when there are none.\n */\nfunction renderInputs(task: QueuedTask): string {\n const entries = Object.entries(task.inputs ?? {});\n if (entries.length === 0) return '';\n const lines = entries.map(([k, v]) => `- ${k}: ${formatInputValue(v)}`);\n return `## Your task input\\n\\n${lines.join('\\n')}`;\n}\n\nfunction formatInputValue(value: unknown): string {\n if (typeof value === 'string') return value;\n return JSON.stringify(value);\n}\n\n/**\n * The ids of every task `task` transitively depends on — the full upstream\n * chain, not just direct dependencies — ordered roots-first, each once. A `seen`\n * set dedupes diamonds and guards against cycles.\n */\nfunction ancestorIds(task: QueuedTask, store: QueueStore): string[] {\n const seen = new Set<string>();\n const ordered: string[] = [];\n const visit = (id: string): void => {\n if (seen.has(id)) return;\n seen.add(id);\n const t = store.get(id);\n if (!t) return;\n for (const dep of t.dependsOn) visit(dep); // ancestors before dependents\n ordered.push(id);\n };\n for (const dep of task.dependsOn) visit(dep);\n return ordered;\n}\n\n/**\n * Render the handoffs of every step `task` transitively depends on into a context\n * section, so a fresh agent sees the whole upstream chain — not just its direct\n * dependencies. Reliability over token economy: a step must never have to\n * re-discover what any ancestor already established just because an intermediate\n * handoff happened to omit it. Empty when there are no completed ancestors.\n */\nfunction renderHandoffContext(task: QueuedTask, store: QueueStore): string {\n const lines: string[] = [];\n for (const id of ancestorIds(task, store)) {\n const dep = store.get(id);\n const handoff = store.readHandoff(id);\n if (!dep || !handoff) continue;\n lines.push(`### ${dep.type}`);\n lines.push(`- did: ${handoff.did}`);\n lines.push(`- for you: ${handoff.forNextAgent}`);\n if (handoff.filesTouched?.length) {\n lines.push(`- files: ${handoff.filesTouched.join(', ')}`);\n }\n if (handoff.evidence) {\n lines.push(`- evidence: ${handoff.evidence}`);\n }\n if (handoff.assumptions) {\n lines.push(`- assumed: ${handoff.assumptions}`);\n }\n if (handoff.reportSection) {\n lines.push(`- report section:\\n${handoff.reportSection}`);\n }\n lines.push('');\n }\n if (lines.length === 0) return '';\n return `## Context from previous steps\\n\\n${lines.join('\\n')}`.trim();\n}\n\n/**\n * Resolve a queued task to its run config: the prompt body (with upstream\n * handoffs appended), the model, and the tool lists with orchestrator tool names\n * MCP-qualified. The model precedence is enqueue override, then prompt, then\n * default. Throws if no prompt is registered for the task's type.\n */\nexport function resolveTask(\n registry: AgentRegistry,\n task: QueuedTask,\n store: QueueStore,\n): ResolvedTask {\n const prompt = registry.get(task.type);\n if (!prompt) {\n throw new Error(`No agent prompt registered for task type \"${task.type}\"`);\n }\n\n const body = [\n renderInputs(task),\n prompt.body,\n renderHandoffContext(task, store),\n ]\n .filter(Boolean)\n .join('\\n\\n');\n\n return {\n ...agentRunTools(prompt),\n prompt: body,\n skills: prompt.skills,\n };\n}\n\n/** The model + effort a task runs on for a harness: enqueue override, then the\n * prompt's per-profile frontmatter; the caller's switchboard pick is the fallback. */\nexport function taskModelSpec(\n registry: AgentRegistry,\n task: QueuedTask,\n harness: string,\n): { model?: string; effort?: ThinkingLevel } {\n const picked = promptModelFor(\n registry.get(task.type) ?? EMPTY_PROMPT,\n harness,\n );\n return {\n model: task.model ?? picked.model,\n effort: picked.effort,\n };\n}\n\nconst EMPTY_PROMPT = {} as AgentPrompt;\n"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { _ as TASK_RUN_ID, f as VERSION, g as TASK_ID, h as RUN_SURFACE, o as logToFile } from "./debug-
|
|
1
|
+
import { _ as TASK_RUN_ID, f as VERSION, g as TASK_ID, h as RUN_SURFACE, o as logToFile } from "./debug-BvEbemym.js";
|
|
2
2
|
import { a as WizardError } from "./errors-DhFeR8Sp.js";
|
|
3
3
|
import { PostHog } from "posthog-node";
|
|
4
4
|
import { v4 } from "uuid";
|
|
@@ -318,7 +318,8 @@ const DUMMY_PROJECT_API_KEY = "_YOUR_POSTHOG_PROJECT_TOKEN_";
|
|
|
318
318
|
* - insight:write create the onboarding insights during setup
|
|
319
319
|
* - notebook:write upload the events-audit report as a PostHog notebook
|
|
320
320
|
* in step 6 of the events-audit skill (notebooks-create
|
|
321
|
-
* MCP tool requires this scope)
|
|
321
|
+
* MCP tool requires this scope), and mirror the handoff
|
|
322
|
+
* report into a notebook (`publish_handoff`)
|
|
322
323
|
* - event_definition:write
|
|
323
324
|
* create event definitions from the completed wizard
|
|
324
325
|
* session's event plan
|
|
@@ -732,4 +733,4 @@ const analytics = new Analytics();
|
|
|
732
733
|
//#endregion
|
|
733
734
|
export { WIZARD_REMARK_EVENT_NAME as $, POSTHOG_APP_URL as A, REPLAY_VISION_SUPPORTED as B, HAIKU_MODEL as C, Integration as D, ISSUES_URL as E, POSTHOG_PRIVACY_URL as F, WIZARD_CONTACT_EMAIL as G, SONNET_5_MODEL as H, POSTHOG_PROXY_CLIENT_ID as I, WIZARD_OAUTH_SCOPES as J, WIZARD_DEFAULT_AIO_LOGS_FLAG_KEY as K, POSTHOG_STATUS_PAGE_URL as L, POSTHOG_DOCS_URL as M, POSTHOG_EU_CLIENT_ID as N, OAUTH_PORTS as O, POSTHOG_ORG_AI_SETTINGS_URL as P, WIZARD_PROVISIONING_SCOPES as Q, POSTHOG_TERMS_URL as R, GPT5_6_TERRA_MODEL as S, Harness as T, Sequence as U, SETUP_REPORT_FILE as V, WIZARD_BASIC_INTEGRATION_AGENTIC_DETECTION_FLAG_KEY as W, WIZARD_ORCHESTRATOR_OVERRIDE_FLAG_KEY as X, WIZARD_ORCHESTRATOR_FLAG_KEY as Y, WIZARD_ORCHESTRATOR_SEEDED_TASKS_FLAG_KEY as Z, EVENT_INVENTORY_PART_PATTERN as _, ciExcludedTaskTypes as a, MCP_LOCAL_URL as at, GPT5_6_LUNA_MODEL as b, AGENTIC_DETECTION_RETRY_TIMEOUT_MS as c, getLocalDev as ct, CONTEXT_MILL_URL as d, resolveLocalDev as dt, WIZARD_RUN_SYNC_FLAG_KEY as et, DEFAULT_AGENT_MODEL as f, EVENT_INVENTORY_FILE as g, EVENTS_AUDIT_REPORT_FILE as h, sessionProperties as i, wizardUserAgentForProgram as it, POSTHOG_DEV_CLIENT_ID as j, OAUTH_TIMEOUT_MS as k, AWS_SKILLS_BASE_URL as l, initLocalDev as lt, DUMMY_PROJECT_API_KEY as m, analytics as n, WIZARD_USER_AGENT as nt, mayReportScanResults as o, POSTHOG_LOCAL_URL as ot, DETECTION_TIMEOUT_MS as p, WIZARD_INTERACTION_EVENT_NAME as q, groupsFromUser as r, getSkillsBaseUrl as rt, AGENTIC_DETECTION_FIRST_ATTEMPT_TIMEOUT_MS as s, checkLocalServices as st, Analytics as t, WIZARD_SELF_DRIVING_USE_PI_HARNESS_FLAG_KEY as tt, CONTEXT_MILL_RELEASES_URL as u, localMcpSkillsNotice as ut, EVENT_PLAN_FILE as v, HAIKU_TRIAGE_MODEL as w, GPT5_6_SOL_MODEL as x, GITHUB_SKILLS_BASE_URL as y, POSTHOG_US_CLIENT_ID as z };
|
|
734
735
|
|
|
735
|
-
//# sourceMappingURL=analytics-
|
|
736
|
+
//# sourceMappingURL=analytics-B_xFshPY.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analytics-B_xFshPY.js","names":["uuidv4"],"sources":["../src/shared/local-dev.ts","../src/shared/constants.ts","../src/shared/run-state.ts","../src/shared/utils/ci-flag-overrides.ts","../src/shared/utils/analytics.ts"],"sourcesContent":["/**\n * Local development targets: context-mill (skills), MCP, and PostHog. Each is\n * independently switchable — CI runs local skills against the *production* MCP\n * — with `--local-dev` as a shorthand for all three.\n *\n * `wizard mcp add --local` is unrelated: it writes a `posthog-local` entry into\n * the user's editor config rather than retargeting this run. Hence no\n * `--local` here.\n */\n\n// `CONTEXT_MILL_URL` points `--local-context-mill` at another port.\nexport const CONTEXT_MILL_LOCAL_URL =\n process.env.CONTEXT_MILL_URL || 'http://localhost:8765';\nexport const MCP_LOCAL_URL = 'http://localhost:8787/mcp';\n/** Pins the API, app, OAuth, and the LLM gateway derived from them. */\nexport const POSTHOG_LOCAL_URL = 'http://localhost:8010';\n\n/**\n * Raw yargs values. The three specific flags are declared without a `default`\n * so they stay `undefined` when absent — see `resolveLocalDev`.\n */\nexport type LocalDevFlags = {\n localDev?: boolean;\n localMcp?: boolean;\n localContextMill?: boolean;\n localPosthog?: boolean;\n};\n\nexport type ResolvedLocalDev = {\n localMcp: boolean;\n localContextMill: boolean;\n localPosthog: boolean;\n};\n\n/**\n * A specific flag beats `--local-dev` in both directions. `??` is load-bearing:\n * it falls through only on `undefined`, so giving the specific flags a yargs\n * `default: false` would silently break both the umbrella and `--no-local-mcp`.\n */\nexport function resolveLocalDev(flags: LocalDevFlags): ResolvedLocalDev {\n const umbrella = flags.localDev ?? false;\n return {\n localMcp: flags.localMcp ?? umbrella,\n localContextMill: flags.localContextMill ?? umbrella,\n localPosthog: flags.localPosthog ?? umbrella,\n };\n}\n\n/** yargs populates both spellings, so read either. */\nexport function localDevFromArgv(\n argv: Record<string, unknown>,\n): ResolvedLocalDev {\n const read = (kebab: string, camel: string): boolean | undefined => {\n const value = argv[camel] ?? argv[kebab];\n return typeof value === 'boolean' ? value : undefined;\n };\n return resolveLocalDev({\n localDev: read('local-dev', 'localDev'),\n localMcp: read('local-mcp', 'localMcp'),\n localContextMill: read('local-context-mill', 'localContextMill'),\n localPosthog: read('local-posthog', 'localPosthog'),\n });\n}\n\nconst PRODUCTION_TARGETS: ResolvedLocalDev = {\n localMcp: false,\n localContextMill: false,\n localPosthog: false,\n};\n\nlet resolved: ResolvedLocalDev | null = null;\n\n/**\n * Resolve this process's targets. Called once from the yargs middleware, which\n * covers every command and the env-var spellings alike.\n */\nexport function initLocalDev(argv: Record<string, unknown>): ResolvedLocalDev {\n resolved = localDevFromArgv(argv);\n return resolved;\n}\n\n/**\n * Where this run points. Resolved once because the flags are parsed at startup\n * and never change; threading them through state let call sites read whichever\n * field they happened to have, which is how the intro screens ended up asking\n * the MCP flag where to fetch skills.\n *\n * Uninitialized means no CLI parse ran (a direct-call test) — production.\n */\nexport function getLocalDev(): ResolvedLocalDev {\n return resolved ?? PRODUCTION_TARGETS;\n}\n\n/** Tests only. */\nexport function resetLocalDev(): void {\n resolved = null;\n}\n\ntype LocalService = {\n key: keyof ResolvedLocalDev;\n label: string;\n flag: string;\n /** Probed for reachability — any HTTP reply proves the port is bound. */\n url: string;\n startHint: string;\n};\n\nconst LOCAL_SERVICES: readonly LocalService[] = [\n {\n key: 'localContextMill',\n label: 'context-mill',\n flag: '--local-context-mill',\n url: `${CONTEXT_MILL_LOCAL_URL}/skill-menu.json`,\n startHint: 'npm run dev (in the context-mill repo)',\n },\n {\n key: 'localMcp',\n label: 'MCP server',\n flag: '--local-mcp',\n url: MCP_LOCAL_URL,\n startHint:\n 'hogli start (or pnpm dev:local-resources in posthog/services/mcp)',\n },\n {\n key: 'localPosthog',\n label: 'PostHog',\n flag: '--local-posthog',\n url: POSTHOG_LOCAL_URL,\n startHint: './bin/start (in the posthog repo)',\n },\n] as const;\n\n// Retries because this gate gates `fetchWithRetry` (see fetch-retry.ts) — a\n// blip it would ride out must not abort the run instead. A missing server\n// refuses instantly, so the retries only cost time when one is slow to answer,\n// which is exactly when they're worth paying for.\nconst PROBE_TIMEOUT_MS = 2_000;\nconst PROBE_ATTEMPTS = 3;\nconst PROBE_BACKOFF_MS = 250;\n\nasync function isReachable(url: string): Promise<boolean> {\n for (let attempt = 1; attempt <= PROBE_ATTEMPTS; attempt++) {\n try {\n // Any reply — including 404 or 405 — proves something is listening, which\n // is the only question here. Deliberately not `fetchWithRetry`: that\n // treats a non-ok status as failure, and here it is a pass.\n await fetch(url, { signal: AbortSignal.timeout(PROBE_TIMEOUT_MS) });\n return true;\n } catch {\n if (attempt < PROBE_ATTEMPTS) {\n await new Promise((r) =>\n setTimeout(r, PROBE_BACKOFF_MS * 2 ** (attempt - 1)),\n );\n }\n }\n }\n return false;\n}\n\n/**\n * Probe every service the flags asked to be local. Returns one message naming\n * each unreachable one, or undefined if all are up (or none were requested).\n *\n * Without this a dead local server surfaces late and badly: a missing\n * context-mill looks like a registry outage, a missing MCP fails mid-run after\n * the agent has already edited files.\n */\nexport async function checkLocalServices(\n resolved: ResolvedLocalDev,\n): Promise<string | undefined> {\n const requested = LOCAL_SERVICES.filter((s) => resolved[s.key]);\n if (requested.length === 0) return undefined;\n\n const reachable = await Promise.all(requested.map((s) => isReachable(s.url)));\n const dead = requested.filter((_, i) => !reachable[i]);\n if (dead.length === 0) return undefined;\n\n return [\n `Local ${dead.length === 1 ? 'service is' : 'services are'} not running:`,\n '',\n ...dead.map(\n (s) =>\n ` ${s.label} — nothing listening at ${new URL(s.url).origin}\\n` +\n ` requested by ${s.flag}\\n` +\n ` start it with: ${s.startHint}`,\n ),\n '',\n 'Start the missing services, or drop the flag to use production.',\n ].join('\\n');\n}\n\n/**\n * `--local-mcp` used to select local skills too; anyone still relying on that\n * would silently get production skills. Temporary — drop after a release or two.\n */\nexport function localMcpSkillsNotice(flags: LocalDevFlags): string | undefined {\n if (flags.localMcp !== true) return undefined;\n if (flags.localContextMill !== undefined || flags.localDev === true) {\n return undefined;\n }\n return (\n '--local-mcp now points only at the local MCP server. ' +\n 'For local skills, add --local-context-mill (or use --local-dev for both).'\n );\n}\n","/**\n * Shared constants for the PostHog wizard.\n */\n\nimport { VERSION } from './version';\n\n// ── Models ──────────────────────────────────────────────────────────\n// Coordinate IDs/efforts with the mint allowlists and gateway transport/required prompt policy; see agent/runner/README.md.\n\n/** Next sonnet generation (a `MODEL_FLAG_VARIANTS` key in the switchboard). */\nexport const SONNET_5_MODEL = 'claude-sonnet-5';\n\n/**\n * Default model for agent runs. Bare model IDs (no `anthropic/` prefix) so the\n * LLM gateway's Bedrock fallback can match map_to_bedrock_model().\n */\nexport const DEFAULT_AGENT_MODEL = SONNET_5_MODEL;\n\n/** Undated haiku, for scan triage. The alias tracks the current 4.5 release rather than pinning one. */\nexport const HAIKU_TRIAGE_MODEL = 'claude-haiku-4-5';\n\n/**\n * Cheaper, faster model for mechanical agent work (e.g. repo classification\n * during source-map detection). Passed via AgentConfig.modelOverride.\n */\nexport const HAIKU_MODEL = HAIKU_TRIAGE_MODEL;\n\n// Locally supported OpenAI models; constants alone do not authorize gateway use.\nexport const GPT5_6_LUNA_MODEL = 'openai/gpt-5.6-luna';\nexport const GPT5_6_TERRA_MODEL = 'openai/gpt-5.6-terra';\nexport const GPT5_6_SOL_MODEL = 'openai/gpt-5.6-sol';\n\n// ── Agent runner routing axes ────────────────────────────────────────\n\n/**\n * The two agent runner routing axes: **harness** (which agent SDK drives the LLM)\n * and **sequence** (which pipeline shape orchestrates the work). Single source\n * of truth for yargs `choices`, session fields, the runner registry, and tests\n * — `Object.values(Harness)` gives an iterable of the values when an array is\n * needed. Adding a member is enough to pick it up everywhere.\n *\n * Naming matches the directory layout — see `src/agent/runner/harness/`\n * and `src/agent/runner/sequence/`.\n */\nexport enum Harness {\n anthropic = 'anthropic',\n pi = 'pi',\n}\n\nexport enum Sequence {\n linear = 'linear',\n orchestrator = 'orchestrator',\n}\n\n/**\n * What kind of call produced a gateway generation — the `call_type` trace tag.\n * Splits a program's LLM spend by workload. Every member is sent explicitly so\n * an absent value means \"old build\", not \"agent\".\n */\nexport enum CallType {\n /** The agent doing the work the user asked for. */\n agent = 'agent',\n /** Warlock's classifier deciding whether a YARA match is a true positive. */\n yaraTriage = 'yara-triage',\n /** The cheap repo scan that classifies which projects a program acts on. */\n detection = 'detection',\n}\n\n// ── Integration / CLI ───────────────────────────────────────────────\n\n/**\n * Detection order matters: put framework-specific integrations BEFORE basic language fallbacks.\n */\nexport enum Integration {\n // Frameworks\n nextjs = 'nextjs',\n nuxt = 'nuxt',\n vue = 'vue',\n reactRouter = 'react-router',\n tanstackStart = 'tanstack-start',\n tanstackRouter = 'tanstack-router',\n reactNative = 'react-native',\n angular = 'angular',\n astro = 'astro',\n django = 'django',\n flask = 'flask',\n fastapi = 'fastapi',\n laravel = 'laravel',\n sveltekit = 'sveltekit',\n flutter = 'flutter',\n kmp = 'kmp',\n swift = 'swift',\n android = 'android',\n rails = 'rails',\n elixir = 'elixir',\n go = 'go',\n rust = 'rust',\n // Must stay after kmp/swift/android: those claim gradle projects first.\n java = 'java',\n\n // Language fallbacks. Keep javascriptNode last: it matches any package.json.\n python = 'python',\n ruby = 'ruby',\n javascript_web = 'javascript_web',\n javascriptNode = 'javascript_node',\n}\n\n/**\n * The platforms session replay can actually record on. Replay vision watches\n * recordings, so a platform with no recordings has nothing to set up — the\n * run must stop before any work, not after a pointless agent run.\n *\n * Web frameworks record through posthog-js (server-rendered frameworks\n * included — they serve pages), and the mobile SDKs with replay support are\n * React Native, Android, iOS, and Flutter. Excluded: pure backend targets\n * (`javascript_node`, `python`, `ruby`) and KMP, which has no replay support\n * yet.\n */\nexport const REPLAY_VISION_SUPPORTED: ReadonlySet<Integration> = new Set([\n Integration.nextjs,\n Integration.nuxt,\n Integration.vue,\n Integration.reactRouter,\n Integration.tanstackStart,\n Integration.tanstackRouter,\n Integration.angular,\n Integration.astro,\n Integration.sveltekit,\n Integration.javascript_web,\n Integration.django,\n Integration.flask,\n Integration.fastapi,\n Integration.laravel,\n Integration.rails,\n Integration.reactNative,\n Integration.android,\n Integration.swift,\n Integration.flutter,\n]);\n\n// ── Documents the wizard's programs write into the user's project ────\n// Named here so the scanner's documentation allowlist can list them without\n// importing a program; each program re-exports its own.\n/** The events-audit report. */\nexport const EVENTS_AUDIT_REPORT_FILE = 'posthog-events-audit-report.md';\nexport const EVENT_INVENTORY_FILE = '.posthog-events-inventory.json';\n/** Per-part filename pattern emitted by events-audit subagents (e.g. `.posthog-events-inventory.part-3.json`). */\nexport const EVENT_INVENTORY_PART_PATTERN =\n /^\\.posthog-events-inventory\\.part-\\d+\\.json$/;\n/** The integration program's event plan. */\nexport const EVENT_PLAN_FILE = '.posthog-events.json';\n/** The integration program's setup report; Self-driving reads it as a hint. */\nexport const SETUP_REPORT_FILE = 'posthog-setup-report.md';\n\nexport interface Args {\n debug: boolean;\n integration: Integration;\n}\n\n// ── Environment ──────────────────────────────────────────────────────\n\nimport { IS_DEV } from '@env';\nimport {\n CONTEXT_MILL_LOCAL_URL,\n getLocalDev,\n POSTHOG_LOCAL_URL,\n} from './local-dev';\nexport { IS_DEV };\nexport const DEBUG = false;\n\n// ── URLs ─────────────────────────────────────────────────────────────\n\nexport const DEFAULT_URL = IS_DEV\n ? POSTHOG_LOCAL_URL\n : 'https://us.posthog.com';\n/**\n * Region-agnostic PostHog app URL. Resolves to us.posthog.com or\n * eu.posthog.com server-side based on the signed-in user's profile.\n * Use this for share-with-user links (e.g. settings pages) so they\n * land on the right region without us needing to know it client-side.\n */\nexport const POSTHOG_APP_URL = IS_DEV\n ? POSTHOG_LOCAL_URL\n : 'https://app.posthog.com';\nexport const DEFAULT_HOST_URL = IS_DEV\n ? POSTHOG_LOCAL_URL\n : 'https://us.i.posthog.com';\nexport const ISSUES_URL = 'https://github.com/posthog/wizard/issues';\n/** Public status page, linked from transient-failure guidance (e.g. OAuth server_error). */\nexport const POSTHOG_STATUS_PAGE_URL = 'https://www.posthogstatus.com';\nexport const CONTEXT_MILL_URL = 'https://github.com/PostHog/context-mill';\n/**\n * Latest context-mill release page — the BYOAI download link shown in\n * the privacy panel. Deliberately the release PAGE, not a direct asset\n * URL: asset URLs are ~89 chars and hard-wrap inside the 64-col panel,\n * which corrupts terminal copy/paste with a mid-URL line break. This\n * stays under one line; the panel names the exact asset to grab.\n */\nexport const CONTEXT_MILL_RELEASES_URL =\n 'https://github.com/PostHog/context-mill/releases/latest';\nexport const POSTHOG_DOCS_URL = 'https://posthog.com/docs';\nexport const POSTHOG_WIZARD_REPO_URL = 'https://github.com/PostHog/wizard';\nexport const POSTHOG_TERMS_URL = 'https://posthog.com/terms';\nexport const POSTHOG_PRIVACY_URL = 'https://posthog.com/privacy';\nexport const POSTHOG_ORG_AI_SETTINGS_URL =\n 'https://app.posthog.com/settings/organization-details#setting=organization-ai-consent';\nexport const WIZARD_CONTACT_EMAIL = 'wizard@posthog.com';\n\n/**\n * Two origins for the same release, same filenames. Interchangeable bases, so\n * making AWS primary is a `getSkillsBaseUrl` change, not a code change.\n */\nexport const GITHUB_SKILLS_BASE_URL =\n 'https://github.com/PostHog/context-mill/releases/latest/download';\nexport const AWS_SKILLS_BASE_URL = 'https://context-mill.posthog.com/latest';\n/** Alias of `@shared/local-dev`'s constant, kept for existing importers. */\nexport const LOCAL_SKILLS_BASE_URL = CONTEXT_MILL_LOCAL_URL;\n\n/**\n * Driven by `--local-context-mill`, NOT `--local-mcp` (which used to select\n * both). Takes no argument on purpose: callers can't pass the wrong flag.\n */\nexport function getSkillsBaseUrl(): string {\n return getLocalDev().localContextMill\n ? LOCAL_SKILLS_BASE_URL\n : GITHUB_SKILLS_BASE_URL;\n}\n\n// ── Analytics (internal) ──────────────────────────────────────────────\n\nexport const ANALYTICS_POSTHOG_PUBLIC_PROJECT_WRITE_KEY = 'sTMFPsFhdP1Ssg';\nexport const ANALYTICS_HOST_URL = 'https://internal-j.posthog.com';\nexport const ANALYTICS_TEAM_TAG = 'docs-and-wizard';\n\n// ── OAuth / Auth ────────────────────────────────────────────────────\n\nexport const OAUTH_PORTS = [8239, 8238, 8240, 8237, 8236, 8235] as const;\nexport const POSTHOG_US_CLIENT_ID = 'c4Rdw8DIxgtQfA80IiSnGKlNX8QN00cFWF00QQhM';\nexport const POSTHOG_EU_CLIENT_ID = 'bx2C5sZRN03TkdjraCcetvQFPGH6N2Y9vRLkcKEy';\nexport const POSTHOG_DEV_CLIENT_ID = 'DC5uRLVbGI02YQ82grxgnK6Qn12SXWpCqdPb60oZ';\nexport const POSTHOG_PROXY_CLIENT_ID = POSTHOG_US_CLIENT_ID;\nexport const DUMMY_PROJECT_API_KEY = '_YOUR_POSTHOG_PROJECT_TOKEN_';\n\n/**\n * Scopes the wizard requests during the agentic provisioning signup flow.\n *\n * Each entry is justified by what the wizard's agent step does after signup:\n * - user:read identify the user for analytics + agent context\n * - project:read look up the freshly-provisioned project\n * - llm_gateway:read authenticate to gateway.{us,eu}.posthog.com/wizard\n * (the agent's LLM calls — without this scope, every\n * agent message returns 401)\n * - query:read run HogQL queries when the agent needs data\n * - dashboard:write create the onboarding dashboard during setup\n * - insight:write create the onboarding insights during setup\n * - notebook:write upload the events-audit report as a PostHog notebook\n * in step 6 of the events-audit skill (notebooks-create\n * MCP tool requires this scope), and mirror the handoff\n * report into a notebook (`publish_handoff`)\n * - event_definition:write\n * create event definitions from the completed wizard\n * session's event plan\n *\n * Must be a subset of `ALLOWED_PROVISIONING_SCOPES` in\n * `ee/api/agentic_provisioning/views.py` on the backend.\n */\nexport const WIZARD_PROVISIONING_SCOPES = [\n 'user:read',\n 'project:read',\n 'llm_gateway:read',\n 'dashboard:write',\n 'insight:write',\n 'query:read',\n 'notebook:write',\n 'event_definition:write',\n] as const;\n\n/**\n * Scopes the wizard requests during the OAuth login flow. Superset of\n * `WIZARD_PROVISIONING_SCOPES` with scopes that only apply to the login\n * path and are not in the provisioning allowlist:\n * - health_issue:read used by `wizard doctor`\n * - wizard_session:read list / retrieve / stream sessions\n * - wizard_session:write stream run state to /api/projects/{id}/wizard/sessions/\n * - event_definition:write\n * create event definitions when a session completes\n * - organization:read read `organization.is_ai_data_processing_approved`\n * from /api/users/@me/ for the AI opt-in gate\n *\n * NOTE: every scope here must be within the wizard OAuth application's\n * server-side scope ceiling (`OAuthApplication.scopes` in posthog, set\n * via Django admin on BOTH prod regions). A scope outside the ceiling\n * does NOT fail the authorize request — the server silently clamps the\n * request to the ceiling (`clamp_scopes_to_ceiling` in posthog) and the\n * token comes back without it. Separately, the consent screen lets the\n * user deselect any scope the app doesn't mark required. Both paths\n * yield a granted `scope` narrower than requested, with no error, so\n * never assume the token carries this list — the token response's\n * `scope` field is the truth, and `missingOAuthScopes` (utils/oauth.ts)\n * diffs it at login. Ceiling procedure: the \"scope-ceiling-invalid-scope\"\n * runbook in PostHog/runbooks. Keep its worked example in sync when this\n * list changes.\n */\nexport const WIZARD_OAUTH_SCOPES = [\n ...WIZARD_PROVISIONING_SCOPES,\n 'health_issue:read',\n 'wizard_session:read',\n 'wizard_session:write',\n 'wizard_run:write',\n 'organization:read',\n] as const;\n\n// ── Wizard run / variants ───────────────────────────────────────────\n\nexport const WIZARD_INTERACTION_EVENT_NAME = 'wizard interaction';\nexport const WIZARD_REMARK_EVENT_NAME = 'wizard remark';\n/** Multivariate flag: `wizard-run` publishes to WizardRun, anything else to WizardSession. */\nexport const WIZARD_RUN_SYNC_FLAG_KEY = 'wizard-run-sync';\n/** Boolean feature flag that routes a run to the experimental orchestrator runner. */\nexport const WIZARD_ORCHESTRATOR_FLAG_KEY = 'wizard-orchestrator';\n/** Multivariate flag: per-stage orchestrator overrides ride each variant's JSON payload (`{stage: {model?, effort?}}`). */\nexport const WIZARD_ORCHESTRATOR_OVERRIDE_FLAG_KEY =\n 'wizard-orchestrator-override';\n/** Boolean flag: on → pi for self-driving. Payload carries `{model, effort?, harness?, sequence?}` (model = a `MODEL_FLAG_VARIANTS` key); missing/invalid payload keeps the non-flagged default. */\nexport const WIZARD_SELF_DRIVING_USE_PI_HARNESS_FLAG_KEY =\n 'wizard-self-driving-use-pi-harness';\n/** Boolean flag: agentic project scoping for non-interactive basic-integration runs. */\nexport const WIZARD_BASIC_INTEGRATION_AGENTIC_DETECTION_FLAG_KEY =\n 'wizard-basic-integration-agentic-detection';\n/** Boolean flag: the orchestrator queues the program's runner-seeded tasks (the warehouse step). Off, no task is queued and the run is byte-identical to a no-sources project. */\nexport const WIZARD_ORCHESTRATOR_SEEDED_TASKS_FLAG_KEY =\n 'wizard-orchestrator-seeded-tasks';\n/** Kill switch over a shipped default: ONLY an explicit 'false' excludes the AI Observability and Logs tasks from default integration runs — absent or fetch-failed means include, so the default never vanishes on a network hiccup. */\nexport const WIZARD_DEFAULT_AIO_LOGS_FLAG_KEY = 'wizard-default-aio-logs';\n// Reading a flag enters this run into that flag's experiment, so a closed set — not a\n// `wizard-` prefix anyone can name into — decides what a run evaluates. Test-pinned exhaustive.\nexport const WIZARD_FLAG_KEYS = [\n WIZARD_RUN_SYNC_FLAG_KEY,\n WIZARD_ORCHESTRATOR_FLAG_KEY,\n WIZARD_ORCHESTRATOR_OVERRIDE_FLAG_KEY,\n WIZARD_ORCHESTRATOR_SEEDED_TASKS_FLAG_KEY,\n WIZARD_SELF_DRIVING_USE_PI_HARNESS_FLAG_KEY,\n WIZARD_BASIC_INTEGRATION_AGENTIC_DETECTION_FLAG_KEY,\n WIZARD_DEFAULT_AIO_LOGS_FLAG_KEY,\n] as const;\n/** User-Agent for wizard HTTP requests and MCP server identification. */\nexport const WIZARD_USER_AGENT = `posthog/wizard; version: ${VERSION}`;\n\n/**\n * User-Agent for a specific program's MCP calls, tagged with `program: <id>` so the\n * backend can attribute work per program (e.g. the `self-driving` program's warehouse\n * sources are recorded as `created_via=self_driving` rather than plain `wizard`). The\n * base `posthog/wizard` token is preserved, so anything keying only on that still matches.\n */\nexport function wizardUserAgentForProgram(programId?: string): string {\n return programId\n ? `${WIZARD_USER_AGENT}; program: ${programId}`\n : WIZARD_USER_AGENT;\n}\n\n// ── Timeouts ─────────────────────────────────────────────────────────\n\n/** Timeout for framework / project detection probes (ms). */\nexport const DETECTION_TIMEOUT_MS = 10_000;\n\n/** Timeout for the agentic project scan (ms); past it the run falls back to root detection. */\nexport const AGENTIC_DETECTION_FIRST_ATTEMPT_TIMEOUT_MS = 60_000;\nexport const AGENTIC_DETECTION_RETRY_TIMEOUT_MS = 90_000;\n\n/**\n * Timeout for the OAuth authorization flow (ms).\n *\n * How long the user has to complete the browser login. The authorization\n * code is minted at approval and exchanged immediately on callback, so the\n * server-side code expiry (`AUTHORIZATION_CODE_EXPIRE_SECONDS`, 5 minutes)\n * bounds only the approval→exchange gap — manual-paste users have 5 minutes\n * from approval to submit the code — not how long this window may stay open.\n */\nexport const OAUTH_TIMEOUT_MS = 1_800_000;\n","/** Run lifecycle and consent states the TUI, the CLI and programs share. */\n\nimport type { DiscoveredFeature } from './discovered-feature';\n\n/** How an agent run ended: the agent decides it, and the programs and hosts read it. */\nexport enum RunOutcome {\n Success = 'success',\n Aborted = 'aborted',\n Failed = 'failed',\n Crashed = 'crashed',\n}\n\n/** Lifecycle phase of the main work (agent run, MCP install, etc.) */\nexport enum RunPhase {\n /** Still gathering input (intro, setup screens) */\n Idle = 'idle',\n /** Main work is in progress */\n Running = 'running',\n /** Main work finished successfully */\n Completed = 'completed',\n /** Main work finished with an error */\n Error = 'error',\n}\n\n/** Consent to report what local detection found (see `scanConsent` below). */\nexport enum ScanConsent {\n Undecided = 'undecided',\n Granted = 'granted',\n Declined = 'declined',\n}\n\n/** Outcome of the MCP server installation step */\nexport enum McpOutcome {\n NoClients = 'no_clients',\n Skipped = 'skipped',\n Installed = 'installed',\n Failed = 'failed',\n}\n\n/** One place to ask, so a new consent state does not need three edits. */\nexport function mayReportScanResults(session: {\n scanConsent: ScanConsent;\n}): boolean {\n return session.scanConsent === ScanConsent.Granted;\n}\n\n/** Lives here so analytics infrastructure never learns what consent means. */\nexport function reportableDiscoveredFeatures(session: {\n scanConsent: ScanConsent;\n discoveredFeatures: DiscoveredFeature[];\n}): DiscoveredFeature[] | undefined {\n return mayReportScanResults(session) ? session.discoveredFeatures : undefined;\n}\n\n/** Also a scan result, so it travels under the same consent as the rest. */\nexport function reportablePosthogSdkDetected(session: {\n scanConsent: ScanConsent;\n posthogSdkDetected: boolean;\n}): boolean | undefined {\n return mayReportScanResults(session) ? session.posthogSdkDetected : undefined;\n}\n","/**\n * CI-only feature-flag overrides.\n *\n * CI must route deterministically: a run that tests the orchestrator arm says\n * so explicitly instead of depending on a live feature flag someone can edit\n * mid-week. The override env var (see the allowlist in `env.ts`) is a JSON\n * object of flag key → value, merged over whatever PostHog returned.\n *\n * The override path exists only in CI builds (`pnpm build:ci`). Published\n * builds inline NODE_ENV as the literal \"production\", the guards collapse,\n * and tsdown strips the rest from the bundle — and the smoke test asserts the\n * env var names are physically absent from production output (which is also\n * why no comment in this file may spell them out), so this can never quietly\n * become a production surface.\n */\nimport { runtimeEnv } from '@env';\nimport { logToFile } from './debug';\n\nexport function applyCiFlagOverrides(\n flags: Record<string, string>,\n payloads: Record<string, unknown> = {},\n): { flags: Record<string, string>; payloads: Record<string, unknown> } {\n // Compared inline (not via env.ts's IS_PRODUCTION_BUILD) so tsdown replaces\n // it with a literal right here and the bundler can prove the rest of this\n // function unreachable in production builds. The smoke test enforces that.\n if (process.env.NODE_ENV === 'production') return { flags, payloads };\n\n const raw = runtimeEnv('WIZARD_CI_FLAG_OVERRIDES');\n if (!raw) return { flags, payloads };\n\n let overrides: Record<string, unknown>;\n try {\n overrides = JSON.parse(raw) as Record<string, unknown>;\n } catch {\n // A malformed override is a CI misconfiguration. Fail the run loudly\n // rather than silently testing whatever the live flags happen to say.\n throw new Error(\n 'The CI flag-override env var is not valid JSON (expected {\"flag-key\": value, ...}).',\n );\n }\n\n const mergedFlags = { ...flags };\n const mergedPayloads = { ...payloads };\n for (const [key, value] of Object.entries(overrides)) {\n // An object/array override is a flag payload: the flag reads 'true' and\n // the value rides the payload channel, matching a payload-carrying flag.\n if (typeof value === 'object' && value !== null) {\n mergedFlags[key] = 'true';\n mergedPayloads[key] = value;\n } else {\n mergedFlags[key] = String(value);\n }\n }\n logToFile('[flags] CI overrides applied', overrides);\n return { flags: mergedFlags, payloads: mergedPayloads };\n}\n\n/**\n * Task types excluded from this run. The exclusion env var (see the allowlist\n * in `env.ts`) is a comma-separated list (e.g. `dashboard`), set by the CI\n * harness that owns the policy — the wizard and the served content stay\n * run-mode agnostic. CI-build only, same as the flag overrides: published\n * builds strip this path.\n */\nexport function ciExcludedTaskTypes(): readonly string[] {\n if (process.env.NODE_ENV === 'production') return [];\n\n const raw = runtimeEnv('WIZARD_CI_EXCLUDE_TASKS');\n if (!raw) return [];\n const types = raw\n .split(',')\n .map((t) => t.trim())\n .filter(Boolean);\n if (types.length > 0) logToFile('[flags] CI task exclusions', types);\n return types;\n}\n","import { PostHog } from 'posthog-node';\nimport {\n ANALYTICS_HOST_URL,\n ANALYTICS_POSTHOG_PUBLIC_PROJECT_WRITE_KEY,\n ANALYTICS_TEAM_TAG,\n WIZARD_FLAG_KEYS,\n} from '@shared/constants';\nimport {\n reportableDiscoveredFeatures,\n reportablePosthogSdkDetected,\n type RunPhase,\n type ScanConsent,\n} from '@shared/run-state';\nimport type { DiscoveredFeature } from '@shared/discovered-feature';\nimport type { Integration } from '@shared/constants';\nimport type { ApiUser } from '@shared/api';\nimport { v4 as uuidv4 } from 'uuid';\nimport { IS_PRODUCTION_BUILD, RUN_SURFACE, TASK_ID, TASK_RUN_ID } from '@env';\nimport { VERSION } from '@shared/version';\nimport { WizardError } from '@shared/errors';\nimport { logToFile } from './debug';\nimport { applyCiFlagOverrides } from './ci-flag-overrides';\n\n/**\n * The invocation, reduced to flag-safe strings: the command word (first\n * positional, 'default' for the bare flow) and the sorted option NAMES.\n * Option values never leave the machine — they carry paths, emails, keys.\n */\nfunction invocationProperties(): { command: string; cli_flags: string } {\n const args = process.argv.slice(2);\n const command = args.find((a) => !a.startsWith('-')) ?? 'default';\n const flags = [\n ...new Set(\n args\n .filter((a) => a.startsWith('--'))\n .map((a) => a.slice(2).split('=')[0]),\n ),\n ].sort();\n return { command, cli_flags: flags.join(',') };\n}\n\n/** The session facts analytics reports on every capture. */\nexport type SessionFacts = {\n integration: Integration | null;\n skillId: string | null;\n detectedFrameworkLabel: string | null;\n typescript: boolean;\n credentials: { projectId: number } | null;\n discoveredFeatures: DiscoveredFeature[];\n scanConsent: ScanConsent;\n runPhase: RunPhase;\n posthogSdkDetected: boolean;\n};\n\n/**\n * Extract a standard property bag from the current session.\n * Used by store-level analytics and available for ad-hoc captures.\n */\nexport function sessionProperties(\n session: SessionFacts,\n): Record<string, unknown> {\n // reportableDiscoveredFeatures() owns the consent decision; this file\n // never needs to know what `scanConsent` means, only that the result\n // might be absent. An absent key is unambiguous, while an empty array\n // would read as \"we looked and found nothing\" instead of \"not reported\".\n const discoveredFeatures = reportableDiscoveredFeatures(session);\n const posthogSdkDetected = reportablePosthogSdkDetected(session);\n\n return {\n integration: session.integration,\n // Discriminates a family leaf: `command` is the first positional, so every\n // `wizard audit <leaf>` reports the same `audit`.\n skill_id: session.skillId,\n detected_framework: session.detectedFrameworkLabel,\n typescript: session.typescript,\n project_id: session.credentials?.projectId,\n ...(discoveredFeatures !== undefined\n ? { discovered_features: discoveredFeatures }\n : {}),\n scan_consent: session.scanConsent,\n run_phase: session.runPhase,\n ...(posthogSdkDetected !== undefined\n ? { posthog_sdk_detected: posthogSdkDetected }\n : {}),\n };\n}\n\nexport function groupsFromUser(\n user: ApiUser | null,\n host: string,\n): Record<string, string> {\n const groups: Record<string, string> = { instance: host };\n if (!user) return groups;\n\n const organizationId = user.organization?.id;\n if (organizationId) groups.organization = organizationId;\n\n const customerId = user.organization?.customer_id;\n if (customerId) groups.customer = customerId;\n\n const projectUuid = user.team?.uuid;\n if (projectUuid) groups.project = projectUuid;\n\n return groups;\n}\n\nconst WIZARD_FLAGS: ReadonlySet<string> = new Set(WIZARD_FLAG_KEYS);\n\n// Widen back to the SDK's shape — a filter on `true` never matches `'true'`.\nfunction flagResponseValue(value: string): string | boolean {\n if (value === 'true') return true;\n if (value === 'false') return false;\n return value;\n}\n\nexport class Analytics {\n private client: PostHog;\n private tags: Record<string, string | boolean | number | null | undefined> =\n {};\n private distinctId?: string;\n private anonymousId: string;\n private _runId: string;\n private sessionId: string | null = null;\n private appName = 'wizard';\n private activeFlags: Record<string, string> | null = null;\n private activeFlagPayloads: Record<string, unknown> = {};\n private groups: Record<string, string> = {};\n private personProperties: Record<string, string> = {};\n private terminalEventSent = false;\n\n constructor() {\n this.client = new PostHog(ANALYTICS_POSTHOG_PUBLIC_PROJECT_WRITE_KEY, {\n host: ANALYTICS_HOST_URL,\n flushAt: 1,\n flushInterval: 0,\n enableExceptionAutocapture: true,\n before_send: (event) => {\n if (!event) return event;\n if (Object.keys(this.groups).length > 0) {\n event.groups = { ...this.groups, ...event.groups };\n }\n // Autocaptured exceptions arrive with a random uuid and\n // `$process_person_profile: false` — reattach the run's identity\n // and tags so they land on the same person as everything else.\n if (event.event === '$exception') {\n event.distinctId = this.distinctId ?? this.anonymousId;\n const { $process_person_profile, ...properties } =\n event.properties ?? {};\n void $process_person_profile;\n event.properties = { ...this.tags, ...properties };\n }\n // The SDK captures this one itself, bypassing capture(), so tags merge here.\n if (event.event === '$feature_flag_called') {\n event.properties = { ...this.tags, ...(event.properties ?? {}) };\n }\n return event;\n },\n });\n\n this.tags = { $app_name: this.appName };\n // Tag every run with its build type so prod / dev / ci / headless segment\n // cleanly in analytics. tsdown inlines IS_PRODUCTION_BUILD to `true` in\n // published builds and `false` for dev/tsx/test runs. Non-interactive runs\n // upgrade this in runWizardCI: dev `--ci` runs to 'ci', published headless\n // runs to 'headless'.\n this.tags.build = IS_PRODUCTION_BUILD ? 'prod' : 'dev';\n\n this.tags.run_surface = RUN_SURFACE;\n\n // The build this run came from. Already sent as a flag-evaluation person\n // property; tagging it makes every event attributable to a release, so a\n // regression can be bounded to the versions that carry it.\n this.tags.version = VERSION;\n\n // Cloud runs only: the task run that owns the sandbox. Without these the\n // wizard's events and the run that launched it are two unrelated streams,\n // since run_id above is minted per process and never leaves the wizard.\n // Left unset for local runs so the properties stay absent rather than null.\n if (TASK_RUN_ID) this.tags.task_run_id = TASK_RUN_ID;\n if (TASK_ID) this.tags.task_id = TASK_ID;\n\n this.anonymousId = uuidv4();\n\n // One id per process = one id per wizard run, registered in the tag bag\n // so it rides on every capture, exception, and autocaptured exception\n // (all of which merge `this.tags`). Lets you separate two runs by the\n // same logged-in user, who otherwise share one distinct id. Distinct\n // from `anonymousId`, the pre-login *person* id that gets aliased onto\n // the real user at login. `$session_id` is intentionally not set here —\n // it stays null until OAuth completes (see identifyUser).\n this._runId = uuidv4();\n this.tags.run_id = this._runId;\n\n this.distinctId = undefined;\n }\n\n /** Per-process run id, tagged on every event and gateway trace. */\n get runId(): string {\n return this._runId;\n }\n\n /** Build type for this run ('prod' | 'dev' | 'ci' | 'headless') — the same value tagged on every analytics event. */\n get build(): string {\n return String(this.tags.build ?? 'dev');\n }\n\n /**\n * Associate the run with the logged-in user, once per id. Identifies them\n * (email, name) and records those person properties so events carry them and\n * feature flags can target the individual user — without the email here the\n * wizard only sends `$app_name`, so email-targeted flags never match. Opens\n * the analytics session on first login, then aliases the run's anonymous id\n * onto the identified person so pre-login events merge in.\n */\n identifyUser(user: ApiUser) {\n const distinctId = user.distinct_id;\n if (this.distinctId === distinctId || distinctId === this.anonymousId) {\n return;\n }\n this.distinctId = distinctId;\n // Open the analytics session on first login. Null until here, so\n // pre-OAuth events carry only `run_id`; from now on every event also\n // carries `$session_id` and PostHog groups the authenticated run into a\n // native Session. Stored in the tag bag so it rides on every subsequent\n // capture and exception.\n if (!this.sessionId) {\n this.sessionId = uuidv4();\n this.tags.$session_id = this.sessionId;\n }\n const props: Record<string, string> = {};\n if (user.email) props.email = user.email;\n const name = [user.first_name, user.last_name]\n .filter(Boolean)\n .join(' ')\n .trim();\n if (name) props.name = name;\n this.personProperties = props;\n this.client.identify({ distinctId, properties: { $set: props } });\n this.client.alias({\n distinctId,\n alias: this.anonymousId,\n });\n // The flag snapshot is per identity. Anything evaluated before login (the\n // intro screen reads the tools-menu flag) was anonymous — drop it so the\n // next read re-evaluates as this user.\n this.activeFlags = null;\n this.activeFlagPayloads = {};\n }\n\n /** Person properties sent with flag evaluation: app name plus the user's. */\n private flagPersonProperties(): Record<string, string> {\n // Environment facts flag conditions can scope on — evaluation-time only,\n // never persisted onto the person. `version` gates an experiment to\n // releases that implement it (a condition on a property older builds\n // don't send silently never matches them); `run_surface`/`build` scope\n // by invocation surface; `command`/`cli_flags` by subcommand; `os`/\n // `node_major` by platform.\n return {\n $app_name: this.appName,\n version: VERSION,\n run_surface: RUN_SURFACE,\n build: String(this.tags.build ?? 'dev'),\n os: process.platform,\n node_major: process.versions.node.split('.')[0],\n ...invocationProperties(),\n ...this.personProperties,\n };\n }\n\n setTag(key: string, value: string | boolean | number | null | undefined) {\n this.tags[key] = value;\n }\n\n setGroups(groups: Record<string, string>) {\n this.groups = groups;\n }\n\n captureException(error: Error, properties: Record<string, unknown> = {}) {\n const code =\n (error instanceof WizardError && error.code) ||\n (typeof properties.error_code === 'string' ? properties.error_code : '');\n this.client.captureException(error, this.distinctId ?? this.anonymousId, {\n team: ANALYTICS_TEAM_TAG,\n // One issue per error code: the stack's install path and chunk hash otherwise split it per route and release.\n ...(code ? { $exception_fingerprint: `wizard_${code}` } : {}),\n ...this.tags,\n ...properties,\n });\n }\n\n capture(eventName: string, properties?: Record<string, unknown>) {\n this.client.capture({\n distinctId: this.distinctId ?? this.anonymousId,\n event: eventName,\n properties: {\n ...this.tags,\n ...this.wizardFlagFeatureProperties(),\n ...properties,\n },\n });\n }\n\n groupIdentify(\n groupType: string,\n groupKey: string,\n properties: Record<string, unknown>,\n ) {\n this.client.groupIdentify({ groupType, groupKey, properties });\n }\n\n // Built from the resolved map, not the SDK snapshot: a CI-forced variant must own its events.\n private wizardFlagFeatureProperties(): Record<string, unknown> {\n if (this.activeFlags === null) return {};\n const props: Record<string, unknown> = {};\n const active: string[] = [];\n for (const [key, value] of Object.entries(this.activeFlags)) {\n if (!WIZARD_FLAGS.has(key)) continue;\n const resolved = flagResponseValue(value);\n props[`$feature/${key}`] = resolved;\n if (resolved !== false) active.push(key);\n }\n if (active.length > 0) props.$active_feature_flags = active.sort();\n return props;\n }\n\n /**\n * Capture a wizard-specific event. Automatically prepends \"wizard: \" to the event name.\n * All new wizard analytics should use this method instead of capture() directly.\n */\n wizardCapture(eventName: string, properties?: Record<string, unknown>): void {\n this.capture(`wizard: ${eventName}`, properties);\n }\n\n /**\n * Flush pending events without firing the \"setup wizard finished\" terminal\n * event. Use this from CLI error paths that exit before any wizard run\n * starts — `shutdown()` would inflate the run count with a \"finished\" event\n * for a parse error that never actually ran the wizard.\n */\n async flush(): Promise<void> {\n await this.client.shutdown();\n }\n\n /**\n * Evaluate all feature flags for the current user at the start of a run.\n * Result is cached; subsequent calls in the same run return the same map.\n * Returns flag key -> string value (booleans become 'true'/'false').\n */\n // Only a getFlag read records an exposure; the bulk response records none.\n async getAllFlagsForWizard(): Promise<Record<string, string>> {\n if (this.activeFlags !== null) {\n return this.activeFlags;\n }\n const out: Record<string, string> = {};\n const payloads: Record<string, unknown> = {};\n try {\n const distinctId = this.distinctId ?? this.anonymousId;\n logToFile('[flags] evaluating as', {\n distinctId,\n identified: this.distinctId !== undefined,\n personProperties: this.flagPersonProperties(),\n });\n const evaluation = await this.client.evaluateFlags(distinctId, {\n personProperties: this.flagPersonProperties(),\n });\n for (const key of WIZARD_FLAG_KEYS) {\n const value = evaluation.getFlag(key);\n if (value === undefined) continue;\n out[key] = String(value);\n const payload = evaluation.getFlagPayload(key);\n if (payload !== undefined) payloads[key] = payload;\n }\n } catch (error) {\n logToFile('Failed to get all feature flags:', error);\n this.captureException(\n error instanceof Error ? error : new Error(String(error)),\n { step: 'get_all_flags' },\n );\n }\n // Outside the fetch guard on purpose: a malformed CI override must fail\n // the run loudly, and a valid one applies even when the fetch failed —\n // CI routing stays deterministic either way.\n const merged = applyCiFlagOverrides(out, payloads);\n this.activeFlags = merged.flags;\n this.activeFlagPayloads = merged.payloads;\n logToFile('[flags] evaluated', this.activeFlags);\n return this.activeFlags;\n }\n\n getCachedWizardFlags(): Readonly<Record<string, string>> | null {\n return this.activeFlags;\n }\n\n /** Flag payloads from the same snapshot; empty until the flags are fetched. */\n getWizardFlagPayloads(): Record<string, unknown> {\n return this.activeFlagPayloads;\n }\n\n async shutdown(status: 'success' | 'error' | 'cancelled') {\n if (Object.keys(this.tags).length === 0) {\n return;\n }\n\n // First status wins. The interrupt fallback in start-tui fires\n // shutdown('cancelled') on every TUI teardown; without this guard a run\n // that already reported 'success' would emit a second, contradicting\n // terminal event when the user dismisses the outro.\n if (this.terminalEventSent) {\n return;\n }\n this.terminalEventSent = true;\n\n this.client.capture({\n distinctId: this.distinctId ?? this.anonymousId,\n event: 'setup wizard finished',\n properties: {\n // Flat for filtering; the nested `tags` snapshot stays for back-compat.\n ...this.tags,\n run_id: this._runId,\n ...(this.sessionId ? { $session_id: this.sessionId } : {}),\n status,\n tags: this.tags,\n },\n });\n\n await this.client.shutdown();\n }\n}\n\nexport const analytics = new Analytics();\n"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as analytics, nt as WIZARD_USER_AGENT } from "./analytics-
|
|
1
|
+
import { n as analytics, nt as WIZARD_USER_AGENT } from "./analytics-B_xFshPY.js";
|
|
2
2
|
import axios from "axios";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
//#region src/shared/api.ts
|
|
@@ -147,6 +147,74 @@ async function fetchGithubConnected(accessToken, projectId, baseUrl, signal) {
|
|
|
147
147
|
if (!parsed.success) return false;
|
|
148
148
|
return parsed.data.results.some((i) => i.kind === "github");
|
|
149
149
|
}
|
|
150
|
+
/** One `ph-markdown-notebook` node holding the report, the shape the MCP's `notebooks-create-markdown` stores. */
|
|
151
|
+
const markdownNotebookBody = (markdown) => ({
|
|
152
|
+
content: {
|
|
153
|
+
type: "doc",
|
|
154
|
+
content: [{
|
|
155
|
+
type: "ph-markdown-notebook",
|
|
156
|
+
attrs: {
|
|
157
|
+
nodeId: "markdown-notebook-v2",
|
|
158
|
+
markdown
|
|
159
|
+
}
|
|
160
|
+
}]
|
|
161
|
+
},
|
|
162
|
+
text_content: markdown
|
|
163
|
+
});
|
|
164
|
+
const NotebookResponseSchema = z.object({ version: z.number() }).passthrough();
|
|
165
|
+
const NOTEBOOK_TIMEOUT_MS = 15e3;
|
|
166
|
+
const notebookRequest = (accessToken, signal) => ({
|
|
167
|
+
headers: {
|
|
168
|
+
Authorization: `Bearer ${accessToken}`,
|
|
169
|
+
"User-Agent": WIZARD_USER_AGENT
|
|
170
|
+
},
|
|
171
|
+
timeout: NOTEBOOK_TIMEOUT_MS,
|
|
172
|
+
signal
|
|
173
|
+
});
|
|
174
|
+
/**
|
|
175
|
+
* Create a notebook holding `markdown` under a caller-chosen `shortId`.
|
|
176
|
+
* Requires the `notebook:write` scope. Returns the notebook's version for the
|
|
177
|
+
* next update; throws on failure — the caller decides how to degrade.
|
|
178
|
+
*/
|
|
179
|
+
async function createMarkdownNotebook(accessToken, projectId, baseUrl, notebook, signal) {
|
|
180
|
+
const response = await axios.post(`${baseUrl}/api/projects/${projectId}/notebooks/`, {
|
|
181
|
+
title: notebook.title,
|
|
182
|
+
short_id: notebook.shortId,
|
|
183
|
+
...markdownNotebookBody(notebook.markdown)
|
|
184
|
+
}, notebookRequest(accessToken, signal));
|
|
185
|
+
return NotebookResponseSchema.parse(response.data).version;
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Replace the markdown of a notebook `createMarkdownNotebook` made. `version`
|
|
189
|
+
* is the one the previous create or update returned. Returns the new version;
|
|
190
|
+
* throws on failure.
|
|
191
|
+
*/
|
|
192
|
+
async function updateMarkdownNotebook(accessToken, projectId, baseUrl, notebook, signal) {
|
|
193
|
+
const response = await axios.patch(`${baseUrl}/api/projects/${projectId}/notebooks/${notebook.shortId}/`, {
|
|
194
|
+
...markdownNotebookBody(notebook.markdown),
|
|
195
|
+
version: notebook.version
|
|
196
|
+
}, notebookRequest(accessToken, signal));
|
|
197
|
+
return NotebookResponseSchema.parse(response.data).version;
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Read a notebook's current version and markdown by `shortId`, or null when it
|
|
201
|
+
* does not exist. Lets a caller whose create or update lost its response find
|
|
202
|
+
* out whether the write landed.
|
|
203
|
+
*/
|
|
204
|
+
async function fetchMarkdownNotebook(accessToken, projectId, baseUrl, shortId, signal) {
|
|
205
|
+
try {
|
|
206
|
+
const response = await axios.get(`${baseUrl}/api/projects/${projectId}/notebooks/${shortId}/`, notebookRequest(accessToken, signal));
|
|
207
|
+
const notebook = NotebookResponseSchema.parse(response.data);
|
|
208
|
+
const text = response.data.text_content;
|
|
209
|
+
return {
|
|
210
|
+
version: notebook.version,
|
|
211
|
+
markdown: typeof text === "string" ? text : void 0
|
|
212
|
+
};
|
|
213
|
+
} catch (error) {
|
|
214
|
+
if (axios.isAxiosError(error) && error.response?.status === 404) return null;
|
|
215
|
+
throw error;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
150
218
|
function handleApiError(error, operation) {
|
|
151
219
|
if (axios.isAxiosError(error)) {
|
|
152
220
|
const axiosError = error;
|
|
@@ -162,6 +230,6 @@ function handleApiError(error, operation) {
|
|
|
162
230
|
return new ApiError(`Unexpected error while trying to ${operation}: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
163
231
|
}
|
|
164
232
|
//#endregion
|
|
165
|
-
export {
|
|
233
|
+
export { createMarkdownNotebook as a, fetchProjectData as c, handleApiError as d, updateMarkdownNotebook as f, ApiUserSchema as i, fetchSlackConnected as l, ApiError as n, fetchGithubConnected as o, ApiProjectSchema as r, fetchMarkdownNotebook as s, ActivityLogEntrySchema as t, fetchUserData as u };
|
|
166
234
|
|
|
167
|
-
//# sourceMappingURL=api-
|
|
235
|
+
//# sourceMappingURL=api-C_9KsN-7.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api-BkDwZoL4.js","names":[],"sources":["../src/shared/api.ts"],"sourcesContent":["import axios, { AxiosError } from 'axios';\nimport { z } from 'zod';\nimport { analytics } from '@utils/analytics';\nimport { WIZARD_USER_AGENT } from './constants';\nimport type { HostResolution } from './host-resolution';\n\n/**\n * User payload from `/api/users/@me/`. Schema typed for the fields the\n * wizard actually reads + passthrough on everything else so the full\n * upstream response rides through to the session for downstream features\n * (account-aware copy, plan-gated flows, org/team metadata, etc.).\n *\n * Top-level uses `.passthrough()` so unknown fields aren't stripped;\n * the few nested objects we care about (team, organization,\n * organizations[]) do the same so their additional fields survive too.\n *\n * Keep `distinct_id` required — analytics depends on it. Everything\n * else added here is nullish so partial responses don't fail parsing.\n */\n/** A pre-issued gateway token and the gateway it's for. */\nexport type GatewayCredential = { token: string; url: string };\n\n/** What a login (or a CI api key) resolves to: the wizard's access to one project. */\nexport interface Credentials {\n accessToken: string;\n /** OAuth refresh token when the grant carried one; absent on CI api-key runs. */\n refreshToken?: string;\n /** Epoch ms when `accessToken` expires — drives the pre-run refresh. */\n expiresAt?: number;\n /** Minting OAuth client when it differs from the default login app (provisioning signups). */\n oauthClientId?: string;\n projectApiKey: string;\n /** Resolved at auth time and immutable thereafter — see {@link HostResolution}. */\n host: HostResolution;\n projectId: number;\n /** A pre-issued gateway token the run uses instead of minting (dev and test `--ci` runs). */\n gateway?: GatewayCredential;\n /**\n * Requested OAuth scopes the grant came back without — deselected on the\n * consent screen or clamped by the app's ceiling. Read when a run fails so\n * the error can name the missing permission and the fix (re-run and grant\n * it during the OAuth flow) instead of the generic report-a-bug line.\n * Empty/absent on CI api-key runs, where there is no scope request to diff\n * against.\n */\n missingScopes?: readonly string[];\n}\n\nexport const ApiUserSchema = z\n .object({\n // Identifiers\n distinct_id: z.string(),\n uuid: z.string().nullish(),\n id: z.number().nullish(),\n\n // Profile\n email: z.string().nullish(),\n first_name: z.string().nullish(),\n last_name: z.string().nullish(),\n date_joined: z.string().nullish(),\n is_email_verified: z.boolean().nullish(),\n is_2fa_enabled: z.boolean().nullish(),\n is_staff: z.boolean().nullish(),\n\n // Preferences\n theme_mode: z.string().nullish(),\n toolbar_mode: z.string().nullish(),\n hide_mcp_hints: z.boolean().nullish(),\n\n // Optional / nullable on the backend — pre-onboarding signup paths\n // return null and older accounts may not have it set. Treat as a\n // hint, never a guarantee.\n role_at_organization: z.string().nullish(),\n\n // Current team + organization (objects from the API, kept typed on\n // the fields the wizard uses; passthrough preserves the rest).\n team: z\n .object({\n id: z.number(),\n uuid: z.string().nullish(),\n organization: z.string().uuid(),\n api_token: z.string().nullish(),\n project_id: z.number().nullish(),\n name: z.string().nullish(),\n timezone: z.string().nullish(),\n })\n .passthrough(),\n organization: z\n .object({\n id: z.string().uuid(),\n name: z.string().nullish(),\n slug: z.string().nullish(),\n membership_level: z.number().nullish(),\n customer_id: z.string().nullish(),\n // Org-level AI consent gate. Signals drops all findings while\n // this is not true. Null on older orgs (pre-2026-05 default\n // flip) — treat null as \"unknown\", not \"off\".\n is_ai_data_processing_approved: z.boolean().nullish(),\n })\n .passthrough(),\n organizations: z.array(\n z\n .object({\n id: z.string().uuid(),\n name: z.string().nullish(),\n membership_level: z.number().nullish(),\n })\n .passthrough(),\n ),\n })\n .passthrough();\n\n/**\n * Single activity log entry the wizard cares about. The PostHog endpoint\n * returns much more — schema kept minimal so changes upstream don't break us.\n *\n * @unused — no current caller after the Phase 6 streaming-agent pivot\n * dropped activity_log polling. Deliberately retained: this is a thin,\n * well-typed wrapper around a stable PostHog endpoint, and we're likely\n * to want it again for a future feature (e.g. \"what changed in your\n * project recently\"). Re-deriving the schema is more work than letting\n * it sit dormant.\n */\nexport const ActivityLogEntrySchema = z\n .object({\n scope: z.string().nullish(),\n activity: z.string().nullish(),\n created_at: z.string().nullish(),\n })\n .passthrough();\n\n/** @unused — see ActivityLogEntrySchema. */\nexport const ActivityLogResponseSchema = z.object({\n results: z.array(ActivityLogEntrySchema),\n});\n\n/** @unused — see ActivityLogEntrySchema. */\nexport type ActivityLogEntry = z.infer<typeof ActivityLogEntrySchema>;\n\nexport const ApiProjectSchema = z.object({\n id: z.number(),\n uuid: z.string().uuid(),\n organization: z.string().uuid(),\n api_token: z.string(),\n name: z.string(),\n // Product opt-ins (TeamSerializer-compat fields on /api/projects/:id).\n // Project-level truth for \"is this product enabled\" — a product can be\n // instrumented from another repo or the snippet, so these settings\n // override repo-local evidence. Null/absent = unknown. Only the\n // opt-ins a signals decision consumes: replay + exception autocapture\n // feed signal-source choices; surveys feeds the surveys-scout tuning.\n session_recording_opt_in: z.boolean().nullish(),\n autocapture_exceptions_opt_in: z.boolean().nullish(),\n surveys_opt_in: z.boolean().nullish(),\n});\n\nexport type ApiUser = z.infer<typeof ApiUserSchema>;\nexport type ApiProject = z.infer<typeof ApiProjectSchema>;\n\nexport class ApiError extends Error {\n constructor(\n message: string,\n public readonly statusCode?: number,\n public readonly endpoint?: string,\n ) {\n super(message);\n this.name = 'ApiError';\n }\n}\n\nexport async function fetchUserData(\n accessToken: string,\n baseUrl: string,\n): Promise<ApiUser> {\n try {\n const response = await axios.get(`${baseUrl}/api/users/@me/`, {\n headers: {\n Authorization: `Bearer ${accessToken}`,\n 'User-Agent': WIZARD_USER_AGENT,\n },\n });\n\n return ApiUserSchema.parse(response.data);\n } catch (error) {\n const apiError = handleApiError(error, 'fetch user data');\n analytics.captureException(apiError, {\n endpoint: '/api/users/@me/',\n baseUrl,\n });\n throw apiError;\n }\n}\n\n/**\n * Best-effort fetch of recent activity log entries. Returns [] on any error\n * so callers can treat absence of results as \"haven't detected anything yet\"\n * rather than a hard failure.\n *\n * @unused — no current caller after the Phase 6 streaming-agent pivot\n * dropped activity_log polling from McpSuggestedPromptsScreen.\n * Deliberately retained for future features that want a soft signal of\n * recent project changes (e.g. dashboards, audit summaries). See the\n * ActivityLogEntrySchema doc comment for the keep-vs-delete rationale.\n */\nexport async function fetchRecentActivity(\n accessToken: string,\n projectId: number,\n baseUrl: string,\n since: Date,\n): Promise<ActivityLogEntry[]> {\n try {\n const response = await axios.get(\n `${baseUrl}/api/projects/${projectId}/activity_log/`,\n {\n params: { limit: 10 },\n headers: {\n Authorization: `Bearer ${accessToken}`,\n 'User-Agent': WIZARD_USER_AGENT,\n },\n // Short timeout — best-effort probe, not a critical path.\n timeout: 4000,\n },\n );\n const parsed = ActivityLogResponseSchema.safeParse(response.data);\n if (!parsed.success) return [];\n const sinceMs = since.getTime();\n return parsed.data.results.filter((entry) => {\n if (!entry.created_at) return false;\n const t = Date.parse(entry.created_at);\n return Number.isFinite(t) && t >= sinceMs;\n });\n } catch {\n return [];\n }\n}\n\nexport async function fetchProjectData(\n accessToken: string,\n projectId: number,\n baseUrl: string,\n): Promise<ApiProject> {\n try {\n const response = await axios.get(`${baseUrl}/api/projects/${projectId}/`, {\n headers: {\n Authorization: `Bearer ${accessToken}`,\n 'User-Agent': WIZARD_USER_AGENT,\n },\n });\n\n return ApiProjectSchema.parse(response.data);\n } catch (error) {\n const apiError = handleApiError(error, 'fetch project data');\n analytics.captureException(apiError, {\n endpoint: `/api/projects/${projectId}/`,\n baseUrl,\n projectId,\n });\n throw apiError;\n }\n}\n\n/** Minimal shape of `/api/projects/:id/integrations/` — we only read `kind`. */\nconst IntegrationsResponseSchema = z.object({\n results: z.array(z.object({ kind: z.string().nullish() }).passthrough()),\n});\n\n/**\n * Check whether the project already has a Slack integration connected.\n * Requires the `integration:read` scope. Throws on failure — callers\n * (including the SlackConnectScreen poll) decide how to degrade and\n * are responsible for capturing the error exactly once.\n */\nexport async function fetchSlackConnected(\n accessToken: string,\n projectId: number,\n baseUrl: string,\n signal?: AbortSignal,\n): Promise<boolean> {\n const response = await axios.get(\n `${baseUrl}/api/projects/${projectId}/integrations/`,\n {\n headers: {\n Authorization: `Bearer ${accessToken}`,\n 'User-Agent': WIZARD_USER_AGENT,\n },\n signal,\n },\n );\n const parsed = IntegrationsResponseSchema.safeParse(response.data);\n if (!parsed.success) return false;\n return parsed.data.results.some((i) => i.kind === 'slack');\n}\n\n/**\n * Check whether the project already has a GitHub App integration connected.\n * Requires the `integration:read` scope. Throws on failure — callers (the\n * SelfDrivingGitHubScreen poll) decide how to degrade and are responsible for\n * capturing the error exactly once.\n */\nexport async function fetchGithubConnected(\n accessToken: string,\n projectId: number,\n baseUrl: string,\n signal?: AbortSignal,\n): Promise<boolean> {\n const response = await axios.get(\n `${baseUrl}/api/projects/${projectId}/integrations/`,\n {\n // The list is paged, so a project with many integrations could push GitHub off page one.\n params: { kind: 'github' },\n headers: {\n Authorization: `Bearer ${accessToken}`,\n 'User-Agent': WIZARD_USER_AGENT,\n },\n signal,\n },\n );\n const parsed = IntegrationsResponseSchema.safeParse(response.data);\n if (!parsed.success) return false;\n return parsed.data.results.some((i) => i.kind === 'github');\n}\n\nexport function handleApiError(error: unknown, operation: string): ApiError {\n if (axios.isAxiosError(error)) {\n const axiosError = error as AxiosError<{ detail?: string }>;\n const status = axiosError.response?.status;\n const detail = axiosError.response?.data?.detail;\n const endpoint = axiosError.config?.url;\n\n if (status === 401) {\n return new ApiError(\n `Authentication failed while trying to ${operation}`,\n status,\n endpoint,\n );\n }\n\n if (status === 403) {\n return new ApiError(\n `Access denied while trying to ${operation}`,\n status,\n endpoint,\n );\n }\n\n if (status === 404) {\n return new ApiError(\n `Resource not found while trying to ${operation}`,\n status,\n endpoint,\n );\n }\n\n const message = detail || `Failed to ${operation}`;\n return new ApiError(message, status, endpoint);\n }\n\n if (error instanceof z.ZodError) {\n return new ApiError(`Invalid response format while trying to ${operation}`);\n }\n\n return new ApiError(\n `Unexpected error while trying to ${operation}: ${\n error instanceof Error ? error.message : 'Unknown error'\n }`,\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"api-C_9KsN-7.js","names":[],"sources":["../src/shared/api.ts"],"sourcesContent":["import axios, { AxiosError } from 'axios';\nimport { z } from 'zod';\nimport { analytics } from '@utils/analytics';\nimport { WIZARD_USER_AGENT } from './constants';\nimport type { HostResolution } from './host-resolution';\n\n/**\n * User payload from `/api/users/@me/`. Schema typed for the fields the\n * wizard actually reads + passthrough on everything else so the full\n * upstream response rides through to the session for downstream features\n * (account-aware copy, plan-gated flows, org/team metadata, etc.).\n *\n * Top-level uses `.passthrough()` so unknown fields aren't stripped;\n * the few nested objects we care about (team, organization,\n * organizations[]) do the same so their additional fields survive too.\n *\n * Keep `distinct_id` required — analytics depends on it. Everything\n * else added here is nullish so partial responses don't fail parsing.\n */\n/** A pre-issued gateway token and the gateway it's for. */\nexport type GatewayCredential = { token: string; url: string };\n\n/** What a login (or a CI api key) resolves to: the wizard's access to one project. */\nexport interface Credentials {\n accessToken: string;\n /** OAuth refresh token when the grant carried one; absent on CI api-key runs. */\n refreshToken?: string;\n /** Epoch ms when `accessToken` expires — drives the pre-run refresh. */\n expiresAt?: number;\n /** Minting OAuth client when it differs from the default login app (provisioning signups). */\n oauthClientId?: string;\n projectApiKey: string;\n /** Resolved at auth time and immutable thereafter — see {@link HostResolution}. */\n host: HostResolution;\n projectId: number;\n /** A pre-issued gateway token the run uses instead of minting (dev and test `--ci` runs). */\n gateway?: GatewayCredential;\n /**\n * Requested OAuth scopes the grant came back without — deselected on the\n * consent screen or clamped by the app's ceiling. Read when a run fails so\n * the error can name the missing permission and the fix (re-run and grant\n * it during the OAuth flow) instead of the generic report-a-bug line.\n * Empty/absent on CI api-key runs, where there is no scope request to diff\n * against.\n */\n missingScopes?: readonly string[];\n}\n\nexport const ApiUserSchema = z\n .object({\n // Identifiers\n distinct_id: z.string(),\n uuid: z.string().nullish(),\n id: z.number().nullish(),\n\n // Profile\n email: z.string().nullish(),\n first_name: z.string().nullish(),\n last_name: z.string().nullish(),\n date_joined: z.string().nullish(),\n is_email_verified: z.boolean().nullish(),\n is_2fa_enabled: z.boolean().nullish(),\n is_staff: z.boolean().nullish(),\n\n // Preferences\n theme_mode: z.string().nullish(),\n toolbar_mode: z.string().nullish(),\n hide_mcp_hints: z.boolean().nullish(),\n\n // Optional / nullable on the backend — pre-onboarding signup paths\n // return null and older accounts may not have it set. Treat as a\n // hint, never a guarantee.\n role_at_organization: z.string().nullish(),\n\n // Current team + organization (objects from the API, kept typed on\n // the fields the wizard uses; passthrough preserves the rest).\n team: z\n .object({\n id: z.number(),\n uuid: z.string().nullish(),\n organization: z.string().uuid(),\n api_token: z.string().nullish(),\n project_id: z.number().nullish(),\n name: z.string().nullish(),\n timezone: z.string().nullish(),\n })\n .passthrough(),\n organization: z\n .object({\n id: z.string().uuid(),\n name: z.string().nullish(),\n slug: z.string().nullish(),\n membership_level: z.number().nullish(),\n customer_id: z.string().nullish(),\n // Org-level AI consent gate. Signals drops all findings while\n // this is not true. Null on older orgs (pre-2026-05 default\n // flip) — treat null as \"unknown\", not \"off\".\n is_ai_data_processing_approved: z.boolean().nullish(),\n })\n .passthrough(),\n organizations: z.array(\n z\n .object({\n id: z.string().uuid(),\n name: z.string().nullish(),\n membership_level: z.number().nullish(),\n })\n .passthrough(),\n ),\n })\n .passthrough();\n\n/**\n * Single activity log entry the wizard cares about. The PostHog endpoint\n * returns much more — schema kept minimal so changes upstream don't break us.\n *\n * @unused — no current caller after the Phase 6 streaming-agent pivot\n * dropped activity_log polling. Deliberately retained: this is a thin,\n * well-typed wrapper around a stable PostHog endpoint, and we're likely\n * to want it again for a future feature (e.g. \"what changed in your\n * project recently\"). Re-deriving the schema is more work than letting\n * it sit dormant.\n */\nexport const ActivityLogEntrySchema = z\n .object({\n scope: z.string().nullish(),\n activity: z.string().nullish(),\n created_at: z.string().nullish(),\n })\n .passthrough();\n\n/** @unused — see ActivityLogEntrySchema. */\nexport const ActivityLogResponseSchema = z.object({\n results: z.array(ActivityLogEntrySchema),\n});\n\n/** @unused — see ActivityLogEntrySchema. */\nexport type ActivityLogEntry = z.infer<typeof ActivityLogEntrySchema>;\n\nexport const ApiProjectSchema = z.object({\n id: z.number(),\n uuid: z.string().uuid(),\n organization: z.string().uuid(),\n api_token: z.string(),\n name: z.string(),\n // Product opt-ins (TeamSerializer-compat fields on /api/projects/:id).\n // Project-level truth for \"is this product enabled\" — a product can be\n // instrumented from another repo or the snippet, so these settings\n // override repo-local evidence. Null/absent = unknown. Only the\n // opt-ins a signals decision consumes: replay + exception autocapture\n // feed signal-source choices; surveys feeds the surveys-scout tuning.\n session_recording_opt_in: z.boolean().nullish(),\n autocapture_exceptions_opt_in: z.boolean().nullish(),\n surveys_opt_in: z.boolean().nullish(),\n});\n\nexport type ApiUser = z.infer<typeof ApiUserSchema>;\nexport type ApiProject = z.infer<typeof ApiProjectSchema>;\n\nexport class ApiError extends Error {\n constructor(\n message: string,\n public readonly statusCode?: number,\n public readonly endpoint?: string,\n ) {\n super(message);\n this.name = 'ApiError';\n }\n}\n\nexport async function fetchUserData(\n accessToken: string,\n baseUrl: string,\n): Promise<ApiUser> {\n try {\n const response = await axios.get(`${baseUrl}/api/users/@me/`, {\n headers: {\n Authorization: `Bearer ${accessToken}`,\n 'User-Agent': WIZARD_USER_AGENT,\n },\n });\n\n return ApiUserSchema.parse(response.data);\n } catch (error) {\n const apiError = handleApiError(error, 'fetch user data');\n analytics.captureException(apiError, {\n endpoint: '/api/users/@me/',\n baseUrl,\n });\n throw apiError;\n }\n}\n\n/**\n * Best-effort fetch of recent activity log entries. Returns [] on any error\n * so callers can treat absence of results as \"haven't detected anything yet\"\n * rather than a hard failure.\n *\n * @unused — no current caller after the Phase 6 streaming-agent pivot\n * dropped activity_log polling from McpSuggestedPromptsScreen.\n * Deliberately retained for future features that want a soft signal of\n * recent project changes (e.g. dashboards, audit summaries). See the\n * ActivityLogEntrySchema doc comment for the keep-vs-delete rationale.\n */\nexport async function fetchRecentActivity(\n accessToken: string,\n projectId: number,\n baseUrl: string,\n since: Date,\n): Promise<ActivityLogEntry[]> {\n try {\n const response = await axios.get(\n `${baseUrl}/api/projects/${projectId}/activity_log/`,\n {\n params: { limit: 10 },\n headers: {\n Authorization: `Bearer ${accessToken}`,\n 'User-Agent': WIZARD_USER_AGENT,\n },\n // Short timeout — best-effort probe, not a critical path.\n timeout: 4000,\n },\n );\n const parsed = ActivityLogResponseSchema.safeParse(response.data);\n if (!parsed.success) return [];\n const sinceMs = since.getTime();\n return parsed.data.results.filter((entry) => {\n if (!entry.created_at) return false;\n const t = Date.parse(entry.created_at);\n return Number.isFinite(t) && t >= sinceMs;\n });\n } catch {\n return [];\n }\n}\n\nexport async function fetchProjectData(\n accessToken: string,\n projectId: number,\n baseUrl: string,\n): Promise<ApiProject> {\n try {\n const response = await axios.get(`${baseUrl}/api/projects/${projectId}/`, {\n headers: {\n Authorization: `Bearer ${accessToken}`,\n 'User-Agent': WIZARD_USER_AGENT,\n },\n });\n\n return ApiProjectSchema.parse(response.data);\n } catch (error) {\n const apiError = handleApiError(error, 'fetch project data');\n analytics.captureException(apiError, {\n endpoint: `/api/projects/${projectId}/`,\n baseUrl,\n projectId,\n });\n throw apiError;\n }\n}\n\n/** Minimal shape of `/api/projects/:id/integrations/` — we only read `kind`. */\nconst IntegrationsResponseSchema = z.object({\n results: z.array(z.object({ kind: z.string().nullish() }).passthrough()),\n});\n\n/**\n * Check whether the project already has a Slack integration connected.\n * Requires the `integration:read` scope. Throws on failure — callers\n * (including the SlackConnectScreen poll) decide how to degrade and\n * are responsible for capturing the error exactly once.\n */\nexport async function fetchSlackConnected(\n accessToken: string,\n projectId: number,\n baseUrl: string,\n signal?: AbortSignal,\n): Promise<boolean> {\n const response = await axios.get(\n `${baseUrl}/api/projects/${projectId}/integrations/`,\n {\n headers: {\n Authorization: `Bearer ${accessToken}`,\n 'User-Agent': WIZARD_USER_AGENT,\n },\n signal,\n },\n );\n const parsed = IntegrationsResponseSchema.safeParse(response.data);\n if (!parsed.success) return false;\n return parsed.data.results.some((i) => i.kind === 'slack');\n}\n\n/**\n * Check whether the project already has a GitHub App integration connected.\n * Requires the `integration:read` scope. Throws on failure — callers (the\n * SelfDrivingGitHubScreen poll) decide how to degrade and are responsible for\n * capturing the error exactly once.\n */\nexport async function fetchGithubConnected(\n accessToken: string,\n projectId: number,\n baseUrl: string,\n signal?: AbortSignal,\n): Promise<boolean> {\n const response = await axios.get(\n `${baseUrl}/api/projects/${projectId}/integrations/`,\n {\n // The list is paged, so a project with many integrations could push GitHub off page one.\n params: { kind: 'github' },\n headers: {\n Authorization: `Bearer ${accessToken}`,\n 'User-Agent': WIZARD_USER_AGENT,\n },\n signal,\n },\n );\n const parsed = IntegrationsResponseSchema.safeParse(response.data);\n if (!parsed.success) return false;\n return parsed.data.results.some((i) => i.kind === 'github');\n}\n\n/** One `ph-markdown-notebook` node holding the report, the shape the MCP's `notebooks-create-markdown` stores. */\nconst markdownNotebookBody = (markdown: string) => ({\n content: {\n type: 'doc',\n content: [\n {\n type: 'ph-markdown-notebook',\n attrs: { nodeId: 'markdown-notebook-v2', markdown },\n },\n ],\n },\n text_content: markdown,\n});\n\nconst NotebookResponseSchema = z.object({ version: z.number() }).passthrough();\n\nconst NOTEBOOK_TIMEOUT_MS = 15_000;\n\nconst notebookRequest = (accessToken: string, signal?: AbortSignal) => ({\n headers: {\n Authorization: `Bearer ${accessToken}`,\n 'User-Agent': WIZARD_USER_AGENT,\n },\n timeout: NOTEBOOK_TIMEOUT_MS,\n signal,\n});\n\n/**\n * Create a notebook holding `markdown` under a caller-chosen `shortId`.\n * Requires the `notebook:write` scope. Returns the notebook's version for the\n * next update; throws on failure — the caller decides how to degrade.\n */\nexport async function createMarkdownNotebook(\n accessToken: string,\n projectId: number,\n baseUrl: string,\n notebook: { shortId: string; title: string; markdown: string },\n signal?: AbortSignal,\n): Promise<number> {\n const response = await axios.post(\n `${baseUrl}/api/projects/${projectId}/notebooks/`,\n {\n title: notebook.title,\n short_id: notebook.shortId,\n ...markdownNotebookBody(notebook.markdown),\n },\n notebookRequest(accessToken, signal),\n );\n return NotebookResponseSchema.parse(response.data).version;\n}\n\n/**\n * Replace the markdown of a notebook `createMarkdownNotebook` made. `version`\n * is the one the previous create or update returned. Returns the new version;\n * throws on failure.\n */\nexport async function updateMarkdownNotebook(\n accessToken: string,\n projectId: number,\n baseUrl: string,\n notebook: { shortId: string; markdown: string; version: number },\n signal?: AbortSignal,\n): Promise<number> {\n const response = await axios.patch(\n `${baseUrl}/api/projects/${projectId}/notebooks/${notebook.shortId}/`,\n { ...markdownNotebookBody(notebook.markdown), version: notebook.version },\n notebookRequest(accessToken, signal),\n );\n return NotebookResponseSchema.parse(response.data).version;\n}\n\n/**\n * Read a notebook's current version and markdown by `shortId`, or null when it\n * does not exist. Lets a caller whose create or update lost its response find\n * out whether the write landed.\n */\nexport async function fetchMarkdownNotebook(\n accessToken: string,\n projectId: number,\n baseUrl: string,\n shortId: string,\n signal?: AbortSignal,\n): Promise<{ version: number; markdown: string | undefined } | null> {\n try {\n const response = await axios.get(\n `${baseUrl}/api/projects/${projectId}/notebooks/${shortId}/`,\n notebookRequest(accessToken, signal),\n );\n const notebook = NotebookResponseSchema.parse(response.data);\n const text = (response.data as { text_content?: unknown }).text_content;\n return {\n version: notebook.version,\n markdown: typeof text === 'string' ? text : undefined,\n };\n } catch (error) {\n if (axios.isAxiosError(error) && error.response?.status === 404) {\n return null;\n }\n throw error;\n }\n}\n\nexport function handleApiError(error: unknown, operation: string): ApiError {\n if (axios.isAxiosError(error)) {\n const axiosError = error as AxiosError<{ detail?: string }>;\n const status = axiosError.response?.status;\n const detail = axiosError.response?.data?.detail;\n const endpoint = axiosError.config?.url;\n\n if (status === 401) {\n return new ApiError(\n `Authentication failed while trying to ${operation}`,\n status,\n endpoint,\n );\n }\n\n if (status === 403) {\n return new ApiError(\n `Access denied while trying to ${operation}`,\n status,\n endpoint,\n );\n }\n\n if (status === 404) {\n return new ApiError(\n `Resource not found while trying to ${operation}`,\n status,\n endpoint,\n );\n }\n\n const message = detail || `Failed to ${operation}`;\n return new ApiError(message, status, endpoint);\n }\n\n if (error instanceof z.ZodError) {\n return new ApiError(`Invalid response format while trying to ${operation}`);\n }\n\n return new ApiError(\n `Unexpected error while trying to ${operation}: ${\n error instanceof Error ? error.message : 'Unknown error'\n }`,\n );\n}\n"],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { o as logToFile } from "./debug-
|
|
2
|
-
import { n as analytics } from "./analytics-
|
|
3
|
-
import { t as HostResolution } from "./host-resolution-
|
|
4
|
-
import { c as
|
|
1
|
+
import { o as logToFile } from "./debug-BvEbemym.js";
|
|
2
|
+
import { n as analytics } from "./analytics-B_xFshPY.js";
|
|
3
|
+
import { t as HostResolution } from "./host-resolution-BxnNn8Jm.js";
|
|
4
|
+
import { c as fetchProjectData, u as fetchUserData } from "./api-C_9KsN-7.js";
|
|
5
5
|
//#region src/shared/api-key-login.ts
|
|
6
6
|
/** What a personal API key logs in to, the way `--ci` does: no browser, no OAuth. */
|
|
7
7
|
/** The host, the project and, when the key can read it, the user `apiKey` belongs to. */
|
|
@@ -52,4 +52,4 @@ async function fetchProjectDataById(apiKey, projectId, cloudUrl) {
|
|
|
52
52
|
//#endregion
|
|
53
53
|
export { resolveApiKeyProject as t };
|
|
54
54
|
|
|
55
|
-
//# sourceMappingURL=api-key-login-
|
|
55
|
+
//# sourceMappingURL=api-key-login-oUJQDmOV.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api-key-login-
|
|
1
|
+
{"version":3,"file":"api-key-login-oUJQDmOV.js","names":[],"sources":["../src/shared/api-key-login.ts"],"sourcesContent":["/** What a personal API key logs in to, the way `--ci` does: no browser, no OAuth. */\n\nimport {\n fetchProjectData,\n fetchUserData,\n type ApiProject,\n type ApiUser,\n} from './api';\nimport { HostResolution } from './host-resolution';\nimport { analytics } from './utils/analytics';\nimport { logToFile } from './utils/debug';\nimport type { CloudRegion } from './utils/types';\n\nexport type ApiKeyLoginOptions = {\n region?: CloudRegion;\n baseUrl?: string; // pins every PostHog origin and skips region resolution\n localMcp?: boolean; // resolves `host.mcpUrl` to the local MCP server\n projectId?: number; // the project to use; else the key's current project\n onWarning?: (message: string) => void; // shown when the key can't read its user\n onInfo?: (message: string) => void; // the login's progress line\n};\n\n/** The host, the project and, when the key can read it, the user `apiKey` belongs to. */\nexport async function resolveApiKeyProject(\n apiKey: string,\n options: ApiKeyLoginOptions = {},\n): Promise<{\n host: HostResolution;\n project: ApiProject;\n projectId: number;\n apiUser: ApiUser | null;\n}> {\n options.onInfo?.('Using provided API key (CI mode - OAuth bypassed)');\n const host = await HostResolution.fromAccessToken(apiKey, {\n region: options.region,\n localMcp: options.localMcp,\n baseUrl: options.baseUrl,\n });\n const cloudUrl = host.appHost;\n const projectData =\n options.projectId != null\n ? await fetchProjectDataById(apiKey, options.projectId, cloudUrl)\n : await fetchProjectDataWithApiKey(apiKey, cloudUrl);\n\n // Best-effort user fetch — CI flows may run with project-scoped keys\n // that 403 on /api/users/@me/, so swallow errors and continue with\n // a null user (and null role).\n let apiUser: ApiUser | null = null;\n try {\n apiUser = await fetchUserData(apiKey, cloudUrl);\n } catch (err) {\n logToFile(\n '[ci-auth] user lookup failed:',\n err instanceof Error ? err.message : String(err),\n );\n }\n if (apiUser) {\n analytics.identifyUser(apiUser);\n logToFile(\n '[ci-auth] identified via API key; flags evaluate as the key owner',\n );\n } else {\n options.onWarning?.(\n 'Could not resolve the API key user (key needs user:read scope) — feature flags evaluate anonymously; user-targeted flags will not match.',\n );\n }\n return {\n host,\n project: projectData.project,\n projectId: projectData.id,\n apiUser,\n };\n}\n\nasync function fetchProjectDataWithApiKey(\n apiKey: string,\n cloudUrl: string,\n): Promise<{ api_token: string; id: number; project: ApiProject }> {\n const userData = await fetchUserData(apiKey, cloudUrl);\n const projectId = userData.team?.id;\n\n if (!projectId) {\n throw new Error(\n 'Could not determine project ID from API key. Please ensure your API key has access to a project in this cloud region.',\n );\n }\n\n const projectData = await fetchProjectData(apiKey, projectId, cloudUrl);\n return {\n api_token: projectData.api_token,\n id: projectId,\n project: projectData,\n };\n}\n\nasync function fetchProjectDataById(\n apiKey: string,\n projectId: number,\n cloudUrl: string,\n): Promise<{ api_token: string; id: number; project: ApiProject }> {\n const projectData = await fetchProjectData(apiKey, projectId, cloudUrl);\n return {\n api_token: projectData.api_token,\n id: projectId,\n project: projectData,\n };\n}\n"],"mappings":";;;;;;;AAuBA,eAAsB,qBACpB,QACA,UAA8B,EAAE,EAM/B;AACD,SAAQ,SAAS,oDAAoD;CACrE,MAAM,OAAO,MAAM,eAAe,gBAAgB,QAAQ;EACxD,QAAQ,QAAQ;EAChB,UAAU,QAAQ;EAClB,SAAS,QAAQ;EAClB,CAAC;CACF,MAAM,WAAW,KAAK;CACtB,MAAM,cACJ,QAAQ,aAAa,OACjB,MAAM,qBAAqB,QAAQ,QAAQ,WAAW,SAAS,GAC/D,MAAM,2BAA2B,QAAQ,SAAS;CAKxD,IAAI,UAA0B;AAC9B,KAAI;AACF,YAAU,MAAM,cAAc,QAAQ,SAAS;UACxC,KAAK;AACZ,YACE,iCACA,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI,CACjD;;AAEH,KAAI,SAAS;AACX,YAAU,aAAa,QAAQ;AAC/B,YACE,oEACD;OAED,SAAQ,YACN,2IACD;AAEH,QAAO;EACL;EACA,SAAS,YAAY;EACrB,WAAW,YAAY;EACvB;EACD;;AAGH,eAAe,2BACb,QACA,UACiE;CAEjE,MAAM,aADW,MAAM,cAAc,QAAQ,SAAS,EAC3B,MAAM;AAEjC,KAAI,CAAC,UACH,OAAM,IAAI,MACR,wHACD;CAGH,MAAM,cAAc,MAAM,iBAAiB,QAAQ,WAAW,SAAS;AACvE,QAAO;EACL,WAAW,YAAY;EACvB,IAAI;EACJ,SAAS;EACV;;AAGH,eAAe,qBACb,QACA,WACA,UACiE;CACjE,MAAM,cAAc,MAAM,iBAAiB,QAAQ,WAAW,SAAS;AACvE,QAAO;EACL,WAAW,YAAY;EACvB,IAAI;EACJ,SAAS;EACV"}
|