@koda-sl/baker-cli 0.173.1 → 0.174.0-dev.04aff5532
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 +7 -1
- package/dist/{chunk-JOPBGFXC.js → chunk-7QMWNMRW.js} +19 -5
- package/dist/chunk-7QMWNMRW.js.map +1 -0
- package/dist/{chunk-CG6OTNUU.js → chunk-K424D3CY.js} +4 -4
- package/dist/chunk-K424D3CY.js.map +1 -0
- package/dist/{chunk-R4PG6VCS.js → chunk-TFPZLUVM.js} +10 -3
- package/dist/chunk-TFPZLUVM.js.map +1 -0
- package/dist/{chunk-LVCBCJWF.js → chunk-ZH6ZNYPB.js} +5 -2
- package/dist/chunk-ZH6ZNYPB.js.map +1 -0
- package/dist/cli.js +543 -27
- package/dist/cli.js.map +1 -1
- package/dist/{client-R5ZSVIK3.js → client-XRQWOADV.js} +2 -2
- package/dist/engine/index.js +1 -1
- package/dist/{output-KLK2GZXR.js → output-FL7WRIJF.js} +3 -3
- package/dist/{shared-AXAXNKGE.js → shared-OIYLDW3G.js} +6 -4
- package/package.json +1 -1
- package/dist/chunk-CG6OTNUU.js.map +0 -1
- package/dist/chunk-JOPBGFXC.js.map +0 -1
- package/dist/chunk-LVCBCJWF.js.map +0 -1
- package/dist/chunk-R4PG6VCS.js.map +0 -1
- /package/dist/{client-R5ZSVIK3.js.map → client-XRQWOADV.js.map} +0 -0
- /package/dist/{output-KLK2GZXR.js.map → output-FL7WRIJF.js.map} +0 -0
- /package/dist/{shared-AXAXNKGE.js.map → shared-OIYLDW3G.js.map} +0 -0
package/README.md
CHANGED
|
@@ -820,7 +820,7 @@ adsets # filter by --campaign-id
|
|
|
820
820
|
ads # filter by --adset-id or --campaign-id
|
|
821
821
|
creatives # list per account, or fetch single via --creative-id
|
|
822
822
|
audiences # custom audiences (incl. lookalikes)
|
|
823
|
-
pixels # list, or --stats for one --pixel-id
|
|
823
|
+
pixels # list, or --stats for one --pixel-id (see "Restricted pixel fields")
|
|
824
824
|
activities # account audit log, ~90d retention
|
|
825
825
|
insights # see "Smart defaults" above
|
|
826
826
|
preview # iframe HTML for a creative or ad in a given ad_format
|
|
@@ -835,6 +835,12 @@ The HTTP backend exposes more endpoints (catalogs, ad-studies, ad-images, labels
|
|
|
835
835
|
- All cached actions are keyed per company; two companies that connect to the same Meta account never share cache entries.
|
|
836
836
|
- `--skip-cache` on any command forces a re-fetch.
|
|
837
837
|
|
|
838
|
+
#### Restricted pixel fields
|
|
839
|
+
|
|
840
|
+
A Pixel/Dataset is a Business asset assigned separately from the ad account, and Meta refuses the *whole* read with `code 200` if any requested field needs a role in the owning Business — so an agency with partner access used to get nothing back. `pixels` now retries without those fields (`code`, `creator`, `owner_business`, `data_use_setting`, `enable_automatic_matching`, `first_party_cookie_status`) and prints the usual `{ ok: true, data, warnings }` with a `PIXEL_FIELDS_RESTRICTED` warning naming them (the HTTP endpoint carries them as `restrictedFields`). IDs, names, creation and last-fired times are always complete; a missing install code means "not visible to us", not "not configured". As elsewhere in the family, `--output csv|jsonl|md` prints rows only — use the default JSON to see the warning.
|
|
841
|
+
|
|
842
|
+
A `FORBIDDEN` from any Meta command is a permission refusal on a live connection, not a missing one. Meta uses one code for two causes, so the `error.fix` gives the agent the test rather than a verdict: if other reads on the account also fail the connection is short a scope and only a reconnect widens it; if they work, the asset needs assigning in Business settings and reconnecting cannot help.
|
|
843
|
+
|
|
838
844
|
#### Notes / gotchas
|
|
839
845
|
|
|
840
846
|
- `spend` is a decimal string in account currency, not an integer.
|
|
@@ -1,19 +1,32 @@
|
|
|
1
1
|
import {
|
|
2
2
|
handleConnectionError,
|
|
3
|
+
needsConnectionFix,
|
|
3
4
|
writeAdsJson
|
|
4
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-TFPZLUVM.js";
|
|
5
6
|
import {
|
|
6
7
|
ApiError
|
|
7
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-ZH6ZNYPB.js";
|
|
8
9
|
import {
|
|
9
10
|
getEnv
|
|
10
11
|
} from "./chunk-RK67WL4O.js";
|
|
11
12
|
|
|
12
13
|
// src/commands/ads/meta/shared.ts
|
|
13
14
|
var DAY_MS = 864e5;
|
|
15
|
+
function metaForbiddenFix(message) {
|
|
16
|
+
return {
|
|
17
|
+
action: "reject",
|
|
18
|
+
explanation: [
|
|
19
|
+
message,
|
|
20
|
+
"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.",
|
|
21
|
+
"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.",
|
|
22
|
+
"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 \u2014 the client assigns that asset to the connected user or partner in Meta Business settings, under the asset's own section (Data sources \u2192 Datasets for a pixel).",
|
|
23
|
+
"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 \u2014 never present the task as verified when this step was skipped."
|
|
24
|
+
].join("\n\n")
|
|
25
|
+
};
|
|
26
|
+
}
|
|
14
27
|
function handleMetaError(err) {
|
|
15
28
|
if (err instanceof ApiError) {
|
|
16
|
-
if (err.code
|
|
29
|
+
if (needsConnectionFix(err.code, err.message)) {
|
|
17
30
|
handleConnectionError("meta_ads", err.message);
|
|
18
31
|
}
|
|
19
32
|
const envelope = {
|
|
@@ -21,7 +34,7 @@ function handleMetaError(err) {
|
|
|
21
34
|
error: {
|
|
22
35
|
code: err.code,
|
|
23
36
|
message: err.message,
|
|
24
|
-
fix: { action: "reject", explanation: err.message },
|
|
37
|
+
fix: err.code === "FORBIDDEN" ? metaForbiddenFix(err.message) : { action: "reject", explanation: err.message },
|
|
25
38
|
retryable: err.code === "RATE_LIMITED" || err.code === "INTERNAL_ERROR"
|
|
26
39
|
}
|
|
27
40
|
};
|
|
@@ -86,6 +99,7 @@ function resolveEffectiveStatus(args) {
|
|
|
86
99
|
}
|
|
87
100
|
|
|
88
101
|
export {
|
|
102
|
+
metaForbiddenFix,
|
|
89
103
|
handleMetaError,
|
|
90
104
|
resolveAccountIdArg,
|
|
91
105
|
getDateRange,
|
|
@@ -94,4 +108,4 @@ export {
|
|
|
94
108
|
csvOrJson,
|
|
95
109
|
resolveEffectiveStatus
|
|
96
110
|
};
|
|
97
|
-
//# sourceMappingURL=chunk-
|
|
111
|
+
//# sourceMappingURL=chunk-7QMWNMRW.js.map
|
|
@@ -0,0 +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 === \"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;AAAA,MACzD;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":[]}
|
|
@@ -7,9 +7,9 @@ import {
|
|
|
7
7
|
__toESM
|
|
8
8
|
} from "./chunk-RK67WL4O.js";
|
|
9
9
|
|
|
10
|
-
//
|
|
10
|
+
// ../../.pnpm-store/v10/links/@/safe-stable-stringify/2.5.0/810146e81bae4e3a061fe487864f2fde80c4b03b886877dc0f1fffbc6480b67e/node_modules/safe-stable-stringify/index.js
|
|
11
11
|
var require_safe_stable_stringify = __commonJS({
|
|
12
|
-
"
|
|
12
|
+
"../../.pnpm-store/v10/links/@/safe-stable-stringify/2.5.0/810146e81bae4e3a061fe487864f2fde80c4b03b886877dc0f1fffbc6480b67e/node_modules/safe-stable-stringify/index.js"(exports, module) {
|
|
13
13
|
"use strict";
|
|
14
14
|
var { hasOwnProperty } = Object.prototype;
|
|
15
15
|
var stringify = configure2();
|
|
@@ -1075,7 +1075,7 @@ function resolveAdaptFormats(params) {
|
|
|
1075
1075
|
return params.formats ?? [];
|
|
1076
1076
|
}
|
|
1077
1077
|
|
|
1078
|
-
//
|
|
1078
|
+
// ../../.pnpm-store/v10/links/@/safe-stable-stringify/2.5.0/810146e81bae4e3a061fe487864f2fde80c4b03b886877dc0f1fffbc6480b67e/node_modules/safe-stable-stringify/esm/wrapper.js
|
|
1079
1079
|
var import__ = __toESM(require_safe_stable_stringify(), 1);
|
|
1080
1080
|
var configure = import__.default.configure;
|
|
1081
1081
|
var wrapper_default = import__.default;
|
|
@@ -8028,4 +8028,4 @@ export {
|
|
|
8028
8028
|
defaultRegistry,
|
|
8029
8029
|
createEngineFromEnv
|
|
8030
8030
|
};
|
|
8031
|
-
//# sourceMappingURL=chunk-
|
|
8031
|
+
//# sourceMappingURL=chunk-K424D3CY.js.map
|