@koda-sl/baker-cli 0.25.1 → 0.27.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +218 -0
- package/dist/cli.js +1 -1
- package/dist/commands/ads/index.d.ts.map +1 -1
- package/dist/commands/ads/index.js +12 -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/commands/ads/x/accounts.d.ts +14 -0
- package/dist/commands/ads/x/accounts.d.ts.map +1 -0
- package/dist/commands/ads/x/accounts.js +73 -0
- package/dist/commands/ads/x/accounts.js.map +1 -0
- package/dist/commands/ads/x/active-entities.d.ts +43 -0
- package/dist/commands/ads/x/active-entities.d.ts.map +1 -0
- package/dist/commands/ads/x/active-entities.js +88 -0
- package/dist/commands/ads/x/active-entities.js.map +1 -0
- package/dist/commands/ads/x/audiences.d.ts +19 -0
- package/dist/commands/ads/x/audiences.d.ts.map +1 -0
- package/dist/commands/ads/x/audiences.js +65 -0
- package/dist/commands/ads/x/audiences.js.map +1 -0
- package/dist/commands/ads/x/campaigns.d.ts +34 -0
- package/dist/commands/ads/x/campaigns.d.ts.map +1 -0
- package/dist/commands/ads/x/campaigns.js +56 -0
- package/dist/commands/ads/x/campaigns.js.map +1 -0
- package/dist/commands/ads/x/cards.d.ts +19 -0
- package/dist/commands/ads/x/cards.d.ts.map +1 -0
- package/dist/commands/ads/x/cards.js +65 -0
- package/dist/commands/ads/x/cards.js.map +1 -0
- package/dist/commands/ads/x/error-parser.d.ts +3 -0
- package/dist/commands/ads/x/error-parser.d.ts.map +1 -0
- package/dist/commands/ads/x/error-parser.js +80 -0
- package/dist/commands/ads/x/error-parser.js.map +1 -0
- package/dist/commands/ads/x/funding.d.ts +19 -0
- package/dist/commands/ads/x/funding.d.ts.map +1 -0
- package/dist/commands/ads/x/funding.js +65 -0
- package/dist/commands/ads/x/funding.js.map +1 -0
- package/dist/commands/ads/x/index.d.ts +2 -0
- package/dist/commands/ads/x/index.d.ts.map +1 -0
- package/dist/commands/ads/x/index.js +50 -0
- package/dist/commands/ads/x/index.js.map +1 -0
- package/dist/commands/ads/x/line-items.d.ts +34 -0
- package/dist/commands/ads/x/line-items.d.ts.map +1 -0
- package/dist/commands/ads/x/line-items.js +55 -0
- package/dist/commands/ads/x/line-items.js.map +1 -0
- package/dist/commands/ads/x/media.d.ts +24 -0
- package/dist/commands/ads/x/media.d.ts.map +1 -0
- package/dist/commands/ads/x/media.js +70 -0
- package/dist/commands/ads/x/media.js.map +1 -0
- package/dist/commands/ads/x/output.d.ts +13 -0
- package/dist/commands/ads/x/output.d.ts.map +1 -0
- package/dist/commands/ads/x/output.js +75 -0
- package/dist/commands/ads/x/output.js.map +1 -0
- package/dist/commands/ads/x/presets.d.ts +15 -0
- package/dist/commands/ads/x/presets.d.ts.map +1 -0
- package/dist/commands/ads/x/presets.js +60 -0
- package/dist/commands/ads/x/presets.js.map +1 -0
- package/dist/commands/ads/x/promoted-tweets.d.ts +29 -0
- package/dist/commands/ads/x/promoted-tweets.d.ts.map +1 -0
- package/dist/commands/ads/x/promoted-tweets.js +74 -0
- package/dist/commands/ads/x/promoted-tweets.js.map +1 -0
- package/dist/commands/ads/x/run-list.d.ts +17 -0
- package/dist/commands/ads/x/run-list.d.ts.map +1 -0
- package/dist/commands/ads/x/run-list.js +60 -0
- package/dist/commands/ads/x/run-list.js.map +1 -0
- package/dist/commands/ads/x/stats/index.d.ts +2 -0
- package/dist/commands/ads/x/stats/index.d.ts.map +1 -0
- package/dist/commands/ads/x/stats/index.js +32 -0
- package/dist/commands/ads/x/stats/index.js.map +1 -0
- package/dist/commands/ads/x/stats/job-create.d.ts +58 -0
- package/dist/commands/ads/x/stats/job-create.d.ts.map +1 -0
- package/dist/commands/ads/x/stats/job-create.js +95 -0
- package/dist/commands/ads/x/stats/job-create.js.map +1 -0
- package/dist/commands/ads/x/stats/job-status.d.ts +18 -0
- package/dist/commands/ads/x/stats/job-status.d.ts.map +1 -0
- package/dist/commands/ads/x/stats/job-status.js +58 -0
- package/dist/commands/ads/x/stats/job-status.js.map +1 -0
- package/dist/commands/ads/x/stats/job.d.ts +63 -0
- package/dist/commands/ads/x/stats/job.d.ts.map +1 -0
- package/dist/commands/ads/x/stats/job.js +183 -0
- package/dist/commands/ads/x/stats/job.js.map +1 -0
- package/dist/commands/ads/x/stats/sync.d.ts +73 -0
- package/dist/commands/ads/x/stats/sync.d.ts.map +1 -0
- package/dist/commands/ads/x/stats/sync.js +151 -0
- package/dist/commands/ads/x/stats/sync.js.map +1 -0
- package/dist/commands/ads/x/targeting-constants.d.ts +34 -0
- package/dist/commands/ads/x/targeting-constants.d.ts.map +1 -0
- package/dist/commands/ads/x/targeting-constants.js +80 -0
- package/dist/commands/ads/x/targeting-constants.js.map +1 -0
- package/dist/commands/ads/x/targeting-criteria.d.ts +24 -0
- package/dist/commands/ads/x/targeting-criteria.d.ts.map +1 -0
- package/dist/commands/ads/x/targeting-criteria.js +69 -0
- package/dist/commands/ads/x/targeting-criteria.js.map +1 -0
- package/dist/env.d.ts +2 -0
- package/dist/env.d.ts.map +1 -1
- package/dist/env.js +8 -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 +6 -0
- package/dist/error-handler.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"promoted-tweets.js","sourceRoot":"","sources":["../../../../src/commands/ads/x/promoted-tweets.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
interface ListParams {
|
|
2
|
+
path: string;
|
|
3
|
+
cacheCategory: string;
|
|
4
|
+
cacheKey: string;
|
|
5
|
+
cacheTtlMs?: number;
|
|
6
|
+
params: Record<string, string | undefined>;
|
|
7
|
+
format: string;
|
|
8
|
+
accountId?: string;
|
|
9
|
+
useCache: boolean;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Shared GET-list flow: cache check → request → cache set → format-aware output.
|
|
13
|
+
* Centralizing this keeps each CLI command file under Biome's complexity budget.
|
|
14
|
+
*/
|
|
15
|
+
export declare function runListCommand(opts: ListParams): Promise<void>;
|
|
16
|
+
export {};
|
|
17
|
+
//# sourceMappingURL=run-list.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run-list.d.ts","sourceRoot":"","sources":["../../../../src/commands/ads/x/run-list.ts"],"names":[],"mappings":"AAKA,UAAU,UAAU;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;IAC3C,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,OAAO,CAAC;CACnB;AAwCD;;;GAGG;AACH,wBAAsB,cAAc,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAYpE"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { ApiError, apiGet } from "../../../client.js";
|
|
2
|
+
import { cacheGet, cacheSet } from "../cache.js";
|
|
3
|
+
import { writeAdsJson, writeAdsOutput } from "../output.js";
|
|
4
|
+
import { parseXApiError } from "./error-parser.js";
|
|
5
|
+
function buildCleanParams(opts) {
|
|
6
|
+
const out = {};
|
|
7
|
+
for (const [k, v] of Object.entries(opts.params)) {
|
|
8
|
+
if (v !== undefined && v !== "") {
|
|
9
|
+
out[k] = v;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
if (!opts.useCache) {
|
|
13
|
+
out["skip-cache"] = "true";
|
|
14
|
+
}
|
|
15
|
+
return out;
|
|
16
|
+
}
|
|
17
|
+
function emitFromCache(opts) {
|
|
18
|
+
if (!opts.useCache || opts.cacheTtlMs === undefined)
|
|
19
|
+
return false;
|
|
20
|
+
const cached = cacheGet(opts.cacheCategory, opts.cacheKey);
|
|
21
|
+
if (!cached)
|
|
22
|
+
return false;
|
|
23
|
+
writeAdsJson({ ok: true, data: cached.data, cached: true });
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
function emitData(opts, data) {
|
|
27
|
+
if (opts.format !== "json") {
|
|
28
|
+
writeAdsOutput(data, opts.format);
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
writeAdsJson({ ok: true, data });
|
|
32
|
+
}
|
|
33
|
+
function emitError(opts, err) {
|
|
34
|
+
if (err instanceof ApiError) {
|
|
35
|
+
writeAdsJson(parseXApiError(err.message, opts.accountId ?? "—"));
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
38
|
+
writeAdsJson({ ok: false, error: { code: "NETWORK_ERROR", message: "Unexpected error" } });
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Shared GET-list flow: cache check → request → cache set → format-aware output.
|
|
43
|
+
* Centralizing this keeps each CLI command file under Biome's complexity budget.
|
|
44
|
+
*/
|
|
45
|
+
export async function runListCommand(opts) {
|
|
46
|
+
if (emitFromCache(opts))
|
|
47
|
+
return;
|
|
48
|
+
const params = buildCleanParams(opts);
|
|
49
|
+
try {
|
|
50
|
+
const data = await apiGet(opts.path, params);
|
|
51
|
+
if (opts.useCache && opts.cacheTtlMs !== undefined) {
|
|
52
|
+
cacheSet(opts.cacheCategory, opts.cacheKey, data, opts.cacheTtlMs);
|
|
53
|
+
}
|
|
54
|
+
emitData(opts, data);
|
|
55
|
+
}
|
|
56
|
+
catch (err) {
|
|
57
|
+
emitError(opts, err);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=run-list.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run-list.js","sourceRoot":"","sources":["../../../../src/commands/ads/x/run-list.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/commands/ads/x/stats/index.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,YAAY,qDAyBvB,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { defineCommand } from "citty";
|
|
2
|
+
import { statsJobCommand } from "./job.js";
|
|
3
|
+
import { statsJobCreateCommand } from "./job-create.js";
|
|
4
|
+
import { statsJobStatusCommand } from "./job-status.js";
|
|
5
|
+
import { statsSyncCommand } from "./sync.js";
|
|
6
|
+
export const statsCommand = defineCommand({
|
|
7
|
+
meta: {
|
|
8
|
+
name: "stats",
|
|
9
|
+
description: `X Ads analytics. Sync (≤7 days, no segmentation) or async jobs (≤90 days, segmentable).
|
|
10
|
+
|
|
11
|
+
Subcommands:
|
|
12
|
+
sync — synchronous stats (max 7 days). Cheapest, fastest.
|
|
13
|
+
job — async stats job, end-to-end (creates + polls + downloads). Must run in the background.
|
|
14
|
+
Use for ranges >7 days, segmented stats, or when sync limits are hit.
|
|
15
|
+
job-create — low-level: create an async stats job and return the ID immediately
|
|
16
|
+
job-status — low-level: poll job status / get the download URL
|
|
17
|
+
|
|
18
|
+
Examples:
|
|
19
|
+
baker ads x stats sync --preset campaign-engagement-7d --entity-ids abc,def
|
|
20
|
+
baker ads x stats job --account-id 18ce53xyz --entity CAMPAIGN \\
|
|
21
|
+
--entity-ids abc,def --start-time 2026-04-01T00:00:00Z \\
|
|
22
|
+
--end-time 2026-05-01T00:00:00Z --metric-groups ENGAGEMENT,BILLING
|
|
23
|
+
baker ads x stats job-status --account-id 18ce53xyz --job-id abc`,
|
|
24
|
+
},
|
|
25
|
+
subCommands: {
|
|
26
|
+
sync: statsSyncCommand,
|
|
27
|
+
job: statsJobCommand,
|
|
28
|
+
"job-create": statsJobCreateCommand,
|
|
29
|
+
"job-status": statsJobStatusCommand,
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../src/commands/ads/x/stats/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AACtC,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAC3C,OAAO,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAE7C,MAAM,CAAC,MAAM,YAAY,GAAG,aAAa,CAAC;IACxC,IAAI,EAAE;QACJ,IAAI,EAAE,OAAO;QACb,WAAW,EAAE;;;;;;;;;;;;;;mEAckD;KAChE;IACD,WAAW,EAAE;QACX,IAAI,EAAE,gBAAgB;QACtB,GAAG,EAAE,eAAe;QACpB,YAAY,EAAE,qBAAqB;QACnC,YAAY,EAAE,qBAAqB;KACpC;CACF,CAAC,CAAC"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
export declare const statsJobCreateCommand: import("citty").CommandDef<{
|
|
2
|
+
readonly "account-id": {
|
|
3
|
+
readonly type: "string";
|
|
4
|
+
readonly description: "X Ads account ID";
|
|
5
|
+
readonly required: false;
|
|
6
|
+
};
|
|
7
|
+
readonly entity: {
|
|
8
|
+
readonly type: "string";
|
|
9
|
+
readonly description: "Entity type";
|
|
10
|
+
readonly required: true;
|
|
11
|
+
};
|
|
12
|
+
readonly "entity-ids": {
|
|
13
|
+
readonly type: "string";
|
|
14
|
+
readonly description: "CSV of entity IDs";
|
|
15
|
+
readonly required: true;
|
|
16
|
+
};
|
|
17
|
+
readonly "start-time": {
|
|
18
|
+
readonly type: "string";
|
|
19
|
+
readonly description: "ISO start";
|
|
20
|
+
readonly required: true;
|
|
21
|
+
};
|
|
22
|
+
readonly "end-time": {
|
|
23
|
+
readonly type: "string";
|
|
24
|
+
readonly description: "ISO end";
|
|
25
|
+
readonly required: true;
|
|
26
|
+
};
|
|
27
|
+
readonly granularity: {
|
|
28
|
+
readonly type: "string";
|
|
29
|
+
readonly description: "Granularity";
|
|
30
|
+
readonly required: false;
|
|
31
|
+
};
|
|
32
|
+
readonly "metric-groups": {
|
|
33
|
+
readonly type: "string";
|
|
34
|
+
readonly description: "Metric groups CSV";
|
|
35
|
+
readonly required: true;
|
|
36
|
+
};
|
|
37
|
+
readonly placement: {
|
|
38
|
+
readonly type: "string";
|
|
39
|
+
readonly description: "Placement";
|
|
40
|
+
readonly required: false;
|
|
41
|
+
};
|
|
42
|
+
readonly "segmentation-type": {
|
|
43
|
+
readonly type: "string";
|
|
44
|
+
readonly description: "Segmentation type";
|
|
45
|
+
readonly required: false;
|
|
46
|
+
};
|
|
47
|
+
readonly country: {
|
|
48
|
+
readonly type: "string";
|
|
49
|
+
readonly description: "Country filter";
|
|
50
|
+
readonly required: false;
|
|
51
|
+
};
|
|
52
|
+
readonly platform: {
|
|
53
|
+
readonly type: "string";
|
|
54
|
+
readonly description: "Platform filter";
|
|
55
|
+
readonly required: false;
|
|
56
|
+
};
|
|
57
|
+
}>;
|
|
58
|
+
//# sourceMappingURL=job-create.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"job-create.d.ts","sourceRoot":"","sources":["../../../../../src/commands/ads/x/stats/job-create.ts"],"names":[],"mappings":"AAmCA,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA4DhC,CAAC"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { defineCommand } from "citty";
|
|
2
|
+
import { ApiError, apiPost } from "../../../../client.js";
|
|
3
|
+
import { registerSchema } from "../../../../schemas.js";
|
|
4
|
+
import { writeAdsJson } from "../../output.js";
|
|
5
|
+
import { parseXApiError } from "../error-parser.js";
|
|
6
|
+
import { resolveXAccountId } from "../output.js";
|
|
7
|
+
registerSchema({
|
|
8
|
+
command: "ads.x.statsJobCreate",
|
|
9
|
+
description: "Create an asynchronous X Ads stats job (range up to 90 days non-segmented, 45 days segmented). Returns a job id; poll with `stats job-status`. Times must be ISO 8601 hour-aligned.",
|
|
10
|
+
args: {
|
|
11
|
+
"account-id": { type: "string", description: "X Ads account ID", required: false },
|
|
12
|
+
entity: { type: "string", description: "CAMPAIGN | LINE_ITEM | PROMOTED_TWEET | ...", required: true },
|
|
13
|
+
"entity-ids": { type: "string", description: "CSV of entity IDs (max 20)", required: true },
|
|
14
|
+
"start-time": { type: "string", description: "ISO 8601 hour-aligned start", required: true },
|
|
15
|
+
"end-time": { type: "string", description: "ISO 8601 hour-aligned end", required: true },
|
|
16
|
+
granularity: { type: "string", description: "TOTAL | DAY | HOUR", required: false },
|
|
17
|
+
"metric-groups": { type: "string", description: "CSV of metric groups", required: true },
|
|
18
|
+
placement: { type: "string", description: "ALL_ON_TWITTER | PUBLISHER_NETWORK", required: false },
|
|
19
|
+
"segmentation-type": { type: "string", description: "Segmentation (e.g. AGE, GENDER, LOCATIONS)", required: false },
|
|
20
|
+
country: { type: "string", description: "Country filter", required: false },
|
|
21
|
+
platform: { type: "string", description: "Platform filter", required: false },
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
function parseCsv(v) {
|
|
25
|
+
if (typeof v !== "string" || v.length === 0)
|
|
26
|
+
return undefined;
|
|
27
|
+
const parts = v
|
|
28
|
+
.split(",")
|
|
29
|
+
.map((s) => s.trim())
|
|
30
|
+
.filter(Boolean);
|
|
31
|
+
return parts.length > 0 ? parts : undefined;
|
|
32
|
+
}
|
|
33
|
+
export const statsJobCreateCommand = defineCommand({
|
|
34
|
+
meta: {
|
|
35
|
+
name: "job-create",
|
|
36
|
+
description: `Create an async X Ads stats job (up to 90 days, supports segmentation).
|
|
37
|
+
|
|
38
|
+
Examples:
|
|
39
|
+
baker ads x stats job-create --account-id 18ce53xyz --entity CAMPAIGN \\
|
|
40
|
+
--entity-ids abc,def --start-time 2026-04-01T00:00:00Z \\
|
|
41
|
+
--end-time 2026-05-01T00:00:00Z --metric-groups ENGAGEMENT,BILLING \\
|
|
42
|
+
--segmentation-type LOCATIONS`,
|
|
43
|
+
},
|
|
44
|
+
args: {
|
|
45
|
+
"account-id": { type: "string", description: "X Ads account ID", required: false },
|
|
46
|
+
entity: { type: "string", description: "Entity type", required: true },
|
|
47
|
+
"entity-ids": { type: "string", description: "CSV of entity IDs", required: true },
|
|
48
|
+
"start-time": { type: "string", description: "ISO start", required: true },
|
|
49
|
+
"end-time": { type: "string", description: "ISO end", required: true },
|
|
50
|
+
granularity: { type: "string", description: "Granularity", required: false },
|
|
51
|
+
"metric-groups": { type: "string", description: "Metric groups CSV", required: true },
|
|
52
|
+
placement: { type: "string", description: "Placement", required: false },
|
|
53
|
+
"segmentation-type": { type: "string", description: "Segmentation type", required: false },
|
|
54
|
+
country: { type: "string", description: "Country filter", required: false },
|
|
55
|
+
platform: { type: "string", description: "Platform filter", required: false },
|
|
56
|
+
},
|
|
57
|
+
run: async ({ args }) => {
|
|
58
|
+
const accountId = await resolveXAccountId(args);
|
|
59
|
+
const entityIds = parseCsv(args["entity-ids"]);
|
|
60
|
+
const metricGroups = parseCsv(args["metric-groups"]);
|
|
61
|
+
if (!entityIds || !metricGroups) {
|
|
62
|
+
writeAdsJson({
|
|
63
|
+
ok: false,
|
|
64
|
+
error: { code: "MISSING_PARAMETER", message: "entity-ids and metric-groups are required" },
|
|
65
|
+
});
|
|
66
|
+
process.exit(1);
|
|
67
|
+
}
|
|
68
|
+
const body = {
|
|
69
|
+
accountId,
|
|
70
|
+
entity: args.entity,
|
|
71
|
+
entityIds,
|
|
72
|
+
startTime: args["start-time"],
|
|
73
|
+
endTime: args["end-time"],
|
|
74
|
+
granularity: args.granularity ?? "DAY",
|
|
75
|
+
metricGroups,
|
|
76
|
+
placement: args.placement ?? "ALL_ON_TWITTER",
|
|
77
|
+
segmentationType: args["segmentation-type"],
|
|
78
|
+
country: args.country,
|
|
79
|
+
platform: args.platform,
|
|
80
|
+
};
|
|
81
|
+
try {
|
|
82
|
+
const data = await apiPost("/api/ads/x/stats/jobs", body);
|
|
83
|
+
writeAdsJson({ ok: true, data });
|
|
84
|
+
}
|
|
85
|
+
catch (err) {
|
|
86
|
+
if (err instanceof ApiError) {
|
|
87
|
+
writeAdsJson(parseXApiError(err.message, accountId));
|
|
88
|
+
process.exit(1);
|
|
89
|
+
}
|
|
90
|
+
writeAdsJson({ ok: false, error: { code: "NETWORK_ERROR", message: "Unexpected error" } });
|
|
91
|
+
process.exit(1);
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
});
|
|
95
|
+
//# sourceMappingURL=job-create.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"job-create.js","sourceRoot":"","sources":["../../../../../src/commands/ads/x/stats/job-create.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export declare const statsJobStatusCommand: import("citty").CommandDef<{
|
|
2
|
+
readonly "account-id": {
|
|
3
|
+
readonly type: "string";
|
|
4
|
+
readonly description: "X Ads account ID";
|
|
5
|
+
readonly required: false;
|
|
6
|
+
};
|
|
7
|
+
readonly "job-id": {
|
|
8
|
+
readonly type: "string";
|
|
9
|
+
readonly description: "Job ID";
|
|
10
|
+
readonly required: false;
|
|
11
|
+
};
|
|
12
|
+
readonly "job-ids": {
|
|
13
|
+
readonly type: "string";
|
|
14
|
+
readonly description: "CSV of job IDs";
|
|
15
|
+
readonly required: false;
|
|
16
|
+
};
|
|
17
|
+
}>;
|
|
18
|
+
//# sourceMappingURL=job-status.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"job-status.d.ts","sourceRoot":"","sources":["../../../../../src/commands/ads/x/stats/job-status.ts"],"names":[],"mappings":"AAkBA,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;EAsChC,CAAC"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { defineCommand } from "citty";
|
|
2
|
+
import { ApiError, apiGet } from "../../../../client.js";
|
|
3
|
+
import { registerSchema } from "../../../../schemas.js";
|
|
4
|
+
import { writeAdsJson } from "../../output.js";
|
|
5
|
+
import { parseXApiError } from "../error-parser.js";
|
|
6
|
+
import { resolveXAccountId } from "../output.js";
|
|
7
|
+
registerSchema({
|
|
8
|
+
command: "ads.x.statsJobStatus",
|
|
9
|
+
description: "Check the status of one or more X Ads stats jobs. Returns status (PROCESSING|SUCCESS|FAILED) and a downloadable url when SUCCESS. Pass --job-id or --job-ids (CSV).",
|
|
10
|
+
args: {
|
|
11
|
+
"account-id": { type: "string", description: "X Ads account ID", required: false },
|
|
12
|
+
"job-id": { type: "string", description: "Single job ID", required: false },
|
|
13
|
+
"job-ids": { type: "string", description: "CSV of job IDs", required: false },
|
|
14
|
+
},
|
|
15
|
+
});
|
|
16
|
+
export const statsJobStatusCommand = defineCommand({
|
|
17
|
+
meta: {
|
|
18
|
+
name: "job-status",
|
|
19
|
+
description: `Poll the status of an async X Ads stats job.
|
|
20
|
+
|
|
21
|
+
Examples:
|
|
22
|
+
baker ads x stats job-status --account-id 18ce53xyz --job-id abc
|
|
23
|
+
baker ads x stats job-status --account-id 18ce53xyz --job-ids abc,def`,
|
|
24
|
+
},
|
|
25
|
+
args: {
|
|
26
|
+
"account-id": { type: "string", description: "X Ads account ID", required: false },
|
|
27
|
+
"job-id": { type: "string", description: "Job ID", required: false },
|
|
28
|
+
"job-ids": { type: "string", description: "CSV of job IDs", required: false },
|
|
29
|
+
},
|
|
30
|
+
run: async ({ args }) => {
|
|
31
|
+
const accountId = await resolveXAccountId(args);
|
|
32
|
+
if (!args["job-id"] && !args["job-ids"]) {
|
|
33
|
+
writeAdsJson({
|
|
34
|
+
ok: false,
|
|
35
|
+
error: { code: "MISSING_PARAMETER", message: "Pass --job-id or --job-ids" },
|
|
36
|
+
});
|
|
37
|
+
process.exit(1);
|
|
38
|
+
}
|
|
39
|
+
try {
|
|
40
|
+
const params = { "account-id": accountId };
|
|
41
|
+
if (args["job-id"])
|
|
42
|
+
params["job-id"] = args["job-id"];
|
|
43
|
+
if (args["job-ids"])
|
|
44
|
+
params["job-ids"] = args["job-ids"];
|
|
45
|
+
const data = await apiGet("/api/ads/x/stats/jobs", params);
|
|
46
|
+
writeAdsJson({ ok: true, data });
|
|
47
|
+
}
|
|
48
|
+
catch (err) {
|
|
49
|
+
if (err instanceof ApiError) {
|
|
50
|
+
writeAdsJson(parseXApiError(err.message, accountId));
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
53
|
+
writeAdsJson({ ok: false, error: { code: "NETWORK_ERROR", message: "Unexpected error" } });
|
|
54
|
+
process.exit(1);
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
//# sourceMappingURL=job-status.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"job-status.js","sourceRoot":"","sources":["../../../../../src/commands/ads/x/stats/job-status.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
export declare const statsJobCommand: import("citty").CommandDef<{
|
|
2
|
+
readonly "account-id": {
|
|
3
|
+
readonly type: "string";
|
|
4
|
+
readonly description: "X Ads account ID";
|
|
5
|
+
readonly required: false;
|
|
6
|
+
};
|
|
7
|
+
readonly entity: {
|
|
8
|
+
readonly type: "string";
|
|
9
|
+
readonly description: "Entity type";
|
|
10
|
+
readonly required: true;
|
|
11
|
+
};
|
|
12
|
+
readonly "entity-ids": {
|
|
13
|
+
readonly type: "string";
|
|
14
|
+
readonly description: "CSV of entity IDs";
|
|
15
|
+
readonly required: true;
|
|
16
|
+
};
|
|
17
|
+
readonly "start-time": {
|
|
18
|
+
readonly type: "string";
|
|
19
|
+
readonly description: "ISO start";
|
|
20
|
+
readonly required: true;
|
|
21
|
+
};
|
|
22
|
+
readonly "end-time": {
|
|
23
|
+
readonly type: "string";
|
|
24
|
+
readonly description: "ISO end";
|
|
25
|
+
readonly required: true;
|
|
26
|
+
};
|
|
27
|
+
readonly granularity: {
|
|
28
|
+
readonly type: "string";
|
|
29
|
+
readonly description: "Granularity";
|
|
30
|
+
readonly required: false;
|
|
31
|
+
};
|
|
32
|
+
readonly "metric-groups": {
|
|
33
|
+
readonly type: "string";
|
|
34
|
+
readonly description: "Metric groups CSV";
|
|
35
|
+
readonly required: true;
|
|
36
|
+
};
|
|
37
|
+
readonly placement: {
|
|
38
|
+
readonly type: "string";
|
|
39
|
+
readonly description: "Placement";
|
|
40
|
+
readonly required: false;
|
|
41
|
+
};
|
|
42
|
+
readonly "segmentation-type": {
|
|
43
|
+
readonly type: "string";
|
|
44
|
+
readonly description: "Segmentation type";
|
|
45
|
+
readonly required: false;
|
|
46
|
+
};
|
|
47
|
+
readonly country: {
|
|
48
|
+
readonly type: "string";
|
|
49
|
+
readonly description: "Country filter";
|
|
50
|
+
readonly required: false;
|
|
51
|
+
};
|
|
52
|
+
readonly platform: {
|
|
53
|
+
readonly type: "string";
|
|
54
|
+
readonly description: "Platform filter";
|
|
55
|
+
readonly required: false;
|
|
56
|
+
};
|
|
57
|
+
readonly "no-cache": {
|
|
58
|
+
readonly type: "boolean";
|
|
59
|
+
readonly description: "Skip result cache";
|
|
60
|
+
readonly required: false;
|
|
61
|
+
};
|
|
62
|
+
}>;
|
|
63
|
+
//# sourceMappingURL=job.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"job.d.ts","sourceRoot":"","sources":["../../../../../src/commands/ads/x/stats/job.ts"],"names":[],"mappings":"AA+FA,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA2G1B,CAAC"}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { gunzipSync } from "node:zlib";
|
|
2
|
+
import { defineCommand } from "citty";
|
|
3
|
+
import { ApiError, apiGet, apiPost } from "../../../../client.js";
|
|
4
|
+
import { registerSchema } from "../../../../schemas.js";
|
|
5
|
+
import { cacheGet, cacheSet } from "../../cache.js";
|
|
6
|
+
import { writeAdsJson } from "../../output.js";
|
|
7
|
+
import { parseXApiError } from "../error-parser.js";
|
|
8
|
+
import { resolveXAccountId } from "../output.js";
|
|
9
|
+
const POLL_INTERVAL_MS = 10_000;
|
|
10
|
+
const DEADLINE_MS = 12 * 60 * 1000;
|
|
11
|
+
const RESULT_CACHE_TTL_MS = 6 * 60 * 60 * 1000;
|
|
12
|
+
registerSchema({
|
|
13
|
+
command: "ads.x.statsJob",
|
|
14
|
+
description: "Asynchronous X Ads stats job, fully synchronous from the CLI's perspective. Creates the job, polls every 10s, downloads + decompresses the gzip result, returns inline JSON. Hard deadline 12 min — must run in the background. Use this for ranges >7 days, segmented stats, or any query that hits sync limits. For fine-grained control, use `stats job-create` + `stats job-status` separately.",
|
|
15
|
+
args: {
|
|
16
|
+
"account-id": { type: "string", description: "X Ads account ID", required: false },
|
|
17
|
+
entity: { type: "string", description: "CAMPAIGN | LINE_ITEM | PROMOTED_TWEET | ...", required: true },
|
|
18
|
+
"entity-ids": { type: "string", description: "CSV of entity IDs (max 20)", required: true },
|
|
19
|
+
"start-time": { type: "string", description: "ISO 8601 hour-aligned start", required: true },
|
|
20
|
+
"end-time": { type: "string", description: "ISO 8601 hour-aligned end", required: true },
|
|
21
|
+
granularity: { type: "string", description: "TOTAL | DAY | HOUR", required: false },
|
|
22
|
+
"metric-groups": { type: "string", description: "CSV of metric groups", required: true },
|
|
23
|
+
placement: { type: "string", description: "ALL_ON_TWITTER | PUBLISHER_NETWORK", required: false },
|
|
24
|
+
"segmentation-type": { type: "string", description: "Segmentation (e.g. AGE, GENDER, LOCATIONS)", required: false },
|
|
25
|
+
country: { type: "string", description: "Country filter", required: false },
|
|
26
|
+
platform: { type: "string", description: "Platform filter", required: false },
|
|
27
|
+
"no-cache": { type: "boolean", description: "Skip result cache (still does the live job)", required: false },
|
|
28
|
+
},
|
|
29
|
+
});
|
|
30
|
+
function parseCsv(v) {
|
|
31
|
+
if (typeof v !== "string" || v.length === 0)
|
|
32
|
+
return undefined;
|
|
33
|
+
const parts = v
|
|
34
|
+
.split(",")
|
|
35
|
+
.map((s) => s.trim())
|
|
36
|
+
.filter(Boolean);
|
|
37
|
+
return parts.length > 0 ? parts : undefined;
|
|
38
|
+
}
|
|
39
|
+
function sleep(ms) {
|
|
40
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
41
|
+
}
|
|
42
|
+
async function downloadAndDecompress(url) {
|
|
43
|
+
const res = await fetch(url, { signal: AbortSignal.timeout(60_000) });
|
|
44
|
+
if (!res.ok) {
|
|
45
|
+
throw new ApiError("INTERNAL_ERROR", `Stats job download failed (${res.status})`);
|
|
46
|
+
}
|
|
47
|
+
const buf = Buffer.from(await res.arrayBuffer());
|
|
48
|
+
const decompressed = gunzipSync(buf);
|
|
49
|
+
return JSON.parse(decompressed.toString("utf-8"));
|
|
50
|
+
}
|
|
51
|
+
async function pollUntilDone(accountId, jobId) {
|
|
52
|
+
const deadline = Date.now() + DEADLINE_MS;
|
|
53
|
+
let lastLog = 0;
|
|
54
|
+
while (Date.now() < deadline) {
|
|
55
|
+
await sleep(POLL_INTERVAL_MS);
|
|
56
|
+
const jobs = await apiGet("/api/ads/x/stats/jobs", {
|
|
57
|
+
"account-id": accountId,
|
|
58
|
+
"job-id": jobId,
|
|
59
|
+
});
|
|
60
|
+
const job = jobs[0];
|
|
61
|
+
if (!job) {
|
|
62
|
+
throw new ApiError("NOT_FOUND", `Stats job ${jobId} not found`);
|
|
63
|
+
}
|
|
64
|
+
if (job.status === "SUCCESS") {
|
|
65
|
+
return job;
|
|
66
|
+
}
|
|
67
|
+
if (job.status === "FAILED" || job.status === "CANCELLED") {
|
|
68
|
+
throw new ApiError("INTERNAL_ERROR", `Stats job ${jobId} ended in status ${job.status}`);
|
|
69
|
+
}
|
|
70
|
+
const elapsed = DEADLINE_MS - (deadline - Date.now());
|
|
71
|
+
if (elapsed - lastLog > 30_000) {
|
|
72
|
+
process.stderr.write(` polling... (${job.status}, ${Math.round(elapsed / 1000)}s elapsed)\n`);
|
|
73
|
+
lastLog = elapsed;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
throw new ApiError("TIMEOUT", `Stats job ${jobId} did not complete within ${DEADLINE_MS / 60_000} minutes`);
|
|
77
|
+
}
|
|
78
|
+
function buildCacheKey(body) {
|
|
79
|
+
return `stats-job:${JSON.stringify(body)}`;
|
|
80
|
+
}
|
|
81
|
+
export const statsJobCommand = defineCommand({
|
|
82
|
+
meta: {
|
|
83
|
+
name: "job",
|
|
84
|
+
description: `Async X Ads stats job, sync from the CLI's perspective. Creates → polls → downloads → returns.
|
|
85
|
+
|
|
86
|
+
Must run in the background (the harness enforces this).
|
|
87
|
+
|
|
88
|
+
Use this when:
|
|
89
|
+
• Date range > 7 days
|
|
90
|
+
• Need segmentation (AGE, GENDER, LOCATIONS, ...)
|
|
91
|
+
• Sync stats hit a limit
|
|
92
|
+
|
|
93
|
+
Examples:
|
|
94
|
+
baker ads x stats job --account-id 18ce53xyz --entity CAMPAIGN \\
|
|
95
|
+
--entity-ids abc,def --start-time 2026-04-01T00:00:00Z \\
|
|
96
|
+
--end-time 2026-05-01T00:00:00Z --metric-groups ENGAGEMENT,BILLING
|
|
97
|
+
|
|
98
|
+
baker ads x stats job --account-id 18ce53xyz --entity LINE_ITEM \\
|
|
99
|
+
--entity-ids abc --start-time 2026-04-01T00:00:00Z \\
|
|
100
|
+
--end-time 2026-05-01T00:00:00Z --metric-groups ENGAGEMENT \\
|
|
101
|
+
--segmentation-type LOCATIONS
|
|
102
|
+
|
|
103
|
+
For fine-grained control (don't wait, poll yourself), use:
|
|
104
|
+
baker ads x stats job-create — create + return job ID immediately
|
|
105
|
+
baker ads x stats job-status — poll job status / get download URL`,
|
|
106
|
+
},
|
|
107
|
+
args: {
|
|
108
|
+
"account-id": { type: "string", description: "X Ads account ID", required: false },
|
|
109
|
+
entity: { type: "string", description: "Entity type", required: true },
|
|
110
|
+
"entity-ids": { type: "string", description: "CSV of entity IDs", required: true },
|
|
111
|
+
"start-time": { type: "string", description: "ISO start", required: true },
|
|
112
|
+
"end-time": { type: "string", description: "ISO end", required: true },
|
|
113
|
+
granularity: { type: "string", description: "Granularity", required: false },
|
|
114
|
+
"metric-groups": { type: "string", description: "Metric groups CSV", required: true },
|
|
115
|
+
placement: { type: "string", description: "Placement", required: false },
|
|
116
|
+
"segmentation-type": { type: "string", description: "Segmentation type", required: false },
|
|
117
|
+
country: { type: "string", description: "Country filter", required: false },
|
|
118
|
+
platform: { type: "string", description: "Platform filter", required: false },
|
|
119
|
+
"no-cache": { type: "boolean", description: "Skip result cache", required: false },
|
|
120
|
+
},
|
|
121
|
+
run: async ({ args }) => {
|
|
122
|
+
const accountId = await resolveXAccountId(args);
|
|
123
|
+
const entityIds = parseCsv(args["entity-ids"]);
|
|
124
|
+
const metricGroups = parseCsv(args["metric-groups"]);
|
|
125
|
+
if (!entityIds || !metricGroups) {
|
|
126
|
+
writeAdsJson({
|
|
127
|
+
ok: false,
|
|
128
|
+
error: { code: "MISSING_PARAMETER", message: "entity-ids and metric-groups are required" },
|
|
129
|
+
});
|
|
130
|
+
process.exit(1);
|
|
131
|
+
}
|
|
132
|
+
const body = {
|
|
133
|
+
accountId,
|
|
134
|
+
entity: args.entity,
|
|
135
|
+
entityIds,
|
|
136
|
+
startTime: args["start-time"],
|
|
137
|
+
endTime: args["end-time"],
|
|
138
|
+
granularity: args.granularity ?? "DAY",
|
|
139
|
+
metricGroups,
|
|
140
|
+
placement: args.placement ?? "ALL_ON_TWITTER",
|
|
141
|
+
segmentationType: args["segmentation-type"],
|
|
142
|
+
country: args.country,
|
|
143
|
+
platform: args.platform,
|
|
144
|
+
};
|
|
145
|
+
const useCache = !args["no-cache"];
|
|
146
|
+
const cacheKey = buildCacheKey(body);
|
|
147
|
+
if (useCache) {
|
|
148
|
+
const cached = cacheGet("x-stats", cacheKey);
|
|
149
|
+
if (cached) {
|
|
150
|
+
process.stderr.write("Result served from cache.\n");
|
|
151
|
+
writeAdsJson({ ok: true, data: cached.data, cached: true });
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
try {
|
|
156
|
+
process.stderr.write(`Creating X Ads stats job for entity=${body.entity}, ${entityIds.length} IDs...\n`);
|
|
157
|
+
const job = await apiPost("/api/ads/x/stats/jobs", body);
|
|
158
|
+
process.stderr.write(`Job ${job.id} queued. Polling every ${POLL_INTERVAL_MS / 1000}s...\n`);
|
|
159
|
+
const finished = await pollUntilDone(accountId, job.id);
|
|
160
|
+
if (!finished.url) {
|
|
161
|
+
throw new ApiError("INTERNAL_ERROR", `Stats job ${job.id} succeeded but returned no download URL`);
|
|
162
|
+
}
|
|
163
|
+
process.stderr.write("Job complete. Downloading + decompressing results...\n");
|
|
164
|
+
const data = await downloadAndDecompress(finished.url);
|
|
165
|
+
if (useCache) {
|
|
166
|
+
cacheSet("x-stats", cacheKey, data, RESULT_CACHE_TTL_MS);
|
|
167
|
+
}
|
|
168
|
+
writeAdsJson({ ok: true, data });
|
|
169
|
+
}
|
|
170
|
+
catch (err) {
|
|
171
|
+
if (err instanceof ApiError) {
|
|
172
|
+
writeAdsJson(parseXApiError(err.message, accountId));
|
|
173
|
+
process.exit(1);
|
|
174
|
+
}
|
|
175
|
+
writeAdsJson({
|
|
176
|
+
ok: false,
|
|
177
|
+
error: { code: "NETWORK_ERROR", message: err instanceof Error ? err.message : "Unexpected error" },
|
|
178
|
+
});
|
|
179
|
+
process.exit(1);
|
|
180
|
+
}
|
|
181
|
+
},
|
|
182
|
+
});
|
|
183
|
+
//# sourceMappingURL=job.js.map
|