adapty 0.3.0 → 0.5.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.
Files changed (88) hide show
  1. package/README.md +156 -5
  2. package/dist/commands/asa/ad-groups/create.d.ts +22 -0
  3. package/dist/commands/asa/ad-groups/create.js +54 -0
  4. package/dist/commands/asa/ad-groups/get.d.ts +11 -0
  5. package/dist/commands/asa/ad-groups/get.js +21 -0
  6. package/dist/commands/asa/ad-groups/list.d.ts +18 -0
  7. package/dist/commands/asa/ad-groups/list.js +24 -0
  8. package/dist/commands/asa/ad-groups/update.d.ts +23 -0
  9. package/dist/commands/asa/ad-groups/update.js +62 -0
  10. package/dist/commands/asa/ads/create.d.ts +16 -0
  11. package/dist/commands/asa/ads/create.js +41 -0
  12. package/dist/commands/asa/ads/get.d.ts +11 -0
  13. package/dist/commands/asa/ads/get.js +21 -0
  14. package/dist/commands/asa/ads/list.d.ts +18 -0
  15. package/dist/commands/asa/ads/list.js +24 -0
  16. package/dist/commands/asa/ads/update.d.ts +17 -0
  17. package/dist/commands/asa/ads/update.js +44 -0
  18. package/dist/commands/asa/apps/list.d.ts +13 -0
  19. package/dist/commands/asa/apps/list.js +18 -0
  20. package/dist/commands/asa/automations/create.d.ts +16 -0
  21. package/dist/commands/asa/automations/create.js +60 -0
  22. package/dist/commands/asa/automations/get.d.ts +11 -0
  23. package/dist/commands/asa/automations/get.js +21 -0
  24. package/dist/commands/asa/automations/list.d.ts +13 -0
  25. package/dist/commands/asa/automations/list.js +18 -0
  26. package/dist/commands/asa/automations/run.d.ts +16 -0
  27. package/dist/commands/asa/automations/run.js +41 -0
  28. package/dist/commands/asa/automations/runs.d.ts +16 -0
  29. package/dist/commands/asa/automations/runs.js +24 -0
  30. package/dist/commands/asa/automations/update.d.ts +21 -0
  31. package/dist/commands/asa/automations/update.js +73 -0
  32. package/dist/commands/asa/campaigns/bulk-create.d.ts +25 -0
  33. package/dist/commands/asa/campaigns/bulk-create.js +177 -0
  34. package/dist/commands/asa/campaigns/bulk-status.d.ts +15 -0
  35. package/dist/commands/asa/campaigns/bulk-status.js +26 -0
  36. package/dist/commands/asa/campaigns/create.d.ts +25 -0
  37. package/dist/commands/asa/campaigns/create.js +67 -0
  38. package/dist/commands/asa/campaigns/get.d.ts +11 -0
  39. package/dist/commands/asa/campaigns/get.js +21 -0
  40. package/dist/commands/asa/campaigns/list.d.ts +17 -0
  41. package/dist/commands/asa/campaigns/list.js +24 -0
  42. package/dist/commands/asa/campaigns/update.d.ts +23 -0
  43. package/dist/commands/asa/campaigns/update.js +66 -0
  44. package/dist/commands/asa/competitors/summary.d.ts +11 -0
  45. package/dist/commands/asa/competitors/summary.js +56 -0
  46. package/dist/commands/asa/connect.d.ts +12 -0
  47. package/dist/commands/asa/connect.js +39 -0
  48. package/dist/commands/asa/creatives/list.d.ts +15 -0
  49. package/dist/commands/asa/creatives/list.js +24 -0
  50. package/dist/commands/asa/keywords/add.d.ts +20 -0
  51. package/dist/commands/asa/keywords/add.js +73 -0
  52. package/dist/commands/asa/keywords/list.d.ts +19 -0
  53. package/dist/commands/asa/keywords/list.js +24 -0
  54. package/dist/commands/asa/keywords/update.d.ts +21 -0
  55. package/dist/commands/asa/keywords/update.js +64 -0
  56. package/dist/commands/asa/metrics/index.d.ts +21 -0
  57. package/dist/commands/asa/metrics/index.js +68 -0
  58. package/dist/commands/asa/metrics/overview.d.ts +15 -0
  59. package/dist/commands/asa/metrics/overview.js +51 -0
  60. package/dist/commands/asa/negative-keywords/add.d.ts +18 -0
  61. package/dist/commands/asa/negative-keywords/add.js +70 -0
  62. package/dist/commands/asa/negative-keywords/list.d.ts +19 -0
  63. package/dist/commands/asa/negative-keywords/list.js +29 -0
  64. package/dist/commands/asa/orgs/list.d.ts +13 -0
  65. package/dist/commands/asa/orgs/list.js +18 -0
  66. package/dist/commands/asa/product-pages/list.d.ts +15 -0
  67. package/dist/commands/asa/product-pages/list.js +24 -0
  68. package/dist/commands/asa/product-pages/sync.d.ts +13 -0
  69. package/dist/commands/asa/product-pages/sync.js +28 -0
  70. package/dist/commands/asa/search-terms/list.d.ts +20 -0
  71. package/dist/commands/asa/search-terms/list.js +25 -0
  72. package/dist/commands/asa/whoami.d.ts +8 -0
  73. package/dist/commands/asa/whoami.js +22 -0
  74. package/dist/lib/api-client.d.ts +16 -3
  75. package/dist/lib/api-client.js +53 -18
  76. package/dist/lib/asa-client.d.ts +16 -0
  77. package/dist/lib/asa-client.js +46 -0
  78. package/dist/lib/asa-confirm.d.ts +18 -0
  79. package/dist/lib/asa-confirm.js +41 -0
  80. package/dist/lib/asa-flags.d.ts +88 -0
  81. package/dist/lib/asa-flags.js +137 -0
  82. package/dist/lib/asa-schemas.d.ts +327 -0
  83. package/dist/lib/asa-schemas.js +1 -0
  84. package/dist/lib/errors.d.ts +25 -2
  85. package/dist/lib/errors.js +48 -7
  86. package/dist/lib/output.js +45 -15
  87. package/oclif.manifest.json +2956 -157
  88. package/package.json +41 -1
@@ -0,0 +1,64 @@
1
+ import { Args, Command, Flags } from '@oclif/core';
2
+ import { asaWrite, createAsaClient, noteReplay } from '../../../lib/asa-client.js';
3
+ import { confirmFlags, confirmMutation } from '../../../lib/asa-confirm.js';
4
+ import { currencyFlag, idempotencyFlags, money, moneyFlag, reportBulkOutcome } from '../../../lib/asa-flags.js';
5
+ import { isValidUuid } from '../../../lib/flags.js';
6
+ export default class AsaKeywordsUpdate extends Command {
7
+ static args = {
8
+ keyword_id: Args.string({ description: 'Keyword ID (UUID), repeatable as extra arguments', required: true }),
9
+ };
10
+ static description = 'Change bid, status, text or match type of keywords';
11
+ static enableJsonFlag = true;
12
+ static examples = [
13
+ '<%= config.bin %> asa keywords update UUID --bid 2.00',
14
+ '<%= config.bin %> asa keywords update UUID_A UUID_B --status PAUSED',
15
+ ];
16
+ static flags = {
17
+ ...currencyFlag,
18
+ ...confirmFlags,
19
+ ...idempotencyFlags,
20
+ bid: moneyFlag('Bid'),
21
+ 'match-type': Flags.string({ description: 'Match type', options: ['BROAD', 'EXACT'] }),
22
+ status: Flags.string({ description: 'Keyword status', options: ['ACTIVE', 'PAUSED'] }),
23
+ text: Flags.string({ description: 'Keyword text (only meaningful for a single keyword)' }),
24
+ };
25
+ static strict = false;
26
+ async run() {
27
+ const { argv, flags } = await this.parse(AsaKeywordsUpdate);
28
+ const keywordIds = argv;
29
+ for (const id of keywordIds) {
30
+ if (!isValidUuid(id))
31
+ this.error(`Invalid keyword ID format: ${id}`, { exit: 2 });
32
+ }
33
+ const change = {};
34
+ if (flags.bid !== undefined)
35
+ change.bid_amount = money(flags.bid, flags.currency);
36
+ if (flags.status !== undefined)
37
+ change.status = flags.status;
38
+ if (flags['match-type'] !== undefined)
39
+ change.match_type = flags['match-type'];
40
+ if (flags.text !== undefined)
41
+ change.text = flags.text;
42
+ if (Object.keys(change).length === 0) {
43
+ this.error('Nothing to change. Pass at least one field, e.g. --bid 2.00.', { exit: 2 });
44
+ }
45
+ if (flags.text !== undefined && keywordIds.length > 1) {
46
+ this.error('--text would give every keyword the same text. Update them one at a time.', { exit: 2 });
47
+ }
48
+ const body = { keywords: keywordIds.map((id) => ({ internal_id: id, ...change })) };
49
+ await confirmMutation(this, { body, method: 'PUT', path: '/keywords/', summary: `Update ${keywordIds.length} keyword(s)` }, flags.yes);
50
+ const client = await createAsaClient(this.config);
51
+ const { replayed, result } = await asaWrite(client, 'put', '/keywords', {
52
+ body,
53
+ idempotencyKey: flags['idempotency-key'],
54
+ });
55
+ noteReplay(replayed, this.log.bind(this));
56
+ reportBulkOutcome({
57
+ applied: result.keywords,
58
+ errors: result.errors,
59
+ isValidationFailure: result.is_validation_failure,
60
+ kind: 'keywords',
61
+ }, this.log.bind(this));
62
+ return result;
63
+ }
64
+ }
@@ -0,0 +1,21 @@
1
+ import { Command } from '@oclif/core';
2
+ import { type PaginatedResponse } from '../../../lib/flags.js';
3
+ export default class AsaMetrics extends Command {
4
+ static description: string;
5
+ static enableJsonFlag: boolean;
6
+ static examples: string[];
7
+ static flags: {
8
+ 'date-from': import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
9
+ 'date-to': import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
10
+ entity: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
11
+ 'group-by': import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
12
+ metric: import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
13
+ order: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
14
+ 'order-by': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
15
+ 'order-by-day': import("@oclif/core/interfaces").OptionFlag<number | undefined, import("@oclif/core/interfaces").CustomOptions>;
16
+ 'by-days': import("@oclif/core/interfaces").OptionFlag<number[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
17
+ page: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
18
+ 'page-size': import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
19
+ };
20
+ run(): Promise<PaginatedResponse<Record<string, unknown>>>;
21
+ }
@@ -0,0 +1,68 @@
1
+ import { Command, Flags } from '@oclif/core';
2
+ import { asaWrite, createAsaClient } from '../../../lib/asa-client.js';
3
+ import { ASA_GROUP_BY_DIMENSIONS, ASA_METRIC_ENTITIES, asaPaginationFlags, byDaysFlag, MAX_BY_DAYS } from '../../../lib/asa-flags.js';
4
+ import { paginationParams } from '../../../lib/flags.js';
5
+ import { printList } from '../../../lib/output.js';
6
+ export default class AsaMetrics extends Command {
7
+ static description = `Query metrics for any level of the account over a date range
8
+
9
+ One row per entity, already aggregated server-side and sorted by --order-by, so a top-N question is one
10
+ call with --order-by and --page-size N — never sum pages yourself. Account-level totals are one call to
11
+ asa metrics overview instead. The date window is capped by the coarsest --group-by period: 90 days for
12
+ day or no period grouping, 180 by week, 365 by month and coarser — widen the window by coarsening the
13
+ grouping, not by splitting into more calls. Budget: 5 metrics calls per minute, at most 2 per 10 seconds.`;
14
+ static enableJsonFlag = true;
15
+ static examples = [
16
+ '<%= config.bin %> asa metrics --entity campaign --date-from 2026-07-01 --date-to 2026-07-31',
17
+ '<%= config.bin %> asa metrics --entity campaign --date-from 2026-07-01 --date-to 2026-07-31 --order-by spend --page-size 5',
18
+ '<%= config.bin %> asa metrics --entity campaign --date-from 2026-07-01 --date-to 2026-07-31 --group-by country --page-size 1000',
19
+ '<%= config.bin %> asa metrics --entity keyword --date-from 2026-07-01 --date-to 2026-07-31 --metric spend --metric roas',
20
+ '<%= config.bin %> asa metrics --entity campaign --date-from 2026-07-01 --date-to 2026-07-31 --metric roas --by-days 7 --by-days 90',
21
+ ];
22
+ static flags = {
23
+ ...asaPaginationFlags,
24
+ ...byDaysFlag,
25
+ 'date-from': Flags.string({ description: 'Start of the period (YYYY-MM-DD)', required: true }),
26
+ 'date-to': Flags.string({ description: 'End of the period (YYYY-MM-DD)', required: true }),
27
+ entity: Flags.string({ description: 'What to report on', options: ASA_METRIC_ENTITIES, required: true }),
28
+ 'group-by': Flags.string({
29
+ description: 'Break the rows down by a dimension, repeatable',
30
+ multiple: true,
31
+ options: ASA_GROUP_BY_DIMENSIONS,
32
+ }),
33
+ metric: Flags.string({
34
+ description: 'Metric name (dashboard nomenclature, e.g. spend, taps, gross_roas), repeatable; omit for every metric; a wrong name fails listing all valid ones',
35
+ multiple: true,
36
+ }),
37
+ order: Flags.string({ default: 'desc', description: 'Sort direction', options: ['asc', 'desc'] }),
38
+ 'order-by': Flags.string({
39
+ description: 'Metric or field to sort by; cohort metrics rank via their gross_/proceeds_/net_ names',
40
+ }),
41
+ 'order-by-day': Flags.integer({
42
+ description: 'Rank by a cohort metric at this renewal window; must be one of the --by-days values',
43
+ }),
44
+ };
45
+ async run() {
46
+ const { flags } = await this.parse(AsaMetrics);
47
+ if (flags['by-days'] && flags['by-days'].length > MAX_BY_DAYS) {
48
+ this.error(`At most ${MAX_BY_DAYS} renewal windows per call, got ${flags['by-days'].length}.`, { exit: 2 });
49
+ }
50
+ const client = await createAsaClient(this.config);
51
+ const { result } = await asaWrite(client, 'post', '/metrics', {
52
+ body: {
53
+ date_from: flags['date-from'],
54
+ date_to: flags['date-to'],
55
+ entity: flags.entity,
56
+ order: flags.order,
57
+ ...(flags.metric === undefined ? {} : { metrics: flags.metric }),
58
+ ...(flags['by-days'] === undefined ? {} : { by_days: flags['by-days'] }),
59
+ ...(flags['group-by'] === undefined ? {} : { group_by: flags['group-by'] }),
60
+ ...(flags['order-by'] === undefined ? {} : { order_by: flags['order-by'] }),
61
+ ...(flags['order-by-day'] === undefined ? {} : { order_by_day: flags['order-by-day'] }),
62
+ },
63
+ params: paginationParams(flags),
64
+ });
65
+ printList(result.data, this.log.bind(this), result.meta?.pagination);
66
+ return result;
67
+ }
68
+ }
@@ -0,0 +1,15 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class AsaMetricsOverview extends Command {
3
+ static description: string;
4
+ static enableJsonFlag: boolean;
5
+ static examples: string[];
6
+ static flags: {
7
+ 'date-from': import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
8
+ 'date-to': import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
9
+ entity: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
10
+ metric: import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
11
+ 'period-unit': import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
12
+ 'by-days': import("@oclif/core/interfaces").OptionFlag<number[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
13
+ };
14
+ run(): Promise<Record<string, unknown>>;
15
+ }
@@ -0,0 +1,51 @@
1
+ import { Command, Flags } from '@oclif/core';
2
+ import { asaWrite, createAsaClient } from '../../../lib/asa-client.js';
3
+ import { ASA_METRIC_ENTITIES, byDaysFlag, MAX_BY_DAYS } from '../../../lib/asa-flags.js';
4
+ import { printResponse } from '../../../lib/output.js';
5
+ export default class AsaMetricsOverview extends Command {
6
+ static description = `Totals for a period, optionally split into day, week or month buckets
7
+
8
+ The one-call answer to "how much did I spend / earn overall" and to any single-period trend question:
9
+ totals for the whole entity level plus a per-period series, no pagination, no client-side summing. The
10
+ date window is capped by --period-unit: 90 days at day, 180 by week, 365 by month and coarser. Shares
11
+ the 5-per-minute metrics budget with asa metrics.`;
12
+ static enableJsonFlag = true;
13
+ static examples = [
14
+ '<%= config.bin %> asa metrics overview --entity campaign --date-from 2026-07-01 --date-to 2026-07-31',
15
+ '<%= config.bin %> asa metrics overview --entity campaign --date-from 2026-07-01 --date-to 2026-07-31 --period-unit WEEK',
16
+ ];
17
+ static flags = {
18
+ ...byDaysFlag,
19
+ 'date-from': Flags.string({ description: 'Start of the period (YYYY-MM-DD)', required: true }),
20
+ 'date-to': Flags.string({ description: 'End of the period (YYYY-MM-DD)', required: true }),
21
+ entity: Flags.string({ description: 'What to report on', options: ASA_METRIC_ENTITIES, required: true }),
22
+ metric: Flags.string({
23
+ description: 'Metric name, repeatable; cohort roots only here (revenue, roas, arpu), not their gross_/proceeds_/net_ variants; omit for every metric',
24
+ multiple: true,
25
+ }),
26
+ 'period-unit': Flags.string({
27
+ default: 'day',
28
+ description: 'Bucket size',
29
+ options: ['day', 'month', 'quarter', 'week', 'year'],
30
+ }),
31
+ };
32
+ async run() {
33
+ const { flags } = await this.parse(AsaMetricsOverview);
34
+ if (flags['by-days'] && flags['by-days'].length > MAX_BY_DAYS) {
35
+ this.error(`At most ${MAX_BY_DAYS} renewal windows per call, got ${flags['by-days'].length}.`, { exit: 2 });
36
+ }
37
+ const client = await createAsaClient(this.config);
38
+ const { result } = await asaWrite(client, 'post', '/metrics/overview', {
39
+ body: {
40
+ date_from: flags['date-from'],
41
+ date_to: flags['date-to'],
42
+ entity: flags.entity,
43
+ period_unit: flags['period-unit'],
44
+ ...(flags.metric === undefined ? {} : { metrics: flags.metric }),
45
+ ...(flags['by-days'] === undefined ? {} : { by_days: flags['by-days'] }),
46
+ },
47
+ });
48
+ printResponse(result, this.log.bind(this));
49
+ return result;
50
+ }
51
+ }
@@ -0,0 +1,18 @@
1
+ import { Command } from '@oclif/core';
2
+ import type { AsaNegativeKeywordMutationDTO } from '../../../lib/asa-schemas.js';
3
+ export default class AsaNegativeKeywordsAdd extends Command {
4
+ static description: string;
5
+ static enableJsonFlag: boolean;
6
+ static examples: string[];
7
+ static flags: {
8
+ 'ad-group': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
9
+ 'all-ad-groups': import("@oclif/core/interfaces").BooleanFlag<boolean>;
10
+ campaign: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
11
+ 'match-type': import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
12
+ status: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
13
+ text: import("@oclif/core/interfaces").OptionFlag<string[], import("@oclif/core/interfaces").CustomOptions>;
14
+ 'idempotency-key': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
15
+ yes: import("@oclif/core/interfaces").BooleanFlag<boolean>;
16
+ };
17
+ run(): Promise<AsaNegativeKeywordMutationDTO>;
18
+ }
@@ -0,0 +1,70 @@
1
+ import { Command, Flags } from '@oclif/core';
2
+ import { asaWrite, createAsaClient, noteReplay } from '../../../lib/asa-client.js';
3
+ import { confirmFlags, confirmMutation } from '../../../lib/asa-confirm.js';
4
+ import { idempotencyFlags, MAX_BULK_ITEMS, reportBulkOutcome } from '../../../lib/asa-flags.js';
5
+ import { isValidUuid } from '../../../lib/flags.js';
6
+ export default class AsaNegativeKeywordsAdd extends Command {
7
+ static description = 'Add negative keywords to an ad group or across a campaign';
8
+ static enableJsonFlag = true;
9
+ static examples = [
10
+ '<%= config.bin %> asa negative-keywords add --ad-group UUID --text "free"',
11
+ '<%= config.bin %> asa negative-keywords add --campaign UUID --text "free" --text "cheap"',
12
+ '<%= config.bin %> asa negative-keywords add --campaign UUID --all-ad-groups --text "free"',
13
+ ];
14
+ static flags = {
15
+ ...confirmFlags,
16
+ ...idempotencyFlags,
17
+ 'ad-group': Flags.string({ description: 'Ad group ID (UUID) — the campaign is resolved from it', exclusive: ['campaign'] }),
18
+ 'all-ad-groups': Flags.boolean({
19
+ dependsOn: ['campaign'],
20
+ description: 'Apply to every ad group of the campaign instead of the campaign itself',
21
+ }),
22
+ campaign: Flags.string({ description: 'Campaign ID (UUID)', exclusive: ['ad-group'] }),
23
+ 'match-type': Flags.string({ default: 'EXACT', description: 'Match type', options: ['BROAD', 'EXACT'] }),
24
+ status: Flags.string({ default: 'ACTIVE', description: 'Keyword status', options: ['ACTIVE', 'PAUSED'] }),
25
+ text: Flags.string({ description: 'Keyword text, repeatable', multiple: true, required: true }),
26
+ };
27
+ async run() {
28
+ const { flags } = await this.parse(AsaNegativeKeywordsAdd);
29
+ const target = flags['ad-group'] ?? flags.campaign;
30
+ if (!target)
31
+ this.error('Pass either --ad-group or --campaign.', { exit: 2 });
32
+ if (!isValidUuid(target))
33
+ this.error('Invalid ID format.', { exit: 2 });
34
+ if (flags.text.length > MAX_BULK_ITEMS) {
35
+ this.error(`A single call takes at most ${MAX_BULK_ITEMS} keywords, got ${flags.text.length}.`, { exit: 2 });
36
+ }
37
+ const scope = flags['ad-group'] ? 'AD_GROUP' : flags['all-ad-groups'] ? 'ALL_CAMPAIGN_AD_GROUPS' : 'CAMPAIGN';
38
+ const parent = flags['ad-group'] ? { ad_group_id: flags['ad-group'] } : { campaign_id: flags.campaign };
39
+ const body = {
40
+ negative_keywords: flags.text.map((text) => ({
41
+ ...parent,
42
+ match_type: flags['match-type'],
43
+ status: flags.status,
44
+ text,
45
+ })),
46
+ scope,
47
+ };
48
+ await confirmMutation(this, {
49
+ body,
50
+ method: 'POST',
51
+ path: '/negative-keywords/',
52
+ summary: scope === 'ALL_CAMPAIGN_AD_GROUPS'
53
+ ? `Add ${flags.text.length} negative keyword(s) to every ad group of the campaign`
54
+ : `Add ${flags.text.length} negative keyword(s) in ${scope} scope`,
55
+ }, flags.yes);
56
+ const client = await createAsaClient(this.config);
57
+ const { replayed, result } = await asaWrite(client, 'post', '/negative-keywords', {
58
+ body,
59
+ idempotencyKey: flags['idempotency-key'],
60
+ });
61
+ noteReplay(replayed, this.log.bind(this));
62
+ reportBulkOutcome({
63
+ applied: result.negative_keywords,
64
+ errors: result.errors,
65
+ isValidationFailure: result.is_validation_failure,
66
+ kind: 'negative keywords',
67
+ }, this.log.bind(this));
68
+ return result;
69
+ }
70
+ }
@@ -0,0 +1,19 @@
1
+ import { Command } from '@oclif/core';
2
+ import type { AsaNegativeKeywordDTO } from '../../../lib/asa-schemas.js';
3
+ import { type PaginatedResponse } from '../../../lib/flags.js';
4
+ export default class AsaNegativeKeywordsList extends Command {
5
+ static description: string;
6
+ static enableJsonFlag: boolean;
7
+ static examples: string[];
8
+ static flags: {
9
+ 'campaign-level-only': import("@oclif/core/interfaces").BooleanFlag<boolean>;
10
+ 'ad-group': import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
11
+ campaign: import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
12
+ search: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
13
+ app: import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
14
+ 'campaign-group': import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
15
+ page: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
16
+ 'page-size': import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
17
+ };
18
+ run(): Promise<PaginatedResponse<AsaNegativeKeywordDTO>>;
19
+ }
@@ -0,0 +1,29 @@
1
+ import { Command, Flags } from '@oclif/core';
2
+ import { createAsaClient } from '../../../lib/asa-client.js';
3
+ import { adGroupScopeFlags, asaPaginationFlags, scopeParams } from '../../../lib/asa-flags.js';
4
+ import { paginationParams } from '../../../lib/flags.js';
5
+ import { printList } from '../../../lib/output.js';
6
+ export default class AsaNegativeKeywordsList extends Command {
7
+ static description = 'List negative keywords; ad_group_id is empty for campaign-level ones';
8
+ static enableJsonFlag = true;
9
+ static examples = [
10
+ '<%= config.bin %> asa negative-keywords list',
11
+ '<%= config.bin %> asa negative-keywords list --campaign CAMPAIGN_UUID',
12
+ ];
13
+ static flags = {
14
+ ...asaPaginationFlags,
15
+ ...adGroupScopeFlags,
16
+ 'campaign-level-only': Flags.boolean({ description: 'Keep only campaign-level rows (ad_group_id is null)' }),
17
+ };
18
+ async run() {
19
+ const { flags } = await this.parse(AsaNegativeKeywordsList);
20
+ const client = await createAsaClient(this.config);
21
+ const result = await client.get('/negative-keywords', {
22
+ ...paginationParams(flags),
23
+ ...scopeParams(flags),
24
+ ...(flags['campaign-level-only'] ? { campaign_level_only: 'true' } : {}),
25
+ });
26
+ printList(result.data, this.log.bind(this), result.meta.pagination);
27
+ return result;
28
+ }
29
+ }
@@ -0,0 +1,13 @@
1
+ import { Command } from '@oclif/core';
2
+ import type { AsaCampaignGroupDTO } from '../../../lib/asa-schemas.js';
3
+ import { type PaginatedResponse } from '../../../lib/flags.js';
4
+ export default class AsaOrgsList extends Command {
5
+ static description: string;
6
+ static enableJsonFlag: boolean;
7
+ static examples: string[];
8
+ static flags: {
9
+ page: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
10
+ 'page-size': import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
11
+ };
12
+ run(): Promise<PaginatedResponse<AsaCampaignGroupDTO>>;
13
+ }
@@ -0,0 +1,18 @@
1
+ import { Command } from '@oclif/core';
2
+ import { createAsaClient } from '../../../lib/asa-client.js';
3
+ import { asaPaginationFlags } from '../../../lib/asa-flags.js';
4
+ import { paginationParams } from '../../../lib/flags.js';
5
+ import { printList } from '../../../lib/output.js';
6
+ export default class AsaOrgsList extends Command {
7
+ static description = 'List the Apple Search Ads organizations this company can spend from';
8
+ static enableJsonFlag = true;
9
+ static examples = ['<%= config.bin %> asa orgs list'];
10
+ static flags = { ...asaPaginationFlags };
11
+ async run() {
12
+ const { flags } = await this.parse(AsaOrgsList);
13
+ const client = await createAsaClient(this.config);
14
+ const result = await client.get('/campaign-groups', paginationParams(flags));
15
+ printList(result.data, this.log.bind(this), result.meta.pagination);
16
+ return result;
17
+ }
18
+ }
@@ -0,0 +1,15 @@
1
+ import { Command } from '@oclif/core';
2
+ import type { AsaProductPageDTO } from '../../../lib/asa-schemas.js';
3
+ import { type PaginatedResponse } from '../../../lib/flags.js';
4
+ export default class AsaProductPagesList extends Command {
5
+ static description: string;
6
+ static enableJsonFlag: boolean;
7
+ static examples: string[];
8
+ static flags: {
9
+ app: import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
+ 'campaign-group': import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
11
+ page: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
12
+ 'page-size': import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
13
+ };
14
+ run(): Promise<PaginatedResponse<AsaProductPageDTO>>;
15
+ }
@@ -0,0 +1,24 @@
1
+ import { Command } from '@oclif/core';
2
+ import { createAsaClient } from '../../../lib/asa-client.js';
3
+ import { asaPaginationFlags, assetScopeFlags, scopeParams } from '../../../lib/asa-flags.js';
4
+ import { paginationParams } from '../../../lib/flags.js';
5
+ import { printList } from '../../../lib/output.js';
6
+ export default class AsaProductPagesList extends Command {
7
+ static description = 'List custom product pages (read-only; authoring stays in App Store Connect)';
8
+ static enableJsonFlag = true;
9
+ static examples = [
10
+ '<%= config.bin %> asa product-pages list',
11
+ '<%= config.bin %> asa product-pages list --app APP_UUID',
12
+ ];
13
+ static flags = { ...asaPaginationFlags, ...assetScopeFlags };
14
+ async run() {
15
+ const { flags } = await this.parse(AsaProductPagesList);
16
+ const client = await createAsaClient(this.config);
17
+ const result = await client.get('/product-pages', {
18
+ ...paginationParams(flags),
19
+ ...scopeParams(flags),
20
+ });
21
+ printList(result.data, this.log.bind(this), result.meta.pagination);
22
+ return result;
23
+ }
24
+ }
@@ -0,0 +1,13 @@
1
+ import { Command } from '@oclif/core';
2
+ import type { AsaProductPageSyncDTO } from '../../../lib/asa-schemas.js';
3
+ export default class AsaProductPagesSync extends Command {
4
+ static description: string;
5
+ static enableJsonFlag: boolean;
6
+ static examples: string[];
7
+ static flags: {
8
+ 'adam-id': import("@oclif/core/interfaces").OptionFlag<number | undefined, import("@oclif/core/interfaces").CustomOptions>;
9
+ 'idempotency-key': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
+ yes: import("@oclif/core/interfaces").BooleanFlag<boolean>;
11
+ };
12
+ run(): Promise<AsaProductPageSyncDTO>;
13
+ }
@@ -0,0 +1,28 @@
1
+ import { Command, Flags } from '@oclif/core';
2
+ import { asaWrite, createAsaClient } from '../../../lib/asa-client.js';
3
+ import { confirmFlags, confirmMutation } from '../../../lib/asa-confirm.js';
4
+ import { idempotencyFlags } from '../../../lib/asa-flags.js';
5
+ import { printResponse } from '../../../lib/output.js';
6
+ export default class AsaProductPagesSync extends Command {
7
+ static description = 'Refresh custom product pages from Apple';
8
+ static enableJsonFlag = true;
9
+ static examples = ['<%= config.bin %> asa product-pages sync', '<%= config.bin %> asa product-pages sync --adam-id 123456'];
10
+ static flags = {
11
+ ...confirmFlags,
12
+ ...idempotencyFlags,
13
+ 'adam-id': Flags.integer({ description: 'Limit the refresh to one app; omit to cover every app' }),
14
+ };
15
+ async run() {
16
+ const { flags } = await this.parse(AsaProductPagesSync);
17
+ const body = { ...(flags['adam-id'] === undefined ? {} : { adam_id: flags['adam-id'] }) };
18
+ await confirmMutation(this, { body, method: 'POST', path: '/product-pages/sync/', summary: 'Queue a product page refresh from Apple' }, flags.yes);
19
+ const client = await createAsaClient(this.config);
20
+ const { result } = await asaWrite(client, 'post', '/product-pages/sync', {
21
+ body,
22
+ idempotencyKey: flags['idempotency-key'],
23
+ });
24
+ this.log(result.replayed ? 'Already running; nothing new was queued.' : 'Sync queued.');
25
+ printResponse(result, this.log.bind(this));
26
+ return result;
27
+ }
28
+ }
@@ -0,0 +1,20 @@
1
+ import { Command } from '@oclif/core';
2
+ import type { AsaSearchTermDTO } from '../../../lib/asa-schemas.js';
3
+ import { type PaginatedResponse } from '../../../lib/flags.js';
4
+ export default class AsaSearchTermsList extends Command {
5
+ static description: string;
6
+ static enableJsonFlag: boolean;
7
+ static examples: string[];
8
+ static flags: {
9
+ 'ad-group': import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
+ campaign: import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
11
+ search: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
12
+ app: import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
13
+ 'campaign-group': import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
14
+ 'date-from': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
15
+ 'date-to': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
16
+ page: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
17
+ 'page-size': import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
18
+ };
19
+ run(): Promise<PaginatedResponse<AsaSearchTermDTO>>;
20
+ }
@@ -0,0 +1,25 @@
1
+ import { Command } from '@oclif/core';
2
+ import { createAsaClient } from '../../../lib/asa-client.js';
3
+ import { adGroupScopeFlags, asaPaginationFlags, periodFlags, periodParams, scopeParams } from '../../../lib/asa-flags.js';
4
+ import { paginationParams } from '../../../lib/flags.js';
5
+ import { printList } from '../../../lib/output.js';
6
+ export default class AsaSearchTermsList extends Command {
7
+ static description = 'List the search terms your ads matched, with their metrics';
8
+ static enableJsonFlag = true;
9
+ static examples = [
10
+ '<%= config.bin %> asa search-terms list --date-from 2026-07-01 --date-to 2026-07-31',
11
+ '<%= config.bin %> asa search-terms list --ad-group AD_GROUP_UUID',
12
+ ];
13
+ static flags = { ...asaPaginationFlags, ...periodFlags, ...adGroupScopeFlags };
14
+ async run() {
15
+ const { flags } = await this.parse(AsaSearchTermsList);
16
+ const client = await createAsaClient(this.config);
17
+ const result = await client.get('/search-terms', {
18
+ ...paginationParams(flags),
19
+ ...periodParams(flags),
20
+ ...scopeParams(flags),
21
+ });
22
+ printList(result.data, this.log.bind(this), result.meta.pagination);
23
+ return result;
24
+ }
25
+ }
@@ -0,0 +1,8 @@
1
+ import { Command } from '@oclif/core';
2
+ import type { AsaMeDTO } from '../../lib/asa-schemas.js';
3
+ export default class AsaWhoami extends Command {
4
+ static description: string;
5
+ static enableJsonFlag: boolean;
6
+ static examples: string[];
7
+ run(): Promise<AsaMeDTO>;
8
+ }
@@ -0,0 +1,22 @@
1
+ import { Command } from '@oclif/core';
2
+ import { createAsaClient } from '../../lib/asa-client.js';
3
+ import { printResponse } from '../../lib/output.js';
4
+ export default class AsaWhoami extends Command {
5
+ static description = 'Show which company the token unlocks and whether Apple Ads is connected';
6
+ static enableJsonFlag = true;
7
+ static examples = ['<%= config.bin %> asa whoami'];
8
+ async run() {
9
+ await this.parse(AsaWhoami);
10
+ const client = await createAsaClient(this.config);
11
+ const result = await client.get('/me');
12
+ printResponse(result, this.log.bind(this));
13
+ if (result.apple_credentials_status !== 'active') {
14
+ this.log('\nApple Ads is not connected. Run `adapty asa connect` to link an account.');
15
+ }
16
+ if (result.access_source === 'none') {
17
+ this.log('\nNo active Ads Manager subscription for this company: connecting an account works, but every');
18
+ this.log('data command answers 402 until the subscription is in place.');
19
+ }
20
+ return result;
21
+ }
22
+ }
@@ -1,15 +1,28 @@
1
+ import { type ApiErrorFormat } from './errors.js';
2
+ export type QueryParams = Record<string, string | string[] | undefined>;
3
+ export interface RequestOptions {
4
+ headers?: Record<string, string>;
5
+ onResponse?: (headers: Headers) => void;
6
+ }
1
7
  export interface ApiClientOptions {
2
8
  baseUrl?: string;
9
+ defaultBaseUrl?: string;
10
+ errorFormat?: ApiErrorFormat;
3
11
  token?: null | string;
12
+ urlEnvVar?: string;
4
13
  userAgent?: string;
5
14
  }
6
15
  export declare class ApiClient {
7
16
  private baseUrl;
17
+ private errorFormat;
8
18
  private token;
9
19
  private userAgent;
10
20
  constructor(opts?: ApiClientOptions);
11
- get<T = unknown>(path: string, params?: Record<string, string>): Promise<T>;
12
- post<T = unknown>(path: string, body?: unknown): Promise<T>;
13
- put<T = unknown>(path: string, body?: unknown): Promise<T>;
21
+ get<T = unknown>(path: string, params?: QueryParams): Promise<T>;
22
+ post<T = unknown>(path: string, body?: unknown, params?: QueryParams, opts?: RequestOptions): Promise<T>;
23
+ postForm<T = unknown>(path: string, form: FormData, params?: QueryParams, opts?: RequestOptions): Promise<T>;
24
+ put<T = unknown>(path: string, body?: unknown, params?: QueryParams, opts?: RequestOptions): Promise<T>;
25
+ private buildUrl;
26
+ private isRetryableRateLimit;
14
27
  private request;
15
28
  }