@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,76 @@
|
|
|
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. See LinkedIn 'targetingCriteria' shape."));
|
|
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
|
+
export const bidPricingCommand = defineCommand({
|
|
28
|
+
meta: {
|
|
29
|
+
name: "bid-pricing",
|
|
30
|
+
description: `Get LinkedIn's suggested bid range for a targeting + objective + cost type.
|
|
31
|
+
|
|
32
|
+
Returns LinkedIn's min / suggested / max bid plus the playbook §06 floor
|
|
33
|
+
(2/3 of suggested — the recommended manual CPC starting point).
|
|
34
|
+
|
|
35
|
+
Objective types: BRAND_AWARENESS | WEBSITE_TRAFFIC | WEBSITE_VISIT |
|
|
36
|
+
ENGAGEMENT | WEBSITE_CONVERSION | LEAD_GENERATION |
|
|
37
|
+
JOB_APPLICANT | VIDEO_VIEW
|
|
38
|
+
Cost types: CPC | CPM | CPV | CPS
|
|
39
|
+
|
|
40
|
+
Examples:
|
|
41
|
+
baker ads linkedin bid-pricing --account-id 503001492 --objective WEBSITE_CONVERSION --cost-type CPC --targeting-file targeting.json
|
|
42
|
+
baker ads linkedin bid-pricing --account-id 503001492 --objective LEAD_GENERATION --cost-type CPM --targeting '{"include":...}'`,
|
|
43
|
+
},
|
|
44
|
+
args: {
|
|
45
|
+
"account-id": { type: "string", description: "Numeric account ID or urn:li:sponsoredAccount:N" },
|
|
46
|
+
"account-urn": { type: "string", description: "Alias for --account-id" },
|
|
47
|
+
objective: {
|
|
48
|
+
type: "string",
|
|
49
|
+
description: "Objective type (e.g. WEBSITE_CONVERSION, LEAD_GENERATION)",
|
|
50
|
+
required: true,
|
|
51
|
+
},
|
|
52
|
+
"cost-type": { type: "string", description: "CPC | CPM | CPV | CPS", required: true },
|
|
53
|
+
targeting: { type: "string", description: "Inline JSON targetingCriteria" },
|
|
54
|
+
"targeting-file": { type: "string", description: "Path to JSON file with targetingCriteria" },
|
|
55
|
+
"skip-cache": { type: "boolean", description: "Bypass server-side cache" },
|
|
56
|
+
output: { type: "string", description: "json", default: "json" },
|
|
57
|
+
},
|
|
58
|
+
run: async ({ args }) => {
|
|
59
|
+
const accountId = resolveAccountIdArg(args);
|
|
60
|
+
const targetingCriteria = loadTargeting(args);
|
|
61
|
+
try {
|
|
62
|
+
const data = await apiPost("/api/ads/linkedin/bid-pricing", {
|
|
63
|
+
accountId,
|
|
64
|
+
objectiveType: String(args.objective),
|
|
65
|
+
costType: String(args["cost-type"]).toUpperCase(),
|
|
66
|
+
targetingCriteria,
|
|
67
|
+
skipCache: Boolean(args["skip-cache"]),
|
|
68
|
+
});
|
|
69
|
+
writeAdsJson({ ok: true, data });
|
|
70
|
+
}
|
|
71
|
+
catch (err) {
|
|
72
|
+
handleLinkedinError(err);
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
});
|
|
76
|
+
//# sourceMappingURL=bid-pricing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bid-pricing.js","sourceRoot":"","sources":["../../../../src/commands/ads/linkedin/bid-pricing.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;AAevE,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,iBAAiB,GAAG,aAAa,CAAC;IAC7C,IAAI,EAAE;QACJ,IAAI,EAAE,aAAa;QACnB,WAAW,EAAE;;;;;;;;;;;;kIAYiH;KAC/H;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;YACT,IAAI,EAAE,QAAQ;YACd,WAAW,EAAE,2DAA2D;YACxE,QAAQ,EAAE,IAAI;SACf;QACD,WAAW,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,uBAAuB,EAAE,QAAQ,EAAE,IAAI,EAAE;QACrF,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,CAAmB,+BAA+B,EAAE;gBAC5E,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,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,32 @@
|
|
|
1
|
+
export declare const campaignGroupsCommand: 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 "all-statuses": {
|
|
11
|
+
readonly type: "boolean";
|
|
12
|
+
readonly description: "Drop the default ACTIVE filter";
|
|
13
|
+
};
|
|
14
|
+
readonly statuses: {
|
|
15
|
+
readonly type: "string";
|
|
16
|
+
readonly description: "CSV of statuses (ACTIVE,PAUSED,ARCHIVED,COMPLETED,DRAFT)";
|
|
17
|
+
};
|
|
18
|
+
readonly limit: {
|
|
19
|
+
readonly type: "string";
|
|
20
|
+
readonly description: "Max rows (default: 500)";
|
|
21
|
+
};
|
|
22
|
+
readonly "skip-cache": {
|
|
23
|
+
readonly type: "boolean";
|
|
24
|
+
readonly description: "Bypass server-side cache";
|
|
25
|
+
};
|
|
26
|
+
readonly output: {
|
|
27
|
+
readonly type: "string";
|
|
28
|
+
readonly description: "json|csv|jsonl|md";
|
|
29
|
+
readonly default: "json";
|
|
30
|
+
};
|
|
31
|
+
}>;
|
|
32
|
+
//# sourceMappingURL=campaign-groups.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"campaign-groups.d.ts","sourceRoot":"","sources":["../../../../src/commands/ads/linkedin/campaign-groups.ts"],"names":[],"mappings":"AAgBA,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAyChC,CAAC"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { defineCommand } from "citty";
|
|
2
|
+
import { apiGet } from "../../../client.js";
|
|
3
|
+
import { writeAdsJson, writeAdsOutput } from "../output.js";
|
|
4
|
+
import { csvOrJson, handleLinkedinError, resolveAccountIdArg, resolveStatusFilter } from "./shared.js";
|
|
5
|
+
export const campaignGroupsCommand = defineCommand({
|
|
6
|
+
meta: {
|
|
7
|
+
name: "campaign-groups",
|
|
8
|
+
description: `List LinkedIn campaign groups.
|
|
9
|
+
|
|
10
|
+
Default: ACTIVE only. Pass --all-statuses to widen, or --statuses ACTIVE,PAUSED to override.
|
|
11
|
+
|
|
12
|
+
Examples:
|
|
13
|
+
baker ads linkedin campaign-groups --account-id 503001492
|
|
14
|
+
baker ads linkedin campaign-groups --account-id 503001492 --all-statuses
|
|
15
|
+
baker ads linkedin campaign-groups --account-id 503001492 --statuses ACTIVE,PAUSED --output csv`,
|
|
16
|
+
},
|
|
17
|
+
args: {
|
|
18
|
+
"account-id": { type: "string", description: "Numeric account ID or urn:li:sponsoredAccount:N" },
|
|
19
|
+
"account-urn": { type: "string", description: "Alias for --account-id" },
|
|
20
|
+
"all-statuses": { type: "boolean", description: "Drop the default ACTIVE filter" },
|
|
21
|
+
statuses: { type: "string", description: "CSV of statuses (ACTIVE,PAUSED,ARCHIVED,COMPLETED,DRAFT)" },
|
|
22
|
+
limit: { type: "string", description: "Max rows (default: 500)" },
|
|
23
|
+
"skip-cache": { type: "boolean", description: "Bypass server-side cache" },
|
|
24
|
+
output: { type: "string", description: "json|csv|jsonl|md", default: "json" },
|
|
25
|
+
},
|
|
26
|
+
run: async ({ args }) => {
|
|
27
|
+
const accountId = resolveAccountIdArg(args);
|
|
28
|
+
try {
|
|
29
|
+
const params = { "account-id": accountId };
|
|
30
|
+
const statuses = resolveStatusFilter(args);
|
|
31
|
+
if (statuses)
|
|
32
|
+
params.statuses = statuses.join(",");
|
|
33
|
+
if (args.limit)
|
|
34
|
+
params.limit = String(args.limit);
|
|
35
|
+
if (args["skip-cache"])
|
|
36
|
+
params["skip-cache"] = "true";
|
|
37
|
+
const data = await apiGet("/api/ads/linkedin/campaign-groups", params);
|
|
38
|
+
const fmt = csvOrJson(args);
|
|
39
|
+
if (fmt !== "json") {
|
|
40
|
+
writeAdsOutput(data, fmt);
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
writeAdsJson({ ok: true, data });
|
|
44
|
+
}
|
|
45
|
+
catch (err) {
|
|
46
|
+
handleLinkedinError(err);
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
//# sourceMappingURL=campaign-groups.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"campaign-groups.js","sourceRoot":"","sources":["../../../../src/commands/ads/linkedin/campaign-groups.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AACtC,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC5D,OAAO,EAAE,SAAS,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAavG,MAAM,CAAC,MAAM,qBAAqB,GAAG,aAAa,CAAC;IACjD,IAAI,EAAE;QACJ,IAAI,EAAE,iBAAiB;QACvB,WAAW,EAAE;;;;;;;kGAOiF;KAC/F;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,cAAc,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,gCAAgC,EAAE;QAClF,QAAQ,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,0DAA0D,EAAE;QACrG,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,yBAAyB,EAAE;QACjE,YAAY,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,0BAA0B,EAAE;QAC1E,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,mBAAmB,EAAE,OAAO,EAAE,MAAM,EAAE;KAC9E;IACD,GAAG,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE;QACtB,MAAM,SAAS,GAAG,mBAAmB,CAAC,IAAI,CAAC,CAAC;QAC5C,IAAI,CAAC;YACH,MAAM,MAAM,GAA2B,EAAE,YAAY,EAAE,SAAS,EAAE,CAAC;YACnE,MAAM,QAAQ,GAAG,mBAAmB,CAAC,IAAI,CAAC,CAAC;YAC3C,IAAI,QAAQ;gBAAE,MAAM,CAAC,QAAQ,GAAG,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACnD,IAAI,IAAI,CAAC,KAAK;gBAAE,MAAM,CAAC,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAClD,IAAI,IAAI,CAAC,YAAY,CAAC;gBAAE,MAAM,CAAC,YAAY,CAAC,GAAG,MAAM,CAAC;YAEtD,MAAM,IAAI,GAAG,MAAM,MAAM,CAA6B,mCAAmC,EAAE,MAAM,CAAC,CAAC;YACnG,MAAM,GAAG,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;YAC5B,IAAI,GAAG,KAAK,MAAM,EAAE,CAAC;gBACnB,cAAc,CAAC,IAAiD,EAAE,GAAG,CAAC,CAAC;gBACvE,OAAO;YACT,CAAC;YACD,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,36 @@
|
|
|
1
|
+
export declare const campaignsCommand: 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-group-id": {
|
|
11
|
+
readonly type: "string";
|
|
12
|
+
readonly description: "Filter by campaign group";
|
|
13
|
+
};
|
|
14
|
+
readonly "all-statuses": {
|
|
15
|
+
readonly type: "boolean";
|
|
16
|
+
readonly description: "Drop the default ACTIVE filter";
|
|
17
|
+
};
|
|
18
|
+
readonly statuses: {
|
|
19
|
+
readonly type: "string";
|
|
20
|
+
readonly description: "CSV of statuses";
|
|
21
|
+
};
|
|
22
|
+
readonly limit: {
|
|
23
|
+
readonly type: "string";
|
|
24
|
+
readonly description: "Max rows (default: 500)";
|
|
25
|
+
};
|
|
26
|
+
readonly "skip-cache": {
|
|
27
|
+
readonly type: "boolean";
|
|
28
|
+
readonly description: "Bypass server-side cache";
|
|
29
|
+
};
|
|
30
|
+
readonly output: {
|
|
31
|
+
readonly type: "string";
|
|
32
|
+
readonly description: "json|csv|jsonl|md";
|
|
33
|
+
readonly default: "json";
|
|
34
|
+
};
|
|
35
|
+
}>;
|
|
36
|
+
//# sourceMappingURL=campaigns.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"campaigns.d.ts","sourceRoot":"","sources":["../../../../src/commands/ads/linkedin/campaigns.ts"],"names":[],"mappings":"AA2BA,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA+C3B,CAAC"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { defineCommand } from "citty";
|
|
2
|
+
import { apiGet } from "../../../client.js";
|
|
3
|
+
import { writeAdsJson, writeAdsOutput } from "../output.js";
|
|
4
|
+
import { csvOrJson, handleLinkedinError, resolveAccountIdArg, resolveStatusFilter } from "./shared.js";
|
|
5
|
+
export const campaignsCommand = defineCommand({
|
|
6
|
+
meta: {
|
|
7
|
+
name: "campaigns",
|
|
8
|
+
description: `List LinkedIn campaigns.
|
|
9
|
+
|
|
10
|
+
Default: ACTIVE only. The schema includes the audit-relevant settings:
|
|
11
|
+
audienceExpansionEnabled — playbook §02 silent budget drain (should be false)
|
|
12
|
+
offsiteDeliveryEnabled — LinkedIn Audience Network gate
|
|
13
|
+
optimizationTargetType — driver of the bidding strategy
|
|
14
|
+
costType — CPC | CPM | CPV | CPS
|
|
15
|
+
|
|
16
|
+
Examples:
|
|
17
|
+
baker ads linkedin campaigns --account-id 503001492
|
|
18
|
+
baker ads linkedin campaigns --account-id 503001492 --campaign-group-id 12345
|
|
19
|
+
baker ads linkedin campaigns --account-id 503001492 --all-statuses --output csv`,
|
|
20
|
+
},
|
|
21
|
+
args: {
|
|
22
|
+
"account-id": { type: "string", description: "Numeric account ID or urn:li:sponsoredAccount:N" },
|
|
23
|
+
"account-urn": { type: "string", description: "Alias for --account-id" },
|
|
24
|
+
"campaign-group-id": { type: "string", description: "Filter by campaign group" },
|
|
25
|
+
"all-statuses": { type: "boolean", description: "Drop the default ACTIVE filter" },
|
|
26
|
+
statuses: { type: "string", description: "CSV of statuses" },
|
|
27
|
+
limit: { type: "string", description: "Max rows (default: 500)" },
|
|
28
|
+
"skip-cache": { type: "boolean", description: "Bypass server-side cache" },
|
|
29
|
+
output: { type: "string", description: "json|csv|jsonl|md", default: "json" },
|
|
30
|
+
},
|
|
31
|
+
run: async ({ args }) => {
|
|
32
|
+
const accountId = resolveAccountIdArg(args);
|
|
33
|
+
try {
|
|
34
|
+
const params = { "account-id": accountId };
|
|
35
|
+
const statuses = resolveStatusFilter(args);
|
|
36
|
+
if (statuses)
|
|
37
|
+
params.statuses = statuses.join(",");
|
|
38
|
+
if (args["campaign-group-id"])
|
|
39
|
+
params["campaign-group-id"] = String(args["campaign-group-id"]);
|
|
40
|
+
if (args.limit)
|
|
41
|
+
params.limit = String(args.limit);
|
|
42
|
+
if (args["skip-cache"])
|
|
43
|
+
params["skip-cache"] = "true";
|
|
44
|
+
const data = await apiGet("/api/ads/linkedin/campaigns", params);
|
|
45
|
+
const fmt = csvOrJson(args);
|
|
46
|
+
if (fmt !== "json") {
|
|
47
|
+
writeAdsOutput(data, fmt);
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
writeAdsJson({ ok: true, data });
|
|
51
|
+
}
|
|
52
|
+
catch (err) {
|
|
53
|
+
handleLinkedinError(err);
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
//# sourceMappingURL=campaigns.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"campaigns.js","sourceRoot":"","sources":["../../../../src/commands/ads/linkedin/campaigns.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AACtC,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC5D,OAAO,EAAE,SAAS,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAwBvG,MAAM,CAAC,MAAM,gBAAgB,GAAG,aAAa,CAAC;IAC5C,IAAI,EAAE;QACJ,IAAI,EAAE,WAAW;QACjB,WAAW,EAAE;;;;;;;;;;;kFAWiE;KAC/E;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,mBAAmB,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,0BAA0B,EAAE;QAChF,cAAc,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,gCAAgC,EAAE;QAClF,QAAQ,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,iBAAiB,EAAE;QAC5D,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,yBAAyB,EAAE;QACjE,YAAY,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,0BAA0B,EAAE;QAC1E,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,mBAAmB,EAAE,OAAO,EAAE,MAAM,EAAE;KAC9E;IACD,GAAG,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE;QACtB,MAAM,SAAS,GAAG,mBAAmB,CAAC,IAAI,CAAC,CAAC;QAC5C,IAAI,CAAC;YACH,MAAM,MAAM,GAA2B,EAAE,YAAY,EAAE,SAAS,EAAE,CAAC;YACnE,MAAM,QAAQ,GAAG,mBAAmB,CAAC,IAAI,CAAC,CAAC;YAC3C,IAAI,QAAQ;gBAAE,MAAM,CAAC,QAAQ,GAAG,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACnD,IAAI,IAAI,CAAC,mBAAmB,CAAC;gBAAE,MAAM,CAAC,mBAAmB,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC,CAAC;YAC/F,IAAI,IAAI,CAAC,KAAK;gBAAE,MAAM,CAAC,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAClD,IAAI,IAAI,CAAC,YAAY,CAAC;gBAAE,MAAM,CAAC,YAAY,CAAC,GAAG,MAAM,CAAC;YAEtD,MAAM,IAAI,GAAG,MAAM,MAAM,CAAwB,6BAA6B,EAAE,MAAM,CAAC,CAAC;YACxF,MAAM,GAAG,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;YAC5B,IAAI,GAAG,KAAK,MAAM,EAAE,CAAC;gBACnB,cAAc,CAAC,IAAiD,EAAE,GAAG,CAAC,CAAC;gBACvE,OAAO;YACT,CAAC;YACD,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,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Conversation Ads (Sponsored Messaging) per-button analytics. Wraps
|
|
3
|
+
* --pivot conversation-node-button on /api/ads/linkedin/analytics.
|
|
4
|
+
*
|
|
5
|
+
* Lets agents see which CTA button inside a multi-step Sponsored Message
|
|
6
|
+
* actually drives clicks — no other ad platform exposes this granularity.
|
|
7
|
+
*/
|
|
8
|
+
export declare const conversationCommand: import("citty").CommandDef<{
|
|
9
|
+
readonly "campaign-id": {
|
|
10
|
+
readonly type: "string";
|
|
11
|
+
readonly description: "Comma-separated campaign IDs (Conversation Ad campaigns)";
|
|
12
|
+
readonly required: true;
|
|
13
|
+
};
|
|
14
|
+
readonly start: {
|
|
15
|
+
readonly type: "string";
|
|
16
|
+
readonly description: "Start date YYYY-MM-DD";
|
|
17
|
+
};
|
|
18
|
+
readonly end: {
|
|
19
|
+
readonly type: "string";
|
|
20
|
+
readonly description: "End date YYYY-MM-DD";
|
|
21
|
+
};
|
|
22
|
+
readonly "last-days": {
|
|
23
|
+
readonly type: "string";
|
|
24
|
+
readonly description: "Window relative to today (default: 30)";
|
|
25
|
+
};
|
|
26
|
+
readonly "skip-cache": {
|
|
27
|
+
readonly type: "boolean";
|
|
28
|
+
readonly description: "Bypass server-side cache";
|
|
29
|
+
};
|
|
30
|
+
readonly output: {
|
|
31
|
+
readonly type: "string";
|
|
32
|
+
readonly description: "json|csv|jsonl|md";
|
|
33
|
+
readonly default: "json";
|
|
34
|
+
};
|
|
35
|
+
}>;
|
|
36
|
+
//# sourceMappingURL=conversation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conversation.d.ts","sourceRoot":"","sources":["../../../../src/commands/ads/linkedin/conversation.ts"],"names":[],"mappings":"AAWA;;;;;;GAMG;AACH,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;EAgE9B,CAAC"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { defineCommand } from "citty";
|
|
2
|
+
import { apiPost } from "../../../client.js";
|
|
3
|
+
import { writeAdsJson, writeAdsOutput } from "../output.js";
|
|
4
|
+
import { csvOrJson, daysAgoIso, handleLinkedinError, todayIso } from "./shared.js";
|
|
5
|
+
/**
|
|
6
|
+
* Conversation Ads (Sponsored Messaging) per-button analytics. Wraps
|
|
7
|
+
* --pivot conversation-node-button on /api/ads/linkedin/analytics.
|
|
8
|
+
*
|
|
9
|
+
* Lets agents see which CTA button inside a multi-step Sponsored Message
|
|
10
|
+
* actually drives clicks — no other ad platform exposes this granularity.
|
|
11
|
+
*/
|
|
12
|
+
export const conversationCommand = defineCommand({
|
|
13
|
+
meta: {
|
|
14
|
+
name: "conversation",
|
|
15
|
+
description: `Per-button click rates inside Sponsored Messaging / Conversation Ads.
|
|
16
|
+
|
|
17
|
+
Returns one row per CONVERSATION_NODE_BUTTON pivot, sorted by clicks desc.
|
|
18
|
+
Use this to A/B test button copy and the cascade tree.
|
|
19
|
+
|
|
20
|
+
Examples:
|
|
21
|
+
baker ads linkedin conversation --campaign-id 1234
|
|
22
|
+
baker ads linkedin conversation --campaign-id 1234 --last-days 30 --output csv`,
|
|
23
|
+
},
|
|
24
|
+
args: {
|
|
25
|
+
"campaign-id": {
|
|
26
|
+
type: "string",
|
|
27
|
+
description: "Comma-separated campaign IDs (Conversation Ad campaigns)",
|
|
28
|
+
required: true,
|
|
29
|
+
},
|
|
30
|
+
start: { type: "string", description: "Start date YYYY-MM-DD" },
|
|
31
|
+
end: { type: "string", description: "End date YYYY-MM-DD" },
|
|
32
|
+
"last-days": { type: "string", description: "Window relative to today (default: 30)" },
|
|
33
|
+
"skip-cache": { type: "boolean", description: "Bypass server-side cache" },
|
|
34
|
+
output: { type: "string", description: "json|csv|jsonl|md", default: "json" },
|
|
35
|
+
},
|
|
36
|
+
run: async ({ args }) => {
|
|
37
|
+
const campaignIdsRaw = args["campaign-id"];
|
|
38
|
+
if (!campaignIdsRaw) {
|
|
39
|
+
handleLinkedinError(new Error("--campaign-id is required"));
|
|
40
|
+
}
|
|
41
|
+
const ids = campaignIdsRaw
|
|
42
|
+
.split(",")
|
|
43
|
+
.map((s) => s.trim())
|
|
44
|
+
.filter(Boolean);
|
|
45
|
+
const start = args.start ?? daysAgoIso(args["last-days"] ? Number(args["last-days"]) : 30);
|
|
46
|
+
const end = args.end ?? todayIso();
|
|
47
|
+
try {
|
|
48
|
+
const data = await apiPost("/api/ads/linkedin/analytics", {
|
|
49
|
+
request: {
|
|
50
|
+
level: "campaign",
|
|
51
|
+
ids,
|
|
52
|
+
intent: "inmail",
|
|
53
|
+
pivot: "conversation-node-button",
|
|
54
|
+
start,
|
|
55
|
+
end,
|
|
56
|
+
granularity: "ALL",
|
|
57
|
+
limit: 500,
|
|
58
|
+
},
|
|
59
|
+
skipCache: Boolean(args["skip-cache"]),
|
|
60
|
+
});
|
|
61
|
+
const sorted = [...data.rows].sort((a, b) => Number(b.clicks ?? 0) - Number(a.clicks ?? 0));
|
|
62
|
+
const fmt = csvOrJson(args);
|
|
63
|
+
if (fmt !== "json") {
|
|
64
|
+
writeAdsOutput(sorted, fmt, data.fields);
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
writeAdsJson({
|
|
68
|
+
ok: true,
|
|
69
|
+
data: { query: { campaigns: ids, start, end }, rows: sorted, warnings: data.warnings },
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
catch (err) {
|
|
73
|
+
handleLinkedinError(err);
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
//# sourceMappingURL=conversation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conversation.js","sourceRoot":"","sources":["../../../../src/commands/ads/linkedin/conversation.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conversions.d.ts","sourceRoot":"","sources":["../../../../src/commands/ads/linkedin/conversions.ts"],"names":[],"mappings":"AAsHA,eAAO,MAAM,kBAAkB,qDAa7B,CAAC"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { defineCommand } from "citty";
|
|
2
|
+
import { apiGet } from "../../../client.js";
|
|
3
|
+
import { writeAdsJson, writeAdsOutput } from "../output.js";
|
|
4
|
+
import { csvOrJson, handleLinkedinError, resolveAccountIdArg } from "./shared.js";
|
|
5
|
+
const DAY_MS = 86_400_000;
|
|
6
|
+
function healthOf(rules) {
|
|
7
|
+
const enabled = rules.filter((r) => r.enabled !== false);
|
|
8
|
+
const capi = enabled.filter((r) => r.conversionMethod === "CONVERSIONS_API");
|
|
9
|
+
const pixel = enabled.filter((r) => r.conversionMethod === "PIXEL");
|
|
10
|
+
const staleCapi = capi.filter((r) => !r.lastConversionReportedAt || Date.now() - r.lastConversionReportedAt > 7 * DAY_MS);
|
|
11
|
+
const longView = enabled.filter((r) => r.viewThroughAttributionWindowSize !== undefined && r.viewThroughAttributionWindowSize > 7);
|
|
12
|
+
const leads = enabled.filter((r) => r.type === "LEAD");
|
|
13
|
+
const wrongDedup = leads.filter((r) => r.countingMethod && r.countingMethod !== "ONE_TIME_EACH_MEMBER");
|
|
14
|
+
return {
|
|
15
|
+
totalRules: rules.length,
|
|
16
|
+
enabledRules: enabled.length,
|
|
17
|
+
capiRules: capi.length,
|
|
18
|
+
pixelRules: pixel.length,
|
|
19
|
+
hasLeadEvent: leads.length > 0,
|
|
20
|
+
hasPurchaseEvent: enabled.some((r) => r.type === "PURCHASE"),
|
|
21
|
+
staleCapiRules: staleCapi,
|
|
22
|
+
longViewWindow: longView,
|
|
23
|
+
wrongLeadDedup: wrongDedup,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
const listCmd = defineCommand({
|
|
27
|
+
meta: {
|
|
28
|
+
name: "list",
|
|
29
|
+
description: `List conversion rules on the account.`,
|
|
30
|
+
},
|
|
31
|
+
args: {
|
|
32
|
+
"account-id": { type: "string", description: "Numeric account ID or urn:li:sponsoredAccount:N" },
|
|
33
|
+
"account-urn": { type: "string", description: "Alias for --account-id" },
|
|
34
|
+
"skip-cache": { type: "boolean", description: "Bypass server-side cache" },
|
|
35
|
+
output: { type: "string", description: "json|csv|jsonl|md", default: "json" },
|
|
36
|
+
},
|
|
37
|
+
run: async ({ args }) => {
|
|
38
|
+
const accountId = resolveAccountIdArg(args);
|
|
39
|
+
try {
|
|
40
|
+
const params = { "account-id": accountId };
|
|
41
|
+
if (args["skip-cache"])
|
|
42
|
+
params["skip-cache"] = "true";
|
|
43
|
+
const data = await apiGet("/api/ads/linkedin/conversions", params);
|
|
44
|
+
const fmt = csvOrJson(args);
|
|
45
|
+
if (fmt !== "json") {
|
|
46
|
+
writeAdsOutput(data, fmt);
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
writeAdsJson({ ok: true, data });
|
|
50
|
+
}
|
|
51
|
+
catch (err) {
|
|
52
|
+
handleLinkedinError(err);
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
const healthCmd = defineCommand({
|
|
57
|
+
meta: {
|
|
58
|
+
name: "health",
|
|
59
|
+
description: `5-point Insight Tag / CAPI health check (playbook §07).
|
|
60
|
+
|
|
61
|
+
Surfaces:
|
|
62
|
+
- Are rules enabled?
|
|
63
|
+
- Do we have at least one LEAD or PURCHASE event?
|
|
64
|
+
- Do CAPI rules show recent activity (≤7 days)?
|
|
65
|
+
- Is the view-through window ≤7 days?
|
|
66
|
+
- Do LEAD rules use ONE_TIME_EACH_MEMBER de-dup?`,
|
|
67
|
+
},
|
|
68
|
+
args: {
|
|
69
|
+
"account-id": { type: "string", description: "Numeric account ID or urn:li:sponsoredAccount:N" },
|
|
70
|
+
"account-urn": { type: "string", description: "Alias for --account-id" },
|
|
71
|
+
"skip-cache": { type: "boolean", description: "Bypass server-side cache" },
|
|
72
|
+
output: { type: "string", description: "json", default: "json" },
|
|
73
|
+
},
|
|
74
|
+
run: async ({ args }) => {
|
|
75
|
+
const accountId = resolveAccountIdArg(args);
|
|
76
|
+
try {
|
|
77
|
+
const params = { "account-id": accountId };
|
|
78
|
+
if (args["skip-cache"])
|
|
79
|
+
params["skip-cache"] = "true";
|
|
80
|
+
const data = await apiGet("/api/ads/linkedin/conversions", params);
|
|
81
|
+
writeAdsJson({ ok: true, data: healthOf(data) });
|
|
82
|
+
}
|
|
83
|
+
catch (err) {
|
|
84
|
+
handleLinkedinError(err);
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
export const conversionsCommand = defineCommand({
|
|
89
|
+
meta: {
|
|
90
|
+
name: "conversions",
|
|
91
|
+
description: `Conversion rules — Insight Tag and Conversions API.
|
|
92
|
+
|
|
93
|
+
Subcommands:
|
|
94
|
+
list — every rule on the account
|
|
95
|
+
health — playbook §07 5-point health check`,
|
|
96
|
+
},
|
|
97
|
+
subCommands: {
|
|
98
|
+
list: listCmd,
|
|
99
|
+
health: healthCmd,
|
|
100
|
+
},
|
|
101
|
+
});
|
|
102
|
+
//# sourceMappingURL=conversions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conversions.js","sourceRoot":"","sources":["../../../../src/commands/ads/linkedin/conversions.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export declare const creativesCommand: 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: "Filter by campaign ID";
|
|
13
|
+
};
|
|
14
|
+
readonly "all-statuses": {
|
|
15
|
+
readonly type: "boolean";
|
|
16
|
+
readonly description: "Drop the default ACTIVE filter";
|
|
17
|
+
};
|
|
18
|
+
readonly statuses: {
|
|
19
|
+
readonly type: "string";
|
|
20
|
+
readonly description: "CSV of intended statuses (ACTIVE,PAUSED,DRAFT,ARCHIVED)";
|
|
21
|
+
};
|
|
22
|
+
readonly limit: {
|
|
23
|
+
readonly type: "string";
|
|
24
|
+
readonly description: "Max rows (default: 500)";
|
|
25
|
+
};
|
|
26
|
+
readonly "skip-cache": {
|
|
27
|
+
readonly type: "boolean";
|
|
28
|
+
readonly description: "Bypass server-side cache";
|
|
29
|
+
};
|
|
30
|
+
readonly output: {
|
|
31
|
+
readonly type: "string";
|
|
32
|
+
readonly description: "json|csv|jsonl|md";
|
|
33
|
+
readonly default: "json";
|
|
34
|
+
};
|
|
35
|
+
}>;
|
|
36
|
+
//# sourceMappingURL=creatives.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"creatives.d.ts","sourceRoot":"","sources":["../../../../src/commands/ads/linkedin/creatives.ts"],"names":[],"mappings":"AAkBA,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA+C3B,CAAC"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { defineCommand } from "citty";
|
|
2
|
+
import { apiGet } from "../../../client.js";
|
|
3
|
+
import { writeAdsJson, writeAdsOutput } from "../output.js";
|
|
4
|
+
import { csvOrJson, handleLinkedinError, resolveAccountIdArg, resolveStatusFilter } from "./shared.js";
|
|
5
|
+
export const creativesCommand = defineCommand({
|
|
6
|
+
meta: {
|
|
7
|
+
name: "creatives",
|
|
8
|
+
description: `List LinkedIn creatives (ads).
|
|
9
|
+
|
|
10
|
+
Defaults to ACTIVE intended-status. Pass --all-statuses or --statuses ACTIVE,PAUSED to widen.
|
|
11
|
+
|
|
12
|
+
The 'content' block is opaque (varies by creative type — image, video, document,
|
|
13
|
+
spotlight, conversation, dynamic). Use 'reviewStatus' to spot rejected/pending
|
|
14
|
+
creatives during an audit.
|
|
15
|
+
|
|
16
|
+
Examples:
|
|
17
|
+
baker ads linkedin creatives --account-id 503001492
|
|
18
|
+
baker ads linkedin creatives --account-id 503001492 --campaign-id 1234
|
|
19
|
+
baker ads linkedin creatives --account-id 503001492 --all-statuses --output csv`,
|
|
20
|
+
},
|
|
21
|
+
args: {
|
|
22
|
+
"account-id": { type: "string", description: "Numeric account ID or urn:li:sponsoredAccount:N" },
|
|
23
|
+
"account-urn": { type: "string", description: "Alias for --account-id" },
|
|
24
|
+
"campaign-id": { type: "string", description: "Filter by campaign ID" },
|
|
25
|
+
"all-statuses": { type: "boolean", description: "Drop the default ACTIVE filter" },
|
|
26
|
+
statuses: { type: "string", description: "CSV of intended statuses (ACTIVE,PAUSED,DRAFT,ARCHIVED)" },
|
|
27
|
+
limit: { type: "string", description: "Max rows (default: 500)" },
|
|
28
|
+
"skip-cache": { type: "boolean", description: "Bypass server-side cache" },
|
|
29
|
+
output: { type: "string", description: "json|csv|jsonl|md", default: "json" },
|
|
30
|
+
},
|
|
31
|
+
run: async ({ args }) => {
|
|
32
|
+
const accountId = resolveAccountIdArg(args);
|
|
33
|
+
try {
|
|
34
|
+
const params = { "account-id": accountId };
|
|
35
|
+
const statuses = resolveStatusFilter(args);
|
|
36
|
+
if (statuses)
|
|
37
|
+
params.statuses = statuses.join(",");
|
|
38
|
+
if (args["campaign-id"])
|
|
39
|
+
params["campaign-id"] = String(args["campaign-id"]);
|
|
40
|
+
if (args.limit)
|
|
41
|
+
params.limit = String(args.limit);
|
|
42
|
+
if (args["skip-cache"])
|
|
43
|
+
params["skip-cache"] = "true";
|
|
44
|
+
const data = await apiGet("/api/ads/linkedin/creatives", params);
|
|
45
|
+
const fmt = csvOrJson(args);
|
|
46
|
+
if (fmt !== "json") {
|
|
47
|
+
writeAdsOutput(data, fmt);
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
writeAdsJson({ ok: true, data });
|
|
51
|
+
}
|
|
52
|
+
catch (err) {
|
|
53
|
+
handleLinkedinError(err);
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
//# sourceMappingURL=creatives.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"creatives.js","sourceRoot":"","sources":["../../../../src/commands/ads/linkedin/creatives.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AACtC,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC5D,OAAO,EAAE,SAAS,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAevG,MAAM,CAAC,MAAM,gBAAgB,GAAG,aAAa,CAAC;IAC5C,IAAI,EAAE;QACJ,IAAI,EAAE,WAAW;QACjB,WAAW,EAAE;;;;;;;;;;;kFAWiE;KAC/E;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,aAAa,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,uBAAuB,EAAE;QACvE,cAAc,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,gCAAgC,EAAE;QAClF,QAAQ,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,yDAAyD,EAAE;QACpG,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,yBAAyB,EAAE;QACjE,YAAY,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,0BAA0B,EAAE;QAC1E,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,mBAAmB,EAAE,OAAO,EAAE,MAAM,EAAE;KAC9E;IACD,GAAG,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE;QACtB,MAAM,SAAS,GAAG,mBAAmB,CAAC,IAAI,CAAC,CAAC;QAC5C,IAAI,CAAC;YACH,MAAM,MAAM,GAA2B,EAAE,YAAY,EAAE,SAAS,EAAE,CAAC;YACnE,MAAM,QAAQ,GAAG,mBAAmB,CAAC,IAAI,CAAC,CAAC;YAC3C,IAAI,QAAQ;gBAAE,MAAM,CAAC,QAAQ,GAAG,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACnD,IAAI,IAAI,CAAC,aAAa,CAAC;gBAAE,MAAM,CAAC,aAAa,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC;YAC7E,IAAI,IAAI,CAAC,KAAK;gBAAE,MAAM,CAAC,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAClD,IAAI,IAAI,CAAC,YAAY,CAAC;gBAAE,MAAM,CAAC,YAAY,CAAC,GAAG,MAAM,CAAC;YAEtD,MAAM,IAAI,GAAG,MAAM,MAAM,CAAwB,6BAA6B,EAAE,MAAM,CAAC,CAAC;YACxF,MAAM,GAAG,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;YAC5B,IAAI,GAAG,KAAK,MAAM,EAAE,CAAC;gBACnB,cAAc,CAAC,IAAiD,EAAE,GAAG,CAAC,CAAC;gBACvE,OAAO;YACT,CAAC;YACD,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"}
|