@koda-sl/baker-cli 0.526.0 → 0.528.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 (101) hide show
  1. package/README.md +2 -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-6ESCXPHK.js} +3 -3
  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/{performance-L3Q57H4H.js → performance-6XFKDJGY.js} +11 -2
  52. package/dist/performance-6XFKDJGY.js.map +1 -0
  53. package/dist/{pixels-AZCIWWXK.js → pixels-QOSEYXCP.js} +3 -3
  54. package/dist/pixels-QOSEYXCP.js.map +1 -0
  55. package/dist/{preview-BIEVHIDJ.js → preview-X6EJGPAY.js} +2 -2
  56. package/dist/{rule-writes-QLZNJ7CT.js → rule-writes-A4BFYHPA.js} +7 -7
  57. package/dist/rule-writes-A4BFYHPA.js.map +1 -0
  58. package/dist/{rules-FJHYG4SJ.js → rules-HLREXY2M.js} +11 -11
  59. package/dist/rules-HLREXY2M.js.map +1 -0
  60. package/dist/{shared-HY2VP7PT.js → shared-I3SB77VH.js} +2 -2
  61. package/dist/{studies-Z2ZIWLTO.js → studies-IGOCWM7F.js} +7 -7
  62. package/dist/{studies-Z2ZIWLTO.js.map → studies-IGOCWM7F.js.map} +1 -1
  63. package/dist/{study-writes-V7BXLTQG.js → study-writes-Z5P2W7JH.js} +6 -6
  64. package/dist/study-writes-Z5P2W7JH.js.map +1 -0
  65. package/dist/{targeting-YBIWD27C.js → targeting-7HXKBXWY.js} +12 -12
  66. package/dist/targeting-7HXKBXWY.js.map +1 -0
  67. package/dist/{write-commands-R5SN6CVQ.js → write-commands-CY734U34.js} +22 -22
  68. package/dist/write-commands-CY734U34.js.map +1 -0
  69. package/package.json +1 -1
  70. package/dist/account-assets-TAUEBCY5.js.map +0 -1
  71. package/dist/account-level-2MYDHOID.js.map +0 -1
  72. package/dist/ads-NNKUR4HG.js.map +0 -1
  73. package/dist/adsets-VO5C7PDM.js.map +0 -1
  74. package/dist/audience-writes-QDDADCYE.js.map +0 -1
  75. package/dist/audiences-TQ5ZEWIR.js.map +0 -1
  76. package/dist/campaigns-IQYYYD2S.js.map +0 -1
  77. package/dist/catalogs-PLBY32BE.js.map +0 -1
  78. package/dist/chunk-5LN7GWRE.js.map +0 -1
  79. package/dist/chunk-HPAGYPAF.js.map +0 -1
  80. package/dist/chunk-OEJNZSJG.js.map +0 -1
  81. package/dist/creatives-GTRQAZSB.js.map +0 -1
  82. package/dist/page-photos-SHIGXDYE.js.map +0 -1
  83. package/dist/performance-L3Q57H4H.js.map +0 -1
  84. package/dist/pixels-AZCIWWXK.js.map +0 -1
  85. package/dist/rule-writes-QLZNJ7CT.js.map +0 -1
  86. package/dist/rules-FJHYG4SJ.js.map +0 -1
  87. package/dist/study-writes-V7BXLTQG.js.map +0 -1
  88. package/dist/targeting-YBIWD27C.js.map +0 -1
  89. package/dist/write-commands-R5SN6CVQ.js.map +0 -1
  90. /package/dist/{accounts-VIAQC3PA.js.map → accounts-KRW3JNCW.js.map} +0 -0
  91. /package/dist/{ads-O4RL4JXD.js.map → ads-6ESCXPHK.js.map} +0 -0
  92. /package/dist/{asset-writes-PQNENVYY.js.map → asset-writes-SSI3CO2A.js.map} +0 -0
  93. /package/dist/{businesses-CW4RB36R.js.map → businesses-DXRDQIFP.js.map} +0 -0
  94. /package/dist/{chunk-XB2FODWU.js.map → chunk-TKDOVFO6.js.map} +0 -0
  95. /package/dist/{draft-ME4TNPO7.js.map → draft-G432FJ7N.js.map} +0 -0
  96. /package/dist/{insights-INIFHYR7.js.map → insights-IVW6W5CP.js.map} +0 -0
  97. /package/dist/{media-23IW442U.js.map → media-HDHZULQ7.js.map} +0 -0
  98. /package/dist/{meta-H3KBPU33.js.map → meta-MOSJVYIE.js.map} +0 -0
  99. /package/dist/{pages-XJF3FBOJ.js.map → pages-BAUS7LQM.js.map} +0 -0
  100. /package/dist/{preview-BIEVHIDJ.js.map → preview-X6EJGPAY.js.map} +0 -0
  101. /package/dist/{shared-HY2VP7PT.js.map → shared-I3SB77VH.js.map} +0 -0
@@ -2,7 +2,7 @@ import {
2
2
  csvOrJson,
3
3
  handleMetaError,
4
4
  resolveAccountIdArg
5
- } from "./chunk-HPAGYPAF.js";
5
+ } from "./chunk-BN4TDL5L.js";
6
6
  import "./chunk-QALAJNBG.js";
7
7
  import {
8
8
  writeAdsJson,
@@ -40,7 +40,7 @@ var listCommand = defineCommand({
40
40
  if (args["creative-id"]) {
41
41
  params["creative-id"] = args["creative-id"];
42
42
  } else {
43
- const accountId = resolveAccountIdArg(args);
43
+ const accountId = await resolveAccountIdArg(args);
44
44
  params["account-id"] = accountId;
45
45
  }
46
46
  if (args.limit) params.limit = args.limit;
@@ -72,9 +72,9 @@ Examples:
72
72
  },
73
73
  subCommands: {
74
74
  list: listCommand,
75
- create: () => import("./write-commands-R5SN6CVQ.js").then((m) => m.adCreativesCreateCommand),
76
- update: () => import("./write-commands-R5SN6CVQ.js").then((m) => m.adCreativesUpdateCommand),
77
- delete: () => import("./asset-writes-PQNENVYY.js").then((m) => m.creativesDeleteCommand)
75
+ create: () => import("./write-commands-CY734U34.js").then((m) => m.adCreativesCreateCommand),
76
+ update: () => import("./write-commands-CY734U34.js").then((m) => m.adCreativesUpdateCommand),
77
+ delete: () => import("./asset-writes-SSI3CO2A.js").then((m) => m.creativesDeleteCommand)
78
78
  },
79
79
  // `args` stays even though the parent has no `run`: citty scans rawArgs for the
80
80
  // subcommand name using THIS command's arg defs, so without them `--account-id act_1`
@@ -86,4 +86,4 @@ Examples:
86
86
  export {
87
87
  creativesCommand
88
88
  };
89
- //# sourceMappingURL=creatives-GTRQAZSB.js.map
89
+ //# sourceMappingURL=creatives-6VJDUHEY.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/ads/meta/creatives.ts"],"sourcesContent":["import { defineCommand } from \"citty\";\nimport { NO_CACHE_ARG } from \"../../../cache-flag.ts\";\nimport { apiGet } from \"../../../client.ts\";\nimport { writeAdsJson, writeAdsOutput } from \"../output.ts\";\nimport { csvOrJson, handleMetaError, resolveAccountIdArg } from \"./shared.ts\";\n\nconst listArgs = {\n \"account-id\": { type: \"string\", description: \"Meta ad account ID (required if no --creative-id)\" },\n \"creative-id\": { type: \"string\", description: \"Single creative ID\" },\n limit: { type: \"string\", description: \"Max rows (default 500)\" },\n \"no-cache\": NO_CACHE_ARG,\n output: { type: \"string\", description: \"Output format\", default: \"json\" },\n} as const;\n\nconst listCommand = defineCommand({\n meta: {\n name: \"list\",\n description:\n \"List ad creatives, or fetch one by ID. Runs by default when no subcommand is given. Example: baker ads meta creatives --account-id act_123\",\n },\n args: listArgs,\n run: async ({ args }) => {\n try {\n const params: Record<string, string> = {};\n if (args[\"creative-id\"]) {\n params[\"creative-id\"] = args[\"creative-id\"] as string;\n } else {\n const accountId = await resolveAccountIdArg(args);\n params[\"account-id\"] = accountId;\n }\n if (args.limit) params.limit = args.limit as string;\n if (args[\"no-cache\"]) params[\"skip-cache\"] = \"true\";\n const data = await apiGet<unknown>(\"/api/ads/meta/creatives\", params);\n const fmt = csvOrJson(args);\n if (Array.isArray(data) && fmt !== \"json\") {\n writeAdsOutput(data as Array<Record<string, unknown>>, fmt);\n return;\n }\n writeAdsJson({ ok: true, data });\n } catch (err) {\n handleMetaError(err);\n }\n },\n});\n\nexport const creativesCommand = defineCommand({\n meta: {\n name: \"creatives\",\n description: `List ad creatives, fetch one by ID, or stage writes (create/update). Write subcommands stage ops on this chat's draft.\n\nImportant: Meta creatives are effectively immutable once attached to an ad. Editing copy/image/CTA = create\na new creative + repoint the ad (baker ads meta ads update <adId> --creative <ref>).\n\nExamples:\n baker ads meta creatives --account-id act_123\n baker ads meta creatives --creative-id 23842... # fetch one\n baker ads meta creatives create --page 555 --message \"Save now\" --link https://x.com --image-hash abc… --cta SHOP_NOW`,\n },\n subCommands: {\n list: listCommand,\n create: () => import(\"./write-commands.ts\").then((m) => m.adCreativesCreateCommand),\n update: () => import(\"./write-commands.ts\").then((m) => m.adCreativesUpdateCommand),\n delete: () => import(\"./asset-writes.ts\").then((m) => m.creativesDeleteCommand),\n },\n // `args` stays even though the parent has no `run`: citty scans rawArgs for the\n // subcommand name using THIS command's arg defs, so without them `--account-id act_1`\n // would read `act_1` as a subcommand. `default` (not `run`) dispatches the bare\n // invocation — citty falls through to `run` after a subcommand, double-executing it.\n args: listArgs,\n default: \"list\",\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAAA,SAAS,qBAAqB;AAM9B,IAAM,WAAW;AAAA,EACf,cAAc,EAAE,MAAM,UAAU,aAAa,oDAAoD;AAAA,EACjG,eAAe,EAAE,MAAM,UAAU,aAAa,qBAAqB;AAAA,EACnE,OAAO,EAAE,MAAM,UAAU,aAAa,yBAAyB;AAAA,EAC/D,YAAY;AAAA,EACZ,QAAQ,EAAE,MAAM,UAAU,aAAa,iBAAiB,SAAS,OAAO;AAC1E;AAEA,IAAM,cAAc,cAAc;AAAA,EAChC,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,aACE;AAAA,EACJ;AAAA,EACA,MAAM;AAAA,EACN,KAAK,OAAO,EAAE,KAAK,MAAM;AACvB,QAAI;AACF,YAAM,SAAiC,CAAC;AACxC,UAAI,KAAK,aAAa,GAAG;AACvB,eAAO,aAAa,IAAI,KAAK,aAAa;AAAA,MAC5C,OAAO;AACL,cAAM,YAAY,MAAM,oBAAoB,IAAI;AAChD,eAAO,YAAY,IAAI;AAAA,MACzB;AACA,UAAI,KAAK,MAAO,QAAO,QAAQ,KAAK;AACpC,UAAI,KAAK,UAAU,EAAG,QAAO,YAAY,IAAI;AAC7C,YAAM,OAAO,MAAM,OAAgB,2BAA2B,MAAM;AACpE,YAAM,MAAM,UAAU,IAAI;AAC1B,UAAI,MAAM,QAAQ,IAAI,KAAK,QAAQ,QAAQ;AACzC,uBAAe,MAAwC,GAAG;AAC1D;AAAA,MACF;AACA,mBAAa,EAAE,IAAI,MAAM,KAAK,CAAC;AAAA,IACjC,SAAS,KAAK;AACZ,sBAAgB,GAAG;AAAA,IACrB;AAAA,EACF;AACF,CAAC;AAEM,IAAM,mBAAmB,cAAc;AAAA,EAC5C,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASf;AAAA,EACA,aAAa;AAAA,IACX,MAAM;AAAA,IACN,QAAQ,MAAM,OAAO,8BAAqB,EAAE,KAAK,CAAC,MAAM,EAAE,wBAAwB;AAAA,IAClF,QAAQ,MAAM,OAAO,8BAAqB,EAAE,KAAK,CAAC,MAAM,EAAE,wBAAwB;AAAA,IAClF,QAAQ,MAAM,OAAO,4BAAmB,EAAE,KAAK,CAAC,MAAM,EAAE,sBAAsB;AAAA,EAChF;AAAA;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM;AAAA,EACN,SAAS;AACX,CAAC;","names":[]}
@@ -2,7 +2,7 @@ import {
2
2
  csvOrJson,
3
3
  handleMetaError,
4
4
  resolveAccountIdArg
5
- } from "./chunk-HPAGYPAF.js";
5
+ } from "./chunk-BN4TDL5L.js";
6
6
  import "./chunk-QALAJNBG.js";
7
7
  import {
8
8
  writeAdsOutput,
@@ -79,7 +79,7 @@ var listCommand = defineCommand({
79
79
  },
80
80
  args: listArgs,
81
81
  run: async ({ args }) => {
82
- const accountId = resolveAccountIdArg(args);
82
+ const accountId = await resolveAccountIdArg(args);
83
83
  try {
84
84
  const params = { "account-id": accountId };
85
85
  if (args.limit) params.limit = args.limit;
@@ -110,9 +110,9 @@ Examples:
110
110
  },
111
111
  subCommands: {
112
112
  list: listCommand,
113
- create: () => import("./write-commands-R5SN6CVQ.js").then((m) => m.customConversionsCreateCommand),
114
- update: () => import("./asset-writes-PQNENVYY.js").then((m) => m.customConversionsUpdateCommand),
115
- delete: () => import("./asset-writes-PQNENVYY.js").then((m) => m.customConversionsDeleteCommand)
113
+ create: () => import("./write-commands-CY734U34.js").then((m) => m.customConversionsCreateCommand),
114
+ update: () => import("./asset-writes-SSI3CO2A.js").then((m) => m.customConversionsUpdateCommand),
115
+ delete: () => import("./asset-writes-SSI3CO2A.js").then((m) => m.customConversionsDeleteCommand)
116
116
  },
117
117
  // `args` stays even though the parent has no `run`: citty scans rawArgs for
118
118
  // the subcommand name using THIS command's arg defs. `default` (not `run`)
@@ -125,4 +125,4 @@ export {
125
125
  customConversionHints,
126
126
  customConversionsCommand
127
127
  };
128
- //# sourceMappingURL=custom-conversions-VQ3LBWYV.js.map
128
+ //# sourceMappingURL=custom-conversions-NDY5RHSM.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/commands/ads/meta/custom-conversions.ts"],"sourcesContent":["import { defineCommand } from \"citty\";\nimport { NO_CACHE_ARG } from \"../../../cache-flag.ts\";\nimport { apiGet } from \"../../../client.ts\";\nimport { registerSchema } from \"../../../schemas.ts\";\nimport { writeAdsOutput, writeJsonEnvelope } from \"../output.ts\";\nimport { csvOrJson, handleMetaError, resolveAccountIdArg } from \"./shared.ts\";\n\n/** A Custom Conversion as the backend returns it, rule already read back as a phrase. */\nexport interface MetaCustomConversion {\n id: string;\n name?: string;\n description?: string;\n custom_event_type?: string;\n event_source_type?: string;\n pixel?: { id?: string; name?: string };\n rule?: string;\n rule_summary?: string;\n default_conversion_value?: number;\n retention_days?: number;\n is_archived?: boolean;\n is_unavailable?: boolean;\n creation_time?: string;\n first_fired_time?: string;\n last_fired_time?: string;\n}\n\n/** What the caller needs to pick one. The raw rule and the timestamps sit behind --full. */\ninterface CompactCustomConversion {\n id: string;\n name?: string;\n custom_event_type?: string;\n counts?: string;\n pixel_id?: string;\n last_fired_time?: string;\n archived?: true;\n unavailable?: true;\n}\n\nexport function compactCustomConversion(row: MetaCustomConversion): CompactCustomConversion {\n return {\n id: row.id,\n name: row.name,\n custom_event_type: row.custom_event_type,\n counts: row.rule_summary,\n pixel_id: row.pixel?.id,\n last_fired_time: row.last_fired_time,\n ...(row.is_archived ? { archived: true as const } : {}),\n ...(row.is_unavailable ? { unavailable: true as const } : {}),\n };\n}\n\n/**\n * What to say about a listing, once.\n *\n * A conversion that has never fired is the one thing that reads as \"broken\" and\n * usually is not — Meta creates it counting nothing until the rule matches a\n * real event — while pointing an ad set at one is a real decision with a real\n * cost, because Meta cannot optimize toward an event it has never seen. Saying\n * so here is cheaper than the chat where it gets discovered after a week of\n * delivery.\n */\nexport function customConversionHints(rows: MetaCustomConversion[]): string[] {\n if (rows.length === 0) {\n return [\n \"This account has no custom conversions. Optimize toward a standard pixel event instead (`--pixel-id` + `--custom-event-type`), or stage one: `baker ads meta custom-conversions create --name … --pixel-id … --custom-event-type LEAD --url-contains /thank-you`.\",\n ];\n }\n const hints: string[] = [\n \"To optimize an ad set toward one: `baker ads meta adsets create … --optimization-goal OFFSITE_CONVERSIONS --custom-conversion-id <id>`. Baker fills the pixel and the event category from the conversion — do not also pass --pixel-id or --custom-event-type.\",\n ];\n const neverFired = rows.filter((row) => !row.is_archived && !row.last_fired_time);\n if (neverFired.length > 0) {\n hints.push(\n `${neverFired.length === 1 ? \"This conversion has\" : `${neverFired.length} of these have`} never fired (${neverFired\n .map((row) => row.name ?? row.id)\n .join(\n \", \",\n )}). Meta cannot optimize toward an event it has never seen, so an ad set pointed at one delivers without learning — check the rule matches a page the site actually serves before spending on it.`,\n );\n }\n return hints;\n}\n\nregisterSchema({\n command: \"ads.meta.custom-conversions\",\n description:\n \"List the Custom Conversions defined on a Meta ad account — the measurement objects a marketer sets up in Events Manager (a pixel event narrowed by a rule, e.g. 'PageView on /thank-you'). Start here whenever the user talks about their own conversions rather than a standard event: an ad set optimizes toward one with `adsets create --custom-conversion-id <id>`.\",\n args: {\n \"account-id\": { type: \"string\", description: \"Meta ad account ID (act_123)\", required: false },\n full: { type: \"boolean\", description: \"Include the raw rule JSON, retention and creation times\", required: false },\n limit: { type: \"string\", description: \"Max rows\", required: false },\n \"no-cache\": { type: \"boolean\", description: \"Bypass the cache\", required: false },\n output: { type: \"string\", description: \"json|csv\", required: false, default: \"json\" },\n },\n});\n\nconst listArgs = {\n \"account-id\": { type: \"string\", description: \"Meta ad account ID\" },\n full: { type: \"boolean\", description: \"Include the raw rule JSON, retention and creation times\" },\n limit: { type: \"string\", description: \"Max rows\" },\n \"no-cache\": NO_CACHE_ARG,\n output: { type: \"string\", description: \"Output format\", default: \"json\" },\n} as const;\n\nconst listCommand = defineCommand({\n meta: {\n name: \"list\",\n description:\n \"List custom conversions. Runs by default when no subcommand is given. Example: baker ads meta custom-conversions --account-id act_123\",\n },\n args: listArgs,\n run: async ({ args }) => {\n const accountId = resolveAccountIdArg(args);\n try {\n const params: Record<string, string> = { \"account-id\": accountId };\n if (args.limit) params.limit = args.limit as string;\n if (args[\"no-cache\"]) params[\"skip-cache\"] = \"true\";\n const rows = await apiGet<MetaCustomConversion[]>(\"/api/ads/meta/custom-conversions\", params);\n const data: Array<Record<string, unknown>> = args.full\n ? rows.map((row) => ({ ...row }))\n : rows.map((row) => ({ ...compactCustomConversion(row) }));\n const fmt = csvOrJson(args);\n if (fmt !== \"json\") {\n writeAdsOutput(data, fmt);\n return;\n }\n writeJsonEnvelope({ ok: true, data, hints: customConversionHints(rows) });\n } catch (err) {\n handleMetaError(err);\n }\n },\n});\n\nexport const customConversionsCommand = defineCommand({\n meta: {\n name: \"custom-conversions\",\n description: `List the Custom Conversions on an ad account, or stage a new one — a pixel event narrowed by a rule, set up in Events Manager.\nUse this whenever the user says \"my conversions\" rather than naming a standard event: the id here is what an ad set optimizes toward.\n\nExamples:\n baker ads meta custom-conversions --account-id act_123\n baker ads meta custom-conversions --account-id act_123 --full # raw rule JSON\n baker ads meta custom-conversions create --name \"Lead formulario\" --pixel-id 9988 --custom-event-type LEAD --url-contains \"/gracias\"`,\n },\n subCommands: {\n list: listCommand,\n create: () => import(\"./write-commands.ts\").then((m) => m.customConversionsCreateCommand),\n update: () => import(\"./asset-writes.ts\").then((m) => m.customConversionsUpdateCommand),\n delete: () => import(\"./asset-writes.ts\").then((m) => m.customConversionsDeleteCommand),\n },\n // `args` stays even though the parent has no `run`: citty scans rawArgs for\n // the subcommand name using THIS command's arg defs. `default` (not `run`)\n // dispatches the bare invocation.\n args: listArgs,\n default: \"list\",\n});\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/commands/ads/meta/custom-conversions.ts"],"sourcesContent":["import { defineCommand } from \"citty\";\nimport { NO_CACHE_ARG } from \"../../../cache-flag.ts\";\nimport { apiGet } from \"../../../client.ts\";\nimport { registerSchema } from \"../../../schemas.ts\";\nimport { writeAdsOutput, writeJsonEnvelope } from \"../output.ts\";\nimport { csvOrJson, handleMetaError, resolveAccountIdArg } from \"./shared.ts\";\n\n/** A Custom Conversion as the backend returns it, rule already read back as a phrase. */\nexport interface MetaCustomConversion {\n id: string;\n name?: string;\n description?: string;\n custom_event_type?: string;\n event_source_type?: string;\n pixel?: { id?: string; name?: string };\n rule?: string;\n rule_summary?: string;\n default_conversion_value?: number;\n retention_days?: number;\n is_archived?: boolean;\n is_unavailable?: boolean;\n creation_time?: string;\n first_fired_time?: string;\n last_fired_time?: string;\n}\n\n/** What the caller needs to pick one. The raw rule and the timestamps sit behind --full. */\ninterface CompactCustomConversion {\n id: string;\n name?: string;\n custom_event_type?: string;\n counts?: string;\n pixel_id?: string;\n last_fired_time?: string;\n archived?: true;\n unavailable?: true;\n}\n\nexport function compactCustomConversion(row: MetaCustomConversion): CompactCustomConversion {\n return {\n id: row.id,\n name: row.name,\n custom_event_type: row.custom_event_type,\n counts: row.rule_summary,\n pixel_id: row.pixel?.id,\n last_fired_time: row.last_fired_time,\n ...(row.is_archived ? { archived: true as const } : {}),\n ...(row.is_unavailable ? { unavailable: true as const } : {}),\n };\n}\n\n/**\n * What to say about a listing, once.\n *\n * A conversion that has never fired is the one thing that reads as \"broken\" and\n * usually is not — Meta creates it counting nothing until the rule matches a\n * real event — while pointing an ad set at one is a real decision with a real\n * cost, because Meta cannot optimize toward an event it has never seen. Saying\n * so here is cheaper than the chat where it gets discovered after a week of\n * delivery.\n */\nexport function customConversionHints(rows: MetaCustomConversion[]): string[] {\n if (rows.length === 0) {\n return [\n \"This account has no custom conversions. Optimize toward a standard pixel event instead (`--pixel-id` + `--custom-event-type`), or stage one: `baker ads meta custom-conversions create --name … --pixel-id … --custom-event-type LEAD --url-contains /thank-you`.\",\n ];\n }\n const hints: string[] = [\n \"To optimize an ad set toward one: `baker ads meta adsets create … --optimization-goal OFFSITE_CONVERSIONS --custom-conversion-id <id>`. Baker fills the pixel and the event category from the conversion — do not also pass --pixel-id or --custom-event-type.\",\n ];\n const neverFired = rows.filter((row) => !row.is_archived && !row.last_fired_time);\n if (neverFired.length > 0) {\n hints.push(\n `${neverFired.length === 1 ? \"This conversion has\" : `${neverFired.length} of these have`} never fired (${neverFired\n .map((row) => row.name ?? row.id)\n .join(\n \", \",\n )}). Meta cannot optimize toward an event it has never seen, so an ad set pointed at one delivers without learning — check the rule matches a page the site actually serves before spending on it.`,\n );\n }\n return hints;\n}\n\nregisterSchema({\n command: \"ads.meta.custom-conversions\",\n description:\n \"List the Custom Conversions defined on a Meta ad account — the measurement objects a marketer sets up in Events Manager (a pixel event narrowed by a rule, e.g. 'PageView on /thank-you'). Start here whenever the user talks about their own conversions rather than a standard event: an ad set optimizes toward one with `adsets create --custom-conversion-id <id>`.\",\n args: {\n \"account-id\": { type: \"string\", description: \"Meta ad account ID (act_123)\", required: false },\n full: { type: \"boolean\", description: \"Include the raw rule JSON, retention and creation times\", required: false },\n limit: { type: \"string\", description: \"Max rows\", required: false },\n \"no-cache\": { type: \"boolean\", description: \"Bypass the cache\", required: false },\n output: { type: \"string\", description: \"json|csv\", required: false, default: \"json\" },\n },\n});\n\nconst listArgs = {\n \"account-id\": { type: \"string\", description: \"Meta ad account ID\" },\n full: { type: \"boolean\", description: \"Include the raw rule JSON, retention and creation times\" },\n limit: { type: \"string\", description: \"Max rows\" },\n \"no-cache\": NO_CACHE_ARG,\n output: { type: \"string\", description: \"Output format\", default: \"json\" },\n} as const;\n\nconst listCommand = defineCommand({\n meta: {\n name: \"list\",\n description:\n \"List custom conversions. Runs by default when no subcommand is given. Example: baker ads meta custom-conversions --account-id act_123\",\n },\n args: listArgs,\n run: async ({ args }) => {\n const accountId = await resolveAccountIdArg(args);\n try {\n const params: Record<string, string> = { \"account-id\": accountId };\n if (args.limit) params.limit = args.limit as string;\n if (args[\"no-cache\"]) params[\"skip-cache\"] = \"true\";\n const rows = await apiGet<MetaCustomConversion[]>(\"/api/ads/meta/custom-conversions\", params);\n const data: Array<Record<string, unknown>> = args.full\n ? rows.map((row) => ({ ...row }))\n : rows.map((row) => ({ ...compactCustomConversion(row) }));\n const fmt = csvOrJson(args);\n if (fmt !== \"json\") {\n writeAdsOutput(data, fmt);\n return;\n }\n writeJsonEnvelope({ ok: true, data, hints: customConversionHints(rows) });\n } catch (err) {\n handleMetaError(err);\n }\n },\n});\n\nexport const customConversionsCommand = defineCommand({\n meta: {\n name: \"custom-conversions\",\n description: `List the Custom Conversions on an ad account, or stage a new one — a pixel event narrowed by a rule, set up in Events Manager.\nUse this whenever the user says \"my conversions\" rather than naming a standard event: the id here is what an ad set optimizes toward.\n\nExamples:\n baker ads meta custom-conversions --account-id act_123\n baker ads meta custom-conversions --account-id act_123 --full # raw rule JSON\n baker ads meta custom-conversions create --name \"Lead formulario\" --pixel-id 9988 --custom-event-type LEAD --url-contains \"/gracias\"`,\n },\n subCommands: {\n list: listCommand,\n create: () => import(\"./write-commands.ts\").then((m) => m.customConversionsCreateCommand),\n update: () => import(\"./asset-writes.ts\").then((m) => m.customConversionsUpdateCommand),\n delete: () => import(\"./asset-writes.ts\").then((m) => m.customConversionsDeleteCommand),\n },\n // `args` stays even though the parent has no `run`: citty scans rawArgs for\n // the subcommand name using THIS command's arg defs. `default` (not `run`)\n // dispatches the bare invocation.\n args: listArgs,\n default: \"list\",\n});\n"],"mappings":"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","names":[]}
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  handleMetaError
3
- } from "./chunk-HPAGYPAF.js";
3
+ } from "./chunk-BN4TDL5L.js";
4
4
  import {
5
5
  hintLines
6
6
  } from "./chunk-D5FY4FAK.js";
@@ -237,4 +237,4 @@ var metaDraftCommand = defineCommand({
237
237
  export {
238
238
  metaDraftCommand
239
239
  };
240
- //# sourceMappingURL=draft-ME4TNPO7.js.map
240
+ //# sourceMappingURL=draft-G432FJ7N.js.map
@@ -7,7 +7,7 @@ import {
7
7
  csvOrJson,
8
8
  getDateRange,
9
9
  handleMetaError
10
- } from "./chunk-HPAGYPAF.js";
10
+ } from "./chunk-BN4TDL5L.js";
11
11
  import {
12
12
  metaBreakdownSyncedHint,
13
13
  syncedFirstHint
@@ -436,4 +436,4 @@ export {
436
436
  insightsCommand,
437
437
  wantsAsync
438
438
  };
439
- //# sourceMappingURL=insights-INIFHYR7.js.map
439
+ //# sourceMappingURL=insights-IVW6W5CP.js.map
@@ -5,13 +5,13 @@ import {
5
5
  mergePayload,
6
6
  requireTarget,
7
7
  stageOp
8
- } from "./chunk-OEJNZSJG.js";
8
+ } from "./chunk-4XXA4WCS.js";
9
9
  import "./chunk-GC5P4KWY.js";
10
10
  import "./chunk-GDFJOCDO.js";
11
11
  import {
12
12
  csvOrJson,
13
13
  handleMetaError
14
- } from "./chunk-HPAGYPAF.js";
14
+ } from "./chunk-BN4TDL5L.js";
15
15
  import "./chunk-ERSX3MKH.js";
16
16
  import "./chunk-WPAW6EVY.js";
17
17
  import "./chunk-4NZ47VXO.js";
@@ -189,7 +189,7 @@ Then: baker ads meta creatives create --type instant_experience --canvas-id meta
189
189
  file: { type: "string", description: "JSON file with the whole payload (page_id, name, elements[], publish)" }
190
190
  },
191
191
  run: async ({ args, rawArgs }) => {
192
- const accountId = bareAccountId(args);
192
+ const accountId = await bareAccountId(args);
193
193
  const elements = canvasElementsFromRawArgs(rawArgs);
194
194
  const payload = mergePayload(loadJsonFileArg(args.file), {
195
195
  page_id: args["page-id"] === void 0 ? void 0 : String(args["page-id"]),
@@ -226,7 +226,7 @@ Example: baker ads meta instant-experiences update 1788652952402490 --account-id
226
226
  publish: { type: "boolean", description: "Publish it, so ads can open it \u2014 it is then fixed" }
227
227
  },
228
228
  run: async ({ args }) => {
229
- const accountId = bareAccountId(args);
229
+ const accountId = await bareAccountId(args);
230
230
  await stageOp({
231
231
  kind: "canvas.update",
232
232
  accountId,
@@ -257,4 +257,4 @@ export {
257
257
  instantExperiencesCreateCommand,
258
258
  instantExperiencesUpdateCommand
259
259
  };
260
- //# sourceMappingURL=instant-experiences-NMJUWNP3.js.map
260
+ //# sourceMappingURL=instant-experiences-6F6GXZ67.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/commands/ads/meta/instant-experiences.ts"],"sourcesContent":["import { defineCommand } from \"citty\";\nimport { apiGet } from \"../../../client.ts\";\nimport { registerSchema } from \"../../../schemas.ts\";\nimport { writeAdsOutput, writeJsonEnvelope } from \"../output.ts\";\nimport { csvOrJson, handleMetaError } from \"./shared.ts\";\nimport {\n bareAccountId,\n failWriteValidation,\n loadJsonFileArg,\n mergePayload,\n requireTarget,\n stageOp,\n} from \"./write-shared.ts\";\n\n/**\n * Instant Experiences — the full-screen page an ad opens on the phone (Meta's\n * API calls them canvases). Reading lists a Page's experiences; creating and\n * publishing stage on this chat's draft and happen at publish. Meta deletes\n * none and changes only an unpublished one, so a published experience stays\n * in the Page's library as it was made.\n */\n\nconst STAGED_NOTE = \"Staged until publish — review with `baker ads meta draft`.\";\n\ntype Args = Record<string, unknown>;\n\n/** The element flags, in the order they appear on the command line — that order is the experience's. */\nconst ELEMENT_FLAGS = [\"text\", \"button\", \"photo-id\", \"video-id\", \"video-ref\", \"product-set-id\", \"footer\"] as const;\n\nfunction linkPair(flag: string, value: string): { text: string; url: string } {\n const [text, ...rest] = value.split(\"=>\");\n const url = rest.join(\"=>\").trim();\n if (!text?.trim() || !url) {\n failWriteValidation(`--${flag} takes \"Button text => https://…\" — got \"${value}\"`);\n }\n return { text: text.trim(), url };\n}\n\nfunction elementFor(flag: (typeof ELEMENT_FLAGS)[number], value: string): Record<string, unknown> {\n switch (flag) {\n case \"text\":\n return { type: \"text\", text: value };\n case \"button\":\n return { type: \"button\", ...linkPair(flag, value) };\n case \"footer\":\n return { type: \"footer\", ...linkPair(flag, value) };\n case \"photo-id\":\n return { type: \"photo\", photo_id: value };\n case \"video-id\":\n return { type: \"video\", video_id: value };\n case \"video-ref\":\n return { type: \"video\", videoRef: value };\n default:\n return { type: \"product_set\", product_set_id: value };\n }\n}\n\n/** A command-line token as an element flag and its inline value (`--text=Hello`), if it is one. */\nfunction elementFlagOf(arg: string): { flag: (typeof ELEMENT_FLAGS)[number]; inline?: string } | undefined {\n if (!arg.startsWith(\"--\")) return undefined;\n const eq = arg.indexOf(\"=\");\n const name = eq === -1 ? arg.slice(2) : arg.slice(2, eq);\n const flag = ELEMENT_FLAGS.find((candidate) => candidate === name);\n if (!flag) return undefined;\n return eq === -1 ? { flag } : { flag, inline: arg.slice(eq + 1) };\n}\n\n/** Every element flag, repeatable, read from the command line in order (citty keeps only a flag's last value). */\nexport function canvasElementsFromRawArgs(rawArgs: readonly string[]): Record<string, unknown>[] {\n const elements: Record<string, unknown>[] = [];\n const end = rawArgs.indexOf(\"--\");\n const args = end === -1 ? rawArgs : rawArgs.slice(0, end);\n for (let i = 0; i < args.length; i++) {\n const found = elementFlagOf(args[i] ?? \"\");\n if (!found) continue;\n const value = found.inline ?? args[++i];\n if (value === undefined || value.startsWith(\"--\")) failWriteValidation(`--${found.flag} needs a value`);\n elements.push(elementFor(found.flag, value));\n }\n return elements;\n}\n\n// --- List ---\n\nregisterSchema({\n command: \"ads.meta.instant-experiences\",\n description:\n \"A Facebook Page's Instant Experiences — the full-screen pages an ad can open (creatives create --type instant_experience --canvas-id). Shows whether each is published: an ad opens only a published one.\",\n args: {\n \"page-id\": { type: \"string\", description: \"Facebook Page id (baker ads meta pages)\", required: true },\n output: { type: \"string\", description: \"json|csv|jsonl|md\", required: false, default: \"json\" },\n },\n});\n\nconst listCommand = defineCommand({\n meta: {\n name: \"list\",\n description:\n \"A Page's Instant Experiences, published or not. Runs by default. Example: baker ads meta instant-experiences --page-id 904238019441800\",\n },\n args: {\n \"page-id\": { type: \"string\", description: \"Facebook Page id — from `baker ads meta pages`\" },\n output: { type: \"string\", description: \"json|csv|jsonl|md\", default: \"json\" },\n },\n run: async ({ args }) => {\n if (!args[\"page-id\"]) failWriteValidation(\"pass --page-id (from `baker ads meta pages`)\");\n try {\n const rows = await apiGet<Record<string, unknown>[]>(\"/api/ads/meta/instant-experiences\", {\n \"page-id\": String(args[\"page-id\"]),\n });\n const hints =\n rows.length === 0\n ? [\"This Page has no Instant Experiences. Stage one with `baker ads meta instant-experiences create`.\"]\n : [\n \"An ad opens only a published experience (is_published true): `creatives create --type instant_experience --canvas-id <id>`. A published one cannot be changed or deleted.\",\n ];\n const fmt = csvOrJson(args);\n if (fmt !== \"json\") {\n writeAdsOutput(rows, fmt, undefined, hints);\n return;\n }\n writeJsonEnvelope({ ok: true, data: rows, hints });\n } catch (err) {\n handleMetaError(err);\n }\n },\n});\n\n// --- Create ---\n\nregisterSchema({\n command: \"ads.meta.instant-experiences.create\",\n description:\n \"Stage a new Instant Experience on a Page — its elements in command-line order — made (and published, unless --unpublished) when the chat publishes. Meta deletes no experience, so it is only ever made at publish.\",\n args: {\n \"account-id\": { type: \"string\", description: \"Ad account the change is staged under\", required: false },\n \"page-id\": { type: \"string\", description: \"The Facebook Page it belongs to\", required: true },\n name: { type: \"string\", description: \"Name in the Page's Instant Experience library\", required: true },\n text: { type: \"string\", description: \"A paragraph, repeatable\", required: false },\n button: { type: \"string\", description: '\"Text => https://…\", repeatable', required: false },\n \"photo-id\": {\n type: \"string\",\n description: \"A photo on the Page, or meta_temp_* of a staged `page-photos upload`, repeatable\",\n required: false,\n },\n \"video-id\": { type: \"string\", description: \"A video of the ad account, repeatable\", required: false },\n \"video-ref\": { type: \"string\", description: \"meta_temp_* of a staged video upload, repeatable\", required: false },\n \"product-set-id\": { type: \"string\", description: \"Products of a catalog's product set\", required: false },\n footer: { type: \"string\", description: '\"Text => https://…\" pinned at the bottom (last)', required: false },\n background: { type: \"string\", description: \"Background colour, six hex digits\", required: false },\n unpublished: { type: \"boolean\", description: \"Leave it unpublished (no ad can open it yet)\", required: false },\n file: { type: \"string\", description: \"JSON file with the whole payload\", required: false },\n },\n});\n\nexport const instantExperiencesCreateCommand = defineCommand({\n meta: {\n name: \"create\",\n description: `Stage a new Instant Experience (the full-screen page an ad opens). ${STAGED_NOTE}\nElements appear in the order of their flags; every element flag repeats. Published at publish unless --unpublished — an ad opens only a published one, a published one cannot be changed, and Meta deletes none.\nPictures are Page photos (--photo-id: one on the Page, or the ref of a staged \\`page-photos upload\\`; a collection's photo experience takes four); videos come from the ad account (--video-id, or --video-ref of a staged \\`media upload --kind video\\`).\nExample: baker ads meta instant-experiences create --account-id act_123 --page-id 904238019441800 --name \"Spring\" --video-id 1508023623826870 --text \"Our spring range\" --button \"Shop now => https://shop.example/spring\"\nThen: baker ads meta creatives create --type instant_experience --canvas-id meta_temp_… --page 904238019441800 --message \"Tap to explore\" --image-hash …`,\n },\n args: {\n \"account-id\": { type: \"string\", description: \"Numeric account ID or act_N (the account the ads run from)\" },\n \"page-id\": { type: \"string\", description: \"The Facebook Page it belongs to — from `baker ads meta pages`\" },\n name: { type: \"string\", description: \"Name in the Page's Instant Experience library\" },\n text: { type: \"string\", description: \"A paragraph of text (repeatable)\" },\n button: { type: \"string\", description: '\"Button text => https://…\" (repeatable)' },\n \"photo-id\": {\n type: \"string\",\n description: \"A photo on the Page, or meta_temp_* of a staged `page-photos upload` (repeatable)\",\n },\n \"video-id\": { type: \"string\", description: \"A video of the ad account (repeatable)\" },\n \"video-ref\": {\n type: \"string\",\n description: \"meta_temp_* ref of a staged `media upload --kind video` (repeatable)\",\n },\n \"product-set-id\": {\n type: \"string\",\n description: \"Products from a catalog's product set, also shown under the ad (collection) — repeatable\",\n },\n footer: { type: \"string\", description: '\"Button text => https://…\" pinned at the bottom; must be last' },\n background: { type: \"string\", description: \"Background colour, six hex digits (e.g. FFFFFF)\" },\n unpublished: {\n type: \"boolean\",\n description: \"Leave it unpublished — it can be changed, but no ad can open it yet\",\n },\n file: { type: \"string\", description: \"JSON file with the whole payload (page_id, name, elements[], publish)\" },\n },\n run: async ({ args, rawArgs }) => {\n const accountId = bareAccountId(args as Args);\n const elements = canvasElementsFromRawArgs(rawArgs);\n const payload = mergePayload(loadJsonFileArg(args.file), {\n page_id: args[\"page-id\"] === undefined ? undefined : String(args[\"page-id\"]),\n name: args.name,\n background_color: args.background,\n elements: elements.length > 0 ? elements : undefined,\n publish: args.unpublished === true ? false : undefined,\n });\n await stageOp({ kind: \"canvas.create\", accountId, payload }, [\n \"An ad opens it with `creatives create --type instant_experience --canvas-id <ref>`.\",\n ]);\n },\n});\n\n// --- Update ---\n\nregisterSchema({\n command: \"ads.meta.instant-experiences.update\",\n description:\n \"Rename or publish an UNPUBLISHED Instant Experience at publish. Meta changes nothing on a published one.\",\n args: {\n id: { type: \"positional\", description: \"Instant Experience id or meta_temp_* ref\", required: true },\n \"account-id\": { type: \"string\", description: \"Ad account the change is staged under\", required: false },\n name: { type: \"string\", description: \"New name\", required: false },\n publish: { type: \"boolean\", description: \"Publish it so ads can open it\", required: false },\n },\n});\n\nexport const instantExperiencesUpdateCommand = defineCommand({\n meta: {\n name: \"update\",\n description: `Rename or publish an unpublished Instant Experience. ${STAGED_NOTE}\nExample: baker ads meta instant-experiences update 1788652952402490 --account-id act_123 --publish`,\n },\n args: {\n id: { type: \"positional\", description: \"Instant Experience id or meta_temp_* ref\", required: true },\n \"account-id\": { type: \"string\", description: \"Numeric account ID or act_N\" },\n name: { type: \"string\", description: \"New name\" },\n publish: { type: \"boolean\", description: \"Publish it, so ads can open it — it is then fixed\" },\n },\n run: async ({ args }) => {\n const accountId = bareAccountId(args as Args);\n await stageOp({\n kind: \"canvas.update\",\n accountId,\n target: requireTarget(args as Args, \"Instant Experience\"),\n payload: { name: args.name, publish: args.publish === true ? true : undefined },\n });\n },\n});\n\nexport const instantExperiencesCommand = defineCommand({\n meta: {\n name: \"instant-experiences\",\n description: `Instant Experiences — the full-screen page an ad opens on the phone (also behind collection ads).\n baker ads meta instant-experiences --page-id 904238019441800 — list, with whether each is published\n baker ads meta instant-experiences create --page-id … --name … --text … --button \"Shop => https://…\"\n baker ads meta instant-experiences update <id> --publish`,\n },\n subCommands: {\n list: listCommand,\n create: instantExperiencesCreateCommand,\n update: instantExperiencesUpdateCommand,\n },\n args: { \"page-id\": { type: \"string\", description: \"Facebook Page id\" }, output: { type: \"string\", default: \"json\" } },\n default: \"list\",\n});\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/commands/ads/meta/instant-experiences.ts"],"sourcesContent":["import { defineCommand } from \"citty\";\nimport { apiGet } from \"../../../client.ts\";\nimport { registerSchema } from \"../../../schemas.ts\";\nimport { writeAdsOutput, writeJsonEnvelope } from \"../output.ts\";\nimport { csvOrJson, handleMetaError } from \"./shared.ts\";\nimport {\n bareAccountId,\n failWriteValidation,\n loadJsonFileArg,\n mergePayload,\n requireTarget,\n stageOp,\n} from \"./write-shared.ts\";\n\n/**\n * Instant Experiences — the full-screen page an ad opens on the phone (Meta's\n * API calls them canvases). Reading lists a Page's experiences; creating and\n * publishing stage on this chat's draft and happen at publish. Meta deletes\n * none and changes only an unpublished one, so a published experience stays\n * in the Page's library as it was made.\n */\n\nconst STAGED_NOTE = \"Staged until publish — review with `baker ads meta draft`.\";\n\ntype Args = Record<string, unknown>;\n\n/** The element flags, in the order they appear on the command line — that order is the experience's. */\nconst ELEMENT_FLAGS = [\"text\", \"button\", \"photo-id\", \"video-id\", \"video-ref\", \"product-set-id\", \"footer\"] as const;\n\nfunction linkPair(flag: string, value: string): { text: string; url: string } {\n const [text, ...rest] = value.split(\"=>\");\n const url = rest.join(\"=>\").trim();\n if (!text?.trim() || !url) {\n failWriteValidation(`--${flag} takes \"Button text => https://…\" — got \"${value}\"`);\n }\n return { text: text.trim(), url };\n}\n\nfunction elementFor(flag: (typeof ELEMENT_FLAGS)[number], value: string): Record<string, unknown> {\n switch (flag) {\n case \"text\":\n return { type: \"text\", text: value };\n case \"button\":\n return { type: \"button\", ...linkPair(flag, value) };\n case \"footer\":\n return { type: \"footer\", ...linkPair(flag, value) };\n case \"photo-id\":\n return { type: \"photo\", photo_id: value };\n case \"video-id\":\n return { type: \"video\", video_id: value };\n case \"video-ref\":\n return { type: \"video\", videoRef: value };\n default:\n return { type: \"product_set\", product_set_id: value };\n }\n}\n\n/** A command-line token as an element flag and its inline value (`--text=Hello`), if it is one. */\nfunction elementFlagOf(arg: string): { flag: (typeof ELEMENT_FLAGS)[number]; inline?: string } | undefined {\n if (!arg.startsWith(\"--\")) return undefined;\n const eq = arg.indexOf(\"=\");\n const name = eq === -1 ? arg.slice(2) : arg.slice(2, eq);\n const flag = ELEMENT_FLAGS.find((candidate) => candidate === name);\n if (!flag) return undefined;\n return eq === -1 ? { flag } : { flag, inline: arg.slice(eq + 1) };\n}\n\n/** Every element flag, repeatable, read from the command line in order (citty keeps only a flag's last value). */\nexport function canvasElementsFromRawArgs(rawArgs: readonly string[]): Record<string, unknown>[] {\n const elements: Record<string, unknown>[] = [];\n const end = rawArgs.indexOf(\"--\");\n const args = end === -1 ? rawArgs : rawArgs.slice(0, end);\n for (let i = 0; i < args.length; i++) {\n const found = elementFlagOf(args[i] ?? \"\");\n if (!found) continue;\n const value = found.inline ?? args[++i];\n if (value === undefined || value.startsWith(\"--\")) failWriteValidation(`--${found.flag} needs a value`);\n elements.push(elementFor(found.flag, value));\n }\n return elements;\n}\n\n// --- List ---\n\nregisterSchema({\n command: \"ads.meta.instant-experiences\",\n description:\n \"A Facebook Page's Instant Experiences — the full-screen pages an ad can open (creatives create --type instant_experience --canvas-id). Shows whether each is published: an ad opens only a published one.\",\n args: {\n \"page-id\": { type: \"string\", description: \"Facebook Page id (baker ads meta pages)\", required: true },\n output: { type: \"string\", description: \"json|csv|jsonl|md\", required: false, default: \"json\" },\n },\n});\n\nconst listCommand = defineCommand({\n meta: {\n name: \"list\",\n description:\n \"A Page's Instant Experiences, published or not. Runs by default. Example: baker ads meta instant-experiences --page-id 904238019441800\",\n },\n args: {\n \"page-id\": { type: \"string\", description: \"Facebook Page id — from `baker ads meta pages`\" },\n output: { type: \"string\", description: \"json|csv|jsonl|md\", default: \"json\" },\n },\n run: async ({ args }) => {\n if (!args[\"page-id\"]) failWriteValidation(\"pass --page-id (from `baker ads meta pages`)\");\n try {\n const rows = await apiGet<Record<string, unknown>[]>(\"/api/ads/meta/instant-experiences\", {\n \"page-id\": String(args[\"page-id\"]),\n });\n const hints =\n rows.length === 0\n ? [\"This Page has no Instant Experiences. Stage one with `baker ads meta instant-experiences create`.\"]\n : [\n \"An ad opens only a published experience (is_published true): `creatives create --type instant_experience --canvas-id <id>`. A published one cannot be changed or deleted.\",\n ];\n const fmt = csvOrJson(args);\n if (fmt !== \"json\") {\n writeAdsOutput(rows, fmt, undefined, hints);\n return;\n }\n writeJsonEnvelope({ ok: true, data: rows, hints });\n } catch (err) {\n handleMetaError(err);\n }\n },\n});\n\n// --- Create ---\n\nregisterSchema({\n command: \"ads.meta.instant-experiences.create\",\n description:\n \"Stage a new Instant Experience on a Page — its elements in command-line order — made (and published, unless --unpublished) when the chat publishes. Meta deletes no experience, so it is only ever made at publish.\",\n args: {\n \"account-id\": { type: \"string\", description: \"Ad account the change is staged under\", required: false },\n \"page-id\": { type: \"string\", description: \"The Facebook Page it belongs to\", required: true },\n name: { type: \"string\", description: \"Name in the Page's Instant Experience library\", required: true },\n text: { type: \"string\", description: \"A paragraph, repeatable\", required: false },\n button: { type: \"string\", description: '\"Text => https://…\", repeatable', required: false },\n \"photo-id\": {\n type: \"string\",\n description: \"A photo on the Page, or meta_temp_* of a staged `page-photos upload`, repeatable\",\n required: false,\n },\n \"video-id\": { type: \"string\", description: \"A video of the ad account, repeatable\", required: false },\n \"video-ref\": { type: \"string\", description: \"meta_temp_* of a staged video upload, repeatable\", required: false },\n \"product-set-id\": { type: \"string\", description: \"Products of a catalog's product set\", required: false },\n footer: { type: \"string\", description: '\"Text => https://…\" pinned at the bottom (last)', required: false },\n background: { type: \"string\", description: \"Background colour, six hex digits\", required: false },\n unpublished: { type: \"boolean\", description: \"Leave it unpublished (no ad can open it yet)\", required: false },\n file: { type: \"string\", description: \"JSON file with the whole payload\", required: false },\n },\n});\n\nexport const instantExperiencesCreateCommand = defineCommand({\n meta: {\n name: \"create\",\n description: `Stage a new Instant Experience (the full-screen page an ad opens). ${STAGED_NOTE}\nElements appear in the order of their flags; every element flag repeats. Published at publish unless --unpublished — an ad opens only a published one, a published one cannot be changed, and Meta deletes none.\nPictures are Page photos (--photo-id: one on the Page, or the ref of a staged \\`page-photos upload\\`; a collection's photo experience takes four); videos come from the ad account (--video-id, or --video-ref of a staged \\`media upload --kind video\\`).\nExample: baker ads meta instant-experiences create --account-id act_123 --page-id 904238019441800 --name \"Spring\" --video-id 1508023623826870 --text \"Our spring range\" --button \"Shop now => https://shop.example/spring\"\nThen: baker ads meta creatives create --type instant_experience --canvas-id meta_temp_… --page 904238019441800 --message \"Tap to explore\" --image-hash …`,\n },\n args: {\n \"account-id\": { type: \"string\", description: \"Numeric account ID or act_N (the account the ads run from)\" },\n \"page-id\": { type: \"string\", description: \"The Facebook Page it belongs to — from `baker ads meta pages`\" },\n name: { type: \"string\", description: \"Name in the Page's Instant Experience library\" },\n text: { type: \"string\", description: \"A paragraph of text (repeatable)\" },\n button: { type: \"string\", description: '\"Button text => https://…\" (repeatable)' },\n \"photo-id\": {\n type: \"string\",\n description: \"A photo on the Page, or meta_temp_* of a staged `page-photos upload` (repeatable)\",\n },\n \"video-id\": { type: \"string\", description: \"A video of the ad account (repeatable)\" },\n \"video-ref\": {\n type: \"string\",\n description: \"meta_temp_* ref of a staged `media upload --kind video` (repeatable)\",\n },\n \"product-set-id\": {\n type: \"string\",\n description: \"Products from a catalog's product set, also shown under the ad (collection) — repeatable\",\n },\n footer: { type: \"string\", description: '\"Button text => https://…\" pinned at the bottom; must be last' },\n background: { type: \"string\", description: \"Background colour, six hex digits (e.g. FFFFFF)\" },\n unpublished: {\n type: \"boolean\",\n description: \"Leave it unpublished — it can be changed, but no ad can open it yet\",\n },\n file: { type: \"string\", description: \"JSON file with the whole payload (page_id, name, elements[], publish)\" },\n },\n run: async ({ args, rawArgs }) => {\n const accountId = await bareAccountId(args as Args);\n const elements = canvasElementsFromRawArgs(rawArgs);\n const payload = mergePayload(loadJsonFileArg(args.file), {\n page_id: args[\"page-id\"] === undefined ? undefined : String(args[\"page-id\"]),\n name: args.name,\n background_color: args.background,\n elements: elements.length > 0 ? elements : undefined,\n publish: args.unpublished === true ? false : undefined,\n });\n await stageOp({ kind: \"canvas.create\", accountId, payload }, [\n \"An ad opens it with `creatives create --type instant_experience --canvas-id <ref>`.\",\n ]);\n },\n});\n\n// --- Update ---\n\nregisterSchema({\n command: \"ads.meta.instant-experiences.update\",\n description:\n \"Rename or publish an UNPUBLISHED Instant Experience at publish. Meta changes nothing on a published one.\",\n args: {\n id: { type: \"positional\", description: \"Instant Experience id or meta_temp_* ref\", required: true },\n \"account-id\": { type: \"string\", description: \"Ad account the change is staged under\", required: false },\n name: { type: \"string\", description: \"New name\", required: false },\n publish: { type: \"boolean\", description: \"Publish it so ads can open it\", required: false },\n },\n});\n\nexport const instantExperiencesUpdateCommand = defineCommand({\n meta: {\n name: \"update\",\n description: `Rename or publish an unpublished Instant Experience. ${STAGED_NOTE}\nExample: baker ads meta instant-experiences update 1788652952402490 --account-id act_123 --publish`,\n },\n args: {\n id: { type: \"positional\", description: \"Instant Experience id or meta_temp_* ref\", required: true },\n \"account-id\": { type: \"string\", description: \"Numeric account ID or act_N\" },\n name: { type: \"string\", description: \"New name\" },\n publish: { type: \"boolean\", description: \"Publish it, so ads can open it — it is then fixed\" },\n },\n run: async ({ args }) => {\n const accountId = await bareAccountId(args as Args);\n await stageOp({\n kind: \"canvas.update\",\n accountId,\n target: requireTarget(args as Args, \"Instant Experience\"),\n payload: { name: args.name, publish: args.publish === true ? true : undefined },\n });\n },\n});\n\nexport const instantExperiencesCommand = defineCommand({\n meta: {\n name: \"instant-experiences\",\n description: `Instant Experiences — the full-screen page an ad opens on the phone (also behind collection ads).\n baker ads meta instant-experiences --page-id 904238019441800 — list, with whether each is published\n baker ads meta instant-experiences create --page-id … --name … --text … --button \"Shop => https://…\"\n baker ads meta instant-experiences update <id> --publish`,\n },\n subCommands: {\n list: listCommand,\n create: instantExperiencesCreateCommand,\n update: instantExperiencesUpdateCommand,\n },\n args: { \"page-id\": { type: \"string\", description: \"Facebook Page id\" }, output: { type: \"string\", default: \"json\" } },\n default: \"list\",\n});\n"],"mappings":"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","names":[]}
@@ -6,7 +6,7 @@ import {
6
6
  requireTarget,
7
7
  stageOp,
8
8
  upper
9
- } from "./chunk-OEJNZSJG.js";
9
+ } from "./chunk-4XXA4WCS.js";
10
10
  import {
11
11
  isMetaLeadAdsRefusal
12
12
  } from "./chunk-GC5P4KWY.js";
@@ -14,7 +14,7 @@ import "./chunk-GDFJOCDO.js";
14
14
  import {
15
15
  csvOrJson,
16
16
  handleMetaError
17
- } from "./chunk-HPAGYPAF.js";
17
+ } from "./chunk-BN4TDL5L.js";
18
18
  import {
19
19
  repeatedValues
20
20
  } from "./chunk-ERSX3MKH.js";
@@ -211,7 +211,7 @@ Full spec: --file form.json (see \`baker schema ads.meta.lead-forms.create\` and
211
211
  file: { type: "string", description: "JSON form payload; flags override file keys" }
212
212
  },
213
213
  run: async ({ args, rawArgs }) => {
214
- const accountId = bareAccountId(args);
214
+ const accountId = await bareAccountId(args);
215
215
  const payload = mergePayload(
216
216
  formFileForStaging(loadJsonFileArg(args.file)),
217
217
  leadFormPayloadFromFlags(args, rawArgs)
@@ -271,7 +271,7 @@ Then point the ads at the copy (\`creatives create --lead-form <ref>\`) and \`le
271
271
  file: { type: "string", description: "JSON with the keys to change" }
272
272
  },
273
273
  run: async ({ args, rawArgs }) => {
274
- const accountId = bareAccountId(args);
274
+ const accountId = await bareAccountId(args);
275
275
  if (!str(args.name)) failWriteValidation("--name is required: Meta refuses a second form with the same name");
276
276
  let source;
277
277
  try {
@@ -310,7 +310,7 @@ Example: baker ads meta lead-forms ${name} 1460955752594831 --account-id act_123
310
310
  run: async ({ args }) => {
311
311
  await stageOp({
312
312
  kind: "leadForm.update",
313
- accountId: bareAccountId(args),
313
+ accountId: await bareAccountId(args),
314
314
  target: requireTarget(args, "Instant Form"),
315
315
  payload: { status }
316
316
  });
@@ -537,4 +537,4 @@ export {
537
537
  leadPageHints,
538
538
  leadsCommand
539
539
  };
540
- //# sourceMappingURL=leads-QBWR7FKT.js.map
540
+ //# sourceMappingURL=leads-CJKRKB2Q.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/commands/ads/meta/leads.ts","../src/commands/ads/meta/lead-form-writes.ts"],"sourcesContent":["import { isMetaLeadAdsRefusal } from \"@baker/api/ads-meta\";\nimport { defineCommand } from \"citty\";\nimport { ApiError, apiGet } from \"../../../client.ts\";\nimport { registerSchema } from \"../../../schemas.ts\";\nimport { writeAdsJson, writeAdsOutput, writeJsonEnvelope } from \"../output.ts\";\nimport {\n leadFormsArchiveCommand,\n leadFormsCreateCommand,\n leadFormsDuplicateCommand,\n leadFormsSpecCommand,\n leadFormsUnarchiveCommand,\n} from \"./lead-form-writes.ts\";\nimport { csvOrJson, handleMetaError } from \"./shared.ts\";\n\n/** An instant form attached to a Facebook Page. */\nexport interface MetaLeadForm {\n id: string;\n name?: string;\n status?: string;\n locale?: string;\n leads_count?: number;\n created_time?: string;\n}\n\n/** One page of leads plus the cursor that continues it. */\nexport interface MetaLeadPage {\n leads: MetaLead[];\n /** Present only while Meta says another page exists. */\n nextCursor?: string;\n /** What the backend says about this page (every answer masked when it could not tell contact questions apart). */\n hints?: string[];\n}\n\n/** One lead, already shaped by the backend (contact values masked unless --full). */\nexport interface MetaLead {\n id: string;\n created_time?: string;\n ad_id?: string;\n ad_name?: string;\n adset_id?: string;\n adset_name?: string;\n campaign_id?: string;\n campaign_name?: string;\n form_id?: string;\n is_organic?: boolean;\n platform?: string;\n answers: Record<string, string>;\n /** Each disclaimer checkbox by its key: ticked or not. */\n consents?: Record<string, boolean>;\n partner_name?: string;\n}\n\nregisterSchema({\n command: \"ads.meta.lead-forms\",\n description:\n \"List the instant (lead) forms on a Facebook Page, with how many leads each has collected. Start here: a lead is fetched by form id. Use a Page from `baker ads meta pages` with hasPageAccessToken: true.\",\n args: {\n \"page-id\": {\n type: \"string\",\n description: \"Facebook Page ID — from `baker ads meta pages` with hasPageAccessToken: true\",\n required: true,\n },\n full: { type: \"boolean\", description: \"Include each form's questions\", required: false },\n output: { type: \"string\", description: \"json|csv\", required: false, default: \"json\" },\n },\n});\n\nregisterSchema({\n command: \"ads.meta.leads\",\n description:\n \"Read the leads an instant form collected. Contact details (email, phone) come back masked; pass --full only when the raw values are genuinely needed, e.g. handing them to a CRM.\",\n args: {\n \"form-id\": { type: \"string\", description: \"Instant form ID — from `baker ads meta lead-forms`\", required: false },\n \"ad-id\": { type: \"string\", description: \"Read the leads of one ad instead of a whole form\", required: false },\n \"adset-id\": { type: \"string\", description: \"Read the leads of every ad in an ad set\", required: false },\n \"campaign-id\": { type: \"string\", description: \"Read the leads of every ad in a campaign\", required: false },\n since: { type: \"string\", description: \"Earliest submission date, YYYY-MM-DD\", required: false },\n until: { type: \"string\", description: \"Latest submission date, YYYY-MM-DD (inclusive)\", required: false },\n limit: { type: \"string\", description: \"Leads per page (default 100, max 500)\", required: false },\n after: {\n type: \"string\",\n description: \"Cursor from a previous call's nextCursor — fetches the following page\",\n required: false,\n },\n full: { type: \"boolean\", description: \"Return raw contact details instead of masked ones\", required: false },\n output: { type: \"string\", description: \"json|csv\", required: false, default: \"json\" },\n },\n});\n\n/**\n * A form that exists but has never collected anything is the single most common\n * thing to misread here — it looks identical to a permissions problem in the\n * output, and the remedies are opposite. Say which it is.\n */\n/** The backend drains instant forms up to this many; see `META_LEAD_FORMS_MAX`. */\nconst LEAD_FORMS_CAP = 500;\n\nexport function leadFormHints(forms: MetaLeadForm[]): string[] {\n if (forms.length === 0) {\n return [\"This Page has NO instant forms. Nothing to read leads from — the form has to be created first.\"];\n }\n if (forms.length >= LEAD_FORMS_CAP) {\n return [\n `PARTIAL LIST — this Page has at least ${LEAD_FORMS_CAP} instant forms and only the first ${LEAD_FORMS_CAP} came back. Do not describe this as every form on the Page.`,\n ];\n }\n const empty = forms.filter((form) => (form.leads_count ?? 0) === 0);\n if (empty.length === forms.length) {\n return [\n \"Every form on this Page has collected 0 leads. That is a real, readable answer — the connection works. Do not retry and do not report it as a permissions problem.\",\n ];\n }\n return [];\n}\n\n/**\n * A `FORBIDDEN` that Baker itself produced is already conclusive, so it must not\n * collect the generic ambiguous-403 remedy.\n *\n * `metaForbiddenFix` exists for Meta's own 403, which covers two causes with\n * opposite fixes and can only be told apart by testing the blast radius. Pasting\n * that after *\"this needs switching on by the Baker team\"* would hand the agent\n * a paragraph suggesting a reconnect right underneath a sentence saying a\n * reconnect cannot help — and the agent would pass it on, costing the client a\n * pointless round trip through their Meta login. Everything that is genuinely\n * Meta's still goes through the normal handler.\n */\nfunction handleLeadsError(err: unknown): never {\n if (err instanceof ApiError && err.code === \"FORBIDDEN\" && isMetaLeadAdsRefusal(err.message)) {\n writeAdsJson({\n ok: false,\n error: {\n code: err.code,\n message: err.message,\n fix: { action: \"reject\", explanation: err.message },\n retryable: false,\n },\n });\n process.exit(1);\n }\n handleMetaError(err);\n}\n\n/**\n * What the agent has to know before it reports a number.\n *\n * Counting is what this command is mostly used for, so a page that is full is\n * the dangerous case: without a word, 100 rows read as \"100 leads\" and the\n * client is told something false. The cursor is the remedy, and it has to be\n * named in the same breath as the warning.\n */\nexport function leadPageHints(page: MetaLeadPage, full: boolean): string[] {\n const hints: string[] = [];\n if (page.nextCursor) {\n hints.push(\n `PARTIAL PAGE — more leads exist. This is ${page.leads.length} of an unknown larger number; do NOT report it as a total. Fetch the next page with \\`--after ${page.nextCursor}\\` (repeat until no nextCursor), or narrow the window with --since/--until.`,\n );\n }\n if (!full) {\n hints.push(\n \"Answers that give contact details (email, phone, any question asking for one) are masked. Report on them as they are; pass --full only when the raw values have to leave Baker (a CRM hand-off), and never paste them into the chat otherwise.\",\n );\n }\n hints.push(...(page.hints ?? []));\n return hints;\n}\n\nconst leadFormsListArgs = {\n \"page-id\": { type: \"string\", description: \"Facebook Page ID\" },\n full: { type: \"boolean\", description: \"Include each form's questions\" },\n output: { type: \"string\", description: \"Output format\", default: \"json\" },\n} as const;\n\nconst leadFormsListCommand = defineCommand({\n meta: {\n name: \"list\",\n description:\n \"List the Instant Forms on a Facebook Page, with lead counts. Choose a Page with hasPageAccessToken: true from `baker ads meta pages`. Pass --full to include questions.\",\n },\n args: leadFormsListArgs,\n run: async ({ args }) => {\n try {\n const pageId = args[\"page-id\"] as string | undefined;\n if (!pageId) {\n handleLeadsError(\n new Error(\n \"--page-id is required. Run `baker ads meta pages` and choose a Page with hasPageAccessToken: true.\",\n ),\n );\n }\n const params: Record<string, string> = { \"page-id\": pageId };\n if (args.full) params.full = \"true\";\n const data = await apiGet<MetaLeadForm[]>(\"/api/ads/meta/lead-forms\", params);\n const fmt = csvOrJson(args);\n if (fmt !== \"json\") {\n writeAdsOutput(data as unknown as Array<Record<string, unknown>>, fmt);\n return;\n }\n const hints = leadFormHints(data);\n writeJsonEnvelope(hints.length > 0 ? { ok: true, data, hints } : { ok: true, data });\n } catch (err) {\n handleLeadsError(err);\n }\n },\n});\n\nexport const leadFormsCommand = defineCommand({\n meta: {\n name: \"lead-forms\",\n description: `List Instant Forms on a Facebook Page, or stage a new one, a changed copy, an archive.\nA form is permanent on Meta and nothing on it changes once made: to change one, duplicate it with changes and archive the old one.\nExamples:\n baker ads meta lead-forms --page-id 100635183117758\n baker ads meta lead-forms --page-id 100635183117758 --full\n baker ads meta lead-forms create --account-id act_123 --page-id 555 --file form.json\n baker ads meta lead-forms spec 987654321 — a live form as a create --file payload\n baker ads meta lead-forms duplicate 987654321 --account-id act_123 --name \"Free audit (ES)\" --locale ES_LA\n baker ads meta lead-forms archive 987654321 --account-id act_123`,\n },\n args: leadFormsListArgs,\n subCommands: {\n list: leadFormsListCommand,\n create: leadFormsCreateCommand,\n spec: leadFormsSpecCommand,\n duplicate: leadFormsDuplicateCommand,\n archive: leadFormsArchiveCommand,\n unarchive: leadFormsUnarchiveCommand,\n },\n default: \"list\",\n});\n\nexport const leadsCommand = defineCommand({\n meta: {\n name: \"leads\",\n description: `Read the leads an instant form collected. Every question comes back; email and phone answers are MASKED unless you pass --full, so you can report on what a form is collecting without moving contact details around.\n\nExamples:\n baker ads meta leads --form-id 987654321\n baker ads meta leads --form-id 987654321 --since 2026-08-01 --until 2026-08-31\n baker ads meta leads --ad-id 6123456789 --limit 50\n baker ads meta leads --adset-id 6123456780 --since 2026-08-01 # every ad in the ad set (Meta has no ad set edge)\n baker ads meta leads --form-id 987654321 --after <nextCursor> # the next page\n baker ads meta leads --form-id 987654321 --full # raw contact details — only when handing leads over\n\nResults are PAGED. A response carrying \\`nextCursor\\` is not the whole set — follow it before reporting any total.`,\n },\n args: {\n \"form-id\": { type: \"string\", description: \"Instant form ID (from `baker ads meta lead-forms`)\" },\n \"ad-id\": { type: \"string\", description: \"Read one ad's leads instead of a whole form\" },\n \"adset-id\": { type: \"string\", description: \"Read the leads of every ad in an ad set\" },\n \"campaign-id\": { type: \"string\", description: \"Read the leads of every ad in a campaign\" },\n since: { type: \"string\", description: \"Earliest submission date, YYYY-MM-DD\" },\n until: { type: \"string\", description: \"Latest submission date, YYYY-MM-DD (inclusive)\" },\n limit: { type: \"string\", description: \"Leads per page (default 100, max 500)\" },\n after: { type: \"string\", description: \"Cursor from a previous call's nextCursor\" },\n full: { type: \"boolean\", description: \"Return raw contact details instead of masked ones\" },\n output: { type: \"string\", description: \"Output format\", default: \"json\" },\n },\n run: async ({ args }) => {\n try {\n const params: Record<string, string> = {};\n for (const flag of [\"form-id\", \"ad-id\", \"adset-id\", \"campaign-id\"] as const) {\n const value = args[flag];\n if (typeof value === \"string\" && value.length > 0) params[flag] = value;\n }\n if (Object.keys(params).length !== 1) {\n handleLeadsError(\n new Error(\"Pass one of --form-id (from `baker ads meta lead-forms`), --ad-id, --adset-id or --campaign-id.\"),\n );\n }\n if (args.since) params.since = args.since as string;\n if (args.until) params.until = args.until as string;\n if (args.limit) params.limit = String(args.limit);\n if (args.after) params.after = String(args.after);\n if (args.full) params.full = \"true\";\n const page = await apiGet<MetaLeadPage>(\"/api/ads/meta/leads\", params);\n const fmt = csvOrJson(args);\n if (fmt !== \"json\") {\n writeAdsOutput(page.leads as unknown as Array<Record<string, unknown>>, fmt);\n return;\n }\n writeJsonEnvelope({\n ok: true,\n data: page.leads,\n ...(page.nextCursor ? { nextCursor: page.nextCursor } : {}),\n hints: leadPageHints(page, args.full === true),\n });\n } catch (err) {\n handleLeadsError(err);\n }\n },\n});\n","import { defineCommand } from \"citty\";\nimport { apiGet } from \"../../../client.ts\";\nimport { repeatedValues } from \"../../../lib/repeatedFlags.ts\";\nimport { registerSchema } from \"../../../schemas.ts\";\nimport { writeJsonEnvelope } from \"../output.ts\";\nimport { handleMetaError } from \"./shared.ts\";\nimport {\n bareAccountId,\n failWriteValidation,\n loadJsonFileArg,\n mergePayload,\n requireTarget,\n stageOp,\n upper,\n} from \"./write-shared.ts\";\n\n/**\n * Staging Instant Forms. A form is permanent on Meta — it is never deleted,\n * only archived — and nothing on it changes once it exists, so every write\n * here is a new form (create, duplicate) or an archive / unarchive, all staged\n * on this chat's draft and applied at publish.\n */\n\ntype Args = Record<string, unknown>;\n\nconst STAGED_NOTE = \"Staged until publish — review with `baker ads meta draft`.\";\n\nconst str = (value: unknown): string | undefined => (typeof value === \"string\" && value.length > 0 ? value : undefined);\n\n/** `\"Question? => answer | answer\"` → a CUSTOM question, multiple choice when it lists answers. */\nfunction customQuestion(value: string): Record<string, unknown> {\n const [label = \"\", answers] = value.split(\"=>\").map((part) => part.trim());\n const options = answers\n ?.split(\"|\")\n .map((answer) => answer.trim())\n .filter(Boolean)\n .map((answer) => ({ value: answer }));\n return { type: \"CUSTOM\", label, ...(options && options.length > 0 ? { options } : {}) };\n}\n\nfunction questionsFromFlags(args: Args, rawArgs: readonly string[] | undefined): unknown[] | undefined {\n const predefined = (str(args.questions) ?? \"\")\n .split(\",\")\n .map((type) => type.trim().toUpperCase())\n .filter(Boolean)\n .map((type) => ({ type }));\n const custom = repeatedValues(rawArgs, \"question\", args.question).map(customQuestion);\n const all = [...predefined, ...custom];\n return all.length > 0 ? all : undefined;\n}\n\n/**\n * The intro fields the flags name, and only those: a duplicate keeps every\n * other intro field of the live form (`mergeDuplicateSpec`), so `--intro-title`\n * alone does not wipe the text and `--intro-button` alone is not dropped.\n */\nfunction introFromFlags(args: Args, rawArgs: readonly string[] | undefined): Record<string, unknown> | undefined {\n const content = repeatedValues(rawArgs, \"intro-text\", args[\"intro-text\"]);\n const set = {\n ...(str(args[\"intro-title\"]) ? { title: str(args[\"intro-title\"]) } : {}),\n ...(content.length > 0 ? { style: content.length > 1 ? \"LIST_STYLE\" : \"PARAGRAPH_STYLE\", content } : {}),\n ...(str(args[\"intro-button\"]) ? { buttonText: str(args[\"intro-button\"]) } : {}),\n };\n return Object.keys(set).length > 0 ? set : undefined;\n}\n\nfunction thankYouFromFlags(args: Args): Record<string, unknown> | undefined {\n const fields = {\n title: str(args[\"thank-you-title\"]),\n body: str(args[\"thank-you-body\"]),\n buttonType: upper(args[\"thank-you-button\"]),\n buttonText: str(args[\"thank-you-button-text\"]),\n websiteUrl: str(args[\"thank-you-url\"]),\n businessPhoneNumber: str(args[\"thank-you-phone\"]),\n countryCode: upper(args[\"thank-you-country\"]),\n };\n const set = Object.fromEntries(Object.entries(fields).filter(([, value]) => value !== undefined));\n return Object.keys(set).length > 0 ? set : undefined;\n}\n\nfunction trackingFromFlags(args: Args, rawArgs: readonly string[] | undefined): Record<string, string> | undefined {\n const pairs = repeatedValues(rawArgs, \"tracking\", args.tracking).map((pair) => {\n const at = pair.indexOf(\"=\");\n if (at <= 0) failWriteValidation(`--tracking takes name=value, got \"${pair}\"`);\n return [pair.slice(0, at).trim(), pair.slice(at + 1).trim()] as const;\n });\n return pairs.length > 0 ? Object.fromEntries(pairs) : undefined;\n}\n\n/** The `leadForm.create` fields the flags name; anything a flag leaves out comes from `--file`. */\nexport function leadFormPayloadFromFlags(args: Args, rawArgs?: readonly string[]): Record<string, unknown> {\n const quality = args[\"higher-intent\"] ?? args[\"optimized-for-quality\"];\n return {\n page_id: str(args[\"page-id\"]),\n name: str(args.name),\n locale: upper(args.locale),\n questionPageHeadline: str(args.headline),\n questions: questionsFromFlags(args, rawArgs),\n privacyPolicyUrl: str(args[\"privacy-policy-url\"]),\n privacyPolicyLinkText: str(args[\"privacy-link-text\"]),\n followUpActionUrl: str(args[\"follow-up-url\"]),\n optimizedForQuality: typeof quality === \"boolean\" ? quality : undefined,\n intro: introFromFlags(args, rawArgs),\n thankYou: thankYouFromFlags(args),\n sharing: str(args.sharing)?.toLowerCase(),\n smsVerification: args[\"sms-verification\"] === true ? true : undefined,\n trackingParameters: trackingFromFlags(args, rawArgs),\n };\n}\n\n/**\n * A `--file` form as staged. `lead-forms spec > form.json` writes the CLI's\n * `{ok, data, hints}` envelope, so the form is its `data`; a hand-written file\n * is the form itself.\n */\nexport function formFileForStaging(raw: Record<string, unknown>): Record<string, unknown> {\n const data = raw.ok === true && \"data\" in raw ? raw.data : raw;\n return data && typeof data === \"object\" && !Array.isArray(data) ? (data as Record<string, unknown>) : raw;\n}\n\n/** The screens a duplicate changes field by field, so a flag naming one field keeps the others. */\nconst SCREENS = [\"intro\", \"thankYou\"] as const;\n\nfunction record(value: unknown): Record<string, unknown> | undefined {\n return value && typeof value === \"object\" && !Array.isArray(value) ? (value as Record<string, unknown>) : undefined;\n}\n\n/** `over` on `base`, the intro and thank-you screens merged a field at a time. */\nfunction mergeOver(base: Record<string, unknown>, over: Record<string, unknown>): Record<string, unknown> {\n const merged = mergePayload(base, over);\n for (const screen of SCREENS) {\n const from = record(base[screen]);\n const to = record(over[screen]);\n if (from && to) merged[screen] = mergePayload(from, to);\n }\n return merged;\n}\n\n/**\n * A duplicate: the live form, then the `--file` changes over it, then the\n * flags over both. Forms are permanent, so the intro and thank-you screens\n * change only in the fields named — never wholesale.\n */\nexport function mergeDuplicateSpec(\n live: Record<string, unknown>,\n changes: Record<string, unknown>,\n flags: Record<string, unknown>,\n): Record<string, unknown> {\n return mergeOver(mergeOver(live, changes), flags);\n}\n\nconst FORM_FLAG_ARGS = {\n name: { type: \"string\", description: \"Form name — unique among the Page's forms (archived ones count)\" },\n locale: {\n type: \"string\",\n description:\n \"Form language, e.g. EN_US, ES_LA, PT_BR, FR_FR, DE_DE (Meta's 32) — Meta words its own questions in it\",\n },\n headline: { type: \"string\", description: \"Headline above the questions\" },\n questions: {\n type: \"string\",\n description:\n \"Questions Meta words itself, comma-separated: FULL_NAME,EMAIL,PHONE,WORK_EMAIL,COMPANY_NAME,JOB_TITLE,CITY,ZIP,DOB…\",\n },\n question: {\n type: \"string\",\n description: 'A custom question, repeatable: \"Question?\" or multiple choice \"Question? => Answer | Answer\"',\n },\n \"privacy-policy-url\": { type: \"string\", description: \"Public https privacy policy (required)\" },\n \"privacy-link-text\": { type: \"string\", description: \"Words of the privacy link (≤70, default Privacy Policy)\" },\n \"follow-up-url\": { type: \"string\", description: \"https link of the default View website button\" },\n \"higher-intent\": {\n type: \"boolean\",\n description: \"Higher intent: people review their answers before submitting (default: more volume)\",\n },\n \"optimized-for-quality\": { type: \"boolean\", description: \"Same as --higher-intent\" },\n \"intro-title\": { type: \"string\", description: \"Intro screen title\" },\n \"intro-text\": { type: \"string\", description: \"Intro paragraph; repeat for bullet points\" },\n \"intro-button\": { type: \"string\", description: \"Intro screen button text\" },\n \"thank-you-title\": { type: \"string\", description: \"Thank-you screen title\" },\n \"thank-you-body\": { type: \"string\", description: \"Thank-you screen text\" },\n \"thank-you-button\": {\n type: \"string\",\n description: \"VIEW_WEBSITE|DOWNLOAD|BOOK_ON_WEBSITE|CALL_BUSINESS|WHATSAPP|P2B_MESSENGER|PROMO_CODE|NONE\",\n },\n \"thank-you-button-text\": { type: \"string\", description: \"Thank-you button text\" },\n \"thank-you-url\": { type: \"string\", description: \"Website the thank-you button opens\" },\n \"thank-you-phone\": { type: \"string\", description: \"CALL_BUSINESS: phone number, e.g. +14155550100\" },\n \"thank-you-country\": { type: \"string\", description: \"CALL_BUSINESS: the number's country, e.g. US\" },\n sharing: { type: \"string\", description: \"restricted (only people shown the ad, Meta's default) | open\" },\n \"sms-verification\": { type: \"boolean\", description: \"Confirm the PHONE answer with a text-message code\" },\n tracking: { type: \"string\", description: \"name=value Meta adds to every lead, repeatable\" },\n} as const;\n\nconst SCHEMA_FORM_ARGS = Object.fromEntries(\n Object.entries(FORM_FLAG_ARGS).map(([key, arg]) => [key, { ...arg, required: false }]),\n);\n\n// --- Create ---\n\nregisterSchema({\n command: \"ads.meta.lead-forms.create\",\n description:\n \"Start here to stage a Page-owned Meta Instant Form. The form is created only when this Session publishes, and it is permanent (Meta archives, never deletes). Use the returned meta_temp_* ref as --lead-form on a creative in the same Session. --file takes the whole spec (questions with keys, options, inlineContext; intro; disclaimer with checkboxes; thankYou; sharing; trackingParameters).\",\n args: {\n \"account-id\": { type: \"string\", description: \"Selected Meta ad account, act_<id>\", required: true },\n \"page-id\": {\n type: \"string\",\n description:\n \"Facebook Page that owns the form — choose one with this account in promotingAccountIds and hasPageAccessToken: true from `baker ads meta pages`\",\n required: true,\n },\n ...SCHEMA_FORM_ARGS,\n file: {\n type: \"string\",\n description: \"JSON file with the full form payload; flags override its keys\",\n required: false,\n },\n },\n});\n\nexport const leadFormsCreateCommand = defineCommand({\n meta: {\n name: \"create\",\n description: `Stage a Meta Instant Form on a Facebook Page. ${STAGED_NOTE} The form is permanent once published (archive it to retire it).\nChoose a Page with this account in promotingAccountIds and hasPageAccessToken: true from \\`baker ads meta pages\\`.\nExample: baker ads meta lead-forms create --account-id act_123 --page-id 456 --name \"Free audit\" --questions FULL_NAME,EMAIL,PHONE --question \"Budget? => Under 1k | 1k+\" --privacy-policy-url https://example.com/privacy --follow-up-url https://example.com\nFull spec: --file form.json (see \\`baker schema ads.meta.lead-forms.create\\` and the ads-meta tool doc).`,\n },\n args: {\n \"account-id\": { type: \"string\", description: \"Selected Meta ad account\" },\n \"page-id\": { type: \"string\", description: \"Facebook Page ID\" },\n ...FORM_FLAG_ARGS,\n file: { type: \"string\", description: \"JSON form payload; flags override file keys\" },\n },\n run: async ({ args, rawArgs }) => {\n const accountId = bareAccountId(args as Args);\n const payload = mergePayload(\n formFileForStaging(loadJsonFileArg(args.file)),\n leadFormPayloadFromFlags(args as Args, rawArgs),\n );\n await stageOp({ kind: \"leadForm.create\", accountId, payload }, [\n \"An ad opens it with `creatives create --cta SIGN_UP --lead-form <ref>` into an ad set with destination ON_AD.\",\n ]);\n },\n});\n\n// --- Spec + duplicate ---\n\ninterface FormSpec {\n spec: Record<string, unknown>;\n notCopied: string[];\n status?: string;\n}\n\nregisterSchema({\n command: \"ads.meta.lead-forms.spec\",\n description:\n \"Read a live Instant Form as the exact payload `lead-forms create --file` takes — the starting point for a changed copy. Lists what could not be carried over.\",\n args: { id: { type: \"positional\", description: \"Instant Form id\", required: true } },\n});\n\nexport const leadFormsSpecCommand = defineCommand({\n meta: {\n name: \"spec\",\n description: `A live form as the payload \\`lead-forms create --file\\` / \\`lead-forms duplicate --file\\` take.\nExample: baker ads meta lead-forms spec 1460955752594831 > form.json`,\n },\n args: { id: { type: \"positional\", description: \"Instant Form id\", required: true } },\n run: async ({ args }) => {\n try {\n const data = await apiGet<FormSpec>(\"/api/ads/meta/lead-forms/spec\", { \"form-id\": String(args.id) });\n writeJsonEnvelope({\n ok: true,\n data: data.spec,\n ...(data.notCopied.length > 0 ? { hints: [`Not carried over: ${data.notCopied.join(\"; \")}`] } : {}),\n });\n } catch (err) {\n handleMetaError(err);\n }\n },\n});\n\nregisterSchema({\n command: \"ads.meta.lead-forms.duplicate\",\n description:\n \"Stage a NEW Instant Form copied from a live one, with changes — the only way Meta lets a form change (it keeps a form's questions, texts and settings as made). --file holds the changed keys; flags override both. Point ads at the copy, then archive the original.\",\n args: {\n id: { type: \"positional\", description: \"Instant Form id to copy\", required: true },\n \"account-id\": { type: \"string\", description: \"Selected Meta ad account, act_<id>\", required: true },\n ...SCHEMA_FORM_ARGS,\n name: { type: \"string\", description: \"Name of the copy — unique on the Page\", required: true },\n file: { type: \"string\", description: \"JSON with the keys to change (same shape as create)\", required: false },\n },\n});\n\nexport const leadFormsDuplicateCommand = defineCommand({\n meta: {\n name: \"duplicate\",\n description: `Stage a changed copy of a live Instant Form (Meta changes nothing on an existing form). ${STAGED_NOTE}\nExample: baker ads meta lead-forms duplicate 1460955752594831 --account-id act_123 --name \"Free audit (ES)\" --locale ES_LA\nThen point the ads at the copy (\\`creatives create --lead-form <ref>\\`) and \\`lead-forms archive <old id>\\`.`,\n },\n args: {\n id: { type: \"positional\", description: \"Instant Form id to copy\", required: true },\n \"account-id\": { type: \"string\", description: \"Selected Meta ad account\" },\n ...FORM_FLAG_ARGS,\n file: { type: \"string\", description: \"JSON with the keys to change\" },\n },\n run: async ({ args, rawArgs }) => {\n const accountId = bareAccountId(args as Args);\n if (!str(args.name)) failWriteValidation(\"--name is required: Meta refuses a second form with the same name\");\n let source: FormSpec;\n try {\n source = await apiGet<FormSpec>(\"/api/ads/meta/lead-forms/spec\", { \"form-id\": String(args.id) });\n } catch (err) {\n handleMetaError(err);\n }\n const flags = leadFormPayloadFromFlags(args as Args, rawArgs);\n const payload = mergeDuplicateSpec(source.spec, formFileForStaging(loadJsonFileArg(args.file)), flags);\n const notCopied = source.notCopied.length > 0 ? [`Not carried over: ${source.notCopied.join(\"; \")}`] : [];\n await stageOp({ kind: \"leadForm.create\", accountId, payload }, [\n ...notCopied,\n `Point the ads at the copy, then \\`baker ads meta lead-forms archive ${String(args.id)}\\` to retire the original.`,\n ]);\n },\n});\n\n// --- Archive / unarchive ---\n\nfunction statusCommand(name: \"archive\" | \"unarchive\", status: \"ARCHIVED\" | \"ACTIVE\", description: string) {\n registerSchema({\n command: `ads.meta.lead-forms.${name}`,\n description,\n args: {\n id: { type: \"positional\", description: \"Instant Form id\", required: true },\n \"account-id\": { type: \"string\", description: \"Ad account the change is staged under\", required: true },\n },\n });\n return defineCommand({\n meta: {\n name,\n description: `${description} ${STAGED_NOTE}\nExample: baker ads meta lead-forms ${name} 1460955752594831 --account-id act_123`,\n },\n args: {\n id: { type: \"positional\", description: \"Instant Form id\", required: true },\n \"account-id\": { type: \"string\", description: \"Numeric account ID or act_N\" },\n },\n run: async ({ args }) => {\n await stageOp({\n kind: \"leadForm.update\",\n accountId: bareAccountId(args as Args),\n target: requireTarget(args as Args, \"Instant Form\"),\n payload: { status },\n });\n },\n });\n}\n\nexport const leadFormsArchiveCommand = statusCommand(\n \"archive\",\n \"ARCHIVED\",\n \"Archive an Instant Form: it leaves the Page's form library and no ad can use it. Meta never deletes a form; this is how one is retired.\",\n);\n\nexport const leadFormsUnarchiveCommand = statusCommand(\n \"unarchive\",\n \"ACTIVE\",\n \"Bring an archived Instant Form back so ads can use it again.\",\n);\n"],"mappings":"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","names":["defineCommand","defineCommand"]}
1
+ {"version":3,"sources":["../src/commands/ads/meta/leads.ts","../src/commands/ads/meta/lead-form-writes.ts"],"sourcesContent":["import { isMetaLeadAdsRefusal } from \"@baker/api/ads-meta\";\nimport { defineCommand } from \"citty\";\nimport { ApiError, apiGet } from \"../../../client.ts\";\nimport { registerSchema } from \"../../../schemas.ts\";\nimport { writeAdsJson, writeAdsOutput, writeJsonEnvelope } from \"../output.ts\";\nimport {\n leadFormsArchiveCommand,\n leadFormsCreateCommand,\n leadFormsDuplicateCommand,\n leadFormsSpecCommand,\n leadFormsUnarchiveCommand,\n} from \"./lead-form-writes.ts\";\nimport { csvOrJson, handleMetaError } from \"./shared.ts\";\n\n/** An instant form attached to a Facebook Page. */\nexport interface MetaLeadForm {\n id: string;\n name?: string;\n status?: string;\n locale?: string;\n leads_count?: number;\n created_time?: string;\n}\n\n/** One page of leads plus the cursor that continues it. */\nexport interface MetaLeadPage {\n leads: MetaLead[];\n /** Present only while Meta says another page exists. */\n nextCursor?: string;\n /** What the backend says about this page (every answer masked when it could not tell contact questions apart). */\n hints?: string[];\n}\n\n/** One lead, already shaped by the backend (contact values masked unless --full). */\nexport interface MetaLead {\n id: string;\n created_time?: string;\n ad_id?: string;\n ad_name?: string;\n adset_id?: string;\n adset_name?: string;\n campaign_id?: string;\n campaign_name?: string;\n form_id?: string;\n is_organic?: boolean;\n platform?: string;\n answers: Record<string, string>;\n /** Each disclaimer checkbox by its key: ticked or not. */\n consents?: Record<string, boolean>;\n partner_name?: string;\n}\n\nregisterSchema({\n command: \"ads.meta.lead-forms\",\n description:\n \"List the instant (lead) forms on a Facebook Page, with how many leads each has collected. Start here: a lead is fetched by form id. Use a Page from `baker ads meta pages` with hasPageAccessToken: true.\",\n args: {\n \"page-id\": {\n type: \"string\",\n description: \"Facebook Page ID — from `baker ads meta pages` with hasPageAccessToken: true\",\n required: true,\n },\n full: { type: \"boolean\", description: \"Include each form's questions\", required: false },\n output: { type: \"string\", description: \"json|csv\", required: false, default: \"json\" },\n },\n});\n\nregisterSchema({\n command: \"ads.meta.leads\",\n description:\n \"Read the leads an instant form collected. Contact details (email, phone) come back masked; pass --full only when the raw values are genuinely needed, e.g. handing them to a CRM.\",\n args: {\n \"form-id\": { type: \"string\", description: \"Instant form ID — from `baker ads meta lead-forms`\", required: false },\n \"ad-id\": { type: \"string\", description: \"Read the leads of one ad instead of a whole form\", required: false },\n \"adset-id\": { type: \"string\", description: \"Read the leads of every ad in an ad set\", required: false },\n \"campaign-id\": { type: \"string\", description: \"Read the leads of every ad in a campaign\", required: false },\n since: { type: \"string\", description: \"Earliest submission date, YYYY-MM-DD\", required: false },\n until: { type: \"string\", description: \"Latest submission date, YYYY-MM-DD (inclusive)\", required: false },\n limit: { type: \"string\", description: \"Leads per page (default 100, max 500)\", required: false },\n after: {\n type: \"string\",\n description: \"Cursor from a previous call's nextCursor — fetches the following page\",\n required: false,\n },\n full: { type: \"boolean\", description: \"Return raw contact details instead of masked ones\", required: false },\n output: { type: \"string\", description: \"json|csv\", required: false, default: \"json\" },\n },\n});\n\n/**\n * A form that exists but has never collected anything is the single most common\n * thing to misread here — it looks identical to a permissions problem in the\n * output, and the remedies are opposite. Say which it is.\n */\n/** The backend drains instant forms up to this many; see `META_LEAD_FORMS_MAX`. */\nconst LEAD_FORMS_CAP = 500;\n\nexport function leadFormHints(forms: MetaLeadForm[]): string[] {\n if (forms.length === 0) {\n return [\"This Page has NO instant forms. Nothing to read leads from — the form has to be created first.\"];\n }\n if (forms.length >= LEAD_FORMS_CAP) {\n return [\n `PARTIAL LIST — this Page has at least ${LEAD_FORMS_CAP} instant forms and only the first ${LEAD_FORMS_CAP} came back. Do not describe this as every form on the Page.`,\n ];\n }\n const empty = forms.filter((form) => (form.leads_count ?? 0) === 0);\n if (empty.length === forms.length) {\n return [\n \"Every form on this Page has collected 0 leads. That is a real, readable answer — the connection works. Do not retry and do not report it as a permissions problem.\",\n ];\n }\n return [];\n}\n\n/**\n * A `FORBIDDEN` that Baker itself produced is already conclusive, so it must not\n * collect the generic ambiguous-403 remedy.\n *\n * `metaForbiddenFix` exists for Meta's own 403, which covers two causes with\n * opposite fixes and can only be told apart by testing the blast radius. Pasting\n * that after *\"this needs switching on by the Baker team\"* would hand the agent\n * a paragraph suggesting a reconnect right underneath a sentence saying a\n * reconnect cannot help — and the agent would pass it on, costing the client a\n * pointless round trip through their Meta login. Everything that is genuinely\n * Meta's still goes through the normal handler.\n */\nfunction handleLeadsError(err: unknown): never {\n if (err instanceof ApiError && err.code === \"FORBIDDEN\" && isMetaLeadAdsRefusal(err.message)) {\n writeAdsJson({\n ok: false,\n error: {\n code: err.code,\n message: err.message,\n fix: { action: \"reject\", explanation: err.message },\n retryable: false,\n },\n });\n process.exit(1);\n }\n handleMetaError(err);\n}\n\n/**\n * What the agent has to know before it reports a number.\n *\n * Counting is what this command is mostly used for, so a page that is full is\n * the dangerous case: without a word, 100 rows read as \"100 leads\" and the\n * client is told something false. The cursor is the remedy, and it has to be\n * named in the same breath as the warning.\n */\nexport function leadPageHints(page: MetaLeadPage, full: boolean): string[] {\n const hints: string[] = [];\n if (page.nextCursor) {\n hints.push(\n `PARTIAL PAGE — more leads exist. This is ${page.leads.length} of an unknown larger number; do NOT report it as a total. Fetch the next page with \\`--after ${page.nextCursor}\\` (repeat until no nextCursor), or narrow the window with --since/--until.`,\n );\n }\n if (!full) {\n hints.push(\n \"Answers that give contact details (email, phone, any question asking for one) are masked. Report on them as they are; pass --full only when the raw values have to leave Baker (a CRM hand-off), and never paste them into the chat otherwise.\",\n );\n }\n hints.push(...(page.hints ?? []));\n return hints;\n}\n\nconst leadFormsListArgs = {\n \"page-id\": { type: \"string\", description: \"Facebook Page ID\" },\n full: { type: \"boolean\", description: \"Include each form's questions\" },\n output: { type: \"string\", description: \"Output format\", default: \"json\" },\n} as const;\n\nconst leadFormsListCommand = defineCommand({\n meta: {\n name: \"list\",\n description:\n \"List the Instant Forms on a Facebook Page, with lead counts. Choose a Page with hasPageAccessToken: true from `baker ads meta pages`. Pass --full to include questions.\",\n },\n args: leadFormsListArgs,\n run: async ({ args }) => {\n try {\n const pageId = args[\"page-id\"] as string | undefined;\n if (!pageId) {\n handleLeadsError(\n new Error(\n \"--page-id is required. Run `baker ads meta pages` and choose a Page with hasPageAccessToken: true.\",\n ),\n );\n }\n const params: Record<string, string> = { \"page-id\": pageId };\n if (args.full) params.full = \"true\";\n const data = await apiGet<MetaLeadForm[]>(\"/api/ads/meta/lead-forms\", params);\n const fmt = csvOrJson(args);\n if (fmt !== \"json\") {\n writeAdsOutput(data as unknown as Array<Record<string, unknown>>, fmt);\n return;\n }\n const hints = leadFormHints(data);\n writeJsonEnvelope(hints.length > 0 ? { ok: true, data, hints } : { ok: true, data });\n } catch (err) {\n handleLeadsError(err);\n }\n },\n});\n\nexport const leadFormsCommand = defineCommand({\n meta: {\n name: \"lead-forms\",\n description: `List Instant Forms on a Facebook Page, or stage a new one, a changed copy, an archive.\nA form is permanent on Meta and nothing on it changes once made: to change one, duplicate it with changes and archive the old one.\nExamples:\n baker ads meta lead-forms --page-id 100635183117758\n baker ads meta lead-forms --page-id 100635183117758 --full\n baker ads meta lead-forms create --account-id act_123 --page-id 555 --file form.json\n baker ads meta lead-forms spec 987654321 — a live form as a create --file payload\n baker ads meta lead-forms duplicate 987654321 --account-id act_123 --name \"Free audit (ES)\" --locale ES_LA\n baker ads meta lead-forms archive 987654321 --account-id act_123`,\n },\n args: leadFormsListArgs,\n subCommands: {\n list: leadFormsListCommand,\n create: leadFormsCreateCommand,\n spec: leadFormsSpecCommand,\n duplicate: leadFormsDuplicateCommand,\n archive: leadFormsArchiveCommand,\n unarchive: leadFormsUnarchiveCommand,\n },\n default: \"list\",\n});\n\nexport const leadsCommand = defineCommand({\n meta: {\n name: \"leads\",\n description: `Read the leads an instant form collected. Every question comes back; email and phone answers are MASKED unless you pass --full, so you can report on what a form is collecting without moving contact details around.\n\nExamples:\n baker ads meta leads --form-id 987654321\n baker ads meta leads --form-id 987654321 --since 2026-08-01 --until 2026-08-31\n baker ads meta leads --ad-id 6123456789 --limit 50\n baker ads meta leads --adset-id 6123456780 --since 2026-08-01 # every ad in the ad set (Meta has no ad set edge)\n baker ads meta leads --form-id 987654321 --after <nextCursor> # the next page\n baker ads meta leads --form-id 987654321 --full # raw contact details — only when handing leads over\n\nResults are PAGED. A response carrying \\`nextCursor\\` is not the whole set — follow it before reporting any total.`,\n },\n args: {\n \"form-id\": { type: \"string\", description: \"Instant form ID (from `baker ads meta lead-forms`)\" },\n \"ad-id\": { type: \"string\", description: \"Read one ad's leads instead of a whole form\" },\n \"adset-id\": { type: \"string\", description: \"Read the leads of every ad in an ad set\" },\n \"campaign-id\": { type: \"string\", description: \"Read the leads of every ad in a campaign\" },\n since: { type: \"string\", description: \"Earliest submission date, YYYY-MM-DD\" },\n until: { type: \"string\", description: \"Latest submission date, YYYY-MM-DD (inclusive)\" },\n limit: { type: \"string\", description: \"Leads per page (default 100, max 500)\" },\n after: { type: \"string\", description: \"Cursor from a previous call's nextCursor\" },\n full: { type: \"boolean\", description: \"Return raw contact details instead of masked ones\" },\n output: { type: \"string\", description: \"Output format\", default: \"json\" },\n },\n run: async ({ args }) => {\n try {\n const params: Record<string, string> = {};\n for (const flag of [\"form-id\", \"ad-id\", \"adset-id\", \"campaign-id\"] as const) {\n const value = args[flag];\n if (typeof value === \"string\" && value.length > 0) params[flag] = value;\n }\n if (Object.keys(params).length !== 1) {\n handleLeadsError(\n new Error(\"Pass one of --form-id (from `baker ads meta lead-forms`), --ad-id, --adset-id or --campaign-id.\"),\n );\n }\n if (args.since) params.since = args.since as string;\n if (args.until) params.until = args.until as string;\n if (args.limit) params.limit = String(args.limit);\n if (args.after) params.after = String(args.after);\n if (args.full) params.full = \"true\";\n const page = await apiGet<MetaLeadPage>(\"/api/ads/meta/leads\", params);\n const fmt = csvOrJson(args);\n if (fmt !== \"json\") {\n writeAdsOutput(page.leads as unknown as Array<Record<string, unknown>>, fmt);\n return;\n }\n writeJsonEnvelope({\n ok: true,\n data: page.leads,\n ...(page.nextCursor ? { nextCursor: page.nextCursor } : {}),\n hints: leadPageHints(page, args.full === true),\n });\n } catch (err) {\n handleLeadsError(err);\n }\n },\n});\n","import { defineCommand } from \"citty\";\nimport { apiGet } from \"../../../client.ts\";\nimport { repeatedValues } from \"../../../lib/repeatedFlags.ts\";\nimport { registerSchema } from \"../../../schemas.ts\";\nimport { writeJsonEnvelope } from \"../output.ts\";\nimport { handleMetaError } from \"./shared.ts\";\nimport {\n bareAccountId,\n failWriteValidation,\n loadJsonFileArg,\n mergePayload,\n requireTarget,\n stageOp,\n upper,\n} from \"./write-shared.ts\";\n\n/**\n * Staging Instant Forms. A form is permanent on Meta — it is never deleted,\n * only archived — and nothing on it changes once it exists, so every write\n * here is a new form (create, duplicate) or an archive / unarchive, all staged\n * on this chat's draft and applied at publish.\n */\n\ntype Args = Record<string, unknown>;\n\nconst STAGED_NOTE = \"Staged until publish — review with `baker ads meta draft`.\";\n\nconst str = (value: unknown): string | undefined => (typeof value === \"string\" && value.length > 0 ? value : undefined);\n\n/** `\"Question? => answer | answer\"` → a CUSTOM question, multiple choice when it lists answers. */\nfunction customQuestion(value: string): Record<string, unknown> {\n const [label = \"\", answers] = value.split(\"=>\").map((part) => part.trim());\n const options = answers\n ?.split(\"|\")\n .map((answer) => answer.trim())\n .filter(Boolean)\n .map((answer) => ({ value: answer }));\n return { type: \"CUSTOM\", label, ...(options && options.length > 0 ? { options } : {}) };\n}\n\nfunction questionsFromFlags(args: Args, rawArgs: readonly string[] | undefined): unknown[] | undefined {\n const predefined = (str(args.questions) ?? \"\")\n .split(\",\")\n .map((type) => type.trim().toUpperCase())\n .filter(Boolean)\n .map((type) => ({ type }));\n const custom = repeatedValues(rawArgs, \"question\", args.question).map(customQuestion);\n const all = [...predefined, ...custom];\n return all.length > 0 ? all : undefined;\n}\n\n/**\n * The intro fields the flags name, and only those: a duplicate keeps every\n * other intro field of the live form (`mergeDuplicateSpec`), so `--intro-title`\n * alone does not wipe the text and `--intro-button` alone is not dropped.\n */\nfunction introFromFlags(args: Args, rawArgs: readonly string[] | undefined): Record<string, unknown> | undefined {\n const content = repeatedValues(rawArgs, \"intro-text\", args[\"intro-text\"]);\n const set = {\n ...(str(args[\"intro-title\"]) ? { title: str(args[\"intro-title\"]) } : {}),\n ...(content.length > 0 ? { style: content.length > 1 ? \"LIST_STYLE\" : \"PARAGRAPH_STYLE\", content } : {}),\n ...(str(args[\"intro-button\"]) ? { buttonText: str(args[\"intro-button\"]) } : {}),\n };\n return Object.keys(set).length > 0 ? set : undefined;\n}\n\nfunction thankYouFromFlags(args: Args): Record<string, unknown> | undefined {\n const fields = {\n title: str(args[\"thank-you-title\"]),\n body: str(args[\"thank-you-body\"]),\n buttonType: upper(args[\"thank-you-button\"]),\n buttonText: str(args[\"thank-you-button-text\"]),\n websiteUrl: str(args[\"thank-you-url\"]),\n businessPhoneNumber: str(args[\"thank-you-phone\"]),\n countryCode: upper(args[\"thank-you-country\"]),\n };\n const set = Object.fromEntries(Object.entries(fields).filter(([, value]) => value !== undefined));\n return Object.keys(set).length > 0 ? set : undefined;\n}\n\nfunction trackingFromFlags(args: Args, rawArgs: readonly string[] | undefined): Record<string, string> | undefined {\n const pairs = repeatedValues(rawArgs, \"tracking\", args.tracking).map((pair) => {\n const at = pair.indexOf(\"=\");\n if (at <= 0) failWriteValidation(`--tracking takes name=value, got \"${pair}\"`);\n return [pair.slice(0, at).trim(), pair.slice(at + 1).trim()] as const;\n });\n return pairs.length > 0 ? Object.fromEntries(pairs) : undefined;\n}\n\n/** The `leadForm.create` fields the flags name; anything a flag leaves out comes from `--file`. */\nexport function leadFormPayloadFromFlags(args: Args, rawArgs?: readonly string[]): Record<string, unknown> {\n const quality = args[\"higher-intent\"] ?? args[\"optimized-for-quality\"];\n return {\n page_id: str(args[\"page-id\"]),\n name: str(args.name),\n locale: upper(args.locale),\n questionPageHeadline: str(args.headline),\n questions: questionsFromFlags(args, rawArgs),\n privacyPolicyUrl: str(args[\"privacy-policy-url\"]),\n privacyPolicyLinkText: str(args[\"privacy-link-text\"]),\n followUpActionUrl: str(args[\"follow-up-url\"]),\n optimizedForQuality: typeof quality === \"boolean\" ? quality : undefined,\n intro: introFromFlags(args, rawArgs),\n thankYou: thankYouFromFlags(args),\n sharing: str(args.sharing)?.toLowerCase(),\n smsVerification: args[\"sms-verification\"] === true ? true : undefined,\n trackingParameters: trackingFromFlags(args, rawArgs),\n };\n}\n\n/**\n * A `--file` form as staged. `lead-forms spec > form.json` writes the CLI's\n * `{ok, data, hints}` envelope, so the form is its `data`; a hand-written file\n * is the form itself.\n */\nexport function formFileForStaging(raw: Record<string, unknown>): Record<string, unknown> {\n const data = raw.ok === true && \"data\" in raw ? raw.data : raw;\n return data && typeof data === \"object\" && !Array.isArray(data) ? (data as Record<string, unknown>) : raw;\n}\n\n/** The screens a duplicate changes field by field, so a flag naming one field keeps the others. */\nconst SCREENS = [\"intro\", \"thankYou\"] as const;\n\nfunction record(value: unknown): Record<string, unknown> | undefined {\n return value && typeof value === \"object\" && !Array.isArray(value) ? (value as Record<string, unknown>) : undefined;\n}\n\n/** `over` on `base`, the intro and thank-you screens merged a field at a time. */\nfunction mergeOver(base: Record<string, unknown>, over: Record<string, unknown>): Record<string, unknown> {\n const merged = mergePayload(base, over);\n for (const screen of SCREENS) {\n const from = record(base[screen]);\n const to = record(over[screen]);\n if (from && to) merged[screen] = mergePayload(from, to);\n }\n return merged;\n}\n\n/**\n * A duplicate: the live form, then the `--file` changes over it, then the\n * flags over both. Forms are permanent, so the intro and thank-you screens\n * change only in the fields named — never wholesale.\n */\nexport function mergeDuplicateSpec(\n live: Record<string, unknown>,\n changes: Record<string, unknown>,\n flags: Record<string, unknown>,\n): Record<string, unknown> {\n return mergeOver(mergeOver(live, changes), flags);\n}\n\nconst FORM_FLAG_ARGS = {\n name: { type: \"string\", description: \"Form name — unique among the Page's forms (archived ones count)\" },\n locale: {\n type: \"string\",\n description:\n \"Form language, e.g. EN_US, ES_LA, PT_BR, FR_FR, DE_DE (Meta's 32) — Meta words its own questions in it\",\n },\n headline: { type: \"string\", description: \"Headline above the questions\" },\n questions: {\n type: \"string\",\n description:\n \"Questions Meta words itself, comma-separated: FULL_NAME,EMAIL,PHONE,WORK_EMAIL,COMPANY_NAME,JOB_TITLE,CITY,ZIP,DOB…\",\n },\n question: {\n type: \"string\",\n description: 'A custom question, repeatable: \"Question?\" or multiple choice \"Question? => Answer | Answer\"',\n },\n \"privacy-policy-url\": { type: \"string\", description: \"Public https privacy policy (required)\" },\n \"privacy-link-text\": { type: \"string\", description: \"Words of the privacy link (≤70, default Privacy Policy)\" },\n \"follow-up-url\": { type: \"string\", description: \"https link of the default View website button\" },\n \"higher-intent\": {\n type: \"boolean\",\n description: \"Higher intent: people review their answers before submitting (default: more volume)\",\n },\n \"optimized-for-quality\": { type: \"boolean\", description: \"Same as --higher-intent\" },\n \"intro-title\": { type: \"string\", description: \"Intro screen title\" },\n \"intro-text\": { type: \"string\", description: \"Intro paragraph; repeat for bullet points\" },\n \"intro-button\": { type: \"string\", description: \"Intro screen button text\" },\n \"thank-you-title\": { type: \"string\", description: \"Thank-you screen title\" },\n \"thank-you-body\": { type: \"string\", description: \"Thank-you screen text\" },\n \"thank-you-button\": {\n type: \"string\",\n description: \"VIEW_WEBSITE|DOWNLOAD|BOOK_ON_WEBSITE|CALL_BUSINESS|WHATSAPP|P2B_MESSENGER|PROMO_CODE|NONE\",\n },\n \"thank-you-button-text\": { type: \"string\", description: \"Thank-you button text\" },\n \"thank-you-url\": { type: \"string\", description: \"Website the thank-you button opens\" },\n \"thank-you-phone\": { type: \"string\", description: \"CALL_BUSINESS: phone number, e.g. +14155550100\" },\n \"thank-you-country\": { type: \"string\", description: \"CALL_BUSINESS: the number's country, e.g. US\" },\n sharing: { type: \"string\", description: \"restricted (only people shown the ad, Meta's default) | open\" },\n \"sms-verification\": { type: \"boolean\", description: \"Confirm the PHONE answer with a text-message code\" },\n tracking: { type: \"string\", description: \"name=value Meta adds to every lead, repeatable\" },\n} as const;\n\nconst SCHEMA_FORM_ARGS = Object.fromEntries(\n Object.entries(FORM_FLAG_ARGS).map(([key, arg]) => [key, { ...arg, required: false }]),\n);\n\n// --- Create ---\n\nregisterSchema({\n command: \"ads.meta.lead-forms.create\",\n description:\n \"Start here to stage a Page-owned Meta Instant Form. The form is created only when this Session publishes, and it is permanent (Meta archives, never deletes). Use the returned meta_temp_* ref as --lead-form on a creative in the same Session. --file takes the whole spec (questions with keys, options, inlineContext; intro; disclaimer with checkboxes; thankYou; sharing; trackingParameters).\",\n args: {\n \"account-id\": { type: \"string\", description: \"Selected Meta ad account, act_<id>\", required: true },\n \"page-id\": {\n type: \"string\",\n description:\n \"Facebook Page that owns the form — choose one with this account in promotingAccountIds and hasPageAccessToken: true from `baker ads meta pages`\",\n required: true,\n },\n ...SCHEMA_FORM_ARGS,\n file: {\n type: \"string\",\n description: \"JSON file with the full form payload; flags override its keys\",\n required: false,\n },\n },\n});\n\nexport const leadFormsCreateCommand = defineCommand({\n meta: {\n name: \"create\",\n description: `Stage a Meta Instant Form on a Facebook Page. ${STAGED_NOTE} The form is permanent once published (archive it to retire it).\nChoose a Page with this account in promotingAccountIds and hasPageAccessToken: true from \\`baker ads meta pages\\`.\nExample: baker ads meta lead-forms create --account-id act_123 --page-id 456 --name \"Free audit\" --questions FULL_NAME,EMAIL,PHONE --question \"Budget? => Under 1k | 1k+\" --privacy-policy-url https://example.com/privacy --follow-up-url https://example.com\nFull spec: --file form.json (see \\`baker schema ads.meta.lead-forms.create\\` and the ads-meta tool doc).`,\n },\n args: {\n \"account-id\": { type: \"string\", description: \"Selected Meta ad account\" },\n \"page-id\": { type: \"string\", description: \"Facebook Page ID\" },\n ...FORM_FLAG_ARGS,\n file: { type: \"string\", description: \"JSON form payload; flags override file keys\" },\n },\n run: async ({ args, rawArgs }) => {\n const accountId = await bareAccountId(args as Args);\n const payload = mergePayload(\n formFileForStaging(loadJsonFileArg(args.file)),\n leadFormPayloadFromFlags(args as Args, rawArgs),\n );\n await stageOp({ kind: \"leadForm.create\", accountId, payload }, [\n \"An ad opens it with `creatives create --cta SIGN_UP --lead-form <ref>` into an ad set with destination ON_AD.\",\n ]);\n },\n});\n\n// --- Spec + duplicate ---\n\ninterface FormSpec {\n spec: Record<string, unknown>;\n notCopied: string[];\n status?: string;\n}\n\nregisterSchema({\n command: \"ads.meta.lead-forms.spec\",\n description:\n \"Read a live Instant Form as the exact payload `lead-forms create --file` takes — the starting point for a changed copy. Lists what could not be carried over.\",\n args: { id: { type: \"positional\", description: \"Instant Form id\", required: true } },\n});\n\nexport const leadFormsSpecCommand = defineCommand({\n meta: {\n name: \"spec\",\n description: `A live form as the payload \\`lead-forms create --file\\` / \\`lead-forms duplicate --file\\` take.\nExample: baker ads meta lead-forms spec 1460955752594831 > form.json`,\n },\n args: { id: { type: \"positional\", description: \"Instant Form id\", required: true } },\n run: async ({ args }) => {\n try {\n const data = await apiGet<FormSpec>(\"/api/ads/meta/lead-forms/spec\", { \"form-id\": String(args.id) });\n writeJsonEnvelope({\n ok: true,\n data: data.spec,\n ...(data.notCopied.length > 0 ? { hints: [`Not carried over: ${data.notCopied.join(\"; \")}`] } : {}),\n });\n } catch (err) {\n handleMetaError(err);\n }\n },\n});\n\nregisterSchema({\n command: \"ads.meta.lead-forms.duplicate\",\n description:\n \"Stage a NEW Instant Form copied from a live one, with changes — the only way Meta lets a form change (it keeps a form's questions, texts and settings as made). --file holds the changed keys; flags override both. Point ads at the copy, then archive the original.\",\n args: {\n id: { type: \"positional\", description: \"Instant Form id to copy\", required: true },\n \"account-id\": { type: \"string\", description: \"Selected Meta ad account, act_<id>\", required: true },\n ...SCHEMA_FORM_ARGS,\n name: { type: \"string\", description: \"Name of the copy — unique on the Page\", required: true },\n file: { type: \"string\", description: \"JSON with the keys to change (same shape as create)\", required: false },\n },\n});\n\nexport const leadFormsDuplicateCommand = defineCommand({\n meta: {\n name: \"duplicate\",\n description: `Stage a changed copy of a live Instant Form (Meta changes nothing on an existing form). ${STAGED_NOTE}\nExample: baker ads meta lead-forms duplicate 1460955752594831 --account-id act_123 --name \"Free audit (ES)\" --locale ES_LA\nThen point the ads at the copy (\\`creatives create --lead-form <ref>\\`) and \\`lead-forms archive <old id>\\`.`,\n },\n args: {\n id: { type: \"positional\", description: \"Instant Form id to copy\", required: true },\n \"account-id\": { type: \"string\", description: \"Selected Meta ad account\" },\n ...FORM_FLAG_ARGS,\n file: { type: \"string\", description: \"JSON with the keys to change\" },\n },\n run: async ({ args, rawArgs }) => {\n const accountId = await bareAccountId(args as Args);\n if (!str(args.name)) failWriteValidation(\"--name is required: Meta refuses a second form with the same name\");\n let source: FormSpec;\n try {\n source = await apiGet<FormSpec>(\"/api/ads/meta/lead-forms/spec\", { \"form-id\": String(args.id) });\n } catch (err) {\n handleMetaError(err);\n }\n const flags = leadFormPayloadFromFlags(args as Args, rawArgs);\n const payload = mergeDuplicateSpec(source.spec, formFileForStaging(loadJsonFileArg(args.file)), flags);\n const notCopied = source.notCopied.length > 0 ? [`Not carried over: ${source.notCopied.join(\"; \")}`] : [];\n await stageOp({ kind: \"leadForm.create\", accountId, payload }, [\n ...notCopied,\n `Point the ads at the copy, then \\`baker ads meta lead-forms archive ${String(args.id)}\\` to retire the original.`,\n ]);\n },\n});\n\n// --- Archive / unarchive ---\n\nfunction statusCommand(name: \"archive\" | \"unarchive\", status: \"ARCHIVED\" | \"ACTIVE\", description: string) {\n registerSchema({\n command: `ads.meta.lead-forms.${name}`,\n description,\n args: {\n id: { type: \"positional\", description: \"Instant Form id\", required: true },\n \"account-id\": { type: \"string\", description: \"Ad account the change is staged under\", required: true },\n },\n });\n return defineCommand({\n meta: {\n name,\n description: `${description} ${STAGED_NOTE}\nExample: baker ads meta lead-forms ${name} 1460955752594831 --account-id act_123`,\n },\n args: {\n id: { type: \"positional\", description: \"Instant Form id\", required: true },\n \"account-id\": { type: \"string\", description: \"Numeric account ID or act_N\" },\n },\n run: async ({ args }) => {\n await stageOp({\n kind: \"leadForm.update\",\n accountId: await bareAccountId(args as Args),\n target: requireTarget(args as Args, \"Instant Form\"),\n payload: { status },\n });\n },\n });\n}\n\nexport const leadFormsArchiveCommand = statusCommand(\n \"archive\",\n \"ARCHIVED\",\n \"Archive an Instant Form: it leaves the Page's form library and no ad can use it. Meta never deletes a form; this is how one is retired.\",\n);\n\nexport const leadFormsUnarchiveCommand = statusCommand(\n \"unarchive\",\n \"ACTIVE\",\n \"Bring an archived Instant Form back so ads can use it again.\",\n);\n"],"mappings":"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","names":["defineCommand","defineCommand"]}
@@ -8,11 +8,11 @@ var metaMediaCommand = defineCommand({
8
8
  description: "Stage Baker library media uploads to Meta so creatives can reference them (upload), or take an image out of the account's image library (delete --image-hash). Upload publishes an ad-image hash or video id a creative binds via --image-ref/--video-ref."
9
9
  },
10
10
  subCommands: {
11
- upload: () => import("./write-commands-R5SN6CVQ.js").then((m) => m.mediaUploadCommand),
12
- delete: () => import("./asset-writes-PQNENVYY.js").then((m) => m.mediaDeleteCommand)
11
+ upload: () => import("./write-commands-CY734U34.js").then((m) => m.mediaUploadCommand),
12
+ delete: () => import("./asset-writes-SSI3CO2A.js").then((m) => m.mediaDeleteCommand)
13
13
  }
14
14
  });
15
15
  export {
16
16
  metaMediaCommand
17
17
  };
18
- //# sourceMappingURL=media-23IW442U.js.map
18
+ //# sourceMappingURL=media-HDHZULQ7.js.map