@koda-sl/baker-cli 0.526.0 → 0.527.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 +1 -0
- package/dist/{account-APMJVG2F.js → account-CQJFUII3.js} +3 -3
- package/dist/{account-APMJVG2F.js.map → account-CQJFUII3.js.map} +1 -1
- package/dist/{account-assets-TAUEBCY5.js → account-assets-IDVG4DGK.js} +12 -12
- package/dist/account-assets-IDVG4DGK.js.map +1 -0
- package/dist/{account-level-2MYDHOID.js → account-level-GK7M2MMQ.js} +14 -14
- package/dist/account-level-GK7M2MMQ.js.map +1 -0
- package/dist/{accounts-VIAQC3PA.js → accounts-KRW3JNCW.js} +2 -2
- package/dist/{activities-MJMAV3SI.js → activities-2RAJXP3J.js} +3 -3
- package/dist/{activities-MJMAV3SI.js.map → activities-2RAJXP3J.js.map} +1 -1
- package/dist/{ads-O4RL4JXD.js → ads-F2PURRHL.js} +2 -2
- package/dist/{ads-NNKUR4HG.js → ads-JVBLNTBF.js} +20 -15
- package/dist/ads-JVBLNTBF.js.map +1 -0
- package/dist/{adsets-VO5C7PDM.js → adsets-FN5ZWSYF.js} +11 -11
- package/dist/adsets-FN5ZWSYF.js.map +1 -0
- package/dist/{asset-writes-PQNENVYY.js → asset-writes-SSI3CO2A.js} +4 -4
- package/dist/{audience-writes-QDDADCYE.js → audience-writes-G64754LR.js} +6 -6
- package/dist/audience-writes-G64754LR.js.map +1 -0
- package/dist/{audiences-TQ5ZEWIR.js → audiences-JAVNJ5NC.js} +8 -8
- package/dist/audiences-JAVNJ5NC.js.map +1 -0
- package/dist/{businesses-CW4RB36R.js → businesses-DXRDQIFP.js} +2 -2
- package/dist/{campaigns-IQYYYD2S.js → campaigns-GQWFSKGC.js} +11 -11
- package/dist/campaigns-GQWFSKGC.js.map +1 -0
- package/dist/{catalogs-PLBY32BE.js → catalogs-TBRONC2D.js} +15 -15
- package/dist/catalogs-TBRONC2D.js.map +1 -0
- package/dist/{chunk-OEJNZSJG.js → chunk-4XXA4WCS.js} +4 -4
- package/dist/chunk-4XXA4WCS.js.map +1 -0
- package/dist/{chunk-HPAGYPAF.js → chunk-BN4TDL5L.js} +65 -23
- package/dist/chunk-BN4TDL5L.js.map +1 -0
- package/dist/{chunk-5LN7GWRE.js → chunk-GEFZVG4P.js} +10 -10
- package/dist/chunk-GEFZVG4P.js.map +1 -0
- package/dist/{chunk-XB2FODWU.js → chunk-TKDOVFO6.js} +2 -2
- package/dist/cli.js +1 -1
- package/dist/{copy-writes-GZ744FHU.js → copy-writes-EUFEVWDW.js} +5 -5
- package/dist/{copy-writes-GZ744FHU.js.map → copy-writes-EUFEVWDW.js.map} +1 -1
- package/dist/{creatives-GTRQAZSB.js → creatives-6VJDUHEY.js} +6 -6
- package/dist/creatives-6VJDUHEY.js.map +1 -0
- package/dist/{custom-conversions-VQ3LBWYV.js → custom-conversions-NDY5RHSM.js} +6 -6
- package/dist/{custom-conversions-VQ3LBWYV.js.map → custom-conversions-NDY5RHSM.js.map} +1 -1
- package/dist/{draft-ME4TNPO7.js → draft-G432FJ7N.js} +2 -2
- package/dist/{insights-INIFHYR7.js → insights-IVW6W5CP.js} +2 -2
- package/dist/{instant-experiences-NMJUWNP3.js → instant-experiences-6F6GXZ67.js} +5 -5
- package/dist/{instant-experiences-NMJUWNP3.js.map → instant-experiences-6F6GXZ67.js.map} +1 -1
- package/dist/{leads-QBWR7FKT.js → leads-CJKRKB2Q.js} +6 -6
- package/dist/{leads-QBWR7FKT.js.map → leads-CJKRKB2Q.js.map} +1 -1
- package/dist/{media-23IW442U.js → media-HDHZULQ7.js} +3 -3
- package/dist/{meta-H3KBPU33.js → meta-MOSJVYIE.js} +38 -38
- package/dist/{page-photos-SHIGXDYE.js → page-photos-4JTIKXAW.js} +5 -5
- package/dist/page-photos-4JTIKXAW.js.map +1 -0
- package/dist/{pages-XJF3FBOJ.js → pages-BAUS7LQM.js} +2 -2
- package/dist/{pixels-AZCIWWXK.js → pixels-QOSEYXCP.js} +3 -3
- package/dist/pixels-QOSEYXCP.js.map +1 -0
- package/dist/{preview-BIEVHIDJ.js → preview-X6EJGPAY.js} +2 -2
- package/dist/{rule-writes-QLZNJ7CT.js → rule-writes-A4BFYHPA.js} +7 -7
- package/dist/rule-writes-A4BFYHPA.js.map +1 -0
- package/dist/{rules-FJHYG4SJ.js → rules-HLREXY2M.js} +11 -11
- package/dist/rules-HLREXY2M.js.map +1 -0
- package/dist/{shared-HY2VP7PT.js → shared-I3SB77VH.js} +2 -2
- package/dist/{studies-Z2ZIWLTO.js → studies-IGOCWM7F.js} +7 -7
- package/dist/{studies-Z2ZIWLTO.js.map → studies-IGOCWM7F.js.map} +1 -1
- package/dist/{study-writes-V7BXLTQG.js → study-writes-Z5P2W7JH.js} +6 -6
- package/dist/study-writes-Z5P2W7JH.js.map +1 -0
- package/dist/{targeting-YBIWD27C.js → targeting-7HXKBXWY.js} +12 -12
- package/dist/targeting-7HXKBXWY.js.map +1 -0
- package/dist/{write-commands-R5SN6CVQ.js → write-commands-CY734U34.js} +22 -22
- package/dist/write-commands-CY734U34.js.map +1 -0
- package/package.json +1 -1
- package/dist/account-assets-TAUEBCY5.js.map +0 -1
- package/dist/account-level-2MYDHOID.js.map +0 -1
- package/dist/ads-NNKUR4HG.js.map +0 -1
- package/dist/adsets-VO5C7PDM.js.map +0 -1
- package/dist/audience-writes-QDDADCYE.js.map +0 -1
- package/dist/audiences-TQ5ZEWIR.js.map +0 -1
- package/dist/campaigns-IQYYYD2S.js.map +0 -1
- package/dist/catalogs-PLBY32BE.js.map +0 -1
- package/dist/chunk-5LN7GWRE.js.map +0 -1
- package/dist/chunk-HPAGYPAF.js.map +0 -1
- package/dist/chunk-OEJNZSJG.js.map +0 -1
- package/dist/creatives-GTRQAZSB.js.map +0 -1
- package/dist/page-photos-SHIGXDYE.js.map +0 -1
- package/dist/pixels-AZCIWWXK.js.map +0 -1
- package/dist/rule-writes-QLZNJ7CT.js.map +0 -1
- package/dist/rules-FJHYG4SJ.js.map +0 -1
- package/dist/study-writes-V7BXLTQG.js.map +0 -1
- package/dist/targeting-YBIWD27C.js.map +0 -1
- package/dist/write-commands-R5SN6CVQ.js.map +0 -1
- /package/dist/{accounts-VIAQC3PA.js.map → accounts-KRW3JNCW.js.map} +0 -0
- /package/dist/{ads-O4RL4JXD.js.map → ads-F2PURRHL.js.map} +0 -0
- /package/dist/{asset-writes-PQNENVYY.js.map → asset-writes-SSI3CO2A.js.map} +0 -0
- /package/dist/{businesses-CW4RB36R.js.map → businesses-DXRDQIFP.js.map} +0 -0
- /package/dist/{chunk-XB2FODWU.js.map → chunk-TKDOVFO6.js.map} +0 -0
- /package/dist/{draft-ME4TNPO7.js.map → draft-G432FJ7N.js.map} +0 -0
- /package/dist/{insights-INIFHYR7.js.map → insights-IVW6W5CP.js.map} +0 -0
- /package/dist/{media-23IW442U.js.map → media-HDHZULQ7.js.map} +0 -0
- /package/dist/{meta-H3KBPU33.js.map → meta-MOSJVYIE.js.map} +0 -0
- /package/dist/{pages-XJF3FBOJ.js.map → pages-BAUS7LQM.js.map} +0 -0
- /package/dist/{preview-BIEVHIDJ.js.map → preview-X6EJGPAY.js.map} +0 -0
- /package/dist/{shared-HY2VP7PT.js.map → shared-I3SB77VH.js.map} +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../api/src/ads-meta/adStudyDescription.ts","../src/commands/ads/meta/studies.ts"],"sourcesContent":["/**\n * A Meta A/B test in words — the one reading shared by the staged summary, the\n * draft review rows and the CLI, so all three say the same thing: what each\n * version runs and how much of the audience it gets, what decides the winner,\n * for how long, and how sure Meta has to be.\n */\nimport type { AdStudyCell, AdStudyObjective } from \"./adStudies\";\n\nexport interface AdStudyWordsInput {\n type?: string;\n start_time?: number;\n end_time?: number;\n confidence_level?: number;\n cells?: readonly Pick<\n AdStudyCell,\n \"name\" | \"treatment_percentage\" | \"control_percentage\" | \"adsets\" | \"campaigns\" | \"ads\"\n >[];\n objectives?: readonly AdStudyObjective[];\n}\n\nexport interface AdStudyWords {\n sentence: string;\n versions?: string;\n measuring: string;\n /** The measures besides the primary one, by name. */\n alsoMeasured?: string;\n schedule?: string;\n confidence?: string;\n}\n\nconst MONTHS = [\"Jan\", \"Feb\", \"Mar\", \"Apr\", \"May\", \"Jun\", \"Jul\", \"Aug\", \"Sep\", \"Oct\", \"Nov\", \"Dec\"];\n\nexport function adStudyTime(seconds: number): string {\n const date = new Date(seconds * 1000);\n const hh = String(date.getUTCHours()).padStart(2, \"0\");\n const mm = String(date.getUTCMinutes()).padStart(2, \"0\");\n return `${date.getUTCDate()} ${MONTHS[date.getUTCMonth()]} ${date.getUTCFullYear()}, ${hh}:${mm} UTC`;\n}\n\nfunction daysLabel(seconds: number): string {\n const days = Math.round((seconds / 86_400) * 10) / 10;\n return `${days} ${days === 1 ? \"day\" : \"days\"}`;\n}\n\nfunction quoted(id: string, names: Record<string, string>): string {\n const name = names[id];\n return name ? `\"${name}\"` : id;\n}\n\nfunction cellWords(\n cell: Pick<AdStudyCell, \"name\" | \"treatment_percentage\" | \"control_percentage\" | \"adsets\" | \"campaigns\" | \"ads\">,\n names: Record<string, string>,\n): string {\n const [noun, ids] = cell.ads\n ? [\"ad\", cell.ads]\n : cell.adsets\n ? [\"ad set\", cell.adsets]\n : [\"campaign\", cell.campaigns ?? []];\n const plural = ids.length > 1 ? `${noun}s` : noun;\n const runs = ids.length > 0 ? ` (${plural} ${ids.map((id) => quoted(id, names)).join(\", \")})` : \"\";\n const share =\n cell.control_percentage === undefined\n ? `${cell.treatment_percentage}%`\n : `${cell.treatment_percentage}% see the ads, ${cell.control_percentage}% held out`;\n return `${cell.name}${runs} ${share}`;\n}\n\nconst STUDY_KIND: Record<string, string> = {\n SPLIT_TEST: \"Split test\",\n LIFT: \"Lift study\",\n SPLIT_TEST_V2: \"Creative test\",\n};\n\nfunction primaryObjective(objectives: readonly AdStudyObjective[] | undefined): AdStudyObjective | undefined {\n if (!objectives || objectives.length === 0) {\n return undefined;\n }\n return objectives.find((objective) => objective.is_primary === true) ?? objectives[0];\n}\n\nconst SOURCE_NOUN: Record<\"adspixels\" | \"applications\" | \"offsite_datasets\", string> = {\n adspixels: \"pixel\",\n applications: \"app\",\n offsite_datasets: \"dataset\",\n};\n\n/** \"Lead on pixel \"Site\"\", one per event source. */\nfunction sourceWords(objective: AdStudyObjective, names: Record<string, string>): string[] {\n const words: string[] = [];\n for (const key of [\"adspixels\", \"applications\", \"offsite_datasets\"] as const) {\n for (const source of objective[key] ?? []) {\n const events = source.event_names?.join(\" or \") ?? \"any event\";\n words.push(`${events} on ${SOURCE_NOUN[key]} ${quoted(source.id, names)}`);\n }\n }\n for (const source of objective.customconversions ?? []) {\n words.push(`custom conversion ${quoted(source.id, names)}`);\n }\n return words;\n}\n\n/** What decides the winner. Meta's split test compares cost per result of the primary measure. */\nfunction measuringWords(objectives: readonly AdStudyObjective[] | undefined, names: Record<string, string>): string {\n const primary = primaryObjective(objectives);\n if (!primary) {\n return \"cost per result on the campaigns' objective\";\n }\n const sources = sourceWords(primary, names);\n return `cost per \"${primary.name}\"${sources.length > 0 ? ` (${sources.join(\", \")})` : \"\"}`;\n}\n\n/** What the sentence says the study measures: a lift study reads the lift, a test the cost per result. */\nfunction measuringPhrase(type: string | undefined, primary: AdStudyObjective | undefined, measuring: string): string {\n if (type === \"LIFT\") return `measuring the lift in ${primary ? `\"${primary.name}\"` : \"results\"}`;\n return `measuring ${primary ? `cost per \"${primary.name}\"` : measuring}`;\n}\n\nexport function describeAdStudy(\n study: AdStudyWordsInput,\n { names = {} }: { names?: Record<string, string> } = {},\n): AdStudyWords {\n const versions =\n study.cells && study.cells.length > 0 ? study.cells.map((cell) => cellWords(cell, names)).join(\" vs \") : undefined;\n const measuring = measuringWords(study.objectives, names);\n const primary = primaryObjective(study.objectives);\n const others = (study.objectives ?? []).filter((objective) => objective !== primary);\n const alsoMeasured = others.length > 0 ? others.map((objective) => `\"${objective.name}\"`).join(\", \") : undefined;\n const hasSchedule = study.start_time !== undefined && study.end_time !== undefined;\n const length = hasSchedule ? daysLabel((study.end_time ?? 0) - (study.start_time ?? 0)) : undefined;\n const schedule = hasSchedule\n ? `${adStudyTime(study.start_time ?? 0)} → ${adStudyTime(study.end_time ?? 0)} · ${length}`\n : undefined;\n const confidence = study.confidence_level !== undefined ? `${Math.round(study.confidence_level * 100)}%` : undefined;\n const parts = [\n versions,\n measuringPhrase(study.type, primary, measuring),\n length,\n confidence ? `${confidence} confidence` : undefined,\n ].filter((part): part is string => part !== undefined);\n const kind = STUDY_KIND[study.type ?? \"SPLIT_TEST\"] ?? \"Split test\";\n return { sentence: `${kind}: ${parts.join(\", \")}`, versions, measuring, alsoMeasured, schedule, confidence };\n}\n","import { adStudyFromWire, cellObjects, unixSeconds } from \"@baker/api/ads-meta/adStudies\";\nimport { adStudyTime, describeAdStudy } from \"@baker/api/ads-meta/adStudyDescription\";\nimport { defineCommand } from \"citty\";\nimport { NO_CACHE_ARG } from \"../../../cache-flag.ts\";\nimport { apiGet } from \"../../../client.ts\";\nimport { registerSchema } from \"../../../schemas.ts\";\nimport { writeJsonEnvelope } from \"../output.ts\";\nimport { handleMetaError, resolveAccountIdArg } from \"./shared.ts\";\n\n/**\n * Meta A/B tests (ad studies) — reads. A test splits one audience between\n * versions (ad sets, campaigns, or a creative test's ads) so each person sees one, and Meta calls the\n * winner. Writes (create / update / end / delete) stage like every Meta change\n * and live in `study-writes.ts`.\n */\n\ntype Row = Record<string, unknown>;\n\nexport type StudyState = \"scheduled\" | \"running\" | \"ended\" | \"cancelled\";\n\nexport function studyState(row: Row, nowSeconds: number): StudyState {\n if (row.canceled_time) return \"cancelled\";\n const start = unixSeconds(row.start_time) ?? 0;\n const end = unixSeconds(row.end_time) ?? Number.POSITIVE_INFINITY;\n if (nowSeconds < start) return \"scheduled\";\n return nowSeconds < end ? \"running\" : \"ended\";\n}\n\n/** What an agent needs to pick a test and know what it compares. The raw node sits behind --full. */\nexport function compactStudy(row: Row, nowSeconds: number): Row {\n const study = adStudyFromWire(row);\n const words = describeAdStudy(study, { names: study.names });\n return {\n id: study.id,\n name: study.name,\n state: studyState(row, nowSeconds),\n compares: words.versions,\n ...(study.start_time !== undefined ? { starts: adStudyTime(study.start_time) } : {}),\n ...(study.end_time !== undefined ? { ends: adStudyTime(study.end_time) } : {}),\n ...(row.results_first_available_date ? { results_from: row.results_first_available_date } : {}),\n };\n}\n\n/** One test in full: each version with its cell id (what `studies update --split` names), each measure with Meta's results. */\nexport function studyDetail(row: Row, nowSeconds: number): Row {\n const study = adStudyFromWire(row);\n const measures = ((row.objectives as { data?: Row[] } | undefined)?.data ?? []).map((objective) => ({\n id: objective.id,\n name: objective.name,\n primary: objective.is_primary === true,\n ...(objective.results !== undefined ? { results: objective.results } : {}),\n }));\n return {\n ...compactStudy(row, nowSeconds),\n ...(study.description ? { description: study.description } : {}),\n versions: study.cells.map((cell) => ({\n cell_id: cell.id,\n name: cell.name,\n share: `${cell.treatment_percentage}%`,\n runs: cellObjects(cell).map((id) => ({ id, name: study.names[id] })),\n level: cell.ads ? \"ad\" : cell.adsets ? \"ad set\" : \"campaign\",\n })),\n measures,\n };\n}\n\nexport function studyHints(detail: Row): string[] {\n const state = detail.state as StudyState;\n if (state === \"scheduled\") {\n return [\n \"Not started: it can still be re-timed, re-split (`studies update --split <cell_id>=60 …`) or deleted. Results appear once it runs.\",\n ];\n }\n const measures = (detail.measures as Row[] | undefined) ?? [];\n const hasResults = measures.some((measure) => measure.results !== undefined);\n return [\n hasResults\n ? \"Meta's verdict is in each measure's results, with its confidence. Report a winner only when Meta names one at the test's confidence level; otherwise say the versions performed alike.\"\n : \"No results yet — Meta publishes them once the test has gathered enough. Compare per-version delivery meanwhile with `baker ads meta insights --object <ad set or campaign id>`, and do not call a winner from it.\",\n state === \"running\"\n ? \"Running: its split and start are fixed; it can be renamed, its end moved, ended now (`studies end`) or deleted (results and all).\"\n : \"Over. Read the per-version numbers with `baker ads meta insights --object <id>` for the period it ran.\",\n ];\n}\n\nregisterSchema({\n command: \"ads.meta.studies\",\n description:\n \"Meta A/B tests (ad studies) whose versions run in an ad account — what each compares, when it runs and its state (scheduled, running, ended). --study-id reads one: every version with its cell id and share, and each measure with Meta's results. --business-id lists every test on a Business. Stage new ones with `studies create`.\",\n args: {\n \"account-id\": { type: \"string\", description: \"Meta ad account ID (act_123)\", required: false },\n \"business-id\": { type: \"string\", description: \"List every test on this Business instead\", required: false },\n \"study-id\": { type: \"string\", description: \"One test: its versions and results\", required: false },\n full: { type: \"boolean\", description: \"The tests as Meta returns them\", required: false },\n \"no-cache\": { type: \"boolean\", description: \"Bypass the cache\", required: false },\n },\n});\n\nconst listArgs = {\n \"account-id\": { type: \"string\", description: \"Meta ad account ID\" },\n \"business-id\": { type: \"string\", description: \"List every test on this Business instead\" },\n \"study-id\": { type: \"string\", description: \"Read one test: versions, cell ids, results\" },\n full: { type: \"boolean\", description: \"The tests as Meta returns them\" },\n \"no-cache\": NO_CACHE_ARG,\n} as const;\n\nasync function read<T>(path: string, params: Record<string, string>, noCache: unknown): Promise<T> {\n if (noCache) params[\"skip-cache\"] = \"true\";\n return await apiGet<T>(path, params);\n}\n\nconst listCommand = defineCommand({\n meta: {\n name: \"list\",\n description:\n \"List A/B tests. Runs by default. Example: baker ads meta studies --account-id act_123 · one test: baker ads meta studies --study-id 6123\",\n },\n args: listArgs,\n run: async ({ args }) => {\n const now = Math.floor(Date.now() / 1000);\n try {\n if (args[\"study-id\"]) {\n const row = await read<Row>(\n \"/api/ads/meta/study-results\",\n { \"study-id\": String(args[\"study-id\"]) },\n args[\"no-cache\"],\n );\n const detail = studyDetail(row, now);\n writeJsonEnvelope({ ok: true, data: args.full ? row : detail, hints: studyHints(detail) });\n return;\n }\n const params: Record<string, string> = args[\"business-id\"]\n ? { \"business-id\": String(args[\"business-id\"]) }\n : { \"account-id\": resolveAccountIdArg(args) };\n const rows = await read<Row[]>(\"/api/ads/meta/studies\", params, args[\"no-cache\"]);\n const hints =\n rows.length === 0\n ? [\n \"No A/B tests. To set one up: duplicate the ad set (`adsets duplicate`), change ONE thing on the copy, then `studies create --adsets <original>,<copy>`.\",\n ]\n : [\"`studies --study-id <id>` reads one test's versions and results.\"];\n writeJsonEnvelope({ ok: true, data: args.full ? rows : rows.map((row) => compactStudy(row, now)), hints });\n } catch (err) {\n handleMetaError(err);\n }\n },\n});\n\nexport const studiesCommand = defineCommand({\n meta: {\n name: \"studies\",\n description: `Meta A/B tests (ad studies): Meta splits one audience between versions — ad sets or campaigns — so each person sees one, and calls the winner.\nNot Baker's own page experiments (\\`baker experiment\\`), which test landing pages.\n\nExamples:\n baker ads meta studies --account-id act_123 # tests on this account and their state\n baker ads meta studies --study-id 6123 # versions, cell ids, Meta's results\n baker ads meta studies create --name \"Broad vs lookalike\" --adsets 6123,6124 --start 2026-10-20T08:00Z --days 14 --confidence 90\n baker ads meta studies update 6123 --end 2026-11-10T08:00Z\n baker ads meta studies end 6123 | delete 6123`,\n },\n subCommands: {\n list: listCommand,\n create: () => import(\"./study-writes.ts\").then((m) => m.studiesCreateCommand),\n update: () => import(\"./study-writes.ts\").then((m) => m.studiesUpdateCommand),\n end: () => import(\"./study-writes.ts\").then((m) => m.studiesEndCommand),\n delete: () => import(\"./study-writes.ts\").then((m) => m.studiesDeleteCommand),\n },\n args: listArgs,\n default: \"list\",\n});\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../api/src/ads-meta/adStudyDescription.ts","../src/commands/ads/meta/studies.ts"],"sourcesContent":["/**\n * A Meta A/B test in words — the one reading shared by the staged summary, the\n * draft review rows and the CLI, so all three say the same thing: what each\n * version runs and how much of the audience it gets, what decides the winner,\n * for how long, and how sure Meta has to be.\n */\nimport type { AdStudyCell, AdStudyObjective } from \"./adStudies\";\n\nexport interface AdStudyWordsInput {\n type?: string;\n start_time?: number;\n end_time?: number;\n confidence_level?: number;\n cells?: readonly Pick<\n AdStudyCell,\n \"name\" | \"treatment_percentage\" | \"control_percentage\" | \"adsets\" | \"campaigns\" | \"ads\"\n >[];\n objectives?: readonly AdStudyObjective[];\n}\n\nexport interface AdStudyWords {\n sentence: string;\n versions?: string;\n measuring: string;\n /** The measures besides the primary one, by name. */\n alsoMeasured?: string;\n schedule?: string;\n confidence?: string;\n}\n\nconst MONTHS = [\"Jan\", \"Feb\", \"Mar\", \"Apr\", \"May\", \"Jun\", \"Jul\", \"Aug\", \"Sep\", \"Oct\", \"Nov\", \"Dec\"];\n\nexport function adStudyTime(seconds: number): string {\n const date = new Date(seconds * 1000);\n const hh = String(date.getUTCHours()).padStart(2, \"0\");\n const mm = String(date.getUTCMinutes()).padStart(2, \"0\");\n return `${date.getUTCDate()} ${MONTHS[date.getUTCMonth()]} ${date.getUTCFullYear()}, ${hh}:${mm} UTC`;\n}\n\nfunction daysLabel(seconds: number): string {\n const days = Math.round((seconds / 86_400) * 10) / 10;\n return `${days} ${days === 1 ? \"day\" : \"days\"}`;\n}\n\nfunction quoted(id: string, names: Record<string, string>): string {\n const name = names[id];\n return name ? `\"${name}\"` : id;\n}\n\nfunction cellWords(\n cell: Pick<AdStudyCell, \"name\" | \"treatment_percentage\" | \"control_percentage\" | \"adsets\" | \"campaigns\" | \"ads\">,\n names: Record<string, string>,\n): string {\n const [noun, ids] = cell.ads\n ? [\"ad\", cell.ads]\n : cell.adsets\n ? [\"ad set\", cell.adsets]\n : [\"campaign\", cell.campaigns ?? []];\n const plural = ids.length > 1 ? `${noun}s` : noun;\n const runs = ids.length > 0 ? ` (${plural} ${ids.map((id) => quoted(id, names)).join(\", \")})` : \"\";\n const share =\n cell.control_percentage === undefined\n ? `${cell.treatment_percentage}%`\n : `${cell.treatment_percentage}% see the ads, ${cell.control_percentage}% held out`;\n return `${cell.name}${runs} ${share}`;\n}\n\nconst STUDY_KIND: Record<string, string> = {\n SPLIT_TEST: \"Split test\",\n LIFT: \"Lift study\",\n SPLIT_TEST_V2: \"Creative test\",\n};\n\nfunction primaryObjective(objectives: readonly AdStudyObjective[] | undefined): AdStudyObjective | undefined {\n if (!objectives || objectives.length === 0) {\n return undefined;\n }\n return objectives.find((objective) => objective.is_primary === true) ?? objectives[0];\n}\n\nconst SOURCE_NOUN: Record<\"adspixels\" | \"applications\" | \"offsite_datasets\", string> = {\n adspixels: \"pixel\",\n applications: \"app\",\n offsite_datasets: \"dataset\",\n};\n\n/** \"Lead on pixel \"Site\"\", one per event source. */\nfunction sourceWords(objective: AdStudyObjective, names: Record<string, string>): string[] {\n const words: string[] = [];\n for (const key of [\"adspixels\", \"applications\", \"offsite_datasets\"] as const) {\n for (const source of objective[key] ?? []) {\n const events = source.event_names?.join(\" or \") ?? \"any event\";\n words.push(`${events} on ${SOURCE_NOUN[key]} ${quoted(source.id, names)}`);\n }\n }\n for (const source of objective.customconversions ?? []) {\n words.push(`custom conversion ${quoted(source.id, names)}`);\n }\n return words;\n}\n\n/** What decides the winner. Meta's split test compares cost per result of the primary measure. */\nfunction measuringWords(objectives: readonly AdStudyObjective[] | undefined, names: Record<string, string>): string {\n const primary = primaryObjective(objectives);\n if (!primary) {\n return \"cost per result on the campaigns' objective\";\n }\n const sources = sourceWords(primary, names);\n return `cost per \"${primary.name}\"${sources.length > 0 ? ` (${sources.join(\", \")})` : \"\"}`;\n}\n\n/** What the sentence says the study measures: a lift study reads the lift, a test the cost per result. */\nfunction measuringPhrase(type: string | undefined, primary: AdStudyObjective | undefined, measuring: string): string {\n if (type === \"LIFT\") return `measuring the lift in ${primary ? `\"${primary.name}\"` : \"results\"}`;\n return `measuring ${primary ? `cost per \"${primary.name}\"` : measuring}`;\n}\n\nexport function describeAdStudy(\n study: AdStudyWordsInput,\n { names = {} }: { names?: Record<string, string> } = {},\n): AdStudyWords {\n const versions =\n study.cells && study.cells.length > 0 ? study.cells.map((cell) => cellWords(cell, names)).join(\" vs \") : undefined;\n const measuring = measuringWords(study.objectives, names);\n const primary = primaryObjective(study.objectives);\n const others = (study.objectives ?? []).filter((objective) => objective !== primary);\n const alsoMeasured = others.length > 0 ? others.map((objective) => `\"${objective.name}\"`).join(\", \") : undefined;\n const hasSchedule = study.start_time !== undefined && study.end_time !== undefined;\n const length = hasSchedule ? daysLabel((study.end_time ?? 0) - (study.start_time ?? 0)) : undefined;\n const schedule = hasSchedule\n ? `${adStudyTime(study.start_time ?? 0)} → ${adStudyTime(study.end_time ?? 0)} · ${length}`\n : undefined;\n const confidence = study.confidence_level !== undefined ? `${Math.round(study.confidence_level * 100)}%` : undefined;\n const parts = [\n versions,\n measuringPhrase(study.type, primary, measuring),\n length,\n confidence ? `${confidence} confidence` : undefined,\n ].filter((part): part is string => part !== undefined);\n const kind = STUDY_KIND[study.type ?? \"SPLIT_TEST\"] ?? \"Split test\";\n return { sentence: `${kind}: ${parts.join(\", \")}`, versions, measuring, alsoMeasured, schedule, confidence };\n}\n","import { adStudyFromWire, cellObjects, unixSeconds } from \"@baker/api/ads-meta/adStudies\";\nimport { adStudyTime, describeAdStudy } from \"@baker/api/ads-meta/adStudyDescription\";\nimport { defineCommand } from \"citty\";\nimport { NO_CACHE_ARG } from \"../../../cache-flag.ts\";\nimport { apiGet } from \"../../../client.ts\";\nimport { registerSchema } from \"../../../schemas.ts\";\nimport { writeJsonEnvelope } from \"../output.ts\";\nimport { handleMetaError, resolveAccountIdArg } from \"./shared.ts\";\n\n/**\n * Meta A/B tests (ad studies) — reads. A test splits one audience between\n * versions (ad sets, campaigns, or a creative test's ads) so each person sees one, and Meta calls the\n * winner. Writes (create / update / end / delete) stage like every Meta change\n * and live in `study-writes.ts`.\n */\n\ntype Row = Record<string, unknown>;\n\nexport type StudyState = \"scheduled\" | \"running\" | \"ended\" | \"cancelled\";\n\nexport function studyState(row: Row, nowSeconds: number): StudyState {\n if (row.canceled_time) return \"cancelled\";\n const start = unixSeconds(row.start_time) ?? 0;\n const end = unixSeconds(row.end_time) ?? Number.POSITIVE_INFINITY;\n if (nowSeconds < start) return \"scheduled\";\n return nowSeconds < end ? \"running\" : \"ended\";\n}\n\n/** What an agent needs to pick a test and know what it compares. The raw node sits behind --full. */\nexport function compactStudy(row: Row, nowSeconds: number): Row {\n const study = adStudyFromWire(row);\n const words = describeAdStudy(study, { names: study.names });\n return {\n id: study.id,\n name: study.name,\n state: studyState(row, nowSeconds),\n compares: words.versions,\n ...(study.start_time !== undefined ? { starts: adStudyTime(study.start_time) } : {}),\n ...(study.end_time !== undefined ? { ends: adStudyTime(study.end_time) } : {}),\n ...(row.results_first_available_date ? { results_from: row.results_first_available_date } : {}),\n };\n}\n\n/** One test in full: each version with its cell id (what `studies update --split` names), each measure with Meta's results. */\nexport function studyDetail(row: Row, nowSeconds: number): Row {\n const study = adStudyFromWire(row);\n const measures = ((row.objectives as { data?: Row[] } | undefined)?.data ?? []).map((objective) => ({\n id: objective.id,\n name: objective.name,\n primary: objective.is_primary === true,\n ...(objective.results !== undefined ? { results: objective.results } : {}),\n }));\n return {\n ...compactStudy(row, nowSeconds),\n ...(study.description ? { description: study.description } : {}),\n versions: study.cells.map((cell) => ({\n cell_id: cell.id,\n name: cell.name,\n share: `${cell.treatment_percentage}%`,\n runs: cellObjects(cell).map((id) => ({ id, name: study.names[id] })),\n level: cell.ads ? \"ad\" : cell.adsets ? \"ad set\" : \"campaign\",\n })),\n measures,\n };\n}\n\nexport function studyHints(detail: Row): string[] {\n const state = detail.state as StudyState;\n if (state === \"scheduled\") {\n return [\n \"Not started: it can still be re-timed, re-split (`studies update --split <cell_id>=60 …`) or deleted. Results appear once it runs.\",\n ];\n }\n const measures = (detail.measures as Row[] | undefined) ?? [];\n const hasResults = measures.some((measure) => measure.results !== undefined);\n return [\n hasResults\n ? \"Meta's verdict is in each measure's results, with its confidence. Report a winner only when Meta names one at the test's confidence level; otherwise say the versions performed alike.\"\n : \"No results yet — Meta publishes them once the test has gathered enough. Compare per-version delivery meanwhile with `baker ads meta insights --object <ad set or campaign id>`, and do not call a winner from it.\",\n state === \"running\"\n ? \"Running: its split and start are fixed; it can be renamed, its end moved, ended now (`studies end`) or deleted (results and all).\"\n : \"Over. Read the per-version numbers with `baker ads meta insights --object <id>` for the period it ran.\",\n ];\n}\n\nregisterSchema({\n command: \"ads.meta.studies\",\n description:\n \"Meta A/B tests (ad studies) whose versions run in an ad account — what each compares, when it runs and its state (scheduled, running, ended). --study-id reads one: every version with its cell id and share, and each measure with Meta's results. --business-id lists every test on a Business. Stage new ones with `studies create`.\",\n args: {\n \"account-id\": { type: \"string\", description: \"Meta ad account ID (act_123)\", required: false },\n \"business-id\": { type: \"string\", description: \"List every test on this Business instead\", required: false },\n \"study-id\": { type: \"string\", description: \"One test: its versions and results\", required: false },\n full: { type: \"boolean\", description: \"The tests as Meta returns them\", required: false },\n \"no-cache\": { type: \"boolean\", description: \"Bypass the cache\", required: false },\n },\n});\n\nconst listArgs = {\n \"account-id\": { type: \"string\", description: \"Meta ad account ID\" },\n \"business-id\": { type: \"string\", description: \"List every test on this Business instead\" },\n \"study-id\": { type: \"string\", description: \"Read one test: versions, cell ids, results\" },\n full: { type: \"boolean\", description: \"The tests as Meta returns them\" },\n \"no-cache\": NO_CACHE_ARG,\n} as const;\n\nasync function read<T>(path: string, params: Record<string, string>, noCache: unknown): Promise<T> {\n if (noCache) params[\"skip-cache\"] = \"true\";\n return await apiGet<T>(path, params);\n}\n\nconst listCommand = defineCommand({\n meta: {\n name: \"list\",\n description:\n \"List A/B tests. Runs by default. Example: baker ads meta studies --account-id act_123 · one test: baker ads meta studies --study-id 6123\",\n },\n args: listArgs,\n run: async ({ args }) => {\n const now = Math.floor(Date.now() / 1000);\n try {\n if (args[\"study-id\"]) {\n const row = await read<Row>(\n \"/api/ads/meta/study-results\",\n { \"study-id\": String(args[\"study-id\"]) },\n args[\"no-cache\"],\n );\n const detail = studyDetail(row, now);\n writeJsonEnvelope({ ok: true, data: args.full ? row : detail, hints: studyHints(detail) });\n return;\n }\n const params: Record<string, string> = args[\"business-id\"]\n ? { \"business-id\": String(args[\"business-id\"]) }\n : { \"account-id\": await resolveAccountIdArg(args) };\n const rows = await read<Row[]>(\"/api/ads/meta/studies\", params, args[\"no-cache\"]);\n const hints =\n rows.length === 0\n ? [\n \"No A/B tests. To set one up: duplicate the ad set (`adsets duplicate`), change ONE thing on the copy, then `studies create --adsets <original>,<copy>`.\",\n ]\n : [\"`studies --study-id <id>` reads one test's versions and results.\"];\n writeJsonEnvelope({ ok: true, data: args.full ? rows : rows.map((row) => compactStudy(row, now)), hints });\n } catch (err) {\n handleMetaError(err);\n }\n },\n});\n\nexport const studiesCommand = defineCommand({\n meta: {\n name: \"studies\",\n description: `Meta A/B tests (ad studies): Meta splits one audience between versions — ad sets or campaigns — so each person sees one, and calls the winner.\nNot Baker's own page experiments (\\`baker experiment\\`), which test landing pages.\n\nExamples:\n baker ads meta studies --account-id act_123 # tests on this account and their state\n baker ads meta studies --study-id 6123 # versions, cell ids, Meta's results\n baker ads meta studies create --name \"Broad vs lookalike\" --adsets 6123,6124 --start 2026-10-20T08:00Z --days 14 --confidence 90\n baker ads meta studies update 6123 --end 2026-11-10T08:00Z\n baker ads meta studies end 6123 | delete 6123`,\n },\n subCommands: {\n list: listCommand,\n create: () => import(\"./study-writes.ts\").then((m) => m.studiesCreateCommand),\n update: () => import(\"./study-writes.ts\").then((m) => m.studiesUpdateCommand),\n end: () => import(\"./study-writes.ts\").then((m) => m.studiesEndCommand),\n delete: () => import(\"./study-writes.ts\").then((m) => m.studiesDeleteCommand),\n },\n args: listArgs,\n default: \"list\",\n});\n"],"mappings":"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","names":[]}
|
|
@@ -4,10 +4,10 @@ import {
|
|
|
4
4
|
loadJsonFileArg,
|
|
5
5
|
requireTarget,
|
|
6
6
|
stageOp
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-4XXA4WCS.js";
|
|
8
8
|
import "./chunk-GC5P4KWY.js";
|
|
9
9
|
import "./chunk-GDFJOCDO.js";
|
|
10
|
-
import "./chunk-
|
|
10
|
+
import "./chunk-BN4TDL5L.js";
|
|
11
11
|
import {
|
|
12
12
|
repeatedValues
|
|
13
13
|
} from "./chunk-ERSX3MKH.js";
|
|
@@ -371,7 +371,7 @@ Examples:
|
|
|
371
371
|
},
|
|
372
372
|
args: createArgs,
|
|
373
373
|
run: async ({ args, rawArgs }) => {
|
|
374
|
-
const accountId = bareAccountId(args);
|
|
374
|
+
const accountId = await bareAccountId(args);
|
|
375
375
|
const file = loadJsonFileArg(args.file);
|
|
376
376
|
const ignored = Object.keys(file).length > 0 ? studyFileIgnoredFlags(args) : [];
|
|
377
377
|
if (ignored.length > 0) {
|
|
@@ -412,7 +412,7 @@ Example: baker ads meta studies update 6123 --end 2026-11-10T08:00Z`
|
|
|
412
412
|
},
|
|
413
413
|
args: updateArgs,
|
|
414
414
|
run: async ({ args, rawArgs }) => {
|
|
415
|
-
const accountId = bareAccountId(args);
|
|
415
|
+
const accountId = await bareAccountId(args);
|
|
416
416
|
const target = requireTarget(args, "test");
|
|
417
417
|
const payload = orFail(() => studyUpdateFromFlags(studyFlagsFromArgs(args, rawArgs)));
|
|
418
418
|
if (Object.keys(payload).length === 0) {
|
|
@@ -448,7 +448,7 @@ function actionCommand(name) {
|
|
|
448
448
|
"account-id": createArgs["account-id"]
|
|
449
449
|
},
|
|
450
450
|
run: async ({ args }) => {
|
|
451
|
-
const accountId = bareAccountId(args);
|
|
451
|
+
const accountId = await bareAccountId(args);
|
|
452
452
|
await stageOp({
|
|
453
453
|
kind: "adStudy.update",
|
|
454
454
|
accountId,
|
|
@@ -467,4 +467,4 @@ export {
|
|
|
467
467
|
studiesUpdateCommand,
|
|
468
468
|
studyFlagsFromArgs
|
|
469
469
|
};
|
|
470
|
-
//# sourceMappingURL=study-writes-
|
|
470
|
+
//# sourceMappingURL=study-writes-Z5P2W7JH.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/ads/meta/study-writes.ts","../src/commands/ads/meta/study-flags.ts"],"sourcesContent":["import { defineCommand } from \"citty\";\nimport { repeatedValues } from \"../../../lib/repeatedFlags.ts\";\nimport { registerSchema } from \"../../../schemas.ts\";\nimport { type StudyFlags, studyCreateFromFlags, studyFileIgnoredFlags, studyUpdateFromFlags } from \"./study-flags.ts\";\nimport { bareAccountId, failWriteValidation, loadJsonFileArg, requireTarget, stageOp } from \"./write-shared.ts\";\n\n/**\n * Staged Meta A/B test writes. Nothing here reaches Meta: each command stages\n * an op on this chat's draft, and the test is created, changed, ended or\n * deleted when the chat publishes — after the ad sets or campaigns it names\n * that the same draft creates.\n */\n\nconst STAGED_NOTE = \"Staged until publish — review with `baker ads meta draft`.\";\n\nconst timeArgs = {\n start: {\n type: \"string\",\n description:\n \"When the test begins: ISO date/time (2026-10-20T08:00Z) or epoch seconds — in the future, after this chat publishes\",\n },\n end: { type: \"string\", description: \"When it ends (ISO or epoch seconds). Or --days\" },\n days: { type: \"string\", description: \"How long it runs, from --start: 1–30 days (Meta recommends at least 7)\" },\n} as const;\n\nconst createArgs = {\n \"account-id\": { type: \"string\", description: \"Numeric account ID or act_N\" },\n name: { type: \"string\", description: \"Test name, shown in Ads Manager → Experiments\" },\n description: { type: \"string\", description: \"What the test answers, e.g. which audience brings cheaper leads\" },\n adsets: {\n type: \"string\",\n description:\n \"One ad set per version (repeatable or comma-separated; meta_temp_* refs from this chat work) — ad sets that differ in ONE thing\",\n },\n campaigns: { type: \"string\", description: \"One campaign per version instead (repeatable or comma-separated)\" },\n \"version-names\": {\n type: \"string\",\n description:\n 'A name for each version, in order — repeat once per version: --version-names Broad --version-names \"Lookalike, 1%\" (default Version A, Version B, …; --type lift takes one, for its group)',\n },\n split: {\n type: \"string\",\n description:\n \"Each version's share of the audience in %, in order, adding up to 100 (default even). Not for --type lift, which splits by --holdout\",\n },\n ...timeArgs,\n confidence: { type: \"string\", description: \"How sure Meta must be to call a winner: 80, 90 or 95 (%)\" },\n \"measure-pixel\": {\n type: \"string\",\n description: \"Decide on cost per pixel event: the pixel id (with --measure-event)\",\n },\n \"measure-event\": {\n type: \"string\",\n description: \"The pixel event that counts: Lead, Purchase, CompleteRegistration, …\",\n },\n \"measure-conversion\": { type: \"string\", description: \"Decide on cost per custom conversion instead: its id\" },\n \"measure-name\": { type: \"string\", description: \"What to call the measure in the review (default: the event)\" },\n type: {\n type: \"string\",\n description:\n \"split (default: A/B test of ad sets or campaigns) | lift (a held-out group shows what the ads add — needs a measure) | creative (2–5 ads of one ad set, --ads)\",\n },\n ads: { type: \"string\", description: \"--type creative: one ad per version (comma-separated or repeatable)\" },\n holdout: { type: \"string\", description: \"--type lift: % of the audience that never sees the ads (default 10)\" },\n \"test-daily-budget\": {\n type: \"string\",\n description: \"--type creative: the part of the ad set's daily budget the test spends, e.g. 2\",\n },\n \"observe-until\": {\n type: \"string\",\n description:\n \"--type lift or creative: count results until this time (ISO) — after the end, for late conversions (default the end)\",\n },\n file: {\n type: \"string\",\n description:\n \"JSON with the full test in Meta's shape — {name, start_time, end_time, cells:[{name, treatment_percentage, adsets|campaigns}], objectives, confidence_level}, times in unix seconds. Needed for several ad sets per version. --name overrides its name; any other flag beside --file is refused.\",\n },\n} as const;\n\nfunction schemaArgs(\n args: Record<string, { type: string; description: string }>,\n): Record<string, { type: string; description: string; required: boolean }> {\n return Object.fromEntries(Object.entries(args).map(([key, arg]) => [key, { ...arg, required: false }]));\n}\n\nfunction listValues(rawArgs: readonly string[] | undefined, flag: string, parsed: unknown): string[] | undefined {\n const values = repeatedValues(rawArgs, flag, parsed)\n .flatMap((value) => value.split(\",\"))\n .map((value) => value.trim())\n .filter(Boolean);\n return values.length > 0 ? values : undefined;\n}\n\n/** A free-text flag, one value per occurrence and never split: a version's name may hold a comma. */\nfunction wholeValues(rawArgs: readonly string[] | undefined, flag: string, parsed: unknown): string[] | undefined {\n const values = repeatedValues(rawArgs, flag, parsed)\n .map((value) => value.trim())\n .filter(Boolean);\n return values.length > 0 ? values : undefined;\n}\n\n/** The command line as study flags, every repeatable flag read whole (citty keeps only the last). */\nexport function studyFlagsFromArgs(args: Record<string, unknown>, rawArgs: readonly string[] | undefined): StudyFlags {\n const text = (key: string) => (typeof args[key] === \"string\" && args[key] !== \"\" ? String(args[key]) : undefined);\n return {\n name: text(\"name\"),\n description: text(\"description\"),\n adsets: listValues(rawArgs, \"adsets\", args.adsets),\n campaigns: listValues(rawArgs, \"campaigns\", args.campaigns),\n \"version-names\": wholeValues(rawArgs, \"version-names\", args[\"version-names\"]),\n split: listValues(rawArgs, \"split\", args.split),\n start: text(\"start\"),\n end: text(\"end\"),\n days: text(\"days\"),\n confidence: text(\"confidence\"),\n \"measure-name\": text(\"measure-name\"),\n \"measure-pixel\": text(\"measure-pixel\"),\n \"measure-event\": listValues(rawArgs, \"measure-event\", args[\"measure-event\"]),\n \"measure-conversion\": text(\"measure-conversion\"),\n type: text(\"type\"),\n ads: listValues(rawArgs, \"ads\", args.ads),\n holdout: text(\"holdout\"),\n \"test-daily-budget\": text(\"test-daily-budget\"),\n \"observe-until\": text(\"observe-until\"),\n };\n}\n\nfunction orFail<T>(build: () => T): T {\n try {\n return build();\n } catch (err) {\n failWriteValidation(err instanceof Error ? err.message : String(err));\n }\n}\n\nregisterSchema({\n command: \"ads.meta.studies.create\",\n description:\n \"Stage a Meta A/B test (split test) — created at publish. Meta splits the audience so each person sees one version and calls the winner. Shorthand: --adsets <a>,<b> (or --campaigns) --start <ISO> --days 14 [--split 50,50] [--version-names …] [--confidence 90] [--measure-pixel <id> --measure-event Lead | --measure-conversion <id>]. Without a measure Meta decides on cost per result of the campaigns' objective. Or --file with Meta's shape. --type lift: a held-out group (--holdout, default 10%) shows what the --campaigns/--adsets add, needs a measure. --type creative: 2–5 --ads of one ad set compared on --test-daily-budget.\",\n args: schemaArgs(createArgs),\n});\n\nexport const studiesCreateCommand = defineCommand({\n meta: {\n name: \"create\",\n description: `Stage a Meta A/B test. ${STAGED_NOTE}\nSet it up properly: versions that differ in ONE thing (duplicate the ad set with \\`adsets duplicate\\`, change the audience, placement or creative on the copy), each with its own budget, none in another running test.\nExamples:\n baker ads meta studies create --name \"Broad vs lookalike\" --adsets 6123,6124 --start 2026-10-20T08:00Z --days 14\n baker ads meta studies create --name \"Lead cost\" --adsets 6123,meta_temp_ab12 --start 2026-10-20T08:00Z --days 14 --confidence 90 --measure-pixel 1275507706837548 --measure-event Lead\n baker ads meta studies create --file test.json`,\n },\n args: createArgs,\n run: async ({ args, rawArgs }) => {\n const accountId = await bareAccountId(args as Record<string, unknown>);\n const file = loadJsonFileArg(args.file);\n const ignored = Object.keys(file).length > 0 ? studyFileIgnoredFlags(args as Record<string, unknown>) : [];\n if (ignored.length > 0) {\n failWriteValidation(\n `--file holds the whole test, so ${ignored.join(\", \")} would be ignored — put them in the file, or drop --file`,\n );\n }\n const flags = studyFlagsFromArgs(args, rawArgs);\n const payload =\n Object.keys(file).length > 0\n ? { ...file, ...(flags.name ? { name: flags.name } : {}) }\n : orFail(() => studyCreateFromFlags(flags));\n await stageOp({ kind: \"adStudy.create\", accountId, payload });\n },\n});\n\nconst updateArgs = {\n id: { type: \"positional\", description: \"Test id (or a meta_temp_* ref staged in this chat)\", required: true },\n \"account-id\": createArgs[\"account-id\"],\n name: createArgs.name,\n description: createArgs.description,\n ...timeArgs,\n split: {\n type: \"string\",\n description:\n \"New shares by cell id, adding up to 100: --split 2231801141074157=60,1425644426371372=40 (`studies --study-id` lists the cell ids). Only before it starts.\",\n },\n confidence: createArgs.confidence,\n} as const;\n\nregisterSchema({\n command: \"ads.meta.studies.update\",\n description:\n \"Stage a change to a Meta A/B test: rename, describe, move the end (--end; --days counts from a new --start), re-time or re-split it (--start, --split <cell_id>=60,…) before it starts, change the confidence. A running test keeps its start and split — end it and start a new one to change those.\",\n args: {\n id: updateArgs.id,\n ...schemaArgs(Object.fromEntries(Object.entries(updateArgs).filter(([key]) => key !== \"id\"))),\n },\n});\n\nexport const studiesUpdateCommand = defineCommand({\n meta: {\n name: \"update\",\n description: `Stage a change to an A/B test. ${STAGED_NOTE}\nExample: baker ads meta studies update 6123 --end 2026-11-10T08:00Z`,\n },\n args: updateArgs,\n run: async ({ args, rawArgs }) => {\n const accountId = await bareAccountId(args as Record<string, unknown>);\n const target = requireTarget(args as Record<string, unknown>, \"test\");\n const payload = orFail(() => studyUpdateFromFlags(studyFlagsFromArgs(args, rawArgs)));\n if (Object.keys(payload).length === 0) {\n failWriteValidation(\"nothing to change — pass --name, --description, --start, --end, --split or --confidence\");\n }\n await stageOp({ kind: \"adStudy.update\", accountId, target, payload });\n },\n});\n\nconst ACTIONS = {\n end: {\n payload: { end_now: true },\n note: \"The test stops a few minutes after publish and keeps its results — use this to stop a running test early.\",\n },\n delete: {\n payload: { status: \"DELETED\" },\n note: \"Deleting removes the test from Meta, results and all. For a running or finished test prefer `studies end` unless the user asked to delete it; for one that has not started, deleting is how to cancel it.\",\n },\n} as const;\n\nfunction actionCommand(name: keyof typeof ACTIONS) {\n const { payload, note } = ACTIONS[name];\n registerSchema({\n command: `ads.meta.studies.${name}`,\n description: `Stage ${name === \"end\" ? \"ending\" : \"deleting\"} a Meta A/B test. ${note}`,\n args: {\n id: { type: \"positional\", description: \"Test id\", required: true },\n \"account-id\": { type: \"string\", description: \"Meta ad account ID (act_123)\", required: false },\n },\n });\n return defineCommand({\n meta: { name, description: `Stage ${name === \"end\" ? \"ending\" : \"deleting\"} an A/B test. ${note} ${STAGED_NOTE}` },\n args: {\n id: { type: \"positional\", description: \"Test id\", required: true },\n \"account-id\": createArgs[\"account-id\"],\n },\n run: async ({ args }) => {\n const accountId = await bareAccountId(args as Record<string, unknown>);\n await stageOp({\n kind: \"adStudy.update\",\n accountId,\n target: requireTarget(args as Record<string, unknown>, \"test\"),\n payload,\n });\n },\n });\n}\n\nexport const studiesEndCommand = actionCommand(\"end\");\nexport const studiesDeleteCommand = actionCommand(\"delete\");\n","import type { AdStudyCreate, AdStudyObjective, AdStudyUpdate } from \"@baker/api/ads-meta/adStudies\";\n\n/**\n * The marketer's shorthand for a Meta A/B test, read into Meta's own shape.\n * One version per ad set (or campaign), split evenly unless told; times as ISO\n * or epoch; confidence in percent. A test that needs several ad sets per\n * version is staged with `--file` in Meta's shape instead.\n */\n\nexport interface StudyFlags {\n name?: string;\n description?: string;\n adsets?: string[];\n campaigns?: string[];\n \"version-names\"?: string[];\n split?: string[];\n start?: string;\n end?: string;\n days?: string;\n confidence?: string;\n \"measure-name\"?: string;\n \"measure-pixel\"?: string;\n \"measure-event\"?: string[] | string;\n \"measure-conversion\"?: string;\n /** split (default) | lift | creative */\n type?: string;\n /** A creative test: one ad per version. */\n ads?: string[];\n /** A lift study: the share of the audience held out from the ads, in %. */\n holdout?: string;\n \"test-daily-budget\"?: string;\n \"observe-until\"?: string;\n}\n\nconst DAY_SECONDS = 86_400;\n\n/** ISO date/time or epoch seconds/milliseconds, as unix seconds. */\nexport function studyTimeFlag(value: string, flag: string): number {\n if (/^\\d{10}$/.test(value)) return Number(value);\n if (/^\\d{13}$/.test(value)) return Math.floor(Number(value) / 1000);\n const ms = Date.parse(value);\n if (Number.isNaN(ms)) {\n throw new Error(`${flag} must be an ISO date/time (2026-10-20T08:00Z) or epoch seconds`);\n }\n return Math.floor(ms / 1000);\n}\n\nfunction numberFlag(value: string, flag: string): number {\n const n = Number(value.replace(/%$/, \"\"));\n if (!Number.isFinite(n)) throw new Error(`${flag} must be a number, got \"${value}\"`);\n return n;\n}\n\n/** n shares that add up to exactly 100, the remainder on the first: 3 → 33.34, 33.33, 33.33. */\nexport function evenSplit(count: number): number[] {\n const base = Math.floor((100 / count) * 100) / 100;\n const first = Math.round((100 - base * (count - 1)) * 100) / 100;\n return Array.from({ length: count }, (_, index) => (index === 0 ? first : base));\n}\n\nfunction endTime(flags: StudyFlags, start: number | undefined): number | undefined {\n if (flags.end) return studyTimeFlag(flags.end, \"--end\");\n if (flags.days !== undefined) {\n if (start === undefined) throw new Error(\"--days counts from --start — pass --start too, or --end\");\n return start + Math.round(numberFlag(flags.days, \"--days\") * DAY_SECONDS);\n }\n return undefined;\n}\n\nfunction confidence(value: string | undefined): number | undefined {\n if (value === undefined) return undefined;\n const n = numberFlag(value, \"--confidence\");\n return n > 1 ? n / 100 : n;\n}\n\nfunction measure(flags: StudyFlags): AdStudyObjective[] | undefined {\n const events = [flags[\"measure-event\"] ?? []].flat().filter(Boolean);\n if (flags[\"measure-pixel\"]) {\n if (events.length === 0) {\n throw new Error(\"--measure-pixel needs --measure-event (the pixel event that counts, e.g. Lead or Purchase)\");\n }\n const name = flags[\"measure-name\"] ?? events.join(\" or \");\n return [\n { name, type: \"CONVERSIONS\", is_primary: true, adspixels: [{ id: flags[\"measure-pixel\"], event_names: events }] },\n ];\n }\n if (flags[\"measure-conversion\"]) {\n const name = flags[\"measure-name\"] ?? \"Custom conversion\";\n return [{ name, type: \"CONVERSIONS\", is_primary: true, customconversions: [{ id: flags[\"measure-conversion\"] }] }];\n }\n if (events.length > 0) throw new Error(\"--measure-event counts on a pixel — pass --measure-pixel too\");\n return undefined;\n}\n\nfunction versionLetter(index: number): string {\n return `Version ${String.fromCharCode(65 + index)}`;\n}\n\n/** A lift study or creative test: when Meta stops counting results (default the end). */\nfunction observationEnd(flags: StudyFlags): { observation_end_time?: number } {\n const until = flags[\"observe-until\"];\n return until ? { observation_end_time: studyTimeFlag(until, \"--observe-until\") } : {};\n}\n\n/** A lift study: every ad set or campaign named runs in one cell, with a share held out (default 10%). */\nfunction liftFromFlags(flags: StudyFlags, start: number, end: number): AdStudyCreate {\n const level = flags.adsets?.length ? \"adsets\" : flags.campaigns?.length ? \"campaigns\" : undefined;\n if (!level) throw new Error(\"a lift study measures --campaigns or --adsets (comma-separated, all in one group)\");\n if (flags.split) {\n throw new Error(\n \"a lift study shows the ads to everyone but a held-out share — set that share with --holdout (e.g. --holdout 10), not --split\",\n );\n }\n const names = flags[\"version-names\"] ?? [];\n if (names.length > 1) {\n throw new Error(\"a lift study runs as one group — pass one --version-names for it, or none (default Lift)\");\n }\n const holdout = flags.holdout === undefined ? 10 : numberFlag(flags.holdout, \"--holdout\");\n const objectives = measure(flags);\n const conf = confidence(flags.confidence);\n if (!objectives)\n throw new Error(\n \"a lift study needs a measure: --measure-pixel <id> --measure-event Lead, or --measure-conversion <id>\",\n );\n return {\n name: flags.name ?? \"\",\n ...(flags.description ? { description: flags.description } : {}),\n type: \"LIFT\",\n start_time: start,\n end_time: end,\n ...observationEnd(flags),\n cells: [\n {\n name: names[0] ?? \"Lift\",\n treatment_percentage: 100 - holdout,\n control_percentage: holdout,\n [level]: (level === \"adsets\" ? flags.adsets : flags.campaigns) ?? [],\n },\n ],\n objectives,\n ...(conf !== undefined ? { confidence_level: conf } : {}),\n };\n}\n\n/** A creative test: one ad per version, split evenly unless told, on a share of the ad set's daily budget. */\nfunction creativeTestFromFlags(flags: StudyFlags, start: number, end: number): AdStudyCreate {\n const ads = flags.ads ?? [];\n if (ads.length < 2) throw new Error(\"a creative test compares 2 to 5 ads of one ad set: --ads <ad>,<ad>\");\n if (!flags[\"test-daily-budget\"])\n throw new Error(\"pass --test-daily-budget: the part of the ad set's daily budget the test spends\");\n const shares = flags.split?.map((share) => numberFlag(share, \"--split\")) ?? evenSplit(ads.length);\n const names = flags[\"version-names\"] ?? [];\n const objectives = measure(flags);\n const conf = confidence(flags.confidence);\n return {\n name: flags.name ?? \"\",\n ...(flags.description ? { description: flags.description } : {}),\n type: \"SPLIT_TEST_V2\",\n start_time: start,\n end_time: end,\n ...observationEnd(flags),\n testDailyBudget: { amount: flags[\"test-daily-budget\"] },\n cells: ads.map((id, index) => ({\n name: names[index] ?? versionLetter(index),\n treatment_percentage: shares[index] ?? 0,\n ads: [id],\n })),\n ...(conf !== undefined ? { confidence_level: conf } : {}),\n ...(objectives ? { objectives } : {}),\n };\n}\n\n/** A lift study or a creative test, from its own flags. */\nfunction otherStudyFromFlags(flags: StudyFlags, type: \"lift\" | \"creative\"): AdStudyCreate {\n if (!flags.start) throw new Error(\"pass --start: when the test begins (ISO, in the future)\");\n const start = studyTimeFlag(flags.start, \"--start\");\n const end = endTime(flags, start);\n if (end === undefined) throw new Error(\"pass --days or --end\");\n return type === \"lift\" ? liftFromFlags(flags, start, end) : creativeTestFromFlags(flags, start, end);\n}\n\ntype StudyKind = \"split\" | \"lift\" | \"creative\";\n\n/** The flags only some types of test take, and which. */\nconst TYPE_FLAGS: Readonly<Partial<Record<keyof StudyFlags, readonly StudyKind[]>>> = {\n adsets: [\"split\", \"lift\"],\n campaigns: [\"split\", \"lift\"],\n ads: [\"creative\"],\n holdout: [\"lift\"],\n \"test-daily-budget\": [\"creative\"],\n \"observe-until\": [\"lift\", \"creative\"],\n};\n\n/** A flag the type does not take is refused, never dropped: the test would not be the one asked for. */\nfunction refuseForeignFlags(flags: StudyFlags, kind: StudyKind): void {\n for (const [flag, kinds] of Object.entries(TYPE_FLAGS) as [keyof StudyFlags, readonly StudyKind[]][]) {\n const value = flags[flag];\n const given = Array.isArray(value) ? value.length > 0 : value !== undefined && value !== \"\";\n if (given && !kinds.includes(kind)) {\n throw new Error(\n `--${flag} is for ${kinds.map((other) => `--type ${other}`).join(\" or \")}, not a ${kind} test — drop it, or pass the --type it belongs to`,\n );\n }\n }\n}\n\nexport function studyCreateFromFlags(flags: StudyFlags): AdStudyCreate {\n const kind = flags.type ?? \"split\";\n if (kind !== \"split\" && kind !== \"lift\" && kind !== \"creative\") throw new Error(\"--type is split, lift or creative\");\n refuseForeignFlags(flags, kind);\n return kind === \"split\" ? splitTestFromFlags(flags) : otherStudyFromFlags(flags, kind);\n}\n\n/** An A/B test: one version per ad set (or campaign), split evenly unless told. */\nfunction splitTestFromFlags(flags: StudyFlags): AdStudyCreate {\n const level = flags.adsets?.length ? \"adsets\" : flags.campaigns?.length ? \"campaigns\" : undefined;\n if (!level) {\n throw new Error(\"pass --adsets or --campaigns: one id per version, e.g. --adsets 6123,6124\");\n }\n const ids = (level === \"adsets\" ? flags.adsets : flags.campaigns) ?? [];\n if (!flags.start) {\n throw new Error(\"pass --start: when the test begins (ISO, in the future — after this chat publishes)\");\n }\n const start = studyTimeFlag(flags.start, \"--start\");\n const end = endTime(flags, start);\n if (end === undefined) throw new Error(\"pass --days (how long it runs, 7–30 recommended) or --end\");\n const shares = flags.split?.map((share) => numberFlag(share, \"--split\")) ?? evenSplit(ids.length);\n if (shares.length !== ids.length) {\n throw new Error(`--split gives one share per version, in order: ${ids.length} versions, ${shares.length} shares`);\n }\n const names = flags[\"version-names\"] ?? [];\n const objectives = measure(flags);\n const conf = confidence(flags.confidence);\n return {\n name: flags.name ?? \"\",\n ...(flags.description ? { description: flags.description } : {}),\n type: \"SPLIT_TEST\",\n start_time: start,\n end_time: end,\n cells: ids.map((id, index) => ({\n name: names[index] ?? versionLetter(index),\n treatment_percentage: shares[index] ?? 0,\n [level]: [id],\n })),\n ...(conf !== undefined ? { confidence_level: conf } : {}),\n ...(objectives ? { objectives } : {}),\n };\n}\n\nexport function studyUpdateFromFlags(flags: StudyFlags): AdStudyUpdate {\n const start = flags.start ? studyTimeFlag(flags.start, \"--start\") : undefined;\n const end = endTime(flags, start);\n const cells = flags.split?.map((pair) => {\n const match = /^(\\d+)=(.+)$/.exec(pair);\n if (!match?.[1] || !match[2]) {\n throw new Error(\n `--split on a live test names each version by its cell id: --split 2231801141074157=60 (\\`studies --study-id\\` lists them), got \"${pair}\"`,\n );\n }\n return { id: match[1], treatment_percentage: numberFlag(match[2], \"--split\") };\n });\n const conf = confidence(flags.confidence);\n const update: AdStudyUpdate = {};\n if (flags.name) update.name = flags.name;\n if (flags.description) update.description = flags.description;\n if (start !== undefined) update.start_time = start;\n if (end !== undefined) update.end_time = end;\n if (conf !== undefined) update.confidence_level = conf;\n if (cells) update.cells = cells;\n return update;\n}\n\n/** Flags a `--file` test takes beside the file: its name, and the account. */\nconst FILE_COMPANION_FLAGS = new Set([\"file\", \"name\", \"account-id\"]);\n\n/**\n * The flags given beside `--file` that the file would silently win over. The\n * file is the whole test in Meta's shape, so only `--name` overrides it; any\n * other flag is refused rather than dropped.\n */\nexport function studyFileIgnoredFlags(args: Record<string, unknown>): string[] {\n const given = Object.entries(args)\n .filter(([key, value]) => key !== \"_\" && value !== undefined && value !== false && value !== \"\")\n // citty hands each flag over under its kebab and its camelCase name alike.\n .map(([key]) => key.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`))\n .filter((key) => !FILE_COMPANION_FLAGS.has(key));\n return [...new Set(given)].map((key) => `--${key}`);\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
loadJsonFileArg
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-4XXA4WCS.js";
|
|
4
4
|
import {
|
|
5
5
|
metaTargetingSchema
|
|
6
6
|
} from "./chunk-GC5P4KWY.js";
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
csvOrJson,
|
|
10
10
|
handleMetaError,
|
|
11
11
|
resolveAccountIdArg
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-BN4TDL5L.js";
|
|
13
13
|
import {
|
|
14
14
|
repeatedValues
|
|
15
15
|
} from "./chunk-ERSX3MKH.js";
|
|
@@ -439,7 +439,7 @@ var searchCommand = defineCommand({
|
|
|
439
439
|
},
|
|
440
440
|
run: async ({ args }) => runLookup(args, {
|
|
441
441
|
mode: "search",
|
|
442
|
-
accountId: resolveAccountIdArg(args),
|
|
442
|
+
accountId: await resolveAccountIdArg(args),
|
|
443
443
|
q: args.q,
|
|
444
444
|
type: args.type,
|
|
445
445
|
specialAdCategories: csvList(args["special-ad-category"]),
|
|
@@ -467,7 +467,7 @@ var browseCommand = defineCommand({
|
|
|
467
467
|
},
|
|
468
468
|
run: async ({ args }) => runLookup(args, {
|
|
469
469
|
mode: "browse",
|
|
470
|
-
accountId: resolveAccountIdArg(args),
|
|
470
|
+
accountId: await resolveAccountIdArg(args),
|
|
471
471
|
type: args.type,
|
|
472
472
|
q: args.q,
|
|
473
473
|
limit: limitArg(args)
|
|
@@ -499,7 +499,7 @@ var categoriesCommand = defineCommand({
|
|
|
499
499
|
},
|
|
500
500
|
run: async ({ args }) => runLookup(args, {
|
|
501
501
|
mode: "categories",
|
|
502
|
-
accountId: resolveAccountIdArg(args),
|
|
502
|
+
accountId: await resolveAccountIdArg(args),
|
|
503
503
|
class: args.class,
|
|
504
504
|
q: args.q,
|
|
505
505
|
limit: limitArg(args)
|
|
@@ -528,7 +528,7 @@ var suggestCommand = defineCommand({
|
|
|
528
528
|
},
|
|
529
529
|
run: async ({ args, rawArgs }) => runLookup(args, {
|
|
530
530
|
mode: "suggest",
|
|
531
|
-
accountId: resolveAccountIdArg(args),
|
|
531
|
+
accountId: await resolveAccountIdArg(args),
|
|
532
532
|
options: optionRefs(rawArgs, args.id),
|
|
533
533
|
limit: limitArg(args)
|
|
534
534
|
})
|
|
@@ -572,7 +572,7 @@ var validateCommand = defineCommand({
|
|
|
572
572
|
}
|
|
573
573
|
await runLookup(args, {
|
|
574
574
|
mode: "validate",
|
|
575
|
-
accountId: resolveAccountIdArg(args),
|
|
575
|
+
accountId: await resolveAccountIdArg(args),
|
|
576
576
|
...options.length > 0 ? { options } : {},
|
|
577
577
|
...names.length > 0 ? { names } : {}
|
|
578
578
|
});
|
|
@@ -605,7 +605,7 @@ var geoCommand = defineCommand({
|
|
|
605
605
|
},
|
|
606
606
|
run: async ({ args }) => runLookup(args, {
|
|
607
607
|
mode: "geo",
|
|
608
|
-
accountId: resolveAccountIdArg(args),
|
|
608
|
+
accountId: await resolveAccountIdArg(args),
|
|
609
609
|
q: args.q,
|
|
610
610
|
locationTypes: csvList(args["location-types"]),
|
|
611
611
|
countryCode: args.country,
|
|
@@ -629,7 +629,7 @@ var localesCommand = defineCommand({
|
|
|
629
629
|
q: { type: "string", description: "Language name", required: true },
|
|
630
630
|
...commonArgs
|
|
631
631
|
},
|
|
632
|
-
run: async ({ args }) => runLookup(args, { mode: "locales", accountId: resolveAccountIdArg(args), q: args.q, limit: limitArg(args) })
|
|
632
|
+
run: async ({ args }) => runLookup(args, { mode: "locales", accountId: await resolveAccountIdArg(args), q: args.q, limit: limitArg(args) })
|
|
633
633
|
});
|
|
634
634
|
registerSchema({
|
|
635
635
|
command: "ads.meta.targeting.estimate",
|
|
@@ -673,7 +673,7 @@ var estimateCommand = defineCommand({
|
|
|
673
673
|
},
|
|
674
674
|
run: async ({ args }) => {
|
|
675
675
|
const parsed = metaDeliveryEstimateRequestSchema.safeParse({
|
|
676
|
-
accountId: resolveAccountIdArg(args),
|
|
676
|
+
accountId: await resolveAccountIdArg(args),
|
|
677
677
|
targeting: args["targeting-file"] ? loadJsonFileArg(args["targeting-file"]) : void 0,
|
|
678
678
|
adSetId: args["adset-id"],
|
|
679
679
|
optimizationGoal: args["optimization-goal"],
|
|
@@ -712,7 +712,7 @@ var describeCommand = defineCommand({
|
|
|
712
712
|
},
|
|
713
713
|
run: async ({ args }) => {
|
|
714
714
|
const parsed = metaTargetingDescribeRequestSchema.safeParse({
|
|
715
|
-
accountId: resolveAccountIdArg(args),
|
|
715
|
+
accountId: await resolveAccountIdArg(args),
|
|
716
716
|
targeting: args["targeting-file"] ? loadJsonFileArg(args["targeting-file"]) : void 0,
|
|
717
717
|
adSetId: args["adset-id"]
|
|
718
718
|
});
|
|
@@ -759,4 +759,4 @@ Start here before writing interests, behaviors, places or languages into an ad s
|
|
|
759
759
|
export {
|
|
760
760
|
targetingCommand
|
|
761
761
|
};
|
|
762
|
-
//# sourceMappingURL=targeting-
|
|
762
|
+
//# sourceMappingURL=targeting-7HXKBXWY.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../api/src/ads-meta/discovery.ts","../src/commands/ads/meta/targeting.ts","../src/commands/ads/meta/targeting-rows.ts"],"sourcesContent":["/**\n * Wire contract for Meta's discovery reads — the lookups an agent needs before\n * it can write targeting at all, because Meta's targeting spec takes ids, never\n * names: detailed targeting (interests, behaviors, demographics, employers…),\n * places, languages, and the audience size a spec reaches.\n *\n * Shared by the CLI (which builds the request) and the backend route (which\n * parses it), so the two cannot drift.\n */\nimport { z } from \"zod\";\nimport { metaTargetingSchema } from \"./ops\";\n\n/**\n * `adTargetingCategory` classes Meta lists whole (v25.0). Each returns every\n * option of that class with its spec key in `type`, so a category is the way to\n * browse what exists when there is no word to search for.\n *\n * `user_device` and `user_os` are classes too, but their rows carry no id —\n * those keys take plain strings (`\"iPhone\"`, `\"iOS_ver_14.0_and_above\"`) — so\n * they are not listed here as options to paste.\n */\nexport const META_TARGETING_CATEGORY_CLASSES = [\n \"interests\",\n \"behaviors\",\n \"demographics\",\n \"life_events\",\n \"industries\",\n \"income\",\n \"family_statuses\",\n] as const;\n\n/**\n * `adgeolocation` location types. All of these answered a live search on v25.0\n * (2026-10-14); `location_types` on an ad set's own geo_locations is a separate,\n * retired modifier (1870199) and has nothing to do with this list.\n */\nexport const META_GEO_LOCATION_TYPES = [\n \"country\",\n \"country_group\",\n \"region\",\n \"city\",\n \"zip\",\n \"geo_market\",\n \"electoral_district\",\n \"neighborhood\",\n \"subcity\",\n \"metro_area\",\n \"medium_geo_area\",\n \"place\",\n] as const;\n\nconst accountIdSchema = z.string().regex(/^(act_)?\\d+$/, \"account id must be act_<digits>\");\nconst limitSchema = z.number().int().positive().max(1000).optional();\n\n/** A detailed-targeting option named by its spec key, as targeting_list wants it. */\nexport const metaTargetingRefSchema = z.object({ id: z.string().regex(/^\\d+$/), type: z.string().min(1) });\nexport type MetaTargetingRef = z.infer<typeof metaTargetingRefSchema>;\n\nexport const metaTargetingLookupRequestSchema = z.discriminatedUnion(\"mode\", [\n z.object({\n mode: z.literal(\"search\"),\n accountId: accountIdSchema,\n q: z.string().min(1),\n /** A spec key (interests, behaviors, work_employers, …) to keep the search to one kind. */\n type: z.string().min(1).optional(),\n /** Special Ad Categories: only the options Meta allows under them come back. */\n specialAdCategories: z.array(z.string().min(1)).optional(),\n limit: limitSchema,\n }),\n z.object({\n mode: z.literal(\"browse\"),\n accountId: accountIdSchema,\n type: z.string().min(1).optional(),\n /** Kept by Baker after the read: Meta's browse takes no text filter. */\n q: z.string().min(1).optional(),\n limit: limitSchema,\n }),\n z.object({\n mode: z.literal(\"categories\"),\n accountId: accountIdSchema,\n class: z.enum(META_TARGETING_CATEGORY_CLASSES),\n q: z.string().min(1).optional(),\n limit: limitSchema,\n }),\n z.object({\n mode: z.literal(\"suggest\"),\n accountId: accountIdSchema,\n options: z.array(metaTargetingRefSchema).min(1).max(50),\n limit: limitSchema,\n }),\n z.object({\n mode: z.literal(\"validate\"),\n accountId: accountIdSchema,\n options: z.array(metaTargetingRefSchema).max(500).optional(),\n names: z.array(z.string().min(1)).max(500).optional(),\n }),\n z.object({\n mode: z.literal(\"geo\"),\n accountId: accountIdSchema,\n q: z.string().min(1),\n locationTypes: z.array(z.enum(META_GEO_LOCATION_TYPES)).optional(),\n countryCode: z.string().length(2).optional(),\n limit: limitSchema,\n }),\n z.object({\n mode: z.literal(\"locales\"),\n accountId: accountIdSchema,\n q: z.string().min(1),\n limit: limitSchema,\n }),\n]);\nexport type MetaTargetingLookupRequest = z.infer<typeof metaTargetingLookupRequestSchema>;\nexport type MetaTargetingLookupMode = MetaTargetingLookupRequest[\"mode\"];\n\n/**\n * One option, whatever the lookup. `type` is the key it goes under in a\n * targeting spec (`interests`, `work_employers`, `city`, `locale`…), and for a\n * place or a language `id` is Meta's `key`.\n */\nexport interface MetaTargetingOption {\n id: string;\n name?: string;\n type?: string;\n path?: string[];\n description?: string;\n audience_size_lower_bound?: number;\n audience_size_upper_bound?: number;\n /** validate only: whether Meta accepts the option on this account. */\n valid?: boolean;\n /** validate only: Meta's delivery status for a valid option — NORMAL, DEPRECATING, NON-DELIVERABLE. */\n status?: string;\n country_code?: string;\n country_name?: string;\n region?: string;\n}\n\nexport const metaDeliveryEstimateRequestSchema = z\n .object({\n accountId: accountIdSchema,\n targeting: metaTargetingSchema.optional(),\n adSetId: z.string().regex(/^\\d+$/).optional(),\n optimizationGoal: z.string().min(1).optional(),\n promotedObject: z.record(z.string(), z.unknown()).optional(),\n })\n .refine((r) => (r.targeting === undefined) !== (r.adSetId === undefined), {\n message: \"pass either a targeting spec or an existing ad set id, not both\",\n })\n // An ad set carries its own goal; a bare spec has none, and Meta's estimate refuses it without one.\n .refine((r) => r.targeting === undefined || r.optimizationGoal !== undefined, {\n message: \"a targeting spec is sized for a goal — pass --optimization-goal (the ad set's, e.g. LINK_CLICKS)\",\n path: [\"optimizationGoal\"],\n });\nexport type MetaDeliveryEstimateRequest = z.infer<typeof metaDeliveryEstimateRequestSchema>;\n\n/**\n * A targeting spec, or a live ad set's, in Meta's own words — the lines Ads\n * Manager prints under \"Audience definition\" (`targetingsentencelines`).\n */\nexport const metaTargetingDescribeRequestSchema = z\n .object({\n accountId: accountIdSchema,\n targeting: metaTargetingSchema.optional(),\n adSetId: z.string().regex(/^\\d+$/).optional(),\n })\n .refine((r) => (r.targeting === undefined) !== (r.adSetId === undefined), {\n message: \"pass either a targeting spec or an existing ad set id, not both\",\n });\nexport type MetaTargetingDescribeRequest = z.infer<typeof metaTargetingDescribeRequestSchema>;\n\n/** Meta's sentence lines as one line each: \"Location - living in: Spain\". */\nexport function targetingSentences(lines: ReadonlyArray<{ content?: unknown; children?: unknown }>): string[] {\n return lines.flatMap((line) => {\n const head = typeof line.content === \"string\" ? line.content.trim() : \"\";\n const items = Array.isArray(line.children) ? line.children.filter((c): c is string => typeof c === \"string\") : [];\n if (!head && items.length === 0) return [];\n return [items.length > 0 ? `${head} ${items.join(\"; \")}`.trim() : head];\n });\n}\n\nexport interface MetaDeliveryEstimate {\n /** Monthly active people the spec can reach, as Meta's range. */\n audienceLowerBound?: number;\n audienceUpperBound?: number;\n /** False while Meta is still computing; ask again in a moment. */\n ready: boolean;\n}\n\n/**\n * At or below this many people Meta may not deliver at all, and an ad set that\n * does cannot exit learning. It is also the smallest number Meta's delivery\n * estimate ever reports: live on v25.0 a few-hundred-person spec estimated as\n * exactly 1000–1000, so an estimate AT the floor is the narrow one.\n */\nexport const META_SMALL_AUDIENCE_THRESHOLD = 1000;\n\n/** `geo_locations` key for each geo type that is listed as `[{ key }]`. */\nconst GEO_KEY_LISTS: Readonly<Record<string, string>> = {\n region: \"regions\",\n city: \"cities\",\n zip: \"zips\",\n geo_market: \"geo_markets\",\n electoral_district: \"electoral_districts\",\n neighborhood: \"neighborhoods\",\n subcity: \"subcities\",\n metro_area: \"metro_areas\",\n medium_geo_area: \"medium_geo_areas\",\n place: \"places\",\n};\n\ntype Fragment = Record<string, unknown>;\n\nfunction pushTo<T>(record: Record<string, T[]>, key: string, value: T): void {\n const list = record[key] ?? [];\n list.push(value);\n record[key] = list;\n}\n\n/** Where a place goes in geo_locations, and as what; undefined for anything that is not a place. */\nfunction geoEntry(option: MetaTargetingOption, type: string): { key: string; value: unknown } | undefined {\n if (type === \"country\") return { key: \"countries\", value: option.country_code ?? option.id };\n if (type === \"country_group\") return { key: \"country_groups\", value: option.id };\n const list = GEO_KEY_LISTS[type];\n return list ? { key: list, value: { key: option.id } } : undefined;\n}\n\n/**\n * The targeting fragment the given options make, ready to paste into the\n * `--targeting-file` of `adsets create|update`. Detailed options share one\n * flexible_spec group — Meta ORs inside a group and ANDs across groups — so a\n * fragment of interests and behaviors reaches people with ANY of them; places\n * become geo_locations, languages become locales.\n */\nexport function targetingFragment(options: readonly MetaTargetingOption[]): Fragment {\n const detailed: Record<string, Array<{ id: string; name?: string }>> = {};\n const geo: Record<string, unknown[]> = {};\n const locales: number[] = [];\n for (const option of options) {\n const type = option.type ?? \"interests\";\n const place = geoEntry(option, type);\n if (type === \"locale\") {\n locales.push(Number(option.id));\n } else if (place) {\n pushTo(geo, place.key, place.value);\n } else {\n pushTo(detailed, type, option.name ? { id: option.id, name: option.name } : { id: option.id });\n }\n }\n const fragment: Fragment = {};\n if (Object.keys(geo).length > 0) fragment.geo_locations = geo;\n if (locales.length > 0) fragment.locales = locales;\n if (Object.keys(detailed).length > 0) fragment.flexible_spec = [detailed];\n return fragment;\n}\n\n/** Keys whose entries are audiences or connections, never Targeting Search options. */\nconst NOT_DETAILED_OPTIONS = new Set([\n \"custom_audiences\",\n \"excluded_custom_audiences\",\n \"connections\",\n \"excluded_connections\",\n \"friends_of_connections\",\n \"product_audience_specs\",\n \"excluded_product_audience_specs\",\n]);\n\nconst NUMERIC_ID = /^\\d+$/;\n\nfunction entryId(entry: unknown): string | undefined {\n const id = typeof entry === \"object\" && entry !== null ? (entry as { id?: unknown }).id : undefined;\n // An id written as a JSON number is the same option; Meta takes either.\n const text = typeof id === \"number\" && Number.isSafeInteger(id) ? String(id) : id;\n return typeof text === \"string\" && NUMERIC_ID.test(text) ? text : undefined;\n}\n\n/**\n * The detailed-targeting keys a spec may carry at its top level as well as in\n * a flexible_spec group (Meta's detailed-targeting reference). Everything else\n * at the top level — places, ages, placements, audiences, demographic codes\n * written as bare numbers — is not a Targeting Search option.\n */\nconst TOP_LEVEL_DETAILED_KEYS = [\n \"interests\",\n \"behaviors\",\n \"life_events\",\n \"industries\",\n \"income\",\n \"family_statuses\",\n \"user_adclusters\",\n \"work_employers\",\n \"work_positions\",\n \"education_schools\",\n \"education_majors\",\n] as const;\n\nfunction collectEntries(type: string, entries: unknown[], seen: Set<string>, out: MetaTargetingRef[]): void {\n for (const entry of entries) {\n const id = entryId(entry);\n if (id === undefined || seen.has(`${type}:${id}`)) continue;\n seen.add(`${type}:${id}`);\n out.push({ id, type });\n }\n}\n\nfunction collectFrom(group: unknown, seen: Set<string>, out: MetaTargetingRef[]): void {\n if (typeof group !== \"object\" || group === null || Array.isArray(group)) return;\n for (const [type, entries] of Object.entries(group)) {\n if (!NOT_DETAILED_OPTIONS.has(type) && Array.isArray(entries)) collectEntries(type, entries, seen, out);\n }\n}\n\n/**\n * Every detailed-targeting option a spec names — at the top level, in each\n * flexible_spec group, and in exclusions — once each, with the key\n * it sits under, which is the `type` Meta's targeting validation needs.\n */\nexport function targetingOptionRefs(targeting: Record<string, unknown>): MetaTargetingRef[] {\n const seen = new Set<string>();\n const out: MetaTargetingRef[] = [];\n const { flexible_spec, exclusions, ...topLevel } = targeting;\n const detailed: ReadonlySet<string> = new Set(TOP_LEVEL_DETAILED_KEYS);\n collectFrom(Object.fromEntries(Object.entries(topLevel).filter(([key]) => detailed.has(key))), seen, out);\n if (Array.isArray(flexible_spec)) {\n for (const group of flexible_spec) collectFrom(group, seen, out);\n }\n collectFrom(exclusions, seen, out);\n return out;\n}\n","import {\n META_GEO_LOCATION_TYPES,\n META_TARGETING_CATEGORY_CLASSES,\n type MetaDeliveryEstimate,\n type MetaTargetingOption,\n type MetaTargetingRef,\n metaDeliveryEstimateRequestSchema,\n metaTargetingDescribeRequestSchema,\n metaTargetingLookupRequestSchema,\n targetingOptionRefs,\n} from \"@baker/api/ads-meta/discovery\";\nimport { defineCommand } from \"citty\";\nimport { NO_CACHE_ARG } from \"../../../cache-flag.ts\";\nimport { apiPost } from \"../../../client.ts\";\nimport { repeatedValues } from \"../../../lib/repeatedFlags.ts\";\nimport { registerSchema } from \"../../../schemas.ts\";\nimport { writeAdsJson, writeAdsOutput, writeJsonEnvelope } from \"../output.ts\";\nimport { csvOrJson, handleMetaError, resolveAccountIdArg } from \"./shared.ts\";\nimport { compactTargetingOption, estimateHints, parseOptionRef, targetingHints } from \"./targeting-rows.ts\";\nimport { loadJsonFileArg } from \"./write-shared.ts\";\n\ntype Args = Record<string, unknown>;\n\nfunction failValidation(message: string): never {\n writeAdsJson({ ok: false, error: { code: \"VALIDATION_ERROR\", message } });\n process.exit(1);\n}\n\nfunction limitArg(args: Args): number | undefined {\n if (args.limit === undefined) return undefined;\n const n = Number(args.limit);\n if (!Number.isInteger(n) || n <= 0 || n > 1000) failValidation(\"--limit must be a whole number from 1 to 1000\");\n return n;\n}\n\nfunction csvList(value: unknown): string[] | undefined {\n if (typeof value !== \"string\" || value.trim() === \"\") return undefined;\n return value\n .split(\",\")\n .map((part) => part.trim())\n .filter(Boolean);\n}\n\nfunction optionRefs(rawArgs: readonly string[] | undefined, parsed: unknown): MetaTargetingRef[] {\n return repeatedValues(rawArgs, \"id\", parsed).map((raw) => {\n const ref = parseOptionRef(raw);\n if (!ref) failValidation(`--id \"${raw}\" is not <type>:<id> (e.g. interests:6003437140731) or a bare id`);\n return ref;\n });\n}\n\nasync function runLookup(args: Args, request: Record<string, unknown>): Promise<void> {\n const parsed = metaTargetingLookupRequestSchema.safeParse(request);\n if (!parsed.success) {\n const issue = parsed.error.issues[0];\n failValidation(issue ? `${issue.path.join(\".\") || \"request\"}: ${issue.message}` : \"invalid request\");\n }\n try {\n const rows = await apiPost<MetaTargetingOption[]>(\"/api/ads/meta/targeting\", {\n ...parsed.data,\n ...(args[\"no-cache\"] ? { skipCache: true } : {}),\n });\n const data = args.full ? rows.map((row) => ({ ...row })) : rows.map(compactTargetingOption);\n const hints = targetingHints(parsed.data.mode, rows);\n const fmt = csvOrJson(args);\n if (fmt !== \"json\") {\n writeAdsOutput(data, fmt, undefined, hints);\n return;\n }\n writeJsonEnvelope({ ok: true, data, hints });\n } catch (err) {\n handleMetaError(err);\n }\n}\n\nconst commonArgs = {\n \"account-id\": {\n type: \"string\",\n description: \"Meta ad account ID (act_...) — options are looked up as this account sees them\",\n },\n limit: { type: \"string\", description: \"Max options (default 25, up to 1000)\" },\n full: {\n type: \"boolean\",\n description: \"Every field Meta sent (description, raw audience bounds) instead of the compact row\",\n },\n \"no-cache\": NO_CACHE_ARG,\n output: { type: \"string\", description: \"json|csv|jsonl|md\", default: \"json\" },\n} as const;\n\nconst schemaCommonArgs = {\n \"account-id\": { type: \"string\", description: \"Meta ad account ID (act_123)\", required: false },\n limit: { type: \"string\", description: \"Max options (default 25)\", required: false },\n full: { type: \"boolean\", description: \"Every field Meta sent\", required: false },\n \"no-cache\": { type: \"boolean\", description: \"Bypass the cache\", required: false },\n output: { type: \"string\", description: \"json|csv|jsonl|md\", required: false, default: \"json\" },\n};\n\nregisterSchema({\n command: \"ads.meta.targeting.search\",\n description:\n \"Find Meta detailed-targeting ids by word — interests, behaviors, demographics, employers, job titles, schools, life events — across every kind at once. Start here before writing any interest or behavior into a --targeting-file: targeting takes ids, never names. Each row's `type` is the key it goes under.\",\n args: {\n q: { type: \"string\", description: \"Word to search (English names work best)\", required: true },\n type: {\n type: \"string\",\n description:\n \"Keep to one kind: interests|behaviors|work_employers|work_positions|education_schools|life_events|…\",\n required: false,\n },\n \"special-ad-category\": {\n type: \"string\",\n description: \"HOUSING|EMPLOYMENT|FINANCIAL_PRODUCTS_SERVICES — only options allowed under it\",\n required: false,\n },\n ...schemaCommonArgs,\n },\n});\n\nconst searchCommand = defineCommand({\n meta: {\n name: \"search\",\n description:\n 'Find detailed-targeting ids by word. Start here: baker ads meta targeting search --account-id act_123 --q \"solar energy\"',\n },\n args: {\n q: { type: \"string\", description: \"Word to search (English names work best)\", required: true },\n type: {\n type: \"string\",\n description: \"Keep to one spec key, e.g. interests, behaviors, work_employers, work_positions\",\n },\n \"special-ad-category\": {\n type: \"string\",\n description: \"Comma list of Special Ad Categories — returns only options Meta allows under them\",\n },\n ...commonArgs,\n },\n run: async ({ args }) =>\n runLookup(args, {\n mode: \"search\",\n accountId: await resolveAccountIdArg(args),\n q: args.q,\n type: args.type,\n specialAdCategories: csvList(args[\"special-ad-category\"]),\n limit: limitArg(args),\n }),\n});\n\nregisterSchema({\n command: \"ads.meta.targeting.browse\",\n description:\n \"Browse Meta's detailed-targeting tree (what exists, with no word to search for), optionally one kind and narrowed by a word in the name or path.\",\n args: {\n type: { type: \"string\", description: \"interests|behaviors|life_events|work_positions|…\", required: false },\n q: { type: \"string\", description: \"Keep rows whose name or path contains this\", required: false },\n ...schemaCommonArgs,\n },\n});\n\nconst browseCommand = defineCommand({\n meta: {\n name: \"browse\",\n description:\n \"Browse the detailed-targeting tree. Example: baker ads meta targeting browse --account-id act_123 --type behaviors --q travel\",\n },\n args: {\n type: { type: \"string\", description: \"One spec key: interests, behaviors, life_events, …\" },\n q: { type: \"string\", description: \"Keep rows whose name or path contains this\" },\n ...commonArgs,\n },\n run: async ({ args }) =>\n runLookup(args, {\n mode: \"browse\",\n accountId: await resolveAccountIdArg(args),\n type: args.type,\n q: args.q,\n limit: limitArg(args),\n }),\n});\n\nregisterSchema({\n command: \"ads.meta.targeting.categories\",\n description:\n \"List a whole Meta targeting class (behaviors, demographics, life_events, industries, income, family_statuses, …) — the fixed lists a search word rarely finds.\",\n args: {\n class: {\n type: \"string\",\n description: \"Targeting class\",\n required: true,\n enum: [...META_TARGETING_CATEGORY_CLASSES],\n },\n q: { type: \"string\", description: \"Keep rows whose name or path contains this\", required: false },\n ...schemaCommonArgs,\n },\n});\n\nconst categoriesCommand = defineCommand({\n meta: {\n name: \"categories\",\n description: `List one targeting class whole. Classes: ${META_TARGETING_CATEGORY_CLASSES.join(\", \")}. Example: baker ads meta targeting categories --account-id act_123 --class life_events --q parent`,\n },\n args: {\n class: { type: \"string\", description: `One of ${META_TARGETING_CATEGORY_CLASSES.join(\"|\")}`, required: true },\n q: { type: \"string\", description: \"Keep rows whose name or path contains this\" },\n ...commonArgs,\n },\n run: async ({ args }) =>\n runLookup(args, {\n mode: \"categories\",\n accountId: await resolveAccountIdArg(args),\n class: args.class,\n q: args.q,\n limit: limitArg(args),\n }),\n});\n\nregisterSchema({\n command: \"ads.meta.targeting.suggest\",\n description:\n \"Options Meta suggests next to ones you already chose (Meta's 'Suggestions' in Ads Manager). Repeat --id <type>:<id>.\",\n args: {\n id: {\n type: \"string\",\n description: \"Repeatable <type>:<id>, e.g. interests:6003437140731 (a bare id is an interest)\",\n required: true,\n },\n ...schemaCommonArgs,\n },\n});\n\nconst suggestCommand = defineCommand({\n meta: {\n name: \"suggest\",\n description:\n \"Related options for ones you picked. Example: baker ads meta targeting suggest --account-id act_123 --id interests:6003437140731 --id behaviors:6002714898572\",\n },\n args: {\n id: { type: \"string\", description: \"Repeatable <type>:<id> (a bare id is read as an interest)\", required: true },\n ...commonArgs,\n },\n run: async ({ args, rawArgs }) =>\n runLookup(args, {\n mode: \"suggest\",\n accountId: await resolveAccountIdArg(args),\n options: optionRefs(rawArgs, args.id),\n limit: limitArg(args),\n }),\n});\n\nregisterSchema({\n command: \"ads.meta.targeting.validate\",\n description:\n \"Check detailed-targeting ids before staging: whether Meta accepts each on this account under its key, and whether Meta is retiring it. Pass a whole --targeting-file, repeated --id <type>:<id>, or --name.\",\n args: {\n \"targeting-file\": {\n type: \"string\",\n description: \"The ad set's targeting JSON — every id in it is checked\",\n required: false,\n },\n id: { type: \"string\", description: \"Repeatable <type>:<id>\", required: false },\n name: { type: \"string\", description: \"Repeatable option name\", required: false },\n ...schemaCommonArgs,\n },\n});\n\nconst validateCommand = defineCommand({\n meta: {\n name: \"validate\",\n description:\n \"Validate targeting ids before staging. Example: baker ads meta targeting validate --account-id act_123 --targeting-file t.json\",\n },\n args: {\n \"targeting-file\": {\n type: \"string\",\n description: \"The ad set's targeting JSON — every detailed-targeting id in it\",\n },\n id: { type: \"string\", description: \"Repeatable <type>:<id>\" },\n name: { type: \"string\", description: \"Repeatable option name, matched exactly\" },\n ...commonArgs,\n },\n run: async ({ args, rawArgs }) => {\n const fromFile = args[\"targeting-file\"] ? targetingOptionRefs(loadJsonFileArg(args[\"targeting-file\"])) : [];\n const options = [...fromFile, ...optionRefs(rawArgs, args.id)];\n const names = repeatedValues(rawArgs, \"name\", args.name);\n if (options.length === 0 && names.length === 0) {\n failValidation(\n args[\"targeting-file\"]\n ? \"the targeting file names no detailed-targeting ids (interests, behaviors, flexible_spec …) — nothing to validate\"\n : \"pass --targeting-file, --id <type>:<id> or --name\",\n );\n }\n await runLookup(args, {\n mode: \"validate\",\n accountId: await resolveAccountIdArg(args),\n ...(options.length > 0 ? { options } : {}),\n ...(names.length > 0 ? { names } : {}),\n });\n },\n});\n\nregisterSchema({\n command: \"ads.meta.targeting.geo\",\n description:\n \"Find Meta location keys — countries, regions, cities, zips, DMAs (geo_market), electoral districts, neighborhoods — for geo_locations.\",\n args: {\n q: { type: \"string\", description: \"Place name\", required: true },\n \"location-types\": {\n type: \"string\",\n description: `Comma list of ${META_GEO_LOCATION_TYPES.join(\"|\")}`,\n required: false,\n },\n country: { type: \"string\", description: \"ISO-2 country code to search within\", required: false },\n ...schemaCommonArgs,\n },\n});\n\nconst geoCommand = defineCommand({\n meta: {\n name: \"geo\",\n description:\n \"Find location keys for geo_locations. Example: baker ads meta targeting geo --account-id act_123 --q Madrid --location-types city,region --country ES\",\n },\n args: {\n q: { type: \"string\", description: \"Place name\", required: true },\n \"location-types\": { type: \"string\", description: `Comma list: ${META_GEO_LOCATION_TYPES.join(\", \")}` },\n country: { type: \"string\", description: \"ISO-2 country code to search within (ES, US, …)\" },\n ...commonArgs,\n },\n run: async ({ args }) =>\n runLookup(args, {\n mode: \"geo\",\n accountId: await resolveAccountIdArg(args),\n q: args.q,\n locationTypes: csvList(args[\"location-types\"]),\n countryCode: args.country,\n limit: limitArg(args),\n }),\n});\n\nregisterSchema({\n command: \"ads.meta.targeting.locales\",\n description: \"Find Meta locale numbers for targeting.locales (languages people use Facebook in).\",\n args: {\n q: { type: \"string\", description: \"Language name, e.g. Spanish\", required: true },\n ...schemaCommonArgs,\n },\n});\n\nconst localesCommand = defineCommand({\n meta: {\n name: \"locales\",\n description:\n \"Find language numbers for targeting.locales. Example: baker ads meta targeting locales --account-id act_123 --q Spanish\",\n },\n args: {\n q: { type: \"string\", description: \"Language name\", required: true },\n ...commonArgs,\n },\n run: async ({ args }) =>\n runLookup(args, { mode: \"locales\", accountId: await resolveAccountIdArg(args), q: args.q, limit: limitArg(args) }),\n});\n\nregisterSchema({\n command: \"ads.meta.targeting.estimate\",\n description:\n \"How many people a targeting spec reaches on this account for a goal (Meta's delivery estimate), or what an existing ad set reaches. Use it to size an audience before staging and to compare variants.\",\n args: {\n \"targeting-file\": { type: \"string\", description: \"Targeting JSON, as for adsets create\", required: false },\n \"adset-id\": { type: \"string\", description: \"Estimate an existing ad set instead\", required: false },\n \"optimization-goal\": {\n type: \"string\",\n description: \"e.g. LEAD_GENERATION, OFFSITE_CONVERSIONS, LINK_CLICKS\",\n required: false,\n },\n \"promoted-object\": {\n type: \"string\",\n description: 'JSON, e.g. {\"pixel_id\":\"111\",\"custom_event_type\":\"LEAD\"}',\n required: false,\n },\n \"account-id\": { type: \"string\", description: \"Meta ad account ID (act_123)\", required: false },\n },\n});\n\nfunction promotedObjectArg(raw: unknown): Record<string, unknown> | undefined {\n if (typeof raw !== \"string\" || raw === \"\") return undefined;\n try {\n const parsed = JSON.parse(raw) as unknown;\n if (parsed && typeof parsed === \"object\" && !Array.isArray(parsed)) return parsed as Record<string, unknown>;\n } catch {\n /* refused below */\n }\n failValidation(\"--promoted-object must be a JSON object\");\n}\n\nconst estimateCommand = defineCommand({\n meta: {\n name: \"estimate\",\n description:\n \"Audience size for a targeting spec or an ad set. Example: baker ads meta targeting estimate --account-id act_123 --targeting-file t.json --optimization-goal LEAD_GENERATION\",\n },\n args: {\n \"targeting-file\": { type: \"string\", description: \"Targeting JSON (the same file adsets create takes)\" },\n \"adset-id\": { type: \"string\", description: \"Existing ad set to estimate instead of a file\" },\n \"optimization-goal\": { type: \"string\", description: \"The ad set's optimization goal — the estimate depends on it\" },\n \"promoted-object\": { type: \"string\", description: \"Promoted object JSON for conversion goals\" },\n \"account-id\": commonArgs[\"account-id\"],\n },\n run: async ({ args }) => {\n const parsed = metaDeliveryEstimateRequestSchema.safeParse({\n accountId: await resolveAccountIdArg(args),\n targeting: args[\"targeting-file\"] ? loadJsonFileArg(args[\"targeting-file\"]) : undefined,\n adSetId: args[\"adset-id\"],\n optimizationGoal: args[\"optimization-goal\"],\n promotedObject: promotedObjectArg(args[\"promoted-object\"]),\n });\n if (!parsed.success) {\n const issue = parsed.error.issues[0];\n failValidation(issue ? `${issue.path.join(\".\") || \"request\"}: ${issue.message}` : \"invalid request\");\n }\n try {\n const data = await apiPost<MetaDeliveryEstimate>(\"/api/ads/meta/delivery-estimate\", parsed.data);\n writeJsonEnvelope({ ok: true, data, hints: estimateHints(data) });\n } catch (err) {\n handleMetaError(err);\n }\n },\n});\n\nregisterSchema({\n command: \"ads.meta.targeting.describe\",\n description:\n \"A targeting spec, or a live ad set's targeting, in Meta's own words — the audience definition Ads Manager shows (locations, ages, interests, placements). Use it to say who an ad set reaches in plain words.\",\n args: {\n \"targeting-file\": { type: \"string\", description: \"Targeting JSON, as for adsets create\", required: false },\n \"adset-id\": { type: \"string\", description: \"Describe an existing ad set instead\", required: false },\n \"account-id\": { type: \"string\", description: \"Meta ad account ID (act_123)\", required: false },\n },\n});\n\nconst describeCommand = defineCommand({\n meta: {\n name: \"describe\",\n description:\n \"Who a targeting spec or an ad set reaches, in Meta's words. Example: baker ads meta targeting describe --account-id act_123 --adset-id 6123\",\n },\n args: {\n \"targeting-file\": { type: \"string\", description: \"Targeting JSON (the same file adsets create takes)\" },\n \"adset-id\": { type: \"string\", description: \"Existing ad set to describe instead of a file\" },\n \"account-id\": commonArgs[\"account-id\"],\n },\n run: async ({ args }) => {\n const parsed = metaTargetingDescribeRequestSchema.safeParse({\n accountId: await resolveAccountIdArg(args),\n targeting: args[\"targeting-file\"] ? loadJsonFileArg(args[\"targeting-file\"]) : undefined,\n adSetId: args[\"adset-id\"],\n });\n if (!parsed.success) {\n const issue = parsed.error.issues[0];\n failValidation(issue ? `${issue.path.join(\".\") || \"request\"}: ${issue.message}` : \"invalid request\");\n }\n try {\n const data = await apiPost<string[]>(\"/api/ads/meta/targeting-describe\", parsed.data);\n writeJsonEnvelope({ ok: true, data });\n } catch (err) {\n handleMetaError(err);\n }\n },\n});\n\nexport const targetingCommand = defineCommand({\n meta: {\n name: \"targeting\",\n description: `Look up the ids Meta targeting takes, check them, and size the audience — all reads, nothing staged.\n\nStart here before writing interests, behaviors, places or languages into an ad set's --targeting-file:\n baker ads meta targeting search --account-id act_123 --q \"solar energy\" — detailed targeting ids, every kind\n baker ads meta targeting geo --account-id act_123 --q Madrid --country ES — location keys\n baker ads meta targeting locales --account-id act_123 --q Spanish — language numbers\n baker ads meta targeting categories --account-id act_123 --class behaviors — a whole class\n baker ads meta targeting browse --account-id act_123 --type interests --q energy\n baker ads meta targeting suggest --account-id act_123 --id interests:6003437140731\n baker ads meta targeting validate --account-id act_123 --targeting-file t.json — before staging\n baker ads meta targeting estimate --account-id act_123 --targeting-file t.json --optimization-goal LEAD_GENERATION\n baker ads meta targeting describe --account-id act_123 --adset-id 6123 — the audience in Meta's words`,\n },\n subCommands: {\n search: searchCommand,\n browse: browseCommand,\n categories: categoriesCommand,\n suggest: suggestCommand,\n validate: validateCommand,\n geo: geoCommand,\n locales: localesCommand,\n estimate: estimateCommand,\n describe: describeCommand,\n },\n});\n","import {\n META_SMALL_AUDIENCE_THRESHOLD,\n type MetaDeliveryEstimate,\n type MetaTargetingLookupMode,\n type MetaTargetingOption,\n type MetaTargetingRef,\n targetingFragment,\n} from \"@baker/api/ads-meta/discovery\";\n\n/** How many options the paste-ready fragment in the hints carries. */\nconst FRAGMENT_SIZE = 5;\n\nfunction humanCount(n: number): string {\n if (n >= 1e9) return `${(n / 1e9).toFixed(1).replace(/\\.0$/, \"\")}B`;\n if (n >= 1e6) return `${Math.round(n / 1e6)}M`;\n if (n >= 1e4) return `${Math.round(n / 1e3)}K`;\n return String(n);\n}\n\nexport function audienceRange(lower: number | undefined, upper: number | undefined): string | undefined {\n if (lower === undefined && upper === undefined) return undefined;\n if (lower === undefined || upper === undefined || lower === upper) return humanCount(upper ?? lower ?? 0);\n return `${humanCount(lower)}–${humanCount(upper)}`;\n}\n\n/** One option as the agent picks from it; the description and raw bounds sit behind --full. */\nexport function compactTargetingOption(option: MetaTargetingOption): Record<string, unknown> {\n const row: Record<string, unknown> = { id: option.id, name: option.name, type: option.type };\n if (option.path?.length) row.path = option.path.join(\" > \");\n const audience = audienceRange(option.audience_size_lower_bound, option.audience_size_upper_bound);\n if (audience) row.audience = audience;\n if (option.country_code) row.country = option.country_code;\n if (option.region) row.region = option.region;\n if (option.valid !== undefined) row.valid = option.valid;\n if (option.status) row.status = option.status;\n return row;\n}\n\nconst DETAILED_MODES: ReadonlySet<MetaTargetingLookupMode> = new Set([\"search\", \"browse\", \"categories\", \"suggest\"]);\n\nfunction pasteHint(rows: readonly MetaTargetingOption[], where: string): string {\n const fragment = JSON.stringify(targetingFragment(rows.slice(0, FRAGMENT_SIZE)));\n return `${where} — the first ${Math.min(rows.length, FRAGMENT_SIZE)} as a fragment of the ad set's --targeting-file (keep only the ones you mean): ${fragment}`;\n}\n\nfunction validationHints(rows: readonly MetaTargetingOption[]): string[] {\n const invalid = rows.filter((row) => row.valid === false);\n const retiring = rows.filter((row) => row.status === \"DEPRECATING\" || row.status === \"NON-DELIVERABLE\");\n const hints: string[] = [];\n if (invalid.length > 0) {\n hints.push(\n `${invalid.length} option(s) are not valid on this account (${invalid.map((r) => `${r.type ?? \"?\"} ${r.id}`).join(\", \")}) — staging an ad set with them is refused. An id only counts under the key Meta lists it with; find the right one with \\`targeting search --q <word>\\` and use its \\`type\\`.`,\n );\n }\n if (retiring.length > 0) {\n hints.push(\n `${retiring.map((r) => `${r.name ?? r.id} (${r.status})`).join(\", \")}: Meta is retiring these or no longer delivers to them — replace them; \\`targeting suggest --id <type>:<id>\\` lists close alternatives.`,\n );\n }\n if (hints.length === 0 && rows.length > 0) hints.push(\"Every option is valid and delivering on this account.\");\n return hints;\n}\n\n/** What to say about a lookup's rows, once: how to use them, or why there are none. */\nexport function targetingHints(mode: MetaTargetingLookupMode, rows: readonly MetaTargetingOption[]): string[] {\n if (mode === \"validate\") return validationHints(rows);\n if (rows.length === 0) {\n return [\n mode === \"geo\" || mode === \"locales\"\n ? \"Nothing matched. Meta matches the start of a place or language name — try the local spelling or a shorter word.\"\n : \"Nothing matched. Meta names options in English — try an English or broader word, `targeting browse --type interests`, or `targeting categories --class behaviors`.\",\n ];\n }\n if (DETAILED_MODES.has(mode)) {\n return [\n pasteHint(rows, \"Detailed targeting goes in flexible_spec\"),\n \"Options in one flexible_spec group are OR'd (anyone matching one); a second group ANDs (must match both). `type` is the key each id goes under — never move an id to another key. Audience sizes are Meta's worldwide range for the option alone; `targeting estimate` sizes the whole spec.\",\n ];\n }\n if (mode === \"geo\") {\n return [\n pasteHint(rows, \"Places go in geo_locations\"),\n 'A city can take \"radius\" + \"distance_unit\" (10–50 mile / 17–80 kilometer). Special Ad Categories refuse zips and electoral districts and need a 15 mile radius on cities.',\n ];\n }\n return [pasteHint(rows, \"Languages go in locales (numbers, not names)\")];\n}\n\nexport function estimateHints(estimate: MetaDeliveryEstimate): string[] {\n if (!estimate.ready) {\n return [\n \"Meta has not finished this estimate — run it again in a few seconds before reading the numbers. If it stays unready at 0, Meta could not size the spec at all: check its ids with `targeting validate --targeting-file <file>`.\",\n ];\n }\n const upper = estimate.audienceUpperBound;\n if (upper !== undefined && upper <= META_SMALL_AUDIENCE_THRESHOLD) {\n return [\n \"1,000 is the smallest estimate Meta reports, so this audience may be far smaller. Too narrow to deliver steadily or leave learning — widen places, ages or interests unless reaching only these people is the point. Staging an ad set with this audience warns the same.\",\n ];\n }\n return [\n \"Monthly people the spec can reach for this goal (Meta's range, not a forecast of results). Compare variants by re-running with a changed --targeting-file.\",\n ];\n}\n\n/**\n * `--id <type>:<id>` — the spec key and id a search row carries. A bare id is\n * read as an interest, the case almost every suggestion starts from; Meta needs\n * the type, and refuses a suggestion request without it (subcode 1870017).\n */\nexport function parseOptionRef(raw: string): MetaTargetingRef | undefined {\n const match = /^(?:([a-z_]+):)?(\\d+)$/.exec(raw.trim());\n if (!match?.[2]) return undefined;\n return { type: match[1] ?? \"interests\", id: match[2] };\n}\n"],"mappings":"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","names":[]}
|