@koda-sl/baker-cli 0.507.0 → 0.508.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.
- package/README.md +1 -1
- package/dist/{ads-T5RPISTB.js → ads-BOLVA4YI.js} +7 -7
- package/dist/{analytics-XW4ZEEAR.js → analytics-YIRKA7OC.js} +2 -2
- package/dist/{chatgpt-SVGWMEQD.js → chatgpt-DEG26ES2.js} +2 -2
- package/dist/{chunk-LLBB6OAB.js → chunk-GACSA5A6.js} +2 -2
- package/dist/{chunk-TWKZVPOA.js → chunk-Y2FAUFWK.js} +2 -2
- package/dist/chunk-Y2FAUFWK.js.map +1 -0
- package/dist/cli.js +1 -1
- package/dist/{google-3GSINSOV.js → google-X6LRA27Z.js} +4 -4
- package/dist/{insights-EQW34IL4.js → insights-M3CYB22P.js} +2 -2
- package/dist/{insights-JZVXC7IJ.js → insights-XCT3ONC7.js} +2 -2
- package/dist/{linkedin-4MGRSWXZ.js → linkedin-AWUXPBIV.js} +2 -2
- package/dist/{meta-KS3SQSYZ.js → meta-VL5RNWCW.js} +2 -2
- package/dist/{performance-WPFGEJTE.js → performance-34XRXF2I.js} +117 -33
- package/dist/performance-34XRXF2I.js.map +1 -0
- package/dist/{query-42QT56FJ.js → query-QM7MBQD7.js} +3 -3
- package/dist/{stats-LNEQJ42S.js → stats-HUWCLN7J.js} +2 -2
- package/dist/{sync-YOKQEZ46.js → sync-PHH65JFS.js} +2 -2
- package/dist/{x-7VY6K7JS.js → x-AHQPDNB4.js} +2 -2
- package/package.json +1 -1
- package/dist/chunk-TWKZVPOA.js.map +0 -1
- package/dist/performance-WPFGEJTE.js.map +0 -1
- /package/dist/{ads-T5RPISTB.js.map → ads-BOLVA4YI.js.map} +0 -0
- /package/dist/{analytics-XW4ZEEAR.js.map → analytics-YIRKA7OC.js.map} +0 -0
- /package/dist/{chatgpt-SVGWMEQD.js.map → chatgpt-DEG26ES2.js.map} +0 -0
- /package/dist/{chunk-LLBB6OAB.js.map → chunk-GACSA5A6.js.map} +0 -0
- /package/dist/{google-3GSINSOV.js.map → google-X6LRA27Z.js.map} +0 -0
- /package/dist/{insights-EQW34IL4.js.map → insights-M3CYB22P.js.map} +0 -0
- /package/dist/{insights-JZVXC7IJ.js.map → insights-XCT3ONC7.js.map} +0 -0
- /package/dist/{linkedin-4MGRSWXZ.js.map → linkedin-AWUXPBIV.js.map} +0 -0
- /package/dist/{meta-KS3SQSYZ.js.map → meta-VL5RNWCW.js.map} +0 -0
- /package/dist/{query-42QT56FJ.js.map → query-QM7MBQD7.js.map} +0 -0
- /package/dist/{stats-LNEQJ42S.js.map → stats-HUWCLN7J.js.map} +0 -0
- /package/dist/{sync-YOKQEZ46.js.map → sync-PHH65JFS.js.map} +0 -0
- /package/dist/{x-7VY6K7JS.js.map → x-AHQPDNB4.js.map} +0 -0
package/README.md
CHANGED
|
@@ -182,7 +182,7 @@ Multi-platform ad commands. Google, Meta, and LinkedIn support both reads and **
|
|
|
182
182
|
|
|
183
183
|
Every connected ad platform's numbers from Baker's stored copy (refreshed every few hours, full history), without calling the platforms. Read-only. Results are the company's chosen conversion goals, else each platform's headline conversions; money is shown in the company's reporting currency at the period's average ECB rate (`--currency <ISO>` for another, `--currency native` for each ad account's own), and comparisons use only days whose conversions have settled.
|
|
184
184
|
|
|
185
|
-
It is the first read for any ads report, analysis or alert: no rate limit and the whole history, a few hours behind
|
|
185
|
+
It is the first read for any ads report, analysis or alert: no rate limit and the whole history, a few hours behind. Every read's first hint says when each platform's copy was last updated and which days it holds; when an update failed or history is still loading, it names the days the copy lacks and the live read that fills them. The live `baker ads <platform>` reads are for today's numbers and what this lacks, and their answers name the synced command that covers the same question.
|
|
186
186
|
|
|
187
187
|
```bash
|
|
188
188
|
baker ads performance overview --days 30 # spend, results, cost per result per platform, vs the period before
|
|
@@ -33,16 +33,16 @@ Examples:
|
|
|
33
33
|
Full guides: __tooling__/docs/tools/baker/ads-<platform>.md (google|meta|linkedin|x|chatgpt|performance|terms)`
|
|
34
34
|
},
|
|
35
35
|
subCommands: {
|
|
36
|
-
google: () => import("./google-
|
|
37
|
-
meta: () => import("./meta-
|
|
38
|
-
linkedin: () => import("./linkedin-
|
|
39
|
-
x: () => import("./x-
|
|
40
|
-
chatgpt: () => import("./chatgpt-
|
|
41
|
-
performance: () => import("./performance-
|
|
36
|
+
google: () => import("./google-X6LRA27Z.js").then((m) => m.googleCommand),
|
|
37
|
+
meta: () => import("./meta-VL5RNWCW.js").then((m) => m.metaCommand),
|
|
38
|
+
linkedin: () => import("./linkedin-AWUXPBIV.js").then((m) => m.linkedinCommand),
|
|
39
|
+
x: () => import("./x-AHQPDNB4.js").then((m) => m.xCommand),
|
|
40
|
+
chatgpt: () => import("./chatgpt-DEG26ES2.js").then((m) => m.chatgptCommand),
|
|
41
|
+
performance: () => import("./performance-34XRXF2I.js").then((m) => m.performanceCommand),
|
|
42
42
|
terms: () => import("./terms-O6ODXEOK.js").then((m) => m.termsCommand)
|
|
43
43
|
}
|
|
44
44
|
});
|
|
45
45
|
export {
|
|
46
46
|
adsCommand
|
|
47
47
|
};
|
|
48
|
-
//# sourceMappingURL=ads-
|
|
48
|
+
//# sourceMappingURL=ads-BOLVA4YI.js.map
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
} from "./chunk-CLUJWAQE.js";
|
|
8
8
|
import {
|
|
9
9
|
syncedFirstHint
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-Y2FAUFWK.js";
|
|
11
11
|
import {
|
|
12
12
|
writeAdsJson,
|
|
13
13
|
writeAdsOutput
|
|
@@ -442,4 +442,4 @@ Examples \u2014 common AI questions:
|
|
|
442
442
|
export {
|
|
443
443
|
analyticsCommand
|
|
444
444
|
};
|
|
445
|
-
//# sourceMappingURL=analytics-
|
|
445
|
+
//# sourceMappingURL=analytics-YIRKA7OC.js.map
|
|
@@ -36,7 +36,7 @@ Full guide: __tooling__/docs/tools/baker/ads-chatgpt.md`
|
|
|
36
36
|
audiences: () => import("./audiences-WCDWN64L.js").then((m) => m.audiencesCommand),
|
|
37
37
|
catalogs: () => import("./catalogs-T23RKYFC.js").then((m) => m.catalogsCommand),
|
|
38
38
|
history: () => import("./history-5MRLNTCI.js").then((m) => m.historyCommand),
|
|
39
|
-
insights: () => import("./insights-
|
|
39
|
+
insights: () => import("./insights-XCT3ONC7.js").then((m) => m.insightsCommand),
|
|
40
40
|
locations: () => import("./locations-UKBOT6SB.js").then((m) => m.locationsCommand),
|
|
41
41
|
draft: () => import("./draft-LPVK6YHI.js").then((m) => m.draftCommand)
|
|
42
42
|
}
|
|
@@ -44,4 +44,4 @@ Full guide: __tooling__/docs/tools/baker/ads-chatgpt.md`
|
|
|
44
44
|
export {
|
|
45
45
|
chatgptCommand
|
|
46
46
|
};
|
|
47
|
-
//# sourceMappingURL=chatgpt-
|
|
47
|
+
//# sourceMappingURL=chatgpt-DEG26ES2.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
googlePresetSyncedHint
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-Y2FAUFWK.js";
|
|
4
4
|
import {
|
|
5
5
|
getFieldDescriptions
|
|
6
6
|
} from "./chunk-JPK34QPS.js";
|
|
@@ -1643,4 +1643,4 @@ export {
|
|
|
1643
1643
|
runQuery,
|
|
1644
1644
|
queryCommand
|
|
1645
1645
|
};
|
|
1646
|
-
//# sourceMappingURL=chunk-
|
|
1646
|
+
//# sourceMappingURL=chunk-GACSA5A6.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// src/commands/ads/synced-first.ts
|
|
2
2
|
function hint(command) {
|
|
3
|
-
return `Synced copy first: \`${command}\` answers this with no rate limit and full history, a few hours behind (
|
|
3
|
+
return `Synced copy first: \`${command}\` answers this with no rate limit and full history, a few hours behind (every synced read says how fresh, and which days it lacks). Read live only for today's numbers, a field the synced copy lacks, or right before a change.`;
|
|
4
4
|
}
|
|
5
5
|
function syncedCommand(platform, level) {
|
|
6
6
|
const scope = `--platforms ${platform}`;
|
|
@@ -47,4 +47,4 @@ export {
|
|
|
47
47
|
metaBreakdownSyncedHint,
|
|
48
48
|
googlePresetSyncedHint
|
|
49
49
|
};
|
|
50
|
-
//# sourceMappingURL=chunk-
|
|
50
|
+
//# sourceMappingURL=chunk-Y2FAUFWK.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/ads/synced-first.ts"],"sourcesContent":["/**\n * The nudge on every live performance read (`baker ads <platform> insights`,\n * `analytics`, `stats`, GAQL performance presets): Baker's synced copy\n * (`baker ads performance`) answers the same question with no rate limit and\n * the whole history, so it is the first read; live is for today's numbers, a\n * field the copy lacks, or the read right before a change.\n */\n\nexport type SyncedPlatform = \"meta\" | \"google\" | \"linkedin\" | \"x\" | \"chatgpt\";\nexport type SyncedLevel = \"account\" | \"campaign\" | \"ad_group\" | \"ad\" | \"keyword\";\n\nfunction hint(command: string): string {\n return `Synced copy first: \\`${command}\\` answers this with no rate limit and full history, a few hours behind (every synced read says how fresh, and which days it lacks). Read live only for today's numbers, a field the synced copy lacks, or right before a change.`;\n}\n\nfunction syncedCommand(platform: SyncedPlatform, level: SyncedLevel): string {\n const scope = `--platforms ${platform}`;\n if (level === \"account\") return `baker ads performance overview ${scope}`;\n return `baker ads performance entities ${scope} --level ${level}`;\n}\n\nexport function syncedFirstHint(platform: SyncedPlatform, level: SyncedLevel): string {\n return hint(syncedCommand(platform, level));\n}\n\n/** Meta insights breakdowns the synced copy keeps, and the `breakdown --dimension` that reads each. */\nconst META_BREAKDOWN_DIMENSION: Record<string, string> = {\n age: \"age\",\n gender: \"gender\",\n country: \"country\",\n publisher_platform: \"placement\",\n platform_position: \"placement\",\n impression_device: \"device\",\n device_platform: \"device\",\n};\n\n/** The synced read for a Meta breakdown; null when the copy does not keep it or would need two dimensions at once. */\nexport function metaBreakdownSyncedHint(breakdowns: readonly string[]): string | null {\n const dimensions = new Set(breakdowns.map((breakdown) => META_BREAKDOWN_DIMENSION[breakdown]));\n const [dimension] = dimensions;\n if (dimensions.size !== 1 || dimension === undefined) return null;\n return hint(`baker ads performance breakdown --dimension ${dimension} --platforms meta`);\n}\n\n/** Google Ads presets the synced copy answers, and the read that does. Inventories and live-only reports are absent. */\nconst GOOGLE_PRESET_READS: Record<string, string> = {\n \"account-summary\": \"baker ads performance overview --platforms google\",\n \"campaign-performance\": \"baker ads performance entities --platforms google --level campaign\",\n \"budget-limited\": \"baker ads performance impression-share\",\n \"keyword-analysis\": \"baker ads performance entities --platforms google --level keyword\",\n \"search-terms\": \"baker ads performance search-terms\",\n \"ad-copy-performance\": \"baker ads performance entities --platforms google --level ad\",\n \"geo-performance\": \"baker ads performance breakdown --dimension country --platforms google\",\n \"landing-pages\": \"baker ads performance landings --platforms google\",\n \"conversions-by-action\": \"baker ads performance goals\",\n \"account-budget\": \"baker ads performance pacing --platforms google\",\n};\n\nexport function googlePresetSyncedHint(preset: string | undefined): string | null {\n const command = preset === undefined ? undefined : GOOGLE_PRESET_READS[preset];\n return command === undefined ? null : hint(command);\n}\n"],"mappings":";AAWA,SAAS,KAAK,SAAyB;AACrC,SAAO,wBAAwB,OAAO;AACxC;AAEA,SAAS,cAAc,UAA0B,OAA4B;AAC3E,QAAM,QAAQ,eAAe,QAAQ;AACrC,MAAI,UAAU,UAAW,QAAO,kCAAkC,KAAK;AACvE,SAAO,kCAAkC,KAAK,YAAY,KAAK;AACjE;AAEO,SAAS,gBAAgB,UAA0B,OAA4B;AACpF,SAAO,KAAK,cAAc,UAAU,KAAK,CAAC;AAC5C;AAGA,IAAM,2BAAmD;AAAA,EACvD,KAAK;AAAA,EACL,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,oBAAoB;AAAA,EACpB,mBAAmB;AAAA,EACnB,mBAAmB;AAAA,EACnB,iBAAiB;AACnB;AAGO,SAAS,wBAAwB,YAA8C;AACpF,QAAM,aAAa,IAAI,IAAI,WAAW,IAAI,CAAC,cAAc,yBAAyB,SAAS,CAAC,CAAC;AAC7F,QAAM,CAAC,SAAS,IAAI;AACpB,MAAI,WAAW,SAAS,KAAK,cAAc,OAAW,QAAO;AAC7D,SAAO,KAAK,+CAA+C,SAAS,mBAAmB;AACzF;AAGA,IAAM,sBAA8C;AAAA,EAClD,mBAAmB;AAAA,EACnB,wBAAwB;AAAA,EACxB,kBAAkB;AAAA,EAClB,oBAAoB;AAAA,EACpB,gBAAgB;AAAA,EAChB,uBAAuB;AAAA,EACvB,mBAAmB;AAAA,EACnB,iBAAiB;AAAA,EACjB,yBAAyB;AAAA,EACzB,kBAAkB;AACpB;AAEO,SAAS,uBAAuB,QAA2C;AAChF,QAAM,UAAU,WAAW,SAAY,SAAY,oBAAoB,MAAM;AAC7E,SAAO,YAAY,SAAY,OAAO,KAAK,OAAO;AACpD;","names":[]}
|
package/dist/cli.js
CHANGED
|
@@ -117,7 +117,7 @@ Introspection: Run 'baker schema <command>' to inspect argument schemas.`
|
|
|
117
117
|
capabilities: () => import("./capabilities-QREOIRIH.js").then((m) => m.capabilitiesCommand),
|
|
118
118
|
actions: () => import("./actions-IG2D6NRC.js").then((m) => m.actionsCommand),
|
|
119
119
|
"scheduled-agents": () => import("./scheduled-agents-7JW3JIOL.js").then((m) => m.scheduledAgentsCommand),
|
|
120
|
-
ads: () => import("./ads-
|
|
120
|
+
ads: () => import("./ads-BOLVA4YI.js").then((m) => m.adsCommand),
|
|
121
121
|
brand: () => import("./brand-JB6NXAAL.js").then((m) => m.brandCommand),
|
|
122
122
|
analytics: () => import("./analytics-P5UFA6I4.js").then((m) => m.analyticsCommand),
|
|
123
123
|
experiment: () => import("./experiment-I5CRLOVN.js").then((m) => m.experimentCommand),
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
runQuery
|
|
3
|
-
} from "./chunk-
|
|
4
|
-
import "./chunk-
|
|
3
|
+
} from "./chunk-GACSA5A6.js";
|
|
4
|
+
import "./chunk-Y2FAUFWK.js";
|
|
5
5
|
import "./chunk-JPK34QPS.js";
|
|
6
6
|
import {
|
|
7
7
|
AD_COPY_LIST_USAGE,
|
|
@@ -3254,7 +3254,7 @@ Full guide: __tooling__/docs/tools/baker/ads-google.md`
|
|
|
3254
3254
|
"identity-verification": () => import("./account-reads-KJHPUVEV.js").then((m) => m.identityVerificationCommand),
|
|
3255
3255
|
previews: () => import("./account-reads-KJHPUVEV.js").then((m) => m.previewsCommand),
|
|
3256
3256
|
brands: () => import("./account-reads-KJHPUVEV.js").then((m) => m.brandsCommand),
|
|
3257
|
-
query: () => import("./query-
|
|
3257
|
+
query: () => import("./query-QM7MBQD7.js").then((m) => m.queryCommand),
|
|
3258
3258
|
keywords: keywordsWithWrites,
|
|
3259
3259
|
...googleWriteCommandsWithLists,
|
|
3260
3260
|
"merchant-center": merchantCenterCommand,
|
|
@@ -3264,4 +3264,4 @@ Full guide: __tooling__/docs/tools/baker/ads-google.md`
|
|
|
3264
3264
|
export {
|
|
3265
3265
|
googleCommand
|
|
3266
3266
|
};
|
|
3267
|
-
//# sourceMappingURL=google-
|
|
3267
|
+
//# sourceMappingURL=google-X6LRA27Z.js.map
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
import {
|
|
12
12
|
metaBreakdownSyncedHint,
|
|
13
13
|
syncedFirstHint
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-Y2FAUFWK.js";
|
|
15
15
|
import "./chunk-Y2JZX6JB.js";
|
|
16
16
|
import {
|
|
17
17
|
writeAdsJson,
|
|
@@ -434,4 +434,4 @@ export {
|
|
|
434
434
|
insightsCommand,
|
|
435
435
|
wantsAsync
|
|
436
436
|
};
|
|
437
|
-
//# sourceMappingURL=insights-
|
|
437
|
+
//# sourceMappingURL=insights-M3CYB22P.js.map
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
} from "./chunk-M7HD42M6.js";
|
|
11
11
|
import {
|
|
12
12
|
syncedFirstHint
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-Y2FAUFWK.js";
|
|
14
14
|
import {
|
|
15
15
|
writeJsonEnvelope
|
|
16
16
|
} from "./chunk-ZRP7Y2DM.js";
|
|
@@ -127,4 +127,4 @@ export {
|
|
|
127
127
|
chatgptInsightsHints,
|
|
128
128
|
insightsCommand
|
|
129
129
|
};
|
|
130
|
-
//# sourceMappingURL=insights-
|
|
130
|
+
//# sourceMappingURL=insights-XCT3ONC7.js.map
|
|
@@ -1291,7 +1291,7 @@ Full guide: __tooling__/docs/tools/baker/ads-linkedin.md`
|
|
|
1291
1291
|
"lead-forms": () => import("./write-commands-ZQ5JYOUB.js").then((m) => m.leadFormsCommand),
|
|
1292
1292
|
experiments: () => import("./experiments-PJWBABLJ.js").then((m) => m.experimentsCommand),
|
|
1293
1293
|
draft: () => import("./draft-RTZBROFS.js").then((m) => m.linkedinDraftCommand),
|
|
1294
|
-
analytics: () => import("./analytics-
|
|
1294
|
+
analytics: () => import("./analytics-YIRKA7OC.js").then((m) => m.analyticsCommand),
|
|
1295
1295
|
demographics: () => import("./demographics-MZBHPCT3.js").then((m) => m.demographicsCommand),
|
|
1296
1296
|
companies: () => import("./companies-2DHNNS34.js").then((m) => m.companiesCommand),
|
|
1297
1297
|
"top-companies": () => import("./top-companies-HZQTHV2H.js").then((m) => m.topCompaniesCommand),
|
|
@@ -1311,4 +1311,4 @@ Full guide: __tooling__/docs/tools/baker/ads-linkedin.md`
|
|
|
1311
1311
|
export {
|
|
1312
1312
|
linkedinCommand
|
|
1313
1313
|
};
|
|
1314
|
-
//# sourceMappingURL=linkedin-
|
|
1314
|
+
//# sourceMappingURL=linkedin-AWUXPBIV.js.map
|
|
@@ -92,7 +92,7 @@ Full guide: __tooling__/docs/tools/baker/ads-meta.md`
|
|
|
92
92
|
"custom-conversions": () => import("./custom-conversions-6KYF26SK.js").then((m) => m.customConversionsCommand),
|
|
93
93
|
pixels: () => import("./pixels-ZZTB6NWF.js").then((m) => m.pixelsCommand),
|
|
94
94
|
activities: () => import("./activities-LKLPORDI.js").then((m) => m.activitiesCommand),
|
|
95
|
-
insights: () => import("./insights-
|
|
95
|
+
insights: () => import("./insights-M3CYB22P.js").then((m) => m.insightsCommand),
|
|
96
96
|
preview: () => import("./preview-CWRGDFCP.js").then((m) => m.previewCommand),
|
|
97
97
|
targeting: () => import("./targeting-SOMGPSKV.js").then((m) => m.targetingCommand),
|
|
98
98
|
"instagram-accounts": () => import("./account-assets-WZOYMT3C.js").then((m) => m.instagramAccountsCommand),
|
|
@@ -119,4 +119,4 @@ Full guide: __tooling__/docs/tools/baker/ads-meta.md`
|
|
|
119
119
|
export {
|
|
120
120
|
metaCommand
|
|
121
121
|
};
|
|
122
|
-
//# sourceMappingURL=meta-
|
|
122
|
+
//# sourceMappingURL=meta-VL5RNWCW.js.map
|
|
@@ -1469,15 +1469,119 @@ import { defineCommand as defineCommand2 } from "citty";
|
|
|
1469
1469
|
|
|
1470
1470
|
// src/commands/ads/performance/reportCommand.ts
|
|
1471
1471
|
import { defineCommand } from "citty";
|
|
1472
|
+
|
|
1473
|
+
// src/commands/ads/performance/syncCoverage.ts
|
|
1474
|
+
var HOUR_MS = 36e5;
|
|
1475
|
+
var DAY_MS = 24 * HOUR_MS;
|
|
1476
|
+
var BEHIND_AFTER_MS = 26 * HOUR_MS;
|
|
1477
|
+
var LIVE_READ = {
|
|
1478
|
+
google: (from, to) => `baker ads google query --preset campaign-performance --date-range "BETWEEN '${from}' AND '${to}'"`,
|
|
1479
|
+
meta: (from, to) => `baker ads meta insights --level campaign --since ${from} --until ${to}`,
|
|
1480
|
+
linkedin: (from, to) => `baker ads linkedin analytics --level campaign --start ${from} --end ${to}`,
|
|
1481
|
+
x: () => "baker ads x stats",
|
|
1482
|
+
chatgpt: () => "baker ads chatgpt insights"
|
|
1483
|
+
};
|
|
1484
|
+
function ago(ms) {
|
|
1485
|
+
const hours = Math.max(0, Math.floor(ms / HOUR_MS));
|
|
1486
|
+
if (hours < 1) return "under an hour";
|
|
1487
|
+
if (hours < 48) return `${hours}h`;
|
|
1488
|
+
return `${Math.floor(hours / 24)} days`;
|
|
1489
|
+
}
|
|
1490
|
+
function utc(at) {
|
|
1491
|
+
return `${new Date(at).toISOString().slice(0, 16).replace("T", " ")} UTC`;
|
|
1492
|
+
}
|
|
1493
|
+
function dayOf(at) {
|
|
1494
|
+
return new Date(at).toISOString().slice(0, 10);
|
|
1495
|
+
}
|
|
1496
|
+
function dayAfter(day, days = 1) {
|
|
1497
|
+
return dayOf(Date.parse(`${day}T00:00:00Z`) + days * DAY_MS);
|
|
1498
|
+
}
|
|
1499
|
+
function earliest(dates) {
|
|
1500
|
+
const known = dates.filter((date) => date !== null);
|
|
1501
|
+
return known.length === 0 ? null : known.reduce((a, b) => b < a ? b : a);
|
|
1502
|
+
}
|
|
1503
|
+
function liveRead(platform, from, to, why) {
|
|
1504
|
+
const command = LIVE_READ[platform]?.(from, to);
|
|
1505
|
+
return command ? `: read live with \`${command}\` (live reads spend the platform's rate limit \u2014 ${why})` : "";
|
|
1506
|
+
}
|
|
1507
|
+
function headline(platform, accounts, now) {
|
|
1508
|
+
const updatedAt = platform.lastSuccessAt;
|
|
1509
|
+
const from = earliest(accounts.map((account) => account.historyFrom));
|
|
1510
|
+
const through = earliest(accounts.map((account) => account.newestDate));
|
|
1511
|
+
const updated = updatedAt === null ? "never updated yet" : `last updated ${ago(now - updatedAt)} ago (${utc(updatedAt)})`;
|
|
1512
|
+
const holds = from !== null && through !== null ? `; holds ${from} \u2192 ${through}` : "";
|
|
1513
|
+
return `${platformLabel(platform.platform)} synced copy: ${updated}${holds}.`;
|
|
1514
|
+
}
|
|
1515
|
+
function loadingSentences(platform, accounts) {
|
|
1516
|
+
return accounts.filter((account) => !account.backfillComplete && account.historyFrom !== null).map((account) => {
|
|
1517
|
+
const before = account.historyFrom ?? "";
|
|
1518
|
+
const live = LIVE_READ[platform.platform]?.("<from>", dayAfter(before, -1));
|
|
1519
|
+
return `History is still loading for ${account.name || account.adAccountId}: days before ${before} are not in the copy yet${live ? `; for an older window read live with \`${live}\`` : ""}.`;
|
|
1520
|
+
});
|
|
1521
|
+
}
|
|
1522
|
+
function emptySentences(accounts) {
|
|
1523
|
+
if (accounts.every((account) => account.newestDate === null)) return [];
|
|
1524
|
+
return accounts.filter((account) => account.newestDate === null).map((account) => `Nothing is stored yet for ${account.name || account.adAccountId}; read it live.`);
|
|
1525
|
+
}
|
|
1526
|
+
function failureSentences(platform, accounts) {
|
|
1527
|
+
const byError = /* @__PURE__ */ new Map();
|
|
1528
|
+
for (const account of accounts) {
|
|
1529
|
+
if (account.error === null) continue;
|
|
1530
|
+
byError.set(account.error, [...byError.get(account.error) ?? [], account.name || account.adAccountId]);
|
|
1531
|
+
}
|
|
1532
|
+
const sentences = [...byError].map(([error, names]) => `Last update failed for ${names.join(", ")}: ${error}`);
|
|
1533
|
+
if (sentences.length === 0 && platform.error) sentences.push(`Last update failed: ${platform.error}`);
|
|
1534
|
+
return sentences;
|
|
1535
|
+
}
|
|
1536
|
+
function behindSentences(platform, accounts, now) {
|
|
1537
|
+
const sentences = failureSentences(platform, accounts);
|
|
1538
|
+
const updatedAt = platform.lastSuccessAt;
|
|
1539
|
+
const overdue = updatedAt === null || now - updatedAt > BEHIND_AFTER_MS;
|
|
1540
|
+
if (!overdue && sentences.length === 0) return sentences;
|
|
1541
|
+
const through = earliest(accounts.map((account) => account.newestDate));
|
|
1542
|
+
const today = dayOf(now);
|
|
1543
|
+
if (through === null) {
|
|
1544
|
+
const live = liveRead(platform.platform, dayOf(now - 29 * DAY_MS), today, "read only what the question needs");
|
|
1545
|
+
sentences.push(`Nothing is in the copy yet${live}.`);
|
|
1546
|
+
} else if (through < today) {
|
|
1547
|
+
const live = liveRead(platform.platform, dayAfter(through), today, "read only the missing days");
|
|
1548
|
+
sentences.push(
|
|
1549
|
+
`Days after ${through} are not in the copy${live}, or tell the person the numbers stop at ${through}.`
|
|
1550
|
+
);
|
|
1551
|
+
}
|
|
1552
|
+
return sentences;
|
|
1553
|
+
}
|
|
1554
|
+
var GOOGLE_ONLY_READS = /* @__PURE__ */ new Set(["brand-split", "impression-share", "keyword-quality", "google-report", "segments"]);
|
|
1555
|
+
function platformsOfRead(path, body) {
|
|
1556
|
+
const named = typeof body === "object" && body !== null ? body : {};
|
|
1557
|
+
if (Array.isArray(named.platforms) && named.platforms.length > 0) return named.platforms.map(String);
|
|
1558
|
+
if (typeof named.platform === "string") return [named.platform];
|
|
1559
|
+
return GOOGLE_ONLY_READS.has(path) ? ["google"] : void 0;
|
|
1560
|
+
}
|
|
1561
|
+
function syncCoverageHints(status, now, platforms) {
|
|
1562
|
+
return status.platforms.filter((platform) => platform.connected && (platforms === void 0 || platforms.includes(platform.platform))).map((platform) => {
|
|
1563
|
+
const accounts = platform.accounts.filter((account) => account.selected);
|
|
1564
|
+
return [
|
|
1565
|
+
headline(platform, accounts, now),
|
|
1566
|
+
...loadingSentences(platform, accounts),
|
|
1567
|
+
...emptySentences(accounts),
|
|
1568
|
+
...behindSentences(platform, accounts, now)
|
|
1569
|
+
].join(" ");
|
|
1570
|
+
});
|
|
1571
|
+
}
|
|
1572
|
+
|
|
1573
|
+
// src/commands/ads/performance/reportCommand.ts
|
|
1472
1574
|
function failInvalid(issues, args) {
|
|
1473
1575
|
writeJson({ ok: false, error: { code: "INVALID_ARGS", message: invalidArgsMessage(issues, args) } });
|
|
1474
1576
|
process.exit(1);
|
|
1475
1577
|
}
|
|
1476
|
-
function postRead(path, body) {
|
|
1477
|
-
|
|
1578
|
+
async function postRead(path, body) {
|
|
1579
|
+
const response = await retryOnceIfBusy(
|
|
1478
1580
|
() => apiPost(`/api/ads/performance/${path}`, body),
|
|
1479
1581
|
(caught) => caught instanceof Error ? caught.message : ""
|
|
1480
1582
|
);
|
|
1583
|
+
const coverage = response.dataStatus ? syncCoverageHints(response.dataStatus, Date.now(), platformsOfRead(path, body)) : [];
|
|
1584
|
+
return { ok: true, data: response.data, coverage };
|
|
1481
1585
|
}
|
|
1482
1586
|
function failRead(err) {
|
|
1483
1587
|
const refusal = err instanceof ApiError ? adPerformanceRefusal(err.code, err.message) : null;
|
|
@@ -1507,7 +1611,7 @@ function report(spec) {
|
|
|
1507
1611
|
if (!parsed.success) failInvalid(parsed.error.issues, spec.args);
|
|
1508
1612
|
try {
|
|
1509
1613
|
const response = await postRead(spec.path, parsed.data);
|
|
1510
|
-
const hints = spec.hints?.(response.data, parsed.data, args) ?? [];
|
|
1614
|
+
const hints = [...response.coverage, ...spec.hints?.(response.data, parsed.data, args) ?? []];
|
|
1511
1615
|
const data = args.full === true ? response.data : (spec.compact ?? roundDeep)(response.data);
|
|
1512
1616
|
writeJson({ ok: true, data, ...hints.length > 0 ? { hints } : {} });
|
|
1513
1617
|
} catch (err) {
|
|
@@ -1901,8 +2005,8 @@ function evidenceWords(evidence) {
|
|
|
1901
2005
|
evidence.format === "different" ? "different format" : null
|
|
1902
2006
|
].filter((part) => part !== null).join(", ");
|
|
1903
2007
|
}
|
|
1904
|
-
var titleOf = (concept,
|
|
1905
|
-
const words = (concept ??
|
|
2008
|
+
var titleOf = (concept, headline2) => {
|
|
2009
|
+
const words = (concept ?? headline2).trim();
|
|
1906
2010
|
return words.length <= 120 ? words : `${words.slice(0, 119).trimEnd()}\u2026`;
|
|
1907
2011
|
};
|
|
1908
2012
|
function whyWords(choice) {
|
|
@@ -2410,16 +2514,6 @@ var PLATFORM_LABEL = {
|
|
|
2410
2514
|
function platformLabel(platform) {
|
|
2411
2515
|
return PLATFORM_LABEL[platform] ?? platform;
|
|
2412
2516
|
}
|
|
2413
|
-
function historyLoadingHint(status) {
|
|
2414
|
-
if (!status.historyLoading) return null;
|
|
2415
|
-
const loading = status.platforms.flatMap(
|
|
2416
|
-
(platform) => platform.accounts.filter((account) => account.selected && !account.backfillComplete).map(
|
|
2417
|
-
(account) => `${account.name || account.adAccountId} (${platformLabel(platform.platform)}${account.historyFrom ? `, stored from ${account.historyFrom}` : ""})`
|
|
2418
|
-
)
|
|
2419
|
-
);
|
|
2420
|
-
const which = loading.length > 0 ? loading.join(", ") : "some ad accounts";
|
|
2421
|
-
return `History is still loading for ${which}; older periods are incomplete. Say so before comparing them, or read a recent window.`;
|
|
2422
|
-
}
|
|
2423
2517
|
var NO_GOALS_HINT = "No result goals are set, so results are each platform's headline conversions. Run `baker ads performance goals` to see every conversion action each platform reported.";
|
|
2424
2518
|
function resultNounHint(noun) {
|
|
2425
2519
|
if (noun.source === "company")
|
|
@@ -2430,8 +2524,6 @@ function resultNounHint(noun) {
|
|
|
2430
2524
|
}
|
|
2431
2525
|
function overviewHints(data) {
|
|
2432
2526
|
const hints = [];
|
|
2433
|
-
const loading = historyLoadingHint(data.dataStatus);
|
|
2434
|
-
if (loading !== null) hints.push(loading);
|
|
2435
2527
|
if (data.goals.length === 0 && data.platforms.length > 0) hints.push(NO_GOALS_HINT);
|
|
2436
2528
|
const noun = resultNounHint(data.resultNoun);
|
|
2437
2529
|
if (noun !== null) hints.push(noun);
|
|
@@ -2616,15 +2708,7 @@ function splitAdsHints(status) {
|
|
|
2616
2708
|
);
|
|
2617
2709
|
}
|
|
2618
2710
|
function statusHints(status) {
|
|
2619
|
-
|
|
2620
|
-
const loading = historyLoadingHint(status);
|
|
2621
|
-
if (loading !== null) hints.push(loading);
|
|
2622
|
-
for (const platform of status.platforms) {
|
|
2623
|
-
if (platform.connected && platform.error)
|
|
2624
|
-
hints.push(`${platformLabel(platform.platform)} last sync failed: ${platform.error}`);
|
|
2625
|
-
}
|
|
2626
|
-
hints.push(...splitAdsHints(status));
|
|
2627
|
-
return hints;
|
|
2711
|
+
return splitAdsHints(status);
|
|
2628
2712
|
}
|
|
2629
2713
|
|
|
2630
2714
|
// src/commands/ads/performance/accounts.ts
|
|
@@ -2966,7 +3050,7 @@ function targetCommand(spec) {
|
|
|
2966
3050
|
if (!parsed.success) failInvalid(parsed.error.issues, spec.args);
|
|
2967
3051
|
try {
|
|
2968
3052
|
const response = await postRead(spec.name, parsed.data);
|
|
2969
|
-
const hints = spec.hints(response.data);
|
|
3053
|
+
const hints = [...response.coverage, ...spec.hints(response.data)];
|
|
2970
3054
|
const data = args.full === true ? response.data : spec.compact(response.data);
|
|
2971
3055
|
writeJson({ ok: true, data, ...hints.length > 0 ? { hints } : {} });
|
|
2972
3056
|
} catch (err) {
|
|
@@ -3324,7 +3408,7 @@ var creativeAssetsCommand = defineCommand4({
|
|
|
3324
3408
|
if (!parsed.success) failInvalid(parsed.error.issues, CREATIVE_ASSETS_ARGS);
|
|
3325
3409
|
try {
|
|
3326
3410
|
const response = await postRead("creative-assets", parsed.data);
|
|
3327
|
-
const hints = creativeAssetsHints(response.data, parsed.data);
|
|
3411
|
+
const hints = [...response.coverage, ...creativeAssetsHints(response.data, parsed.data)];
|
|
3328
3412
|
const data = args.full === true ? response.data : compactCreativeAssets(response.data);
|
|
3329
3413
|
writeJson({ ok: true, data, ...hints.length > 0 ? { hints } : {} });
|
|
3330
3414
|
} catch (err) {
|
|
@@ -3544,7 +3628,7 @@ var creativeHealthCommand = defineCommand5({
|
|
|
3544
3628
|
}
|
|
3545
3629
|
try {
|
|
3546
3630
|
const response = await postRead("creative-health", parsed.data);
|
|
3547
|
-
const hints = creativeHealthHints(response.data, choice);
|
|
3631
|
+
const hints = [...response.coverage, ...creativeHealthHints(response.data, choice)];
|
|
3548
3632
|
const data = args.full === true ? response.data : compactCreativeHealth(response.data, choice);
|
|
3549
3633
|
writeJson({ ok: true, data, ...hints.length > 0 ? { hints } : {} });
|
|
3550
3634
|
} catch (err) {
|
|
@@ -3776,7 +3860,7 @@ function diagnosticCommand(spec) {
|
|
|
3776
3860
|
if (!parsed.success) failInvalid(parsed.error.issues, spec.args);
|
|
3777
3861
|
try {
|
|
3778
3862
|
const response = await postRead(spec.path, parsed.data);
|
|
3779
|
-
const hints = spec.hints(response.data);
|
|
3863
|
+
const hints = [...response.coverage, ...spec.hints(response.data)];
|
|
3780
3864
|
const data = args.full === true ? response.data : spec.compact(response.data);
|
|
3781
3865
|
writeJson({ ok: true, data, ...hints.length > 0 ? { hints } : {} });
|
|
3782
3866
|
} catch (err) {
|
|
@@ -3957,7 +4041,7 @@ var healthCommand = defineCommand7({
|
|
|
3957
4041
|
if (!parsed.success) failInvalid(parsed.error.issues, HEALTH_ARGS);
|
|
3958
4042
|
try {
|
|
3959
4043
|
const response = await postRead("health", parsed.data);
|
|
3960
|
-
const hints = healthHints(response.data);
|
|
4044
|
+
const hints = [...response.coverage, ...healthHints(response.data)];
|
|
3961
4045
|
const data = args.full === true ? response.data : compactHealth(response.data);
|
|
3962
4046
|
writeJson({ ok: true, data, ...hints.length > 0 ? { hints } : {} });
|
|
3963
4047
|
} catch (err) {
|
|
@@ -4231,7 +4315,7 @@ function peopleCommand(spec) {
|
|
|
4231
4315
|
if (!parsed.success) failInvalid(parsed.error.issues, spec.args);
|
|
4232
4316
|
try {
|
|
4233
4317
|
const response = await postRead(spec.path, parsed.data);
|
|
4234
|
-
const hints = spec.hints(response.data);
|
|
4318
|
+
const hints = [...response.coverage, ...spec.hints(response.data)];
|
|
4235
4319
|
const data = args.full === true ? response.data : spec.compact(response.data);
|
|
4236
4320
|
writeJson({ ok: true, data, ...hints.length > 0 ? { hints } : {} });
|
|
4237
4321
|
} catch (err) {
|
|
@@ -5452,4 +5536,4 @@ Full guide: __tooling__/docs/tools/baker/ads-performance.md`
|
|
|
5452
5536
|
export {
|
|
5453
5537
|
performanceCommand
|
|
5454
5538
|
};
|
|
5455
|
-
//# sourceMappingURL=performance-
|
|
5539
|
+
//# sourceMappingURL=performance-34XRXF2I.js.map
|