@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.
- package/README.md +146 -0
- package/dist/cli.js +1 -1
- package/dist/commands/actions/create.d.ts.map +1 -1
- package/dist/commands/actions/create.js +10 -2
- package/dist/commands/actions/create.js.map +1 -1
- package/dist/commands/ads/index.d.ts.map +1 -1
- package/dist/commands/ads/index.js +7 -3
- package/dist/commands/ads/index.js.map +1 -1
- package/dist/commands/ads/linkedin/account.d.ts +20 -0
- package/dist/commands/ads/linkedin/account.d.ts.map +1 -0
- package/dist/commands/ads/linkedin/account.js +39 -0
- package/dist/commands/ads/linkedin/account.js.map +1 -0
- package/dist/commands/ads/linkedin/accounts.d.ts +20 -0
- package/dist/commands/ads/linkedin/accounts.d.ts.map +1 -0
- package/dist/commands/ads/linkedin/accounts.js +56 -0
- package/dist/commands/ads/linkedin/accounts.js.map +1 -0
- package/dist/commands/ads/linkedin/analytics.d.ts +84 -0
- package/dist/commands/ads/linkedin/analytics.d.ts.map +1 -0
- package/dist/commands/ads/linkedin/analytics.js +249 -0
- package/dist/commands/ads/linkedin/analytics.js.map +1 -0
- package/dist/commands/ads/linkedin/audience-size.d.ts +28 -0
- package/dist/commands/ads/linkedin/audience-size.d.ts.map +1 -0
- package/dist/commands/ads/linkedin/audience-size.js +75 -0
- package/dist/commands/ads/linkedin/audience-size.js.map +1 -0
- package/dist/commands/ads/linkedin/audit.d.ts +35 -0
- package/dist/commands/ads/linkedin/audit.d.ts.map +1 -0
- package/dist/commands/ads/linkedin/audit.js +136 -0
- package/dist/commands/ads/linkedin/audit.js.map +1 -0
- package/dist/commands/ads/linkedin/bid-pricing.d.ts +38 -0
- package/dist/commands/ads/linkedin/bid-pricing.d.ts.map +1 -0
- package/dist/commands/ads/linkedin/bid-pricing.js +76 -0
- package/dist/commands/ads/linkedin/bid-pricing.js.map +1 -0
- package/dist/commands/ads/linkedin/campaign-groups.d.ts +32 -0
- package/dist/commands/ads/linkedin/campaign-groups.d.ts.map +1 -0
- package/dist/commands/ads/linkedin/campaign-groups.js +50 -0
- package/dist/commands/ads/linkedin/campaign-groups.js.map +1 -0
- package/dist/commands/ads/linkedin/campaigns.d.ts +36 -0
- package/dist/commands/ads/linkedin/campaigns.d.ts.map +1 -0
- package/dist/commands/ads/linkedin/campaigns.js +57 -0
- package/dist/commands/ads/linkedin/campaigns.js.map +1 -0
- package/dist/commands/ads/linkedin/conversation.d.ts +36 -0
- package/dist/commands/ads/linkedin/conversation.d.ts.map +1 -0
- package/dist/commands/ads/linkedin/conversation.js +77 -0
- package/dist/commands/ads/linkedin/conversation.js.map +1 -0
- package/dist/commands/ads/linkedin/conversions.d.ts +2 -0
- package/dist/commands/ads/linkedin/conversions.d.ts.map +1 -0
- package/dist/commands/ads/linkedin/conversions.js +102 -0
- package/dist/commands/ads/linkedin/conversions.js.map +1 -0
- package/dist/commands/ads/linkedin/creatives.d.ts +36 -0
- package/dist/commands/ads/linkedin/creatives.d.ts.map +1 -0
- package/dist/commands/ads/linkedin/creatives.js +57 -0
- package/dist/commands/ads/linkedin/creatives.js.map +1 -0
- package/dist/commands/ads/linkedin/demographics.d.ts +40 -0
- package/dist/commands/ads/linkedin/demographics.d.ts.map +1 -0
- package/dist/commands/ads/linkedin/demographics.js +117 -0
- package/dist/commands/ads/linkedin/demographics.js.map +1 -0
- package/dist/commands/ads/linkedin/facets.d.ts +2 -0
- package/dist/commands/ads/linkedin/facets.d.ts.map +1 -0
- package/dist/commands/ads/linkedin/facets.js +95 -0
- package/dist/commands/ads/linkedin/facets.js.map +1 -0
- package/dist/commands/ads/linkedin/forecast.d.ts +50 -0
- package/dist/commands/ads/linkedin/forecast.d.ts.map +1 -0
- package/dist/commands/ads/linkedin/forecast.js +83 -0
- package/dist/commands/ads/linkedin/forecast.js.map +1 -0
- package/dist/commands/ads/linkedin/index.d.ts +19 -0
- package/dist/commands/ads/linkedin/index.d.ts.map +1 -0
- package/dist/commands/ads/linkedin/index.js +83 -0
- package/dist/commands/ads/linkedin/index.js.map +1 -0
- package/dist/commands/ads/linkedin/leads.d.ts +40 -0
- package/dist/commands/ads/linkedin/leads.d.ts.map +1 -0
- package/dist/commands/ads/linkedin/leads.js +75 -0
- package/dist/commands/ads/linkedin/leads.js.map +1 -0
- package/dist/commands/ads/linkedin/presets.d.ts +40 -0
- package/dist/commands/ads/linkedin/presets.d.ts.map +1 -0
- package/dist/commands/ads/linkedin/presets.js +193 -0
- package/dist/commands/ads/linkedin/presets.js.map +1 -0
- package/dist/commands/ads/linkedin/presets.test.d.ts +2 -0
- package/dist/commands/ads/linkedin/presets.test.d.ts.map +1 -0
- package/dist/commands/ads/linkedin/presets.test.js +98 -0
- package/dist/commands/ads/linkedin/presets.test.js.map +1 -0
- package/dist/commands/ads/linkedin/schemas.d.ts +2 -0
- package/dist/commands/ads/linkedin/schemas.d.ts.map +1 -0
- package/dist/commands/ads/linkedin/schemas.js +300 -0
- package/dist/commands/ads/linkedin/schemas.js.map +1 -0
- package/dist/commands/ads/linkedin/shared.d.ts +17 -0
- package/dist/commands/ads/linkedin/shared.d.ts.map +1 -0
- package/dist/commands/ads/linkedin/shared.js +116 -0
- package/dist/commands/ads/linkedin/shared.js.map +1 -0
- package/dist/commands/ads/linkedin/top-companies.d.ts +44 -0
- package/dist/commands/ads/linkedin/top-companies.d.ts.map +1 -0
- package/dist/commands/ads/linkedin/top-companies.js +86 -0
- package/dist/commands/ads/linkedin/top-companies.js.map +1 -0
- package/dist/env.d.ts +1 -0
- package/dist/env.d.ts.map +1 -1
- package/dist/env.js +4 -0
- package/dist/env.js.map +1 -1
- package/dist/error-handler.d.ts +1 -1
- package/dist/error-handler.d.ts.map +1 -1
- package/dist/error-handler.js +3 -0
- package/dist/error-handler.js.map +1 -1
- package/dist/output.d.ts +1 -0
- package/dist/output.d.ts.map +1 -1
- package/dist/output.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export declare const demographicsCommand: import("citty").CommandDef<{
|
|
2
|
+
readonly "campaign-id": {
|
|
3
|
+
readonly type: "string";
|
|
4
|
+
readonly description: "Comma-separated campaign IDs (required)";
|
|
5
|
+
};
|
|
6
|
+
readonly pivots: {
|
|
7
|
+
readonly type: "string";
|
|
8
|
+
readonly description: `CSV of pivots (default: ${string})`;
|
|
9
|
+
};
|
|
10
|
+
readonly "top-n": {
|
|
11
|
+
readonly type: "string";
|
|
12
|
+
readonly description: "Top rows per pivot (default: 10)";
|
|
13
|
+
};
|
|
14
|
+
readonly intent: {
|
|
15
|
+
readonly type: "string";
|
|
16
|
+
readonly description: "baseline | revenue | funnel | engagement | lead-gen (default: baseline)";
|
|
17
|
+
};
|
|
18
|
+
readonly start: {
|
|
19
|
+
readonly type: "string";
|
|
20
|
+
readonly description: "Start date YYYY-MM-DD";
|
|
21
|
+
};
|
|
22
|
+
readonly end: {
|
|
23
|
+
readonly type: "string";
|
|
24
|
+
readonly description: "End date YYYY-MM-DD";
|
|
25
|
+
};
|
|
26
|
+
readonly "last-days": {
|
|
27
|
+
readonly type: "string";
|
|
28
|
+
readonly description: "Window relative to today (default: 30)";
|
|
29
|
+
};
|
|
30
|
+
readonly "skip-cache": {
|
|
31
|
+
readonly type: "boolean";
|
|
32
|
+
readonly description: "Bypass server-side cache";
|
|
33
|
+
};
|
|
34
|
+
readonly output: {
|
|
35
|
+
readonly type: "string";
|
|
36
|
+
readonly description: "json";
|
|
37
|
+
readonly default: "json";
|
|
38
|
+
};
|
|
39
|
+
}>;
|
|
40
|
+
//# sourceMappingURL=demographics.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"demographics.d.ts","sourceRoot":"","sources":["../../../../src/commands/ads/linkedin/demographics.ts"],"names":[],"mappings":"AA6CA,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA0F9B,CAAC"}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { defineCommand } from "citty";
|
|
2
|
+
import { apiPost } from "../../../client.js";
|
|
3
|
+
import { writeAdsJson } from "../output.js";
|
|
4
|
+
import { csvOrJson, daysAgoIso, handleLinkedinError, todayIso } from "./shared.js";
|
|
5
|
+
const DEFAULT_PIVOTS = ["job-title", "company", "industry", "seniority", "job-function", "company-size"];
|
|
6
|
+
function numberOf(v) {
|
|
7
|
+
if (typeof v === "number")
|
|
8
|
+
return Number.isFinite(v) ? v : 0;
|
|
9
|
+
if (typeof v === "string") {
|
|
10
|
+
const n = Number(v);
|
|
11
|
+
return Number.isFinite(n) ? n : 0;
|
|
12
|
+
}
|
|
13
|
+
return 0;
|
|
14
|
+
}
|
|
15
|
+
function topByImpressions(rows, limit) {
|
|
16
|
+
return [...rows].sort((a, b) => numberOf(b.impressions) - numberOf(a.impressions)).slice(0, limit);
|
|
17
|
+
}
|
|
18
|
+
export const demographicsCommand = defineCommand({
|
|
19
|
+
meta: {
|
|
20
|
+
name: "demographics",
|
|
21
|
+
description: `Sweep all firmographic pivots in one command — LinkedIn's superpower.
|
|
22
|
+
|
|
23
|
+
Default pivots:
|
|
24
|
+
job-title, company, industry, seniority, job-function, company-size
|
|
25
|
+
|
|
26
|
+
Use --pivots to narrow the sweep. Output is one section per pivot, each with
|
|
27
|
+
top-N rows by impressions (configurable via --top-n).
|
|
28
|
+
|
|
29
|
+
Demographic data is delayed 12-24h with a ≥3-event privacy floor — small
|
|
30
|
+
buckets are silently dropped. The CLI surfaces this in 'warnings' when the
|
|
31
|
+
result set comes back unexpectedly empty.
|
|
32
|
+
|
|
33
|
+
Examples:
|
|
34
|
+
baker ads linkedin demographics --campaign-id 1234
|
|
35
|
+
baker ads linkedin demographics --campaign-id 1234 --pivots job-title,company
|
|
36
|
+
baker ads linkedin demographics --campaign-id 1234 --last-days 30 --top-n 25`,
|
|
37
|
+
},
|
|
38
|
+
args: {
|
|
39
|
+
"campaign-id": { type: "string", description: "Comma-separated campaign IDs (required)" },
|
|
40
|
+
pivots: { type: "string", description: `CSV of pivots (default: ${DEFAULT_PIVOTS.join(",")})` },
|
|
41
|
+
"top-n": { type: "string", description: "Top rows per pivot (default: 10)" },
|
|
42
|
+
intent: { type: "string", description: "baseline | revenue | funnel | engagement | lead-gen (default: baseline)" },
|
|
43
|
+
start: { type: "string", description: "Start date YYYY-MM-DD" },
|
|
44
|
+
end: { type: "string", description: "End date YYYY-MM-DD" },
|
|
45
|
+
"last-days": { type: "string", description: "Window relative to today (default: 30)" },
|
|
46
|
+
"skip-cache": { type: "boolean", description: "Bypass server-side cache" },
|
|
47
|
+
output: { type: "string", description: "json", default: "json" },
|
|
48
|
+
},
|
|
49
|
+
run: async ({ args }) => {
|
|
50
|
+
const fmt = csvOrJson(args);
|
|
51
|
+
if (fmt !== "json") {
|
|
52
|
+
writeAdsJson({
|
|
53
|
+
ok: false,
|
|
54
|
+
error: {
|
|
55
|
+
code: "UNSUPPORTED_FORMAT",
|
|
56
|
+
message: "demographics returns a structured per-pivot report — only --output json supported.",
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
const campaignIdsRaw = args["campaign-id"];
|
|
62
|
+
if (!campaignIdsRaw) {
|
|
63
|
+
handleLinkedinError(new Error("--campaign-id is required (CSV for multi)."));
|
|
64
|
+
}
|
|
65
|
+
const ids = campaignIdsRaw
|
|
66
|
+
.split(",")
|
|
67
|
+
.map((s) => s.trim())
|
|
68
|
+
.filter(Boolean);
|
|
69
|
+
const pivotsRaw = args.pivots?.split(",").map((s) => s.trim().toLowerCase());
|
|
70
|
+
const pivots = (pivotsRaw && pivotsRaw.length > 0 ? pivotsRaw : DEFAULT_PIVOTS);
|
|
71
|
+
const topN = args["top-n"] ? Number(args["top-n"]) : 10;
|
|
72
|
+
const intent = args.intent ?? "baseline";
|
|
73
|
+
const { start, end } = resolveRange(args);
|
|
74
|
+
try {
|
|
75
|
+
const results = [];
|
|
76
|
+
for (const pivot of pivots) {
|
|
77
|
+
const data = await apiPost("/api/ads/linkedin/analytics", {
|
|
78
|
+
request: {
|
|
79
|
+
level: "campaign",
|
|
80
|
+
ids,
|
|
81
|
+
intent,
|
|
82
|
+
pivot,
|
|
83
|
+
start,
|
|
84
|
+
end,
|
|
85
|
+
granularity: "ALL",
|
|
86
|
+
limit: 500,
|
|
87
|
+
},
|
|
88
|
+
skipCache: Boolean(args["skip-cache"]),
|
|
89
|
+
});
|
|
90
|
+
results.push({ pivot, rows: topByImpressions(data.rows, topN), warnings: data.warnings });
|
|
91
|
+
}
|
|
92
|
+
writeAdsJson({
|
|
93
|
+
ok: true,
|
|
94
|
+
data: {
|
|
95
|
+
query: { campaigns: ids, start, end, intent, topN },
|
|
96
|
+
pivots: results,
|
|
97
|
+
},
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
catch (err) {
|
|
101
|
+
handleLinkedinError(err);
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
});
|
|
105
|
+
function resolveRange(args) {
|
|
106
|
+
const start = args.start;
|
|
107
|
+
const end = args.end;
|
|
108
|
+
if (start) {
|
|
109
|
+
return { start, end };
|
|
110
|
+
}
|
|
111
|
+
const days = args["last-days"] ? Number(args["last-days"]) : 30;
|
|
112
|
+
if (!Number.isFinite(days) || days < 1) {
|
|
113
|
+
handleLinkedinError(new Error("Invalid --last-days; must be a positive integer."));
|
|
114
|
+
}
|
|
115
|
+
return { start: daysAgoIso(days), end: todayIso() };
|
|
116
|
+
}
|
|
117
|
+
//# sourceMappingURL=demographics.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"demographics.js","sourceRoot":"","sources":["../../../../src/commands/ads/linkedin/demographics.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"facets.d.ts","sourceRoot":"","sources":["../../../../src/commands/ads/linkedin/facets.ts"],"names":[],"mappings":"AA4FA,eAAO,MAAM,aAAa,qDAcxB,CAAC"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { defineCommand } from "citty";
|
|
2
|
+
import { apiGet } from "../../../client.js";
|
|
3
|
+
import { writeAdsJson, writeAdsOutput } from "../output.js";
|
|
4
|
+
import { csvOrJson, handleLinkedinError } from "./shared.js";
|
|
5
|
+
const listCmd = defineCommand({
|
|
6
|
+
meta: {
|
|
7
|
+
name: "list",
|
|
8
|
+
description: `List every targeting facet LinkedIn supports.
|
|
9
|
+
|
|
10
|
+
Cached 7 days. Use this to discover what dimensions you can target on (industries,
|
|
11
|
+
seniorities, titles, employers, growthRate, companyCategory, skills, etc.).`,
|
|
12
|
+
},
|
|
13
|
+
args: {
|
|
14
|
+
"skip-cache": { type: "boolean", description: "Bypass server-side cache" },
|
|
15
|
+
output: { type: "string", description: "json|csv|jsonl|md", default: "json" },
|
|
16
|
+
},
|
|
17
|
+
run: async ({ args }) => {
|
|
18
|
+
try {
|
|
19
|
+
const params = {};
|
|
20
|
+
if (args["skip-cache"])
|
|
21
|
+
params["skip-cache"] = "true";
|
|
22
|
+
const data = await apiGet("/api/ads/linkedin/facets", params);
|
|
23
|
+
const fmt = csvOrJson(args);
|
|
24
|
+
if (fmt !== "json") {
|
|
25
|
+
writeAdsOutput(data, fmt);
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
writeAdsJson({ ok: true, data });
|
|
29
|
+
}
|
|
30
|
+
catch (err) {
|
|
31
|
+
handleLinkedinError(err);
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
const valuesCmd = defineCommand({
|
|
36
|
+
meta: {
|
|
37
|
+
name: "values",
|
|
38
|
+
description: `Look up entity values for a single facet — full list or typeahead search.
|
|
39
|
+
|
|
40
|
+
Examples:
|
|
41
|
+
baker ads linkedin facets values --facet seniorities — all seniority URNs
|
|
42
|
+
baker ads linkedin facets values --facet titles --query "marketing director" — typeahead
|
|
43
|
+
baker ads linkedin facets values --facet industries --output csv
|
|
44
|
+
|
|
45
|
+
The --facet flag accepts the bare name (industries, seniorities, titles, etc.)
|
|
46
|
+
or the full URN (urn:li:adTargetingFacet:industries).`,
|
|
47
|
+
},
|
|
48
|
+
args: {
|
|
49
|
+
facet: { type: "string", description: "Facet name (e.g. industries) or URN", required: true },
|
|
50
|
+
query: { type: "string", description: "Typeahead query (auto-switches finder to typeahead)" },
|
|
51
|
+
finder: { type: "string", description: "adTargetingFacet | typeahead | similarEntities (default: auto)" },
|
|
52
|
+
locale: { type: "string", description: "Locale (default: en_US)" },
|
|
53
|
+
"skip-cache": { type: "boolean", description: "Bypass server-side cache" },
|
|
54
|
+
output: { type: "string", description: "json|csv|jsonl|md", default: "json" },
|
|
55
|
+
},
|
|
56
|
+
run: async ({ args }) => {
|
|
57
|
+
try {
|
|
58
|
+
const params = { facet: String(args.facet) };
|
|
59
|
+
const finder = args.finder ?? (args.query ? "typeahead" : "adTargetingFacet");
|
|
60
|
+
params.finder = finder;
|
|
61
|
+
if (args.query)
|
|
62
|
+
params.query = String(args.query);
|
|
63
|
+
if (args.locale)
|
|
64
|
+
params.locale = String(args.locale);
|
|
65
|
+
if (args["skip-cache"])
|
|
66
|
+
params["skip-cache"] = "true";
|
|
67
|
+
const data = await apiGet("/api/ads/linkedin/facets/entities", params);
|
|
68
|
+
const fmt = csvOrJson(args);
|
|
69
|
+
if (fmt !== "json") {
|
|
70
|
+
writeAdsOutput(data, fmt);
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
writeAdsJson({ ok: true, data });
|
|
74
|
+
}
|
|
75
|
+
catch (err) {
|
|
76
|
+
handleLinkedinError(err);
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
});
|
|
80
|
+
export const facetsCommand = defineCommand({
|
|
81
|
+
meta: {
|
|
82
|
+
name: "facets",
|
|
83
|
+
description: `LinkedIn targeting facets and entity lookup.
|
|
84
|
+
|
|
85
|
+
Subcommands:
|
|
86
|
+
list — every facet (industries, seniorities, titles, employers, …)
|
|
87
|
+
values --facet titles — entity URNs for one facet
|
|
88
|
+
values --facet titles --query "marketing director" — typeahead`,
|
|
89
|
+
},
|
|
90
|
+
subCommands: {
|
|
91
|
+
list: listCmd,
|
|
92
|
+
values: valuesCmd,
|
|
93
|
+
},
|
|
94
|
+
});
|
|
95
|
+
//# sourceMappingURL=facets.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"facets.js","sourceRoot":"","sources":["../../../../src/commands/ads/linkedin/facets.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
export declare const forecastCommand: 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";
|
|
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 "daily-budget": {
|
|
21
|
+
readonly type: "string";
|
|
22
|
+
readonly description: "e.g. '200 USD' or '200'";
|
|
23
|
+
};
|
|
24
|
+
readonly "total-budget": {
|
|
25
|
+
readonly type: "string";
|
|
26
|
+
readonly description: "e.g. '6000 USD'";
|
|
27
|
+
};
|
|
28
|
+
readonly bid: {
|
|
29
|
+
readonly type: "string";
|
|
30
|
+
readonly description: "Bid e.g. '8 USD'";
|
|
31
|
+
};
|
|
32
|
+
readonly targeting: {
|
|
33
|
+
readonly type: "string";
|
|
34
|
+
readonly description: "Inline JSON targetingCriteria";
|
|
35
|
+
};
|
|
36
|
+
readonly "targeting-file": {
|
|
37
|
+
readonly type: "string";
|
|
38
|
+
readonly description: "Path to JSON file with targetingCriteria";
|
|
39
|
+
};
|
|
40
|
+
readonly "skip-cache": {
|
|
41
|
+
readonly type: "boolean";
|
|
42
|
+
readonly description: "Bypass server-side cache";
|
|
43
|
+
};
|
|
44
|
+
readonly output: {
|
|
45
|
+
readonly type: "string";
|
|
46
|
+
readonly description: "json";
|
|
47
|
+
readonly default: "json";
|
|
48
|
+
};
|
|
49
|
+
}>;
|
|
50
|
+
//# sourceMappingURL=forecast.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"forecast.d.ts","sourceRoot":"","sources":["../../../../src/commands/ads/linkedin/forecast.ts"],"names":[],"mappings":"AA+CA,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA4C1B,CAAC"}
|
|
@@ -0,0 +1,83 @@
|
|
|
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
|
+
function loadTargeting(args) {
|
|
7
|
+
const inline = args.targeting;
|
|
8
|
+
if (inline) {
|
|
9
|
+
try {
|
|
10
|
+
return JSON.parse(inline);
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
handleLinkedinError(new Error("--targeting must be valid JSON."));
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
const file = args["targeting-file"];
|
|
17
|
+
if (file) {
|
|
18
|
+
try {
|
|
19
|
+
return JSON.parse(readFileSync(file, "utf-8"));
|
|
20
|
+
}
|
|
21
|
+
catch (e) {
|
|
22
|
+
handleLinkedinError(new Error(`Failed to read --targeting-file: ${e instanceof Error ? e.message : "I/O error"}`));
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
handleLinkedinError(new Error("Pass --targeting-file <path> or --targeting '{...JSON...}'"));
|
|
26
|
+
}
|
|
27
|
+
function parseMoney(raw) {
|
|
28
|
+
if (!raw)
|
|
29
|
+
return undefined;
|
|
30
|
+
// Accept either "100 USD" or just "100" (defaults to USD).
|
|
31
|
+
const m = /^(\d+(?:\.\d+)?)(?:\s+([A-Z]{3}))?$/.exec(raw.trim());
|
|
32
|
+
if (!m) {
|
|
33
|
+
handleLinkedinError(new Error(`Invalid money "${raw}". Use "100" or "100 EUR".`));
|
|
34
|
+
}
|
|
35
|
+
return { amount: m[1] ?? "0", currencyCode: m[2] ?? "USD" };
|
|
36
|
+
}
|
|
37
|
+
export const forecastCommand = defineCommand({
|
|
38
|
+
meta: {
|
|
39
|
+
name: "forecast",
|
|
40
|
+
description: `Forecast reach + impressions + clicks + spend for a hypothetical campaign.
|
|
41
|
+
|
|
42
|
+
Wraps /rest/adSupplyForecasts. Useful for greenfield (§14) — answers
|
|
43
|
+
"if we launch with this budget + targeting + objective, what's the expected delivery?"
|
|
44
|
+
|
|
45
|
+
Examples:
|
|
46
|
+
baker ads linkedin forecast --account-id 503001492 --objective LEAD_GENERATION --cost-type CPC \\
|
|
47
|
+
--daily-budget "200 USD" --bid "8 USD" --targeting-file targeting.json`,
|
|
48
|
+
},
|
|
49
|
+
args: {
|
|
50
|
+
"account-id": { type: "string", description: "Numeric account ID or urn:li:sponsoredAccount:N" },
|
|
51
|
+
"account-urn": { type: "string", description: "Alias for --account-id" },
|
|
52
|
+
objective: { type: "string", description: "Objective type", required: true },
|
|
53
|
+
"cost-type": { type: "string", description: "CPC | CPM | CPV | CPS", required: true },
|
|
54
|
+
"daily-budget": { type: "string", description: "e.g. '200 USD' or '200'" },
|
|
55
|
+
"total-budget": { type: "string", description: "e.g. '6000 USD'" },
|
|
56
|
+
bid: { type: "string", description: "Bid e.g. '8 USD'" },
|
|
57
|
+
targeting: { type: "string", description: "Inline JSON targetingCriteria" },
|
|
58
|
+
"targeting-file": { type: "string", description: "Path to JSON file with targetingCriteria" },
|
|
59
|
+
"skip-cache": { type: "boolean", description: "Bypass server-side cache" },
|
|
60
|
+
output: { type: "string", description: "json", default: "json" },
|
|
61
|
+
},
|
|
62
|
+
run: async ({ args }) => {
|
|
63
|
+
const accountId = resolveAccountIdArg(args);
|
|
64
|
+
const targetingCriteria = loadTargeting(args);
|
|
65
|
+
try {
|
|
66
|
+
const data = await apiPost("/api/ads/linkedin/forecast", {
|
|
67
|
+
accountId,
|
|
68
|
+
objectiveType: String(args.objective),
|
|
69
|
+
costType: String(args["cost-type"]).toUpperCase(),
|
|
70
|
+
targetingCriteria,
|
|
71
|
+
dailyBudget: parseMoney(args["daily-budget"]),
|
|
72
|
+
totalBudget: parseMoney(args["total-budget"]),
|
|
73
|
+
bid: parseMoney(args.bid),
|
|
74
|
+
skipCache: Boolean(args["skip-cache"]),
|
|
75
|
+
});
|
|
76
|
+
writeAdsJson({ ok: true, data });
|
|
77
|
+
}
|
|
78
|
+
catch (err) {
|
|
79
|
+
handleLinkedinError(err);
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
});
|
|
83
|
+
//# sourceMappingURL=forecast.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"forecast.js","sourceRoot":"","sources":["../../../../src/commands/ads/linkedin/forecast.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;AAWvE,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,iCAAiC,CAAC,CAAC,CAAC;QACpE,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,SAAS,UAAU,CAAC,GAAuB;IACzC,IAAI,CAAC,GAAG;QAAE,OAAO,SAAS,CAAC;IAC3B,2DAA2D;IAC3D,MAAM,CAAC,GAAG,qCAAqC,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;IACjE,IAAI,CAAC,CAAC,EAAE,CAAC;QACP,mBAAmB,CAAC,IAAI,KAAK,CAAC,kBAAkB,GAAG,4BAA4B,CAAC,CAAC,CAAC;IACpF,CAAC;IACD,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,EAAE,YAAY,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,EAAE,CAAC;AAC9D,CAAC;AAED,MAAM,CAAC,MAAM,eAAe,GAAG,aAAa,CAAC;IAC3C,IAAI,EAAE;QACJ,IAAI,EAAE,UAAU;QAChB,WAAW,EAAE;;;;;;;2EAO0D;KACxE;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,gBAAgB,EAAE,QAAQ,EAAE,IAAI,EAAE;QAC5E,WAAW,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,uBAAuB,EAAE,QAAQ,EAAE,IAAI,EAAE;QACrF,cAAc,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,yBAAyB,EAAE;QAC1E,cAAc,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,iBAAiB,EAAE;QAClE,GAAG,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,kBAAkB,EAAE;QACxD,SAAS,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,+BAA+B,EAAE;QAC3E,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,CAAiB,4BAA4B,EAAE;gBACvE,SAAS;gBACT,aAAa,EAAE,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC;gBACrC,QAAQ,EAAE,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,WAAW,EAAE;gBACjD,iBAAiB;gBACjB,WAAW,EAAE,UAAU,CAAC,IAAI,CAAC,cAAc,CAAuB,CAAC;gBACnE,WAAW,EAAE,UAAU,CAAC,IAAI,CAAC,cAAc,CAAuB,CAAC;gBACnE,GAAG,EAAE,UAAU,CAAC,IAAI,CAAC,GAAyB,CAAC;gBAC/C,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,19 @@
|
|
|
1
|
+
import "./schemas.ts";
|
|
2
|
+
/**
|
|
3
|
+
* LinkedIn Ads CLI — designed around LinkedIn's distinguishing strength:
|
|
4
|
+
* firmographic (per-job-title, per-company, per-industry, per-seniority)
|
|
5
|
+
* pivots on adAnalytics. AI agents should reach for `linkedin analytics
|
|
6
|
+
* --pivot job-title` and `--pivot company` when answering ABM/persona
|
|
7
|
+
* questions — Meta and Google can't do that.
|
|
8
|
+
*
|
|
9
|
+
* Read-only v1. All analytics queries default to `--level account --intent
|
|
10
|
+
* baseline --granularity DAILY --last-days 7`. When pivoting on a MEMBER_*
|
|
11
|
+
* dimension, the CLI auto-forces granularity=ALL (LinkedIn rejects
|
|
12
|
+
* DAILY + demographic) and surfaces a "demographic delayed 12-24h" warning.
|
|
13
|
+
*
|
|
14
|
+
* Authentication: requires the LinkedIn integration to be connected in the
|
|
15
|
+
* dashboard with `r_ads` and `r_ads_reporting` scopes. CLI talks to Baker's
|
|
16
|
+
* backend, which holds the OAuth token.
|
|
17
|
+
*/
|
|
18
|
+
export declare const linkedinCommand: import("citty").CommandDef<import("citty").ArgsDef>;
|
|
19
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/commands/ads/linkedin/index.ts"],"names":[],"mappings":"AACA,OAAO,cAAc,CAAC;AAkBtB;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,eAAe,qDA+C1B,CAAC"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { defineCommand } from "citty";
|
|
2
|
+
import "./schemas.js"; // side-effect import: registers `baker schema ads.linkedin.*` entries
|
|
3
|
+
import { accountCommand } from "./account.js";
|
|
4
|
+
import { accountsCommand } from "./accounts.js";
|
|
5
|
+
import { analyticsCommand } from "./analytics.js";
|
|
6
|
+
import { audienceSizeCommand } from "./audience-size.js";
|
|
7
|
+
import { auditCommand } from "./audit.js";
|
|
8
|
+
import { bidPricingCommand } from "./bid-pricing.js";
|
|
9
|
+
import { campaignGroupsCommand } from "./campaign-groups.js";
|
|
10
|
+
import { campaignsCommand } from "./campaigns.js";
|
|
11
|
+
import { conversationCommand } from "./conversation.js";
|
|
12
|
+
import { conversionsCommand } from "./conversions.js";
|
|
13
|
+
import { creativesCommand } from "./creatives.js";
|
|
14
|
+
import { demographicsCommand } from "./demographics.js";
|
|
15
|
+
import { facetsCommand } from "./facets.js";
|
|
16
|
+
import { forecastCommand } from "./forecast.js";
|
|
17
|
+
import { leadsCommand } from "./leads.js";
|
|
18
|
+
import { topCompaniesCommand } from "./top-companies.js";
|
|
19
|
+
/**
|
|
20
|
+
* LinkedIn Ads CLI — designed around LinkedIn's distinguishing strength:
|
|
21
|
+
* firmographic (per-job-title, per-company, per-industry, per-seniority)
|
|
22
|
+
* pivots on adAnalytics. AI agents should reach for `linkedin analytics
|
|
23
|
+
* --pivot job-title` and `--pivot company` when answering ABM/persona
|
|
24
|
+
* questions — Meta and Google can't do that.
|
|
25
|
+
*
|
|
26
|
+
* Read-only v1. All analytics queries default to `--level account --intent
|
|
27
|
+
* baseline --granularity DAILY --last-days 7`. When pivoting on a MEMBER_*
|
|
28
|
+
* dimension, the CLI auto-forces granularity=ALL (LinkedIn rejects
|
|
29
|
+
* DAILY + demographic) and surfaces a "demographic delayed 12-24h" warning.
|
|
30
|
+
*
|
|
31
|
+
* Authentication: requires the LinkedIn integration to be connected in the
|
|
32
|
+
* dashboard with `r_ads` and `r_ads_reporting` scopes. CLI talks to Baker's
|
|
33
|
+
* backend, which holds the OAuth token.
|
|
34
|
+
*/
|
|
35
|
+
export const linkedinCommand = defineCommand({
|
|
36
|
+
meta: {
|
|
37
|
+
name: "linkedin",
|
|
38
|
+
description: `LinkedIn Marketing API — AI-first command surface for B2B ad insights.
|
|
39
|
+
|
|
40
|
+
Discovery:
|
|
41
|
+
baker ads linkedin accounts — accounts in this company's connected scope
|
|
42
|
+
baker ads linkedin accounts --include-all — every account the token can see
|
|
43
|
+
baker ads linkedin account --account-id 503001492 — single-account detail (currency, type, status)
|
|
44
|
+
|
|
45
|
+
Hierarchy (default ACTIVE-only — pass --all-statuses to widen):
|
|
46
|
+
baker ads linkedin campaign-groups --account-id 503001492
|
|
47
|
+
baker ads linkedin campaigns --account-id 503001492
|
|
48
|
+
baker ads linkedin campaigns --account-id 503001492 --campaign-group-id 12345
|
|
49
|
+
baker ads linkedin creatives --account-id 503001492 --campaign-id 67890
|
|
50
|
+
|
|
51
|
+
Performance — the workhorse (3 axes):
|
|
52
|
+
baker ads linkedin analytics — defaults: level=account, intent=baseline, last_7d, DAILY
|
|
53
|
+
baker ads linkedin analytics --intent revenue --last-days 28
|
|
54
|
+
baker ads linkedin analytics --level campaign --campaign-id 1234 --pivot job-title — LinkedIn's superpower (per-title perf)
|
|
55
|
+
baker ads linkedin analytics --level campaign --campaign-id 1234 --pivot company — top companies seeing the ad (ABM gold)
|
|
56
|
+
baker ads linkedin analytics --level campaign --campaign-id 1234 --intent ranking — fatigue: derives frequency
|
|
57
|
+
baker ads linkedin analytics --list-intents
|
|
58
|
+
baker ads linkedin analytics --list-pivots
|
|
59
|
+
|
|
60
|
+
Account ID format:
|
|
61
|
+
--account-id accepts numeric (503001492) or URN (urn:li:sponsoredAccount:503001492).
|
|
62
|
+
Set BAKER_LINKEDIN_AD_ACCOUNT_ID env to skip --account-id on every call.`,
|
|
63
|
+
},
|
|
64
|
+
subCommands: {
|
|
65
|
+
accounts: accountsCommand,
|
|
66
|
+
account: accountCommand,
|
|
67
|
+
"campaign-groups": campaignGroupsCommand,
|
|
68
|
+
campaigns: campaignsCommand,
|
|
69
|
+
creatives: creativesCommand,
|
|
70
|
+
analytics: analyticsCommand,
|
|
71
|
+
demographics: demographicsCommand,
|
|
72
|
+
"top-companies": topCompaniesCommand,
|
|
73
|
+
facets: facetsCommand,
|
|
74
|
+
"audience-size": audienceSizeCommand,
|
|
75
|
+
"bid-pricing": bidPricingCommand,
|
|
76
|
+
forecast: forecastCommand,
|
|
77
|
+
leads: leadsCommand,
|
|
78
|
+
conversions: conversionsCommand,
|
|
79
|
+
conversation: conversationCommand,
|
|
80
|
+
audit: auditCommand,
|
|
81
|
+
},
|
|
82
|
+
});
|
|
83
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/commands/ads/linkedin/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AACtC,OAAO,cAAc,CAAC,CAAC,sEAAsE;AAC7F,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAEzD;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,aAAa,CAAC;IAC3C,IAAI,EAAE;QACJ,IAAI,EAAE,UAAU;QAChB,WAAW,EAAE;;;;;;;;;;;;;;;;;;;;;;;;2EAwB0D;KACxE;IACD,WAAW,EAAE;QACX,QAAQ,EAAE,eAAe;QACzB,OAAO,EAAE,cAAc;QACvB,iBAAiB,EAAE,qBAAqB;QACxC,SAAS,EAAE,gBAAgB;QAC3B,SAAS,EAAE,gBAAgB;QAC3B,SAAS,EAAE,gBAAgB;QAC3B,YAAY,EAAE,mBAAmB;QACjC,eAAe,EAAE,mBAAmB;QACpC,MAAM,EAAE,aAAa;QACrB,eAAe,EAAE,mBAAmB;QACpC,aAAa,EAAE,iBAAiB;QAChC,QAAQ,EAAE,eAAe;QACzB,KAAK,EAAE,YAAY;QACnB,WAAW,EAAE,kBAAkB;QAC/B,YAAY,EAAE,mBAAmB;QACjC,KAAK,EAAE,YAAY;KACpB;CACF,CAAC,CAAC"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export declare const leadsCommand: 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 "form-id": {
|
|
11
|
+
readonly type: "string";
|
|
12
|
+
readonly description: "Filter by Lead Gen Form ID";
|
|
13
|
+
};
|
|
14
|
+
readonly "campaign-id": {
|
|
15
|
+
readonly type: "string";
|
|
16
|
+
readonly description: "Filter by campaign ID";
|
|
17
|
+
};
|
|
18
|
+
readonly "since-days": {
|
|
19
|
+
readonly type: "string";
|
|
20
|
+
readonly description: "Only responses submitted in the last N days";
|
|
21
|
+
};
|
|
22
|
+
readonly "since-ms": {
|
|
23
|
+
readonly type: "string";
|
|
24
|
+
readonly description: "Only responses submitted at or after this epoch ms (alternative to --since-days)";
|
|
25
|
+
};
|
|
26
|
+
readonly limit: {
|
|
27
|
+
readonly type: "string";
|
|
28
|
+
readonly description: "Max rows (default: 500)";
|
|
29
|
+
};
|
|
30
|
+
readonly "skip-cache": {
|
|
31
|
+
readonly type: "boolean";
|
|
32
|
+
readonly description: "Bypass server-side cache";
|
|
33
|
+
};
|
|
34
|
+
readonly output: {
|
|
35
|
+
readonly type: "string";
|
|
36
|
+
readonly description: "json|csv|jsonl|md";
|
|
37
|
+
readonly default: "json";
|
|
38
|
+
};
|
|
39
|
+
}>;
|
|
40
|
+
//# sourceMappingURL=leads.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"leads.d.ts","sourceRoot":"","sources":["../../../../src/commands/ads/linkedin/leads.ts"],"names":[],"mappings":"AAiBA,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA+DvB,CAAC"}
|