@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.
Files changed (98) hide show
  1. package/README.md +1 -0
  2. package/dist/{account-APMJVG2F.js → account-CQJFUII3.js} +3 -3
  3. package/dist/{account-APMJVG2F.js.map → account-CQJFUII3.js.map} +1 -1
  4. package/dist/{account-assets-TAUEBCY5.js → account-assets-IDVG4DGK.js} +12 -12
  5. package/dist/account-assets-IDVG4DGK.js.map +1 -0
  6. package/dist/{account-level-2MYDHOID.js → account-level-GK7M2MMQ.js} +14 -14
  7. package/dist/account-level-GK7M2MMQ.js.map +1 -0
  8. package/dist/{accounts-VIAQC3PA.js → accounts-KRW3JNCW.js} +2 -2
  9. package/dist/{activities-MJMAV3SI.js → activities-2RAJXP3J.js} +3 -3
  10. package/dist/{activities-MJMAV3SI.js.map → activities-2RAJXP3J.js.map} +1 -1
  11. package/dist/{ads-O4RL4JXD.js → ads-F2PURRHL.js} +2 -2
  12. package/dist/{ads-NNKUR4HG.js → ads-JVBLNTBF.js} +20 -15
  13. package/dist/ads-JVBLNTBF.js.map +1 -0
  14. package/dist/{adsets-VO5C7PDM.js → adsets-FN5ZWSYF.js} +11 -11
  15. package/dist/adsets-FN5ZWSYF.js.map +1 -0
  16. package/dist/{asset-writes-PQNENVYY.js → asset-writes-SSI3CO2A.js} +4 -4
  17. package/dist/{audience-writes-QDDADCYE.js → audience-writes-G64754LR.js} +6 -6
  18. package/dist/audience-writes-G64754LR.js.map +1 -0
  19. package/dist/{audiences-TQ5ZEWIR.js → audiences-JAVNJ5NC.js} +8 -8
  20. package/dist/audiences-JAVNJ5NC.js.map +1 -0
  21. package/dist/{businesses-CW4RB36R.js → businesses-DXRDQIFP.js} +2 -2
  22. package/dist/{campaigns-IQYYYD2S.js → campaigns-GQWFSKGC.js} +11 -11
  23. package/dist/campaigns-GQWFSKGC.js.map +1 -0
  24. package/dist/{catalogs-PLBY32BE.js → catalogs-TBRONC2D.js} +15 -15
  25. package/dist/catalogs-TBRONC2D.js.map +1 -0
  26. package/dist/{chunk-OEJNZSJG.js → chunk-4XXA4WCS.js} +4 -4
  27. package/dist/chunk-4XXA4WCS.js.map +1 -0
  28. package/dist/{chunk-HPAGYPAF.js → chunk-BN4TDL5L.js} +65 -23
  29. package/dist/chunk-BN4TDL5L.js.map +1 -0
  30. package/dist/{chunk-5LN7GWRE.js → chunk-GEFZVG4P.js} +10 -10
  31. package/dist/chunk-GEFZVG4P.js.map +1 -0
  32. package/dist/{chunk-XB2FODWU.js → chunk-TKDOVFO6.js} +2 -2
  33. package/dist/cli.js +1 -1
  34. package/dist/{copy-writes-GZ744FHU.js → copy-writes-EUFEVWDW.js} +5 -5
  35. package/dist/{copy-writes-GZ744FHU.js.map → copy-writes-EUFEVWDW.js.map} +1 -1
  36. package/dist/{creatives-GTRQAZSB.js → creatives-6VJDUHEY.js} +6 -6
  37. package/dist/creatives-6VJDUHEY.js.map +1 -0
  38. package/dist/{custom-conversions-VQ3LBWYV.js → custom-conversions-NDY5RHSM.js} +6 -6
  39. package/dist/{custom-conversions-VQ3LBWYV.js.map → custom-conversions-NDY5RHSM.js.map} +1 -1
  40. package/dist/{draft-ME4TNPO7.js → draft-G432FJ7N.js} +2 -2
  41. package/dist/{insights-INIFHYR7.js → insights-IVW6W5CP.js} +2 -2
  42. package/dist/{instant-experiences-NMJUWNP3.js → instant-experiences-6F6GXZ67.js} +5 -5
  43. package/dist/{instant-experiences-NMJUWNP3.js.map → instant-experiences-6F6GXZ67.js.map} +1 -1
  44. package/dist/{leads-QBWR7FKT.js → leads-CJKRKB2Q.js} +6 -6
  45. package/dist/{leads-QBWR7FKT.js.map → leads-CJKRKB2Q.js.map} +1 -1
  46. package/dist/{media-23IW442U.js → media-HDHZULQ7.js} +3 -3
  47. package/dist/{meta-H3KBPU33.js → meta-MOSJVYIE.js} +38 -38
  48. package/dist/{page-photos-SHIGXDYE.js → page-photos-4JTIKXAW.js} +5 -5
  49. package/dist/page-photos-4JTIKXAW.js.map +1 -0
  50. package/dist/{pages-XJF3FBOJ.js → pages-BAUS7LQM.js} +2 -2
  51. package/dist/{pixels-AZCIWWXK.js → pixels-QOSEYXCP.js} +3 -3
  52. package/dist/pixels-QOSEYXCP.js.map +1 -0
  53. package/dist/{preview-BIEVHIDJ.js → preview-X6EJGPAY.js} +2 -2
  54. package/dist/{rule-writes-QLZNJ7CT.js → rule-writes-A4BFYHPA.js} +7 -7
  55. package/dist/rule-writes-A4BFYHPA.js.map +1 -0
  56. package/dist/{rules-FJHYG4SJ.js → rules-HLREXY2M.js} +11 -11
  57. package/dist/rules-HLREXY2M.js.map +1 -0
  58. package/dist/{shared-HY2VP7PT.js → shared-I3SB77VH.js} +2 -2
  59. package/dist/{studies-Z2ZIWLTO.js → studies-IGOCWM7F.js} +7 -7
  60. package/dist/{studies-Z2ZIWLTO.js.map → studies-IGOCWM7F.js.map} +1 -1
  61. package/dist/{study-writes-V7BXLTQG.js → study-writes-Z5P2W7JH.js} +6 -6
  62. package/dist/study-writes-Z5P2W7JH.js.map +1 -0
  63. package/dist/{targeting-YBIWD27C.js → targeting-7HXKBXWY.js} +12 -12
  64. package/dist/targeting-7HXKBXWY.js.map +1 -0
  65. package/dist/{write-commands-R5SN6CVQ.js → write-commands-CY734U34.js} +22 -22
  66. package/dist/write-commands-CY734U34.js.map +1 -0
  67. package/package.json +1 -1
  68. package/dist/account-assets-TAUEBCY5.js.map +0 -1
  69. package/dist/account-level-2MYDHOID.js.map +0 -1
  70. package/dist/ads-NNKUR4HG.js.map +0 -1
  71. package/dist/adsets-VO5C7PDM.js.map +0 -1
  72. package/dist/audience-writes-QDDADCYE.js.map +0 -1
  73. package/dist/audiences-TQ5ZEWIR.js.map +0 -1
  74. package/dist/campaigns-IQYYYD2S.js.map +0 -1
  75. package/dist/catalogs-PLBY32BE.js.map +0 -1
  76. package/dist/chunk-5LN7GWRE.js.map +0 -1
  77. package/dist/chunk-HPAGYPAF.js.map +0 -1
  78. package/dist/chunk-OEJNZSJG.js.map +0 -1
  79. package/dist/creatives-GTRQAZSB.js.map +0 -1
  80. package/dist/page-photos-SHIGXDYE.js.map +0 -1
  81. package/dist/pixels-AZCIWWXK.js.map +0 -1
  82. package/dist/rule-writes-QLZNJ7CT.js.map +0 -1
  83. package/dist/rules-FJHYG4SJ.js.map +0 -1
  84. package/dist/study-writes-V7BXLTQG.js.map +0 -1
  85. package/dist/targeting-YBIWD27C.js.map +0 -1
  86. package/dist/write-commands-R5SN6CVQ.js.map +0 -1
  87. /package/dist/{accounts-VIAQC3PA.js.map → accounts-KRW3JNCW.js.map} +0 -0
  88. /package/dist/{ads-O4RL4JXD.js.map → ads-F2PURRHL.js.map} +0 -0
  89. /package/dist/{asset-writes-PQNENVYY.js.map → asset-writes-SSI3CO2A.js.map} +0 -0
  90. /package/dist/{businesses-CW4RB36R.js.map → businesses-DXRDQIFP.js.map} +0 -0
  91. /package/dist/{chunk-XB2FODWU.js.map → chunk-TKDOVFO6.js.map} +0 -0
  92. /package/dist/{draft-ME4TNPO7.js.map → draft-G432FJ7N.js.map} +0 -0
  93. /package/dist/{insights-INIFHYR7.js.map → insights-IVW6W5CP.js.map} +0 -0
  94. /package/dist/{media-23IW442U.js.map → media-HDHZULQ7.js.map} +0 -0
  95. /package/dist/{meta-H3KBPU33.js.map → meta-MOSJVYIE.js.map} +0 -0
  96. /package/dist/{pages-XJF3FBOJ.js.map → pages-BAUS7LQM.js.map} +0 -0
  97. /package/dist/{preview-BIEVHIDJ.js.map → preview-X6EJGPAY.js.map} +0 -0
  98. /package/dist/{shared-HY2VP7PT.js.map → shared-I3SB77VH.js.map} +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/commands/ads/meta/rules.ts"],"sourcesContent":["import type { MetaAdRuleHistoryRow, MetaAdRuleRow } from \"@baker/api/ads-meta/adRules\";\nimport { defineCommand } from \"citty\";\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 automated rules — reads. A rule is Meta's own automation: it runs on\n * Meta, on Meta's schedule, with no Baker involvement once created. Writes\n * (create / update / enable / disable / delete) stage like every Meta change\n * and live in `rule-writes.ts`.\n */\n\n/** What an agent needs to pick a rule and know what it does. Specs sit behind --full. */\nexport function compactRule(row: MetaAdRuleRow): Record<string, unknown> {\n return {\n id: row.id,\n name: row.name,\n status: row.status,\n does: row.description,\n ...(row.disable_error_code ? { disable_error_code: row.disable_error_code } : {}),\n updated_time: row.updated_time,\n };\n}\n\nexport function ruleListHints(rows: readonly MetaAdRuleRow[]): string[] {\n const hints: string[] = [];\n if (rows.length === 0) {\n hints.push(\n 'No automated rules on this account. Stage one with `baker ads meta rules create --name … --apply-to ad --when \"cost_per_result>20\" --action pause`.',\n );\n }\n const issues = rows.filter((row) => row.status === \"HAS_ISSUES\");\n if (issues.length > 0) {\n hints.push(\n `${issues.map((row) => row.name ?? row.id).join(\", \")} ${issues.length === 1 ? \"has\" : \"have\"} been stopped by Meta (HAS_ISSUES) — read it with --rule-id and fix the spec with \\`rules update\\`.`,\n );\n }\n if (rows.length > 0) {\n hints.push(\n \"`rules history --rule-id <id>` shows what a rule did and when; `rules preview <id>` what it would act on now.\",\n );\n }\n return hints;\n}\n\nregisterSchema({\n command: \"ads.meta.rules\",\n description:\n \"List the automated rules on a Meta ad account (Ads Manager → Automated rules): each rule's status and what it does, in words. --rule-id reads one; --full adds its evaluation_spec, execution_spec and schedule_spec in the shape `rules update --file` takes (money in major units of the account currency).\",\n args: {\n \"account-id\": { type: \"string\", description: \"Meta ad account ID (act_123)\", required: false },\n \"rule-id\": { type: \"string\", description: \"Read one rule\", required: false },\n full: { type: \"boolean\", description: \"Include the specs and timestamps\", required: false },\n },\n});\n\nconst listCommand = defineCommand({\n meta: {\n name: \"list\",\n description:\n \"List automated rules. Runs by default. Example: baker ads meta rules --account-id act_123 · one rule in full: baker ads meta rules --rule-id 1132635173044914 --full\",\n },\n args: {\n \"account-id\": { type: \"string\", description: \"Meta ad account ID\" },\n \"rule-id\": { type: \"string\", description: \"Read one rule\" },\n full: { type: \"boolean\", description: \"Include the specs and timestamps\" },\n },\n run: async ({ args }) => {\n const accountId = resolveAccountIdArg(args);\n try {\n const params: Record<string, string> = { \"account-id\": accountId };\n if (args[\"rule-id\"]) params[\"rule-id\"] = String(args[\"rule-id\"]);\n const rows = await apiGet<MetaAdRuleRow[]>(\"/api/ads/meta/rules\", params);\n const full = Boolean(args.full) || Boolean(args[\"rule-id\"]);\n writeJsonEnvelope({ ok: true, data: full ? rows : rows.map(compactRule), hints: ruleListHints(rows) });\n } catch (err) {\n handleMetaError(err);\n }\n },\n});\n\n/** A run in a line an agent can read: when, which rule, and what it changed. */\nexport function compactHistory(row: MetaAdRuleHistoryRow): Record<string, unknown> {\n const changes = (row.results ?? []).flatMap((result) =>\n (result.actions ?? []).map((action) => ({\n object_id: result.object_id,\n action: action.action,\n ...(action.field ? { field: action.field } : {}),\n ...(action.old_value !== undefined ? { from: action.old_value } : {}),\n ...(action.new_value !== undefined ? { to: action.new_value } : {}),\n })),\n );\n return {\n timestamp: row.timestamp,\n rule_id: row.rule_id,\n ...(row.is_manual ? { manual: true } : {}),\n changes,\n ...(row.exception_message ? { error: row.exception_message } : {}),\n };\n}\n\nregisterSchema({\n command: \"ads.meta.rules.history\",\n description:\n \"What automated rules did and when: each run with the objects it changed (PAUSED, CHANGED_BUDGET, …, old → new value). Without --rule-id, every rule on the account, newest first.\",\n args: {\n \"account-id\": { type: \"string\", description: \"Meta ad account ID (act_123)\", required: false },\n \"rule-id\": { type: \"string\", description: \"One rule's runs\", required: false },\n \"object-id\": { type: \"string\", description: \"Only runs that touched this ad, ad set or campaign\", required: false },\n action: { type: \"string\", description: \"Only this action, e.g. PAUSED, CHANGED_BUDGET\", required: false },\n \"hide-no-changes\": { type: \"boolean\", description: \"Drop runs that changed nothing\", required: false },\n limit: { type: \"string\", description: \"Max runs (default 50)\", required: false },\n full: { type: \"boolean\", description: \"Include the rule's specs at each run\", required: false },\n },\n});\n\nconst historyCommand = defineCommand({\n meta: {\n name: \"history\",\n description:\n \"What rules did and when. Example: baker ads meta rules history --rule-id 1132635173044914 --hide-no-changes · every rule: baker ads meta rules history --account-id act_123\",\n },\n args: {\n \"account-id\": { type: \"string\", description: \"Meta ad account ID\" },\n \"rule-id\": { type: \"string\", description: \"One rule's runs\" },\n \"object-id\": { type: \"string\", description: \"Only runs that touched this object\" },\n action: { type: \"string\", description: \"Only this action (PAUSED, CHANGED_BUDGET, …)\" },\n \"hide-no-changes\": { type: \"boolean\", description: \"Drop runs that changed nothing\" },\n limit: { type: \"string\", description: \"Max runs (default 50)\" },\n full: { type: \"boolean\", description: \"Include the rule's specs at each run\" },\n },\n run: async ({ args }) => {\n const accountId = resolveAccountIdArg(args);\n try {\n const params: Record<string, string> = { \"account-id\": accountId };\n for (const key of [\"rule-id\", \"object-id\", \"action\", \"limit\"] as const) {\n if (args[key]) params[key] = String(args[key]);\n }\n if (args[\"hide-no-changes\"]) params[\"hide-no-changes\"] = \"true\";\n const rows = await apiGet<MetaAdRuleHistoryRow[]>(\"/api/ads/meta/rules/history\", params);\n const hints =\n rows.length === 0\n ? [\n \"No runs recorded. A daily rule first runs just after midnight in the ad account's time zone; a switched-off rule never runs.\",\n ]\n : [];\n writeJsonEnvelope({ ok: true, data: args.full ? rows : rows.map(compactHistory), hints });\n } catch (err) {\n handleMetaError(err);\n }\n },\n});\n\nregisterSchema({\n command: \"ads.meta.rules.preview\",\n description:\n \"Which ads, ad sets or campaigns a live schedule rule would act on right now, by Meta's own evaluation. Changes nothing.\",\n args: {\n id: { type: \"positional\", description: \"Rule id\", required: true },\n \"account-id\": { type: \"string\", description: \"Meta ad account ID (act_123)\", required: false },\n },\n});\n\nconst previewCommand = defineCommand({\n meta: {\n name: \"preview\",\n description:\n \"What a rule would act on right now (changes nothing). Example: baker ads meta rules preview 1132635173044914\",\n },\n args: {\n id: { type: \"positional\", description: \"Rule id\", required: true },\n \"account-id\": { type: \"string\", description: \"Meta ad account ID\" },\n },\n run: async ({ args }) => {\n const accountId = resolveAccountIdArg(args);\n try {\n const data = await apiGet<{ ruleId: string; matched: unknown[] }>(\"/api/ads/meta/rules/preview\", {\n \"account-id\": accountId,\n \"rule-id\": String(args.id),\n });\n const hints =\n data.matched.length === 0 ? [\"Nothing matches right now — the rule would change nothing if it ran now.\"] : [];\n writeJsonEnvelope({ ok: true, data, hints });\n } catch (err) {\n handleMetaError(err);\n }\n },\n});\n\nexport const rulesCommand = defineCommand({\n meta: {\n name: \"rules\",\n description: `Meta automated rules — Meta's own automation, which runs on Meta with no Baker involvement once created.\nUse one for a standing \"if this metric crosses that line, do X\" that needs no judgement; use a Baker scheduled agent when the call needs reasoning.\n\nExamples:\n baker ads meta rules --account-id act_123 # every rule, what it does\n baker ads meta rules --rule-id 1132635173044914 # one rule with its specs\n baker ads meta rules history --rule-id 1132635173044914 --hide-no-changes # what it did\n baker ads meta rules preview 1132635173044914 # what it would act on now\n baker ads meta rules create --name \"Stop expensive ads\" --apply-to ad --when \"cost_per_result>20\" --action pause\n baker ads meta rules update 1132635173044914 --name \"Stop very expensive ads\"\n baker ads meta rules disable|enable|delete 1132635173044914\n baker ads meta rules run 1132635173044914 # run it once, at publish`,\n },\n subCommands: {\n list: listCommand,\n history: historyCommand,\n preview: previewCommand,\n create: () => import(\"./rule-writes.ts\").then((m) => m.rulesCreateCommand),\n update: () => import(\"./rule-writes.ts\").then((m) => m.rulesUpdateCommand),\n enable: () => import(\"./rule-writes.ts\").then((m) => m.rulesEnableCommand),\n disable: () => import(\"./rule-writes.ts\").then((m) => m.rulesDisableCommand),\n delete: () => import(\"./rule-writes.ts\").then((m) => m.rulesDeleteCommand),\n run: () => import(\"./rule-writes.ts\").then((m) => m.rulesRunCommand),\n },\n args: {\n \"account-id\": { type: \"string\", description: \"Meta ad account ID\" },\n \"rule-id\": { type: \"string\", description: \"Read one rule\" },\n full: { type: \"boolean\", description: \"Include the specs and timestamps\" },\n },\n default: \"list\",\n});\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
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 = 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 = 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 = 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 +0,0 @@
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: 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: 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: 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: 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: 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: 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: 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: 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: 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":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/commands/ads/meta/write-commands.ts","../src/commands/ads/meta/creative-flags.ts"],"sourcesContent":["import { buildCustomConversionRule } from \"@baker/api/ads-meta\";\nimport { defineCommand } from \"citty\";\nimport { repeatedValues } from \"../../../lib/repeatedFlags.ts\";\nimport {\n carouselFlags,\n conversationAndAppCta,\n creativeEnhancementsFromFlags,\n welcomeMessageFromFlags,\n} from \"./creative-flags.ts\";\nimport {\n adSetDeliveryArgs,\n adSetDeliveryFromFlags,\n boostEditArgs,\n boostEditsFromFlags,\n budgetBoostArg,\n budgetBoostsFromFlags,\n clearArg,\n clearFromFlags,\n} from \"./delivery-flags.ts\";\nimport {\n bareAccountId,\n failWriteValidation,\n listFlag,\n loadJsonFileArg,\n mergePayload,\n numberFlag,\n parseDateFlag,\n parseMoneyFlag,\n refuseDeleteByUpdate,\n repeatedListFlag,\n requireTarget,\n stageOp,\n stageStatusChange,\n upper,\n} from \"./write-shared.ts\";\n\n/**\n * Staged Meta write subcommands. None of these touch the Meta Graph API — they\n * stage ops on this chat's draft; ops appear in the dashboard as pending Meta\n * changes and apply sequentially when the chat is published. Review with\n * `baker ads meta draft`, undo with `draft remove <ref>`.\n *\n * Full object graph: campaign → ad set → ad creative → ad, plus custom\n * audiences and media uploads. Chain creates with meta_temp_* refs.\n */\n\nconst STAGED_NOTE = \"Staged until publish — review with `baker ads meta draft`.\";\n\nconst accountArgs = {\n \"account-id\": { type: \"string\", description: \"Numeric account ID or act_N\" },\n} as const;\n\n// --- Campaigns ---\n\nfunction campaignPayloadFromFlags(args: Record<string, unknown>): Record<string, unknown> {\n return {\n name: args.name,\n objective: upper(args.objective),\n status: upper(args.status),\n special_ad_categories: listFlag(args[\"special-ad-categories\"])?.map((c) => c.toUpperCase()),\n special_ad_category_country: listFlag(args[\"special-ad-category-country\"])?.map((c) => c.toUpperCase()),\n buying_type: upper(args[\"buying-type\"]),\n bid_strategy: upper(args[\"bid-strategy\"]),\n dailyBudget: parseMoneyFlag(args[\"daily-budget\"], args.currency, \"--daily-budget\"),\n lifetimeBudget: parseMoneyFlag(args[\"lifetime-budget\"], args.currency, \"--lifetime-budget\"),\n spendCap: parseMoneyFlag(args[\"spend-cap\"], args.currency, \"--spend-cap\"),\n adSetBudgetSharing: args[\"adset-budget-sharing\"] === undefined ? undefined : Boolean(args[\"adset-budget-sharing\"]),\n start_time: parseDateFlag(args.start, \"--start\"),\n stop_time: parseDateFlag(args.stop, \"--stop\"),\n promoted_object: args[\"catalog-id\"] ? { product_catalog_id: String(args[\"catalog-id\"]) } : undefined,\n };\n}\n\nconst campaignBudgetArgs = {\n \"daily-budget\": { type: \"string\", description: \"Daily budget (Advantage campaign budget / CBO)\" },\n \"lifetime-budget\": { type: \"string\", description: \"Lifetime budget\" },\n \"spend-cap\": { type: \"string\", description: \"Campaign spend cap\" },\n currency: { type: \"string\", description: \"3-letter currency (defaults to the account currency)\" },\n \"bid-strategy\": {\n type: \"string\",\n description: \"LOWEST_COST_WITHOUT_CAP|LOWEST_COST_WITH_BID_CAP|COST_CAP|LOWEST_COST_WITH_MIN_ROAS\",\n },\n start: { type: \"string\", description: \"Start time (ISO date or epoch ms)\" },\n stop: { type: \"string\", description: \"Stop time\" },\n ...budgetBoostArg,\n} as const;\n\nexport const campaignsCreateCommand = defineCommand({\n meta: {\n name: \"create\",\n description: `Stage a new campaign. ${STAGED_NOTE}\nExample: baker ads meta campaigns create --name \"Q3 Prospecting\" --objective OUTCOME_LEADS --daily-budget 50\nAdvantage+ (Meta's own \"Advantage+ campaign\" state) is not a setting: it follows from a campaign budget here, Advantage+ audience (targeting_automation.advantage_audience 1) and automatic placements (no placement lists) on every ad set. Read it back with \\`baker ads meta campaigns --fields id,name,advantage_state_info\\`.\nBlack Friday boost: baker ads meta campaigns create --name \"BF\" --objective OUTCOME_SALES --daily-budget 50 --budget-boost 2027-11-27T00:00Z..2027-11-28T00:00Z=+100%`,\n },\n args: {\n ...accountArgs,\n name: { type: \"string\", description: \"Campaign name\" },\n objective: {\n type: \"string\",\n description:\n \"OUTCOME_TRAFFIC|OUTCOME_ENGAGEMENT|OUTCOME_LEADS|OUTCOME_SALES|OUTCOME_AWARENESS|OUTCOME_APP_PROMOTION\",\n },\n status: { type: \"string\", description: \"ACTIVE|PAUSED (default PAUSED)\" },\n \"special-ad-categories\": { type: \"string\", description: \"CSV: NONE|HOUSING|EMPLOYMENT|CREDIT|… (default NONE)\" },\n \"special-ad-category-country\": { type: \"string\", description: \"CSV of 2-letter country codes\" },\n \"buying-type\": { type: \"string\", description: \"AUCTION|RESERVED (default AUCTION)\" },\n \"adset-budget-sharing\": {\n type: \"boolean\",\n description:\n \"Let ad sets lend up to 20% of their budget to each other (off unless set; ignored with a campaign budget)\",\n },\n ...campaignBudgetArgs,\n \"catalog-id\": {\n type: \"string\",\n description:\n \"Advantage+ catalog ads: the product catalog the campaign sells from (OUTCOME_SALES) — from `baker ads meta catalogs`. Fixed at creation; each ad set then names one of its product sets with --product-set-id\",\n },\n file: { type: \"string\", description: \"JSON file with the full payload; flags override file keys\" },\n },\n run: async ({ args, rawArgs }) => {\n const accountId = bareAccountId(args as Record<string, unknown>);\n const payload = mergePayload(loadJsonFileArg(args.file), {\n ...campaignPayloadFromFlags(args as Record<string, unknown>),\n budgetSchedules: budgetBoostsFromFlags(args as Record<string, unknown>, rawArgs),\n });\n await stageOp({ kind: \"campaign.create\", accountId, payload });\n },\n});\n\nexport const campaignsUpdateCommand = defineCommand({\n meta: {\n name: \"update\",\n description: `Stage changes to a campaign. ${STAGED_NOTE}\nPass a meta_temp_* ref to amend a campaign staged in this chat. Example: baker ads meta campaigns update 6123 --daily-budget 80\nBudget boosts on a live campaign need its daily budget restated in the same command (Meta reads it from the request): baker ads meta campaigns update 6123 --daily-budget 50 --budget-boost 2027-11-27T00:00Z..2027-11-28T00:00Z=+25\nA boost is ADDED to the ones the campaign has (Meta keeps them, even overlapping); take them all away with --clear boosts, or one at a time with --remove-boost / --move-boost (ids from \\`baker ads meta high-demand-periods --campaign-id 6123\\`): baker ads meta campaigns update 6123 --move-boost 1633135568548763=2027-11-27T06:00Z..2027-11-27T12:00Z`,\n },\n args: {\n id: { type: \"positional\", description: \"Campaign id or meta_temp_* ref\", required: true },\n ...accountArgs,\n name: { type: \"string\", description: \"New name\" },\n status: { type: \"string\", description: \"ACTIVE|PAUSED|ARCHIVED\" },\n ...campaignBudgetArgs,\n clear: { type: \"string\", description: \"boosts — take away every budget boost the campaign has now\" },\n ...boostEditArgs,\n file: { type: \"string\", description: \"JSON file with fields to change\" },\n },\n run: async ({ args, rawArgs }) => {\n const accountId = bareAccountId(args as Record<string, unknown>);\n const payload = mergePayload(loadJsonFileArg(args.file), {\n name: args.name,\n status: upper(args.status),\n budgetSchedules: budgetBoostsFromFlags(args as Record<string, unknown>, rawArgs),\n clear: clearOrFail(rawArgs, \"campaign\"),\n ...boostEditsFromFlags(args as Record<string, unknown>, rawArgs),\n bid_strategy: upper(args[\"bid-strategy\"]),\n dailyBudget: parseMoneyFlag(args[\"daily-budget\"], args.currency, \"--daily-budget\"),\n lifetimeBudget: parseMoneyFlag(args[\"lifetime-budget\"], args.currency, \"--lifetime-budget\"),\n spendCap: parseMoneyFlag(args[\"spend-cap\"], args.currency, \"--spend-cap\"),\n start_time: parseDateFlag(args.start, \"--start\"),\n stop_time: parseDateFlag(args.stop, \"--stop\"),\n });\n refuseDeleteByUpdate(payload, \"campaigns\");\n await stageOp({\n kind: \"campaign.update\",\n accountId,\n target: requireTarget(args as Record<string, unknown>, \"campaign\"),\n payload,\n });\n },\n});\n\n// --- Ad sets ---\n\nfunction clearOrFail(rawArgs: readonly string[] | undefined, entity: \"adSet\" | \"campaign\"): string[] | undefined {\n try {\n return clearFromFlags(rawArgs, entity);\n } catch (err) {\n failWriteValidation(err instanceof Error ? err.message : String(err));\n }\n}\n\nfunction adSetPayloadFromFlags(\n args: Record<string, unknown>,\n rawArgs: readonly string[] | undefined,\n file: Record<string, unknown>,\n): Record<string, unknown> {\n const promotedObject = args[\"promoted-object-file\"]\n ? loadJsonFileArg(args[\"promoted-object-file\"])\n : buildPromotedObject(args);\n return {\n ...adSetDeliveryFromFlags(args, rawArgs),\n budgetSchedules: budgetBoostsFromFlags(args, rawArgs),\n name: args.name,\n campaign_id: args.campaign,\n status: upper(args.status),\n optimization_goal: upper(args[\"optimization-goal\"]),\n billing_event: upper(args[\"billing-event\"]),\n destination_type: upper(args[\"destination-type\"]),\n dailyBudget: parseMoneyFlag(args[\"daily-budget\"], args.currency, \"--daily-budget\"),\n lifetimeBudget: parseMoneyFlag(args[\"lifetime-budget\"], args.currency, \"--lifetime-budget\"),\n bidAmount: parseMoneyFlag(args[\"bid-amount\"], args.currency, \"--bid-amount\"),\n bid_strategy: upper(args[\"bid-strategy\"]),\n start_time: parseDateFlag(args.start, \"--start\"),\n end_time: parseDateFlag(args.end, \"--end\"),\n targeting: targetingFromFlags(args, rawArgs, targetingOf(file)),\n rf_prediction_id: args.reservation,\n promoted_object: promotedObject && Object.keys(promotedObject).length > 0 ? promotedObject : undefined,\n };\n}\n\nfunction targetingOf(file: Record<string, unknown>): Record<string, unknown> | undefined {\n const targeting = file.targeting;\n return targeting && typeof targeting === \"object\" && !Array.isArray(targeting)\n ? (targeting as Record<string, unknown>)\n : undefined;\n}\n\n/**\n * The targeting file — or else the `--file` payload's own targeting — with the\n * brand-safety flags laid over it (`brandSafety.ts`), so a safety flag joins the\n * audience rather than replacing it with safety keys alone.\n */\nexport function targetingFromFlags(\n args: Record<string, unknown>,\n rawArgs: readonly string[] | undefined,\n fileTargeting?: Record<string, unknown>,\n): Record<string, unknown> | undefined {\n const file = args[\"targeting-file\"] ? loadJsonFileArg(args[\"targeting-file\"]) : undefined;\n const base = file ?? fileTargeting;\n const lists = repeatedValues(rawArgs, \"block-list\", args[\"block-list\"]);\n const filters = repeatedValues(rawArgs, \"content-filter\", args[\"content-filter\"]).map((value) => value.toUpperCase());\n const categories = repeatedValues(rawArgs, \"exclude-publisher-category\", args[\"exclude-publisher-category\"]).map(\n (value) => value.toLowerCase(),\n );\n const safety = {\n ...(lists.length > 0 ? { excluded_publisher_list_ids: lists } : {}),\n ...(filters.length > 0 ? { brand_safety_content_filter_levels: filters } : {}),\n ...(categories.length > 0 ? { excluded_publisher_categories: categories } : {}),\n };\n if (Object.keys(safety).length === 0) return file;\n return { ...(base ?? {}), ...safety };\n}\n\nfunction buildPromotedObject(args: Record<string, unknown>): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n if (args[\"page-id\"]) out.page_id = String(args[\"page-id\"]);\n if (args[\"pixel-id\"]) out.pixel_id = String(args[\"pixel-id\"]);\n if (args[\"custom-event-type\"]) out.custom_event_type = upper(args[\"custom-event-type\"]);\n if (args[\"custom-conversion-id\"]) out.custom_conversion_id = String(args[\"custom-conversion-id\"]);\n if (args[\"product-set-id\"]) out.product_set_id = String(args[\"product-set-id\"]);\n if (args[\"app-id\"]) out.application_id = String(args[\"app-id\"]);\n if (args[\"app-store-url\"]) out.object_store_url = String(args[\"app-store-url\"]);\n if (args[\"whatsapp-number\"]) out.whatsapp_phone_number = String(args[\"whatsapp-number\"]);\n return out;\n}\n\n// --- Custom conversions ---\n\n/**\n * The rule, from the flags that cover the conversion people actually create.\n *\n * `--url-contains` is the whole common case (\"count a Lead when somebody\n * reaches /gracias\") and produces the exact shape Events Manager writes.\n * `--rule-file` is the escape hatch for anything Meta's filter language can\n * express and these flags cannot. Requiring one and refusing both keeps a\n * half-specified conversion — which Meta accepts, and which then counts\n * everything or nothing — off the draft.\n */\nexport function customConversionRuleFromFlags(args: Record<string, unknown>): Record<string, unknown> {\n const urlContains = args[\"url-contains\"];\n const ruleFile = args[\"rule-file\"];\n if (urlContains && ruleFile) {\n failWriteValidation(\n \"pass either --url-contains or --rule-file, not both — they are two ways to say the same thing\",\n );\n }\n if (ruleFile) {\n return loadJsonFileArg(ruleFile);\n }\n if (!urlContains) {\n failWriteValidation(\n \"a custom conversion needs a rule — pass --url-contains '/thank-you' (the common case), or --rule-file with Meta's filter JSON\",\n );\n }\n return buildCustomConversionRule({\n event: args.event ? String(args.event) : undefined,\n urlContains: String(urlContains),\n });\n}\n\nexport const customConversionsCreateCommand = defineCommand({\n meta: {\n name: \"create\",\n description: `Stage a new Custom Conversion — a pixel event narrowed by a rule, which an ad set can then optimize toward. ${STAGED_NOTE}\nMeta caps an account at 100 and its delete only archives one, so a duplicate is permanent: check \\`baker ads meta custom-conversions\\` first.\nChain an ad set to it in the same chat with --custom-conversion-id meta_temp_*.\nExample: baker ads meta custom-conversions create --name \"Lead formulario\" --pixel-id 645971030024859 --custom-event-type LEAD --url-contains \"/gracias\"`,\n },\n args: {\n ...accountArgs,\n name: { type: \"string\", description: \"What the marketer will see in Events Manager and in reports\" },\n \"pixel-id\": { type: \"string\", description: \"The pixel it counts from — from `baker ads meta pixels`\" },\n \"custom-event-type\": {\n type: \"string\",\n description: \"Event category Meta files it under: LEAD|PURCHASE|COMPLETE_REGISTRATION|CONTACT|…\",\n },\n \"url-contains\": {\n type: \"string\",\n description: \"Count the event when the URL contains this, e.g. '/gracias'. The common case.\",\n },\n event: { type: \"string\", description: \"Pixel event the URL narrows (default PageView)\" },\n \"rule-file\": { type: \"string\", description: \"JSON file with Meta's raw filter, instead of --url-contains\" },\n description: { type: \"string\", description: \"Free-text note shown in Events Manager\" },\n value: { type: \"string\", description: \"Default value of one conversion, when the site sends no value\" },\n },\n run: async ({ args }) => {\n const accountId = bareAccountId(args as Record<string, unknown>);\n await stageOp({\n kind: \"customConversion.create\",\n accountId,\n payload: {\n name: args.name,\n description: args.description,\n pixel_id: args[\"pixel-id\"] ? String(args[\"pixel-id\"]) : undefined,\n custom_event_type: upper(args[\"custom-event-type\"]),\n rule: customConversionRuleFromFlags(args as Record<string, unknown>),\n default_conversion_value: numberFlag(args.value, \"--value\"),\n },\n });\n },\n});\n\nconst adSetWriteArgs = {\n ...accountArgs,\n name: { type: \"string\", description: \"Ad set name\" },\n status: { type: \"string\", description: \"ACTIVE|PAUSED|ARCHIVED\" },\n \"optimization-goal\": { type: \"string\", description: \"LINK_CLICKS|OFFSITE_CONVERSIONS|REACH|LEAD_GENERATION|…\" },\n \"billing-event\": { type: \"string\", description: \"IMPRESSIONS|LINK_CLICKS|THRUPLAY|…\" },\n \"destination-type\": { type: \"string\", description: \"WEBSITE|APP|MESSENGER|ON_AD|WHATSAPP|…\" },\n \"daily-budget\": { type: \"string\", description: \"Daily budget (ABO; omit if the campaign uses CBO)\" },\n \"lifetime-budget\": { type: \"string\", description: \"Lifetime budget (needs --end)\" },\n \"bid-amount\": { type: \"string\", description: \"Bid amount (required for COST_CAP / bid-cap strategies)\" },\n \"bid-strategy\": {\n type: \"string\",\n description:\n \"LOWEST_COST_WITHOUT_CAP (auto-bid) | LOWEST_COST_WITH_BID_CAP | COST_CAP — needs --bid-amount | LOWEST_COST_WITH_MIN_ROAS — needs --min-roas. Omit and Baker sets auto-bid (or min-ROAS when --min-roas is given); never leave it to Meta, whose default is a bid cap that then fails without an amount.\",\n },\n ...adSetDeliveryArgs,\n currency: { type: \"string\", description: \"3-letter currency (defaults to the account currency)\" },\n start: { type: \"string\", description: \"Start time\" },\n end: { type: \"string\", description: \"End time\" },\n \"targeting-file\": {\n type: \"string\",\n description:\n \"JSON file with the full Meta targeting spec. Ids, never names: look them up with `baker ads meta targeting search|geo|locales` and size it with `targeting estimate`\",\n },\n \"block-list\": {\n type: \"string\",\n description:\n \"Repeatable. Keep the ad set off a publisher block list — an id from `baker ads meta block-lists` or a meta_temp_* ref. On update without --targeting-file, the rest of the live targeting is kept\",\n },\n \"content-filter\": {\n type: \"string\",\n description:\n \"Repeatable, one per family: FEED_|FACEBOOK_|AN_ + RELAXED|STANDARD|STRICT — how strict Meta's content filter next to the ad is (feeds, in-stream/Reels, Audience Network)\",\n },\n \"exclude-publisher-category\": {\n type: \"string\",\n description:\n \"Repeatable: dating|gambling (and, for Audience Network / in-stream, debated_social_issues|mature_audiences|tragedy_and_conflict)\",\n },\n reservation: {\n type: \"string\",\n description:\n \"Create only: a reach-and-frequency reservation this ad set is bought on (id or meta_temp_* from `reach-frequency reserve`); its campaign is --buying-type RESERVED and it takes no budget\",\n },\n \"promoted-object-file\": { type: \"string\", description: \"JSON file with the promoted_object\" },\n \"page-id\": {\n type: \"string\",\n description: \"promoted_object.page_id shortcut (required for LEAD_GENERATION / QUALITY_LEAD / PAGE_LIKES goals)\",\n },\n \"pixel-id\": { type: \"string\", description: \"promoted_object.pixel_id shortcut\" },\n \"custom-event-type\": { type: \"string\", description: \"promoted_object.custom_event_type (PURCHASE|LEAD|…)\" },\n \"custom-conversion-id\": {\n type: \"string\",\n description:\n \"Optimize toward a Custom Conversion — the id from `baker ads meta custom-conversions`. Use it instead of --pixel-id/--custom-event-type, not alongside: Baker reads the conversion off Meta and fills its pixel, event category and rule.\",\n },\n \"product-set-id\": {\n type: \"string\",\n description:\n \"Advantage+ catalog ads: the product set this ad set shows (promoted_object.product_set_id) — from `baker ads meta catalogs --catalog-id <id>`. With OFFSITE_CONVERSIONS pass --custom-event-type (PURCHASE, ADD_TO_CART…); no pixel needed\",\n },\n \"app-id\": {\n type: \"string\",\n description:\n \"App ads: the app to promote (promoted_object.application_id) — from `baker ads meta apps`; needs --app-store-url\",\n },\n \"app-store-url\": {\n type: \"string\",\n description: \"App ads: the app's App Store / Google Play page (promoted_object.object_store_url)\",\n },\n \"whatsapp-number\": {\n type: \"string\",\n description:\n \"Click-to-WhatsApp: the WhatsApp Business number connected to the Page (promoted_object.whatsapp_phone_number)\",\n },\n file: { type: \"string\", description: \"JSON file with the full payload; flags override\" },\n} as const;\n\nexport const adSetsCreateCommand = defineCommand({\n meta: {\n name: \"create\",\n description: `Stage a new ad set. ${STAGED_NOTE}\nChain to a campaign with --campaign (real id or meta_temp_* ref).\nLead-form goals (LEAD_GENERATION, QUALITY_LEAD) and PAGE_LIKES need --page-id (the Facebook Page the leads/likes belong to).\nConversion goals take EITHER --pixel-id + --custom-event-type (a standard event) OR --custom-conversion-id (the marketer's own conversion, from 'baker ads meta custom-conversions').\nExample: baker ads meta adsets create --campaign meta_temp_ab12 --name \"US 25-54\" --optimization-goal LINK_CLICKS --billing-event IMPRESSIONS --daily-budget 50 --targeting-file t.json\nDelivery controls: --min-roas (ROAS goal bidding), --frequency-cap 2/7 (REACH/THRUPLAY), --schedule \"Mon-Fri 09-18\" (dayparting, lifetime budget only), --pacing no_pacing (bid cap only), --daily-spend-cap/--daily-min-spend (an ad set's share of a campaign budget), --budget-boost (high-demand periods), --attribution 7d_click,1d_view, --dynamic-creative (create only).\nExample: baker ads meta adsets create --campaign 6123 --name \"Reach ES\" --optimization-goal REACH --billing-event IMPRESSIONS --lifetime-budget 300 --end 2027-02-01 --frequency-cap 2/7 --schedule \"Mon-Fri 09-18\" --targeting-file t.json`,\n },\n args: {\n campaign: { type: \"string\", description: \"Parent campaign id or meta_temp_* ref\" },\n ...adSetWriteArgs,\n \"budget-boost\": {\n type: \"string\",\n description:\n \"Raise this ad set's daily budget for a high-demand period, repeatable (on a live ad set, `adsets update --budget-boost` adds one beside those it has): START..END=+AMOUNT or START..END=+PERCENT% (max 800%), at least 3 hours, on quarter hours, all amounts or all percentages. Needs --daily-budget. Example: --budget-boost 2027-11-27T00:00Z..2027-11-28T00:00Z=+50%\",\n },\n \"dynamic-creative\": {\n type: \"boolean\",\n description:\n \"Make this a Dynamic Creative ad set: its ONE ad runs a creativeType dynamic creative (Meta mixes the images, videos and texts). Create-only — Meta fixes it for the ad set's life.\",\n },\n \"saved-audience\": {\n type: \"string\",\n description:\n \"Start from a saved audience's targeting (id from `baker ads meta audiences saved`) — copied into the ad set; instead of --targeting-file, and --block-list / --content-filter / --exclude-publisher-category join it\",\n },\n },\n run: async ({ args, rawArgs }) => {\n const accountId = bareAccountId(args as Record<string, unknown>);\n const file = loadJsonFileArg(args.file);\n const payload = mergePayload(file, adSetPayloadFromFlags(args as Record<string, unknown>, rawArgs, file));\n if (args[\"saved-audience\"]) {\n const { savedAudienceTargeting, targetingFromSavedAudience } = await import(\"./audience-writes.ts\");\n const saved = await savedAudienceTargeting(accountId, String(args[\"saved-audience\"]));\n const merged = targetingFromSavedAudience(saved, payload.targeting as Record<string, unknown> | undefined);\n if (\"refusal\" in merged) failWriteValidation(merged.refusal);\n payload.targeting = merged.targeting;\n }\n await stageOp({ kind: \"adSet.create\", accountId, payload });\n },\n});\n\nexport const adSetsUpdateCommand = defineCommand({\n meta: {\n name: \"update\",\n description: `Stage changes to an ad set. ${STAGED_NOTE}\nSwitching --optimization-goal to a lead-form goal (LEAD_GENERATION, QUALITY_LEAD) or PAGE_LIKES needs --page-id in the same change.\nTake a control away with --clear: baker ads meta adsets update 6123 --clear daily-spend-cap,schedule\nBudget boosts on a live ad set with its own daily budget: add with --budget-boost, take one away with --remove-boost, re-time one with --move-boost (ids from \\`baker ads meta high-demand-periods --campaign-id <ad set id>\\`).\nExample: baker ads meta adsets update 6123 --daily-budget 80`,\n },\n args: {\n id: { type: \"positional\", description: \"Ad set id or meta_temp_* ref\", required: true },\n ...adSetWriteArgs,\n \"budget-boost\": {\n type: \"string\",\n description:\n \"Add a budget boost to this ad set (its own daily budget is raised for a high-demand period), repeatable: START..END=+AMOUNT or START..END=+PERCENT% (max 800%), at least 3 hours, on quarter hours. Sent to Meta's boost list for the ad set, so the budget need not be restated. Example: --budget-boost 2027-11-27T00:00Z..2027-11-28T00:00Z=+50%\",\n },\n ...boostEditArgs,\n ...clearArg,\n },\n run: async ({ args, rawArgs }) => {\n const accountId = bareAccountId(args as Record<string, unknown>);\n const file = loadJsonFileArg(args.file);\n const {\n campaign_id: _drop,\n billing_event: _be,\n ...payload\n } = adSetPayloadFromFlags(args as Record<string, unknown>, rawArgs, file);\n const merged = mergePayload(file, {\n ...payload,\n ...boostEditsFromFlags(args as Record<string, unknown>, rawArgs),\n clear: clearOrFail(rawArgs, \"adSet\"),\n });\n refuseDeleteByUpdate(merged, \"adsets\");\n await stageOp({\n kind: \"adSet.update\",\n accountId,\n target: requireTarget(args as Record<string, unknown>, \"ad set\"),\n payload: merged,\n });\n },\n});\n\n// --- Ad creatives ---\n\nfunction creativePayloadFromFlags(args: Record<string, unknown>, rawArgs: readonly string[]): Record<string, unknown> {\n const enhancements = creativeEnhancementsFromFlags(args, rawArgs);\n return {\n // Leave undefined when --type is omitted so a --file's creativeType survives\n // mergePayload (any defined flag overrides the file). Defaulted after merge.\n creativeType: args.type ? String(args.type) : undefined,\n name: args.name,\n page_id: args.page,\n instagram_user_id: args[\"instagram-user\"],\n message: args.message,\n link: args.link,\n headline: args.headline,\n description: args.description,\n caption: args.caption,\n call_to_action: creativeCta(args),\n welcome_message: welcomeMessageFromFlags(args, rawArgs),\n applink_treatment: args[\"applink-treatment\"],\n product_set_id: args[\"product-set-id\"],\n canvas_id: args[\"canvas-id\"],\n format: args.format,\n imageHash: args[\"image-hash\"],\n imageRef: args[\"image-ref\"],\n videoId: args[\"video-id\"],\n videoRef: args[\"video-ref\"],\n thumbnailHash: args[\"thumbnail-hash\"],\n thumbnailRef: args[\"thumbnail-ref\"],\n url_tags: args[\"url-tags\"],\n shop_destination: args[\"shop-destination\"],\n multi_advertiser: args[\"multi-advertiser\"],\n partner: partnerFromFlags(args),\n object_story_id: args[\"post-id\"],\n source_instagram_media_id: args[\"instagram-media-id\"],\n instagram_ad_code: args[\"ad-code\"],\n collection_thumbnails: collectionThumbnailsFromFlags(rawArgs, args[\"collection-thumbnail\"]),\n enhancements,\n ...carouselFlags(args),\n };\n}\n\n/** `--collection-thumbnail <element id>[:<product index>]`, repeatable — the four pictures under a collection's hero. */\nexport function collectionThumbnailsFromFlags(\n rawArgs: readonly string[] | undefined,\n parsed: unknown,\n): Array<{ element_id: string; element_child_index?: number }> | undefined {\n const values = repeatedValues(rawArgs, \"collection-thumbnail\", parsed);\n if (values.length === 0) return undefined;\n return values.map((value) => {\n const [elementId = \"\", index] = value.split(\":\");\n return index === undefined\n ? { element_id: elementId }\n : { element_id: elementId, element_child_index: Number(index) };\n });\n}\n\n/** A partnership ad's second identity, from `--partner-page` / `--partner-instagram` / `--partner-format`. */\nexport function partnerFromFlags(args: Record<string, unknown>): Record<string, unknown> | undefined {\n const partner = {\n facebook_page_id: args[\"partner-page\"],\n instagram_user_id: args[\"partner-instagram\"],\n ad_format: args[\"partner-format\"],\n };\n return Object.values(partner).some((value) => value !== undefined) ? partner : undefined;\n}\n\n/**\n * Only the call-to-action fields the caller actually named. `--lead-form` is the\n * only way to name an instant form: the ad set says leads are collected on the\n * ad but never WHICH form, so a creative without this makes Meta reject every ad\n * in that ad set.\n *\n * Partial on purpose. On `update` this becomes a patch over the creative already\n * staged, and a complete object here would silently undo the other half —\n * `--cta LEARN_MORE` would drop a form the creative already names (re-creating\n * the very publish failure this exists to stop), and `--lead-form <id>` would\n * rewrite an already-chosen button to SIGN_UP.\n */\nexport function ctaPatchFromFlags(args: Record<string, unknown>): Record<string, string> | undefined {\n const patch = {\n ...(args.cta ? { type: String(upper(args.cta)) } : {}),\n ...(args[\"lead-form\"] === undefined ? {} : { lead_gen_form_id: String(args[\"lead-form\"]) }),\n ...conversationAndAppCta(args),\n };\n return Object.keys(patch).length > 0 ? patch : undefined;\n}\n\n/**\n * The call-to-action for a NEW creative, where there is nothing to patch over.\n * Defaults the type to SIGN_UP when a form is named and no --cta is: an\n * instant-form ad has to have a button, and that is the one Meta suggests.\n */\nexport function creativeCta(args: Record<string, unknown>): Record<string, string> | undefined {\n const patch = ctaPatchFromFlags(args);\n if (!patch) return undefined;\n // An instant form's button defaults to SIGN_UP; a conversation's, to the one that opens it.\n const opener = Object.entries({\n MESSENGER: \"MESSAGE_PAGE\",\n WHATSAPP: \"WHATSAPP_MESSAGE\",\n INSTAGRAM_DIRECT: \"INSTAGRAM_MESSAGE\",\n }).find(([app]) => app === patch.app_destination)?.[1];\n return { type: opener ?? (patch.app_link || patch.application ? \"INSTALL_MOBILE_APP\" : \"SIGN_UP\"), ...patch };\n}\n\n/**\n * The content patch for `creatives update`, merged rather than assigned at both\n * levels. `mergePayload` and the backend's `mergeAmendPayload` are both shallow\n * top-level merges, so building a fresh `content` here would throw away every\n * other field a `--file` supplied — silently, which is the failure shape this\n * whole change is about.\n */\nexport function creativeContentPatch(file: Record<string, unknown>, args: Record<string, unknown>): unknown {\n const cta = ctaPatchFromFlags(args);\n if (!cta) {\n return undefined;\n }\n const fileContent = (file.content ?? {}) as Record<string, unknown>;\n const fileCta = (fileContent.call_to_action ?? {}) as Record<string, unknown>;\n return { ...fileContent, call_to_action: { ...fileCta, ...cta } };\n}\n\nexport const adCreativesCreateCommand = defineCommand({\n meta: {\n name: \"create\",\n description: `Stage a new ad creative. ${STAGED_NOTE}\nSingle-image/video via flags; carousel/dynamic via --file (creativeType carousel|dynamic).\nClick-to-message: --cta MESSAGE_PAGE|WHATSAPP_MESSAGE|INSTAGRAM_MESSAGE (opens Messenger|WhatsApp|Instagram Direct) + --greeting \"Hi!\" --ice-breaker \"Prices? => From 99 EUR.\" (ad set: --destination-type MESSENGER --optimization-goal CONVERSATIONS --page-id).\nApp ads: --link <App Store / Google Play URL> --cta INSTALL_MOBILE_APP|USE_MOBILE_APP --deep-link myapp://x --app-id <id> --applink-treatment deeplink_with_appstore_fallback.\nCatalog (Advantage+ catalog ads): --type catalog --product-set-id <id> --message \"{{product.name}}\" --headline \"{{product.price}}\" --link https://shop.example --format carousel|single|slideshow.\nInstant Experience / collection: --type instant_experience --canvas-id <id or meta_temp_*> --message … --image-hash … [--format collection] (build one with \\`baker ads meta instant-experiences create\\`).\nReference media by --image-hash (already on Meta) or --image-ref meta_temp_* (a staged media upload).\nExample: baker ads meta creatives create --page 555 --message \"Save now\" --link https://x.com --headline \"Best cover\" --image-hash abc123… --cta SHOP_NOW`,\n },\n args: {\n ...accountArgs,\n type: {\n type: \"string\",\n description: \"single|carousel|dynamic|existing_post|catalog|instant_experience (default single)\",\n },\n \"product-set-id\": {\n type: \"string\",\n description:\n \"--type catalog: the product set the template shows — from `baker ads meta catalogs --catalog-id <id>`\",\n },\n \"canvas-id\": {\n type: \"string\",\n description:\n \"--type instant_experience: the Instant Experience the ad opens — an id from `baker ads meta instant-experiences --page-id <id>` (published) or the meta_temp_* ref of one this chat creates\",\n },\n format: {\n type: \"string\",\n description:\n \"--type catalog: carousel (default) | single (one product per ad) | slideshow. --type instant_experience: standard (default) | collection (products or photos under the ad)\",\n },\n greeting: {\n type: \"string\",\n description: \"Click-to-message: the first message the person reads when the conversation opens (≤300 chars)\",\n },\n \"ice-breaker\": {\n type: \"string\",\n description:\n 'Click-to-message, repeatable (max 4): a question the person can tap and your answer — \"Question => Answer\"',\n },\n autofill: {\n type: \"string\",\n description: \"Click-to-message (instead of ice breakers): a message pre-filled for the person to send\",\n },\n \"app-destination\": {\n type: \"string\",\n description:\n \"Click-to-message: MESSENGER|WHATSAPP|INSTAGRAM_DIRECT — implied by --cta MESSAGE_PAGE|WHATSAPP_MESSAGE|INSTAGRAM_MESSAGE\",\n },\n \"deep-link\": {\n type: \"string\",\n description:\n \"App ads: where the button opens inside the installed app (e.g. myapp://product/12). --link must be the App Store / Google Play page\",\n },\n \"app-id\": {\n type: \"string\",\n description: \"App ads: the app the button installs or opens — from `baker ads meta apps`\",\n },\n \"applink-treatment\": {\n type: \"string\",\n description:\n \"App ads: how a deep link opens — deeplink_with_web_fallback|deeplink_with_appstore_fallback|web_only|automatic\",\n },\n name: { type: \"string\", description: \"Creative name\" },\n page: { type: \"string\", description: \"Facebook Page id (identity)\" },\n \"instagram-user\": {\n type: \"string\",\n description: \"Instagram account id for IG placements — from `baker ads meta instagram-accounts`\",\n },\n message: { type: \"string\", description: \"Primary text\" },\n link: { type: \"string\", description: \"Destination URL (https)\" },\n headline: { type: \"string\", description: \"Headline\" },\n description: { type: \"string\", description: \"Description\" },\n caption: { type: \"string\", description: \"Display URL / caption\" },\n cta: { type: \"string\", description: \"Call-to-action type (SHOP_NOW|LEARN_MORE|SIGN_UP|…)\" },\n \"lead-form\": {\n type: \"string\",\n description:\n \"Instant form id or meta_temp_* ref from `baker ads meta lead-forms create` — REQUIRED for an ad set that collects leads on the ad. With a form, --cta must be APPLY_NOW|DOWNLOAD|GET_QUOTE|LEARN_MORE|SIGN_UP|SUBSCRIBE\",\n },\n \"image-hash\": { type: \"string\", description: \"Meta ad-image hash (already uploaded)\" },\n \"image-ref\": { type: \"string\", description: \"meta_temp_* ref of a staged media upload\" },\n \"video-id\": { type: \"string\", description: \"Meta video id (already uploaded)\" },\n \"video-ref\": { type: \"string\", description: \"meta_temp_* ref of a staged video upload\" },\n \"thumbnail-hash\": {\n type: \"string\",\n description:\n \"Cover for a video ad: a picture hash already in the ad account (Meta's own frame is used when no cover is given)\",\n },\n \"thumbnail-ref\": {\n type: \"string\",\n description:\n \"Cover for a video ad: meta_temp_* ref of a staged `media upload --kind image` — how a cover picked from the library is used\",\n },\n \"url-tags\": { type: \"string\", description: \"URL tracking params (utm_source=fb&…)\" },\n \"shop-destination\": {\n type: \"string\",\n description:\n \"website_only keeps a shop advertiser's ad on the website (Meta's v26 default also sends it to the shop); website_and_shop asks for both\",\n },\n \"standard-enhancements\": {\n type: \"string\",\n description: \"Meta's suggestions to the ad's TEXT on|off — never changes the picture or video\",\n },\n \"multi-advertiser\": {\n type: \"string\",\n description:\n \"opt_out keeps the ad from being shown beside other advertisers' ads after someone engages (Meta opts every new ad IN by default); opt_in asks for it\",\n },\n \"partner-page\": {\n type: \"string\",\n description:\n \"Partnership ad: the partner's (creator's or other brand's) Facebook Page id, shown beside yours. Needs --instagram-user on the ad and the partner's approval before it runs\",\n },\n \"partner-instagram\": { type: \"string\", description: \"Partnership ad: the partner's Instagram account id\" },\n \"partner-format\": {\n type: \"string\",\n description: \"Partnership ad: both (default — both identities) | first_only | auto (Meta picks, Instagram only)\",\n },\n \"collection-thumbnail\": {\n type: \"string\",\n description:\n \"--format collection over a photo experience, repeat 4 times: an element id of the live experience (`instant-experiences --page-id`), or <product-set element id>:<product index>\",\n },\n \"post-id\": {\n type: \"string\",\n description:\n \"--type existing_post: the Facebook post to boost, <page_id>_<post_id> — from `baker ads meta page-posts --page-id <id>`\",\n },\n \"instagram-media-id\": {\n type: \"string\",\n description:\n \"--type existing_post: the Instagram post to boost (with --page and --instagram-user) — from `baker ads meta instagram-media`\",\n },\n \"ad-code\": {\n type: \"string\",\n description: \"--type existing_post: a creator's partnership ad code for their Instagram post (with --page)\",\n },\n \"card-order\": {\n type: \"string\",\n description:\n \"Carousel only: fixed (default — every card, in the order built) or optimized (Meta reorders by performance and may show only the top 5)\",\n },\n \"end-card\": {\n type: \"string\",\n description: \"Carousel only: on adds Meta's card linking to the Page after the last card (default off)\",\n },\n \"visual-enhancement\": {\n type: \"string\",\n description:\n \"Repeatable. Let Meta change the media with this feature (e.g. image_uncrop, image_animation, music_generation) — ONLY when the user asked for it by name; every such feature is off otherwise\",\n },\n file: { type: \"string\", description: \"JSON file with the full creative payload (carousel cards / dynamic assets)\" },\n },\n run: async ({ args, rawArgs }) => {\n const accountId = bareAccountId(args as Record<string, unknown>);\n const payload = mergePayload(\n loadJsonFileArg(args.file),\n creativePayloadFromFlags(args as Record<string, unknown>, rawArgs),\n );\n // Flag-only creatives default to a single-image ad; a --file that declares\n // its own creativeType (carousel/dynamic/existing_post) is left untouched.\n if (payload.creativeType === undefined) {\n payload.creativeType = \"single\";\n }\n await stageOp({ kind: \"adCreative.create\", accountId, payload });\n },\n});\n\nexport const adCreativesUpdateCommand = defineCommand({\n meta: {\n name: \"update\",\n description: `Stage name/status changes to an ad creative. ${STAGED_NOTE}\nMeta creatives are immutable — to change content, stage a new creative and repoint the ad.\nAmending a staged (meta_temp_*) creative merges fields into the create. Example: baker ads meta creatives update 998 --name \"Renamed\"`,\n },\n args: {\n id: { type: \"positional\", description: \"Creative id or meta_temp_* ref\", required: true },\n ...accountArgs,\n name: { type: \"string\", description: \"New name\" },\n status: { type: \"string\", description: \"ACTIVE|PAUSED|ARCHIVED\" },\n cta: { type: \"string\", description: \"Call-to-action type (staged creatives only)\" },\n \"lead-form\": {\n type: \"string\",\n description: \"Instant form id the button opens (staged creatives only) — `baker ads meta lead-forms` lists them\",\n },\n file: {\n type: \"string\",\n description: \"JSON file with fields to change (content only merges into a staged creative)\",\n },\n },\n run: async ({ args }) => {\n const accountId = bareAccountId(args as Record<string, unknown>);\n const file = loadJsonFileArg(args.file);\n const payload = mergePayload(file, {\n name: args.name,\n status: upper(args.status),\n // A creative's content is immutable on Meta, so this only ever lands on a\n // creative still staged in this chat — which is exactly the fix path when\n // staging an ad refuses it for naming no instant form.\n content: creativeContentPatch(file, args as Record<string, unknown>),\n });\n if (payload.status === \"DELETED\") {\n failWriteValidation(\n \"update does not delete — use `baker ads meta creatives delete <id>` if the user asked to delete it\",\n );\n }\n await stageOp({\n kind: \"adCreative.update\",\n accountId,\n target: requireTarget(args as Record<string, unknown>, \"creative\"),\n payload,\n });\n },\n});\n\n// --- Ads ---\n\nexport const adsCreateCommand = defineCommand({\n meta: {\n name: \"create\",\n description: `Stage a new ad linking an ad set to a creative. ${STAGED_NOTE}\nExample: baker ads meta ads create --name \"Ad 1\" --adset meta_temp_as01 --creative meta_temp_cr01`,\n },\n args: {\n ...accountArgs,\n name: { type: \"string\", description: \"Ad name\" },\n adset: { type: \"string\", description: \"Ad set id or meta_temp_* ref\" },\n creative: { type: \"string\", description: \"Creative id or meta_temp_* ref\" },\n status: { type: \"string\", description: \"ACTIVE|PAUSED (default PAUSED)\" },\n \"tracking-file\": { type: \"string\", description: \"JSON file with tracking_specs\" },\n \"conversion-domain\": {\n type: \"string\",\n description:\n \"The website domain conversions are counted on (example.com — no www., no https://). Meta fills it from the link when left out; set it when the site and the pixel's domain differ\",\n },\n file: { type: \"string\", description: \"JSON file with the full payload\" },\n },\n run: async ({ args }) => {\n const accountId = bareAccountId(args as Record<string, unknown>);\n const tracking = args[\"tracking-file\"] ? loadJsonFileArg(args[\"tracking-file\"]).tracking_specs : undefined;\n const payload = mergePayload(loadJsonFileArg(args.file), {\n name: args.name,\n adset_id: args.adset,\n creative: args.creative ? { creative_id: String(args.creative) } : undefined,\n status: upper(args.status),\n tracking_specs: tracking,\n conversion_domain: args[\"conversion-domain\"],\n });\n await stageOp({ kind: \"ad.create\", accountId, payload });\n },\n});\n\nexport const adsUpdateCommand = defineCommand({\n meta: {\n name: \"update\",\n description: `Stage changes to an ad (name/status, or swap its creative). ${STAGED_NOTE}\nExample: baker ads meta ads update 6123 --creative meta_temp_cr02`,\n },\n args: {\n id: { type: \"positional\", description: \"Ad id or meta_temp_* ref\", required: true },\n ...accountArgs,\n name: { type: \"string\", description: \"New name\" },\n status: { type: \"string\", description: \"ACTIVE|PAUSED|ARCHIVED\" },\n creative: { type: \"string\", description: \"New creative id or meta_temp_* ref\" },\n file: { type: \"string\", description: \"JSON file with fields to change\" },\n },\n run: async ({ args }) => {\n const accountId = bareAccountId(args as Record<string, unknown>);\n const payload = mergePayload(loadJsonFileArg(args.file), {\n name: args.name,\n status: upper(args.status),\n creative: args.creative ? { creative_id: String(args.creative) } : undefined,\n });\n refuseDeleteByUpdate(payload, \"ads\");\n await stageOp({\n kind: \"ad.update\",\n accountId,\n target: requireTarget(args as Record<string, unknown>, \"ad\"),\n payload,\n });\n },\n});\n\nexport const adsRetagCommand = defineCommand({\n meta: {\n name: \"retag\",\n description: `Stage new URL tracking parameters on a LIVE ad — the fix for a wrong utm_medium, a missing ad id, or any tag already running. ${STAGED_NOTE}\nStart here when a report attributes paid traffic to the wrong channel: --set rewrites only the keys you name and leaves the rest of the string alone.\nBaker clones the ad's own creative behind the scenes, so every button, text variant and enhancement is carried across exactly as Meta holds them — nothing is retyped.\nExample: baker ads meta ads retag 120252158038400247 --set \"utm_medium=paid-social&utm_placement={{placement}}\"\nExample: baker ads meta ads retag 120252158038400247 --url-tags \"utm_source={{site_source_name}}&utm_medium=paid-social&ad_id={{ad.id}}\"`,\n },\n args: {\n id: { type: \"positional\", description: \"Ad id (a live ad — not a meta_temp_* ref)\", required: true },\n ...accountArgs,\n \"url-tags\": { type: \"string\", description: \"Replace the WHOLE tracking string\" },\n set: {\n type: \"string\",\n description:\n 'Rewrite only these keys, keeping the rest: \"utm_medium=paid-social&ad_id={{ad.id}}\" (repeatable: --set a=1 --set b=2)',\n },\n },\n run: async ({ args, rawArgs }) => {\n const { apiPost } = await import(\"../../../client.ts\");\n const { requireChatId } = await import(\"../../../env.ts\");\n const { writeJsonEnvelope } = await import(\"../output.ts\");\n const { handleMetaError } = await import(\"./shared.ts\");\n try {\n const accountId = bareAccountId(args as Record<string, unknown>);\n const urlTags = typeof args[\"url-tags\"] === \"string\" ? args[\"url-tags\"] : undefined;\n const setRaw = typeof args.set === \"string\" ? args.set : undefined;\n if ((urlTags === undefined) === (setRaw === undefined)) {\n failWriteValidation(\"pass either --url-tags (the whole string) or --set (the keys to change), not both\");\n }\n const response = await apiPost(\"/api/ads/meta/draft/retag\", {\n chatId: requireChatId(),\n accountId,\n adId: requireTarget(args as Record<string, unknown>, \"ad\"),\n urlTags,\n set: setRaw === undefined ? undefined : retagSetPairs(rawArgs, setRaw),\n });\n writeJsonEnvelope(response as Record<string, unknown>);\n } catch (err) {\n handleMetaError(err);\n }\n },\n});\n\n/**\n * Every `--set`, whether joined by `&` or given as the flag repeated. citty keeps\n * only a repeated flag's last value, and a retag that silently loses all but one\n * key still publishes — so the command line is read directly.\n */\nexport function retagSetPairs(rawArgs: readonly string[] | undefined, parsed: unknown): Record<string, string> {\n return parseSetPairs(repeatedValues(rawArgs, \"set\", parsed).join(\"&\"));\n}\n\nfunction parseSetPairs(raw: string): Record<string, string> {\n const pairs: Record<string, string> = {};\n for (const part of raw.replace(/^[?&]+/, \"\").split(\"&\")) {\n if (part.length === 0) continue;\n const eq = part.indexOf(\"=\");\n if (eq <= 0) {\n failWriteValidation(`--set expects key=value pairs joined by & — could not read \"${part}\"`);\n }\n pairs[part.slice(0, eq)] = part.slice(eq + 1);\n }\n if (Object.keys(pairs).length === 0) {\n failWriteValidation(\"--set needs at least one key=value pair\");\n }\n return pairs;\n}\n\n// --- Custom audiences ---\n\nexport const audiencesCreateCommand = defineCommand({\n meta: {\n name: \"create\",\n description: `Stage a new custom audience. ${STAGED_NOTE}\nInline customer list: --subtype CUSTOM --file with \"members\": [{\"email\",\"phone\",\"country\"}] (up to 2,000 people, hashed before storage, uploaded after creation at Publish).\nCustomer list: --subtype CUSTOM --customer-file-source USER_PROVIDED_ONLY, then put people in it with \\`audiences members add <ref> --file list.csv\\` (hashed on this machine). --value-based for a list where each person carries a value (needs the account's value-based terms).\nLookalike: --subtype LOOKALIKE --lookalike-of <seed audience> --ratio 0.02 --country ES (or --countries ES,PT; --starting-ratio 0.01 for the 1%–2% band). The seed needs 100+ people in those countries. Or seed it from the people who converted in live campaigns: --lookalike-of-conversions <campaign or ad set id,…>.\nWEBSITE/ENGAGEMENT: the rule via --file. A rule carries its own window in retention_seconds, so --retention-days is refused next to one, and one audience covers one kind of event source (a Page and an Instagram profile are two audiences).\nExample: baker ads meta audiences create --name \"Site visitors 30d\" --subtype WEBSITE --file rule.json\nExample: baker ads meta audiences create --name \"Customers\" --subtype CUSTOM --customer-file-source USER_PROVIDED_ONLY`,\n },\n args: {\n ...accountArgs,\n name: { type: \"string\", description: \"Audience name\" },\n subtype: { type: \"string\", description: \"CUSTOM|WEBSITE|ENGAGEMENT|LOOKALIKE\" },\n description: { type: \"string\", description: \"Description\" },\n \"retention-days\": {\n type: \"string\",\n description: \"Retention window in days — customer-file audiences only; a rule carries its own retention_seconds\",\n },\n \"customer-file-source\": {\n type: \"string\",\n description:\n \"Customer list: where its people came from — USER_PROVIDED_ONLY (collected from customers directly) | PARTNER_PROVIDED_ONLY | BOTH_USER_AND_PARTNER_PROVIDED\",\n },\n \"value-based\": {\n type: \"boolean\",\n description: \"Customer list where each person carries a value (LOOKALIKE_VALUE column)\",\n },\n \"lookalike-of\": {\n type: \"string\",\n description: \"Lookalike: the seed audience id (a live audience — one this chat creates cannot be a seed yet)\",\n },\n \"lookalike-of-conversions\": {\n type: \"string\",\n description:\n \"Lookalike seeded from the people who converted in live campaigns or ad sets (ids, comma-separated or repeated) instead of an audience. Meta wants 100+ conversions in the lookalike's countries.\",\n },\n ratio: { type: \"string\", description: \"Lookalike size: 0.01–0.20 of the country, in 0.01 steps\" },\n \"starting-ratio\": {\n type: \"string\",\n description: \"Lookalike band start, below --ratio (0.01 with --ratio 0.03 = the 1%–3% band)\",\n },\n country: { type: \"string\", description: \"Lookalike: one country (ES)\" },\n countries: { type: \"string\", description: \"Lookalike: several countries (ES,PT — comma-separated or repeated)\" },\n \"allow-international-seeds\": {\n type: \"boolean\",\n description: \"Lookalike: let Meta use seed people from other countries when the seed is thin in these\",\n },\n file: {\n type: \"string\",\n description: \"JSON (a path, or inline) with lookalike_spec / rule / members / full payload\",\n },\n },\n run: async ({ args, rawArgs }) => {\n const accountId = bareAccountId(args as Record<string, unknown>);\n const file = loadJsonFileArg(args.file);\n const payload = mergePayload(file, {\n name: args.name,\n subtype: upper(args.subtype),\n description: args.description,\n retention_days: numberFlag(args[\"retention-days\"], \"--retention-days\"),\n customer_file_source: upper(args[\"customer-file-source\"]),\n is_value_based: args[\"value-based\"] ? true : undefined,\n lookalike_spec: lookalikeSpecFromFlags(args as Record<string, unknown>, file.lookalike_spec, rawArgs),\n });\n await stageOp({ kind: \"customAudience.create\", accountId, payload });\n },\n});\n\n/** The lookalike flags folded over a spec from --file; undefined when neither names one. */\nexport function lookalikeSpecFromFlags(\n args: Record<string, unknown>,\n fromFile: unknown,\n rawArgs?: readonly string[],\n): Record<string, unknown> | undefined {\n const base = fromFile && typeof fromFile === \"object\" ? (fromFile as Record<string, unknown>) : {};\n const flags = {\n origin: typeof args[\"lookalike-of\"] === \"string\" ? [{ id: args[\"lookalike-of\"] }] : undefined,\n conversions_of: repeatedListFlag(rawArgs, \"lookalike-of-conversions\", args[\"lookalike-of-conversions\"]),\n ratio: numberFlag(args.ratio, \"--ratio\"),\n starting_ratio: numberFlag(args[\"starting-ratio\"], \"--starting-ratio\"),\n country: typeof args.country === \"string\" ? args.country.toUpperCase() : undefined,\n countries: repeatedListFlag(rawArgs, \"countries\", args.countries)?.map((code) => code.toUpperCase()),\n allow_international_seeds: args[\"allow-international-seeds\"] ? true : undefined,\n };\n const merged = mergePayload(base, flags);\n return Object.keys(merged).length > 0 ? merged : undefined;\n}\n\nexport const audiencesUpdateCommand = defineCommand({\n meta: {\n name: \"update\",\n description: `Stage changes to a custom audience: name, description, where a customer list came from, its opt-out link, or a website/engagement rule (--file {\"rule\": …}). A customer list's retention and a lookalike's seed and size are fixed — a different one is a new audience. Delete with \\`audiences delete\\`. ${STAGED_NOTE}`,\n },\n args: {\n id: { type: \"positional\", description: \"Audience id or meta_temp_* ref\", required: true },\n ...accountArgs,\n name: { type: \"string\", description: \"New name\" },\n description: { type: \"string\", description: \"New description\" },\n \"customer-file-source\": {\n type: \"string\",\n description: \"USER_PROVIDED_ONLY | PARTNER_PROVIDED_ONLY | BOTH_USER_AND_PARTNER_PROVIDED\",\n },\n \"opt-out-link\": { type: \"string\", description: \"https link where people can opt out of this audience\" },\n file: { type: \"string\", description: \"JSON file with fields to change (rule for a website/engagement audience)\" },\n },\n run: async ({ args }) => {\n const accountId = bareAccountId(args as Record<string, unknown>);\n const payload = mergePayload(loadJsonFileArg(args.file), {\n name: args.name,\n description: args.description,\n customer_file_source: upper(args[\"customer-file-source\"]),\n opt_out_link: args[\"opt-out-link\"],\n });\n if (payload.status === \"DELETED\") {\n failWriteValidation(\n \"update does not delete — use `baker ads meta audiences delete <id>` if the user asked to delete it\",\n );\n }\n await stageOp({\n kind: \"customAudience.update\",\n accountId,\n target: requireTarget(args as Record<string, unknown>, \"audience\"),\n payload,\n });\n },\n});\n\n// --- Media upload ---\n\nexport const mediaUploadCommand = defineCommand({\n meta: {\n name: \"upload\",\n description: `Stage a Baker library image/video upload to Meta. Publishes the hash/id a creative references by --image-ref/--video-ref. ${STAGED_NOTE}\nExample: baker ads meta media upload --kind image --image-id <bakerImageId>`,\n },\n args: {\n ...accountArgs,\n kind: { type: \"string\", description: \"image|video\" },\n \"image-id\": { type: \"string\", description: \"Baker library image id (for --kind image)\" },\n \"video-id\": { type: \"string\", description: \"Baker library video id (for --kind video)\" },\n name: { type: \"string\", description: \"Display name / filename hint\" },\n },\n run: async ({ args }) => {\n const accountId = bareAccountId(args as Record<string, unknown>);\n const kind = String(args.kind ?? \"\").toLowerCase();\n if (kind !== \"image\" && kind !== \"video\") {\n failWriteValidation(\"--kind must be image or video\");\n }\n await stageOp({\n kind: \"media.upload\",\n accountId,\n payload: { kind, bakerImageId: args[\"image-id\"], bakerVideoId: args[\"video-id\"], name: args.name },\n });\n },\n});\n\n// --- Status-change + duplicate factories ---\n\ntype StatusKind = \"campaign.update\" | \"adSet.update\" | \"ad.update\";\n\nconst STATUS_BY_COMMAND = { pause: \"PAUSED\", resume: \"ACTIVE\", archive: \"ARCHIVED\", delete: \"DELETED\" } as const;\n\nconst STATUS_COMMAND_NOTE: Partial<Record<keyof typeof STATUS_BY_COMMAND, string>> = {\n archive: \"Archived stays readable in Ads Manager and can be found with --effective-status ARCHIVED.\",\n delete:\n \"Deleting is permanent and cannot be undone — Meta removes it and everything under it. Prefer pause or archive unless the user asked to delete.\",\n};\n\nfunction statusCommand(name: keyof typeof STATUS_BY_COMMAND, kind: StatusKind, label: string) {\n const status = STATUS_BY_COMMAND[name];\n const note = STATUS_COMMAND_NOTE[name];\n return defineCommand({\n meta: { name, description: `Stage ${name} for a ${label}. ${note ? `${note} ` : \"\"}${STAGED_NOTE}` },\n args: { id: { type: \"positional\", description: `${label} id`, required: true }, ...accountArgs },\n run: async ({ args }) => {\n const accountId = bareAccountId(args as Record<string, unknown>);\n await stageStatusChange({\n kind,\n accountId,\n target: requireTarget(args as Record<string, unknown>, label),\n status,\n });\n },\n });\n}\n\nfunction duplicateCommand(entity: \"campaign\" | \"adSet\" | \"ad\", label: string) {\n return defineCommand({\n meta: {\n name: \"duplicate\",\n description: `Stage a paused copy of an existing ${label} (reconstructed from its live snapshot). ${STAGED_NOTE}\n--replace also stages a pause of the original, gated on the copy publishing.`,\n },\n args: {\n id: { type: \"positional\", description: `Source ${label} id`, required: true },\n ...accountArgs,\n name: { type: \"string\", description: \"Override the copy's name\" },\n replace: { type: \"boolean\", description: \"Pause the original once the copy publishes\" },\n },\n run: async ({ args }) => {\n const { apiPost } = await import(\"../../../client.ts\");\n const { requireChatId } = await import(\"../../../env.ts\");\n const { writeJsonEnvelope } = await import(\"../output.ts\");\n const { handleMetaError } = await import(\"./shared.ts\");\n try {\n const accountId = bareAccountId(args as Record<string, unknown>);\n const chatId = requireChatId();\n const response = await apiPost(\"/api/ads/meta/draft/duplicate\", {\n chatId,\n accountId,\n entity,\n sourceId: requireTarget(args as Record<string, unknown>, label),\n overrides: args.name ? { name: args.name } : undefined,\n replace: Boolean(args.replace),\n });\n writeJsonEnvelope(response as Record<string, unknown>);\n } catch (err) {\n handleMetaError(err);\n }\n },\n });\n}\n\nexport const campaignsPauseCommand = statusCommand(\"pause\", \"campaign.update\", \"campaign\");\nexport const campaignsResumeCommand = statusCommand(\"resume\", \"campaign.update\", \"campaign\");\nexport const campaignsArchiveCommand = statusCommand(\"archive\", \"campaign.update\", \"campaign\");\nexport const campaignsDeleteCommand = statusCommand(\"delete\", \"campaign.update\", \"campaign\");\nexport const campaignsDuplicateCommand = duplicateCommand(\"campaign\", \"campaign\");\n\nexport const adSetsPauseCommand = statusCommand(\"pause\", \"adSet.update\", \"ad set\");\nexport const adSetsResumeCommand = statusCommand(\"resume\", \"adSet.update\", \"ad set\");\nexport const adSetsArchiveCommand = statusCommand(\"archive\", \"adSet.update\", \"ad set\");\nexport const adSetsDeleteCommand = statusCommand(\"delete\", \"adSet.update\", \"ad set\");\nexport const adSetsDuplicateCommand = duplicateCommand(\"adSet\", \"ad set\");\n\nexport const adsPauseCommand = statusCommand(\"pause\", \"ad.update\", \"ad\");\nexport const adsResumeCommand = statusCommand(\"resume\", \"ad.update\", \"ad\");\nexport const adsArchiveCommand = statusCommand(\"archive\", \"ad.update\", \"ad\");\nexport const adsDeleteCommand = statusCommand(\"delete\", \"ad.update\", \"ad\");\nexport const adsDuplicateCommand = duplicateCommand(\"ad\", \"ad\");\n","import { VISUAL_ENHANCEMENT_FEATURES } from \"@baker/api/ads-meta/limits\";\nimport { repeatedValues } from \"../../../lib/repeatedFlags.ts\";\nimport { failWriteValidation } from \"./write-shared.ts\";\n\ntype Args = Record<string, unknown>;\n\nfunction parseEnroll(value: unknown): \"OPT_IN\" | \"OPT_OUT\" {\n const raw = String(value).toLowerCase();\n if (raw === \"on\" || raw === \"true\" || raw === \"opt_in\") return \"OPT_IN\";\n if (raw === \"off\" || raw === \"false\" || raw === \"opt_out\") return \"OPT_OUT\";\n failWriteValidation(\"--standard-enhancements must be on|off\");\n}\n\n/**\n * The creative's Advantage+ enhancements from its flags. `--standard-enhancements`\n * is Meta's suggestions to the text; every feature that changes the picture or\n * video stays off unless named with `--visual-enhancement <feature>`.\n */\nexport function creativeEnhancementsFromFlags(\n args: Args,\n rawArgs: readonly string[] | undefined,\n): { standardEnhancements?: \"OPT_IN\" | \"OPT_OUT\"; features?: Record<string, \"OPT_IN\"> } | undefined {\n const visual = repeatedValues(rawArgs, \"visual-enhancement\", args[\"visual-enhancement\"]);\n const unknown = visual.filter((feature) => !(feature in VISUAL_ENHANCEMENT_FEATURES));\n if (unknown.length > 0) {\n failWriteValidation(\n `--visual-enhancement ${unknown.join(\", \")} is not a feature that changes the media — one of ${Object.keys(VISUAL_ENHANCEMENT_FEATURES).join(\", \")}`,\n );\n }\n const standard = args[\"standard-enhancements\"] !== undefined ? parseEnroll(args[\"standard-enhancements\"]) : undefined;\n if (!standard && visual.length === 0) return undefined;\n return {\n ...(standard ? { standardEnhancements: standard } : {}),\n ...(visual.length > 0\n ? { features: Object.fromEntries(visual.map((feature) => [feature, \"OPT_IN\" as const])) }\n : {}),\n };\n}\n\n/** A carousel's card order and end card — Meta's `multi_share_optimized` / `multi_share_end_card`. */\nexport function carouselFlags(args: Args): { card_order?: string; end_card?: boolean } {\n const order = args[\"card-order\"];\n if (order !== undefined && order !== \"fixed\" && order !== \"optimized\") {\n failWriteValidation(\"--card-order must be fixed (the order you built) or optimized (Meta reorders)\");\n }\n const endCard = args[\"end-card\"];\n return {\n ...(order !== undefined ? { card_order: String(order) } : {}),\n ...(endCard !== undefined ? { end_card: parseEnroll(endCard) === \"OPT_IN\" } : {}),\n };\n}\n\n/**\n * A click-to-message greeting from `--greeting`, every `--ice-breaker\n * \"Question => Answer\"` (repeatable) and `--autofill`.\n */\nexport function welcomeMessageFromFlags(\n args: Args,\n rawArgs: readonly string[] | undefined,\n): { greeting?: string; iceBreakers?: { question: string; answer: string }[]; autofill?: string } | undefined {\n const breakers = repeatedValues(rawArgs, \"ice-breaker\", args[\"ice-breaker\"]).map((entry) => {\n const [question, ...rest] = entry.split(\"=>\");\n const answer = rest.join(\"=>\").trim();\n if (!question?.trim() || !answer) {\n failWriteValidation(`--ice-breaker takes \"Question => Answer\" — got \"${entry}\"`);\n }\n return { question: question.trim(), answer };\n });\n if (args.greeting === undefined && breakers.length === 0 && args.autofill === undefined) return undefined;\n return {\n ...(args.greeting !== undefined ? { greeting: String(args.greeting) } : {}),\n ...(breakers.length > 0 ? { iceBreakers: breakers } : {}),\n ...(args.autofill !== undefined ? { autofill: String(args.autofill) } : {}),\n };\n}\n\n/** The app each messaging button opens (Meta's click-to-* guides). */\nconst BUTTON_OPENS: Record<string, string> = {\n MESSAGE_PAGE: \"MESSENGER\",\n WHATSAPP_MESSAGE: \"WHATSAPP\",\n INSTAGRAM_MESSAGE: \"INSTAGRAM_DIRECT\",\n};\n\n/**\n * The button fields of a click-to-message or app ad: which app a conversation\n * opens in (`--app-destination`, implied by a messaging `--cta`), the deep link\n * (`--deep-link`) and the app (`--app-id`).\n */\nexport function conversationAndAppCta(args: Args): Record<string, string> {\n const cta = args.cta === undefined ? undefined : String(args.cta).toUpperCase();\n const destination =\n args[\"app-destination\"] !== undefined\n ? String(args[\"app-destination\"]).toUpperCase()\n : cta\n ? BUTTON_OPENS[cta]\n : undefined;\n return {\n ...(destination ? { app_destination: destination } : {}),\n ...(args[\"deep-link\"] !== undefined ? { app_link: String(args[\"deep-link\"]) } : {}),\n ...(args[\"app-id\"] !== undefined ? { application: String(args[\"app-id\"]) } : {}),\n };\n}\n"],"mappings":"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","names":[]}