@koda-sl/baker-cli 0.26.0 → 0.27.1-dev.bcbc31f8

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.
Files changed (104) hide show
  1. package/README.md +146 -0
  2. package/dist/cli.js +1 -1
  3. package/dist/commands/actions/create.d.ts.map +1 -1
  4. package/dist/commands/actions/create.js +10 -2
  5. package/dist/commands/actions/create.js.map +1 -1
  6. package/dist/commands/ads/index.d.ts.map +1 -1
  7. package/dist/commands/ads/index.js +7 -3
  8. package/dist/commands/ads/index.js.map +1 -1
  9. package/dist/commands/ads/linkedin/account.d.ts +20 -0
  10. package/dist/commands/ads/linkedin/account.d.ts.map +1 -0
  11. package/dist/commands/ads/linkedin/account.js +39 -0
  12. package/dist/commands/ads/linkedin/account.js.map +1 -0
  13. package/dist/commands/ads/linkedin/accounts.d.ts +20 -0
  14. package/dist/commands/ads/linkedin/accounts.d.ts.map +1 -0
  15. package/dist/commands/ads/linkedin/accounts.js +56 -0
  16. package/dist/commands/ads/linkedin/accounts.js.map +1 -0
  17. package/dist/commands/ads/linkedin/analytics.d.ts +84 -0
  18. package/dist/commands/ads/linkedin/analytics.d.ts.map +1 -0
  19. package/dist/commands/ads/linkedin/analytics.js +249 -0
  20. package/dist/commands/ads/linkedin/analytics.js.map +1 -0
  21. package/dist/commands/ads/linkedin/audience-size.d.ts +28 -0
  22. package/dist/commands/ads/linkedin/audience-size.d.ts.map +1 -0
  23. package/dist/commands/ads/linkedin/audience-size.js +75 -0
  24. package/dist/commands/ads/linkedin/audience-size.js.map +1 -0
  25. package/dist/commands/ads/linkedin/audit.d.ts +35 -0
  26. package/dist/commands/ads/linkedin/audit.d.ts.map +1 -0
  27. package/dist/commands/ads/linkedin/audit.js +136 -0
  28. package/dist/commands/ads/linkedin/audit.js.map +1 -0
  29. package/dist/commands/ads/linkedin/bid-pricing.d.ts +38 -0
  30. package/dist/commands/ads/linkedin/bid-pricing.d.ts.map +1 -0
  31. package/dist/commands/ads/linkedin/bid-pricing.js +76 -0
  32. package/dist/commands/ads/linkedin/bid-pricing.js.map +1 -0
  33. package/dist/commands/ads/linkedin/campaign-groups.d.ts +32 -0
  34. package/dist/commands/ads/linkedin/campaign-groups.d.ts.map +1 -0
  35. package/dist/commands/ads/linkedin/campaign-groups.js +50 -0
  36. package/dist/commands/ads/linkedin/campaign-groups.js.map +1 -0
  37. package/dist/commands/ads/linkedin/campaigns.d.ts +36 -0
  38. package/dist/commands/ads/linkedin/campaigns.d.ts.map +1 -0
  39. package/dist/commands/ads/linkedin/campaigns.js +57 -0
  40. package/dist/commands/ads/linkedin/campaigns.js.map +1 -0
  41. package/dist/commands/ads/linkedin/conversation.d.ts +36 -0
  42. package/dist/commands/ads/linkedin/conversation.d.ts.map +1 -0
  43. package/dist/commands/ads/linkedin/conversation.js +77 -0
  44. package/dist/commands/ads/linkedin/conversation.js.map +1 -0
  45. package/dist/commands/ads/linkedin/conversions.d.ts +2 -0
  46. package/dist/commands/ads/linkedin/conversions.d.ts.map +1 -0
  47. package/dist/commands/ads/linkedin/conversions.js +102 -0
  48. package/dist/commands/ads/linkedin/conversions.js.map +1 -0
  49. package/dist/commands/ads/linkedin/creatives.d.ts +36 -0
  50. package/dist/commands/ads/linkedin/creatives.d.ts.map +1 -0
  51. package/dist/commands/ads/linkedin/creatives.js +57 -0
  52. package/dist/commands/ads/linkedin/creatives.js.map +1 -0
  53. package/dist/commands/ads/linkedin/demographics.d.ts +40 -0
  54. package/dist/commands/ads/linkedin/demographics.d.ts.map +1 -0
  55. package/dist/commands/ads/linkedin/demographics.js +117 -0
  56. package/dist/commands/ads/linkedin/demographics.js.map +1 -0
  57. package/dist/commands/ads/linkedin/facets.d.ts +2 -0
  58. package/dist/commands/ads/linkedin/facets.d.ts.map +1 -0
  59. package/dist/commands/ads/linkedin/facets.js +95 -0
  60. package/dist/commands/ads/linkedin/facets.js.map +1 -0
  61. package/dist/commands/ads/linkedin/forecast.d.ts +50 -0
  62. package/dist/commands/ads/linkedin/forecast.d.ts.map +1 -0
  63. package/dist/commands/ads/linkedin/forecast.js +83 -0
  64. package/dist/commands/ads/linkedin/forecast.js.map +1 -0
  65. package/dist/commands/ads/linkedin/index.d.ts +19 -0
  66. package/dist/commands/ads/linkedin/index.d.ts.map +1 -0
  67. package/dist/commands/ads/linkedin/index.js +83 -0
  68. package/dist/commands/ads/linkedin/index.js.map +1 -0
  69. package/dist/commands/ads/linkedin/leads.d.ts +40 -0
  70. package/dist/commands/ads/linkedin/leads.d.ts.map +1 -0
  71. package/dist/commands/ads/linkedin/leads.js +75 -0
  72. package/dist/commands/ads/linkedin/leads.js.map +1 -0
  73. package/dist/commands/ads/linkedin/presets.d.ts +40 -0
  74. package/dist/commands/ads/linkedin/presets.d.ts.map +1 -0
  75. package/dist/commands/ads/linkedin/presets.js +193 -0
  76. package/dist/commands/ads/linkedin/presets.js.map +1 -0
  77. package/dist/commands/ads/linkedin/presets.test.d.ts +2 -0
  78. package/dist/commands/ads/linkedin/presets.test.d.ts.map +1 -0
  79. package/dist/commands/ads/linkedin/presets.test.js +98 -0
  80. package/dist/commands/ads/linkedin/presets.test.js.map +1 -0
  81. package/dist/commands/ads/linkedin/schemas.d.ts +2 -0
  82. package/dist/commands/ads/linkedin/schemas.d.ts.map +1 -0
  83. package/dist/commands/ads/linkedin/schemas.js +300 -0
  84. package/dist/commands/ads/linkedin/schemas.js.map +1 -0
  85. package/dist/commands/ads/linkedin/shared.d.ts +17 -0
  86. package/dist/commands/ads/linkedin/shared.d.ts.map +1 -0
  87. package/dist/commands/ads/linkedin/shared.js +116 -0
  88. package/dist/commands/ads/linkedin/shared.js.map +1 -0
  89. package/dist/commands/ads/linkedin/top-companies.d.ts +44 -0
  90. package/dist/commands/ads/linkedin/top-companies.d.ts.map +1 -0
  91. package/dist/commands/ads/linkedin/top-companies.js +86 -0
  92. package/dist/commands/ads/linkedin/top-companies.js.map +1 -0
  93. package/dist/env.d.ts +1 -0
  94. package/dist/env.d.ts.map +1 -1
  95. package/dist/env.js +4 -0
  96. package/dist/env.js.map +1 -1
  97. package/dist/error-handler.d.ts +1 -1
  98. package/dist/error-handler.d.ts.map +1 -1
  99. package/dist/error-handler.js +3 -0
  100. package/dist/error-handler.js.map +1 -1
  101. package/dist/output.d.ts +1 -0
  102. package/dist/output.d.ts.map +1 -1
  103. package/dist/output.js.map +1 -1
  104. package/package.json +1 -1
@@ -0,0 +1,249 @@
1
+ import { defineCommand } from "citty";
2
+ import { apiPost } from "../../../client.js";
3
+ import { writeAdsJson, writeAdsOutput } from "../output.js";
4
+ import { ALL_LEVELS, ALL_PIVOTS, composeFields, isLevel, isPivot, listIntents, MEMBER_PIVOTS, } from "./presets.js";
5
+ import { csvOrJson, daysAgoIso, handleLinkedinError, resolveAccountIdArg, todayIso } from "./shared.js";
6
+ function commaSplit(input) {
7
+ if (!input)
8
+ return undefined;
9
+ return input
10
+ .split(",")
11
+ .map((s) => s.trim())
12
+ .filter(Boolean);
13
+ }
14
+ function parseGranularity(raw) {
15
+ const v = (raw ?? "DAILY").toUpperCase();
16
+ if (v === "DAILY" || v === "MONTHLY" || v === "YEARLY" || v === "ALL") {
17
+ return v;
18
+ }
19
+ handleLinkedinError(new Error(`Invalid --granularity "${raw}". Use DAILY | MONTHLY | YEARLY | ALL.`));
20
+ }
21
+ function parseLevel(raw) {
22
+ const v = raw ?? "account";
23
+ if (!isLevel(v)) {
24
+ handleLinkedinError(new Error(`Invalid --level "${raw}". Use one of: ${ALL_LEVELS.join(", ")}.`));
25
+ }
26
+ return v;
27
+ }
28
+ function parsePivot(raw) {
29
+ const v = raw ?? "none";
30
+ if (!isPivot(v)) {
31
+ handleLinkedinError(new Error(`Invalid --pivot "${raw}". Use one of: ${ALL_PIVOTS.join(", ")}.`));
32
+ }
33
+ return v;
34
+ }
35
+ function parseIntent(raw) {
36
+ const v = (raw ?? "baseline");
37
+ const known = [
38
+ "baseline",
39
+ "revenue",
40
+ "funnel",
41
+ "engagement",
42
+ "video",
43
+ "lead-gen",
44
+ "inmail",
45
+ "document",
46
+ "ranking",
47
+ "identity",
48
+ ];
49
+ if (!known.includes(v)) {
50
+ handleLinkedinError(new Error(`Invalid --intent "${raw}". Run --list-intents to see options.`));
51
+ }
52
+ return v;
53
+ }
54
+ /**
55
+ * Resolve start/end dates. Precedence:
56
+ * 1. --start + --end (explicit window)
57
+ * 2. --last-days N → end=today, start=today-N
58
+ * 3. default: last 7 days
59
+ */
60
+ function resolveDateRange(args) {
61
+ const start = args.start;
62
+ const end = args.end;
63
+ if (start) {
64
+ return { start, end };
65
+ }
66
+ const lastDaysRaw = args["last-days"];
67
+ const days = lastDaysRaw ? Number(lastDaysRaw) : 7;
68
+ if (!Number.isFinite(days) || days < 1 || days > 730) {
69
+ handleLinkedinError(new Error(`Invalid --last-days "${lastDaysRaw}". Use a positive number ≤ 730.`));
70
+ }
71
+ return { start: daysAgoIso(days), end: todayIso() };
72
+ }
73
+ /**
74
+ * Resolve scope IDs. Each level expects a different flag — but we accept
75
+ * --account-id at every level when level=account, and --campaign-id /
76
+ * --campaign-group-id / --creative-id otherwise. CSV-encoded for multi-id.
77
+ */
78
+ function resolveScopeIds(args, level) {
79
+ if (level === "account") {
80
+ const id = resolveAccountIdArg(args);
81
+ return [id];
82
+ }
83
+ if (level === "campaign-group") {
84
+ const raw = args["campaign-group-id"] ?? args.ids;
85
+ if (!raw) {
86
+ handleLinkedinError(new Error("Pass --campaign-group-id (CSV for multi) when --level campaign-group."));
87
+ }
88
+ return commaSplit(raw) ?? [];
89
+ }
90
+ if (level === "campaign") {
91
+ const raw = args["campaign-id"] ?? args.ids;
92
+ if (!raw) {
93
+ handleLinkedinError(new Error("Pass --campaign-id (CSV for multi) when --level campaign."));
94
+ }
95
+ return commaSplit(raw) ?? [];
96
+ }
97
+ // creative
98
+ const raw = args["creative-id"] ?? args.ids;
99
+ if (!raw) {
100
+ handleLinkedinError(new Error("Pass --creative-id (CSV for multi) when --level creative."));
101
+ }
102
+ return commaSplit(raw) ?? [];
103
+ }
104
+ /** Sort rows by spend desc when present, otherwise by impressions desc. */
105
+ function sortRows(rows) {
106
+ return [...rows].sort((a, b) => {
107
+ const cs = numberOf(b.costInUsd) - numberOf(a.costInUsd);
108
+ if (cs !== 0)
109
+ return cs;
110
+ return numberOf(b.impressions) - numberOf(a.impressions);
111
+ });
112
+ }
113
+ function numberOf(v) {
114
+ if (typeof v === "number")
115
+ return Number.isFinite(v) ? v : 0;
116
+ if (typeof v === "string") {
117
+ const n = Number(v);
118
+ return Number.isFinite(n) ? n : 0;
119
+ }
120
+ return 0;
121
+ }
122
+ export const analyticsCommand = defineCommand({
123
+ meta: {
124
+ name: "analytics",
125
+ description: `Performance reporting — the workhorse for AI agents.
126
+
127
+ Three-axis design (LinkedIn's superpower over Meta/Google):
128
+ --level account | campaign-group | campaign | creative
129
+ --intent baseline | revenue | funnel | engagement | video | lead-gen | inmail | document | ranking | identity
130
+ --pivot none | job-title | company | industry | seniority | function | company-size | country | region
131
+ | device | placement | serving-location | card-index | objective
132
+ | conversation-node | conversation-node-button
133
+
134
+ Smart defaults:
135
+ --level account, --intent baseline, --pivot none, --granularity DAILY, last 7 days
136
+ Pivot on a MEMBER_* dim → granularity auto-forced to ALL (LinkedIn rejects DAILY+demographic)
137
+ Demographic data delayed 12-24h with ≥3-event privacy floor — the CLI flags this in warnings.
138
+ Derived metrics injected client-side: ctr, cpc, cpm, frequency, leadCompletionRate
139
+
140
+ Examples — common AI questions:
141
+ # "How is this account doing this week?"
142
+ baker ads linkedin analytics
143
+
144
+ # "Who are we reaching, by job title?" (the LinkedIn killer)
145
+ baker ads linkedin analytics --level campaign --campaign-id 1234 --pivot job-title --intent baseline --last-days 30
146
+
147
+ # "Top companies seeing my ads" (ABM feedback loop)
148
+ baker ads linkedin analytics --level campaign --campaign-id 1234 --pivot company --last-days 30
149
+
150
+ # "Revenue by campaign over Q1"
151
+ baker ads linkedin analytics --level campaign --campaign-id 1,2,3 --intent revenue --start 2026-01-01 --end 2026-03-31 --granularity MONTHLY
152
+
153
+ # "How is the lead form converting?"
154
+ baker ads linkedin analytics --level campaign --campaign-id 1234 --intent lead-gen
155
+
156
+ # Custom field set (escape hatch)
157
+ baker ads linkedin analytics --metrics impressions,clicks,oneClickLeads --intent identity`,
158
+ },
159
+ args: {
160
+ level: { type: "string", description: "Object scope (default: account)" },
161
+ "account-id": { type: "string", description: "Account ID — numeric or urn:li:sponsoredAccount:N (level=account)" },
162
+ "account-urn": { type: "string", description: "Alias for --account-id (URN form)" },
163
+ "campaign-group-id": { type: "string", description: "Comma-separated IDs (level=campaign-group)" },
164
+ "campaign-id": { type: "string", description: "Comma-separated IDs (level=campaign)" },
165
+ "creative-id": { type: "string", description: "Comma-separated IDs (level=creative)" },
166
+ ids: { type: "string", description: "Generic CSV of IDs at the chosen level (alternative to per-level flags)" },
167
+ intent: {
168
+ type: "string",
169
+ description: "baseline|revenue|funnel|engagement|video|lead-gen|inmail|document|ranking|identity",
170
+ },
171
+ pivot: { type: "string", description: "Pivot dim. Default: none. See `--list-pivots`." },
172
+ metrics: { type: "string", description: "CSV metric override — bypass --intent's bundle" },
173
+ "list-intents": { type: "boolean", description: "Print intent definitions and exit" },
174
+ "list-pivots": { type: "boolean", description: "Print pivot slugs and exit" },
175
+ start: { type: "string", description: "Start date YYYY-MM-DD (overrides --last-days)" },
176
+ end: { type: "string", description: "End date YYYY-MM-DD" },
177
+ "last-days": { type: "string", description: "Window relative to today (default: 7)" },
178
+ granularity: { type: "string", description: "DAILY|MONTHLY|YEARLY|ALL (default: DAILY)" },
179
+ limit: { type: "string", description: "Max rows (default: 1000)" },
180
+ "no-sort": { type: "boolean", description: "Skip default sort by costInUsd desc" },
181
+ "skip-cache": { type: "boolean", description: "Bypass server-side cache" },
182
+ output: { type: "string", description: "Output format json|csv|jsonl|md", default: "json" },
183
+ },
184
+ run: async ({ args }) => {
185
+ if (args["list-intents"]) {
186
+ writeAdsJson({ ok: true, data: listIntents() });
187
+ return;
188
+ }
189
+ if (args["list-pivots"]) {
190
+ writeAdsJson({ ok: true, data: ALL_PIVOTS });
191
+ return;
192
+ }
193
+ const level = parseLevel(args.level);
194
+ const intent = parseIntent(args.intent);
195
+ const pivot = parsePivot(args.pivot);
196
+ let granularity = parseGranularity(args.granularity);
197
+ // CLI-side mirror of the backend's pivot/granularity guard. Backend will
198
+ // also enforce — this lets us echo the auto-correction in stderr early.
199
+ if (MEMBER_PIVOTS.has(pivot) && granularity === "DAILY") {
200
+ process.stderr.write(`note: pivot=${pivot} forces --granularity ALL (LinkedIn rejects DAILY + demographic).\n`);
201
+ granularity = "ALL";
202
+ }
203
+ const ids = resolveScopeIds(args, level);
204
+ if (ids.length === 0) {
205
+ handleLinkedinError(new Error(`No IDs resolved for --level ${level}`));
206
+ }
207
+ const range = resolveDateRange(args);
208
+ const metrics = commaSplit(args.metrics);
209
+ const limit = args.limit ? Number(args.limit) : 1000;
210
+ const request = {
211
+ level,
212
+ ids,
213
+ intent,
214
+ pivot,
215
+ metrics,
216
+ start: range.start,
217
+ end: range.end,
218
+ granularity,
219
+ limit,
220
+ };
221
+ try {
222
+ const data = await apiPost("/api/ads/linkedin/analytics", {
223
+ request,
224
+ skipCache: Boolean(args["skip-cache"]),
225
+ });
226
+ const rows = args["no-sort"] ? data.rows : sortRows(data.rows);
227
+ const fmt = csvOrJson(args);
228
+ if (fmt !== "json") {
229
+ writeAdsOutput(rows, fmt, data.fields);
230
+ return;
231
+ }
232
+ writeAdsJson({
233
+ ok: true,
234
+ data: {
235
+ rows,
236
+ fields: data.fields,
237
+ warnings: data.warnings,
238
+ query: data.query,
239
+ // Echo composed fields so an offline agent can pre-flight w/o this network call.
240
+ composedFields: composeFields(intent, level, metrics),
241
+ },
242
+ });
243
+ }
244
+ catch (err) {
245
+ handleLinkedinError(err);
246
+ }
247
+ },
248
+ });
249
+ //# sourceMappingURL=analytics.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analytics.js","sourceRoot":"","sources":["../../../../src/commands/ads/linkedin/analytics.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ export declare const audienceSizeCommand: import("citty").CommandDef<{
2
+ readonly "account-id": {
3
+ readonly type: "string";
4
+ readonly description: "Numeric account ID or urn:li:sponsoredAccount:N";
5
+ };
6
+ readonly "account-urn": {
7
+ readonly type: "string";
8
+ readonly description: "Alias for --account-id";
9
+ };
10
+ readonly targeting: {
11
+ readonly type: "string";
12
+ readonly description: "Inline JSON targetingCriteria payload";
13
+ };
14
+ readonly "targeting-file": {
15
+ readonly type: "string";
16
+ readonly description: "Path to JSON file with targetingCriteria";
17
+ };
18
+ readonly "skip-cache": {
19
+ readonly type: "boolean";
20
+ readonly description: "Bypass server-side cache";
21
+ };
22
+ readonly output: {
23
+ readonly type: "string";
24
+ readonly description: "json";
25
+ readonly default: "json";
26
+ };
27
+ }>;
28
+ //# sourceMappingURL=audience-size.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audience-size.d.ts","sourceRoot":"","sources":["../../../../src/commands/ads/linkedin/audience-size.ts"],"names":[],"mappings":"AAsCA,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;EA0C9B,CAAC"}
@@ -0,0 +1,75 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { defineCommand } from "citty";
3
+ import { apiPost } from "../../../client.js";
4
+ import { writeAdsJson } from "../output.js";
5
+ import { handleLinkedinError, resolveAccountIdArg } from "./shared.js";
6
+ /**
7
+ * Read targeting JSON from --targeting-file, --targeting (inline JSON), or
8
+ * stdin. Returns the parsed object — failure exits via handleLinkedinError.
9
+ */
10
+ function loadTargeting(args) {
11
+ const inline = args.targeting;
12
+ if (inline) {
13
+ try {
14
+ return JSON.parse(inline);
15
+ }
16
+ catch {
17
+ handleLinkedinError(new Error("--targeting must be valid JSON. See LinkedIn 'targetingCriteria' shape."));
18
+ }
19
+ }
20
+ const file = args["targeting-file"];
21
+ if (file) {
22
+ try {
23
+ return JSON.parse(readFileSync(file, "utf-8"));
24
+ }
25
+ catch (e) {
26
+ handleLinkedinError(new Error(`Failed to read --targeting-file: ${e instanceof Error ? e.message : "I/O error"}`));
27
+ }
28
+ }
29
+ handleLinkedinError(new Error("Pass --targeting-file <path> or --targeting '{...JSON...}'"));
30
+ }
31
+ export const audienceSizeCommand = defineCommand({
32
+ meta: {
33
+ name: "audience-size",
34
+ description: `Estimate audience size for a targeting payload — pre-launch sanity check.
35
+
36
+ Returns total + active counts plus playbook §04 warnings:
37
+ size <20K → too narrow (will not deliver)
38
+ size <50K → outside the 50K–500K sweet spot
39
+ size >500K → outside the sweet spot
40
+ size >1M → too broad, refine ICP
41
+
42
+ The targeting payload must follow LinkedIn's 'targetingCriteria' shape — an
43
+ include/exclude tree of and/or boolean operators with adTargetingFacet keys.
44
+ The simplest form:
45
+ { "include": { "and": [{ "or": { "urn:li:adTargetingFacet:locations": ["urn:li:geo:103644278"] }}]}}
46
+
47
+ Examples:
48
+ baker ads linkedin audience-size --account-id 503001492 --targeting-file targeting.json
49
+ baker ads linkedin audience-size --account-id 503001492 --targeting '{"include":{"and":[]}}'`,
50
+ },
51
+ args: {
52
+ "account-id": { type: "string", description: "Numeric account ID or urn:li:sponsoredAccount:N" },
53
+ "account-urn": { type: "string", description: "Alias for --account-id" },
54
+ targeting: { type: "string", description: "Inline JSON targetingCriteria payload" },
55
+ "targeting-file": { type: "string", description: "Path to JSON file with targetingCriteria" },
56
+ "skip-cache": { type: "boolean", description: "Bypass server-side cache" },
57
+ output: { type: "string", description: "json", default: "json" },
58
+ },
59
+ run: async ({ args }) => {
60
+ const accountId = resolveAccountIdArg(args);
61
+ const targetingCriteria = loadTargeting(args);
62
+ try {
63
+ const data = await apiPost("/api/ads/linkedin/audience-size", {
64
+ accountId,
65
+ targetingCriteria,
66
+ skipCache: Boolean(args["skip-cache"]),
67
+ });
68
+ writeAdsJson({ ok: true, data });
69
+ }
70
+ catch (err) {
71
+ handleLinkedinError(err);
72
+ }
73
+ },
74
+ });
75
+ //# sourceMappingURL=audience-size.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audience-size.js","sourceRoot":"","sources":["../../../../src/commands/ads/linkedin/audience-size.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AACtC,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAQvE;;;GAGG;AACH,SAAS,aAAa,CAAC,IAA6B;IAClD,MAAM,MAAM,GAAG,IAAI,CAAC,SAA+B,CAAC;IACpD,IAAI,MAAM,EAAE,CAAC;QACX,IAAI,CAAC;YACH,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QAC5B,CAAC;QAAC,MAAM,CAAC;YACP,mBAAmB,CAAC,IAAI,KAAK,CAAC,yEAAyE,CAAC,CAAC,CAAC;QAC5G,CAAC;IACH,CAAC;IACD,MAAM,IAAI,GAAG,IAAI,CAAC,gBAAgB,CAAuB,CAAC;IAC1D,IAAI,IAAI,EAAE,CAAC;QACT,IAAI,CAAC;YACH,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC;QACjD,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,mBAAmB,CACjB,IAAI,KAAK,CAAC,oCAAoC,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAC9F,CAAC;QACJ,CAAC;IACH,CAAC;IACD,mBAAmB,CAAC,IAAI,KAAK,CAAC,4DAA4D,CAAC,CAAC,CAAC;AAC/F,CAAC;AAED,MAAM,CAAC,MAAM,mBAAmB,GAAG,aAAa,CAAC;IAC/C,IAAI,EAAE;QACJ,IAAI,EAAE,eAAe;QACrB,WAAW,EAAE;;;;;;;;;;;;;;;+FAe8E;KAC5F;IACD,IAAI,EAAE;QACJ,YAAY,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,iDAAiD,EAAE;QAChG,aAAa,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,wBAAwB,EAAE;QACxE,SAAS,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,uCAAuC,EAAE;QACnF,gBAAgB,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,0CAA0C,EAAE;QAC7F,YAAY,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,0BAA0B,EAAE;QAC1E,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE;KACjE;IACD,GAAG,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE;QACtB,MAAM,SAAS,GAAG,mBAAmB,CAAC,IAAI,CAAC,CAAC;QAC5C,MAAM,iBAAiB,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;QAC9C,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,OAAO,CAAqB,iCAAiC,EAAE;gBAChF,SAAS;gBACT,iBAAiB;gBACjB,SAAS,EAAE,OAAO,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;aACvC,CAAC,CAAC;YACH,YAAY,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;QACnC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,mBAAmB,CAAC,GAAG,CAAC,CAAC;QAC3B,CAAC;IACH,CAAC;CACF,CAAC,CAAC"}
@@ -0,0 +1,35 @@
1
+ export declare const auditCommand: import("citty").CommandDef<{
2
+ readonly "account-id": {
3
+ readonly type: "string";
4
+ readonly description: "Numeric account ID or urn:li:sponsoredAccount:N";
5
+ };
6
+ readonly "account-urn": {
7
+ readonly type: "string";
8
+ readonly description: "Alias for --account-id";
9
+ };
10
+ readonly "campaign-id": {
11
+ readonly type: "string";
12
+ readonly description: "Narrow scope to a single campaign";
13
+ };
14
+ readonly "window-days": {
15
+ readonly type: "string";
16
+ readonly description: "Lookback window for performance checks (default: 30)";
17
+ };
18
+ readonly severity: {
19
+ readonly type: "string";
20
+ readonly description: "CSV severity filter: critical,high,medium,low";
21
+ };
22
+ readonly area: {
23
+ readonly type: "string";
24
+ readonly description: "CSV area filter: Settings,Tracking,Audience,Campaigns,Creative,Performance,Bidding,Compliance,Hygiene";
25
+ };
26
+ readonly format: {
27
+ readonly type: "string";
28
+ readonly description: "Output format: json | md (default: json)";
29
+ };
30
+ readonly "skip-cache": {
31
+ readonly type: "boolean";
32
+ readonly description: "Bypass server-side cache";
33
+ };
34
+ }>;
35
+ //# sourceMappingURL=audit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../../../../src/commands/ads/linkedin/audit.ts"],"names":[],"mappings":"AAwGA,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAuEvB,CAAC"}
@@ -0,0 +1,136 @@
1
+ import { defineCommand } from "citty";
2
+ import { apiPost } from "../../../client.js";
3
+ import { writeAdsJson } from "../output.js";
4
+ import { handleLinkedinError, resolveAccountIdArg } from "./shared.js";
5
+ const SEVERITY_RANK = {
6
+ critical: 0,
7
+ high: 1,
8
+ medium: 2,
9
+ low: 3,
10
+ };
11
+ const STATUS_RANK = {
12
+ fail: 0,
13
+ warn: 1,
14
+ partial: 2,
15
+ pass: 3,
16
+ n_a: 4,
17
+ };
18
+ function filterFindings(findings, severity, area) {
19
+ return findings.filter((f) => {
20
+ if (severity && !severity.includes(f.severity))
21
+ return false;
22
+ if (area && !area.includes(f.area))
23
+ return false;
24
+ return true;
25
+ });
26
+ }
27
+ function sortFindings(findings) {
28
+ return [...findings].sort((a, b) => {
29
+ const sa = STATUS_RANK[a.status] - STATUS_RANK[b.status];
30
+ if (sa !== 0)
31
+ return sa;
32
+ return SEVERITY_RANK[a.severity] - SEVERITY_RANK[b.severity];
33
+ });
34
+ }
35
+ /**
36
+ * Render the audit as a markdown table that mirrors
37
+ * google-ads-playbook/10-audit-summary.md so the agent can drop it into a
38
+ * client deliverable as-is.
39
+ */
40
+ function renderMarkdown(result) {
41
+ const lines = [];
42
+ lines.push(`# LinkedIn Ads Audit — ${result.account.name} (${result.account.id})`);
43
+ lines.push("");
44
+ lines.push(`**Scope:** ${result.scope.campaigns} campaigns, ${result.scope.creatives} creatives, last ${result.scope.windowDays}d`);
45
+ lines.push("");
46
+ lines.push(`**Summary:** ${result.summary.pass} pass • ${result.summary.critical} critical • ${result.summary.high} high • ${result.summary.medium} medium • ${result.summary.low} low • ${result.summary.n_a} n/a (${result.summary.totalChecks} total)`);
47
+ lines.push("");
48
+ lines.push("| # | Area | Check | Status | Severity | Notes |");
49
+ lines.push("|---|------|-------|--------|----------|-------|");
50
+ result.findings.forEach((f, idx) => {
51
+ const note = noteOf(f);
52
+ lines.push(`| ${idx + 1} | ${f.area} | ${escapeMd(f.check)} | ${f.status.toUpperCase()} | ${f.severity} | ${escapeMd(note)} |`);
53
+ });
54
+ return lines.join("\n");
55
+ }
56
+ function escapeMd(text) {
57
+ return text.replace(/\|/g, "\\|").replace(/\n/g, " ");
58
+ }
59
+ function noteOf(f) {
60
+ if (f.status === "pass")
61
+ return "";
62
+ const ev = f.evidence ? JSON.stringify(f.evidence) : "";
63
+ const fix = f.fix?.explanation ?? "";
64
+ return [fix, ev].filter(Boolean).join(" — ");
65
+ }
66
+ export const auditCommand = defineCommand({
67
+ meta: {
68
+ name: "audit",
69
+ description: `Run a LinkedIn Ads playbook audit — 30+ checks across Settings, Tracking,
70
+ Audience, Campaigns, Creative, Performance, Bidding, and Compliance.
71
+
72
+ Each finding has {id, area, check, status, severity, evidence, fix} fields with
73
+ playbook citations. The default JSON output is agent-friendly; --format md
74
+ renders a deliverable-ready table that mirrors the google-ads-playbook
75
+ 10-audit-summary.md style.
76
+
77
+ Exit code 0 always. Use the JSON 'summary' counts to decide if action is needed.
78
+
79
+ Examples:
80
+ baker ads linkedin audit --account-id 503001492
81
+ baker ads linkedin audit --account-id 503001492 --campaign-id 1234 # narrow scope
82
+ baker ads linkedin audit --account-id 503001492 --window-days 90
83
+ baker ads linkedin audit --account-id 503001492 --severity critical,high
84
+ baker ads linkedin audit --account-id 503001492 --area Settings,Tracking
85
+ baker ads linkedin audit --account-id 503001492 --format md`,
86
+ },
87
+ args: {
88
+ "account-id": { type: "string", description: "Numeric account ID or urn:li:sponsoredAccount:N" },
89
+ "account-urn": { type: "string", description: "Alias for --account-id" },
90
+ "campaign-id": { type: "string", description: "Narrow scope to a single campaign" },
91
+ "window-days": { type: "string", description: "Lookback window for performance checks (default: 30)" },
92
+ severity: { type: "string", description: "CSV severity filter: critical,high,medium,low" },
93
+ area: {
94
+ type: "string",
95
+ description: "CSV area filter: Settings,Tracking,Audience,Campaigns,Creative,Performance,Bidding,Compliance,Hygiene",
96
+ },
97
+ format: { type: "string", description: "Output format: json | md (default: json)" },
98
+ "skip-cache": { type: "boolean", description: "Bypass server-side cache" },
99
+ },
100
+ run: async ({ args }) => {
101
+ const accountId = resolveAccountIdArg(args);
102
+ const windowDays = args["window-days"] ? Number(args["window-days"]) : 30;
103
+ if (!Number.isFinite(windowDays) || windowDays < 1 || windowDays > 365) {
104
+ handleLinkedinError(new Error("--window-days must be 1..365"));
105
+ }
106
+ try {
107
+ const data = await apiPost("/api/ads/linkedin/audit", {
108
+ accountId,
109
+ campaignId: args["campaign-id"] ? String(args["campaign-id"]) : undefined,
110
+ windowDays,
111
+ skipCache: Boolean(args["skip-cache"]),
112
+ });
113
+ const severityFilter = args.severity
114
+ ?.split(",")
115
+ .map((s) => s.trim().toLowerCase())
116
+ .filter(Boolean);
117
+ const areaFilter = args.area
118
+ ?.split(",")
119
+ .map((s) => s.trim())
120
+ .filter(Boolean);
121
+ const filtered = filterFindings(data.findings, severityFilter, areaFilter);
122
+ const sorted = sortFindings(filtered);
123
+ const result = { ...data, findings: sorted };
124
+ const fmt = args.format ?? "json";
125
+ if (fmt === "md") {
126
+ process.stdout.write(`${renderMarkdown(result)}\n`);
127
+ return;
128
+ }
129
+ writeAdsJson({ ok: true, data: result });
130
+ }
131
+ catch (err) {
132
+ handleLinkedinError(err);
133
+ }
134
+ },
135
+ });
136
+ //# sourceMappingURL=audit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit.js","sourceRoot":"","sources":["../../../../src/commands/ads/linkedin/audit.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ export declare const bidPricingCommand: import("citty").CommandDef<{
2
+ readonly "account-id": {
3
+ readonly type: "string";
4
+ readonly description: "Numeric account ID or urn:li:sponsoredAccount:N";
5
+ };
6
+ readonly "account-urn": {
7
+ readonly type: "string";
8
+ readonly description: "Alias for --account-id";
9
+ };
10
+ readonly objective: {
11
+ readonly type: "string";
12
+ readonly description: "Objective type (e.g. WEBSITE_CONVERSION, LEAD_GENERATION)";
13
+ readonly required: true;
14
+ };
15
+ readonly "cost-type": {
16
+ readonly type: "string";
17
+ readonly description: "CPC | CPM | CPV | CPS";
18
+ readonly required: true;
19
+ };
20
+ readonly targeting: {
21
+ readonly type: "string";
22
+ readonly description: "Inline JSON targetingCriteria";
23
+ };
24
+ readonly "targeting-file": {
25
+ readonly type: "string";
26
+ readonly description: "Path to JSON file with targetingCriteria";
27
+ };
28
+ readonly "skip-cache": {
29
+ readonly type: "boolean";
30
+ readonly description: "Bypass server-side cache";
31
+ };
32
+ readonly output: {
33
+ readonly type: "string";
34
+ readonly description: "json";
35
+ readonly default: "json";
36
+ };
37
+ }>;
38
+ //# sourceMappingURL=bid-pricing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bid-pricing.d.ts","sourceRoot":"","sources":["../../../../src/commands/ads/linkedin/bid-pricing.ts"],"names":[],"mappings":"AAyCA,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA+C5B,CAAC"}