@koda-sl/baker-cli 0.378.0 → 0.379.0-dev.600795410

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.
@@ -1,10 +1,13 @@
1
1
  import {
2
+ cacheGet,
3
+ cacheSet,
2
4
  handleConnectionError,
3
5
  needsConnectionFix,
4
6
  writeAdsJson
5
7
  } from "./chunk-XHC4LMK4.js";
6
8
  import {
7
- ApiError
9
+ ApiError,
10
+ apiGet
8
11
  } from "./chunk-MF7YA62W.js";
9
12
  import {
10
13
  getEnv
@@ -46,30 +49,80 @@ function handleMetaError(err) {
46
49
  process.exit(1);
47
50
  }
48
51
  var ACCOUNT_ID_RE = /^(act_)?[0-9]+$/;
49
- function resolveAccountIdArg(args) {
52
+ var META_ACCOUNTS_CACHE = { category: "meta-accounts", key: "accounts:scoped", ttlMs: 60 * 60 * 1e3 };
53
+ function withActPrefix(id) {
54
+ return id.startsWith("act_") ? id : `act_${id}`;
55
+ }
56
+ async function fetchConnectedAccounts(useCache) {
57
+ if (useCache) {
58
+ const cached = cacheGet(META_ACCOUNTS_CACHE.category, META_ACCOUNTS_CACHE.key);
59
+ if (cached) return cached.data;
60
+ }
61
+ const data = await apiGet("/api/ads/meta/accounts", useCache ? {} : { "skip-cache": "true" });
62
+ if (useCache) {
63
+ cacheSet(META_ACCOUNTS_CACHE.category, META_ACCOUNTS_CACHE.key, data, META_ACCOUNTS_CACHE.ttlMs);
64
+ }
65
+ return data;
66
+ }
67
+ function connectedAccountId(account) {
68
+ const id = account.externalId ?? account.id ?? account.account_id;
69
+ return id && ACCOUNT_ID_RE.test(id) ? withActPrefix(id) : void 0;
70
+ }
71
+ async function resolveAccountIdArg(args) {
50
72
  const fromArgs = args["account-id"];
51
73
  const id = fromArgs || getEnv().BAKER_META_AD_ACCOUNT_ID;
52
- if (!id) {
74
+ if (id) {
75
+ if (!ACCOUNT_ID_RE.test(id)) {
76
+ writeAdsJson({
77
+ ok: false,
78
+ error: {
79
+ code: "INVALID_ACCOUNT_ID",
80
+ message: `Invalid account ID "${id}". Expected numeric ID, optionally prefixed with "act_".`
81
+ }
82
+ });
83
+ process.exit(1);
84
+ }
85
+ return withActPrefix(id);
86
+ }
87
+ let accounts;
88
+ try {
89
+ accounts = await fetchConnectedAccounts(!args["no-cache"]);
90
+ } catch (err) {
91
+ if (err instanceof ApiError && needsConnectionFix(err.code, err.message)) {
92
+ handleConnectionError("meta_ads", err.message);
93
+ }
53
94
  writeAdsJson({
54
95
  ok: false,
55
96
  error: {
56
97
  code: "MISSING_ACCOUNT_ID",
57
- message: "Pass --account-id or set BAKER_META_AD_ACCOUNT_ID. Run `baker ads meta accounts` to find IDs."
98
+ message: "Could not look up this company's Meta ad accounts. Pass --account-id or set BAKER_META_AD_ACCOUNT_ID. Run `baker ads meta accounts` to find IDs."
58
99
  }
59
100
  });
60
101
  process.exit(1);
61
102
  }
62
- if (!ACCOUNT_ID_RE.test(id)) {
63
- writeAdsJson({
64
- ok: false,
65
- error: {
66
- code: "INVALID_ACCOUNT_ID",
67
- message: `Invalid account ID "${id}". Expected numeric ID, optionally prefixed with "act_".`
68
- }
69
- });
70
- process.exit(1);
103
+ const connected = accounts.flatMap((account) => {
104
+ const accountId = connectedAccountId(account);
105
+ return accountId ? [{ id: accountId, name: account.name }] : [];
106
+ });
107
+ const [single] = connected;
108
+ if (connected.length === 1 && single) {
109
+ process.stderr.write(`Using Meta ad account ${single.name ? `"${single.name}" ` : ""}(${single.id})
110
+ `);
111
+ return single.id;
71
112
  }
72
- return id.startsWith("act_") ? id : `act_${id}`;
113
+ if (connected.length === 0) {
114
+ handleConnectionError("meta_ads");
115
+ }
116
+ const list = connected.map((account) => ` ${account.id}${account.name ? ` ${account.name}` : ""}`).join("\n");
117
+ writeAdsJson({
118
+ ok: false,
119
+ error: {
120
+ code: "MULTIPLE_ACCOUNTS",
121
+ message: `This company connected ${connected.length} Meta ad accounts. Pass --account-id or set BAKER_META_AD_ACCOUNT_ID:
122
+ ${list}`
123
+ }
124
+ });
125
+ process.exit(1);
73
126
  }
74
127
  function getDateRange(args) {
75
128
  const datePreset = args["date-preset"];
@@ -116,6 +169,7 @@ function statusFilterHints(rows, effectiveStatus, noun) {
116
169
  export {
117
170
  metaForbiddenFix,
118
171
  handleMetaError,
172
+ META_ACCOUNTS_CACHE,
119
173
  resolveAccountIdArg,
120
174
  getDateRange,
121
175
  todayIso,
@@ -125,4 +179,4 @@ export {
125
179
  resolveEffectiveStatus,
126
180
  statusFilterHints
127
181
  };
128
- //# sourceMappingURL=chunk-VLUGGVJL.js.map
182
+ //# sourceMappingURL=chunk-MK4AIHRF.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/ads/meta/shared.ts"],"sourcesContent":["import { ApiError, apiGet } from \"../../../client.ts\";\nimport { getEnv } from \"../../../env.ts\";\nimport { handleConnectionError, needsConnectionFix } from \"../../../error-handler.ts\";\nimport { cacheGet, cacheSet } from \"../cache.ts\";\nimport { writeAdsJson } from \"../output.ts\";\nimport type { AdsErrorEnvelope, QueryFix } from \"../types.ts\";\n\nconst DAY_MS = 86_400_000;\n\n/**\n * Meta answers a permission refusal (Graph codes 200/10/294) with a bare\n * \"Permissions error\" that covers two causes needing opposite remedies: a\n * consent grant the connection never got — nothing validates Meta's granted\n * scopes after connect, unlike HubSpot's `requireCapability` — or a role the\n * user lacks on one asset, since a Pixel/Dataset, Page or catalog lives in a\n * Business and is assigned separately from the ad account. Only the blast radius\n * separates them, so hand the agent that test rather than a verdict: guessing\n * \"reconnect\" wastes the user's time on an unassigned asset, and ruling it out\n * strands a connection that really is short a scope.\n */\nexport function metaForbiddenFix(message: string): QueryFix {\n return {\n action: \"reject\",\n explanation: [\n message,\n \"This is a permission refusal, not a dead connection, and Meta returns the same code for two causes with opposite remedies. Tell them apart by how much else works before you answer.\",\n \"If other reads on this account are ALSO refused (campaigns, ad sets, insights), the connection is short a permission it was never granted. Only the user can widen it, by reconnecting Meta from the dashboard.\",\n \"If those reads work and only this object is refused, the asset (pixel/dataset, Page, or catalog) belongs to a Business the connected user holds no role in. Ad-account access does not carry asset access and reconnecting cannot grant it — the client assigns that asset to the connected user or partner in Meta Business settings, under the asset's own section (Data sources → Datasets for a pixel).\",\n \"Either way do not retry the same call, and do not abort the wider job. Continue with what you can read, and state in your answer that this specific check could not run and why — never present the task as verified when this step was skipped.\",\n ].join(\"\\n\\n\"),\n };\n}\n\nexport function handleMetaError(err: unknown): never {\n if (err instanceof ApiError) {\n if (needsConnectionFix(err.code, err.message)) {\n handleConnectionError(\"meta_ads\", err.message);\n }\n const envelope: AdsErrorEnvelope = {\n ok: false,\n error: {\n code: err.code,\n message: err.message,\n fix: err.code === \"FORBIDDEN\" ? metaForbiddenFix(err.message) : { action: \"reject\", explanation: err.message },\n retryable: err.code === \"RATE_LIMITED\" || err.code === \"UNAVAILABLE\" || err.code === \"INTERNAL_ERROR\",\n },\n };\n writeAdsJson(envelope);\n process.exit(1);\n }\n const message = err instanceof Error ? err.message : \"Unexpected error\";\n writeAdsJson({ ok: false, error: { code: \"NETWORK_ERROR\", message } });\n process.exit(1);\n}\n\nconst ACCOUNT_ID_RE = /^(act_)?[0-9]+$/;\n\n/** A row of `/api/ads/meta/accounts` — the company's picked accounts, as `baker ads meta accounts` prints them. */\ninterface ConnectedMetaAccount {\n externalId?: string;\n id?: string;\n account_id?: string;\n name?: string;\n}\n\n/** Shared with `baker ads meta accounts`, so one listing serves both. */\nexport const META_ACCOUNTS_CACHE = { category: \"meta-accounts\", key: \"accounts:scoped\", ttlMs: 60 * 60 * 1000 };\n\nfunction withActPrefix(id: string): string {\n return id.startsWith(\"act_\") ? id : `act_${id}`;\n}\n\nasync function fetchConnectedAccounts(useCache: boolean): Promise<ConnectedMetaAccount[]> {\n if (useCache) {\n const cached = cacheGet<ConnectedMetaAccount[]>(META_ACCOUNTS_CACHE.category, META_ACCOUNTS_CACHE.key);\n if (cached) return cached.data;\n }\n const data = await apiGet<ConnectedMetaAccount[]>(\"/api/ads/meta/accounts\", useCache ? {} : { \"skip-cache\": \"true\" });\n if (useCache) {\n cacheSet(META_ACCOUNTS_CACHE.category, META_ACCOUNTS_CACHE.key, data, META_ACCOUNTS_CACHE.ttlMs);\n }\n return data;\n}\n\nfunction connectedAccountId(account: ConnectedMetaAccount): string | undefined {\n const id = account.externalId ?? account.id ?? account.account_id;\n return id && ACCOUNT_ID_RE.test(id) ? withActPrefix(id) : undefined;\n}\n\n/**\n * The Meta ad account a command works on: `--account-id`, else\n * `BAKER_META_AD_ACCOUNT_ID`, else the company's only connected account — the\n * default `baker ads meta accounts` promises. With several connected, nothing\n * is picked for the agent: the refusal lists them so the next call can name one.\n */\nexport async function resolveAccountIdArg(args: Record<string, unknown>): Promise<string> {\n const fromArgs = args[\"account-id\"] as string | undefined;\n const id = fromArgs || getEnv().BAKER_META_AD_ACCOUNT_ID;\n if (id) {\n if (!ACCOUNT_ID_RE.test(id)) {\n writeAdsJson({\n ok: false,\n error: {\n code: \"INVALID_ACCOUNT_ID\",\n message: `Invalid account ID \"${id}\". Expected numeric ID, optionally prefixed with \"act_\".`,\n },\n });\n process.exit(1);\n }\n return withActPrefix(id);\n }\n\n let accounts: ConnectedMetaAccount[];\n try {\n accounts = await fetchConnectedAccounts(!args[\"no-cache\"]);\n } catch (err) {\n if (err instanceof ApiError && needsConnectionFix(err.code, err.message)) {\n handleConnectionError(\"meta_ads\", err.message);\n }\n writeAdsJson({\n ok: false,\n error: {\n code: \"MISSING_ACCOUNT_ID\",\n message:\n \"Could not look up this company's Meta ad accounts. Pass --account-id or set BAKER_META_AD_ACCOUNT_ID. Run `baker ads meta accounts` to find IDs.\",\n },\n });\n process.exit(1);\n }\n\n const connected = accounts.flatMap((account) => {\n const accountId = connectedAccountId(account);\n return accountId ? [{ id: accountId, name: account.name }] : [];\n });\n const [single] = connected;\n if (connected.length === 1 && single) {\n process.stderr.write(`Using Meta ad account ${single.name ? `\"${single.name}\" ` : \"\"}(${single.id})\\n`);\n return single.id;\n }\n if (connected.length === 0) {\n handleConnectionError(\"meta_ads\");\n }\n const list = connected.map((account) => ` ${account.id}${account.name ? ` ${account.name}` : \"\"}`).join(\"\\n\");\n writeAdsJson({\n ok: false,\n error: {\n code: \"MULTIPLE_ACCOUNTS\",\n message: `This company connected ${connected.length} Meta ad accounts. Pass --account-id or set BAKER_META_AD_ACCOUNT_ID:\\n${list}`,\n },\n });\n process.exit(1);\n}\n\nexport function getDateRange(args: Record<string, unknown>): { datePreset?: string; since?: string; until?: string } {\n const datePreset = args[\"date-preset\"] as string | undefined;\n const since = args.since as string | undefined;\n const until = args.until as string | undefined;\n if (since && until) return { since, until };\n return { datePreset: datePreset ?? \"last_7d\" };\n}\n\nexport function todayIso(): string {\n return new Date().toISOString().slice(0, 10);\n}\n\nexport function daysAgoIso(days: number): string {\n return new Date(Date.now() - days * DAY_MS).toISOString().slice(0, 10);\n}\n\nexport function csvOrJson(args: Record<string, unknown>): string {\n return (args.output as string | undefined) ?? \"json\";\n}\n\n/**\n * Matched by Meta itself, so finding one entity by name reads only the rows that\n * match instead of the whole account.\n */\nexport const NAME_CONTAINS_ARG = {\n type: \"string\",\n description:\n \"Only rows whose name contains this text, matched by Meta before anything is returned. Start here to find one entity by name: --name-contains octubre --all-statuses\",\n} as const;\n\n/**\n * Default to ACTIVE-only on listings — that's almost always what an AI agent or\n * a human glance wants. Pass `--effective-status ACTIVE,PAUSED` to override, or\n * `--all-statuses` to drop the filter entirely.\n */\nexport function resolveEffectiveStatus(args: Record<string, unknown>): string | undefined {\n if (args[\"all-statuses\"]) {\n return undefined;\n }\n const explicit = args[\"effective-status\"] as string | undefined;\n if (explicit) {\n return explicit;\n }\n return \"ACTIVE\";\n}\n\n/**\n * An empty listing under a status filter reads exactly like \"nothing exists\":\n * a paused campaign's ad sets are CAMPAIGN_PAUSED, so the ACTIVE default hides\n * every one of them. Say which filter produced the empty list and how to widen it.\n */\nexport function statusFilterHints(\n rows: ReadonlyArray<unknown>,\n effectiveStatus: string | undefined,\n noun: string,\n): string[] {\n if (rows.length > 0 || effectiveStatus === undefined) return [];\n if (effectiveStatus === \"ACTIVE\") {\n return [\n `No ACTIVE ${noun}. The ACTIVE filter hides paused, archived and in-review ones (under a paused campaign or ad set that is all of them), so this does NOT mean none exist. Re-run with --all-statuses before concluding there are none.`,\n ];\n }\n return [\n `No ${noun} with effective_status ${effectiveStatus}. Re-run with --all-statuses to see every status before concluding there are none.`,\n ];\n}\n"],"mappings":"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","names":[]}
package/dist/cli.js CHANGED
@@ -70,8 +70,9 @@ import {
70
70
  validateCanvasDeep,
71
71
  withoutCastName,
72
72
  ytDlpBlockSignal
73
- } from "./chunk-FGHMWBUD.js";
73
+ } from "./chunk-CHJOTJTZ.js";
74
74
  import {
75
+ META_ACCOUNTS_CACHE,
75
76
  NAME_CONTAINS_ARG,
76
77
  csvOrJson,
77
78
  daysAgoIso,
@@ -81,7 +82,7 @@ import {
81
82
  resolveEffectiveStatus,
82
83
  statusFilterHints,
83
84
  todayIso
84
- } from "./chunk-VLUGGVJL.js";
85
+ } from "./chunk-MK4AIHRF.js";
85
86
  import {
86
87
  buildQueryCacheKey,
87
88
  cacheGet,
@@ -24295,11 +24296,14 @@ Examples:
24295
24296
  BAKER_META_AD_ACCOUNT_ID=act_123 baker ads meta account`
24296
24297
  },
24297
24298
  args: {
24298
- "account-id": { type: "string", description: "Meta ad account ID (act_...)" },
24299
+ "account-id": {
24300
+ type: "string",
24301
+ description: "Meta ad account ID (act_...); defaults to the only connected account"
24302
+ },
24299
24303
  "no-cache": NO_CACHE_ARG
24300
24304
  },
24301
24305
  run: async ({ args: args5 }) => {
24302
- const accountId = resolveAccountIdArg(args5);
24306
+ const accountId = await resolveAccountIdArg(args5);
24303
24307
  try {
24304
24308
  const params = { "account-id": accountId };
24305
24309
  if (args5["no-cache"]) params["skip-cache"] = "true";
@@ -24345,9 +24349,9 @@ Examples:
24345
24349
  run: async ({ args: args5 }) => {
24346
24350
  const includeAll = args5["include-all"] === true;
24347
24351
  const useCache = !args5["no-cache"];
24348
- const cacheKey = `accounts:${includeAll ? "all" : "scoped"}`;
24352
+ const cacheKey = includeAll ? "accounts:all" : META_ACCOUNTS_CACHE.key;
24349
24353
  if (useCache) {
24350
- const cached = cacheGet("meta-accounts", cacheKey);
24354
+ const cached = cacheGet(META_ACCOUNTS_CACHE.category, cacheKey);
24351
24355
  if (cached) {
24352
24356
  writeJsonEnvelope({
24353
24357
  ok: true,
@@ -24364,7 +24368,7 @@ Examples:
24364
24368
  if (!useCache) params["skip-cache"] = "true";
24365
24369
  const data = await apiGet("/api/ads/meta/accounts", params);
24366
24370
  if (useCache) {
24367
- cacheSet("meta-accounts", cacheKey, data, 60 * 60 * 1e3);
24371
+ cacheSet(META_ACCOUNTS_CACHE.category, cacheKey, data, META_ACCOUNTS_CACHE.ttlMs);
24368
24372
  }
24369
24373
  const fmt = csvOrJson(args5);
24370
24374
  if (fmt !== "json") {
@@ -24537,7 +24541,7 @@ function registerMetaListSchema(kind, description, args5) {
24537
24541
 
24538
24542
  // src/commands/ads/meta/activities.ts
24539
24543
  var listArgs4 = {
24540
- "account-id": { type: "string", description: "Meta ad account ID" },
24544
+ "account-id": { type: "string", description: "Meta ad account ID (act_...); defaults to the only connected account" },
24541
24545
  days: { type: "string", description: "Lookback days (default 7)" },
24542
24546
  limit: { type: "string", description: "Max rows" },
24543
24547
  "no-cache": NO_CACHE_ARG,
@@ -24566,7 +24570,7 @@ Examples:
24566
24570
  },
24567
24571
  args: listArgs4,
24568
24572
  run: async ({ args: args5 }) => {
24569
- const accountId = resolveAccountIdArg(args5);
24573
+ const accountId = await resolveAccountIdArg(args5);
24570
24574
  try {
24571
24575
  const params = { "account-id": accountId };
24572
24576
  if (args5.days) params.days = args5.days;
@@ -26239,8 +26243,8 @@ import { defineCommand as defineCommand57 } from "citty";
26239
26243
 
26240
26244
  // src/commands/ads/meta/write-shared.ts
26241
26245
  import { readFileSync as readFileSync9 } from "fs";
26242
- function bareAccountId2(args5) {
26243
- return resolveAccountIdArg(args5).replace(/^act_/, "");
26246
+ async function bareAccountId2(args5) {
26247
+ return (await resolveAccountIdArg(args5)).replace(/^act_/, "");
26244
26248
  }
26245
26249
  function failWriteValidation3(message) {
26246
26250
  writeJsonEnvelope({ ok: false, error: { code: "VALIDATION_ERROR", message } });
@@ -26366,7 +26370,7 @@ async function stageStatusChange2(input) {
26366
26370
  // src/commands/ads/meta/write-commands.ts
26367
26371
  var STAGED_NOTE2 = "Staged until publish \u2014 review with `baker ads meta draft`.";
26368
26372
  var accountArgs2 = {
26369
- "account-id": { type: "string", description: "Numeric account ID or act_N" }
26373
+ "account-id": { type: "string", description: "Numeric account ID or act_N; defaults to the only connected account" }
26370
26374
  };
26371
26375
  function campaignPayloadFromFlags2(args5) {
26372
26376
  return {
@@ -26422,7 +26426,7 @@ Example: baker ads meta campaigns create --name "Q3 Prospecting" --objective OUT
26422
26426
  file: { type: "string", description: "JSON file with the full payload; flags override file keys" }
26423
26427
  },
26424
26428
  run: async ({ args: args5 }) => {
26425
- const accountId = bareAccountId2(args5);
26429
+ const accountId = await bareAccountId2(args5);
26426
26430
  const payload = mergePayload3(loadJsonFileArg3(args5.file), campaignPayloadFromFlags2(args5));
26427
26431
  await stageOp2({ kind: "campaign.create", accountId, payload });
26428
26432
  }
@@ -26442,7 +26446,7 @@ Pass a meta_temp_* ref to amend a campaign staged in this chat. Example: baker a
26442
26446
  file: { type: "string", description: "JSON file with fields to change" }
26443
26447
  },
26444
26448
  run: async ({ args: args5 }) => {
26445
- const accountId = bareAccountId2(args5);
26449
+ const accountId = await bareAccountId2(args5);
26446
26450
  const payload = mergePayload3(loadJsonFileArg3(args5.file), {
26447
26451
  name: args5.name,
26448
26452
  status: upper(args5.status),
@@ -26535,7 +26539,7 @@ Example: baker ads meta custom-conversions create --name "Lead formulario" --pix
26535
26539
  value: { type: "string", description: "Default value of one conversion, when the site sends no value" }
26536
26540
  },
26537
26541
  run: async ({ args: args5 }) => {
26538
- const accountId = bareAccountId2(args5);
26542
+ const accountId = await bareAccountId2(args5);
26539
26543
  await stageOp2({
26540
26544
  kind: "customConversion.create",
26541
26545
  accountId,
@@ -26592,7 +26596,7 @@ Example: baker ads meta adsets create --campaign meta_temp_ab12 --name "US 25-54
26592
26596
  },
26593
26597
  args: { campaign: { type: "string", description: "Parent campaign id or meta_temp_* ref" }, ...adSetWriteArgs },
26594
26598
  run: async ({ args: args5 }) => {
26595
- const accountId = bareAccountId2(args5);
26599
+ const accountId = await bareAccountId2(args5);
26596
26600
  const payload = mergePayload3(loadJsonFileArg3(args5.file), adSetPayloadFromFlags(args5));
26597
26601
  await stageOp2({ kind: "adSet.create", accountId, payload });
26598
26602
  }
@@ -26606,7 +26610,7 @@ Example: baker ads meta adsets update 6123 --daily-budget 80`
26606
26610
  },
26607
26611
  args: { id: { type: "positional", description: "Ad set id or meta_temp_* ref", required: true }, ...adSetWriteArgs },
26608
26612
  run: async ({ args: args5 }) => {
26609
- const accountId = bareAccountId2(args5);
26613
+ const accountId = await bareAccountId2(args5);
26610
26614
  const {
26611
26615
  campaign_id: _drop,
26612
26616
  billing_event: _be,
@@ -26708,7 +26712,7 @@ Example: baker ads meta creatives create --page 555 --message "Save now" --link
26708
26712
  file: { type: "string", description: "JSON file with the full creative payload (carousel cards / dynamic assets)" }
26709
26713
  },
26710
26714
  run: async ({ args: args5 }) => {
26711
- const accountId = bareAccountId2(args5);
26715
+ const accountId = await bareAccountId2(args5);
26712
26716
  const payload = mergePayload3(loadJsonFileArg3(args5.file), creativePayloadFromFlags(args5));
26713
26717
  if (payload.creativeType === void 0) {
26714
26718
  payload.creativeType = "single";
@@ -26739,7 +26743,7 @@ Amending a staged (meta_temp_*) creative merges fields into the create. Example:
26739
26743
  }
26740
26744
  },
26741
26745
  run: async ({ args: args5 }) => {
26742
- const accountId = bareAccountId2(args5);
26746
+ const accountId = await bareAccountId2(args5);
26743
26747
  const file = loadJsonFileArg3(args5.file);
26744
26748
  const payload = mergePayload3(file, {
26745
26749
  name: args5.name,
@@ -26773,7 +26777,7 @@ Example: baker ads meta ads create --name "Ad 1" --adset meta_temp_as01 --creati
26773
26777
  file: { type: "string", description: "JSON file with the full payload" }
26774
26778
  },
26775
26779
  run: async ({ args: args5 }) => {
26776
- const accountId = bareAccountId2(args5);
26780
+ const accountId = await bareAccountId2(args5);
26777
26781
  const tracking = args5["tracking-file"] ? loadJsonFileArg3(args5["tracking-file"]).tracking_specs : void 0;
26778
26782
  const payload = mergePayload3(loadJsonFileArg3(args5.file), {
26779
26783
  name: args5.name,
@@ -26800,7 +26804,7 @@ Example: baker ads meta ads update 6123 --creative meta_temp_cr02`
26800
26804
  file: { type: "string", description: "JSON file with fields to change" }
26801
26805
  },
26802
26806
  run: async ({ args: args5 }) => {
26803
- const accountId = bareAccountId2(args5);
26807
+ const accountId = await bareAccountId2(args5);
26804
26808
  const payload = mergePayload3(loadJsonFileArg3(args5.file), {
26805
26809
  name: args5.name,
26806
26810
  status: upper(args5.status),
@@ -26836,9 +26840,9 @@ Example: baker ads meta ads retag 120252158038400247 --url-tags "utm_source={{si
26836
26840
  const { apiPost: apiPost2 } = await import("./client-GR6YL3YX.js");
26837
26841
  const { requireChatId: requireChatId2 } = await import("./env-4P5HOGNA.js");
26838
26842
  const { writeJsonEnvelope: writeJsonEnvelope2 } = await import("./output-NMQXZF3Z.js");
26839
- const { handleMetaError: handleMetaError2 } = await import("./shared-J7DQZ2ED.js");
26843
+ const { handleMetaError: handleMetaError2 } = await import("./shared-DDBXEUL4.js");
26840
26844
  try {
26841
- const accountId = bareAccountId2(args5);
26845
+ const accountId = await bareAccountId2(args5);
26842
26846
  const urlTags = typeof args5["url-tags"] === "string" ? args5["url-tags"] : void 0;
26843
26847
  const setRaw = typeof args5.set === "string" ? args5.set : void 0;
26844
26848
  if (urlTags === void 0 === (setRaw === void 0)) {
@@ -26892,7 +26896,7 @@ Example: baker ads meta audiences create --name "Site visitors 30d" --subtype WE
26892
26896
  file: { type: "string", description: "JSON file with lookalike_spec / rule / full payload" }
26893
26897
  },
26894
26898
  run: async ({ args: args5 }) => {
26895
- const accountId = bareAccountId2(args5);
26899
+ const accountId = await bareAccountId2(args5);
26896
26900
  const payload = mergePayload3(loadJsonFileArg3(args5.file), {
26897
26901
  name: args5.name,
26898
26902
  subtype: upper(args5.subtype),
@@ -26915,7 +26919,7 @@ var audiencesUpdateCommand = defineCommand57({
26915
26919
  file: { type: "string", description: "JSON file with fields to change" }
26916
26920
  },
26917
26921
  run: async ({ args: args5 }) => {
26918
- const accountId = bareAccountId2(args5);
26922
+ const accountId = await bareAccountId2(args5);
26919
26923
  const payload = mergePayload3(loadJsonFileArg3(args5.file), { name: args5.name, description: args5.description });
26920
26924
  await stageOp2({
26921
26925
  kind: "customAudience.update",
@@ -26939,7 +26943,7 @@ Example: baker ads meta media upload --kind image --image-id <bakerImageId>`
26939
26943
  name: { type: "string", description: "Display name / filename hint" }
26940
26944
  },
26941
26945
  run: async ({ args: args5 }) => {
26942
- const accountId = bareAccountId2(args5);
26946
+ const accountId = await bareAccountId2(args5);
26943
26947
  const kind = String(args5.kind ?? "").toLowerCase();
26944
26948
  if (kind !== "image" && kind !== "video") {
26945
26949
  failWriteValidation3("--kind must be image or video");
@@ -26957,7 +26961,7 @@ function statusCommand3(name, kind, label) {
26957
26961
  meta: { name, description: `Stage ${name} for a ${label}. ${STAGED_NOTE2}` },
26958
26962
  args: { id: { type: "positional", description: `${label} id`, required: true }, ...accountArgs2 },
26959
26963
  run: async ({ args: args5 }) => {
26960
- const accountId = bareAccountId2(args5);
26964
+ const accountId = await bareAccountId2(args5);
26961
26965
  await stageStatusChange2({
26962
26966
  kind,
26963
26967
  accountId,
@@ -26984,9 +26988,9 @@ function duplicateCommand2(entity, label) {
26984
26988
  const { apiPost: apiPost2 } = await import("./client-GR6YL3YX.js");
26985
26989
  const { requireChatId: requireChatId2 } = await import("./env-4P5HOGNA.js");
26986
26990
  const { writeJsonEnvelope: writeJsonEnvelope2 } = await import("./output-NMQXZF3Z.js");
26987
- const { handleMetaError: handleMetaError2 } = await import("./shared-J7DQZ2ED.js");
26991
+ const { handleMetaError: handleMetaError2 } = await import("./shared-DDBXEUL4.js");
26988
26992
  try {
26989
- const accountId = bareAccountId2(args5);
26993
+ const accountId = await bareAccountId2(args5);
26990
26994
  const chatId = requireChatId2();
26991
26995
  const response = await apiPost2("/api/ads/meta/draft/duplicate", {
26992
26996
  chatId,
@@ -27018,7 +27022,7 @@ var adsDuplicateCommand = duplicateCommand2("ad", "ad");
27018
27022
 
27019
27023
  // src/commands/ads/meta/ads.ts
27020
27024
  var listArgs5 = {
27021
- "account-id": { type: "string", description: "Meta ad account ID" },
27025
+ "account-id": { type: "string", description: "Meta ad account ID (act_...); defaults to the only connected account" },
27022
27026
  "adset-id": { type: "string", description: "Filter to one adset" },
27023
27027
  "campaign-id": { type: "string", description: "Filter to one campaign" },
27024
27028
  "effective-status": { type: "string", description: "Comma-separated effective_status filter (overrides default)" },
@@ -27040,7 +27044,7 @@ var listCommand4 = defineCommand58({
27040
27044
  },
27041
27045
  args: listArgs5,
27042
27046
  run: async ({ args: args5 }) => {
27043
- const accountId = resolveAccountIdArg(args5);
27047
+ const accountId = await resolveAccountIdArg(args5);
27044
27048
  const effectiveStatus = resolveEffectiveStatus(args5);
27045
27049
  try {
27046
27050
  const params = { "account-id": accountId };
@@ -27100,7 +27104,7 @@ Examples:
27100
27104
  // src/commands/ads/meta/adsets.ts
27101
27105
  import { defineCommand as defineCommand59 } from "citty";
27102
27106
  var listArgs6 = {
27103
- "account-id": { type: "string", description: "Meta ad account ID" },
27107
+ "account-id": { type: "string", description: "Meta ad account ID (act_...); defaults to the only connected account" },
27104
27108
  "campaign-id": { type: "string", description: "Filter to one campaign" },
27105
27109
  "effective-status": { type: "string", description: "Comma-separated effective_status filter (overrides default)" },
27106
27110
  "all-statuses": { type: "boolean", description: "Drop the default ACTIVE filter" },
@@ -27121,7 +27125,7 @@ var listCommand5 = defineCommand59({
27121
27125
  },
27122
27126
  args: listArgs6,
27123
27127
  run: async ({ args: args5 }) => {
27124
- const accountId = resolveAccountIdArg(args5);
27128
+ const accountId = await resolveAccountIdArg(args5);
27125
27129
  const effectiveStatus = resolveEffectiveStatus(args5);
27126
27130
  try {
27127
27131
  const params = { "account-id": accountId };
@@ -27175,7 +27179,7 @@ Examples:
27175
27179
  // src/commands/ads/meta/audiences.ts
27176
27180
  import { defineCommand as defineCommand60 } from "citty";
27177
27181
  var listArgs7 = {
27178
- "account-id": { type: "string", description: "Meta ad account ID" },
27182
+ "account-id": { type: "string", description: "Meta ad account ID (act_...); defaults to the only connected account" },
27179
27183
  limit: { type: "string", description: "Max rows" },
27180
27184
  "no-cache": NO_CACHE_ARG,
27181
27185
  output: { type: "string", description: "Output format", default: "json" }
@@ -27187,7 +27191,7 @@ var listCommand6 = defineCommand60({
27187
27191
  },
27188
27192
  args: listArgs7,
27189
27193
  run: async ({ args: args5 }) => {
27190
- const accountId = resolveAccountIdArg(args5);
27194
+ const accountId = await resolveAccountIdArg(args5);
27191
27195
  try {
27192
27196
  const params = { "account-id": accountId };
27193
27197
  if (args5.limit) params.limit = args5.limit;
@@ -27261,7 +27265,7 @@ Examples:
27261
27265
  // src/commands/ads/meta/campaigns.ts
27262
27266
  import { defineCommand as defineCommand62 } from "citty";
27263
27267
  var listArgs8 = {
27264
- "account-id": { type: "string", description: "Meta ad account ID" },
27268
+ "account-id": { type: "string", description: "Meta ad account ID (act_...); defaults to the only connected account" },
27265
27269
  "effective-status": { type: "string", description: "Comma-separated effective_status filter (overrides default)" },
27266
27270
  "all-statuses": { type: "boolean", description: "Drop the default ACTIVE filter and return everything" },
27267
27271
  "name-contains": NAME_CONTAINS_ARG,
@@ -27281,7 +27285,7 @@ var listCommand7 = defineCommand62({
27281
27285
  },
27282
27286
  args: listArgs8,
27283
27287
  run: async ({ args: args5 }) => {
27284
- const accountId = resolveAccountIdArg(args5);
27288
+ const accountId = await resolveAccountIdArg(args5);
27285
27289
  const effectiveStatus = resolveEffectiveStatus(args5);
27286
27290
  try {
27287
27291
  const params = { "account-id": accountId };
@@ -27340,7 +27344,10 @@ Examples:
27340
27344
  // src/commands/ads/meta/creatives.ts
27341
27345
  import { defineCommand as defineCommand63 } from "citty";
27342
27346
  var listArgs9 = {
27343
- "account-id": { type: "string", description: "Meta ad account ID (required if no --creative-id)" },
27347
+ "account-id": {
27348
+ type: "string",
27349
+ description: "Meta ad account ID (act_...) when listing without --creative-id; defaults to the only connected account"
27350
+ },
27344
27351
  "creative-id": { type: "string", description: "Single creative ID" },
27345
27352
  limit: { type: "string", description: "Max rows (default 500)" },
27346
27353
  "no-cache": NO_CACHE_ARG,
@@ -27358,7 +27365,7 @@ var listCommand8 = defineCommand63({
27358
27365
  if (args5["creative-id"]) {
27359
27366
  params["creative-id"] = args5["creative-id"];
27360
27367
  } else {
27361
- const accountId = resolveAccountIdArg(args5);
27368
+ const accountId = await resolveAccountIdArg(args5);
27362
27369
  params["account-id"] = accountId;
27363
27370
  }
27364
27371
  if (args5.limit) params.limit = args5.limit;
@@ -27438,7 +27445,11 @@ registerSchema({
27438
27445
  command: "ads.meta.custom-conversions",
27439
27446
  description: "List the Custom Conversions defined on a Meta ad account \u2014 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>`.",
27440
27447
  args: {
27441
- "account-id": { type: "string", description: "Meta ad account ID (act_123)", required: false },
27448
+ "account-id": {
27449
+ type: "string",
27450
+ description: "Meta ad account ID (act_...); defaults to the only connected account",
27451
+ required: false
27452
+ },
27442
27453
  full: { type: "boolean", description: "Include the raw rule JSON, retention and creation times", required: false },
27443
27454
  limit: { type: "string", description: "Max rows", required: false },
27444
27455
  "no-cache": { type: "boolean", description: "Bypass the cache", required: false },
@@ -27446,7 +27457,7 @@ registerSchema({
27446
27457
  }
27447
27458
  });
27448
27459
  var listArgs10 = {
27449
- "account-id": { type: "string", description: "Meta ad account ID" },
27460
+ "account-id": { type: "string", description: "Meta ad account ID (act_...); defaults to the only connected account" },
27450
27461
  full: { type: "boolean", description: "Include the raw rule JSON, retention and creation times" },
27451
27462
  limit: { type: "string", description: "Max rows" },
27452
27463
  "no-cache": NO_CACHE_ARG,
@@ -27459,7 +27470,7 @@ var listCommand9 = defineCommand64({
27459
27470
  },
27460
27471
  args: listArgs10,
27461
27472
  run: async ({ args: args5 }) => {
27462
- const accountId = resolveAccountIdArg(args5);
27473
+ const accountId = await resolveAccountIdArg(args5);
27463
27474
  try {
27464
27475
  const params = { "account-id": accountId };
27465
27476
  if (args5.limit) params.limit = args5.limit;
@@ -28336,7 +28347,10 @@ Examples:
28336
28347
  baker ads meta pixels --account-id act_123 --pixel-id 9876 --stats --days 7`
28337
28348
  },
28338
28349
  args: {
28339
- "account-id": { type: "string", description: "Meta ad account ID" },
28350
+ "account-id": {
28351
+ type: "string",
28352
+ description: "Meta ad account ID (act_...); defaults to the only connected account"
28353
+ },
28340
28354
  "pixel-id": { type: "string", description: "Pixel ID for --stats" },
28341
28355
  stats: { type: "boolean", description: "Fetch firing stats for the pixel" },
28342
28356
  aggregation: {
@@ -28380,7 +28394,7 @@ async function runStats(args5) {
28380
28394
  emit(data, args5);
28381
28395
  }
28382
28396
  async function runList(args5) {
28383
- const accountId = resolveAccountIdArg(args5);
28397
+ const accountId = await resolveAccountIdArg(args5);
28384
28398
  const params = { "account-id": accountId };
28385
28399
  if (args5["no-cache"]) params["skip-cache"] = "true";
28386
28400
  const { pixels, restrictedFields } = await apiGet("/api/ads/meta/pixels", params);