@koda-sl/baker-cli 0.5.1 → 0.11.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 +718 -162
- package/dist/cli.js +7 -3
- package/dist/cli.js.map +1 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +7 -1
- package/dist/client.js.map +1 -1
- package/dist/commands/ads/cache.d.ts +7 -0
- package/dist/commands/ads/cache.d.ts.map +1 -0
- package/dist/commands/ads/cache.js +73 -0
- package/dist/commands/ads/cache.js.map +1 -0
- package/dist/commands/ads/cache.test.d.ts +2 -0
- package/dist/commands/ads/cache.test.d.ts.map +1 -0
- package/dist/commands/ads/cache.test.js +44 -0
- package/dist/commands/ads/cache.test.js.map +1 -0
- package/dist/commands/ads/field-descriptions.d.ts +2 -0
- package/dist/commands/ads/field-descriptions.d.ts.map +1 -0
- package/dist/commands/ads/field-descriptions.js +91 -0
- package/dist/commands/ads/field-descriptions.js.map +1 -0
- package/dist/commands/ads/google/accounts.d.ts +14 -0
- package/dist/commands/ads/google/accounts.d.ts.map +1 -0
- package/dist/commands/ads/google/accounts.js +73 -0
- package/dist/commands/ads/google/accounts.js.map +1 -0
- package/dist/commands/ads/google/changes.d.ts +34 -0
- package/dist/commands/ads/google/changes.d.ts.map +1 -0
- package/dist/commands/ads/google/changes.js +80 -0
- package/dist/commands/ads/google/changes.js.map +1 -0
- package/dist/commands/ads/google/correction-table.d.ts +3 -0
- package/dist/commands/ads/google/correction-table.d.ts.map +1 -0
- package/dist/commands/ads/google/correction-table.js +340 -0
- package/dist/commands/ads/google/correction-table.js.map +1 -0
- package/dist/commands/ads/google/currency.d.ts +13 -0
- package/dist/commands/ads/google/currency.d.ts.map +1 -0
- package/dist/commands/ads/google/currency.js +68 -0
- package/dist/commands/ads/google/currency.js.map +1 -0
- package/dist/commands/ads/google/error-parser.d.ts +3 -0
- package/dist/commands/ads/google/error-parser.d.ts.map +1 -0
- package/dist/commands/ads/google/error-parser.js +91 -0
- package/dist/commands/ads/google/error-parser.js.map +1 -0
- package/dist/commands/ads/google/error-parser.test.d.ts +2 -0
- package/dist/commands/ads/google/error-parser.test.d.ts.map +1 -0
- package/dist/commands/ads/google/error-parser.test.js +48 -0
- package/dist/commands/ads/google/error-parser.test.js.map +1 -0
- package/dist/commands/ads/google/index.d.ts +2 -0
- package/dist/commands/ads/google/index.d.ts.map +1 -0
- package/dist/commands/ads/google/index.js +30 -0
- package/dist/commands/ads/google/index.js.map +1 -0
- package/dist/commands/ads/google/keywords/discover.d.ts +49 -0
- package/dist/commands/ads/google/keywords/discover.d.ts.map +1 -0
- package/dist/commands/ads/google/keywords/discover.js +139 -0
- package/dist/commands/ads/google/keywords/discover.js.map +1 -0
- package/dist/commands/ads/google/keywords/index.d.ts +2 -0
- package/dist/commands/ads/google/keywords/index.d.ts.map +1 -0
- package/dist/commands/ads/google/keywords/index.js +18 -0
- package/dist/commands/ads/google/keywords/index.js.map +1 -0
- package/dist/commands/ads/google/keywords/metrics.d.ts +34 -0
- package/dist/commands/ads/google/keywords/metrics.d.ts.map +1 -0
- package/dist/commands/ads/google/keywords/metrics.js +111 -0
- package/dist/commands/ads/google/keywords/metrics.js.map +1 -0
- package/dist/commands/ads/google/preflight.d.ts +3 -0
- package/dist/commands/ads/google/preflight.d.ts.map +1 -0
- package/dist/commands/ads/google/preflight.js +115 -0
- package/dist/commands/ads/google/preflight.js.map +1 -0
- package/dist/commands/ads/google/preflight.test.d.ts +2 -0
- package/dist/commands/ads/google/preflight.test.d.ts.map +1 -0
- package/dist/commands/ads/google/preflight.test.js +50 -0
- package/dist/commands/ads/google/preflight.test.js.map +1 -0
- package/dist/commands/ads/google/presets.d.ts +12 -0
- package/dist/commands/ads/google/presets.d.ts.map +1 -0
- package/dist/commands/ads/google/presets.js +71 -0
- package/dist/commands/ads/google/presets.js.map +1 -0
- package/dist/commands/ads/google/presets.test.d.ts +2 -0
- package/dist/commands/ads/google/presets.test.d.ts.map +1 -0
- package/dist/commands/ads/google/presets.test.js +79 -0
- package/dist/commands/ads/google/presets.test.js.map +1 -0
- package/dist/commands/ads/google/query.d.ts +64 -0
- package/dist/commands/ads/google/query.d.ts.map +1 -0
- package/dist/commands/ads/google/query.js +304 -0
- package/dist/commands/ads/google/query.js.map +1 -0
- package/dist/commands/ads/index.d.ts +2 -0
- package/dist/commands/ads/index.d.ts.map +1 -0
- package/dist/commands/ads/index.js +19 -0
- package/dist/commands/ads/index.js.map +1 -0
- package/dist/commands/ads/output.d.ts +17 -0
- package/dist/commands/ads/output.d.ts.map +1 -0
- package/dist/commands/ads/output.js +78 -0
- package/dist/commands/ads/output.js.map +1 -0
- package/dist/commands/ads/output.test.d.ts +2 -0
- package/dist/commands/ads/output.test.d.ts.map +1 -0
- package/dist/commands/ads/output.test.js +100 -0
- package/dist/commands/ads/output.test.js.map +1 -0
- package/dist/commands/ads/types.d.ts +69 -0
- package/dist/commands/ads/types.d.ts.map +1 -0
- package/dist/commands/ads/types.js +2 -0
- package/dist/commands/ads/types.js.map +1 -0
- package/dist/commands/research/advertisers.d.ts +34 -0
- package/dist/commands/research/advertisers.d.ts.map +1 -0
- package/dist/commands/research/advertisers.js +75 -0
- package/dist/commands/research/advertisers.js.map +1 -0
- package/dist/commands/research/countries.d.ts +2 -0
- package/dist/commands/research/countries.d.ts.map +1 -0
- package/dist/commands/research/countries.js +69 -0
- package/dist/commands/research/countries.js.map +1 -0
- package/dist/commands/research/index.d.ts +2 -0
- package/dist/commands/research/index.d.ts.map +1 -0
- package/dist/commands/research/index.js +40 -0
- package/dist/commands/research/index.js.map +1 -0
- package/dist/commands/research/intent.d.ts +24 -0
- package/dist/commands/research/intent.d.ts.map +1 -0
- package/dist/commands/research/intent.js +71 -0
- package/dist/commands/research/intent.js.map +1 -0
- package/dist/commands/research/keyword-gap.d.ts +49 -0
- package/dist/commands/research/keyword-gap.d.ts.map +1 -0
- package/dist/commands/research/keyword-gap.js +99 -0
- package/dist/commands/research/keyword-gap.js.map +1 -0
- package/dist/commands/research/keywords-for-site.d.ts +44 -0
- package/dist/commands/research/keywords-for-site.d.ts.map +1 -0
- package/dist/commands/research/keywords-for-site.js +83 -0
- package/dist/commands/research/keywords-for-site.js.map +1 -0
- package/dist/commands/research/languages.d.ts +2 -0
- package/dist/commands/research/languages.d.ts.map +1 -0
- package/dist/commands/research/languages.js +42 -0
- package/dist/commands/research/languages.js.map +1 -0
- package/dist/commands/research/lighthouse.d.ts +24 -0
- package/dist/commands/research/lighthouse.d.ts.map +1 -0
- package/dist/commands/research/lighthouse.js +60 -0
- package/dist/commands/research/lighthouse.js.map +1 -0
- package/dist/commands/research/output.d.ts +29 -0
- package/dist/commands/research/output.d.ts.map +1 -0
- package/dist/commands/research/output.js +81 -0
- package/dist/commands/research/output.js.map +1 -0
- package/dist/env.d.ts +1 -0
- package/dist/env.d.ts.map +1 -1
- package/dist/env.js +5 -1
- package/dist/env.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { defineCommand } from "citty";
|
|
2
|
+
import { accountsCommand } from "./accounts.js";
|
|
3
|
+
import { changesCommand } from "./changes.js";
|
|
4
|
+
import { currencyCommand } from "./currency.js";
|
|
5
|
+
import { keywordsCommand } from "./keywords/index.js";
|
|
6
|
+
import { queryCommand } from "./query.js";
|
|
7
|
+
export const googleCommand = defineCommand({
|
|
8
|
+
meta: {
|
|
9
|
+
name: "google",
|
|
10
|
+
description: `Google Ads commands. Query campaigns, keywords, search terms, and more via GAQL.
|
|
11
|
+
|
|
12
|
+
Start here:
|
|
13
|
+
baker ads google accounts — list accessible accounts
|
|
14
|
+
baker ads google query --list-presets — see available query templates
|
|
15
|
+
|
|
16
|
+
Examples:
|
|
17
|
+
baker ads google query "SELECT campaign.name, metrics.clicks FROM campaign WHERE segments.date DURING LAST_7_DAYS" --customer-id 1234567890
|
|
18
|
+
baker ads google query --preset campaign-performance --customer-id 1234567890
|
|
19
|
+
baker ads google currency --customer-id 1234567890
|
|
20
|
+
baker ads google keywords discover --customer-id 1234567890 --seeds "running shoes"`,
|
|
21
|
+
},
|
|
22
|
+
subCommands: {
|
|
23
|
+
accounts: accountsCommand,
|
|
24
|
+
currency: currencyCommand,
|
|
25
|
+
changes: changesCommand,
|
|
26
|
+
query: queryCommand,
|
|
27
|
+
keywords: keywordsCommand,
|
|
28
|
+
},
|
|
29
|
+
});
|
|
30
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/commands/ads/google/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AACtC,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE1C,MAAM,CAAC,MAAM,aAAa,GAAG,aAAa,CAAC;IACzC,IAAI,EAAE;QACJ,IAAI,EAAE,QAAQ;QACd,WAAW,EAAE;;;;;;;;;;sFAUqE;KACnF;IACD,WAAW,EAAE;QACX,QAAQ,EAAE,eAAe;QACzB,QAAQ,EAAE,eAAe;QACzB,OAAO,EAAE,cAAc;QACvB,KAAK,EAAE,YAAY;QACnB,QAAQ,EAAE,eAAe;KAC1B;CACF,CAAC,CAAC"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
export declare const discoverCommand: import("citty").CommandDef<{
|
|
2
|
+
readonly "customer-id": {
|
|
3
|
+
readonly type: "string";
|
|
4
|
+
readonly description: "Google Ads customer ID";
|
|
5
|
+
readonly required: false;
|
|
6
|
+
};
|
|
7
|
+
readonly seeds: {
|
|
8
|
+
readonly type: "string";
|
|
9
|
+
readonly description: "Comma-separated seed keywords";
|
|
10
|
+
readonly required: false;
|
|
11
|
+
};
|
|
12
|
+
readonly url: {
|
|
13
|
+
readonly type: "string";
|
|
14
|
+
readonly description: "URL to extract ideas from";
|
|
15
|
+
readonly required: false;
|
|
16
|
+
};
|
|
17
|
+
readonly location: {
|
|
18
|
+
readonly type: "string";
|
|
19
|
+
readonly description: "Geo target ID (default 2840)";
|
|
20
|
+
readonly required: false;
|
|
21
|
+
};
|
|
22
|
+
readonly language: {
|
|
23
|
+
readonly type: "string";
|
|
24
|
+
readonly description: "Language ID (default 1000)";
|
|
25
|
+
readonly required: false;
|
|
26
|
+
};
|
|
27
|
+
readonly limit: {
|
|
28
|
+
readonly type: "string";
|
|
29
|
+
readonly description: "Max results (default 20)";
|
|
30
|
+
readonly required: false;
|
|
31
|
+
};
|
|
32
|
+
readonly cursor: {
|
|
33
|
+
readonly type: "string";
|
|
34
|
+
readonly description: "Pagination cursor from previous response";
|
|
35
|
+
readonly required: false;
|
|
36
|
+
};
|
|
37
|
+
readonly "no-cache": {
|
|
38
|
+
readonly type: "boolean";
|
|
39
|
+
readonly description: "Skip cache";
|
|
40
|
+
readonly required: false;
|
|
41
|
+
};
|
|
42
|
+
readonly output: {
|
|
43
|
+
readonly type: "string";
|
|
44
|
+
readonly description: "Format: json|csv|jsonl|md";
|
|
45
|
+
readonly required: false;
|
|
46
|
+
readonly default: "json";
|
|
47
|
+
};
|
|
48
|
+
}>;
|
|
49
|
+
//# sourceMappingURL=discover.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"discover.d.ts","sourceRoot":"","sources":["../../../../../src/commands/ads/google/keywords/discover.ts"],"names":[],"mappings":"AAiGA,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA8D1B,CAAC"}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { defineCommand } from "citty";
|
|
2
|
+
import { ApiError, apiPost } from "../../../../client.js";
|
|
3
|
+
import { registerSchema } from "../../../../schemas.js";
|
|
4
|
+
import { buildQueryCacheKey, cacheGet, cacheSet, getManagerIdForCustomer } from "../../cache.js";
|
|
5
|
+
import { resolveCustomerId, writeAdsJson, writeAdsOutput } from "../../output.js";
|
|
6
|
+
registerSchema({
|
|
7
|
+
command: "ads.google.keywords.discover",
|
|
8
|
+
description: "Discover keyword ideas from seed keywords or URLs. Returns { keywords, total_results, next_page_token? } with snake_case fields matching the Google Ads API.",
|
|
9
|
+
args: {
|
|
10
|
+
"customer-id": {
|
|
11
|
+
type: "string",
|
|
12
|
+
description: "Google Ads customer ID (10 digits, no dashes). Falls back to BAKER_GOOGLE_ADS_CUSTOMER_ID env var.",
|
|
13
|
+
required: false,
|
|
14
|
+
},
|
|
15
|
+
seeds: { type: "string", description: "Comma-separated seed keywords (max 10)", required: false },
|
|
16
|
+
url: { type: "string", description: "URL to extract keyword ideas from", required: false },
|
|
17
|
+
location: { type: "string", description: "Geo target ID (default: 2840 for US)", required: false },
|
|
18
|
+
language: { type: "string", description: "Language ID (default: 1000 for English)", required: false },
|
|
19
|
+
limit: { type: "string", description: "Max keyword ideas (default: 20, max: 100)", required: false, default: 20 },
|
|
20
|
+
cursor: {
|
|
21
|
+
type: "string",
|
|
22
|
+
description: "Pagination cursor from previous response (next_page_token)",
|
|
23
|
+
required: false,
|
|
24
|
+
},
|
|
25
|
+
output: { type: "string", description: "Format: json|csv|jsonl|md", required: false, default: "json" },
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
const KEYWORD_IDEA_FIELDS = {
|
|
29
|
+
keyword: "Suggested keyword text",
|
|
30
|
+
avg_monthly_searches: "Average monthly search volume (string)",
|
|
31
|
+
competition: "Competition level: LOW, MEDIUM, HIGH",
|
|
32
|
+
competition_index: "Competition index 0-100 as string (higher = more competitive)",
|
|
33
|
+
low_top_of_page_bid_micros: "Low-range CPC bid in micros (÷ 1,000,000 for currency)",
|
|
34
|
+
high_top_of_page_bid_micros: "High-range CPC bid in micros (÷ 1,000,000 for currency)",
|
|
35
|
+
monthly_search_volumes: "Monthly search volume breakdown (array of {year, month, monthly_searches})",
|
|
36
|
+
};
|
|
37
|
+
function buildDiscoverBody(customerId, args) {
|
|
38
|
+
const body = {
|
|
39
|
+
customerId,
|
|
40
|
+
pageSize: args.limit ? Number(args.limit) : 20,
|
|
41
|
+
};
|
|
42
|
+
const managerId = getManagerIdForCustomer(customerId);
|
|
43
|
+
if (managerId)
|
|
44
|
+
body.managerId = managerId;
|
|
45
|
+
const seeds = args.seeds
|
|
46
|
+
? args.seeds
|
|
47
|
+
.split(",")
|
|
48
|
+
.map((s) => s.trim())
|
|
49
|
+
.filter(Boolean)
|
|
50
|
+
: undefined;
|
|
51
|
+
if (seeds)
|
|
52
|
+
body.seeds = seeds;
|
|
53
|
+
if (args.url)
|
|
54
|
+
body.url = args.url;
|
|
55
|
+
if (args.location)
|
|
56
|
+
body.locationIds = [args.location];
|
|
57
|
+
if (args.language)
|
|
58
|
+
body.languageId = args.language;
|
|
59
|
+
if (args.cursor)
|
|
60
|
+
body.pageToken = args.cursor;
|
|
61
|
+
return body;
|
|
62
|
+
}
|
|
63
|
+
function handleKeywordError(err) {
|
|
64
|
+
if (err instanceof ApiError) {
|
|
65
|
+
const envelope = {
|
|
66
|
+
ok: false,
|
|
67
|
+
error: {
|
|
68
|
+
code: err.code,
|
|
69
|
+
message: err.message,
|
|
70
|
+
fix: { action: "reject", explanation: err.message },
|
|
71
|
+
retryable: err.code === "RATE_LIMITED",
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
writeAdsJson(envelope);
|
|
75
|
+
process.exit(1);
|
|
76
|
+
}
|
|
77
|
+
writeAdsJson({ ok: false, error: { code: "NETWORK_ERROR", message: "Unexpected error" } });
|
|
78
|
+
process.exit(1);
|
|
79
|
+
}
|
|
80
|
+
export const discoverCommand = defineCommand({
|
|
81
|
+
meta: {
|
|
82
|
+
name: "discover",
|
|
83
|
+
description: `Discover new keyword ideas from seed keywords or competitor URLs.
|
|
84
|
+
|
|
85
|
+
Examples:
|
|
86
|
+
baker ads google keywords discover --customer-id 1234567890 --seeds "running shoes,athletic footwear"
|
|
87
|
+
baker ads google keywords discover --customer-id 1234567890 --url "https://competitor.com"
|
|
88
|
+
baker ads google keywords discover --customer-id 1234567890 --seeds "seo tools" --location 2826 --limit 50`,
|
|
89
|
+
},
|
|
90
|
+
args: {
|
|
91
|
+
"customer-id": { type: "string", description: "Google Ads customer ID", required: false },
|
|
92
|
+
seeds: { type: "string", description: "Comma-separated seed keywords", required: false },
|
|
93
|
+
url: { type: "string", description: "URL to extract ideas from", required: false },
|
|
94
|
+
location: { type: "string", description: "Geo target ID (default 2840)", required: false },
|
|
95
|
+
language: { type: "string", description: "Language ID (default 1000)", required: false },
|
|
96
|
+
limit: { type: "string", description: "Max results (default 20)", required: false },
|
|
97
|
+
cursor: { type: "string", description: "Pagination cursor from previous response", required: false },
|
|
98
|
+
"no-cache": { type: "boolean", description: "Skip cache", required: false },
|
|
99
|
+
output: { type: "string", description: "Format: json|csv|jsonl|md", required: false, default: "json" },
|
|
100
|
+
},
|
|
101
|
+
run: async ({ args }) => {
|
|
102
|
+
const customerId = resolveCustomerId(args);
|
|
103
|
+
if (!args.seeds && !args.url) {
|
|
104
|
+
writeAdsJson({
|
|
105
|
+
ok: false,
|
|
106
|
+
error: { code: "VALIDATION_ERROR", message: "Provide --seeds or --url (at least one required)" },
|
|
107
|
+
});
|
|
108
|
+
process.exit(1);
|
|
109
|
+
}
|
|
110
|
+
const body = buildDiscoverBody(customerId, args);
|
|
111
|
+
const useCache = !args["no-cache"] && !args.cursor;
|
|
112
|
+
if (!useCache)
|
|
113
|
+
body.skipCache = true;
|
|
114
|
+
const cacheKey = buildQueryCacheKey(customerId, JSON.stringify(body));
|
|
115
|
+
if (useCache) {
|
|
116
|
+
const cached = cacheGet("keywords", cacheKey);
|
|
117
|
+
if (cached) {
|
|
118
|
+
writeAdsJson({ ok: true, data: cached.data, cached: true });
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
try {
|
|
123
|
+
const data = await apiPost("/api/ads/google/keywords/discover", body);
|
|
124
|
+
if (useCache) {
|
|
125
|
+
cacheSet("keywords", cacheKey, data, 24 * 60 * 60 * 1000);
|
|
126
|
+
}
|
|
127
|
+
const format = args.output || "json";
|
|
128
|
+
if (format !== "json") {
|
|
129
|
+
writeAdsOutput(data.keywords, format);
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
writeAdsJson({ ok: true, data, fields: KEYWORD_IDEA_FIELDS });
|
|
133
|
+
}
|
|
134
|
+
catch (err) {
|
|
135
|
+
handleKeywordError(err);
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
});
|
|
139
|
+
//# sourceMappingURL=discover.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"discover.js","sourceRoot":"","sources":["../../../../../src/commands/ads/google/keywords/discover.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/commands/ads/google/keywords/index.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,eAAe,qDAa1B,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { defineCommand } from "citty";
|
|
2
|
+
import { discoverCommand } from "./discover.js";
|
|
3
|
+
import { metricsCommand } from "./metrics.js";
|
|
4
|
+
export const keywordsCommand = defineCommand({
|
|
5
|
+
meta: {
|
|
6
|
+
name: "keywords",
|
|
7
|
+
description: `Keyword research tools. Subcommands: discover, metrics.
|
|
8
|
+
|
|
9
|
+
Examples:
|
|
10
|
+
baker ads google keywords discover --customer-id 1234567890 --seeds "running shoes"
|
|
11
|
+
baker ads google keywords metrics --customer-id 1234567890 --keywords "running shoes,nike shoes"`,
|
|
12
|
+
},
|
|
13
|
+
subCommands: {
|
|
14
|
+
discover: discoverCommand,
|
|
15
|
+
metrics: metricsCommand,
|
|
16
|
+
},
|
|
17
|
+
});
|
|
18
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../src/commands/ads/google/keywords/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AACtC,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAE9C,MAAM,CAAC,MAAM,eAAe,GAAG,aAAa,CAAC;IAC3C,IAAI,EAAE;QACJ,IAAI,EAAE,UAAU;QAChB,WAAW,EAAE;;;;mGAIkF;KAChG;IACD,WAAW,EAAE;QACX,QAAQ,EAAE,eAAe;QACzB,OAAO,EAAE,cAAc;KACxB;CACF,CAAC,CAAC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export declare const metricsCommand: import("citty").CommandDef<{
|
|
2
|
+
readonly "customer-id": {
|
|
3
|
+
readonly type: "string";
|
|
4
|
+
readonly description: "Google Ads customer ID";
|
|
5
|
+
readonly required: false;
|
|
6
|
+
};
|
|
7
|
+
readonly keywords: {
|
|
8
|
+
readonly type: "string";
|
|
9
|
+
readonly description: "Comma-separated keywords";
|
|
10
|
+
readonly required: true;
|
|
11
|
+
};
|
|
12
|
+
readonly location: {
|
|
13
|
+
readonly type: "string";
|
|
14
|
+
readonly description: "Geo target ID (default 2840)";
|
|
15
|
+
readonly required: false;
|
|
16
|
+
};
|
|
17
|
+
readonly language: {
|
|
18
|
+
readonly type: "string";
|
|
19
|
+
readonly description: "Language ID (default 1000)";
|
|
20
|
+
readonly required: false;
|
|
21
|
+
};
|
|
22
|
+
readonly "no-cache": {
|
|
23
|
+
readonly type: "boolean";
|
|
24
|
+
readonly description: "Skip cache";
|
|
25
|
+
readonly required: false;
|
|
26
|
+
};
|
|
27
|
+
readonly output: {
|
|
28
|
+
readonly type: "string";
|
|
29
|
+
readonly description: "Format: json|csv|jsonl|md";
|
|
30
|
+
readonly required: false;
|
|
31
|
+
readonly default: "json";
|
|
32
|
+
};
|
|
33
|
+
}>;
|
|
34
|
+
//# sourceMappingURL=metrics.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metrics.d.ts","sourceRoot":"","sources":["../../../../../src/commands/ads/google/keywords/metrics.ts"],"names":[],"mappings":"AAsCA,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA6FzB,CAAC"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { defineCommand } from "citty";
|
|
2
|
+
import { ApiError, apiPost } from "../../../../client.js";
|
|
3
|
+
import { registerSchema } from "../../../../schemas.js";
|
|
4
|
+
import { buildQueryCacheKey, cacheGet, cacheSet, getManagerIdForCustomer } from "../../cache.js";
|
|
5
|
+
import { resolveCustomerId, writeAdsJson, writeAdsOutput } from "../../output.js";
|
|
6
|
+
registerSchema({
|
|
7
|
+
command: "ads.google.keywords.metrics",
|
|
8
|
+
description: "Get historical metrics for specific keywords. Returns { historical_metrics: [...] } with snake_case fields matching the Google Ads API.",
|
|
9
|
+
args: {
|
|
10
|
+
"customer-id": {
|
|
11
|
+
type: "string",
|
|
12
|
+
description: "Google Ads customer ID (10 digits, no dashes). Falls back to BAKER_GOOGLE_ADS_CUSTOMER_ID env var.",
|
|
13
|
+
required: false,
|
|
14
|
+
},
|
|
15
|
+
keywords: { type: "string", description: "Comma-separated keywords to analyze (max 200)", required: true },
|
|
16
|
+
location: { type: "string", description: "Geo target ID (default: 2840 for US)", required: false },
|
|
17
|
+
language: { type: "string", description: "Language ID (default: 1000 for English)", required: false },
|
|
18
|
+
output: { type: "string", description: "Format: json|csv|jsonl|md", required: false, default: "json" },
|
|
19
|
+
},
|
|
20
|
+
});
|
|
21
|
+
export const metricsCommand = defineCommand({
|
|
22
|
+
meta: {
|
|
23
|
+
name: "metrics",
|
|
24
|
+
description: `Get historical search metrics for specific keywords.
|
|
25
|
+
|
|
26
|
+
Examples:
|
|
27
|
+
baker ads google keywords metrics --customer-id 1234567890 --keywords "running shoes,nike shoes,adidas shoes"
|
|
28
|
+
baker ads google keywords metrics --customer-id 1234567890 --keywords "seo tools" --location 2826`,
|
|
29
|
+
},
|
|
30
|
+
args: {
|
|
31
|
+
"customer-id": { type: "string", description: "Google Ads customer ID", required: false },
|
|
32
|
+
keywords: { type: "string", description: "Comma-separated keywords", required: true },
|
|
33
|
+
location: { type: "string", description: "Geo target ID (default 2840)", required: false },
|
|
34
|
+
language: { type: "string", description: "Language ID (default 1000)", required: false },
|
|
35
|
+
"no-cache": { type: "boolean", description: "Skip cache", required: false },
|
|
36
|
+
output: { type: "string", description: "Format: json|csv|jsonl|md", required: false, default: "json" },
|
|
37
|
+
},
|
|
38
|
+
run: async ({ args }) => {
|
|
39
|
+
const customerId = resolveCustomerId(args);
|
|
40
|
+
const keywords = args.keywords
|
|
41
|
+
.split(",")
|
|
42
|
+
.map((k) => k.trim())
|
|
43
|
+
.filter(Boolean);
|
|
44
|
+
if (keywords.length === 0) {
|
|
45
|
+
writeAdsJson({ ok: false, error: { code: "VALIDATION_ERROR", message: "At least one keyword is required" } });
|
|
46
|
+
process.exit(1);
|
|
47
|
+
}
|
|
48
|
+
if (keywords.length > 200) {
|
|
49
|
+
writeAdsJson({ ok: false, error: { code: "VALIDATION_ERROR", message: "Maximum 200 keywords per request" } });
|
|
50
|
+
process.exit(1);
|
|
51
|
+
}
|
|
52
|
+
const body = { customerId, keywords };
|
|
53
|
+
const managerId = getManagerIdForCustomer(customerId);
|
|
54
|
+
if (managerId)
|
|
55
|
+
body.managerId = managerId;
|
|
56
|
+
if (args.location)
|
|
57
|
+
body.locationIds = [args.location];
|
|
58
|
+
if (args.language)
|
|
59
|
+
body.languageId = args.language;
|
|
60
|
+
const useCache = !args["no-cache"];
|
|
61
|
+
if (!useCache)
|
|
62
|
+
body.skipCache = true;
|
|
63
|
+
const cacheKey = buildQueryCacheKey(customerId, JSON.stringify(body));
|
|
64
|
+
if (useCache) {
|
|
65
|
+
const cached = cacheGet("keywords", cacheKey);
|
|
66
|
+
if (cached) {
|
|
67
|
+
writeAdsJson({ ok: true, data: cached.data, cached: true });
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
try {
|
|
72
|
+
const data = await apiPost("/api/ads/google/keywords/metrics", body);
|
|
73
|
+
if (useCache) {
|
|
74
|
+
cacheSet("keywords", cacheKey, data, 24 * 60 * 60 * 1000);
|
|
75
|
+
}
|
|
76
|
+
const format = args.output || "json";
|
|
77
|
+
if (format !== "json") {
|
|
78
|
+
writeAdsOutput(data.historical_metrics, format);
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
const fields = {
|
|
82
|
+
keyword: "The keyword analyzed",
|
|
83
|
+
avg_monthly_searches: "Average monthly search volume as string (last 12 months)",
|
|
84
|
+
competition: "Competition level: LOW, MEDIUM, HIGH",
|
|
85
|
+
competition_index: "Competition index 0-100 as string (higher = more competitive)",
|
|
86
|
+
low_top_of_page_bid_micros: "Low-range CPC bid in micros (÷ 1,000,000 for currency)",
|
|
87
|
+
high_top_of_page_bid_micros: "High-range CPC bid in micros (÷ 1,000,000 for currency)",
|
|
88
|
+
monthly_search_volumes: "Monthly search volume breakdown (array of {year, month, monthly_searches})",
|
|
89
|
+
};
|
|
90
|
+
writeAdsJson({ ok: true, data, fields });
|
|
91
|
+
}
|
|
92
|
+
catch (err) {
|
|
93
|
+
if (err instanceof ApiError) {
|
|
94
|
+
const envelope = {
|
|
95
|
+
ok: false,
|
|
96
|
+
error: {
|
|
97
|
+
code: err.code,
|
|
98
|
+
message: err.message,
|
|
99
|
+
fix: { action: "reject", explanation: err.message },
|
|
100
|
+
retryable: err.code === "RATE_LIMITED",
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
writeAdsJson(envelope);
|
|
104
|
+
process.exit(1);
|
|
105
|
+
}
|
|
106
|
+
writeAdsJson({ ok: false, error: { code: "NETWORK_ERROR", message: "Unexpected error" } });
|
|
107
|
+
process.exit(1);
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
});
|
|
111
|
+
//# sourceMappingURL=metrics.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metrics.js","sourceRoot":"","sources":["../../../../../src/commands/ads/google/keywords/metrics.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preflight.d.ts","sourceRoot":"","sources":["../../../../src/commands/ads/google/preflight.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAc,eAAe,EAAE,MAAM,aAAa,CAAC;AAkD/D,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,eAAe,CAkFpG"}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
function buildCommand(query, customerId) {
|
|
2
|
+
return `baker ads google query "${query}" --customer-id ${customerId}`;
|
|
3
|
+
}
|
|
4
|
+
function applyAutoFixes(query, limit) {
|
|
5
|
+
let corrected = query;
|
|
6
|
+
const warnings = [];
|
|
7
|
+
// keyword.text → ad_group_criterion.keyword.text
|
|
8
|
+
if (/\bkeyword\.text\b/.test(corrected) && !/ad_group_criterion\.keyword\.text/.test(corrected)) {
|
|
9
|
+
corrected = corrected.replace(/\bkeyword\.text\b/g, "ad_group_criterion.keyword.text");
|
|
10
|
+
warnings.push({ code: "FIELD_RENAMED", message: "keyword.text → ad_group_criterion.keyword.text" });
|
|
11
|
+
}
|
|
12
|
+
// keyword.match_type → ad_group_criterion.keyword.match_type
|
|
13
|
+
if (/\bkeyword\.match_type\b/.test(corrected) && !/ad_group_criterion\.keyword\.match_type/.test(corrected)) {
|
|
14
|
+
corrected = corrected.replace(/\bkeyword\.match_type\b/g, "ad_group_criterion.keyword.match_type");
|
|
15
|
+
warnings.push({ code: "FIELD_RENAMED", message: "keyword.match_type → ad_group_criterion.keyword.match_type" });
|
|
16
|
+
}
|
|
17
|
+
// shopping_performance_view.product_* → segments.product_*
|
|
18
|
+
if (/shopping_performance_view\.product_\w+/.test(corrected)) {
|
|
19
|
+
corrected = corrected.replace(/shopping_performance_view\.product_(\w+)/g, "segments.product_$1");
|
|
20
|
+
warnings.push({ code: "FIELD_RENAMED", message: "shopping_performance_view.product_* → segments.product_*" });
|
|
21
|
+
}
|
|
22
|
+
// campaign.status = 'ACTIVE' → 'ENABLED'
|
|
23
|
+
if (/campaign\.status\s*=\s*'ACTIVE'/i.test(corrected)) {
|
|
24
|
+
corrected = corrected.replace(/campaign\.status\s*=\s*'ACTIVE'/gi, "campaign.status = 'ENABLED'");
|
|
25
|
+
warnings.push({ code: "ENUM_CORRECTED", message: "campaign.status ACTIVE → ENABLED" });
|
|
26
|
+
}
|
|
27
|
+
// ad_group.status = 'ACTIVE' → 'ENABLED'
|
|
28
|
+
if (/ad_group\.status\s*=\s*'ACTIVE'/i.test(corrected)) {
|
|
29
|
+
corrected = corrected.replace(/ad_group\.status\s*=\s*'ACTIVE'/gi, "ad_group.status = 'ENABLED'");
|
|
30
|
+
warnings.push({ code: "ENUM_CORRECTED", message: "ad_group.status ACTIVE → ENABLED" });
|
|
31
|
+
}
|
|
32
|
+
// Missing LIMIT
|
|
33
|
+
if (!/\bLIMIT\b/i.test(corrected)) {
|
|
34
|
+
const effectiveLimit = limit ?? 200;
|
|
35
|
+
corrected = `${corrected.trimEnd()} LIMIT ${effectiveLimit}`;
|
|
36
|
+
warnings.push({ code: "AUTO_LIMIT", message: `Added LIMIT ${effectiveLimit}. Use --limit to override.` });
|
|
37
|
+
}
|
|
38
|
+
return { query: corrected, warnings };
|
|
39
|
+
}
|
|
40
|
+
export function validatePreflight(query, customerId, limit) {
|
|
41
|
+
const warnings = [];
|
|
42
|
+
const corrected = query;
|
|
43
|
+
// REJECT: Non-SELECT queries
|
|
44
|
+
const firstWord = corrected.trim().split(/\s+/)[0]?.toUpperCase();
|
|
45
|
+
if (firstWord && firstWord !== "SELECT") {
|
|
46
|
+
return {
|
|
47
|
+
valid: false,
|
|
48
|
+
warnings: [],
|
|
49
|
+
error: {
|
|
50
|
+
ok: false,
|
|
51
|
+
error: {
|
|
52
|
+
code: "READ_ONLY",
|
|
53
|
+
message: `Only SELECT queries are allowed. Got: ${firstWord}`,
|
|
54
|
+
fix: {
|
|
55
|
+
action: "reject",
|
|
56
|
+
explanation: "This CLI only supports GAQL SELECT queries for reading data",
|
|
57
|
+
},
|
|
58
|
+
retryable: false,
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
// REJECT: CONTAINS usage
|
|
64
|
+
const containsMatch = corrected.match(/CONTAINS\s*\(\s*([^,]+),\s*'([^']+)'\s*\)/i);
|
|
65
|
+
if (containsMatch) {
|
|
66
|
+
const field = containsMatch[1]?.trim();
|
|
67
|
+
const value = containsMatch[2] ?? "";
|
|
68
|
+
const fixed = corrected.replace(/CONTAINS\s*\(\s*[^,]+,\s*'[^']+'\s*\)/i, `${field} LIKE '%${value}%'`);
|
|
69
|
+
return {
|
|
70
|
+
valid: false,
|
|
71
|
+
warnings: [],
|
|
72
|
+
error: {
|
|
73
|
+
ok: false,
|
|
74
|
+
error: {
|
|
75
|
+
code: "INVALID_OPERATOR",
|
|
76
|
+
message: "GAQL does not support CONTAINS. Use LIKE with % wildcards.",
|
|
77
|
+
fix: {
|
|
78
|
+
action: "change_operator",
|
|
79
|
+
correctedCommand: buildCommand(fixed, customerId),
|
|
80
|
+
explanation: "GAQL uses LIKE '%value%' for substring matching, not CONTAINS()",
|
|
81
|
+
},
|
|
82
|
+
retryable: false,
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
// REJECT: Open-ended date range
|
|
88
|
+
const openDateMatch = corrected.match(/segments\.date\s*>=\s*'(\d{4}-\d{2}-\d{2})'/i);
|
|
89
|
+
if (openDateMatch && !/BETWEEN/i.test(corrected)) {
|
|
90
|
+
const startDate = openDateMatch[1] ?? "";
|
|
91
|
+
const today = new Date().toISOString().slice(0, 10);
|
|
92
|
+
const fixed = corrected.replace(/segments\.date\s*>=\s*'(\d{4}-\d{2}-\d{2})'/i, `segments.date BETWEEN '${startDate}' AND '${today}'`);
|
|
93
|
+
return {
|
|
94
|
+
valid: false,
|
|
95
|
+
warnings: [],
|
|
96
|
+
error: {
|
|
97
|
+
ok: false,
|
|
98
|
+
error: {
|
|
99
|
+
code: "OPEN_ENDED_DATE",
|
|
100
|
+
message: "Open-ended date ranges are not supported. Use BETWEEN or DURING.",
|
|
101
|
+
fix: {
|
|
102
|
+
action: "narrow_date_range",
|
|
103
|
+
correctedCommand: buildCommand(fixed, customerId),
|
|
104
|
+
explanation: "Use BETWEEN with explicit start and end dates",
|
|
105
|
+
},
|
|
106
|
+
retryable: false,
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
const { query: autoFixed, warnings: autoFixWarnings } = applyAutoFixes(corrected, limit);
|
|
112
|
+
warnings.push(...autoFixWarnings);
|
|
113
|
+
return { valid: true, correctedQuery: autoFixed, warnings };
|
|
114
|
+
}
|
|
115
|
+
//# sourceMappingURL=preflight.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preflight.js","sourceRoot":"","sources":["../../../../src/commands/ads/google/preflight.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preflight.test.d.ts","sourceRoot":"","sources":["../../../../src/commands/ads/google/preflight.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { validatePreflight } from "./preflight.js";
|
|
3
|
+
const CID = "123-456-7890";
|
|
4
|
+
describe("validatePreflight", () => {
|
|
5
|
+
it("rejects non-SELECT queries with READ_ONLY error", () => {
|
|
6
|
+
const result = validatePreflight("INSERT INTO campaign VALUES ('test')", CID);
|
|
7
|
+
expect(result.valid).toBe(false);
|
|
8
|
+
expect(result.error?.error.code).toBe("READ_ONLY");
|
|
9
|
+
});
|
|
10
|
+
it("rejects CONTAINS with INVALID_OPERATOR and suggests LIKE", () => {
|
|
11
|
+
const result = validatePreflight("SELECT campaign.name FROM campaign WHERE CONTAINS(campaign.name, 'test')", CID);
|
|
12
|
+
expect(result.valid).toBe(false);
|
|
13
|
+
expect(result.error?.error.code).toBe("INVALID_OPERATOR");
|
|
14
|
+
expect(result.error?.error.fix.explanation).toMatch(/LIKE/);
|
|
15
|
+
});
|
|
16
|
+
it("rejects open-ended date ranges with OPEN_ENDED_DATE and suggests BETWEEN", () => {
|
|
17
|
+
const result = validatePreflight("SELECT campaign.name FROM campaign WHERE segments.date >= '2024-01-01'", CID);
|
|
18
|
+
expect(result.valid).toBe(false);
|
|
19
|
+
expect(result.error?.error.code).toBe("OPEN_ENDED_DATE");
|
|
20
|
+
expect(result.error?.error.fix.correctedCommand).toMatch(/BETWEEN/);
|
|
21
|
+
});
|
|
22
|
+
it("auto-fixes keyword.text to ad_group_criterion.keyword.text", () => {
|
|
23
|
+
const result = validatePreflight("SELECT keyword.text FROM keyword_view", CID);
|
|
24
|
+
expect(result.valid).toBe(true);
|
|
25
|
+
expect(result.correctedQuery).toMatch(/ad_group_criterion\.keyword\.text/);
|
|
26
|
+
const renamed = result.warnings.find((w) => w.code === "FIELD_RENAMED");
|
|
27
|
+
expect(renamed).toBeDefined();
|
|
28
|
+
});
|
|
29
|
+
it("auto-fixes ACTIVE to ENABLED for campaign status", () => {
|
|
30
|
+
const result = validatePreflight("SELECT campaign.name FROM campaign WHERE campaign.status = 'ACTIVE'", CID);
|
|
31
|
+
expect(result.valid).toBe(true);
|
|
32
|
+
expect(result.correctedQuery).toMatch(/ENABLED/);
|
|
33
|
+
const corrected = result.warnings.find((w) => w.code === "ENUM_CORRECTED");
|
|
34
|
+
expect(corrected).toBeDefined();
|
|
35
|
+
});
|
|
36
|
+
it("adds LIMIT when missing", () => {
|
|
37
|
+
const result = validatePreflight("SELECT campaign.name FROM campaign", CID);
|
|
38
|
+
expect(result.valid).toBe(true);
|
|
39
|
+
expect(result.correctedQuery).toMatch(/LIMIT/);
|
|
40
|
+
const autoLimit = result.warnings.find((w) => w.code === "AUTO_LIMIT");
|
|
41
|
+
expect(autoLimit).toBeDefined();
|
|
42
|
+
});
|
|
43
|
+
it("passes through a valid query with no errors", () => {
|
|
44
|
+
const result = validatePreflight("SELECT campaign.name FROM campaign LIMIT 10", CID);
|
|
45
|
+
expect(result.valid).toBe(true);
|
|
46
|
+
expect(result.error).toBeUndefined();
|
|
47
|
+
expect(result.warnings).toHaveLength(0);
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
//# sourceMappingURL=preflight.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preflight.test.js","sourceRoot":"","sources":["../../../../src/commands/ads/google/preflight.test.ts"],"names":[],"mappings":"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"}
|