@koda-sl/baker-cli 0.505.0 → 0.507.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/README.md +5 -2
- package/dist/{ads-OLOEMAID.js → ads-T5RPISTB.js} +3 -3
- package/dist/{analytics-4CCQYY7Z.js → analytics-P5UFA6I4.js} +315 -18
- package/dist/analytics-P5UFA6I4.js.map +1 -0
- package/dist/{chunk-DI44SZVP.js → chunk-7J3NZBPV.js} +31 -1
- package/dist/chunk-7J3NZBPV.js.map +1 -0
- package/dist/{chunk-VU5Z2V4S.js → chunk-D3KLDIPQ.js} +2 -2
- package/dist/{chunk-GJCB46L6.js → chunk-HWDRW36T.js} +2 -2
- package/dist/{chunk-S3FFLLYF.js → chunk-LIRX2Q66.js} +2 -2
- package/dist/{chunk-DX46OE4U.js → chunk-OJGYYH6K.js} +2 -2
- package/dist/{chunk-ZEGNIP3Y.js → chunk-TVTSA4EN.js} +3 -3
- package/dist/cli.js +4 -4
- package/dist/{critique-DU7K2KVR.js → critique-WOSMZPHW.js} +7 -17
- package/dist/critique-WOSMZPHW.js.map +1 -0
- package/dist/{funnel-727XATM5.js → funnel-S6JTLUK6.js} +3 -3
- package/dist/{google-VPL4KZDP.js → google-3GSINSOV.js} +2 -2
- package/dist/{landing-X4PG6XWU.js → landing-GEWSOHGZ.js} +3 -3
- package/dist/landing-GEWSOHGZ.js.map +1 -0
- package/dist/{lookup-LWJMDEEL.js → lookup-ZJIP2M4X.js} +4 -4
- package/dist/{performance-LMO73TUB.js → performance-WPFGEJTE.js} +2 -2
- package/dist/{plan-O2TTEUIW.js → plan-TVQSEKYL.js} +6 -6
- package/dist/{questionnaire-VE42SW77.js → questionnaire-LEZJLMAV.js} +3 -3
- package/dist/{sends-OLVQGOAE.js → sends-6FI7WELX.js} +3 -3
- package/dist/{sources-PSM2A5QD.js → sources-L3KNVLIB.js} +4 -4
- package/dist/{unstage-QFVL7AZ4.js → unstage-MDSSPUKV.js} +3 -3
- package/dist/{url-options-LA6BL3GS.js → url-options-IHL4OU4C.js} +2 -2
- package/package.json +1 -1
- package/dist/analytics-4CCQYY7Z.js.map +0 -1
- package/dist/chunk-DI44SZVP.js.map +0 -1
- package/dist/critique-DU7K2KVR.js.map +0 -1
- package/dist/landing-X4PG6XWU.js.map +0 -1
- /package/dist/{ads-OLOEMAID.js.map → ads-T5RPISTB.js.map} +0 -0
- /package/dist/{chunk-VU5Z2V4S.js.map → chunk-D3KLDIPQ.js.map} +0 -0
- /package/dist/{chunk-GJCB46L6.js.map → chunk-HWDRW36T.js.map} +0 -0
- /package/dist/{chunk-S3FFLLYF.js.map → chunk-LIRX2Q66.js.map} +0 -0
- /package/dist/{chunk-DX46OE4U.js.map → chunk-OJGYYH6K.js.map} +0 -0
- /package/dist/{chunk-ZEGNIP3Y.js.map → chunk-TVTSA4EN.js.map} +0 -0
- /package/dist/{funnel-727XATM5.js.map → funnel-S6JTLUK6.js.map} +0 -0
- /package/dist/{google-VPL4KZDP.js.map → google-3GSINSOV.js.map} +0 -0
- /package/dist/{lookup-LWJMDEEL.js.map → lookup-ZJIP2M4X.js.map} +0 -0
- /package/dist/{performance-LMO73TUB.js.map → performance-WPFGEJTE.js.map} +0 -0
- /package/dist/{plan-O2TTEUIW.js.map → plan-TVQSEKYL.js.map} +0 -0
- /package/dist/{questionnaire-VE42SW77.js.map → questionnaire-LEZJLMAV.js.map} +0 -0
- /package/dist/{sends-OLVQGOAE.js.map → sends-6FI7WELX.js.map} +0 -0
- /package/dist/{sources-PSM2A5QD.js.map → sources-L3KNVLIB.js.map} +0 -0
- /package/dist/{unstage-QFVL7AZ4.js.map → unstage-MDSSPUKV.js.map} +0 -0
- /package/dist/{url-options-LA6BL3GS.js.map → url-options-IHL4OU4C.js.map} +0 -0
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/commands/analytics/index.ts","../src/commands/analytics/conversionCandidates.ts","../src/commands/analytics/conversionHints.ts","../src/commands/analytics/conversionList.ts","../src/commands/analytics/render.ts","../src/commands/analytics/conversionsMarkdown.ts","../src/commands/analytics/feedRows.ts","../src/commands/analytics/flowConversionKeys.ts","../src/commands/analytics/hints.ts","../src/commands/analytics/presets.ts","../../api/src/accounts/companyName.ts","../src/commands/analytics/visitorCompanies.ts","../src/commands/analytics/websiteTagHints.ts"],"sourcesContent":["import { readFileSync } from \"node:fs\";\nimport type {\n AdPlatform,\n AnalyticsConversionRow,\n AnalyticsConversionsResponse,\n AnalyticsEventCatalogRow,\n AnalyticsMappingResponse,\n AnalyticsPreset,\n AnalyticsQueryResponse,\n AnalyticsSitesResponse,\n AnalyticsTrackingResponse,\n PeopleSearchTerm,\n WebsiteTagResponse,\n} from \"@baker/api/analytics\";\nimport {\n ANALYTICS_EVENT_DOORS,\n isAdPlatform,\n parseEventKey,\n SEND_FILTERS,\n type SendFilter,\n} from \"@baker/api/analytics\";\nimport { flowEventKeys } from \"@baker/api/analytics/flowEventKeys\";\nimport { eventIngestUrl, INGEST_ARRIVALS_MAX, type IngestArrivalsResponse, ingestBrief } from \"@baker/api/event-ingest\";\nimport { defineCommand } from \"citty\";\nimport { ApiError, apiPost } from \"../../client.ts\";\nimport { getEnv } from \"../../env.ts\";\nimport { repeatedValues } from \"../../lib/repeatedFlags.ts\";\nimport { registerSchema } from \"../../schemas.ts\";\nimport { readTargetsFile, type TargetsFile } from \"../accounts/targets.ts\";\nimport { writeJsonEnvelope } from \"../ads/output.ts\";\nimport { flowDataPath, flowExists, listFlowSlugs, readFlowTree } from \"../flows/shared.ts\";\nimport { ConversionArgsError, type ConversionsArgs, conversionsWrite, parseConversionsArgs } from \"./conversionArgs.ts\";\nimport { candidatesForFlow, compactConversionsEnvelope, fullFlagHints } from \"./conversionCandidates.ts\";\nimport { conversionHints, namingHints, stagedHints } from \"./conversionHints.ts\";\nimport { mergeStoredWithCounts } from \"./conversionList.ts\";\nimport { renderConversionsMarkdown, withFormatHint } from \"./conversionsMarkdown.ts\";\nimport { analyticsErrorEnvelope } from \"./errorEnvelope.ts\";\nimport { type FeedShape, feedHints, peopleSearchHints, shapeFeed } from \"./feedRows.ts\";\nimport {\n flowConversionHints,\n flowCoverage,\n flowCoverageHints,\n formKeyRefusal,\n joinFlowConversions,\n} from \"./flowConversionKeys.ts\";\nimport { buildAnalyticsHints } from \"./hints.ts\";\nimport { peopleArgs, peopleLimitError } from \"./lookup.ts\";\nimport { ANALYTICS_PRESET_INFO } from \"./presets.ts\";\nimport {\n ANALYTICS_OUTPUT_FORMATS,\n formatHint,\n isAnalyticsOutputFormat,\n missingBreakdownHint,\n pruneForAgent,\n writeAnalyticsReport,\n} from \"./render.ts\";\nimport { refineCompaniesReport } from \"./visitorCompanies.ts\";\nimport { analyticsSitesHints, websiteTagHints } from \"./websiteTagHints.ts\";\n\n/** Flags every preset takes. Declared once so the five stay identical. */\nconst SHARED_ARGS = {\n days: { type: \"string\", description: \"Lookback window in days (default: 30)\", required: false },\n \"start-date\": { type: \"string\", description: \"Explicit start date (YYYY-MM-DD)\", required: false },\n \"end-date\": { type: \"string\", description: \"Explicit end date (YYYY-MM-DD, inclusive)\", required: false },\n full: {\n type: \"boolean\",\n description: \"Return long breakdowns and the per-day trend instead of the top few\",\n required: false,\n },\n timezone: {\n type: \"string\",\n description:\n \"IANA zone the day boundaries are drawn in, e.g. Europe/Madrid (default: UTC). Pass the client's zone — in UTC, two hours of every Spanish evening land on the following day\",\n required: false,\n },\n granularity: {\n type: \"string\",\n description: \"hour | day (default) | week | month — how wide one point of the trend is\",\n required: false,\n },\n compare: {\n type: \"boolean\",\n description: \"Also return the preceding window of the same length, so every total has something to be read against\",\n required: false,\n },\n landing: {\n type: \"string\",\n description:\n \"Narrow the whole report to one landing, by the internalId in its _definition.md — get it from `baker analytics landings`. Works on every report here: sources, geography, paid campaigns, Form drop-off and delivery all answer for that page alone. Prefer it over --path, which stops matching the moment a page is moved or renamed\",\n required: false,\n },\n role: {\n type: \"string\",\n description:\n 'Narrow the whole report to visits that arrived on one ad dimension: keyword, match_type, campaign_id, adgroup_id, ad_id, placement, network, audience, device, search_query and the rest — run `baker analytics ads` for the full list and what this company actually sends. Pair it with --value. It selects the VISITS that arrived on that value and keeps everything they then did, so `--role keyword --value \"solar panels\"` answers what those people read and whether they converted, not just where they landed',\n required: false,\n },\n value: {\n type: \"string\",\n description:\n \"The value of --role to narrow to. Pass an empty string to get the visits that arrived carrying no such parameter, which is how you size the untagged half of an account\",\n required: false,\n },\n page: {\n type: \"string\",\n description:\n \"Which page of a list report to read, 1-based. `delivery` and `submissions` are the lists here; every other report is a breakdown, which is a top-N by construction and ignores this. The response carries pageInfo.hasMore — when it is true there ARE more rows, so never report a total from one page\",\n required: false,\n },\n \"page-size\": {\n type: \"string\",\n description:\n \"Rows per page, up to 200 (default 50 when --page is given). Without --page or --page-size a list comes back at its old size, so nothing that worked before changes\",\n required: false,\n },\n platform: {\n type: \"string\",\n description:\n \"Read the whole report in one ad platform's vocabulary, and from its traffic only: google, meta, microsoft, linkedin, tiktok, reddit, pinterest, snapchat, chatgpt. Two effects at once — --role resolves against the spellings that platform uses (`cid` is TikTok's ad and nobody else's campaign) and the report narrows to the visits that arrived from it. Start here when comparing platforms, because without it every dimension is the union across all of them and one parameter can land under two roles\",\n required: false,\n },\n origin: {\n type: \"string\",\n description:\n \"Read one door only: `pages` for a browser on a page Baker publishes, `website` for a browser on the client's OWN website (the measurement tag), `server` for what their server posted to POST /v1/events. Omit it for all three, which is the default and the honest superset. Reach for it when a number moved and nothing on the pages changed — a CRM replaying history and a tag on the client's own site both land in the same totals as a landing page visitor, and this is the only thing that separates them. Honoured by overview, traffic, events and conversions; every session-scoped report ignores it, because a server-sent event carries no visit to be counted in\",\n required: false,\n },\n output: {\n type: \"string\",\n description:\n \"json (default) | md. `md` renders each block as a table — one header instead of the keys repeated on every row — which is 20-50% smaller than the JSON on a typical report and more on a long breakdown. Reach for it whenever you are going to read the numbers rather than pick one field out of them. Both carry the same warnings and hints\",\n required: false,\n },\n} as const;\n\ninterface RunOptions {\n preset: AnalyticsPreset;\n path?: string;\n flowSlug?: string;\n grain?: string;\n country?: string;\n landingId?: string;\n tag?: string;\n eventName?: string;\n property?: string;\n where?: { property: string; value: string };\n steps?: string[];\n deviceGrain?: string;\n adRole?: string;\n /** `feed` only: one kind of event, by the key `conversions` hands back. */\n eventKey?: string;\n /** `feed` only: one source, by the name its events carry as `baker_source`. */\n sourceName?: string;\n /** `feed` only: just the uploads a conversion's sends made — every one, or the refused ones. */\n sends?: SendFilter;\n /** `feed` only: one send's uploads, by its id. */\n sendId?: string;\n /** `feed` only: what went to one destination, by side effect type. */\n sentTo?: string;\n /** `feed` only: one event by uid, with everything it set off. */\n eventUid?: string;\n /** `feed` only: several people, each as the reader holds them — resolved and hashed by the server. */\n people?: string[];\n}\n\nfunction handleError(err: unknown): never {\n writeJsonEnvelope({ ok: false, error: analyticsErrorEnvelope(err) });\n process.exit(1);\n}\n\n/**\n * The request body, as a map of optional values with the unset ones dropped.\n *\n * Built declaratively rather than as a run of `if` statements: this is a flat\n * translation from two flag namespaces onto one wire shape, and every new\n * report adds another line to it. As a chain of conditionals it crossed the\n * complexity budget on the fourth preset and would keep doing so; as a filtered\n * object it stays one entry per field however many fields there are.\n */\n/**\n * The page of a list report, as the wire takes it.\n *\n * Lifted out of `requestBody` rather than inlined beside the other flags: two\n * more conditional fields there pushed the translation past the complexity\n * budget, and this pair is one idea rather than two flags anyway.\n */\nfunction pageOf(args: Record<string, unknown>): { page?: number; pageSize?: number } {\n const size = args[\"page-size\"];\n return {\n ...(args.page ? { page: Number(args.page) } : {}),\n ...(size ? { pageSize: Number(size) } : {}),\n };\n}\n\nfunction isWholePositive(raw: unknown): boolean {\n return /^[1-9]\\d*$/.test(String(raw));\n}\n\n/**\n * Why `--page` or `--page-size` cannot be sent, or null when they can.\n *\n * Checked here because the wire only sees `Number(value)`: `--page /pricing`\n * went out as `page: null` and came back as a bare \"expected number to be >=1\"\n * that never said the flag is a page NUMBER, so an agent that had reached for it\n * to name a page path had no way to learn which flag does that.\n */\nexport function paginationError(args: Record<string, unknown>): string | null {\n if (args.page !== undefined && !isWholePositive(args.page)) {\n return (\n `--page takes a page number of a list report (1, 2, …), not \"${String(args.page)}\". ` +\n \"To narrow a report to one page of the site, pass --landing <internalId> (from `baker analytics landings`), \" +\n \"or read one path with `baker analytics page --path /pricing`.\"\n );\n }\n const size = args[\"page-size\"];\n if (size !== undefined && !isWholePositive(size)) {\n return `--page-size takes a number of rows (1 to 200), not \"${String(size)}\".`;\n }\n return null;\n}\n\nexport function requestBody(args: Record<string, unknown>, options: RunOptions): Record<string, unknown> {\n const candidates: Record<string, unknown> = {\n preset: options.preset,\n days: args.days ? Number(args.days) : undefined,\n startDate: args[\"start-date\"],\n endDate: args[\"end-date\"],\n full: args.full ? true : undefined,\n compare: args.compare ? true : undefined,\n timezone: args.timezone ? String(args.timezone) : undefined,\n granularity: args.granularity ? String(args.granularity) : undefined,\n path: options.path,\n flowSlug: options.flowSlug,\n grain: options.grain,\n country: options.country,\n // Read off the shared flag unless a preset resolved one itself. Every\n // report takes it, so wiring it per-preset would be fifteen identical lines\n // and, historically, four of them missing.\n landingId: options.landingId ?? (args.landing ? String(args.landing) : undefined),\n tag: options.tag,\n eventName: options.eventName,\n property: options.property,\n where: options.where,\n steps: options.steps && options.steps.length > 0 ? options.steps : undefined,\n deviceGrain: options.deviceGrain,\n adRole: options.adRole ?? (args.role ? String(args.role) : undefined),\n // Deliberately not dropped when empty: with a role set, an empty value is a\n // real question — \"the visits that arrived carrying none of these\" — and the\n // filter below would otherwise strip it and silently widen the report to\n // everything.\n adValue: args.role !== undefined && args.value !== undefined ? String(args.value) : undefined,\n adPlatform: args.platform ? String(args.platform) : undefined,\n eventOrigin: args.origin ? String(args.origin) : undefined,\n eventKey: options.eventKey,\n sourceName: options.sourceName,\n sends: options.sends,\n // `feed --send` and `--uid` were parsed and then dropped here, so both\n // answered with the whole unnarrowed feed as if they had worked.\n sendId: options.sendId,\n sentTo: options.sentTo,\n eventUid: options.eventUid,\n people: options.people,\n ...pageOf(args),\n };\n return Object.fromEntries(\n Object.entries(candidates).filter(([key, value]) => value !== undefined && (value !== \"\" || key === \"adValue\")),\n );\n}\n\n/**\n * The three doors, named as the request spells them.\n *\n * Imported rather than spelled again. This was a third copy of a list that\n * also lived in the wire schema and in the dashboard's search params, which is\n * how a value ends up accepted on one surface and refused on another.\n */\nconst EVENT_ORIGINS = ANALYTICS_EVENT_DOORS;\n\n/** The feed's own hints — held-back rows, uploads, and the searched people it found nothing for. */\nfunction feedReportHints(feed: FeedShape | undefined, peopleSearch: readonly PeopleSearchTerm[] | undefined): string[] {\n if (feed === undefined) return [];\n return [...feedHints(feed), ...peopleSearchHints(peopleSearch ?? [], feed.rows)];\n}\n\n/**\n * `refine` reshapes the pruned data in place and returns its own hints — the\n * one preset that needs it narrows the rows to a list read from a file.\n */\nasync function runPreset(\n args: Record<string, unknown>,\n options: RunOptions,\n refine?: (data: Record<string, unknown>, raw: AnalyticsQueryResponse[\"data\"]) => string[],\n): Promise<void> {\n const origin = args.origin === undefined ? \"\" : String(args.origin);\n // Refused rather than dropped. A misspelled `--origin sytems` that the wire\n // ignores answers for BOTH collectors and looks exactly like an answer for\n // one, which is the whole failure this flag exists to prevent.\n if (origin !== \"\" && !(EVENT_ORIGINS as readonly string[]).includes(origin)) {\n writeJsonEnvelope({\n ok: false,\n error: {\n code: \"VALIDATION_ERROR\",\n message: `Unknown --origin \"${origin}\". Use one of: ${EVENT_ORIGINS.join(\", \")}, or omit it for all three.`,\n },\n });\n process.exit(1);\n }\n const pagination = paginationError(args);\n if (pagination) {\n writeJsonEnvelope({ ok: false, error: { code: \"VALIDATION_ERROR\", message: pagination } });\n process.exit(1);\n }\n try {\n const response = await apiPost<AnalyticsQueryResponse>(\"/api/analytics/query\", requestBody(args, options));\n // From the flag rather than from `options`, which is the preset's own\n // defaults — `--platform` is what actually scoped the ad blocks in the\n // request built above.\n const requestedPlatform = args.platform === undefined ? \"\" : String(args.platform);\n const adPlatformScope: AdPlatform | undefined = isAdPlatform(requestedPlatform) ? requestedPlatform : undefined;\n const format = outputFormatOrExit(args.output);\n const data = pruneForAgent(response.data, args.full === true);\n // The feed's own rows, read rather than forwarded: the destination named\n // in words, every key the row could be matched on, and the machinery held\n // back with a count. Applied after pruning so the shaper sees the same\n // rows the agent will.\n const feed = \"stream\" in data ? shapeFeed(data.stream, args.everything === true) : undefined;\n if (feed !== undefined) data.stream = feed.rows;\n const refinedHints = refine ? refine(data, response.data) : [];\n const hints = [\n ...feedReportHints(feed, response.data.peopleSearch),\n // The platform the ad blocks were scoped to, so the tagging hint can\n // only speak about the inventory it was actually given. `adParams` is one\n // platform's list; a hint drawn from it without knowing which platform\n // would be a claim about Meta made from Google's URLs.\n ...buildAnalyticsHints(response.data, adPlatformScope, origin === \"\" ? undefined : origin),\n ...missingBreakdownHint(response.data as unknown as Record<string, unknown>, {\n eventName: options.eventName,\n property: options.property,\n }),\n ...refinedHints,\n ...formatHint(data, format),\n ];\n writeAnalyticsReport(\n {\n ok: true,\n data,\n ...(response.warnings && response.warnings.length > 0 ? { warnings: response.warnings } : {}),\n ...(hints.length > 0 ? { hints } : {}),\n },\n format,\n );\n } catch (err) {\n handleError(err);\n }\n}\n\n/** The `--output` asked for, or a refusal naming the ones there are. */\nfunction outputFormatOrExit(raw: unknown): string {\n const format = raw === undefined ? \"json\" : String(raw);\n if (!isAnalyticsOutputFormat(format)) {\n writeJsonEnvelope({\n ok: false,\n error: {\n code: \"VALIDATION_ERROR\",\n message: `Unknown --output \"${format}\". Use one of: ${ANALYTICS_OUTPUT_FORMATS.join(\", \")}.`,\n },\n });\n process.exit(1);\n }\n return format;\n}\n\nfunction presetCommand(options: {\n name: string;\n preset: AnalyticsPreset;\n description: string;\n extraArgs?: Record<string, { type: \"string\" | \"boolean\"; description: string; required?: boolean }>;\n resolve?: (args: Record<string, unknown>) => RunOptions | { error: string };\n}) {\n registerSchema({\n command: `analytics.${options.name}`,\n description: options.description,\n args: { ...SHARED_ARGS, ...(options.extraArgs ?? {}) },\n });\n\n return defineCommand({\n meta: { name: options.name, description: options.description },\n args: { ...SHARED_ARGS, ...(options.extraArgs ?? {}) },\n run: async ({ args }) => {\n const resolved = options.resolve\n ? options.resolve(args as Record<string, unknown>)\n : ({ preset: options.preset } satisfies RunOptions);\n if (\"error\" in resolved) {\n writeJsonEnvelope({ ok: false, error: { code: \"VALIDATION_ERROR\", message: resolved.error } });\n process.exit(1);\n }\n await runPreset(args as Record<string, unknown>, resolved);\n },\n });\n}\n\nconst overviewCommand = presetCommand({\n name: \"overview\",\n preset: \"overview\",\n description:\n \"Start here: traffic, top pages and their conversion rates, where visitors came from, and the worst step of every Form — in one call.\",\n});\n\nconst trafficCommand = presetCommand({\n name: \"traffic\",\n preset: \"traffic\",\n description: \"Where visitors come from — trend, sources, campaigns, countries and devices.\",\n});\n\nconst funnelCommand = presetCommand({\n name: \"funnel\",\n preset: \"funnel\",\n description: \"Per-step drop-off for the company's Forms. Narrow to one with --flow.\",\n extraArgs: { flow: { type: \"string\", description: \"Form slug (default: every Form)\", required: false } },\n resolve: (args) => ({ preset: \"funnel\", flowSlug: args.flow ? String(args.flow) : undefined }),\n});\n\nconst flowCommand = presetCommand({\n name: \"flow\",\n preset: \"flow\",\n description:\n \"One Form in depth: how many people it converted, where they went between steps, the per-step table, and every trigger it raised. Every count is DISTINCT VISITS, so quote shares against `funnels[].visits` — the visits that opened the Form — and never against `starts`, which is only the visits that touched it. The gap between the two is people who read the Form and left, and on most Forms it is the largest loss there is. Read `flowSummary.convertedSessions`, not `submits` — a Form that books a call on a scheduling node in the MIDDLE of the flow never emits a submit, so it reports zero submits and every one of its real bookings as conversions. `flowPaths` is the flow as edges between steps, bracketed by __flow_start__, __flow_converted__ and __flow_exit__, which is how a branching Form is read at all: a per-step table cannot say that of the people who left step two, forty went to the booking branch and ninety went nowhere. `flowDestinations` is the other half and is evidence rather than a count: which Forms demonstrably handed something over in this window. Read it against the scaffold's `flow-no-destination` warning before acting on one — the stored picture of a Form is only rewritten when it is published, so a destination added since then shows there and not in the tree.\",\n extraArgs: { flow: { type: \"string\", description: \"Form slug (default: every Form)\", required: false } },\n resolve: (args) => ({ preset: \"flow\", flowSlug: args.flow ? String(args.flow) : undefined }),\n});\n\nconst pageCommand = presetCommand({\n name: \"page\",\n preset: \"page\",\n description: \"One page in detail — its traffic, entrances, engagement and conversion rate.\",\n extraArgs: { path: { type: \"string\", description: \"Page path, e.g. /pricing (no trailing slash)\", required: true } },\n resolve: (args) =>\n args.path\n ? { preset: \"page\", path: String(args.path) }\n : {\n error:\n \"Provide --path, e.g. --path /pricing. Run `baker analytics overview` to see which paths have traffic.\",\n },\n});\n\nconst triggersCommand = presetCommand({\n name: \"triggers\",\n preset: \"triggers\",\n description:\n \"What visitors actually do inside a Form — link clicks that send them off the site, scheduling widgets, embedded checkouts — and how much of it fires a side effect. Narrow to one Form with --flow.\",\n extraArgs: { flow: { type: \"string\", description: \"Form slug (default: every Form)\", required: false } },\n resolve: (args) => ({ preset: \"triggers\", flowSlug: args.flow ? String(args.flow) : undefined }),\n});\n\nconst releasesCommand = presetCommand({\n name: \"releases\",\n preset: \"releases\",\n description:\n \"Traffic and conversions per published build, newest first. Use it to tell whether a change to a page moved the numbers, instead of guessing from the date.\",\n});\n\nconst paidCommand = presetCommand({\n name: \"paid\",\n preset: \"paid\",\n description:\n \"Paid campaigns, counted per visit rather than per page view, with the click ids that resolve to a real ad click — and how many of those leads were actually delivered. A campaign whose forms all submit while the CRM rejects every one of them reads as perfect everywhere else and as zero delivered here.\",\n});\n\nconst deliveryCommand = presetCommand({\n name: \"delivery\",\n preset: \"delivery\",\n description:\n \"Where each submission went: every destination it reached, whether that destination accepted it, and the id it gave the record it created. Failures first; --full lists every submission. Start here when someone says a lead never arrived.\",\n});\n\n/**\n * Every attempt at a Form, delivered or not.\n *\n * The companion `delivery` structurally cannot be. That list is built from\n * dispatches, so a Form wired to no destination makes none and appears nowhere\n * in it — correct for \"what did we hand their CRM\", and reads as data loss to\n * everybody else. This is the one that answers \"I filled in my own form, where\n * is it?\" honestly.\n */\nconst submissionsCommand = presetCommand({\n name: \"submissions\",\n preset: \"submissions\",\n description:\n \"Every attempt anybody made at a Form — one visitor, one Form, one visit — whether they finished, gave up halfway, or completed a Form that sends nowhere. Read this rather than `delivery` when the question is what was collected: `delivery` starts from the dispatch, so a Form with no destination produces no row there at all. `stepsReached` against `stepsSubmitted` says how far they got; `finished` says whether the Form reached an ending; `destinations` is what it demonstrably reached, which beats the picture in its _data.json. A list, so it pages — read pageInfo.hasMore before quoting a total. Narrow to one Form with --flow.\",\n extraArgs: { flow: { type: \"string\", description: \"Form slug (default: every Form)\", required: false } },\n resolve: (args) => ({ preset: \"submissions\", flowSlug: args.flow ? String(args.flow) : undefined }),\n});\n\nconst geoCommand = presetCommand({\n name: \"geo\",\n preset: \"geo\",\n description:\n \"Where the people who convert actually are. Country by default; --grain city|postal_code|metro_code goes finer, and --country narrows first so postcodes do not collide across markets. Use this when the campaign is local — a radius around a branch is a set of postcodes, never a set of countries.\",\n extraArgs: {\n grain: {\n type: \"string\",\n description: \"country (default) | region | city | postal_code | metro_code\",\n required: false,\n },\n country: { type: \"string\", description: \"Two-letter code to look inside, e.g. ES\", required: false },\n },\n resolve: (args) => ({\n preset: \"geo\",\n grain: args.grain ? String(args.grain) : undefined,\n country: args.country ? String(args.country) : undefined,\n }),\n});\n\nconst botsCommand = presetCommand({\n name: \"bots\",\n preset: \"bots\",\n description:\n \"Which machines are fetching this site — search crawlers, AI answer engines, link-preview scrapers, SEO tools — and whether each one is genuine or only claims to be. Every other report excludes these; this is the one that reads them. Use it to check that a republished page is still being crawled, or that Meta fetched a link preview before spend goes behind the URL.\",\n});\n\nconst COMPANIES_DESCRIPTION =\n \"Which companies are visiting these pages — named by the visitor's own network, never a person — with visits, pages read and first/last seen. Only recorded when an admin turned on 'Show which companies visit your pages', the company measures in full, and the visitor accepted marketing cookies; home and mobile connections are never named. --targets <file> keeps the target accounts that came and lists the ones that did not — the website half of an ABM report.\";\n\nconst COMPANIES_ARGS = {\n ...SHARED_ARGS,\n targets: {\n type: \"string\",\n description:\n 'The target-account list `baker accounts --targets` takes: a .csv (a domain or website column; name and tier optional) or a .json file ([\"acme.com\", …] or [{\"domain\": \"acme.com\", \"name\": \"Acme\", \"tier\": 1}, …]). Matched by company NAME, because a visitor company has no website — give each target its name; one without matches only when its name starts with the domain\\'s first word. Build one with `node __tooling__/scripts/target-accounts.mjs --out targets.csv`',\n required: false,\n },\n} as const;\n\nregisterSchema({ command: \"analytics.companies\", description: COMPANIES_DESCRIPTION, args: COMPANIES_ARGS });\n\nconst companiesCommand = defineCommand({\n meta: { name: \"companies\", description: COMPANIES_DESCRIPTION },\n args: COMPANIES_ARGS,\n run: async ({ args }) => {\n let targets: TargetsFile | undefined;\n if (args.targets) {\n try {\n targets = readTargetsFile(String(args.targets), readFileSync(String(args.targets), \"utf8\"));\n } catch (err) {\n writeJsonEnvelope({\n ok: false,\n error: {\n code: \"VALIDATION_ERROR\",\n message: `Could not read --targets ${String(args.targets)}: ${err instanceof Error ? err.message : String(err)}`,\n },\n });\n process.exit(1);\n }\n }\n // Matching against the compact top five would report every target that\n // visited less often as never having come, so --targets reads the full list.\n const request = targets ? { ...args, full: true } : args;\n await runPreset(request as Record<string, unknown>, { preset: \"companies\" }, (data, raw) =>\n refineCompaniesReport(data, raw.visitorCompanies ?? [], targets),\n );\n },\n});\n\nconst landingsCommand = presetCommand({\n name: \"landings\",\n preset: \"landings\",\n description:\n \"Every landing side by side — views, entrances, conversion rate and a sparkline each, under the name a person gave it rather than its id. This is the one that answers 'which of my pages is working' without having to know the answer first; `page` answers one page in depth once this has named it. Narrow to a campaign with --tag.\",\n extraArgs: {\n tag: { type: \"string\", description: \"Only landings filed under this tag\", required: false },\n },\n resolve: (args) => ({ preset: \"landings\", tag: args.tag ? String(args.tag) : undefined }),\n});\n\nconst tagsCommand = presetCommand({\n name: \"tags\",\n preset: \"tags\",\n description:\n \"The same numbers grouped by the tag a landing is filed under — the unit a campaign is actually run at. Nobody asks how /pricing-b did; they ask how the September push did, and September was six landings. Tags are declared in each landing's _definition.md.\",\n});\n\nconst eventsCommand = presetCommand({\n name: \"events\",\n preset: \"events\",\n description:\n \"Events the pages define for themselves — a section scrolled into view, a video played, a button clicked. Lists each one with the property keys it carries, so you can then break it down: --event video --property milestone. Pass --steps a,b,c for an ORDERED funnel over them, where a session only counts at each step if it got there after the one before.\",\n extraArgs: {\n event: { type: \"string\", description: \"Break this one event down (needs --property too)\", required: false },\n property: { type: \"string\", description: \"Which of that event's property keys to group by\", required: false },\n where: {\n type: \"string\",\n description:\n \"Narrow to one property value before grouping, as key=value — e.g. --event vsl --property milestone --where autoplay=0 for the retention curve of the videos visitors chose to play, rather than the one a muted hero produces on its own\",\n required: false,\n },\n steps: {\n type: \"string\",\n description: \"Comma-separated event names, in order, for a funnel — e.g. hero_view,pricing_view,demo_click\",\n required: false,\n },\n },\n resolve: (args) => {\n const steps = args.steps\n ? String(args.steps)\n .split(\",\")\n .map((step) => step.trim())\n .filter((step) => step !== \"\")\n : undefined;\n if (args.property && !args.event) {\n return {\n error: \"Breaking an event down by a property needs --event too, e.g. --event video --property milestone\",\n };\n }\n if (steps && steps.length < 2) {\n return { error: \"A funnel needs at least two steps, e.g. --steps hero_view,pricing_view\" };\n }\n let where: { property: string; value: string } | undefined;\n if (args.where) {\n const raw = String(args.where);\n const at = raw.indexOf(\"=\");\n // Split on the FIRST `=` only: a property value may well contain one, and\n // the key never does — the validator holds keys to snake_case.\n if (at < 1) {\n return { error: \"--where takes key=value, e.g. --where autoplay=0\" };\n }\n if (!args.property) {\n return { error: \"--where narrows a breakdown, so it needs --event and --property too\" };\n }\n where = { property: raw.slice(0, at), value: raw.slice(at + 1) };\n }\n return {\n preset: \"events\",\n eventName: args.event ? String(args.event) : undefined,\n property: args.property ? String(args.property) : undefined,\n where,\n steps,\n };\n },\n});\n\n/**\n * `baker analytics feed` — the raw event feed, one row per event as it was\n * stored, newest first. The only report that does not reduce: the answer to\n * \"did the thing I just did arrive, and what did it say\" is a row, not a\n * number. Narrowed by the two things a source question needs — which source\n * sent it, which kind it is — on top of the shared window and origin.\n */\n/**\n * `--outcome`, in the same five words the Measure → Events menu offers.\n *\n * Every filter on that screen has to be reachable here, and three of the five\n * outcomes already were — under two different flags and a second command. So\n * this is a translation rather than a new narrowing: `counted` is the feed\n * with nothing added (the store holds only what counted), `sent` and\n * `refused` set `--sends`, and the two that never became events name\n * `baker analytics arrivals` and the flag to use there rather than returning\n * an empty list that reads as \"nothing happened\".\n */\nconst FEED_OUTCOMES: Readonly<Record<string, { sends?: SendFilter; elsewhere?: string }>> = {\n counted: {},\n sent: { sends: \"any\" },\n refused: { sends: \"failed\" },\n skipped: { sends: \"skipped\" },\n test: { elsewhere: \"baker analytics arrivals --mode test\" },\n \"not-counted\": { elsewhere: \"baker analytics arrivals --outcome rejected\" },\n};\n\nexport function feedRequest(args: Record<string, unknown>): RunOptions | { error: string } {\n const tooMany = peopleLimitError(args);\n if (tooMany !== undefined) return { error: tooMany };\n const raw = args.sends === undefined ? undefined : String(args.sends);\n if (raw !== undefined && !(SEND_FILTERS as readonly string[]).includes(raw)) {\n return { error: `--sends is one of ${SEND_FILTERS.join(\", \")}, not \"${raw}\".` };\n }\n const sends = raw as SendFilter | undefined;\n const outcome = args.outcome === undefined ? undefined : String(args.outcome);\n if (outcome !== undefined) {\n const mapped = FEED_OUTCOMES[outcome];\n if (mapped === undefined) {\n return { error: `--outcome is one of: ${Object.keys(FEED_OUTCOMES).join(\", \")}. Not \"${outcome}\".` };\n }\n if (mapped.elsewhere !== undefined) {\n return {\n error: `--outcome ${outcome} is not on the feed: those requests never became events. Run \\`${mapped.elsewhere}\\` instead.`,\n };\n }\n if (mapped.sends !== undefined && sends === undefined) {\n return { ...feedNarrowing(args), sends: mapped.sends };\n }\n }\n return { ...feedNarrowing(args), ...(sends === undefined ? {} : { sends }) };\n}\n\n/** Everything `feed` narrows by other than the outcome, which two paths above share. */\nfunction feedNarrowing(args: Record<string, unknown>): RunOptions {\n return {\n preset: \"stream\",\n ...(args.source ? { sourceName: String(args.source) } : {}),\n ...(args.kind ? { eventKey: String(args.kind) } : {}),\n ...(args.send ? { sendId: String(args.send) } : {}),\n ...(args[\"sent-to\"] ? { sentTo: String(args[\"sent-to\"]) } : {}),\n ...(args.uid ? { eventUid: String(args.uid) } : {}),\n ...peopleArgs(args),\n };\n}\n\nconst feedCommand = presetCommand({\n name: \"feed\",\n preset: \"stream\",\n description:\n 'Every event recorded in the window, newest first, exactly as it was stored — the Measure → Events tab as rows. The one report that does not reduce, for \"did anything arrive from Zapier today\" and \"what did that HubSpot read actually carry\". --source <name> keeps one source\\'s events (names from `baker analytics sources`; every event from an address or a HubSpot read carries its source\\'s name as `baker_source`); --kind <event key> keeps one kind (keys from `conversions`); --origin pages|website|server keeps one door (a page Baker publishes, the client\\'s own website, or their systems); --sends any|failed keeps only the uploads a conversion\\'s sends made to Meta, Google Ads and the rest — `failed` is the rows behind \"6 failed\" on `baker analytics sends`, each with the platform\\'s own words in `error`. Paged with --page; test sends and refusals are on `arrivals`, because they never became an event.',\n extraArgs: {\n outcome: {\n type: \"string\",\n description:\n \"counted | sent | refused | skipped | test | not-counted — the Measure → Events outcome menu, in its own words. `counted` is every stored event (the default); `sent` and `refused` are the uploads an ad platform took and turned away; `skipped` are a send's events never uploaded because they carried nothing the platform matches a person by (no Meta click or email, no gclid, no li_fat_id…), each with what was missing in `error`; `test` and `not-counted` never became events and are answered by `baker analytics arrivals`, which this names if you ask for them\",\n required: false,\n },\n sends: {\n type: \"string\",\n description:\n \"any | failed | skipped — only what was SENT ON: every delivery a Form's actions made (a Meta or Google Ads pixel fired, a Conversions API call, a webhook, an email, a CRM contact) and every upload an event's send made (side_effect_type conversionSend:<platform>); `failed` keeps the ones the destination refused, with its reason in `error`; `skipped` the events a send never uploaded, with what they were missing in `error`. The same narrowing as --outcome sent | refused | skipped\",\n required: false,\n },\n \"sent-to\": {\n type: \"string\",\n description:\n 'One destination, by its type — meta, googleAds, linkedinInsightTag, httpWebhook, email, hubspot… — keeping both a Form\\'s deliveries there and the uploads sends made to it. The answer to \"what went to Meta\"',\n required: false,\n },\n send: {\n type: \"string\",\n description:\n \"One send's uploads and nothing else, by its id from `baker analytics sends` (`s1`) — the rows behind that send's outcome, each with its verdict. Implies --sends any; add --sends failed for the refused ones only\",\n required: false,\n },\n uid: {\n type: \"string\",\n description:\n 'One event by its `event_uid`, together with every upload a send made for it (the rows carrying it as `event_id`), each with the platform\\'s verdict — the answer to \"is this the event that triggered that upload\"',\n required: false,\n },\n source: {\n type: \"string\",\n description:\n \"One source, by its name from `baker analytics sources` — an address a tool posts to, or a HubSpot read. Omit it for every event, sourced or not\",\n required: false,\n },\n kind: {\n type: \"string\",\n description: \"One kind of event, by the key `baker analytics conversions` lists (page:Deal won, form:contact:…)\",\n required: false,\n },\n person: {\n type: \"string\",\n description:\n \"One or more people and everything they did, by whatever identifies them — comma-separated: an email, a phone (+34…), a HubSpot contact id, deal:<HubSpot deal id>, a gclid/fbclid, or the `anonId` off any row. Prefix to be exact (email:, phone:, hubspot:, deal:, gclid:, fbclid:, browser:). Emails and phones are hashed by Baker, never stored in the clear. Each is resolved to the whole person — across devices and every address, phone, CRM id, ad click and cookie ever seen beside it — so the deal their CRM posted is on the list beside the page view that named them. Every row says which of them it is in `personTerms` (positions in `peopleSearch`). Up to 50; `baker analytics lookup` answers the same list as one summary line each\",\n required: false,\n },\n \"people-file\": {\n type: \"string\",\n description: \"A file with one person per line, read like --person — a column pasted from a CRM export\",\n required: false,\n },\n everything: {\n type: \"boolean\",\n description:\n \"Include the rows held back by default: the ones recorded so the reports can be worked out (identifications, step views, session starts, a destination's answer already folded onto its dispatch) and embedded forms and calendars announcing that they drew. Reach for it when debugging the collector rather than an integration — an embed that draws and never fires again is only visible here\",\n required: false,\n },\n },\n resolve: (args) => feedRequest(args),\n});\n\nconst devicesCommand = presetCommand({\n name: \"devices\",\n preset: \"devices\",\n description:\n \"What visitors are actually using, narrow enough to reproduce a bug on: browser and version, OS and version, rendering engine, or viewport width. `traffic` says mobile converts worse; this says it is Safari 17 on viewports under 380px, which is a browser you can open and a width you can resize to.\",\n extraArgs: {\n grain: {\n type: \"string\",\n description: \"browser (default) | os | engine | device | viewport\",\n required: false,\n },\n },\n resolve: (args) => ({\n preset: \"devices\",\n deviceGrain: args.grain ? String(args.grain) : undefined,\n }),\n});\n\nconst presetsCommand = defineCommand({\n meta: { name: \"presets\", description: \"List the available reports and what each answers.\" },\n run: () => {\n writeJsonEnvelope({ ok: true, data: ANALYTICS_PRESET_INFO });\n },\n});\n\nconst adsCommand = presetCommand({\n name: \"ads\",\n preset: \"ads\",\n description:\n \"What the ad platforms told us about each click, and what those people then did. `--role keyword` breaks the window down by keyword with conversions AND bounced visits beside each one — the ad platform can tell you a keyword got clicks, only this can tell you the people it brought left immediately. Three things come back either way. `adPlatformTraffic` is visits, conversions and TAGGED visits per platform — read it first, because a platform with visits and zero tagged visits has untagged ad URLs and no mapping will ever help it. `adParams` is the parameters arriving on the traffic of the platform in --platform (Google when none is given), never a shared list: a visit comes from one platform, so `gclid` traffic is not also Meta traffic. A parameter with no `role` is one nobody has told us the meaning of, and until somebody does its values are not stored at all — only the fact that the name appeared. `adParamsUnattributed` is the parameters on visits with no click id and no recognisable source, which belong to no platform and can only be placed by somebody who recognises them. Map any of them with `baker analytics map`.\",\n extraArgs: {},\n});\n\n/**\n * Reading and changing how this company's URLs are understood.\n *\n * Its own subcommand rather than a flag on `ads` because it is the only thing\n * in this family that WRITES. It is company configuration — it changes what\n * every future report means and what the edge stores from here on — so inside a\n * Session it is STAGED like any other durable effect: reviewable in the chat,\n * applied on publish, reversed by a discard.\n */\nconst mapCommand = (() => {\n const args = {\n platform: {\n type: \"string\",\n description:\n \"Which platform these parameters belong to: google, meta, microsoft, linkedin, tiktok, reddit, pinterest, snapchat, chatgpt. REQUIRED with --set or --remove, because there is no such thing as a parameter that means one thing everywhere — `cid` is TikTok's ad and everyone else's campaign, and Meta cannot send a keyword at all. Leave it off only to read the whole mapping back\",\n required: false,\n },\n set: {\n type: \"string\",\n description:\n \"Parameter=role pairs, comma separated: --set kw=keyword,mt=match_type. Roles come from `baker analytics ads` (every row lists the role in force). Two of them are the flexible ones: `other` captures a parameter and lets you filter by its own name without claiming it means anything, and `ignore` says it is not campaign information at all — Baker stops storing it and stops listing it as something to deal with\",\n required: false,\n },\n remove: {\n type: \"string\",\n description:\n \"Parameter names to stop reading, comma separated. Only affects this company's own entries, and only within --platform — removing an entry scoped to TikTok needs the same --platform tiktok that set it\",\n required: false,\n },\n } as const;\n\n registerSchema({\n command: \"analytics.map\",\n description: \"Stage how this company's campaign URLs spell each ad dimension, applied on publish\",\n args,\n });\n\n return defineCommand({\n meta: {\n name: \"map\",\n description:\n \"Teach Baker how this company's campaign URLs spell each ad dimension on one platform — `--platform google --set kw=keyword`. Run it with no flags to see the whole mapping, including what this chat has already staged. STAGED, not applied: the answers are reviewed in the chat and take effect when it is published, so nothing you map here changes a report or what the edge stores until then; discarding the chat takes them all back. Mappings are always per platform, because the platforms do not describe the same world: `builtIn` in the response is what that platform is already understood to send, so you do not spend a turn mapping `keyword` to keyword. Each --set is checked against the client's OWN ad account — `evidence` in the response says whether their tracking template really fills that name with what you claimed. Read it: `contradicted` means the account disagrees, and mapping it your way would produce a report that is wrong and looks plausible. Renaming a parameter Baker already captured fixes the history too, because roles are applied when a report is read; naming one it never read only starts collecting it from the publish onward, since no value was stored. You do NOT need to have seen a parameter to map it: a name nobody has sent yet is a valid --set, which is how a tracking template gets configured before the campaign that uses it runs. To drop an answer, --remove takes it back to whatever Baker knows by itself, and --set <name>=ignore says it is not campaign information at all so it stops being listed.\",\n },\n args,\n run: async ({ args: raw }) => {\n try {\n const body = {\n platform: raw.platform === undefined ? undefined : String(raw.platform),\n set: parsePairs(raw.set === undefined ? undefined : String(raw.set)),\n remove: parseList(raw.remove === undefined ? undefined : String(raw.remove)),\n };\n const response = await apiPost<AnalyticsMappingResponse>(\"/api/analytics/mapping\", body);\n writeJsonEnvelope(response);\n } catch (err) {\n handleError(err);\n }\n },\n });\n})();\n\n/**\n * Whether this company's ad URLs carry what a report needs — and what to do\n * about each way they do not.\n *\n * Its own command rather than a flag on `ads`, because it answers a question\n * `ads` structurally cannot: `ads` reports the traffic, and the whole point\n * here is a verdict for a platform whose first click has not landed yet, read\n * off the client's own tracking templates. It is also the one place the two\n * fixes are told apart — a name nobody mapped is free to fix and repairs the\n * history with it, a macro the ads never send is a change in the ad account and\n * everything before it stays unattributable.\n */\nconst trackingCommand = (() => {\n const args = {\n platform: {\n type: \"string\",\n description:\n \"One platform: google, meta, microsoft, linkedin, tiktok, reddit, pinterest, snapchat, chatgpt. Omit to check the ones worth checking — every platform sending visits, plus Google and Meta, whose accounts Baker can read directly\",\n required: false,\n },\n days: { type: \"string\", description: \"Lookback window for the traffic half (default: 30)\", required: false },\n } as const;\n\n registerSchema({\n command: \"analytics.tracking\",\n description: \"Check whether a platform's ad URLs carry the campaign, ad group and ad a report needs\",\n args,\n });\n\n return defineCommand({\n meta: {\n name: \"tracking\",\n description: `Check whether this company's ad URLs carry the dimensions any budget decision needs — the campaign, the ad group, the ad, and the keyword where the platform has one.\n\nRead this before auditing an ad account and before answering any \"which ad performed best\" question. Each dimension comes back in one of four states, and they need different things:\n covered — arriving and understood, nothing to do\n unmapped — the URLs carry it under a name Baker does not read yet. Free to fix, and it repairs the traffic already collected: run the \\`mapCommand\\` in the response\n missing — nothing carries it. \\`fix\\` says which LEVELS that platform accepts tagging at (widest first — prefer it), which Baker can stage and which the user must do by hand, and how each field treats what is already in it. \\`suffix\\` is only the MISSING pairs, so read the current value, add them, write the union. This one cannot be filled in later — a parameter's values are only stored once it starts arriving\n unknown — no visit arrived AND the account could not be read. Not a finding; do not report it as untagged\n\nBaker reads the client's own Google tracking templates and Meta URL parameters, so a platform that has not run yet still gets a real answer.\n\nExamples:\n baker analytics tracking\n baker analytics tracking --platform meta\n baker analytics tracking --platform google --days 90`,\n },\n args,\n run: async ({ args: raw }) => {\n try {\n const platform = raw.platform === undefined ? undefined : String(raw.platform);\n if (platform !== undefined && !isAdPlatform(platform)) {\n writeJsonEnvelope({\n ok: false,\n error: { code: \"BAD_REQUEST\", message: `Unknown platform \"${platform}\"` },\n });\n process.exit(1);\n }\n const days = raw.days === undefined ? undefined : Number(raw.days);\n const response = await apiPost<AnalyticsTrackingResponse>(\"/api/analytics/tracking\", {\n ...(platform ? { platform } : {}),\n ...(days !== undefined && Number.isFinite(days) ? { days } : {}),\n });\n writeJsonEnvelope(response);\n } catch (err) {\n handleError(err);\n }\n },\n });\n})();\n\n/**\n * What this company counts as a conversion — read it, or change it.\n *\n * The command that exists because the previous answer to \"what counts\" lived in\n * each Form's `_data.json`, so changing it meant editing a file and publishing,\n * and a landing page's own events could not be counted at all. Measured before\n * the change: 12 of 203 Forms across 45 companies had ever been marked.\n *\n * ## One conversion per call, and why it is not `--set k=v,k=v`\n *\n * Its sibling `analytics map` takes comma-separated `name=role` pairs, which\n * works because a role is a slug from a closed set. A conversion carries a\n * NAME somebody wrote — \"Checkout iniciado — NO es una compra (se mide en\n * /success)\" is a real one from production — and names contain commas, equals\n * signs and em dashes. Splitting on those would truncate the name, store it,\n * and show the truncation on every report as though it were what somebody\n * typed.\n *\n * The obvious alternative, a repeatable `--event`, is worse: citty keeps only\n * the LAST value of a repeated flag, so `--event a --event b` silently counts\n * one of them. That exact shape once staged one of three UTM parameters against\n * a write that replaces the whole set.\n *\n * So: explicit flags, one outcome per call. Two outcomes is two calls, which is\n * cheap and cannot be misread.\n */\n/**\n * Why an event key could not match anything in the Form it names, read from\n * this workspace's own files.\n *\n * Silent about every other family and about a Form this workspace does not\n * have: a `page:` key is whatever a page decided to call an event, and a Form\n * deleted since it collected its history is still countable.\n */\nfunction localFormKeyRefusal(eventKey: string): { message: string; fix: string } | null {\n const parsed = parseEventKey(eventKey);\n if (parsed?.family !== \"form\") return null;\n if (!flowExists(parsed.flowSlug)) return null;\n return formKeyRefusal(eventKey, readFlowTree(parsed.flowSlug), parsed.flowSlug);\n}\n\n/**\n * Refuse an event key that could never match anything in the Form it names.\n *\n * Before the write, because afterwards the mistake is stored and silent: a key\n * naming a step this Form does not have, or a trigger that step never raises,\n * passes every check there is and counts zero for as long as nobody asks.\n */\nfunction refuseUnmatchableKey(eventKey: string | undefined): void {\n const refusal = eventKey === undefined ? null : localFormKeyRefusal(eventKey);\n if (!refusal) return;\n writeJsonEnvelope({ ok: false, error: { code: \"VALIDATION_ERROR\", message: refusal.message, fix: refusal.fix } });\n process.exit(1);\n}\n\n/**\n * Every Form in this workspace, with its tree.\n *\n * A read of local files, so it costs nothing and works with no traffic — and\n * tolerant of a Form whose file is missing or does not parse, because one\n * broken Form must not take the whole conversion report down with it.\n *\n * Read here rather than through `readFlowTree`, which does not throw on a bad\n * file: it exits the process with a `NOT_FOUND` envelope. That is right when\n * the agent named the Form and wrong here, where the list is every directory\n * under `flows/` — a half-created folder from a flow-builder loop would abort\n * the whole call, and because this runs AFTER the write, a `--name` that had\n * already applied server-side would be reported back as a failure.\n */\nfunction readWorkspaceFlows(): { slug: string; tree: unknown }[] {\n return listFlowSlugs().flatMap((slug) => {\n try {\n return [{ slug, tree: JSON.parse(readFileSync(flowDataPath(slug), \"utf-8\")) as unknown }];\n } catch {\n return [];\n }\n });\n}\n\n/** One Form's endings as event keys, against what this company counts. */\nfunction readFlowConversions(slug: string, definitions: readonly AnalyticsConversionRow[]) {\n const tree = readFlowTree(slug);\n const declared = (tree as Record<string, unknown>).noConversion;\n const reason =\n declared && typeof declared === \"object\" && typeof (declared as Record<string, unknown>).reason === \"string\"\n ? String((declared as Record<string, unknown>).reason)\n : null;\n return {\n slug,\n noConversionReason: reason,\n endings: joinFlowConversions(flowEventKeys(tree, slug), definitions),\n };\n}\n\n/** What this call answers with, once the report has come back. */\nexport function conversionsEnvelope(\n args: ConversionsArgs,\n definitions: readonly AnalyticsConversionRow[],\n candidates: readonly AnalyticsEventCatalogRow[],\n /** What leads the answer: what Baker itself said about the read — another open chat's staged count, first of all — and a flag that changed nothing. */\n leadingHints: readonly string[] = [],\n) {\n const flow = args.flowSlug === undefined ? null : readFlowConversions(args.flowSlug, definitions);\n // Only when no Form was named: asked about one Form, answering about thirty\n // buries it. It is otherwise always on, because \"which Forms count nothing\"\n // is the question this report exists to answer and no other report can.\n const forms = flow === null ? readWorkspaceFlows() : [];\n const coverage = flowCoverage(forms, definitions);\n const page = pageCandidates(definitions, candidates, args.candidates);\n const hints = [\n ...leadingHints,\n ...(flow ? flowConversionHints(flow.slug, flow.endings, flow.noConversionReason) : []),\n ...flowCoverageHints(coverage),\n ...stagedHints(definitions),\n ...page.hints,\n ...namingHints(definitions),\n ];\n return {\n ok: true as const,\n data: {\n definitions,\n ...(flow ? { flow } : {}),\n ...(coverage.length > 0 ? { flows: coverage } : {}),\n ...(args.candidates ? { candidates: candidatesForFlow(page.candidates, args.flowSlug) } : {}),\n },\n ...(hints.length > 0 ? { hints } : {}),\n };\n}\n\n/**\n * The counts behind the stored definitions. A change is stored before this\n * read, so when reporting is unavailable the change is still reported as made,\n * with what could not be read; a plain read has nothing else to say and fails.\n */\nexport async function readConversionCounts(\n post: (path: string, body: unknown) => Promise<AnalyticsQueryResponse>,\n days: number | undefined,\n wrote: boolean,\n): Promise<{\n conversions: NonNullable<AnalyticsQueryResponse[\"data\"][\"conversions\"]>;\n eventCatalog: NonNullable<AnalyticsQueryResponse[\"data\"][\"eventCatalog\"]>;\n hints: string[];\n}> {\n try {\n const report = await post(\"/api/analytics/query\", { preset: \"conversions\", days });\n return { conversions: report.data.conversions ?? [], eventCatalog: report.data.eventCatalog ?? [], hints: [] };\n } catch (err) {\n if (!wrote) throw err;\n const reason = err instanceof Error ? err.message : String(err);\n return {\n conversions: [],\n eventCatalog: [],\n hints: [`The change above was saved; its counts could not be read, so none are shown. ${reason}`],\n };\n }\n}\n\nconst conversionsCommand = (() => {\n const args = {\n event: {\n type: \"string\",\n description:\n \"The event to start counting, from the `candidates` or `--flow` list: form:<form>:<step>:<trigger>, submit:<form>, page:<name>, or exit:<host>. Requires --name. REPEATABLE — pass it several times with one --name to count all of them as that single outcome, which is the whole point of the name. Inside a chat it is STAGED: the chat lists it under Sources, on its event, and it counts from when the chat is published — then it scores the WHOLE history, not just from then on, so counting the right event also corrects last month's reports. An event a client's system sends (the API, HubSpot) counts here too: it is credited to the LAST LANDING its person viewed, up to 90 days before, so a deal won weeks later is that landing's conversion. A person who never saw a landing is not counted at all. Such an event is usually also a funnel step (`baker funnel`) and a send (`baker analytics sends`)\",\n required: false,\n },\n name: {\n type: \"string\",\n description:\n \"What to call this outcome on every report — 'Booked a call', 'Quote requested'. Baker groups by EXACT name: reuse one this company already counts and the two become one row and one number, which is how a call booked on three Forms reads as one outcome. Read the existing names first (run with no flags) and reuse one before inventing another; a name that only differs by case, accent, punctuation or a plural is refused, because it would split the number in two\",\n required: false,\n },\n rename: {\n type: \"string\",\n description:\n \"An outcome's current name. With --to, renames every event counted under it. Renaming ONTO a name that already exists MERGES them into one row and one number — the way to fix a company whose Forms each invented their own word for the same outcome\",\n required: false,\n },\n to: {\n type: \"string\",\n description: \"The new name for --rename\",\n required: false,\n },\n \"remove-name\": {\n type: \"string\",\n description:\n \"Stop counting an outcome entirely, by name — removes every event counted under it. Use --remove for a single event key instead\",\n required: false,\n },\n \"count-mode\": {\n type: \"string\",\n description:\n \"once_per_visit (default) or every_time. Ask whether doing it twice in one visit is one outcome or two: a booking clicked twice is one booking, a guide downloaded twice is two downloads. Getting this wrong inflates the rate every budget is set by. Pass it with --event and no --name to change the mode of something already counted, keeping its name\",\n required: false,\n },\n remove: {\n type: \"string\",\n description:\n \"An event key to stop counting. REPEATABLE. Removes it from the history too, for the same reason adding it fills the history in\",\n required: false,\n },\n candidates: {\n type: \"boolean\",\n description:\n \"Also list every event these pages actually produced, ranked by volume, with whether it is already counted. This is where an event key comes from — do not invent one. Compact by default in JSON: each page or system event is one line (eventKey, events, sessions, countedAs when counted) and a Form's keys fold onto its `flows` row as `keys` and `events` (or a `formCandidates` row for a Form not in `flows`). Add --flow <slug> for that Form's keys only, and --full --output json for every field of every row\",\n required: false,\n },\n full: {\n type: \"boolean\",\n description:\n \"With --candidates: every field of every candidate (origin, nodeType, flowSlug, lastSeen, sources, doors) and every Form key unfolded. Large on a busy company — narrow it with --flow first\",\n required: false,\n },\n flow: {\n type: \"string\",\n description:\n \"A Form slug. Lists that Form's own endings already spelled as event keys, each with the name Baker counts it under or nothing — read off the Form's file, so it works on a Form built minutes ago that has never been visited, which is exactly when --candidates is empty. With --candidates, narrows the candidates to that Form's keys. Use it whenever you create or edit a Form: it is the only way to see that the Form counts nothing before it is published counting nothing\",\n required: false,\n },\n days: {\n type: \"string\",\n description: \"Lookback for the counts and for --candidates. Default 30\",\n required: false,\n },\n output: SHARED_ARGS.output,\n } as const;\n\n registerSchema({\n command: \"analytics.conversions\",\n description:\n \"Read or change what this company counts as a conversion on its pages. Also `--flow <slug>` for one Form's own endings as event keys, whether or not any of them has fired.\",\n args,\n });\n\n return defineCommand({\n meta: {\n name: \"conversions\",\n description:\n \"What this company counts as a conversion — the numerator of every conversion rate on Measure. A page event (a Form ending, a data-baker-* event) and an event one of the client's systems sends (a HubSpot event made with `baker analytics sources --create-hubspot`, an order the server posts) both count; a system's event is credited to the last landing its person viewed. A HubSpot event is `page:<its name>` here — `--event 'page:Deal won' --name 'Deal won'`. Run with no flags first: an empty `definitions` list means every conversion number in every other report is zero — not because nobody converted, but because nothing is marked as converting, and Baker never guesses. Add `--candidates` to see everything these pages actually do (every Form step, every data-baker-* event, every outbound destination) with counts and whether it is already counted; that list is where an event key comes from, so never invent one. It is compact: a Form's keys fold onto its `flows` row, `--candidates --flow <slug>` lists one Form's keys, and `--full --output json` returns every field of every row. Then `--event <key> --name 'Booked a call'` to count it. Inside a chat a change is STAGED: the person sees it under Sources in the chat's changes, on the event's own row and it applies when they publish, retroactively — the whole history is rescored, so a company that has been running for a month gets a month of correct numbers. A read shows a proposal marked `staged`. Call an event by its name when you talk to the person, never by its key. Two events given the SAME --name are one row and one number. `--remove <key>` stops counting one. `--flow <slug>` answers the other direction — what one Form counts, with every ending it has already spelled as an event key, read from the Form itself so it works before a single visitor has seen it. Run it whenever you create or edit a Form: a Form nobody named an outcome for is published converting nobody, and no report says so.\",\n },\n args,\n run: async ({ args: raw }) => {\n try {\n const format = outputFormatOrExit(raw.output);\n const args = parseConversionsArgs(raw as Record<string, unknown>, process.argv);\n\n for (const eventKey of args.eventKeys) refuseUnmatchableKey(eventKey);\n\n // The store is read first and read always. A write is built against\n // what is already counted — renaming, changing a count mode and\n // refusing a name that would split a number are all questions about the\n // current set — and the list this command answers with comes from here\n // rather than from the report, because the reporting mirror is posted\n // after the write returns.\n const before = await apiPost<AnalyticsConversionsResponse>(\"/api/analytics/conversions\", {});\n const write = conversionsWrite(args, before.data.definitions);\n // The last answer is the one that knows about this chat's own write.\n const answer = write\n ? await apiPost<AnalyticsConversionsResponse>(\"/api/analytics/conversions\", write)\n : before;\n const stored = answer.data.definitions;\n\n // Always read back through the report, never trust the write's echo:\n // the write says what was stored and the report says what it produced,\n // and \"counted, and it has never fired\" is the finding that matters.\n const report = await readConversionCounts(\n (path, body) => apiPost<AnalyticsQueryResponse>(path, body),\n args.days,\n write !== null,\n );\n\n const envelope = conversionsEnvelope(\n args,\n mergeStoredWithCounts(stored, report.conversions),\n report.eventCatalog,\n [...(answer.hints ?? []), ...report.hints, ...fullFlagHints(args, format)],\n );\n if (format === \"md\") process.stdout.write(renderConversionsMarkdown(envelope));\n else writeJsonEnvelope(withFormatHint(compactConversionsEnvelope(envelope, args), format));\n } catch (err) {\n if (err instanceof ConversionArgsError) {\n if (err.fix !== undefined) {\n writeJsonEnvelope({ ok: false, error: { code: \"VALIDATION_ERROR\", message: err.message, fix: err.fix } });\n process.exit(1);\n }\n handleError(new ApiError(\"VALIDATION_ERROR\", err.message));\n return;\n }\n handleError(err);\n }\n },\n });\n})();\n\n/**\n * Everything these pages and systems produced, offered to count. A system's\n * event counts like a page's — credited to the last landing its person\n * viewed — so it is listed with the rest.\n */\nfunction pageCandidates(\n definitions: Parameters<typeof conversionHints>[0],\n catalogue: Parameters<typeof conversionHints>[1],\n listing: boolean,\n): { candidates: Parameters<typeof conversionHints>[1]; hints: string[] } {\n return { candidates: [...catalogue], hints: conversionHints(definitions, catalogue, listing) };\n}\n\n/** `a=b,c=d` as an object, or undefined when the flag was not passed. */\nfunction parsePairs(raw: string | undefined): Record<string, string> | undefined {\n if (raw === undefined) return undefined;\n const out: Record<string, string> = {};\n for (const pair of raw.split(\",\")) {\n const [name, role] = pair.split(\"=\");\n if (name?.trim() && role?.trim()) out[name.trim()] = role.trim();\n }\n return out;\n}\n\nfunction parseList(raw: string | undefined): string[] | undefined {\n if (raw === undefined) return undefined;\n const out = raw\n .split(\",\")\n .map((entry) => entry.trim())\n .filter((entry) => entry !== \"\");\n return out.length > 0 ? out : undefined;\n}\n\n/**\n * What a client's own server actually posted, and what we made of it.\n *\n * ## Why this cannot be answered from a report\n *\n * A refused event is refused inside a `200` — `rejected: [{ code }]` — which\n * every HTTP client on earth throws away, and a rehearsal is validated,\n * answered identically and deliberately writes nothing. So \"we send events and\n * nothing appears\" has at least four different causes that all look the same\n * from the sender's side, and none of them is visible in any report: the events\n * that would prove it are the events that were never stored.\n *\n * The log is the only record of the request itself. It keeps 48 hours, which is\n * the right window for the only question it answers — somebody is wiring\n * something up *now* — and it holds every request, including the ones that\n * became nothing.\n *\n * ## Names, never values\n *\n * `identityKeys` says which `user_data` fields arrived; `--full` adds the\n * per-field previews (`l***@empresa.com`) and the raw body, both already masked\n * before they were persisted. Nothing here recovers a person, which is what\n * makes it safe in an agent's output.\n */\nconst arrivalsCommand = (() => {\n const args = {\n mode: {\n type: \"string\",\n description:\n \"live | test. Rehearsals (X-Baker-Mode: test) are accepted and write nothing, so they never reach a report — filter to them to confirm a rehearsal landed, or to live to see only what counted\",\n required: false,\n },\n outcome: {\n type: \"string\",\n description:\n \"accepted | rejected. Start with rejected: it is the half the sender's own logs threw away, and the reason to open this at all\",\n required: false,\n },\n event: {\n type: \"string\",\n description: \"Only this event_name, for a company posting several kinds\",\n required: false,\n },\n limit: {\n type: \"string\",\n description: `How many to return, newest first (default 25, max ${INGEST_ARRIVALS_MAX})`,\n required: false,\n },\n full: {\n type: \"boolean\",\n description:\n \"Also return the masked request body and the per-field identity previews. Reach for it only once the codes have not explained it — it is most of the bytes\",\n required: false,\n },\n } as const;\n\n registerSchema({\n command: \"analytics.arrivals\",\n description:\n \"What a client's own server posted to POST /v1/events in the last 48 hours, and why any of it was refused\",\n args,\n });\n\n return defineCommand({\n meta: {\n name: \"arrivals\",\n description: `Every request a client's own server posted to POST /v1/events in the last 48 hours — accepted, refused or rehearsed. The one place a server-side integration can be debugged.\n\nRead this FIRST whenever somebody says events are being sent and nothing shows up. Four different causes look identical from the sender's side and only this tells them apart:\n the request never reached us — nothing here at all: the key, the URL, or their own dispatch\n it was refused — rejectCode says which rule, rejectMessage says what the field had to be\n it was a rehearsal — mode: test is accepted, answered identically, and writes nothing on purpose\n it landed — then the question is a report's, not the endpoint's\n\nExamples:\n baker analytics arrivals — the last 25, newest first\n baker analytics arrivals --outcome rejected — only the refusals, which is usually the whole answer\n baker analytics arrivals --mode test — did the rehearsal they just ran actually arrive\n baker analytics arrivals --event deal_won — one kind only\n baker analytics arrivals --full --limit 5 — the masked body, when the codes have not explained it`,\n },\n args,\n run: async ({ args: raw }) => {\n try {\n const limit = raw.limit === undefined ? undefined : Number(raw.limit);\n const response = await apiPost<IngestArrivalsResponse>(\"/api/analytics/arrivals\", {\n ...(raw.mode ? { mode: String(raw.mode) } : {}),\n ...(raw.outcome ? { outcome: String(raw.outcome) } : {}),\n ...(raw.event ? { eventName: String(raw.event) } : {}),\n ...(limit !== undefined && Number.isFinite(limit) ? { limit } : {}),\n ...(raw.full ? { full: true } : {}),\n });\n writeJsonEnvelope(response);\n } catch (err) {\n handleError(err);\n }\n },\n });\n})();\n\n/**\n * The address the contract is published on — Baker's own host wherever one is\n * published, and the deployment's own only where none is.\n *\n * ## Two variables, one name, opposite meanings\n *\n * `BAKER_API_URL` means the deployment this CLI *talks to* — a\n * `*.convex.site`, handed to the Runtime by `runtimeSession/nodeActions.ts`.\n * On the backend the very same name means the branded host a client is *given*\n * (`convex/lib/env.ts`), which is why the Runtime receives that one separately\n * as `BAKER_PUBLIC_API_URL`.\n *\n * The collision already cost us once: this command passed the transport URL as\n * both arguments of {@link eventIngestUrl}, so the branded host could never\n * win and the brief always printed the deployment. A client's integration was\n * written against a `harmless-koala-793.eu-west-1.convex.site` that we then\n * cannot move without breaking them — in the one place a contract leaves Baker\n * for somebody else's codebase.\n */\nexport function sendingEventsAddress(env: { BAKER_PUBLIC_API_URL?: string; BAKER_API_URL: string }): string {\n return eventIngestUrl(env.BAKER_PUBLIC_API_URL, env.BAKER_API_URL);\n}\n\n/**\n * The whole `POST /v1/events` contract, as Markdown to hand to whoever is\n * writing the integration.\n *\n * ## Why the agent needs this and could not have it\n *\n * A client asks their agent to make their CRM report closed deals to Baker.\n * The agent's options were to invent the payload shape or to ask somebody to\n * paste a dialog out of the dashboard — and an invented shape produces a `200`\n * with `rejected[]` inside it, which is the failure mode this whole contract is\n * written to prevent. The reference existed; it was reachable only from a\n * browser.\n *\n * It is the *same* document the dashboard's \"copy for AI\" button produces —\n * `ingestBrief` in `@baker/api/event-ingest`, built from the same field tables\n * the screen draws — so the two cannot drift.\n *\n * ## No key, deliberately\n *\n * The brief carries `<your sending key>` and tells the reader to take the value\n * from an environment variable. A credential printed by a CLI lands in a\n * transcript, and a transcript is a third party's log; the key is minted in the\n * dashboard under Settings → API keys and travels to the client's own\n * environment, never through here.\n */\nconst sendingEventsCommand = (() => {\n registerSchema({\n command: \"analytics.sending-events\",\n description: \"The POST /v1/events contract as Markdown, for whoever is writing the integration\",\n args: {},\n });\n\n return defineCommand({\n meta: {\n name: \"sending-events\",\n description: `The complete contract for sending events into Baker from a client's own systems — a CRM marking a deal won, a back office recording a refund — as Markdown you can hand straight to whoever writes the integration.\n\nUse it whenever a client wants an outcome that happens AFTER the lead leaves the page to show up in Baker. Every bound, every rejection code and every rule is read off the code that enforces it, so it cannot go stale; the six mistakes that all produce a 200 are called out before the reader starts rather than explained afterwards.\n\nIt contains no key — the brief says to read one from an environment variable, and the key itself is minted in the dashboard under Settings → API keys. Never put one in a file or a message.\n\nExamples:\n baker analytics sending-events — the contract, as Markdown\n baker analytics arrivals --outcome rejected — after they have wired it up, what is being refused`,\n },\n args: {},\n run: () => {\n // Rendered here rather than fetched: it is a constant of the contract,\n // and a round trip would make the one command an agent reaches for while\n // OFFLINE-ish — mid-integration, already holding an error — depend on the\n // backend being reachable to explain itself.\n process.stdout.write(`${ingestBrief(sendingEventsAddress(getEnv()))}\\n`);\n },\n });\n})();\n\n/**\n * The website tag for a site Baker does not publish, with this company's own\n * key in it.\n *\n * ## Why the agent needs this and could not have it\n *\n * \"Put Baker on the client's website\" is a job the agent is asked to do and\n * could not finish: the tag needs the company's public site key, and the only\n * place that key existed was behind a dialog in a browser. So the agent's\n * options were to ask somebody to go and copy it, or to write a tag with a\n * placeholder in it — which installs cleanly, serves 200 and measures nothing.\n *\n * It matters most where the agent can act: a client who has connected Tag\n * Manager can have this staged as a Custom HTML tag on All Pages\n * (`baker tag-manager`), reviewed like every other staged change, with nobody\n * touching a file — staging `tagManagerTag` rather than `tag`. A container\n * rebuilds a Custom HTML tag's script element from its `src` alone and drops\n * the key, so `tag` installs perfectly there and measures nothing. Two fields\n * rather than one field and a paragraph: `tag` is what an agent reaches for,\n * so the safe form had to be somewhere it cannot be skim-read past.\n *\n * ## The key IS printed, unlike the sending key\n *\n * They are not the same kind of secret. A sending key is a credential that\n * writes into the account and belongs in an environment variable; the site key\n * is public by construction — it sits in the source of every page it runs on —\n * and what makes it safe is the origin list, which this prints beside it. A\n * brief with a placeholder where the key goes is a document nobody can act on.\n */\nconst websiteTagCommand = (() => {\n registerSchema({\n command: \"analytics.website-tag\",\n description: \"The measurement tag for a website Baker does not publish, with this company's key in it\",\n args: {\n measure: {\n type: \"string\",\n required: false,\n description: 'Whose activity to measure: \"campaigns\" (default), \"outcomes\" or \"all\"',\n },\n },\n });\n\n return defineCommand({\n meta: {\n name: \"website-tag\",\n description: `The one script tag that measures a website Baker does not publish — the client's own site, their shop, their booking page — with this company's site key already in it, plus the whole install as Markdown.\n\nReach for it whenever measurement has to reach a page Baker did not build. What it returns is real and complete: the tag, the websites the key is accepted from, and the rules for the hidden form fields, the identify call and the conversion call.\n\nThree things worth knowing before you install it:\n the origins list — the key is IGNORED from any origin not on it, silently. An empty list means the tag will measure nothing.\n Tag Manager is usually the way in — a Custom HTML tag on All Pages. If the client has a container connected, you can stage that yourself (baker tag-manager).\n in a container, use tagManagerTag — the response carries two tags. \\`tag\\` is for a page somebody can edit; \\`tagManagerTag\\` is the one to stage, because a container rebuilds a Custom HTML tag's script element from its src alone and drops the key, so \\`tag\\` measures nothing there, published and silent. Check it on the page afterwards: window.baker in the browser console.\n --measure decides the BILL — the tag goes on every page whatever you do; this decides whose activity is measured. Leave it alone unless the client asked for their whole website.\n\n --measure campaigns (default) people your Baker campaigns brought to the site. Their whole journey, through to the outcome. Nobody else is measured at all.\n --measure outcomes nobody's browsing. Only what the site reports with window.baker.track(...), and forms it wired to Baker.\n --measure all every visitor and every page view of the whole website. Their reports then describe their business, not their campaigns, and outcomes from organic, email and direct land in their conversion numbers. A decision, never a default.\n\nExamples:\n baker analytics website-tag — the tag, the origins and the install brief\n baker analytics website-tag --measure all — measure their whole website, because they asked for that\n baker analytics events --origin website — after it is live, what that website is reporting`,\n },\n args: {\n measure: {\n type: \"string\",\n description:\n 'Whose activity to measure: \"campaigns\" (default), \"outcomes\" or \"all\". Widening is a decision — see above.',\n },\n },\n run: async (ctx) => {\n try {\n const measure = typeof ctx.args.measure === \"string\" ? ctx.args.measure : undefined;\n const envelope = await apiPost<{ ok: boolean; data: WebsiteTagResponse }>(\n \"/api/analytics/website-tag\",\n measure ? { measure } : {},\n );\n writeJsonEnvelope({ ...envelope, hints: websiteTagHints(envelope.data) });\n } catch (err) {\n handleError(err);\n }\n },\n });\n})();\n\n/**\n * Declaring which websites may report as this company.\n *\n * Beside `website-tag` rather than under `settings`, because it is never\n * reached for any other reason: an agent has just been handed a tag, found its\n * scope empty or wrong, and needs the tag to work. Before this existed that was\n * where the job stopped — the only way to declare a site was a screen in the\n * dashboard, so the agent had to hand the work back to a person mid-install.\n */\nconst sitesCommand = (() => {\n registerSchema({\n command: \"analytics.sites\",\n description: \"The websites this company's measurement tag is accepted from, and adding or removing one\",\n args: {\n add: { type: \"string\", required: false, description: \"Website address to accept measurement from\" },\n remove: { type: \"string\", required: false, description: \"Website address to stop accepting measurement from\" },\n },\n });\n\n return defineCommand({\n meta: {\n name: \"sites\",\n description: `Which websites this company's measurement tag is accepted from — and the command that changes it.\n\nThe site key ships in the source of every page it is pasted on, so this list is the whole of what stops a stranger who read it appending forged conversions to these numbers. A tag on a website that is NOT on this list is ignored, silently, with no failing request anywhere. That is the single most common reason an install measures nothing.\n\nOne entry covers the estate under it: example.com answers for www.example.com, go.example.com and shop.eu.example.com. It never widens upward — declaring go.example.com does NOT cover example.com — so declare the DOMAIN, not the subdomain you happen to be installing on today.\n\nStart here:\n baker analytics sites — what is declared now, and what is worth adding\n baker analytics sites --add example.com — accept measurement from that site and everything under it\n baker analytics sites --remove old-site.com\n\nExamples:\n baker analytics website-tag — get the tag; if its origins are empty, come here first\n baker analytics sites --add foodforjoe.es — one entry covering www., go. and shop.`,\n },\n args: {\n add: {\n type: \"string\",\n description:\n \"Website address to accept measurement from, e.g. example.com — covers every subdomain of it. Repeat for several\",\n },\n remove: { type: \"string\", description: \"Website address to stop accepting measurement from\" },\n },\n run: async (ctx) => {\n try {\n // citty hands the LAST value of a repeated flag over as a plain string,\n // so `--add a.com --add b.com` would silently declare only b.com. The\n // raw argv is the only place both survive.\n const raw = ctx.rawArgs;\n const body = {\n add: repeatedValues(raw, \"add\", ctx.args.add),\n remove: repeatedValues(raw, \"remove\", ctx.args.remove),\n };\n const envelope = await apiPost<{ ok: boolean; data: AnalyticsSitesResponse }>(\"/api/analytics/sites\", body);\n writeJsonEnvelope({ ...envelope, hints: analyticsSitesHints(envelope.data) });\n } catch (err) {\n handleError(err);\n }\n },\n });\n})();\n\nexport const analyticsCommand = defineCommand({\n meta: {\n name: \"analytics\",\n description: `Baker's own web analytics for this company's published pages. No connection to set up — data starts the moment a page is published.\n\nStart here:\n baker analytics overview — traffic, pages, sources and Form drop-off in one call\n baker analytics landings — every page side by side, with a sparkline each\n baker analytics presets — what each report answers\n\nExamples:\n baker analytics overview --days 7\n baker analytics funnel --flow contact — which step loses people\n baker analytics flow --flow contact — that Form in depth: conversions, step-to-step paths, triggers\n baker analytics overview --landing ld_7f2a — the whole report for one page only\n baker analytics page --path /pricing — one page in detail\n baker analytics triggers --flow contact — what people did in the Form, wired up or not\n baker analytics paid --days 30 — campaigns by click id, and how many leads actually arrived\n baker analytics delivery — submissions whose destination refused them\n baker analytics submissions --flow contact — every attempt at that Form, delivered or not\n baker analytics releases — did the last publish change anything\n baker analytics traffic --days 90 --full — full breakdowns and the per-day trend\n baker analytics geo --grain postal_code --country ES — which postcodes convert\n baker analytics bots --days 30 — is the site still being crawled\n baker analytics companies --targets targets.csv — which target accounts visited, and which did not\n baker analytics landings --tag q3 — how the Q3 campaign's pages did\n baker analytics tags — the same numbers rolled up per campaign\n baker analytics events --event video --property milestone — how far people watch\n baker analytics events --event video --property milestone --where autoplay=0 — ...among videos they chose to play\n baker analytics events --steps hero_view,pricing_view,demo_click — an ordered page funnel\n baker analytics devices --grain viewport — which widths the page has to survive\n baker analytics ads — every campaign parameter on this company's URLs, and what we make of it\n baker analytics ads --role keyword — keywords by conversions AND by how many bounced\n baker analytics ads --platform google --role keyword — the same, in Google's vocabulary and from Google's traffic only\n baker analytics overview --role keyword --value \"solar panels\" — the whole report for those visits only\n baker analytics tracking — do the ad URLs carry the campaign, ad group and ad at all\n baker analytics tracking --platform meta — the same for one platform, read from their own ad account\n baker analytics map --platform google --set kw=keyword — stage a parameter name, seen or not yet\n baker analytics map --platform google --remove kw — take that answer back\n baker analytics unstage --kind conversion --ref 'page:Deal won' — take back a change this chat staged\n baker analytics delivery --page 2 --page-size 50 — the next page of a long list\n baker analytics overview --compare — this window against the one before it\n baker analytics website-tag — the tag for a website Baker does not publish, key included\n baker analytics sending-events — the contract for a client's server to post its own events\n baker analytics arrivals --outcome rejected — what their server posted that we refused, and why\n baker analytics events --origin server — the events their systems reported, not ours\n baker analytics lookup --person 'ana@empresa.com, deal:40001234567' — which of these people Baker has, and what it saw of each\n baker analytics feed --person 'ana@empresa.com' --days 90 — everything one person did, in order\nFull guide: __tooling__/docs/tools/baker/analytics.md`,\n },\n subCommands: {\n overview: overviewCommand,\n traffic: trafficCommand,\n funnel: funnelCommand,\n flow: flowCommand,\n page: pageCommand,\n triggers: triggersCommand,\n releases: releasesCommand,\n paid: paidCommand,\n delivery: deliveryCommand,\n submissions: submissionsCommand,\n geo: geoCommand,\n bots: botsCommand,\n companies: companiesCommand,\n landings: landingsCommand,\n tags: tagsCommand,\n events: eventsCommand,\n feed: feedCommand,\n lookup: () => import(\"./lookup.ts\").then((m) => m.lookupCommand),\n devices: devicesCommand,\n ads: adsCommand,\n conversions: conversionsCommand,\n sends: () => import(\"./sends.ts\").then((m) => m.sendsCommand),\n map: mapCommand,\n unstage: () => import(\"./unstage.ts\").then((m) => m.unstageCommand),\n tracking: trackingCommand,\n arrivals: arrivalsCommand,\n \"sending-events\": sendingEventsCommand,\n sources: () => import(\"./sources.ts\").then((m) => m.sourcesCommand),\n \"website-tag\": websiteTagCommand,\n sites: sitesCommand,\n plan: () => import(\"./plan.ts\").then((m) => m.planCommand),\n questionnaire: () => import(\"./questionnaire.ts\").then((m) => m.questionnaireCommand),\n presets: presetsCommand,\n },\n});\n","/**\n * What `baker analytics conversions --candidates` prints.\n *\n * The catalogue is every event the company's pages and systems produced, and\n * a busy company's is mostly Form keys: one per step and trigger, each spelled\n * with the step's UUID. Printed whole, with every row's doors and sources, it\n * came to 87 KB, and the agent grepped that for one Form three times in one\n * Session. So by default each page or system event is one short line, and a\n * Form's keys fold into one row per Form — onto the Form's own `flows` row when\n * it has one, so no Form is listed twice. `--flow <slug>` narrows the list to\n * that Form's keys, one short line each, and `--full` is every field of every\n * row.\n */\n\nimport type { AnalyticsEventCatalogRow } from \"@baker/api/analytics\";\nimport type { ConversionsEnvelope } from \"./conversionsMarkdown.ts\";\n\nexport interface CompactCandidate {\n eventKey: string;\n events: number;\n sessions: number;\n /** The name it is counted under. Absent when nothing counts it. */\n countedAs?: string;\n}\n\nexport interface FormCandidates {\n form: string;\n /** How many of the Form's event keys fired in the window. */\n keys: number;\n /** How many of them are already counted. */\n countedKeys: number;\n events: number;\n}\n\nexport interface CandidatesView {\n candidates: readonly (AnalyticsEventCatalogRow | CompactCandidate)[];\n formCandidates?: readonly FormCandidates[];\n}\n\nfunction compact(row: AnalyticsEventCatalogRow): CompactCandidate {\n return {\n eventKey: row.eventKey,\n events: row.events,\n sessions: row.sessions,\n ...(row.isCounted ? { countedAs: row.countedAs } : {}),\n };\n}\n\nfunction foldByForm(rows: readonly AnalyticsEventCatalogRow[]): FormCandidates[] {\n const byForm = new Map<string, FormCandidates>();\n for (const row of rows) {\n const form = byForm.get(row.flowSlug) ?? { form: row.flowSlug, keys: 0, countedKeys: 0, events: 0 };\n form.keys += 1;\n if (row.isCounted) form.countedKeys += 1;\n form.events += row.events;\n byForm.set(row.flowSlug, form);\n }\n return [...byForm.values()];\n}\n\n/** The catalogue, narrowed to one Form's keys when one is named. */\nexport function candidatesForFlow(\n catalogue: readonly AnalyticsEventCatalogRow[],\n flowSlug: string | undefined,\n): AnalyticsEventCatalogRow[] {\n return flowSlug === undefined ? [...catalogue] : catalogue.filter((row) => row.flowSlug === flowSlug);\n}\n\n/**\n * Said when Form keys were folded, so the agent asks for one Form rather than\n * piping `--full` into grep.\n */\nexport const FOLDED_FORMS_HINT =\n \"A Form's candidates are folded into its `flows` row (`keys` fired, `events`; no `counted` means it counts nothing), or a `formCandidates` row when not listed. `--candidates --flow <slug>` lists that Form's keys; `--full` adds every field of every key.\";\n\n/**\n * `rows` is already narrowed by `candidatesForFlow` when a Form was named;\n * `flowSlug` only decides that its keys are listed rather than folded.\n */\nexport function candidatesView(\n rows: readonly AnalyticsEventCatalogRow[],\n options: { flowSlug?: string; full: boolean },\n): CandidatesView {\n if (options.full) return { candidates: rows };\n if (options.flowSlug !== undefined) return { candidates: rows.map(compact) };\n const forms = foldByForm(rows.filter((row) => row.flowSlug !== \"\"));\n return {\n candidates: rows.filter((row) => row.flowSlug === \"\").map(compact),\n ...(forms.length > 0 ? { formCandidates: forms } : {}),\n };\n}\n\n/** Said when `--full` changed nothing, so the agent does not read the answer as complete. */\nexport function fullFlagHints(args: { candidates: boolean; full: boolean }, format: string): string[] {\n if (!args.full || (args.candidates && format !== \"md\")) return [];\n return [\"`--full` only applies to `--candidates --output json`, so it changed nothing here.\"];\n}\n\n/**\n * The JSON answer to `--candidates`: the compact view, with each Form's\n * traffic merged onto its `flows` row, which drops `counted` and\n * `noConversion` when they hold nothing — the coverage hint already names\n * every Form that counts nothing. The markdown renderer folds the full rows\n * itself.\n */\nexport function compactConversionsEnvelope(\n envelope: ConversionsEnvelope,\n options: { flowSlug?: string; full: boolean },\n) {\n const { candidates, flows, ...data } = envelope.data;\n if (candidates === undefined) return { ...envelope };\n const { formCandidates, ...view } = candidatesView(candidates, options);\n const traffic = new Map(formCandidates?.map((form) => [form.form, form]));\n const merged = flows?.map(({ slug, counted, noConversion }) => {\n const form = traffic.get(slug);\n traffic.delete(slug);\n return {\n slug,\n ...(counted.length > 0 ? { counted } : {}),\n ...(noConversion !== null ? { noConversion } : {}),\n keys: form?.keys ?? 0,\n events: form?.events ?? 0,\n };\n });\n const hints = [...(envelope.hints ?? []), ...(formCandidates ? [FOLDED_FORMS_HINT] : [])];\n return {\n ...envelope,\n data: {\n ...data,\n ...(merged ? { flows: merged } : {}),\n ...view,\n ...(traffic.size > 0 ? { formCandidates: [...traffic.values()] } : {}),\n },\n ...(hints.length > 0 ? { hints } : {}),\n };\n}\n","/**\n * What to tell the agent after it has read what this company counts.\n *\n * The lightest steering layer there is — a nudge returned after the call — and\n * the right one here, because every case below is a state that looks fine and\n * reads wrong. None of them is an error: the command succeeded, the numbers are\n * correct, and the conclusion an agent would draw from them without these lines\n * is false.\n *\n * Pure so it can be tested without a request.\n */\n\nimport {\n type AnalyticsConversionRow,\n type AnalyticsEventCatalogRow,\n STANDARD_CONVERSION_NAMES,\n} from \"@baker/api/analytics\";\n\n/**\n * How many candidates to name in a hint before it stops being a hint.\n *\n * Three is enough to show the shape of the list and to be actionable; a hint\n * that lists forty event keys is a report, and the agent already has the real\n * one behind `--candidates`.\n */\nconst NAMED_CANDIDATES = 3;\n\nexport function conversionHints(\n definitions: readonly AnalyticsConversionRow[],\n candidates: readonly AnalyticsEventCatalogRow[],\n askedForCandidates: boolean,\n): string[] {\n const hints: string[] = [];\n\n if (definitions.length === 0) {\n // The most consequential hint in the file. An agent that reads `overview`\n // and sees zero conversions concludes the campaigns are failing; the truth\n // is that nothing has been named as an outcome yet, and no other report\n // says so.\n hints.push(\n \"Nothing is counted as a conversion, so EVERY conversion number in every other report is zero — whatever visitors actually did. Baker never guesses which event is the point of a page. Run `baker analytics conversions --candidates` to see what these pages produce, then count the ones that are worth money.\",\n );\n if (!askedForCandidates && candidates.length > 0) {\n const top = candidates\n .slice(0, NAMED_CANDIDATES)\n .map((row) => row.eventKey)\n .join(\", \");\n hints.push(`${candidates.length} distinct events are available to count. Busiest: ${top}.`);\n }\n return hints;\n }\n\n // Counted and never fired. Usually a branch the page never reaches, and it is\n // invisible in every report built from events that happened — which is all of\n // them except this one.\n const silent = definitions.filter((row) => row.lastSeen === null);\n if (silent.length > 0) {\n hints.push(\n `${silent.length} counted outcome(s) have never fired in this window: ${silent\n .slice(0, NAMED_CANDIDATES)\n .map((row) => row.name)\n .join(\n \", \",\n )}. Either the page never reaches them, or the wrong event was named — check against --candidates before reporting a conversion rate.`,\n );\n }\n\n // A busy event nobody counts, on a company that counts something else. Not a\n // mistake by itself, which is why it is a hint and not a warning: a hero\n // section coming into view is genuinely not an outcome. It is worth one line\n // because the opposite case — a booking widget nobody counted — looks\n // identical from every other report.\n const uncounted = candidates.filter((row) => !row.isCounted);\n const busiest = uncounted[0];\n if (busiest && busiest.sessions > 0) {\n const counted = candidates.filter((row) => row.isCounted);\n const busiestCounted = counted.reduce((max, row) => Math.max(max, row.sessions), 0);\n if (busiest.sessions > busiestCounted) {\n hints.push(\n `\\`${busiest.eventKey}\\` happened in ${busiest.sessions} visits and is not counted — more than anything that is. Check it is not the real outcome.`,\n );\n }\n }\n\n return hints;\n}\n\n/**\n * What this company calls its outcomes, said before anybody names another one.\n *\n * Baker groups by **exact name**, so the vocabulary is the grouping: three\n * Forms that all book a call are one number only if all three say \"Booked a\n * call\". Nothing about a report reveals that a name was nearly reused — two\n * rows saying almost the same thing look like two outcomes, and both halves of\n * the split number are plausible.\n *\n * So the names in use lead every answer this command gives. A company with none\n * yet gets the standard set instead, which is the only moment it is useful: once\n * one outcome is named, that name — and its language — is what the next one\n * should match.\n */\nexport function namingHints(definitions: readonly { name: string; eventKeys?: readonly string[] }[]): string[] {\n const names = [...new Set(definitions.map((row) => row.name))];\n if (names.length === 0) {\n return [\n `Nothing is named yet, so the first name sets the vocabulary for this company. Start from the standard set — ${STANDARD_CONVERSION_NAMES.map((entry) => entry.name).join(\", \")} — and stay in the company's own language once one exists.`,\n ];\n }\n return [\n `Names in use here: ${names.join(\", \")}. Reuse one EXACTLY when another Form produces the same outcome — that is what makes them one number — and use \\`--rename \"<old>\" --to \"<existing>\"\\` to merge two that should always have been one.`,\n ];\n}\n\n/**\n * What a proposal made inside a chat is, said in the reply that made it.\n *\n * Counting from a chat is staged: the chat lists it under Sources, on its event, and\n * Publish applies it. Without this line the agent read its own write back,\n * told the person the events \"are conversions now\", and — asked why they\n * read `page:…` — invented a reason. The key is Baker's spelling, never the\n * person's.\n */\nexport function stagedHints(\n definitions: readonly { name: string; eventKeys: readonly string[]; staged?: boolean }[],\n): string[] {\n const staged = definitions.filter((row) => row.staged === true);\n if (staged.length === 0) return [];\n return [\n `Staged on this chat, not counted yet: ${staged.map((row) => `\"${row.name}\"`).join(\", \")}. The person sees ${staged.length === 1 ? \"it\" : \"them\"} under Sources in the chat's changes, on the event's own row, and ${staged.length === 1 ? \"it counts\" : \"they count\"} from when they publish the chat; discarding the chat drops ${staged.length === 1 ? \"it\" : \"them\"}. Publishing also rescores events Baker ALREADY RECORDED under the key — never anything that happened in HubSpot or elsewhere before the event existed, so an event this chat just made starts at zero. Tell them that, not that it is already counted.`,\n 'When you talk to the person, call each event by its name (\"Appointment scheduled\"), never its key (`page:Appointment scheduled`): the key is how Baker spells it internally, and the `page:` part means nothing to them.',\n ];\n}\n","/**\n * What this company counts, listed from the store and counted from the report.\n *\n * The two halves come from different places on purpose. A definition lives in\n * Convex and is true the instant it is written; the numbers beside it live in\n * Tinybird, joined at read time against a dimension the write **posts\n * afterwards**. Reading the list from the report therefore leaves a just-named\n * outcome missing from the very reply that named it — the agent counts\n * something, reads back \"not counted\", and either gives up or writes it twice.\n * The dashboard hit the same wall and answered it the same way: list from\n * Convex, counts from Tinybird.\n *\n * Grouped by name rather than by key, because the name is the unit every report\n * uses: two definitions sharing one are one row and one number.\n */\n\nimport type { AnalyticsConversionRow } from \"@baker/api/analytics\";\n\nexport interface StoredConversion {\n eventKey: string;\n name: string;\n countMode: string;\n /** Proposed in this chat and not counted until it is published. */\n staged?: boolean;\n}\n\nexport function mergeStoredWithCounts(\n stored: readonly StoredConversion[],\n reported: readonly AnalyticsConversionRow[],\n): AnalyticsConversionRow[] {\n const counts = new Map(reported.map((row) => [row.name, row]));\n const groups = new Map<string, StoredConversion[]>();\n for (const row of stored) {\n const group = groups.get(row.name) ?? [];\n group.push(row);\n groups.set(row.name, group);\n }\n\n return [...groups.entries()]\n .map(([name, rows]) => {\n const modes = new Set(rows.map((row) => row.countMode));\n const counted = counts.get(name);\n return {\n name,\n eventKeys: rows.map((row) => row.eventKey).sort(),\n // A real state and usually an editing accident, which is why it is said\n // out loud rather than resolved to one of them.\n countMode: modes.size === 1 ? ([...modes][0] as string) : \"mixed\",\n // Zero rather than absent: an outcome that is counted and has never\n // fired is the most actionable row on this report, and dropping it\n // would make it indistinguishable from one nobody has named.\n conversions: counted?.conversions ?? 0,\n convertingVisits: counted?.convertingVisits ?? 0,\n unplaced: counted?.unplaced ?? 0,\n lastSeen: counted?.lastSeen ?? null,\n doors: counted?.doors ?? [],\n ...(rows.some((row) => row.staged === true) ? { staged: true } : {}),\n };\n })\n .sort((a, b) => a.name.localeCompare(b.name));\n}\n","/**\n * What the agent reads, as opposed to what the wire carries.\n *\n * The dashboard and the sandbox agent read the same `/api/analytics/query`\n * response and want different things out of it. A browser wants every field of\n * every row plus a sparkline it can draw; an agent pays for all of that in\n * context and cannot draw anything.\n *\n * Measured against the wire schema on a 40-landing site: `landings` came back\n * as ~11.5k tokens of pretty-printed JSON, a third of it indentation and half\n * of it sparkline. `landings --days 90 --granularity hour` came back as ~353k\n * tokens — the sparkline is bucketed by `--granularity`, so ninety days of\n * hours is 2,160 numbers per page — which is larger than the window it has to\n * fit inside. Nothing capped it and nothing warned about it.\n *\n * So the shaping happens here rather than in the endpoint: the wire keeps its\n * contract, the dashboard keeps its fields, and the agent gets the same answer\n * at roughly a tenth of the cost.\n */\n\nimport type { AnalyticsQueryData } from \"@baker/api/analytics\";\n\n/**\n * How many points of a sparkline survive `--full`.\n *\n * A sparkline exists to carry a shape, and a shape survives resampling — thirty\n * points still say \"it collapsed in the third week\". The per-bucket counts are\n * not lost: `traffic --landing <id> --granularity hour` is the report that\n * answers at that resolution, and it answers for one page rather than for all\n * of them at once.\n */\nconst SPARKLINE_POINTS = 30;\n\n/** Fields no agent can act on, dropped before anything else is measured. */\nconst DASHBOARD_ONLY_FIELDS = new Set([\n // The Convex row id. It exists so a dashboard can link to the row; an agent\n // addresses a landing by `landingId`, which is the id in its `_definition.md`.\n \"landingRef\",\n]);\n\n/**\n * Blocks that are lists of things rather than breakdowns.\n *\n * Only these are paged, and only these are worth saying \"there are more\" about.\n */\nexport const ANALYTICS_LIST_BLOCKS = [\"deliveries\", \"submissions\", \"visitors\", \"timeline\", \"stream\"] as const;\n\n/**\n * A value carrying nothing a missing key would not carry.\n *\n * `null` is deliberately not in here. Every rate in this response is `null`\n * rather than `0` when it has no denominator, and the docs tell the agent to\n * read the two apart — so `null` is an answer and `\"\"` is an absence.\n * `0` and `false` are answers too.\n */\nfunction isAbsent(value: unknown): boolean {\n if (value === \"\") return true;\n if (Array.isArray(value)) return value.length === 0;\n if (typeof value === \"object\" && value !== null) return Object.keys(value).length === 0;\n return false;\n}\n\n/**\n * A series resampled to at most `points`, by summing into equal buckets.\n *\n * Summing rather than sampling every nth point: these are counts, and dropping\n * four hours in five understates a day by four fifths while still drawing a\n * plausible line. A resampled series totals what the original totalled.\n */\nexport function resample(series: readonly number[], points: number): number[] {\n if (series.length <= points || points < 1) return [...series];\n const out: number[] = [];\n for (let i = 0; i < points; i += 1) {\n const from = Math.floor((i * series.length) / points);\n const to = Math.floor(((i + 1) * series.length) / points);\n let sum = 0;\n for (let j = from; j < to; j += 1) sum += series[j] ?? 0;\n out.push(sum);\n }\n return out;\n}\n\nfunction pruneValue(value: unknown, full: boolean): unknown {\n if (Array.isArray(value)) return value.map((entry) => pruneValue(entry, full));\n if (typeof value === \"object\" && value !== null) {\n return pruneRow(value as Record<string, unknown>, full);\n }\n return value;\n}\n\n/** One row with its absent fields and its dashboard-only fields removed. */\nfunction pruneRow(row: Record<string, unknown>, full: boolean): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(row)) {\n if (DASHBOARD_ONLY_FIELDS.has(key)) continue;\n if (key === \"sparkline\" && Array.isArray(value)) {\n // Dropped outright unless asked for: it is the one field here that is a\n // picture rather than a fact, and it is also the largest.\n if (!full) continue;\n out[key] = resample(value as number[], SPARKLINE_POINTS);\n continue;\n }\n if (isAbsent(value)) continue;\n out[key] = pruneValue(value, full);\n }\n return out;\n}\n\n/**\n * The response an agent should be handed.\n *\n * Per-row rather than per-column pruning, because the saving is in the rows\n * that differ: a replay row carries thirty-five fields of which a page view\n * fills eight, and dropping a column only when it is empty in *every* row would\n * keep all thirty-five on every one of them.\n */\nexport function pruneForAgent(data: AnalyticsQueryData, full: boolean): Record<string, unknown> {\n return pruneRow(data as unknown as Record<string, unknown>, full);\n}\n\n// ---------------------------------------------------------------------------\n// Rendering\n// ---------------------------------------------------------------------------\n\n/** A row that can be a table line: every value is a scalar or a flat list. */\nfunction isTabular(rows: readonly unknown[]): rows is ReadonlyArray<Record<string, unknown>> {\n return rows.every(\n (row) =>\n typeof row === \"object\" &&\n row !== null &&\n !Array.isArray(row) &&\n Object.values(row).every(\n (value) =>\n value === null ||\n typeof value !== \"object\" ||\n (Array.isArray(value) && value.every((e) => typeof e !== \"object\")),\n ),\n );\n}\n\nfunction cell(value: unknown, key?: string): string {\n if (value === null || value === undefined) return \"\";\n if (Array.isArray(value)) return value.join(\" \");\n if (typeof value === \"number\" && !Number.isInteger(value)) {\n // A duration in milliseconds to four decimal places is four digits of\n // noise: nothing downstream is sensitive to a microsecond of dwell time,\n // and every one of them is a character the reader pays for.\n if (key?.endsWith(\"Ms\")) return String(Math.round(value));\n return value.toFixed(4).replace(/0+$/, \"\").replace(/\\.$/, \"\");\n }\n return String(value);\n}\n\n/**\n * Columns that say *which row this is*, before the ones that say how it did.\n *\n * Needed because the keys do not arrive in the order the wire declares them —\n * they come back alphabetised, which buries `landingId` and `name` in the\n * middle of a table and leads with `avgEngagementMs`. A table whose first\n * column is not the subject has to be read twice.\n */\nconst IDENTITY_COLUMNS = [\n \"landingId\",\n \"name\",\n \"slug\",\n \"path\",\n \"flowSlug\",\n \"stepId\",\n \"value\",\n \"key\",\n \"bucket\",\n \"tag\",\n \"eventName\",\n \"release\",\n \"place\",\n \"bot\",\n \"source\",\n \"medium\",\n \"campaign\",\n \"platform\",\n \"sessionId\",\n \"eventId\",\n];\n\nfunction byIdentityFirst(a: string, b: string): number {\n const ia = IDENTITY_COLUMNS.indexOf(a);\n const ib = IDENTITY_COLUMNS.indexOf(b);\n if (ia !== -1 || ib !== -1)\n return (ia === -1 ? Number.MAX_SAFE_INTEGER : ia) - (ib === -1 ? Number.MAX_SAFE_INTEGER : ib);\n return 0;\n}\n\n/**\n * Blocks in the order a report is read: the window it covers, the totals, the\n * rows, and the echo of which preset produced it last of all.\n */\nconst BLOCK_ORDER = [\"window\", \"totals\", \"previous\", \"pageInfo\"];\n\nfunction byBlockOrder(a: string, b: string): number {\n const rank = (key: string) => {\n const at = BLOCK_ORDER.indexOf(key);\n if (at !== -1) return at;\n return key === \"preset\" ? BLOCK_ORDER.length + 1 : BLOCK_ORDER.length;\n };\n return rank(a) - rank(b);\n}\n\n/**\n * Columns, in the order the wire declares them, minus the ones no row filled.\n *\n * Per-column here where the JSON path prunes per-row: a table with a ragged\n * row is not a table, and a column of empty cells costs the header once rather\n * than once per row.\n */\nfunction columnsOf(rows: ReadonlyArray<Record<string, unknown>>): string[] {\n const seen: string[] = [];\n for (const row of rows) {\n for (const key of Object.keys(row)) {\n if (!seen.includes(key)) seen.push(key);\n }\n }\n return seen\n .filter((key) => rows.some((row) => !isAbsent(row[key]) && row[key] !== null && row[key] !== undefined))\n .sort(byIdentityFirst);\n}\n\nfunction table(rows: ReadonlyArray<Record<string, unknown>>): string {\n const cols = columnsOf(rows);\n if (cols.length === 0) return \"\";\n const head = `| ${cols.join(\" | \")} |`;\n const rule = `| ${cols.map(() => \"---\").join(\" | \")} |`;\n const body = rows.map((row) => `| ${cols.map((col) => cell(row[col], col)).join(\" | \")} |`);\n return [head, rule, ...body].join(\"\\n\");\n}\n\n/** A value that is a list of rows, i.e. a sub-table rather than a cell. */\nfunction isRowList(value: unknown): value is Array<Record<string, unknown>> {\n return (\n Array.isArray(value) &&\n value.length > 0 &&\n value.every((entry) => typeof entry === \"object\" && entry !== null && !Array.isArray(entry))\n );\n}\n\n/** Whatever names this row in a heading — its id, its slug, or its position. */\nfunction labelOf(row: Record<string, unknown>, index: number): string {\n for (const key of IDENTITY_COLUMNS) {\n const value = row[key];\n if (typeof value === \"string\" && value !== \"\") return value;\n }\n return `#${index + 1}`;\n}\n\n/**\n * A block whose rows carry nested rows of their own — a funnel and its steps.\n *\n * Rendered as the parent table followed by one sub-table per parent rather than\n * dumped as JSON. The JSON fallback was correct and nearly useless: `funnel`,\n * `flow` and `overview` all nest, they are the three commands most likely to be\n * the *first* call of a turn, and for them `--output md` was saving about 2%.\n */\nfunction nestedBlock(name: string, rows: ReadonlyArray<Record<string, unknown>>): string {\n const parents = rows.map((row) =>\n Object.fromEntries(Object.entries(row).filter(([, value]) => !isRowList(value) && !isPlainRow(value))),\n );\n const parts = [`### ${name}`, table(parents)];\n rows.forEach((row, index) => {\n for (const [key, value] of Object.entries(row)) {\n if (isRowList(value)) parts.push(`#### ${name}[${labelOf(row, index)}].${key}`, table(value));\n else if (isPlainRow(value)) parts.push(`#### ${name}[${labelOf(row, index)}].${key}`, table([value]));\n }\n });\n return parts.filter((part) => part !== \"\").join(\"\\n\");\n}\n\n/** An object that is one row rather than a list — `worstStep`. */\nfunction isPlainRow(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction block(name: string, value: unknown): string {\n if (Array.isArray(value)) {\n if (value.length === 0) return \"\";\n if (isTabular(value)) return `### ${name}\\n${table(value)}`;\n if (isRowList(value)) return nestedBlock(name, value);\n return `### ${name}\\n${JSON.stringify(value)}`;\n }\n if (typeof value === \"object\" && value !== null) {\n const entries = Object.entries(value as Record<string, unknown>);\n if (entries.length === 0) return \"\";\n if (entries.every(([, v]) => v === null || typeof v !== \"object\")) {\n return `### ${name}\\n${entries.map(([k, v]) => `- ${k}: ${v === null ? \"null\" : cell(v, k)}`).join(\"\\n\")}`;\n }\n return `### ${name}\\n${JSON.stringify(value)}`;\n }\n return `### ${name}\\n${cell(value)}`;\n}\n\n/**\n * The whole response as Markdown.\n *\n * A fifth to a half smaller than the compact JSON, because a\n * table names each column once instead of once per row. `warnings` and `hints`\n * lead rather than trail: they are instructions to the reader, and a reader\n * that has already summarised the table below them has acted without them.\n */\nexport function renderMarkdown(envelope: {\n data: Record<string, unknown>;\n warnings?: ReadonlyArray<{ code: string; message: string }>;\n hints?: readonly string[];\n}): string {\n const parts: string[] = [];\n for (const warning of envelope.warnings ?? []) parts.push(`> **${warning.code}** — ${warning.message}`);\n for (const hint of envelope.hints ?? []) parts.push(`> ${hint}`);\n for (const [name, value] of Object.entries(envelope.data).sort(([a], [b]) => byBlockOrder(a, b))) {\n const rendered = block(name, value);\n if (rendered !== \"\") parts.push(rendered);\n }\n return `${parts.join(\"\\n\\n\")}\\n`;\n}\n\n/**\n * A breakdown that was asked for and did not come back.\n *\n * Only answerable here, where the request is still in scope: the response to\n * `--event nope --property x` is the plain event list plus an empty\n * `eventBreakdown`, which is indistinguishable from the plain event list once\n * the empty array is pruned — and reads as a report rather than as \"there is no\n * such event\".\n */\nexport function missingBreakdownHint(\n data: Record<string, unknown>,\n asked: { eventName?: string; property?: string },\n): string[] {\n if (asked.eventName === undefined) return [];\n const breakdown = data.eventBreakdown;\n if (Array.isArray(breakdown) && breakdown.length > 0) return [];\n const known = Array.isArray(data.customEvents)\n ? data.customEvents.flatMap((row) => (isPlainRow(row) && typeof row.eventName === \"string\" ? [row.eventName] : []))\n : [];\n return [\n `No rows came back for --event ${asked.eventName}${asked.property ? ` --property ${asked.property}` : \"\"}. That event or property does not exist on these pages${known.length > 0 ? `; the ones that do are: ${known.slice(0, 12).join(\", \")}${known.length > 12 ? \", …\" : \"\"}` : \"\"}.`,\n ];\n}\n\n/** The formats this family renders. */\nexport const ANALYTICS_OUTPUT_FORMATS = [\"json\", \"md\"] as const;\n\n/**\n * Whether a `--output` value is one we render.\n *\n * Checked rather than defaulted, because the near-misses are the plausible\n * guesses — `markdown`, `table`, `csv` — and every one of them used to fall\n * through to JSON silently. An agent that asked for the cheap format, was\n * handed the expensive one, and was told nothing has no way to notice.\n */\nexport function isAnalyticsOutputFormat(value: string): boolean {\n return (ANALYTICS_OUTPUT_FORMATS as readonly string[]).includes(value);\n}\n\n/**\n * Write the report.\n *\n * JSON stays the default and stays the envelope every other family returns, but\n * it is emitted compact. It used to go through the shared pretty-printer, which\n * indents two spaces per level — measured at a third to a half of the bytes of\n * every analytics response, spent on whitespace the reader gains nothing from.\n */\nexport function writeAnalyticsReport(\n envelope: {\n ok: true;\n data: Record<string, unknown>;\n warnings?: ReadonlyArray<{ code: string; message: string }>;\n hints?: readonly string[];\n },\n format: string,\n): void {\n if (format === \"md\") {\n process.stdout.write(renderMarkdown(envelope));\n return;\n }\n process.stdout.write(`${JSON.stringify(envelope)}\\n`);\n}\n\n/**\n * Above this many rows in one response, the format is worth a sentence.\n *\n * A flag nobody knows about is a flag nobody passes. The saving is real —\n * measured at 70% on a hundred-row keyword breakdown — but it only ever\n * happens on the second call, so the first one has to say so.\n */\nconst ROWS_BEFORE_FORMAT_HINT = 25;\n\n/** How many rows the response carries, across every block that is a list. */\nexport function rowCount(data: Record<string, unknown>): number {\n let total = 0;\n for (const value of Object.values(data)) {\n if (Array.isArray(value)) total += value.length;\n }\n return total;\n}\n\n/** Told once, when it would have mattered, and never on a report already in Markdown. */\nexport function formatHint(data: Record<string, unknown>, format: string): string[] {\n if (format === \"md\") return [];\n const rows = rowCount(data);\n if (rows < ROWS_BEFORE_FORMAT_HINT) return [];\n return [\n `This report is ${rows} rows. If you are going to read the numbers rather than pick one field out of them, --output md returns the same answer as tables, typically a third to a half smaller.`,\n ];\n}\n","/**\n * `baker analytics conversions --output md`.\n *\n * The same answer as the JSON, laid out for the family's table renderer. Three\n * of its blocks carry rows inside rows — each candidate's doors, each Form's\n * counted steps, one Form's endings — and the generic renderer gives every\n * nested list its own sub-table, so a company with two hundred candidates got\n * two hundred three-line tables, labelled by a Form slug they share. Each\n * nested list is folded into one cell here instead, and one Form's endings\n * become a table of their own.\n *\n * The candidates inside a Form are one row per Form, not one per candidate:\n * a 33-Form company produced 235 of them, and their UUID event keys alone\n * were tens of kilobytes that answer nothing a reader asks of the list. One\n * Form's keys are `--flow <slug>`, and every key is the JSON.\n */\n\nimport type { AnalyticsConversionRow, AnalyticsEventCatalogRow } from \"@baker/api/analytics\";\nimport type { FlowConversionRow, FlowCoverageRow } from \"./flowConversionKeys.ts\";\nimport { formatHint, renderMarkdown } from \"./render.ts\";\n\nexport interface ConversionsEnvelope {\n ok: true;\n data: {\n definitions: readonly AnalyticsConversionRow[];\n flow?: { slug: string; noConversionReason: string | null; endings: readonly FlowConversionRow[] };\n flows?: readonly FlowCoverageRow[];\n candidates?: readonly AnalyticsEventCatalogRow[];\n };\n hints?: readonly string[];\n}\n\n/** `pages=28/23 server=10/5` — each door's events and sessions, in one cell. */\nfunction doorsCell(doors: AnalyticsEventCatalogRow[\"doors\"]): string {\n return doors.map((door) => `${door.door}=${door.events}/${door.sessions}`).join(\" \");\n}\n\n/**\n * `page:Call booked + 20 Form ending(s)` — the keys a reader can type, and a\n * count of the Form keys, whose UUIDs `--flow <slug>` spells out.\n */\nfunction eventKeysCell(eventKeys: readonly string[]): string {\n const formKeys = eventKeys.filter((key) => key.startsWith(\"form:\")).length;\n const others = eventKeys.filter((key) => !key.startsWith(\"form:\"));\n return [...others, ...(formKeys > 0 ? [`${formKeys} Form ending(s)`] : [])].join(\" + \");\n}\n\ninterface FormTraffic {\n \"endings seen\": number;\n \"endings counted\": number;\n events: number;\n lastSeen: string;\n}\n\n/** Per Form: how many of its endings fired, how many of those count, and how much traffic. */\nfunction trafficByForm(candidates: readonly AnalyticsEventCatalogRow[]): Map<string, FormTraffic> {\n const byForm = new Map<string, FormTraffic>();\n for (const row of candidates) {\n const form = byForm.get(row.flowSlug) ?? { \"endings seen\": 0, \"endings counted\": 0, events: 0, lastSeen: \"\" };\n if (row.events > 0) form[\"endings seen\"] += 1;\n if (row.isCounted) form[\"endings counted\"] += 1;\n form.events += row.events;\n if (row.lastSeen && row.lastSeen.slice(0, 10) > form.lastSeen) form.lastSeen = row.lastSeen.slice(0, 10);\n byForm.set(row.flowSlug, form);\n }\n return byForm;\n}\n\nconst NO_TRAFFIC: FormTraffic = { \"endings seen\": 0, \"endings counted\": 0, events: 0, lastSeen: \"\" };\n\nconst FORM_KEYS_HINT =\n \"A Form's candidates are one row per Form: `baker analytics conversions --flow <slug>` lists that Form's event keys, and `--candidates --full --output json` lists every key with every field.\";\n\nexport function renderConversionsMarkdown(envelope: ConversionsEnvelope): string {\n const { definitions, flow, flows, candidates } = envelope.data;\n const data: Record<string, unknown> = {\n definitions: definitions.map(({ name, eventKeys, ...row }) => ({\n name,\n eventKeys: eventKeysCell(eventKeys),\n ...row,\n })),\n };\n if (flow) {\n data.flow = { slug: flow.slug, noConversionReason: flow.noConversionReason };\n data.flowEndings = flow.endings;\n }\n const traffic = candidates ? trafficByForm(candidates.filter((row) => row.flowSlug !== \"\")) : new Map();\n if (flows) {\n data.flows = flows.map((row) => ({\n slug: row.slug,\n counted: row.counted.map((entry) => `${entry.name} @ ${entry.step}`).join(\"; \"),\n noConversion: row.noConversion,\n ...(candidates ? (traffic.get(row.slug) ?? NO_TRAFFIC) : {}),\n }));\n }\n const hints = [...(envelope.hints ?? [])];\n if (candidates) {\n // A Form listed under `flows` carries its traffic there; the rest (a Form\n // no longer on disk, or the one Form named by `--flow`, which hides the\n // list and narrows the candidates to it) get their own.\n const listed = new Set(flows?.map((row) => row.slug));\n const unlisted = [...traffic].filter(([slug]) => !listed.has(slug));\n if (unlisted.length > 0) data.formCandidates = unlisted.map(([form, row]) => ({ form, ...row }));\n data.candidates = candidates\n .filter((row) => row.flowSlug === \"\")\n .map(({ doors, flowSlug: _flowSlug, nodeType: _nodeType, ...row }) => ({\n ...row,\n \"doors (events/sessions)\": doorsCell(doors),\n }));\n if (traffic.size > 0) hints.push(FORM_KEYS_HINT);\n }\n return renderMarkdown({ data, ...(hints.length > 0 ? { hints } : {}) });\n}\n\n/** The envelope as printed in `format`, pointing a long JSON read at `--output md` as the preset reports do. */\nexport function withFormatHint<T extends { data: object; hints?: readonly string[] }>(envelope: T, format: string): T {\n const hints = [...(envelope.hints ?? []), ...formatHint({ ...envelope.data }, format)];\n return { ...envelope, ...(hints.length > 0 ? { hints } : {}) };\n}\n","/**\n * What `baker analytics feed` hands an agent, once the rows have been read\n * rather than merely forwarded.\n *\n * The feed used to be a pass-through: whatever `event_stream` returned went\n * into the envelope. That is the right instinct for a raw feed and it left\n * three things wrong, all of which the dashboard had already fixed on the same\n * rows — so the screen a person reads and the rows an agent reads disagreed,\n * and the agent's were the worse half.\n *\n * 1. **The destination was a slug.** A tag firing from our server is logged\n * under its dispatcher's name — `metaCapi`, `ga4Mp` — which names nothing a\n * marketer has ever seen. An agent that reads `metaCapi` writes `metaCapi`\n * into a chat message.\n * 2. **Identity was one key.** `knownBy` is the store's single best key, so an\n * event carrying an address, a phone, a customer id and the click that\n * produced it came back as `email`. An agent given one key concludes the\n * event is fragile, or that two rows are two strangers.\n * 3. **The machinery outnumbered the events.** On one production account, 559\n * of 573 trigger rows were an embedded form announcing that it had drawn.\n * An agent paid for every one of them by the token.\n *\n * None of this reduces what can be reached — `--everything` returns the rows\n * as stored, and the envelope always says how many were held back.\n */\n\nimport { destinationName, isBookkeepingRow, type PeopleSearchTerm, rowMatchKeys } from \"@baker/api/analytics\";\n\n/** The fields of a stream row this module reads. Everything else passes through untouched. */\ninterface StreamRow extends Record<string, unknown> {\n eventType?: string;\n triggerId?: string;\n sideEffectType?: string;\n userData?: Record<string, string>;\n}\n\n/** What one page of the feed held back, so the envelope can own up to it. */\nexport interface FeedShape {\n rows: Record<string, unknown>[];\n heldBack: number;\n}\n\nfunction isStreamRow(value: unknown): value is StreamRow {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n/**\n * One row, with what the agent needs to act on it rather than to re-derive it.\n *\n * Both additions are **beside** the raw fields, never instead of them.\n * `sideEffectType` is still what the store holds and is still what `--sent-to`\n * takes, so an agent that learned the slug keeps working; `destination` is the\n * word to put in a sentence. Same for `matchedBy` beside `knownBy`: one says\n * how the store resolved this row, the other says what else it could have\n * used, and the difference between them is the whole answer to \"will this\n * event still find its person next time\".\n */\nfunction enrich(row: StreamRow): Record<string, unknown> {\n const out: Record<string, unknown> = { ...row };\n const destination = typeof row.sideEffectType === \"string\" ? row.sideEffectType : \"\";\n if (destination !== \"\") out.destination = destinationName(destination);\n const keys = rowMatchKeys(row.userData);\n if (keys.length > 0) out.matchedBy = keys.map((key) => key.field);\n return out;\n}\n\n/**\n * The feed as an agent should read it.\n *\n * `everything` is the escape hatch and it is deliberately total: it returns\n * the rows exactly as the store holds them, enrichment included but nothing\n * removed, because the moment somebody is debugging the collector rather than\n * their integration the held-back rows are the only ones that matter.\n */\nexport function shapeFeed(stream: unknown, everything: boolean): FeedShape {\n if (!Array.isArray(stream)) return { rows: [], heldBack: 0 };\n const rows = stream.filter(isStreamRow);\n const kept = everything\n ? rows\n : rows.filter((row) => !isBookkeepingRow({ eventType: row.eventType ?? \"\", triggerId: row.triggerId ?? \"\" }));\n return { rows: kept.map(enrich), heldBack: rows.length - kept.length };\n}\n\n/**\n * What to tell the agent about the rows it cannot see, and about the ones it can.\n *\n * A count with no way to it is worse than no count, so the first hint names\n * the flag. The second exists because the tie between an event and the uploads\n * it set off is the question this feed gets asked most and the answer is one\n * flag away — an agent staring at a refused upload has `eventId` on the row\n * and no reason to know what to do with it.\n */\nexport function feedHints(shape: FeedShape): string[] {\n const hints: string[] = [];\n if (shape.heldBack > 0) {\n hints.push(\n `${shape.heldBack} ${shape.heldBack === 1 ? \"row is\" : \"rows are\"} not listed: rows recorded to work the reports out — identifications, step views, session starts, a destination's answer already folded onto its dispatch — and embedded forms and calendars announcing that they drew. Add --everything to see them.`,\n );\n }\n const upload = shape.rows.find((row) => row.sideEffectType !== undefined && row.eventId !== undefined);\n if (upload !== undefined) {\n hints.push(\n `A delivery row carries the event it was made for in \\`eventId\\`. \\`baker analytics feed --uid ${String(upload.eventId)}\\` returns that event and every upload made for it, each with the platform's verdict.`,\n );\n }\n return hints;\n}\n\n/**\n * What a people search on the feed found nothing for, line by line.\n *\n * Only the lines with no row on this page, and the server's reason when it\n * has one (a deal HubSpot cannot place). \"Nothing in this window\" rather than\n * \"not found\": the feed is windowed and paged, and `lookup` reads all time.\n */\nexport function peopleSearchHints(\n search: readonly PeopleSearchTerm[],\n rows: readonly Record<string, unknown>[],\n): string[] {\n const found = new Set(rows.flatMap((row) => (Array.isArray(row.personTerms) ? row.personTerms : [])));\n return search\n .filter((term) => !found.has(term.term))\n .map((term) =>\n term.problem !== \"\"\n ? `${term.input}: ${term.problem}`\n : `${term.input}: nothing in this window on this page. \\`baker analytics lookup --person '${term.input}'\\` reads everything Baker ever saw of them.`,\n );\n}\n","/**\n * A Form's endings, named as event keys, before any of them has ever fired.\n *\n * What a Form counts is not in the Form. It is a company-level named event held\n * in Baker (`baker analytics conversions`), and the list an agent picks from —\n * `--candidates` — is built from events that actually arrived. That list is\n * right for a page that has traffic and empty for the Form somebody just built,\n * which is the exact moment the outcome is worth naming: a Form published with\n * nothing counted reports zero conversions for as long as nobody notices.\n *\n * The keys themselves come from `flowEventKeys` in `@baker/api/analytics` —\n * the tree, not the store — because the funnel picker needs the same list for\n * the same reason. What is here is what only the conversions surface needs:\n * joining those keys to what this company already counts, and refusing the one\n * mistake that has no other floor.\n *\n * An event key is syntax, not a reference: `parseEventKey` accepts\n * `form:<anything>:<x>:<y>`, the write succeeds, and a key naming a step that\n * does not exist or a trigger that step never raises counts zero forever\n * without ever erroring. That is the same failure `resolveTrigger` refuses for\n * a side effect, for the same reason.\n */\n\nimport { flowNodeTriggers } from \"@baker/api\";\nimport { parseEventKey } from \"@baker/api/analytics\";\nimport { type FlowEventKey, flowEventKeys, flowNodes } from \"@baker/api/analytics/flowEventKeys\";\n\nexport interface KeyRefusal {\n message: string;\n fix: string;\n}\n\n/**\n * Why this key could never match anything in this Form, or `null`.\n *\n * Answers only for a `form:` key naming THIS slug: a key for another Form, or\n * one whose Form is not in this workspace, is not something a local tree can\n * disprove — and refusing there would block counting an event a Form that has\n * since been deleted still has history for.\n */\nexport function formKeyRefusal(eventKey: string, tree: unknown, slug: string): KeyRefusal | null {\n const parsed = parseEventKey(eventKey);\n if (parsed?.family !== \"form\" || parsed.flowSlug !== slug) return null;\n\n const node = flowNodes(tree).find((candidate) => candidate.id === parsed.nodeId);\n if (!node) {\n return {\n message: `\"${slug}\" has no step with id \"${parsed.nodeId}\", so this key would be stored and count zero forever.`,\n fix: `Steps in this Form: ${flowNodes(tree)\n .map((n) => `${String(n.id)} (${String(n.name ?? \"unnamed\")})`)\n .join(\", \")}. \\`baker analytics conversions --flow ${slug}\\` prints each one already spelled as an event key.`,\n };\n }\n\n const triggers = flowNodeTriggers(node.nodeData);\n // A node type the table does not cover is a stale table, not a wrong key.\n if (!triggers.known || triggers.available.includes(parsed.triggerId)) return null;\n\n return {\n message: `Step \"${String(node.name ?? node.id)}\" never raises \\`${parsed.triggerId}\\`, so counting it would score zero however many people reach that step.`,\n fix:\n triggers.available.length > 0\n ? `It raises: ${triggers.available.join(\", \")}.`\n : (triggers.why ?? \"That step raises nothing a conversion could be counted on.\"),\n };\n}\n\nexport interface FlowConversionRow extends FlowEventKey {\n /** The name Baker counts this under, or `null` when it counts for nothing. */\n countedAs: string | null;\n}\n\n/** Each of a Form's endings, annotated with whether this company counts it. */\nexport function joinFlowConversions(\n candidates: readonly FlowEventKey[],\n definitions: readonly { name: string; eventKeys: readonly string[] }[],\n): FlowConversionRow[] {\n const byKey = new Map<string, string>();\n for (const definition of definitions) {\n for (const key of definition.eventKeys) byKey.set(key, definition.name);\n }\n return candidates.map((candidate) => ({ ...candidate, countedAs: byKey.get(candidate.eventKey) ?? null }));\n}\n\n/**\n * What to say about a Form once its own endings have been read against what\n * this company counts.\n *\n * The empty case is the one that matters, and it is why this exists at all: a\n * Form that counts nothing is indistinguishable, on every other report, from a\n * Form nobody converts on. Nothing errors, nothing warns, and the number is\n * zero — so the nudge carries the whole command rather than the advice to go\n * and find one.\n *\n * `noConversionReason` is the author's answer to the same question, and it\n * silences the nudge completely: a Form that hands the visitor to a shop\n * converts them there, and asking about it on every read is how somebody learns\n * to skip the line on the Forms that really are unfinished.\n */\nexport function flowConversionHints(\n slug: string,\n rows: readonly FlowConversionRow[],\n noConversionReason: string | null,\n): string[] {\n const counted = rows.filter((row) => row.countedAs !== null);\n if (counted.length > 0) {\n return [\n `Baker counts ${counted.length} of this Form's endings: ${counted\n .map((row) => `${row.countedAs} (${row.nodeName})`)\n .join(\n \", \",\n )}. Every other ending here scores nothing — count another with \\`--event <key> --name <outcome>\\`, reusing the SAME name to make two Forms one number.`,\n ];\n }\n\n if (noConversionReason) {\n return [\n `Nothing here is counted, and that is on purpose: this Form declares no outcome — \"${noConversionReason}\". Leave it alone unless that has stopped being true.`,\n ];\n }\n\n // Baker never picks the ending: which one is the point of a Form is a\n // reading of what its steps mean — \"Quote confirmed\" against \"Not a fit\" —\n // and a rule over the tree's shape picked a step both of them pass through.\n return [\n `Nothing on \"${slug}\" is counted, so it converts nobody on every report however many people finish it. Which ending is its outcome is your call, never Baker's: read the endings above by the step each happens on (\\`nodeName\\`), and when the step names do not make it obvious — a Form that branches into a rejection and a confirmation — ask the person which one means a real result. Then \\`baker analytics conversions --event \"<key>\" --name \"<what a person calls it>\"\\`. If this Form is already on a funnel step, count the key that step uses, so the two numbers agree.`,\n `If this Form has no outcome of its own — it hands the visitor to a shop, a portal or a funnel that converts them there — say so instead, with \\`noConversion: { \"reason\": \"…\" }\\` on its first step.`,\n ];\n}\n\nexport interface FlowCoverageRow {\n slug: string;\n /** Every outcome this company counts that happens inside this Form. */\n counted: { name: string; step: string }[];\n /** The author's reason this Form measures nothing, or `null`. */\n noConversion: string | null;\n}\n\n/**\n * Every Form in the workspace, and which of its steps this company counts.\n *\n * The answer to the question nothing could answer in one call: not \"does this\n * company count anything\" — a company whose only outcome is a landing page's\n * button counts plenty and still has Forms reporting zero — but **which Forms\n * count nothing**. That set is invisible on every other report, because a Form\n * that counts nothing looks exactly like a Form nobody converts on.\n *\n * The step's NAME rather than its id, because the id answers nothing a person\n * or an agent can check: \"counted at Book A Time\" is verifiable against the\n * Form in a second, `form:x:9cde868d-…:customForm.onFormSubmitted` is not.\n */\nexport function flowCoverage(\n forms: readonly { slug: string; tree: unknown }[],\n definitions: readonly { name: string; eventKeys: readonly string[] }[],\n): FlowCoverageRow[] {\n return forms.map(({ slug, tree }) => {\n const rows = joinFlowConversions(flowEventKeys(tree, slug), definitions);\n const declared = (tree as { noConversion?: { reason?: unknown } } | null)?.noConversion?.reason;\n const seen = new Set<string>();\n return {\n slug,\n counted: rows\n .filter((row) => row.countedAs !== null)\n // One definition can be counted on two triggers of the same step; the\n // reader wants the step, not the trigger, so the pair is deduplicated.\n .filter((row) => {\n const key = `${row.countedAs} ${row.nodeName}`;\n if (seen.has(key)) return false;\n seen.add(key);\n return true;\n })\n .map((row) => ({ name: String(row.countedAs), step: row.nodeName })),\n noConversion: typeof declared === \"string\" && declared.trim() !== \"\" ? declared.trim() : null,\n };\n });\n}\n\n/**\n * The Forms nobody has answered for, in one line.\n *\n * A Form that declares why it has no outcome is answered and is left out — the\n * whole reason that declaration exists is that warning about it forever is how\n * people learn to skip the warning on the Forms that really are unfinished.\n */\nexport function flowCoverageHints(coverage: readonly FlowCoverageRow[]): string[] {\n const unset = coverage.filter((row) => row.counted.length === 0 && row.noConversion === null);\n if (unset.length === 0) return [];\n return [\n `${unset.length} of ${coverage.length} Form(s) here count nothing, so every report shows them converting nobody: ${unset\n .map((row) => row.slug)\n .join(\n \", \",\n )}. Read each one's endings with \\`baker analytics conversions --flow <slug>\\` and name the outcome, or declare on its first step why it has none.`,\n ];\n}\n","/**\n * Hints returned alongside a report.\n *\n * The point of `baker analytics` is that one call is enough to decide what to\n * do next; a table of numbers is only half of that. These are the nudges the\n * agent would otherwise have to already know — which step to fix, when a number\n * is too small to trust, where the traffic problem actually is.\n *\n * Pure, so the wording is testable and cannot drift from the thresholds.\n */\n\nimport type { AdPlatform, AnalyticsCampaignRow, AnalyticsQueryData } from \"@baker/api/analytics\";\nimport { AD_PARAM_ROLE_SPECS, essentialAdDimensions } from \"@baker/api/analytics\";\n\n/** A drop this steep is the page's problem, not a rounding difference. */\nconst SEVERE_STEP_DROP = 0.6;\n\n/** Below this, a conversation about conversion rate is a conversation about noise. */\nconst MIN_SESSIONS_FOR_RATES = 100;\n\n/** Under ten seconds on a landing page reads as message mismatch, not interest. */\nconst SHALLOW_ENGAGEMENT_MS = 10_000;\n\nfunction percent(value: number): string {\n return `${Math.round(value * 100)}%`;\n}\n\n/** Below this, \"no parameters at all\" is a quiet week rather than a finding. */\nconst MIN_SESSIONS_FOR_TAGGING_CLAIM = 20;\n\n/**\n * What the shape of a report has to say, as opposed to its numbers.\n *\n * Its own function because these are findings about configuration and about\n * what was *not* returned, rather than about performance — and because folding\n * them in with the others put one function past the complexity budget.\n */\nfunction shapeHints(data: AnalyticsQueryData): string[] {\n const hints: string[] = [];\n\n // A list that came back full and paged is a list with more behind it, and an\n // agent that reads one page and reports a total is the same wrong answer the\n // dashboard used to give. Said here because the page is easy to miss in a\n // response that otherwise looks complete.\n const pageInfo = data.pageInfo;\n if (pageInfo?.hasMore) {\n hints.push(\n `This is page ${pageInfo.page} of ${pageInfo.block} (${pageInfo.returned} rows) and there are more — pass --page ${pageInfo.page + 1} for the next one. Do not report a total from one page.`,\n );\n }\n\n // The one finding that explains an empty parameter list, and it is not the\n // one anybody guesses. A platform sending real traffic with nothing on the\n // URL has no tracking template applied; no amount of mapping will help.\n for (const entry of data.adPlatformTraffic ?? []) {\n if (entry.sessions >= MIN_SESSIONS_FOR_TAGGING_CLAIM && entry.taggedSessions === 0) {\n hints.push(\n `${entry.sessions} visits arrived from ${entry.platform} carrying no campaign parameters at all — the ad URLs are untagged, so there is nothing for a mapping to read. That is a change in their ad account, not in Baker.`,\n );\n }\n }\n\n // The row that is actually actionable, and the one that used to be silent.\n // A parameter arriving on this platform's own traffic with no role is not a\n // gap in the data, it is a gap in the mapping — and while it lasts the values\n // are not stored at all, so the breakdown that would have used them comes\n // back empty rather than wrong. Empty reads as \"this campaign brought no\n // keywords\", which is a different and much more expensive conclusion.\n const unread = (data.adParams ?? []).filter((row) => row.role === undefined && row.sessions > 0);\n if (unread.length > 0) {\n const named = unread\n .slice(0, 8)\n .map((row) => `${row.name} (${row.sessions} visits)`)\n .join(\", \");\n hints.push(\n `Nothing reads these parameters yet: ${named}${unread.length > 8 ? \", …\" : \"\"}. Their values are not being stored — only the fact that the name appeared — so any breakdown over them is empty rather than wrong. Name each one with \\`baker analytics map --platform <platform> --set <name>=<role>\\`; mapping also fixes the history for a parameter Baker was already capturing.`,\n );\n }\n\n const unplaceable = data.adParamsUnattributed ?? [];\n if (unplaceable.length > 0) {\n hints.push(\n `Some parameters arrive on visits with no click id and no recognised source, so no platform can be named for them: ${unplaceable.map((row) => row.name).join(\", \")}. If you know where they come from, map them with --platform on that platform.`,\n );\n }\n\n return hints;\n}\n\n/**\n * The ad tiers the URLs are not carrying, and the suffix that would carry them.\n *\n * The one recommendation this CLI makes about somebody else's ad account, and\n * it is here because the cost of staying quiet is unrecoverable. Every other\n * mapping gap is fixable retroactively — a role is resolved when a report is\n * read, so naming a parameter Baker has already been capturing corrects the\n * history too. A parameter the ads never sent has no stored values anywhere for\n * a meaning to apply to, so the month that went by untagged stays permanently\n * unattributable. It is also silent: the breakdown comes back empty, and empty\n * reads as \"this campaign sent no traffic\" rather than \"we cannot tell you\".\n *\n * Only for platforms with traffic in the window. A campaign that has not\n * started has nothing to be wrong about, and an agent told that eight platforms\n * are badly tagged learns to ignore the hint.\n *\n * The suffix is exact and pasteable, in the platform's own macro language, and\n * it contains ONLY what is missing — everything already on the URL keeps\n * working and keeps being read.\n */\nfunction adHierarchyHints(data: AnalyticsQueryData, platform: AdPlatform | undefined): string[] {\n // `adParams` is ONE platform's inventory — the one the report was scoped to —\n // so a hint about it can only be made when the caller says which. Without a\n // platform there is no honest claim to make: `campaign_id` arriving on Google\n // traffic says nothing about whether Meta is sending one.\n if (platform === undefined) return [];\n\n const traffic = (data.adPlatformTraffic ?? []).find((entry) => entry.platform === platform);\n // A campaign that has not started has nothing to be wrong about, and an agent\n // told that eight platforms are badly tagged learns to ignore the hint.\n if (traffic === undefined || traffic.sessions < MIN_SESSIONS_FOR_TAGGING_CLAIM) return [];\n // Nothing at all on the URLs is a different finding with its own hint above —\n // \"there is no tracking template\" rather than \"the template is missing three\n // macros\" — and printing both says the same thing twice.\n if (traffic.taggedSessions === 0) return [];\n\n // The roles the report already resolved, which is the same question\n // `adDimensionCoverage` asks and the only form of it available here.\n // Excluding the rows no traffic produced: a parameter this company configured\n // ahead of launch comes back with `sessions: 0` and `awaitingTraffic`, and\n // treating it as carried silences the hint for the one account it is for.\n const carried = new Set(\n (data.adParams ?? [])\n .filter((row) => row.role !== undefined && row.awaitingTraffic !== true)\n .map((row) => row.role),\n );\n const missing = essentialAdDimensions(platform).filter((entry) => !carried.has(entry.role));\n if (missing.length === 0) return [];\n\n const suffix = missing.map((entry) => `${entry.names[0] ?? entry.role}=${entry.macro}`).join(\"&\");\n return [\n `${platform} traffic carries no ${missing.map((entry) => AD_PARAM_ROLE_SPECS[entry.role].label.toLowerCase()).join(\", \")}, so no report can say which ad sent a visit — only which platform. Add \\`${suffix}\\` to that account's tracking template. This cannot be backfilled: a parameter's values are only stored once it starts arriving.`,\n ];\n}\n\n/**\n * Why a report counted page views and no visits — which is two different\n * findings wearing one number.\n *\n * The consent reading is only ever true of traffic Baker measured itself.\n * Scoped to `--origin server` a session count of zero is arithmetic rather\n * than a finding: an ingest row carries no `session_id` by design, because\n * minting one would invent a visit that never happened. Said the wrong way\n * round it sends somebody to audit a consent banner over a number that could\n * not have been anything else — which is exactly what it did the first time the\n * flag was used against the dev deployment.\n *\n * Its own function so the branch does not push `buildAnalyticsHints` past the\n * complexity budget, which it did.\n */\nfunction sessionlessHint(data: AnalyticsQueryData, origin?: string): string[] {\n if (data.totals.pageViews === 0 || data.totals.sessions > 0) return [];\n if (origin === \"server\") {\n return [\n \"No sessions, because this report is scoped to events a client's own server posted and those carry no visit — a session would be an invented one. Every per-visitor rate here is null for the same reason; read the counts, and drop --origin for anything about behaviour.\",\n ];\n }\n return [\n \"Pages were served but no sessions were counted — visitors are declining analytics consent, so traffic is measurable and per-visitor behaviour is not. Say so rather than reporting zero visitors.\",\n ];\n}\n\n/**\n * What this report is a report ON, when that is not what the agent assumes.\n *\n * Every page report counts only people who visited a Baker landing, and credits\n * a conversion anywhere to the last landing its person viewed within 90 days.\n * An agent that sees a tag measuring the client's whole website (`all`) will\n * expect the whole business in these numbers and go looking for sales that are\n * deliberately not here; under `outcomes` a drop-off between a landing and a\n * sale is invisible rather than absent.\n *\n * A hint rather than a line in the tool doc, because it has to fire whether or\n * not the agent read the doc, and it is only true some of the time — which is\n * exactly the shape `hints[]` exists for.\n *\n * Silent under the default and under `unknown`: the first needs no warning, and\n * the second cannot be told from \"no tag\" or \"a quiet month\" from here.\n */\nfunction measureScopeHint(data: AnalyticsQueryData): string[] {\n const scope = data.measureScope?.scope;\n\n if (scope === \"all\") {\n return [\n \"This company's website tag measures EVERY visitor of their own website, but this report does not: visits, views and conversions here count only people who visited a Baker landing, and a sale on their site counts only when its person viewed a landing in the 90 days before it. Everything else the tag sees is in `baker analytics feed`, not in these numbers.\",\n ];\n }\n if (scope === \"outcomes\") {\n return [\n \"This company's website tag reports outcomes only, so no browsing on their own site is measured. Visits and views cover Baker pages alone, and a drop-off between a Baker page and a sale is invisible rather than absent.\",\n ];\n }\n return [];\n}\n\nexport function buildAnalyticsHints(\n data: AnalyticsQueryData,\n /** The platform the ad blocks were scoped to, when the caller named one. */\n platform?: AdPlatform,\n /** The collector the report was scoped to, when the caller named one. */\n origin?: string,\n): string[] {\n const hints: string[] = [];\n\n // First, because it changes how every hint below it should be read.\n hints.push(...measureScopeHint(data));\n\n for (const funnel of data.funnels ?? []) {\n const worst = funnel.worstStep;\n if (worst && worst.dropRate !== null && worst.dropRate >= SEVERE_STEP_DROP) {\n hints.push(\n `Form \"${funnel.flowSlug}\" loses ${percent(worst.dropRate)} of people at step \"${worst.stepId}\" — open the flow-builder skill and look at what that step asks for.`,\n );\n }\n }\n\n if (data.totals.sessions > 0 && data.totals.sessions < MIN_SESSIONS_FOR_RATES) {\n hints.push(\n \"Too few sessions to read rates as reliable — report the absolute numbers and say the period is small, rather than quoting a conversion rate.\",\n );\n }\n\n if (data.totals.avgEngagementMs !== null && data.totals.avgEngagementMs < SHALLOW_ENGAGEMENT_MS) {\n hints.push(\n \"Visitors leave in under ten seconds on average — that is usually a mismatch between the ad and the page, not a form problem. Check the top sources against the page's headline.\",\n );\n }\n\n hints.push(...sessionlessHint(data, origin));\n\n hints.push(...shapeHints(data));\n hints.push(...adHierarchyHints(data, platform));\n\n if ((data.topCampaigns ?? []).length === 0 && (data.topSources ?? []).length > 0) {\n hints.push(\n \"No campaign tagging on any traffic — ad clicks cannot be attributed to a campaign until the destination URLs carry utm parameters.\",\n );\n }\n\n hints.push(...campaignOriginHints(data.topCampaigns));\n\n return hints;\n}\n\n/**\n * Where each campaign row came from, and the trap in deferring to `ads`.\n *\n * `utm_campaign` is free text somebody typed into a tracking template, and\n * almost everyone types the platform's own macro into it —\n * `utm_campaign={campaignid}` — so on a well-tagged account much of this list\n * is the same campaigns `analytics ads` reports, under a typed string instead\n * of the platform's own id. An agent reading both and not knowing that will\n * double-count, or quote a campaign id as if it were a name.\n *\n * But \"use `ads` instead\" is only true when the account tags a campaign id or\n * name *parameter* as well. `utm_campaign` is one of `RESERVED_PARAM_NAMES`: no\n * mapping may claim it and the edge never captures it into `campaign_params`,\n * which is the only field `ads` reads. So on an account whose sole campaign\n * signal is `utm_campaign` — auto-tagging plus a UTM template, which is common\n * — `ads --role campaign_id` returns nothing at all, and an agent that deferred\n * reports no campaigns for a company running plenty.\n *\n * Both halves of that get said, because either one alone produces a wrong\n * answer with no symptom.\n */\nfunction campaignOriginHints(rows: readonly AnalyticsCampaignRow[] | undefined): string[] {\n const campaigns = rows ?? [];\n const fromAds = campaigns.filter((row) => row.clickPlatform !== \"\");\n if (fromAds.length === 0) return [];\n const own = campaigns.length - fromAds.length;\n return [\n `${fromAds.length} of these campaigns arrived on an ad click (\\`clickPlatform\\` is set). \\`baker analytics ads --role campaign_id\\` MAY report the same campaigns under the platform's own ids — check before you quote both, or you will count one campaign twice. It may equally report nothing: \\`ads\\` only reads parameters the account tags separately, and \\`utm_campaign\\` is reserved and never reaches it, so an account whose only campaign signal is \\`utm_campaign\\` has its campaigns HERE and nowhere else. Never report \"no campaigns\" on the strength of an empty \\`ads\\`. ${own === 0 ? \"Every row here came from an ad.\" : `The other ${own} carry no click id at all — a newsletter, a partner link, a tagged post — and only this report can show them.`}`,\n ];\n}\n","/**\n * What each `baker analytics` preset answers, and what to do with the answer.\n *\n * The descriptions are agent-facing: they are what `--list-presets` prints and\n * what steers a choice between them, so they name the question rather than the\n * dimensions involved.\n */\n\nexport interface AnalyticsPresetInfo {\n name: string;\n description: string;\n /** Which skill turns this data into a decision. */\n playbook: string;\n}\n\nexport const ANALYTICS_PRESET_INFO: readonly AnalyticsPresetInfo[] = [\n {\n name: \"overview\",\n description:\n \"Everything at once: traffic, top pages with their conversion rates, where visitors came from, the worst step of every Form, and what the conversion number consists of — one row per outcome the company marked, by the name they gave it. Start here. A `conversions` row at zero means the outcome is marked and has never fired, which is usually a Form that never reaches it.\",\n playbook: \"landing — diagnose a page that gets traffic but few leads\",\n },\n {\n name: \"traffic\",\n description: \"Where visitors come from — trend, sources, campaigns, countries, devices.\",\n playbook: \"the platform playbook for the channel that dominates\",\n },\n {\n name: \"funnel\",\n description: \"Per-step drop-off for the company's Forms. Narrow to one with --flow.\",\n playbook: \"flow-builder — fix the step that loses people\",\n },\n {\n name: \"flow\",\n description:\n \"One Form in depth: conversions counted at the node that produced them, step-to-step paths for a branching Form, the per-step table, every trigger raised, and each marked conversion by name. Read convertedSessions rather than submits — a Form whose scheduling widget sits mid-flow never emits a submit and reports zero on every other report. An empty `conversions` array means nobody has marked what this Form is for, so every conversion number for it is a zero rather than a measurement — Baker never guesses which ending is the point of a Form. Mark it in the Forms builder or with `conversions: [{ triggerId }]` on the node. Before proposing a mark, check that Form's `_data.json` for `noConversion` on its first step: that is its author saying the outcome happens somewhere Baker cannot see, so its zero is a finished answer rather than a gap. The declaration lives in the Form and never reaches this report, which is built entirely from the event store. `flowDestinations` is the other half and is evidence rather than a count: which Forms demonstrably handed something over in this window. Read it against the scaffold's `flow-no-destination` warning before acting on one — the stored picture of a Form is only rewritten when it is published, so a destination added since then shows there and not in the tree.\",\n playbook: \"flow-builder — fix the branch or the step that loses people\",\n },\n {\n name: \"page\",\n description: \"One page in detail. Requires --path.\",\n playbook: \"landing\",\n },\n {\n name: \"triggers\",\n description:\n \"What visitors did inside a Form and how much of it fired a side effect — link clicks that leave the site, scheduling widgets, embedded checkouts. The funnel shows the steps they walked; this shows what they did. A trigger with many fires and no side effects is behaviour nobody is measuring yet.\",\n playbook: \"flow-builder — wire up what matters, or tags for what should be measured\",\n },\n {\n name: \"paid\",\n description:\n \"Paid campaigns, grouped by session rather than by page view, with the click ids that resolve to a real ad click — and, crucially, how many of those leads were actually DELIVERED to the CRM. A campaign whose forms all submit and whose integration is rejecting every one of them looks perfect on every other preset and reads as zero delivered here.\",\n playbook: \"the platform playbook for the channel — or tags, when the gap is delivery rather than acquisition\",\n },\n {\n name: \"delivery\",\n description:\n 'Where each submission actually went: every destination it reached, whether that destination accepted it, and the id the destination gave the record it created. Failures only by default; --full lists every submission. This is the preset for \"I filled in the form, where is my lead?\".',\n playbook: \"flow-builder — repair the destination that is refusing leads\",\n },\n {\n name: \"submissions\",\n description:\n \"Every attempt anybody made at a Form — one visitor, one Form, one visit — finished, abandoned halfway, or completed on a Form that sends nowhere. The honest companion to `delivery`, which is built from dispatches and so cannot show a Form with no destination at all. Read it when the question is what was collected rather than what was handed over. A list, so it pages.\",\n playbook: \"flow-builder — the Form that collects and sends nothing is the one to wire up\",\n },\n {\n name: \"geo\",\n description:\n \"Where the people who convert actually are, at whatever resolution you ask for: country, region, city, postcode or US DMA. Country answers 'which market is this working in'; postcode answers the question a local advertiser is actually asking, and matches the unit Google and Meta sell that targeting in.\",\n playbook: \"the platform playbook for the channel — narrow the targeting to where it already works\",\n },\n {\n name: \"bots\",\n description:\n \"Which machines fetch this site. Every other report here excludes them; this one reads them. Answers whether Google is still crawling a page after it was republished, whether Meta fetched a link preview before spend went behind the URL, and how much of the traffic is now AI answer engines.\",\n playbook: \"landing — a page that stopped being crawled stops ranking, weeks before the traffic shows it\",\n },\n {\n name: \"companies\",\n description:\n \"Which companies are visiting, named by the visitor's own network — never a person — with visits, pages read and first/last seen. Recorded only for visitors who accepted marketing cookies, on a company that switched it on. --targets keeps the target accounts that came and lists the ones that did not.\",\n playbook: \"abm-playbook — which target accounts the ads warmed enough to read the pages\",\n },\n {\n name: \"landings\",\n description:\n \"Every landing side by side — views, entrances, conversion rate and a sparkline each, under the name a person gave it. Answers 'which of my pages is working' without having to know the answer first; `page` then answers one of them in depth. --tag narrows it to a campaign.\",\n playbook: \"landing — pick the page worth working on before working on one\",\n },\n {\n name: \"tags\",\n description:\n \"The same numbers rolled up by the tag a landing is filed under — the unit a campaign is actually run at. A client asks how the September push did, and September was six landings.\",\n playbook: \"the platform playbook for the channel — judge the campaign, not the page\",\n },\n {\n name: \"events\",\n description:\n \"Events the pages define for themselves: a section seen, a video played, a button clicked. Lists each with the property keys it carries so it can be broken down, and takes --steps for an ORDERED funnel over them — where a session only counts at a step if it reached it after the one before, so 'saw pricing then clicked demo' is not the same number as the reverse.\",\n playbook: \"landing — measure the part of the page the Form funnel cannot see\",\n },\n {\n name: \"devices\",\n description:\n \"Browser, OS, engine or viewport of real visitors, with the conversion rate of each. Narrow enough to reproduce a bug on: 'mobile converts worse' is not actionable, 'Safari 17 under 380px converts at a third of the rate' is.\",\n playbook: \"landing — fix the client the page is broken on\",\n },\n {\n name: \"releases\",\n description:\n \"Traffic and conversions per published build, newest first. Answers whether a change to a page moved the numbers.\",\n playbook: \"landing — compare a page before and after a change\",\n },\n {\n name: \"conversions\",\n description:\n \"What this company counts as a conversion, and what each one produced. The report every other conversion number is derived from, so read it FIRST whenever a conversion figure looks wrong or reads zero: an empty list means nothing is counted and every conversion number everywhere is zero, whatever visitors did. `baker analytics conversions --candidates` lists everything these pages actually produce — every Form step, every data-baker-* event, every outbound destination — and counting one applies immediately and rescores the whole history, with no publish. Anything can be counted, not just a Form.\",\n playbook: \"run `baker analytics conversions` — this one is fixed with the command, not with a skill\",\n },\n];\n","/**\n * One company, however its name is spelled.\n *\n * Visitor companies arrive as a network's registered name — `Banco Santander\n * S.A.`, `ACME-CORP` — and nothing else: Cloudflare gives no website, so they\n * cannot join the Account insights rows or a target list on the registrable\n * domain everything else here joins on. This is the fallback key: case,\n * accents, punctuation and legal-entity suffixes removed, so `Telefónica, S.A.`\n * and `TELEFONICA` meet. Equality on the key, never a fuzzy score — a wrong\n * match puts the wrong company in a sales brief.\n */\n\n/** Legal-entity and filler words that say nothing about which company it is. */\nconst NOISE_WORDS = new Set([\n \"inc\",\n \"incorporated\",\n \"llc\",\n \"ltd\",\n \"limited\",\n \"plc\",\n \"corp\",\n \"corporation\",\n \"co\",\n \"company\",\n \"gmbh\",\n \"ag\",\n \"kg\",\n \"bv\",\n \"nv\",\n \"sa\",\n \"sau\",\n \"sl\",\n \"slu\",\n \"sas\",\n \"sarl\",\n \"srl\",\n \"spa\",\n \"oy\",\n \"ab\",\n \"as\",\n \"aps\",\n \"pty\",\n \"lda\",\n \"ltda\",\n \"kk\",\n \"group\",\n \"grupo\",\n \"holding\",\n \"holdings\",\n \"the\",\n]);\n\n/** The comparable form of a company name; empty when nothing distinctive is left. */\nexport function companyNameKey(name: string): string {\n return name\n .normalize(\"NFD\")\n .replace(/\\p{Diacritic}/gu, \"\")\n .toLowerCase()\n .replace(/\\./g, \"\")\n .split(/[^\\p{L}\\p{N}]+/u)\n .filter((word) => word !== \"\" && !NOISE_WORDS.has(word))\n .join(\" \");\n}\n","/**\n * `baker analytics companies --targets <file>`: the visitor companies that are\n * on a target-account list, and the targets that never came.\n *\n * The list is item 1's (`readTargetsFile`, keyed by registrable domain), but a\n * visitor company is a network's registered name and nothing else — Cloudflare\n * gives no website — so it is matched by name: the target's `name` through\n * `companyNameKey` (case, accents, punctuation and legal suffixes removed),\n * else the domain's first label (`acme.co.uk` → `acme`). Equality, never a\n * fuzzy score: a wrong match puts the wrong company in a sales brief.\n */\n\nimport { companyNameKey } from \"@baker/api/accounts/companyName\";\nimport type { TargetAccount } from \"@baker/api/accounts/targets\";\nimport { ANALYTICS_FULL_BREAKDOWN_LIMIT, type AnalyticsVisitorCompanyRow } from \"@baker/api/analytics\";\nimport type { TargetsFile } from \"../accounts/targets.ts\";\n\n/**\n * The most visitor companies a `--full` read returns — the backend's\n * `visitorCompanies` block asks Tinybird for eight rows per breakdown slot.\n * `--targets` always reads this many, so a target that visited is never lost\n * below a five-row compact cut; a list this long may still be cut.\n */\nexport const VISITOR_COMPANIES_FULL_ROWS = ANALYTICS_FULL_BREAKDOWN_LIMIT * 8;\n\nfunction targetKey(target: TargetAccount): string {\n return companyNameKey(target.name ?? target.domain.split(\".\")[0] ?? \"\");\n}\n\n/** What the agent needs to read the list right, which the rows alone cannot say. */\nexport function visitorCompanyHints(companies: readonly AnalyticsVisitorCompanyRow[]): string[] {\n if (companies.length === 0) {\n return [\n \"No visitor companies in this window. They are recorded only when an admin has turned on 'Show which companies visit your pages' (Settings → General), the company measures in full, and the visitor accepted marketing cookies — so an empty list does not mean no target account came. Tell the user which of those to check rather than reporting zero interest.\",\n ];\n }\n const hints = [\n \"Each name is the visitor's network as registered, never a person, and there is no website: match accounts by name (`--targets` does it), and never present a row as an individual's visit.\",\n ];\n if (companies.some((company) => company.confidence === \"low\")) {\n hints.push(\n \"Rows with confidence low are a business only because their network is not a known ISP, carrier, cloud or VPN. Check the name reads as a real company before it goes into a sales brief.\",\n );\n }\n return hints;\n}\n\nexport interface TargetMatch {\n companies: Array<AnalyticsVisitorCompanyRow & { target: string }>;\n targets: {\n total: number;\n visited: number;\n notVisited: TargetAccount[];\n byTier?: Record<string, { total: number; visited: number }>;\n };\n}\n\nexport function matchTargets(\n companies: readonly AnalyticsVisitorCompanyRow[],\n targets: readonly TargetAccount[],\n): TargetMatch {\n const visitorByKey = new Map<string, AnalyticsVisitorCompanyRow>();\n for (const company of companies) {\n const key = companyNameKey(company.name);\n if (key !== \"\" && !visitorByKey.has(key)) visitorByKey.set(key, company);\n }\n const matched: TargetMatch[\"companies\"] = [];\n const notVisited: TargetAccount[] = [];\n const byTier = new Map<string, { total: number; visited: number }>();\n for (const target of targets) {\n const key = targetKey(target);\n const visitor = key === \"\" ? undefined : visitorByKey.get(key);\n if (visitor) matched.push({ ...visitor, target: target.domain });\n else notVisited.push(target);\n if (target.tier !== undefined) {\n const tier = byTier.get(target.tier) ?? { total: 0, visited: 0 };\n tier.total += 1;\n if (visitor) tier.visited += 1;\n byTier.set(target.tier, tier);\n }\n }\n const summary = { total: targets.length, visited: targets.length - notVisited.length, notVisited };\n return {\n companies: matched,\n targets: byTier.size > 0 ? { ...summary, byTier: Object.fromEntries(byTier) } : summary,\n };\n}\n\n/**\n * Fill a `companies` answer's `visitorCompanies` (and `targets` under\n * `--targets`) and return its hints. The list is always set, empty included,\n * so the key an agent reads does not depend on whether a file was passed.\n */\nexport function refineCompaniesReport(\n data: Record<string, unknown>,\n companies: readonly AnalyticsVisitorCompanyRow[],\n targets?: TargetsFile,\n): string[] {\n if (!targets) {\n data.visitorCompanies = data.visitorCompanies ?? [];\n return visitorCompanyHints(companies);\n }\n const matched = matchTargets(companies, targets.targets);\n data.visitorCompanies = matched.companies;\n data.targets = matched.targets;\n const skipped =\n targets.unreadable.length > 0\n ? [`Skipped targets that are not a website: ${targets.unreadable.map((entry) => `\"${entry}\"`).join(\", \")}.`]\n : [];\n const cut =\n companies.length >= VISITOR_COMPANIES_FULL_ROWS\n ? [\n `Only the ${VISITOR_COMPANIES_FULL_ROWS} companies with the most visits were matched, so a target in notVisited may have come with fewer visits. Narrow the window (--days 7) and run it again before telling anyone an account never visited.`,\n ]\n : [];\n return [...visitorCompanyHints(matched.companies.length > 0 ? matched.companies : companies), ...skipped, ...cut];\n}\n","import type { AnalyticsSitesResponse, WebsiteTagResponse } from \"@baker/api/analytics\";\n\n/** `https://foodforjoe.es` as somebody types it into a command: `foodforjoe.es`. */\nfunction asTyped(origin: string): string {\n return origin.replace(/^https?:\\/\\//, \"\");\n}\n\n/**\n * \"Add this domain\", with the command that does it.\n *\n * The suggestion is only ever a domain Baker already serves a landing page on\n * for this company, so it is not a guess about who they are — and it is the\n * registrable domain rather than the landing host, because one entry covers the\n * whole estate under it and listing subdomains by hand is how a site gets\n * forgotten and silently measures nothing.\n */\nfunction suggestionHint(suggested: readonly string[]): string[] {\n if (suggested.length === 0) return [];\n const commands = suggested.map((origin) => `baker analytics sites --add ${asTyped(origin)}`).join(\" · \");\n return [\n `Baker publishes landing pages on ${suggested.map(asTyped).join(\", \")} for this company, and no declared website covers ${suggested.length === 1 ? \"it\" : \"them\"}. ` +\n `Declaring the domain covers every subdomain of it — the marketing site, the shop, the booking page, and the ones that do not exist yet: ${commands}`,\n ];\n}\n\n/**\n * What the install brief cannot say, because it does not know how the agent got\n * here.\n *\n * Two mistakes account for every website-tag install that has gone wrong, and\n * neither shows up as an error anywhere:\n *\n * - **No declared origin.** The key is ignored from any origin it was not\n * minted for, silently, so the tag is pasted, the container is published, and\n * nothing whatever is measured. There is no failing request to find.\n * - **The wrong scope.** The tag goes on All Pages because that is the only\n * trigger a tag manager can guarantee, and on `all` that means Baker ingests\n * an entire business. It looks identical to a working install from every\n * screen, and it is only visible as a number going up.\n *\n * Pure, so the wording is tested rather than eyeballed.\n */\nexport function websiteTagHints(response: WebsiteTagResponse): string[] {\n const hints: string[] = [];\n\n if (response.origins.length === 0) {\n hints.push(\n \"NO WEBSITE DECLARED — this tag will be ignored everywhere it is installed, silently, with no failing request to find. \" +\n \"Declare the site before installing anything: baker analytics sites --add <the client's domain>. \" +\n \"It takes effect at once and needs no republish.\",\n );\n }\n\n hints.push(...suggestionHint(response.suggestedOrigins));\n\n if (response.firstParty) {\n hints.push(\n `This tag loads from ${response.host} — the client's own domain, not Baker's — so the script and every event it sends ` +\n \"travel first-party, which a content blocker has no list entry for. The trade is a dependency: if that domain is ever \" +\n \"removed from Baker, this tag 404s and measurement stops. Re-run this command after any domain change.\",\n );\n }\n\n if (response.measure === \"all\") {\n hints.push(\n \"This tag measures the client's WHOLE WEBSITE — every visitor and every page view, not only the people your campaigns brought. \" +\n \"Their reports will describe their business rather than their campaigns, and outcomes their site produces from organic, email and direct traffic \" +\n \"will land in their conversion numbers. Only correct if somebody asked for that; otherwise re-run without --measure.\",\n );\n }\n\n if (response.measure === \"campaigns\" && !response.campaignsCanMatch) {\n hints.push(\n \"THIS TAG WILL MEASURE NOBODY — and will look installed. `campaigns` scope recognises a visitor by a first-party cookie, \" +\n \"and a browser does not carry one between two different registrable domains. This company's landings are not on a subdomain of the site \" +\n \"this tag goes on, so no visitor can ever match. Two fixes: put the landings on a subdomain of the client's own domain (a custom domain in Baker), \" +\n \"or install with --measure outcomes, which measures no browsing and reports the outcomes the site tells Baker about.\",\n );\n } else if (response.measure === \"campaigns\") {\n hints.push(\n \"This tag measures only visitors who have been on one of this company's Baker landing pages — their whole journey through the site, to the outcome. \" +\n \"Anybody else is not measured at all. Use --measure all only if the client asked for their whole website measured.\",\n );\n }\n\n // Said on every install, because naming the outcome is the half that gets\n // skipped: the tag fires correctly and every conversion number stays zero,\n // with nothing reporting it. Retroactive, so it can honestly be done after.\n hints.push(\n 'Reporting an outcome is not the same as counting it. After a `window.baker.track(\"name\")` call is live, ' +\n 'name it with: baker analytics conversions --event page:<name> --name \"<what to call it>\". ' +\n \"That applies immediately and counts everything already collected.\",\n );\n\n return hints;\n}\n\n/**\n * What `baker analytics sites` cannot say by listing origins.\n *\n * The list is the whole of what makes the public site key safe, and every way\n * of getting it wrong fails the same way: silently, with a tag that looks\n * perfectly installed. So each hint here names the consequence rather than the\n * state, and the two that follow a write answer the question somebody asks\n * immediately afterwards — does the tag change, and does anything have to be\n * published again.\n */\nexport function analyticsSitesHints(response: AnalyticsSitesResponse): string[] {\n const hints: string[] = [];\n\n if (response.origins.length === 0) {\n hints.push(\n \"No website is declared, so this company's measurement tag is ignored wherever it is installed — silently, with no \" +\n \"failing request to find. Add one with: baker analytics sites --add <the client's domain>\",\n );\n }\n\n hints.push(...suggestionHint(response.suggestedOrigins));\n\n if (response.added.length > 0) {\n hints.push(\n `Live now. A tag already installed on ${response.added.map(asTyped).join(\", \")} starts being accepted immediately — ` +\n \"there is no new tag to paste and nothing to publish. Only events sent from here on are kept; the ones refused \" +\n \"before this were never stored and cannot be recovered.\",\n );\n }\n\n if (response.removed.length > 0) {\n hints.push(\n `Measurement from ${response.removed.map(asTyped).join(\", \")} will stop at once, with no error on that site — a tag ` +\n \"still installed there goes quiet. Everything already collected from it is kept.\",\n );\n }\n\n hints.push(\n \"One entry covers everything under it: example.com answers for www., go. and shop.eu. It never widens upward, so \" +\n \"declaring go.example.com leaves example.com uncovered — declare the domain, not the subdomain you are installing on today.\",\n );\n\n return hints;\n}\n"],"mappings":"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","names":["args"]}
|