@koda-sl/baker-cli 0.364.0-dev.addc883a2 → 0.364.0-dev.c9e3bcde2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- package/dist/{chunk-UWCUB3DJ.js → chunk-BLMXG42J.js} +1 -6
- package/dist/{chunk-UWCUB3DJ.js.map → chunk-BLMXG42J.js.map} +1 -1
- package/dist/cli.js +122 -27
- package/dist/cli.js.map +1 -1
- package/dist/{shared-HOMSTQMP.js → shared-BROA3UXS.js} +2 -4
- package/package.json +1 -1
- /package/dist/{shared-HOMSTQMP.js.map → shared-BROA3UXS.js.map} +0 -0
package/README.md
CHANGED
|
@@ -943,7 +943,8 @@ baker ads meta creatives create --account-id act_123 --page 555 --instagram-us
|
|
|
943
943
|
baker ads meta ads create --account-id act_123 --name "Ad 1" --adset meta_temp_as --creative meta_temp_cr
|
|
944
944
|
|
|
945
945
|
# review + edit before publish
|
|
946
|
-
baker ads meta draft
|
|
946
|
+
baker ads meta draft # one line per op: ref · kind · summary · after <refs>, then advisories
|
|
947
|
+
baker ads meta draft --json # raw JSON envelope for scripting
|
|
947
948
|
baker ads meta draft show meta_temp_cr # full staged payload — the receipt before publish
|
|
948
949
|
baker ads meta draft remove meta_temp_cr # removing a create cascades to dependents
|
|
949
950
|
baker ads meta draft clear
|
|
@@ -87,10 +87,6 @@ function daysAgoIso(days) {
|
|
|
87
87
|
function csvOrJson(args) {
|
|
88
88
|
return args.output ?? "json";
|
|
89
89
|
}
|
|
90
|
-
var NAME_CONTAINS_ARG = {
|
|
91
|
-
type: "string",
|
|
92
|
-
description: "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"
|
|
93
|
-
};
|
|
94
90
|
function resolveEffectiveStatus(args) {
|
|
95
91
|
if (args["all-statuses"]) {
|
|
96
92
|
return void 0;
|
|
@@ -110,7 +106,6 @@ export {
|
|
|
110
106
|
todayIso,
|
|
111
107
|
daysAgoIso,
|
|
112
108
|
csvOrJson,
|
|
113
|
-
NAME_CONTAINS_ARG,
|
|
114
109
|
resolveEffectiveStatus
|
|
115
110
|
};
|
|
116
|
-
//# sourceMappingURL=chunk-
|
|
111
|
+
//# sourceMappingURL=chunk-BLMXG42J.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/commands/ads/meta/shared.ts"],"sourcesContent":["import { ApiError } from \"../../../client.ts\";\nimport { getEnv } from \"../../../env.ts\";\nimport { handleConnectionError, needsConnectionFix } from \"../../../error-handler.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/** Pull the default Meta ad account ID from `--account-id` arg or `BAKER_META_AD_ACCOUNT_ID` env. */\nexport function resolveAccountIdArg(args: Record<string, unknown>): string {\n const fromArgs = args[\"account-id\"] as string | undefined;\n const id = fromArgs || getEnv().BAKER_META_AD_ACCOUNT_ID;\n if (!id) {\n writeAdsJson({\n ok: false,\n error: {\n code: \"MISSING_ACCOUNT_ID\",\n message: \"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 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 id.startsWith(\"act_\") ? id : `act_${id}`;\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 *
|
|
1
|
+
{"version":3,"sources":["../src/commands/ads/meta/shared.ts"],"sourcesContent":["import { ApiError } from \"../../../client.ts\";\nimport { getEnv } from \"../../../env.ts\";\nimport { handleConnectionError, needsConnectionFix } from \"../../../error-handler.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/** Pull the default Meta ad account ID from `--account-id` arg or `BAKER_META_AD_ACCOUNT_ID` env. */\nexport function resolveAccountIdArg(args: Record<string, unknown>): string {\n const fromArgs = args[\"account-id\"] as string | undefined;\n const id = fromArgs || getEnv().BAKER_META_AD_ACCOUNT_ID;\n if (!id) {\n writeAdsJson({\n ok: false,\n error: {\n code: \"MISSING_ACCOUNT_ID\",\n message: \"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 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 id.startsWith(\"act_\") ? id : `act_${id}`;\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 * 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"],"mappings":";;;;;;;;;;;;;AAMA,IAAM,SAAS;AAaR,SAAS,iBAAiB,SAA2B;AAC1D,SAAO;AAAA,IACL,QAAQ;AAAA,IACR,aAAa;AAAA,MACX;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,EAAE,KAAK,MAAM;AAAA,EACf;AACF;AAEO,SAAS,gBAAgB,KAAqB;AACnD,MAAI,eAAe,UAAU;AAC3B,QAAI,mBAAmB,IAAI,MAAM,IAAI,OAAO,GAAG;AAC7C,4BAAsB,YAAY,IAAI,OAAO;AAAA,IAC/C;AACA,UAAM,WAA6B;AAAA,MACjC,IAAI;AAAA,MACJ,OAAO;AAAA,QACL,MAAM,IAAI;AAAA,QACV,SAAS,IAAI;AAAA,QACb,KAAK,IAAI,SAAS,cAAc,iBAAiB,IAAI,OAAO,IAAI,EAAE,QAAQ,UAAU,aAAa,IAAI,QAAQ;AAAA,QAC7G,WAAW,IAAI,SAAS,kBAAkB,IAAI,SAAS,iBAAiB,IAAI,SAAS;AAAA,MACvF;AAAA,IACF;AACA,iBAAa,QAAQ;AACrB,YAAQ,KAAK,CAAC;AAAA,EAChB;AACA,QAAM,UAAU,eAAe,QAAQ,IAAI,UAAU;AACrD,eAAa,EAAE,IAAI,OAAO,OAAO,EAAE,MAAM,iBAAiB,QAAQ,EAAE,CAAC;AACrE,UAAQ,KAAK,CAAC;AAChB;AAEA,IAAM,gBAAgB;AAGf,SAAS,oBAAoB,MAAuC;AACzE,QAAM,WAAW,KAAK,YAAY;AAClC,QAAM,KAAK,YAAY,OAAO,EAAE;AAChC,MAAI,CAAC,IAAI;AACP,iBAAa;AAAA,MACX,IAAI;AAAA,MACJ,OAAO;AAAA,QACL,MAAM;AAAA,QACN,SAAS;AAAA,MACX;AAAA,IACF,CAAC;AACD,YAAQ,KAAK,CAAC;AAAA,EAChB;AACA,MAAI,CAAC,cAAc,KAAK,EAAE,GAAG;AAC3B,iBAAa;AAAA,MACX,IAAI;AAAA,MACJ,OAAO;AAAA,QACL,MAAM;AAAA,QACN,SAAS,uBAAuB,EAAE;AAAA,MACpC;AAAA,IACF,CAAC;AACD,YAAQ,KAAK,CAAC;AAAA,EAChB;AACA,SAAO,GAAG,WAAW,MAAM,IAAI,KAAK,OAAO,EAAE;AAC/C;AAEO,SAAS,aAAa,MAAwF;AACnH,QAAM,aAAa,KAAK,aAAa;AACrC,QAAM,QAAQ,KAAK;AACnB,QAAM,QAAQ,KAAK;AACnB,MAAI,SAAS,MAAO,QAAO,EAAE,OAAO,MAAM;AAC1C,SAAO,EAAE,YAAY,cAAc,UAAU;AAC/C;AAEO,SAAS,WAAmB;AACjC,UAAO,oBAAI,KAAK,GAAE,YAAY,EAAE,MAAM,GAAG,EAAE;AAC7C;AAEO,SAAS,WAAW,MAAsB;AAC/C,SAAO,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,EAAE;AACvE;AAEO,SAAS,UAAU,MAAuC;AAC/D,SAAQ,KAAK,UAAiC;AAChD;AAOO,SAAS,uBAAuB,MAAmD;AACxF,MAAI,KAAK,cAAc,GAAG;AACxB,WAAO;AAAA,EACT;AACA,QAAM,WAAW,KAAK,kBAAkB;AACxC,MAAI,UAAU;AACZ,WAAO;AAAA,EACT;AACA,SAAO;AACT;","names":[]}
|
package/dist/cli.js
CHANGED
|
@@ -71,7 +71,6 @@ import {
|
|
|
71
71
|
ytDlpBlockSignal
|
|
72
72
|
} from "./chunk-K2IKLMCM.js";
|
|
73
73
|
import {
|
|
74
|
-
NAME_CONTAINS_ARG,
|
|
75
74
|
csvOrJson,
|
|
76
75
|
daysAgoIso,
|
|
77
76
|
getDateRange,
|
|
@@ -79,7 +78,7 @@ import {
|
|
|
79
78
|
resolveAccountIdArg,
|
|
80
79
|
resolveEffectiveStatus,
|
|
81
80
|
todayIso
|
|
82
|
-
} from "./chunk-
|
|
81
|
+
} from "./chunk-BLMXG42J.js";
|
|
83
82
|
import {
|
|
84
83
|
buildQueryCacheKey,
|
|
85
84
|
cacheGet,
|
|
@@ -9793,7 +9792,7 @@ import { defineCommand as defineCommand19 } from "citty";
|
|
|
9793
9792
|
function listed(resources) {
|
|
9794
9793
|
return resources.map((resource) => resource.label ? `${resource.id} (${resource.label})` : resource.id).join("; ");
|
|
9795
9794
|
}
|
|
9796
|
-
function connectedResourceHints({ plural, singular, flag, resources }) {
|
|
9795
|
+
function connectedResourceHints({ plural: plural2, singular, flag, resources }) {
|
|
9797
9796
|
if (resources.length === 0) {
|
|
9798
9797
|
return [];
|
|
9799
9798
|
}
|
|
@@ -9801,7 +9800,7 @@ function connectedResourceHints({ plural, singular, flag, resources }) {
|
|
|
9801
9800
|
return [`This company connected one ${singular}: ${listed(resources)}. Commands use it unless you pass ${flag}.`];
|
|
9802
9801
|
}
|
|
9803
9802
|
return [
|
|
9804
|
-
`This company connected ${resources.length} ${
|
|
9803
|
+
`This company connected ${resources.length} ${plural2}: ${listed(resources)}. Each command works on one, so pass ${flag} \u2014 nothing is picked for you. A question about the whole business covers all ${resources.length}: answering from one and reporting it as the total is wrong.`
|
|
9805
9804
|
];
|
|
9806
9805
|
}
|
|
9807
9806
|
|
|
@@ -26383,7 +26382,7 @@ Example: baker ads meta ads retag 120252158038400247 --url-tags "utm_source={{si
|
|
|
26383
26382
|
const { apiPost: apiPost2 } = await import("./client-3DGBQ2H3.js");
|
|
26384
26383
|
const { requireChatId: requireChatId2 } = await import("./env-BSG7TY5Y.js");
|
|
26385
26384
|
const { writeJsonEnvelope: writeJsonEnvelope2 } = await import("./output-M2DBCZFG.js");
|
|
26386
|
-
const { handleMetaError: handleMetaError2 } = await import("./shared-
|
|
26385
|
+
const { handleMetaError: handleMetaError2 } = await import("./shared-BROA3UXS.js");
|
|
26387
26386
|
try {
|
|
26388
26387
|
const accountId = bareAccountId2(args5);
|
|
26389
26388
|
const urlTags = typeof args5["url-tags"] === "string" ? args5["url-tags"] : void 0;
|
|
@@ -26531,7 +26530,7 @@ function duplicateCommand2(entity, label) {
|
|
|
26531
26530
|
const { apiPost: apiPost2 } = await import("./client-3DGBQ2H3.js");
|
|
26532
26531
|
const { requireChatId: requireChatId2 } = await import("./env-BSG7TY5Y.js");
|
|
26533
26532
|
const { writeJsonEnvelope: writeJsonEnvelope2 } = await import("./output-M2DBCZFG.js");
|
|
26534
|
-
const { handleMetaError: handleMetaError2 } = await import("./shared-
|
|
26533
|
+
const { handleMetaError: handleMetaError2 } = await import("./shared-BROA3UXS.js");
|
|
26535
26534
|
try {
|
|
26536
26535
|
const accountId = bareAccountId2(args5);
|
|
26537
26536
|
const chatId = requireChatId2();
|
|
@@ -26570,7 +26569,6 @@ var listArgs4 = {
|
|
|
26570
26569
|
"campaign-id": { type: "string", description: "Filter to one campaign" },
|
|
26571
26570
|
"effective-status": { type: "string", description: "Comma-separated effective_status filter (overrides default)" },
|
|
26572
26571
|
"all-statuses": { type: "boolean", description: "Drop the default ACTIVE filter" },
|
|
26573
|
-
"name-contains": NAME_CONTAINS_ARG,
|
|
26574
26572
|
limit: { type: "string", description: "Max rows (default 1000)" },
|
|
26575
26573
|
"no-cache": NO_CACHE_ARG,
|
|
26576
26574
|
output: { type: "string", description: "Output format", default: "json" }
|
|
@@ -26589,7 +26587,6 @@ var listCommand4 = defineCommand58({
|
|
|
26589
26587
|
if (args5["adset-id"]) params["adset-id"] = args5["adset-id"];
|
|
26590
26588
|
if (args5["campaign-id"]) params["campaign-id"] = args5["campaign-id"];
|
|
26591
26589
|
if (effectiveStatus) params["effective-status"] = effectiveStatus;
|
|
26592
|
-
if (args5["name-contains"]) params["name-contains"] = args5["name-contains"];
|
|
26593
26590
|
if (args5.limit) params.limit = args5.limit;
|
|
26594
26591
|
if (args5["no-cache"]) params["skip-cache"] = "true";
|
|
26595
26592
|
const data = await apiGet("/api/ads/meta/ads", params);
|
|
@@ -26615,7 +26612,6 @@ The dashboard shows effective_status (e.g. WITH_ISSUES, PENDING_REVIEW, DISAPPRO
|
|
|
26615
26612
|
Examples:
|
|
26616
26613
|
baker ads meta ads --account-id act_123
|
|
26617
26614
|
baker ads meta ads --adset-id 6123
|
|
26618
|
-
baker ads meta ads --account-id act_123 --all-statuses --name-contains octubre \u2014 find one ad by name
|
|
26619
26615
|
baker ads meta ads create --name "Ad 1" --adset meta_temp_as01 --creative meta_temp_cr01
|
|
26620
26616
|
baker ads meta ads pause 6123 --account-id act_123
|
|
26621
26617
|
baker ads meta ads retag 6123 --set "utm_medium=paid-social" \u2014 fix tracking on a LIVE ad, keeping everything else`
|
|
@@ -26645,7 +26641,6 @@ var listArgs5 = {
|
|
|
26645
26641
|
"campaign-id": { type: "string", description: "Filter to one campaign" },
|
|
26646
26642
|
"effective-status": { type: "string", description: "Comma-separated effective_status filter (overrides default)" },
|
|
26647
26643
|
"all-statuses": { type: "boolean", description: "Drop the default ACTIVE filter" },
|
|
26648
|
-
"name-contains": NAME_CONTAINS_ARG,
|
|
26649
26644
|
limit: { type: "string", description: "Max rows (default 1000)" },
|
|
26650
26645
|
"no-cache": NO_CACHE_ARG,
|
|
26651
26646
|
output: { type: "string", description: "Output format", default: "json" }
|
|
@@ -26663,7 +26658,6 @@ var listCommand5 = defineCommand59({
|
|
|
26663
26658
|
const params = { "account-id": accountId };
|
|
26664
26659
|
if (args5["campaign-id"]) params["campaign-id"] = args5["campaign-id"];
|
|
26665
26660
|
if (effectiveStatus) params["effective-status"] = effectiveStatus;
|
|
26666
|
-
if (args5["name-contains"]) params["name-contains"] = args5["name-contains"];
|
|
26667
26661
|
if (args5.limit) params.limit = args5.limit;
|
|
26668
26662
|
if (args5["no-cache"]) params["skip-cache"] = "true";
|
|
26669
26663
|
const data = await apiGet("/api/ads/meta/adsets", params);
|
|
@@ -26687,7 +26681,6 @@ Defaults to ACTIVE only. Write subcommands stage ops on this chat's draft.
|
|
|
26687
26681
|
Examples:
|
|
26688
26682
|
baker ads meta adsets --account-id act_123
|
|
26689
26683
|
baker ads meta adsets --campaign-id 6123456789
|
|
26690
|
-
baker ads meta adsets --account-id act_123 --all-statuses --name-contains retargeting \u2014 find one ad set by name
|
|
26691
26684
|
baker ads meta adsets create --campaign meta_temp_ab12 --name "US 25-54" --optimization-goal LINK_CLICKS --billing-event IMPRESSIONS --daily-budget 50 --targeting-file t.json`
|
|
26692
26685
|
},
|
|
26693
26686
|
subCommands: {
|
|
@@ -26799,7 +26792,6 @@ var listArgs7 = {
|
|
|
26799
26792
|
"account-id": { type: "string", description: "Meta ad account ID" },
|
|
26800
26793
|
"effective-status": { type: "string", description: "Comma-separated effective_status filter (overrides default)" },
|
|
26801
26794
|
"all-statuses": { type: "boolean", description: "Drop the default ACTIVE filter and return everything" },
|
|
26802
|
-
"name-contains": NAME_CONTAINS_ARG,
|
|
26803
26795
|
limit: { type: "string", description: "Max rows (default 1000)" },
|
|
26804
26796
|
"no-cache": NO_CACHE_ARG,
|
|
26805
26797
|
output: { type: "string", description: "Output format", default: "json" }
|
|
@@ -26816,7 +26808,6 @@ var listCommand7 = defineCommand62({
|
|
|
26816
26808
|
try {
|
|
26817
26809
|
const params = { "account-id": accountId };
|
|
26818
26810
|
if (effectiveStatus) params["effective-status"] = effectiveStatus;
|
|
26819
|
-
if (args5["name-contains"]) params["name-contains"] = args5["name-contains"];
|
|
26820
26811
|
if (args5.limit) params.limit = args5.limit;
|
|
26821
26812
|
if (args5["no-cache"]) params["skip-cache"] = "true";
|
|
26822
26813
|
const data = await apiGet("/api/ads/meta/campaigns", params);
|
|
@@ -26844,7 +26835,6 @@ The dashboard shows effective_status: ACTIVE, PAUSED, DELETED, ARCHIVED, IN_PROC
|
|
|
26844
26835
|
Examples:
|
|
26845
26836
|
baker ads meta campaigns --account-id act_123 # ACTIVE only
|
|
26846
26837
|
baker ads meta campaigns --account-id act_123 --all-statuses # everything
|
|
26847
|
-
baker ads meta campaigns --account-id act_123 --all-statuses --name-contains Q3 # find one by name
|
|
26848
26838
|
baker ads meta campaigns create --account-id act_123 --name "Q3" --objective OUTCOME_LEADS --daily-budget 50
|
|
26849
26839
|
baker ads meta campaigns pause 6123 --account-id act_123`
|
|
26850
26840
|
},
|
|
@@ -27029,11 +27019,110 @@ Examples:
|
|
|
27029
27019
|
|
|
27030
27020
|
// src/commands/ads/meta/draft.ts
|
|
27031
27021
|
import { defineCommand as defineCommand65 } from "citty";
|
|
27032
|
-
|
|
27022
|
+
|
|
27023
|
+
// src/commands/ads/meta/draft-status.ts
|
|
27024
|
+
var STATUS_HEADINGS = {
|
|
27025
|
+
active: "staged",
|
|
27026
|
+
publishing: "publishing",
|
|
27027
|
+
applied: "published",
|
|
27028
|
+
partial: "published with failures",
|
|
27029
|
+
discarded: "discarded",
|
|
27030
|
+
none: "none"
|
|
27031
|
+
};
|
|
27032
|
+
function plural(count2, word) {
|
|
27033
|
+
return `${count2} ${word}${count2 === 1 ? "" : "s"}`;
|
|
27034
|
+
}
|
|
27035
|
+
function headingLine(draft) {
|
|
27036
|
+
const heading = STATUS_HEADINGS[draft.status];
|
|
27037
|
+
if (draft.status === "active" || draft.status === "discarded") {
|
|
27038
|
+
return `Meta Ads \u2014 ${plural(draft.count, "change")} ${heading} \xB7 ${draft.mode}`;
|
|
27039
|
+
}
|
|
27040
|
+
const counts = /* @__PURE__ */ new Map();
|
|
27041
|
+
for (const op of draft.ops) {
|
|
27042
|
+
const status = op.result?.status ?? "pending";
|
|
27043
|
+
counts.set(status, (counts.get(status) ?? 0) + 1);
|
|
27044
|
+
}
|
|
27045
|
+
const summary = [...counts].map(([status, count2]) => `${count2} ${status}`).join(", ");
|
|
27046
|
+
return `Meta Ads \u2014 ${heading} \xB7 ${summary} \xB7 ${draft.mode}`;
|
|
27047
|
+
}
|
|
27048
|
+
function resultPart(op) {
|
|
27049
|
+
const result = op.result;
|
|
27050
|
+
if (!result) {
|
|
27051
|
+
return void 0;
|
|
27052
|
+
}
|
|
27053
|
+
if (result.status === "failed") {
|
|
27054
|
+
return `\u2717 failed: ${result.error ?? "no reason recorded"}`;
|
|
27055
|
+
}
|
|
27056
|
+
if (result.status === "skipped") {
|
|
27057
|
+
return `\u2717 skipped: waiting on ${result.skippedBecause ?? "a change that did not apply"}`;
|
|
27058
|
+
}
|
|
27059
|
+
const id = result.id ?? result.hash;
|
|
27060
|
+
const applied = `\u2713 ${result.status}${id ? ` \u2192 ${id}` : ""}`;
|
|
27061
|
+
const verification = result.verification;
|
|
27062
|
+
if (verification && verification.status !== "confirmed") {
|
|
27063
|
+
return `${applied} (read-back ${verification.status}${verification.note ? `: ${verification.note}` : ""})`;
|
|
27064
|
+
}
|
|
27065
|
+
return applied;
|
|
27066
|
+
}
|
|
27067
|
+
function opLine(op, showAccount) {
|
|
27068
|
+
const parts = [op.ref, op.kind, op.summary];
|
|
27069
|
+
if (op.target && op.target !== op.ref) {
|
|
27070
|
+
parts.push(`target ${op.target}`);
|
|
27071
|
+
}
|
|
27072
|
+
if (showAccount) {
|
|
27073
|
+
parts.push(`account ${op.accountId}`);
|
|
27074
|
+
}
|
|
27075
|
+
if (op.dependsOn.length > 0) {
|
|
27076
|
+
parts.push(`after ${op.dependsOn.join(", ")}`);
|
|
27077
|
+
}
|
|
27078
|
+
const result = resultPart(op);
|
|
27079
|
+
if (result) {
|
|
27080
|
+
parts.push(result);
|
|
27081
|
+
}
|
|
27082
|
+
return ` ${parts.join(" \xB7 ")}`;
|
|
27083
|
+
}
|
|
27084
|
+
function renderDraftStatus3(draft) {
|
|
27085
|
+
if (draft.status === "none" || draft.count === 0) {
|
|
27086
|
+
return "No staged Meta changes on this chat.";
|
|
27087
|
+
}
|
|
27088
|
+
const lines = [headingLine(draft)];
|
|
27089
|
+
if (draft.status === "active") {
|
|
27090
|
+
lines.push("Publishing makes every change real on the ad account.");
|
|
27091
|
+
}
|
|
27092
|
+
lines.push("");
|
|
27093
|
+
const showAccount = new Set(draft.ops.map((op) => op.accountId)).size > 1;
|
|
27094
|
+
for (const op of draft.ops) {
|
|
27095
|
+
lines.push(opLine(op, showAccount));
|
|
27096
|
+
}
|
|
27097
|
+
if (draft.advisories.length > 0) {
|
|
27098
|
+
lines.push("", "Advisories");
|
|
27099
|
+
for (const advisory of draft.advisories) {
|
|
27100
|
+
lines.push(` ! ${advisory.ref}: ${advisory.message}`);
|
|
27101
|
+
}
|
|
27102
|
+
}
|
|
27103
|
+
lines.push("", "Full staged payload for one op: baker ads meta draft show <ref> \xB7 raw envelope: --json");
|
|
27104
|
+
return lines.join("\n");
|
|
27105
|
+
}
|
|
27106
|
+
|
|
27107
|
+
// src/commands/ads/meta/draft.ts
|
|
27108
|
+
registerSchema({
|
|
27109
|
+
command: "ads.meta.draft",
|
|
27110
|
+
description: "Review every Meta op staged on this chat, one line each: ref \xB7 kind \xB7 summary \xB7 what it waits on, and after a publish \u2713 applied \u2192 id or \u2717 failed: <error>. `draft show <ref>` prints one op's full payload.",
|
|
27111
|
+
args: {
|
|
27112
|
+
json: { type: "boolean", description: "Raw JSON envelope instead of the readable list", required: false },
|
|
27113
|
+
chat: { type: "string", description: "Read another chat's draft instead of this one", required: false }
|
|
27114
|
+
}
|
|
27115
|
+
});
|
|
27116
|
+
async function listDraft2(json, chat) {
|
|
27033
27117
|
try {
|
|
27034
27118
|
const chatId = resolveChatId(chat);
|
|
27035
27119
|
const response = await apiPost("/api/ads/meta/draft", { chatId });
|
|
27036
|
-
|
|
27120
|
+
if (json || !response.ok || !response.data) {
|
|
27121
|
+
writeJsonEnvelope(response);
|
|
27122
|
+
return;
|
|
27123
|
+
}
|
|
27124
|
+
process.stdout.write(`${renderDraftStatus3(response.data)}
|
|
27125
|
+
`);
|
|
27037
27126
|
} catch (err) {
|
|
27038
27127
|
handleMetaError(err);
|
|
27039
27128
|
}
|
|
@@ -27041,11 +27130,14 @@ async function listDraft2(chat) {
|
|
|
27041
27130
|
var listCommand10 = defineCommand65({
|
|
27042
27131
|
meta: {
|
|
27043
27132
|
name: "list",
|
|
27044
|
-
description: "Show every Meta write op staged in this chat,
|
|
27133
|
+
description: "Show every Meta write op staged in this chat, one line each (ref \xB7 kind \xB7 summary \xB7 dependencies \xB7 post-publish result); --json for the raw envelope. Publishing reaches the real ad account, so review this before you finish. Example: baker ads meta draft"
|
|
27134
|
+
},
|
|
27135
|
+
args: {
|
|
27136
|
+
json: { type: "boolean", description: "Print the raw JSON envelope instead of the readable list" },
|
|
27137
|
+
chat: CHAT_READ_ARG
|
|
27045
27138
|
},
|
|
27046
|
-
args: { chat: CHAT_READ_ARG },
|
|
27047
27139
|
run: async ({ args: args5 }) => {
|
|
27048
|
-
await listDraft2(args5.chat);
|
|
27140
|
+
await listDraft2(args5.json === true, args5.chat);
|
|
27049
27141
|
}
|
|
27050
27142
|
});
|
|
27051
27143
|
var showCommand2 = defineCommand65({
|
|
@@ -27115,7 +27207,10 @@ var metaDraftCommand = defineCommand65({
|
|
|
27115
27207
|
},
|
|
27116
27208
|
// Declared on the parent too: `default: "list"` dispatches the bare `draft --chat <id>` form, and
|
|
27117
27209
|
// citty prints help for a flag the parent does not know instead of forwarding it.
|
|
27118
|
-
args: {
|
|
27210
|
+
args: {
|
|
27211
|
+
json: { type: "boolean", description: "Print the raw JSON envelope instead of the readable list" },
|
|
27212
|
+
chat: CHAT_READ_ARG
|
|
27213
|
+
},
|
|
27119
27214
|
subCommands: { list: listCommand10, show: showCommand2, remove: removeCommand3, clear: clearCommand3 },
|
|
27120
27215
|
// `default` (not `run`) dispatches the bare invocation: citty falls through to `run`
|
|
27121
27216
|
// after dispatching a subcommand, so a `run` here would list the draft a second time
|
|
@@ -28928,7 +29023,7 @@ function renderNode3(node, depth, showAccount, out) {
|
|
|
28928
29023
|
}
|
|
28929
29024
|
}
|
|
28930
29025
|
var NO_CHANGES = "No X Ads changes staged";
|
|
28931
|
-
var
|
|
29026
|
+
var STATUS_HEADINGS2 = {
|
|
28932
29027
|
active: "staged",
|
|
28933
29028
|
publishing: "publishing",
|
|
28934
29029
|
applied: "published",
|
|
@@ -28940,8 +29035,8 @@ function didNotApply(op) {
|
|
|
28940
29035
|
function didApply(op) {
|
|
28941
29036
|
return op.result?.status === "applied" || op.result?.status === "simulated";
|
|
28942
29037
|
}
|
|
28943
|
-
function
|
|
28944
|
-
const heading =
|
|
29038
|
+
function headingLine2(draft) {
|
|
29039
|
+
const heading = STATUS_HEADINGS2[draft.status] ?? draft.status;
|
|
28945
29040
|
if (draft.status !== "applied" && draft.status !== "publishing") {
|
|
28946
29041
|
return `X Ads: ${draft.count} change${draft.count === 1 ? "" : "s"} ${heading}`;
|
|
28947
29042
|
}
|
|
@@ -28955,11 +29050,11 @@ function problemLines(draft) {
|
|
|
28955
29050
|
return `\u2717 ${op.summary} \u2014 failed: ${reason}`;
|
|
28956
29051
|
});
|
|
28957
29052
|
}
|
|
28958
|
-
function
|
|
29053
|
+
function renderDraftStatus4(draft) {
|
|
28959
29054
|
if (draft.status === "none" || draft.count === 0) {
|
|
28960
29055
|
return NO_CHANGES;
|
|
28961
29056
|
}
|
|
28962
|
-
const out = [
|
|
29057
|
+
const out = [headingLine2(draft), ""];
|
|
28963
29058
|
const tree = draft.tree ?? [];
|
|
28964
29059
|
const showAccount = accountsInTree3(tree).size > 1;
|
|
28965
29060
|
for (const node of tree) {
|
|
@@ -28997,7 +29092,7 @@ async function listDraft3(json, chat) {
|
|
|
28997
29092
|
writeJsonEnvelope(response);
|
|
28998
29093
|
return;
|
|
28999
29094
|
}
|
|
29000
|
-
process.stdout.write(`${
|
|
29095
|
+
process.stdout.write(`${renderDraftStatus4(response.data)}
|
|
29001
29096
|
`);
|
|
29002
29097
|
} catch (err) {
|
|
29003
29098
|
handleDraftError(err);
|