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,25 @@
1
+ import { Command } from '@oclif/core';
2
+ import type { AsaCampaignMutationDTO } from '../../../lib/asa-schemas.js';
3
+ export default class AsaCampaignsCreate extends Command {
4
+ static description: string;
5
+ static enableJsonFlag: boolean;
6
+ static examples: string[];
7
+ static flags: {
8
+ 'ad-channel-type': import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
9
+ 'adam-id': import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
10
+ 'bidding-strategy': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
11
+ 'billing-event': import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
12
+ budget: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
13
+ country: import("@oclif/core/interfaces").OptionFlag<string[], import("@oclif/core/interfaces").CustomOptions>;
14
+ 'daily-budget': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
15
+ name: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
16
+ org: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
17
+ status: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
18
+ 'supply-source': import("@oclif/core/interfaces").OptionFlag<string[], import("@oclif/core/interfaces").CustomOptions>;
19
+ 'target-cpa': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
20
+ 'idempotency-key': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
21
+ yes: import("@oclif/core/interfaces").BooleanFlag<boolean>;
22
+ currency: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
23
+ };
24
+ run(): Promise<AsaCampaignMutationDTO>;
25
+ }
@@ -0,0 +1,67 @@
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 { currencyFlag, idempotencyFlags, money, moneyFlag } from '../../../lib/asa-flags.js';
5
+ import { isValidUuid } from '../../../lib/flags.js';
6
+ import { printResponse } from '../../../lib/output.js';
7
+ export default class AsaCampaignsCreate extends Command {
8
+ static description = 'Create a campaign in Apple Search Ads';
9
+ static enableJsonFlag = true;
10
+ static examples = [
11
+ '<%= config.bin %> asa campaigns create --org UUID --name "Winter push" --adam-id 123456 --country US --daily-budget 50',
12
+ ];
13
+ static flags = {
14
+ ...currencyFlag,
15
+ ...confirmFlags,
16
+ ...idempotencyFlags,
17
+ 'ad-channel-type': Flags.string({ default: 'SEARCH', description: 'Ad channel type', options: ['DISPLAY', 'SEARCH'] }),
18
+ 'adam-id': Flags.integer({ description: 'App Store app ID (adam_id)', required: true }),
19
+ 'bidding-strategy': Flags.string({
20
+ description: 'Bidding strategy; Apple defaults to MANUAL_CPT when omitted',
21
+ options: ['MANUAL_CPT', 'MAX_CONVERSIONS'],
22
+ }),
23
+ 'billing-event': Flags.string({ default: 'TAPS', description: 'Billing event', options: ['IMPRESSIONS', 'TAPS'] }),
24
+ budget: moneyFlag('Lifetime budget'),
25
+ country: Flags.string({ description: 'Country or region code, repeatable', multiple: true, required: true }),
26
+ 'daily-budget': moneyFlag('Daily budget', { required: true }),
27
+ name: Flags.string({ description: 'Campaign name', required: true }),
28
+ org: Flags.string({ description: 'Campaign group ID (UUID) — see `adapty asa orgs list`', required: true }),
29
+ status: Flags.string({ description: 'Initial status', options: ['ENABLED', 'PAUSED'] }),
30
+ 'supply-source': Flags.string({
31
+ default: ['APPSTORE_SEARCH_RESULTS'],
32
+ description: 'Supply source, repeatable',
33
+ multiple: true,
34
+ }),
35
+ 'target-cpa': moneyFlag('Target CPA'),
36
+ };
37
+ async run() {
38
+ const { flags } = await this.parse(AsaCampaignsCreate);
39
+ if (!isValidUuid(flags.org))
40
+ this.error('Invalid org ID format.', { exit: 2 });
41
+ const body = {
42
+ ad_channel_type: flags['ad-channel-type'],
43
+ adam_id: flags['adam-id'],
44
+ bidding_strategy: flags['bidding-strategy'],
45
+ billing_event: flags['billing-event'],
46
+ budget_amount: money(flags.budget, flags.currency),
47
+ campaign_group_id: flags.org,
48
+ countries_or_regions: flags.country,
49
+ daily_budget_amount: money(flags['daily-budget'], flags.currency),
50
+ name: flags.name,
51
+ status: flags.status,
52
+ supply_sources: flags['supply-source'],
53
+ target_cpa: money(flags['target-cpa'], flags.currency),
54
+ };
55
+ await confirmMutation(this, { body, method: 'POST', path: '/campaigns/', summary: `Create campaign ${flags.name}` }, flags.yes);
56
+ const client = await createAsaClient(this.config);
57
+ const { replayed, result } = await asaWrite(client, 'post', '/campaigns', {
58
+ body,
59
+ idempotencyKey: flags['idempotency-key'],
60
+ });
61
+ noteReplay(replayed, this.log.bind(this));
62
+ if (result.campaign && !replayed)
63
+ this.log('Campaign created!');
64
+ printResponse(result, this.log.bind(this));
65
+ return result;
66
+ }
67
+ }
@@ -0,0 +1,11 @@
1
+ import { Command } from '@oclif/core';
2
+ import type { AsaCampaignDTO } from '../../../lib/asa-schemas.js';
3
+ export default class AsaCampaignsGet extends Command {
4
+ static args: {
5
+ campaign_id: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
6
+ };
7
+ static description: string;
8
+ static enableJsonFlag: boolean;
9
+ static examples: string[];
10
+ run(): Promise<AsaCampaignDTO>;
11
+ }
@@ -0,0 +1,21 @@
1
+ import { Args, Command } from '@oclif/core';
2
+ import { createAsaClient } from '../../../lib/asa-client.js';
3
+ import { isValidUuid } from '../../../lib/flags.js';
4
+ import { printResponse } from '../../../lib/output.js';
5
+ export default class AsaCampaignsGet extends Command {
6
+ static args = {
7
+ campaign_id: Args.string({ description: 'Campaign ID (UUID)', required: true }),
8
+ };
9
+ static description = 'Show one campaign; read numbers with asa metrics';
10
+ static enableJsonFlag = true;
11
+ static examples = ['<%= config.bin %> asa campaigns get 550e8400-e29b-41d4-a716-446655440000'];
12
+ async run() {
13
+ const { args } = await this.parse(AsaCampaignsGet);
14
+ if (!isValidUuid(args.campaign_id))
15
+ this.error('Invalid campaign ID format.', { exit: 2 });
16
+ const client = await createAsaClient(this.config);
17
+ const result = await client.get(`/campaigns/${args.campaign_id}`);
18
+ printResponse(result, this.log.bind(this));
19
+ return result;
20
+ }
21
+ }
@@ -0,0 +1,17 @@
1
+ import { Command } from '@oclif/core';
2
+ import type { AsaCampaignDTO } from '../../../lib/asa-schemas.js';
3
+ import { type PaginatedResponse } from '../../../lib/flags.js';
4
+ export default class AsaCampaignsList extends Command {
5
+ static description: string;
6
+ static enableJsonFlag: boolean;
7
+ static examples: string[];
8
+ static flags: {
9
+ status: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
+ search: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
11
+ app: import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
12
+ 'campaign-group': import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
13
+ page: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
14
+ 'page-size': import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
15
+ };
16
+ run(): Promise<PaginatedResponse<AsaCampaignDTO>>;
17
+ }
@@ -0,0 +1,24 @@
1
+ import { Command } from '@oclif/core';
2
+ import { createAsaClient } from '../../../lib/asa-client.js';
3
+ import { asaPaginationFlags, orgScopeFlags, scopeParams, statusFilter } from '../../../lib/asa-flags.js';
4
+ import { paginationParams } from '../../../lib/flags.js';
5
+ import { printList } from '../../../lib/output.js';
6
+ export default class AsaCampaignsList extends Command {
7
+ static description = 'List Apple Search Ads campaigns; read numbers with asa metrics';
8
+ static enableJsonFlag = true;
9
+ static examples = [
10
+ '<%= config.bin %> asa campaigns list',
11
+ '<%= config.bin %> asa campaigns list --app APP_UUID --status PAUSED',
12
+ ];
13
+ static flags = { ...asaPaginationFlags, ...orgScopeFlags, ...statusFilter(['ENABLED', 'PAUSED']) };
14
+ async run() {
15
+ const { flags } = await this.parse(AsaCampaignsList);
16
+ const client = await createAsaClient(this.config);
17
+ const result = await client.get('/campaigns', {
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,23 @@
1
+ import { Command } from '@oclif/core';
2
+ import type { AsaCampaignMutationDTO } from '../../../lib/asa-schemas.js';
3
+ export default class AsaCampaignsUpdate extends Command {
4
+ static args: {
5
+ campaign_id: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
6
+ };
7
+ static description: string;
8
+ static enableJsonFlag: boolean;
9
+ static examples: string[];
10
+ static flags: {
11
+ 'bidding-strategy': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
12
+ budget: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
13
+ country: import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
14
+ 'daily-budget': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
15
+ name: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
16
+ status: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
17
+ 'target-cpa': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
18
+ 'idempotency-key': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
19
+ yes: import("@oclif/core/interfaces").BooleanFlag<boolean>;
20
+ currency: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
21
+ };
22
+ run(): Promise<AsaCampaignMutationDTO>;
23
+ }
@@ -0,0 +1,66 @@
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 } from '../../../lib/asa-flags.js';
5
+ import { isValidUuid } from '../../../lib/flags.js';
6
+ import { printResponse } from '../../../lib/output.js';
7
+ export default class AsaCampaignsUpdate extends Command {
8
+ static args = {
9
+ campaign_id: Args.string({ description: 'Campaign ID (UUID)', required: true }),
10
+ };
11
+ static description = 'Change a campaign: budgets, countries, status or schedule';
12
+ static enableJsonFlag = true;
13
+ static examples = [
14
+ '<%= config.bin %> asa campaigns update UUID --status PAUSED',
15
+ '<%= config.bin %> asa campaigns update UUID --daily-budget 80',
16
+ ];
17
+ static flags = {
18
+ ...currencyFlag,
19
+ ...confirmFlags,
20
+ ...idempotencyFlags,
21
+ 'bidding-strategy': Flags.string({
22
+ description: 'Bidding strategy',
23
+ options: ['MANUAL_CPT', 'MAX_CONVERSIONS'],
24
+ }),
25
+ budget: moneyFlag('Lifetime budget'),
26
+ country: Flags.string({ description: 'Replace the country list, repeatable', multiple: true }),
27
+ 'daily-budget': moneyFlag('Daily budget'),
28
+ name: Flags.string({ description: 'Campaign name' }),
29
+ status: Flags.string({ description: 'Campaign status', options: ['ENABLED', 'PAUSED'] }),
30
+ 'target-cpa': moneyFlag('Target CPA'),
31
+ };
32
+ async run() {
33
+ const { args, flags } = await this.parse(AsaCampaignsUpdate);
34
+ if (!isValidUuid(args.campaign_id))
35
+ this.error('Invalid campaign ID format.', { exit: 2 });
36
+ const body = {};
37
+ if (flags.name !== undefined)
38
+ body.name = flags.name;
39
+ if (flags.status !== undefined)
40
+ body.status = flags.status;
41
+ if (flags.country !== undefined)
42
+ body.countries_or_regions = flags.country;
43
+ if (flags['daily-budget'] !== undefined)
44
+ body.daily_budget_amount = money(flags['daily-budget'], flags.currency);
45
+ if (flags.budget !== undefined)
46
+ body.budget_amount = money(flags.budget, flags.currency);
47
+ if (flags['target-cpa'] !== undefined)
48
+ body.target_cpa = money(flags['target-cpa'], flags.currency);
49
+ if (flags['bidding-strategy'] !== undefined)
50
+ body.bidding_strategy = flags['bidding-strategy'];
51
+ if (Object.keys(body).length === 0) {
52
+ this.error('Nothing to change. Pass at least one field, e.g. --status PAUSED.', { exit: 2 });
53
+ }
54
+ await confirmMutation(this, { body, method: 'PUT', path: `/campaigns/${args.campaign_id}/`, summary: 'Update campaign' }, flags.yes);
55
+ const client = await createAsaClient(this.config);
56
+ const { replayed, result } = await asaWrite(client, 'put', `/campaigns/${args.campaign_id}`, {
57
+ body,
58
+ idempotencyKey: flags['idempotency-key'],
59
+ });
60
+ noteReplay(replayed, this.log.bind(this));
61
+ if (result.campaign && !replayed)
62
+ this.log('Campaign updated!');
63
+ printResponse(result, this.log.bind(this));
64
+ return result;
65
+ }
66
+ }
@@ -0,0 +1,11 @@
1
+ import { Command } from '@oclif/core';
2
+ import type { AsaCompetitorsSummaryDTO } from '../../../lib/asa-schemas.js';
3
+ export default class AsaCompetitorsSummary extends Command {
4
+ static description: string;
5
+ static enableJsonFlag: boolean;
6
+ static examples: string[];
7
+ static flags: {
8
+ 'app-ids': import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
9
+ };
10
+ run(): Promise<AsaCompetitorsSummaryDTO>;
11
+ }
@@ -0,0 +1,56 @@
1
+ import { Command, Flags } from '@oclif/core';
2
+ import { asaWrite, createAsaClient } from '../../../lib/asa-client.js';
3
+ import { printList, printResponse } from '../../../lib/output.js';
4
+ const MAX_APP_IDS = 5;
5
+ export default class AsaCompetitorsSummary extends Command {
6
+ static description = 'Competitor summary for up to five App Store apps; the server covers the last full month and every country';
7
+ static enableJsonFlag = true;
8
+ static examples = ['<%= config.bin %> asa competitors summary --app-ids 1668337467,6503873027'];
9
+ static flags = {
10
+ 'app-ids': Flags.string({
11
+ description: `Apple App Store IDs (adam_id), comma-separated, 1-${MAX_APP_IDS} values`,
12
+ required: true,
13
+ }),
14
+ };
15
+ async run() {
16
+ const { flags } = await this.parse(AsaCompetitorsSummary);
17
+ const appIds = flags['app-ids']
18
+ .split(',')
19
+ .map((part) => part.trim())
20
+ .filter(Boolean);
21
+ if (appIds.length === 0 || appIds.length > MAX_APP_IDS) {
22
+ this.error(`Pass 1 to ${MAX_APP_IDS} App Store IDs, got ${appIds.length}.`, { exit: 2 });
23
+ }
24
+ if (appIds.some((id) => !/^\d+$/.test(id))) {
25
+ this.error('App Store IDs are numbers, e.g. --app-ids 1668337467,6503873027.', { exit: 2 });
26
+ }
27
+ const client = await createAsaClient(this.config);
28
+ const { result } = await asaWrite(client, 'post', '/competitors/summary', {
29
+ body: { app_ids: appIds.map(Number) },
30
+ });
31
+ const { total } = result;
32
+ printResponse({
33
+ competitors_count: total.competitorsCount,
34
+ countries_asa_count: total.countriesAsaCount,
35
+ countries_with_asa_terms: total.countriesWithAsaTerms,
36
+ total_unique_terms: total.totalUniqueTerms,
37
+ }, this.log.bind(this));
38
+ this.log('');
39
+ this.log('Top apps by performance:');
40
+ printList(total.topAppsByPerformance.map((app) => ({
41
+ adam_id: app.adamId,
42
+ avg_sov: app.avgSov,
43
+ countries: app.countries,
44
+ name: app.name,
45
+ terms_count: app.termsCount,
46
+ })), this.log.bind(this));
47
+ this.log('');
48
+ this.log('Most contested terms:');
49
+ printList(total.mostContestedTerms.map((term) => ({
50
+ competitor_count: term.competitorCount,
51
+ max_sov: term.maxSov,
52
+ term: term.term,
53
+ })), this.log.bind(this));
54
+ return result;
55
+ }
56
+ }
@@ -0,0 +1,12 @@
1
+ import { Command } from '@oclif/core';
2
+ import type { AsaAppleOAuthDTO, AsaMeDTO } from '../../lib/asa-schemas.js';
3
+ export default class AsaConnect extends Command {
4
+ static description: string;
5
+ static enableJsonFlag: boolean;
6
+ static examples: string[];
7
+ static flags: {
8
+ timeout: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
9
+ wait: import("@oclif/core/interfaces").BooleanFlag<boolean>;
10
+ };
11
+ run(): Promise<AsaAppleOAuthDTO | AsaMeDTO>;
12
+ }
@@ -0,0 +1,39 @@
1
+ import { Command, Flags } from '@oclif/core';
2
+ import open from 'open';
3
+ import { createAsaClient } from '../../lib/asa-client.js';
4
+ const POLL_INTERVAL_MS = 3000;
5
+ export default class AsaConnect extends Command {
6
+ static description = 'Connect an Apple Search Ads account to this company';
7
+ static enableJsonFlag = true;
8
+ static examples = ['<%= config.bin %> asa connect', '<%= config.bin %> asa connect --no-wait'];
9
+ static flags = {
10
+ timeout: Flags.integer({ default: 300, description: 'Seconds to wait for the browser step' }),
11
+ wait: Flags.boolean({ allowNo: true, default: true, description: 'Wait until Apple Ads reports as connected' }),
12
+ };
13
+ async run() {
14
+ const { flags } = await this.parse(AsaConnect);
15
+ const client = await createAsaClient(this.config);
16
+ const { auth_url: authUrl } = await client.get('/apple/oauth');
17
+ this.log(`If the browser doesn't open, visit: ${authUrl}\n`);
18
+ this.log('The link is valid for one hour. Sign in to the Adapty dashboard in that browser first — the last');
19
+ this.log('step is authorized by the dashboard session, not by this CLI.');
20
+ await open(authUrl).catch(() => false);
21
+ if (!flags.wait)
22
+ return { auth_url: authUrl };
23
+ const deadline = Date.now() + flags.timeout * 1000;
24
+ let status;
25
+ while (Date.now() < deadline) {
26
+ status = await client.get('/me');
27
+ if (status.apple_credentials_status === 'active') {
28
+ this.log('Apple Ads connected. The first metadata import starts automatically.');
29
+ return status;
30
+ }
31
+ await new Promise((resolve) => {
32
+ setTimeout(resolve, POLL_INTERVAL_MS);
33
+ });
34
+ }
35
+ this.log(`Still not connected after ${flags.timeout}s. The usual cause is a browser that is not signed in to`);
36
+ this.log(`the Adapty dashboard. Finish the browser step, then check with: ${this.config.bin} asa whoami`);
37
+ return status ?? { auth_url: authUrl };
38
+ }
39
+ }
@@ -0,0 +1,15 @@
1
+ import { Command } from '@oclif/core';
2
+ import type { AsaCreativeDTO } from '../../../lib/asa-schemas.js';
3
+ import { type PaginatedResponse } from '../../../lib/flags.js';
4
+ export default class AsaCreativesList 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<AsaCreativeDTO>>;
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 AsaCreativesList extends Command {
7
+ static description = 'List creatives; creative_id is what `asa ads create` needs';
8
+ static enableJsonFlag = true;
9
+ static examples = [
10
+ '<%= config.bin %> asa creatives list',
11
+ '<%= config.bin %> asa creatives list --app APP_UUID',
12
+ ];
13
+ static flags = { ...asaPaginationFlags, ...assetScopeFlags };
14
+ async run() {
15
+ const { flags } = await this.parse(AsaCreativesList);
16
+ const client = await createAsaClient(this.config);
17
+ const result = await client.get('/creatives', {
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,20 @@
1
+ import { Command } from '@oclif/core';
2
+ import type { AsaKeywordMutationDTO } from '../../../lib/asa-schemas.js';
3
+ export default class AsaKeywordsAdd 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, import("@oclif/core/interfaces").CustomOptions>;
9
+ bid: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
+ 'from-file': 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[] | undefined, 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
+ currency: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
17
+ };
18
+ run(): Promise<AsaKeywordMutationDTO>;
19
+ private readTexts;
20
+ }
@@ -0,0 +1,73 @@
1
+ import { Command, Flags } from '@oclif/core';
2
+ import { readFile } from 'node:fs/promises';
3
+ import { asaWrite, createAsaClient, noteReplay } from '../../../lib/asa-client.js';
4
+ import { confirmFlags, confirmMutation } from '../../../lib/asa-confirm.js';
5
+ import { currencyFlag, idempotencyFlags, MAX_BULK_ITEMS, money, moneyFlag, reportBulkOutcome } from '../../../lib/asa-flags.js';
6
+ import { isValidUuid } from '../../../lib/flags.js';
7
+ export default class AsaKeywordsAdd extends Command {
8
+ static description = 'Add targeting keywords to an ad group';
9
+ static enableJsonFlag = true;
10
+ static examples = [
11
+ '<%= config.bin %> asa keywords add --ad-group UUID --text "running shoes" --text "trail shoes" --bid 1.20',
12
+ '<%= config.bin %> asa keywords add --ad-group UUID --from-file keywords.txt --match-type EXACT',
13
+ ];
14
+ static flags = {
15
+ ...currencyFlag,
16
+ ...confirmFlags,
17
+ ...idempotencyFlags,
18
+ 'ad-group': Flags.string({ description: 'Ad group ID (UUID) — the campaign is resolved from it', required: true }),
19
+ bid: moneyFlag('Bid per keyword'),
20
+ 'from-file': Flags.string({ description: 'File with one keyword per line, combined with any --text values' }),
21
+ 'match-type': Flags.string({ default: 'BROAD', description: 'Match type', options: ['BROAD', 'EXACT'] }),
22
+ status: Flags.string({ default: 'ACTIVE', description: 'Keyword status', options: ['ACTIVE', 'PAUSED'] }),
23
+ text: Flags.string({ description: 'Keyword text, repeatable', multiple: true }),
24
+ };
25
+ async run() {
26
+ const { flags } = await this.parse(AsaKeywordsAdd);
27
+ if (!isValidUuid(flags['ad-group']))
28
+ this.error('Invalid ad group ID format.', { exit: 2 });
29
+ const texts = [...(flags.text ?? []), ...(await this.readTexts(flags['from-file']))];
30
+ if (texts.length === 0)
31
+ this.error('Pass at least one --text or a --from-file with keywords.', { exit: 2 });
32
+ if (texts.length > MAX_BULK_ITEMS) {
33
+ this.error(`A single call takes at most ${MAX_BULK_ITEMS} keywords, got ${texts.length}.`, { exit: 2 });
34
+ }
35
+ const body = {
36
+ keywords: texts.map((text) => ({
37
+ ad_group_id: flags['ad-group'],
38
+ bid_amount: money(flags.bid, flags.currency),
39
+ match_type: flags['match-type'],
40
+ status: flags.status,
41
+ text,
42
+ })),
43
+ };
44
+ await confirmMutation(this, { body, method: 'POST', path: '/keywords/', summary: `Add ${texts.length} keyword(s)` }, flags.yes);
45
+ const client = await createAsaClient(this.config);
46
+ const { replayed, result } = await asaWrite(client, 'post', '/keywords', {
47
+ body,
48
+ idempotencyKey: flags['idempotency-key'],
49
+ });
50
+ noteReplay(replayed, this.log.bind(this));
51
+ reportBulkOutcome({
52
+ applied: result.keywords,
53
+ errors: result.errors,
54
+ isValidationFailure: result.is_validation_failure,
55
+ kind: 'keywords',
56
+ }, this.log.bind(this));
57
+ return result;
58
+ }
59
+ async readTexts(path) {
60
+ if (!path)
61
+ return [];
62
+ try {
63
+ const raw = await readFile(path, 'utf8');
64
+ return raw
65
+ .split('\n')
66
+ .map((line) => line.trim())
67
+ .filter(Boolean);
68
+ }
69
+ catch {
70
+ this.error(`Could not read ${path}.`, { exit: 2 });
71
+ }
72
+ }
73
+ }
@@ -0,0 +1,19 @@
1
+ import { Command } from '@oclif/core';
2
+ import type { AsaKeywordDTO } from '../../../lib/asa-schemas.js';
3
+ import { type PaginatedResponse } from '../../../lib/flags.js';
4
+ export default class AsaKeywordsList extends Command {
5
+ static description: string;
6
+ static enableJsonFlag: boolean;
7
+ static examples: string[];
8
+ static flags: {
9
+ status: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
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<AsaKeywordDTO>>;
19
+ }
@@ -0,0 +1,24 @@
1
+ import { Command } from '@oclif/core';
2
+ import { createAsaClient } from '../../../lib/asa-client.js';
3
+ import { adGroupScopeFlags, asaPaginationFlags, scopeParams, statusFilter } from '../../../lib/asa-flags.js';
4
+ import { paginationParams } from '../../../lib/flags.js';
5
+ import { printList } from '../../../lib/output.js';
6
+ export default class AsaKeywordsList extends Command {
7
+ static description = 'List targeting keywords; read numbers with asa metrics';
8
+ static enableJsonFlag = true;
9
+ static examples = [
10
+ '<%= config.bin %> asa keywords list',
11
+ '<%= config.bin %> asa keywords list --ad-group AD_GROUP_UUID --status ACTIVE',
12
+ ];
13
+ static flags = { ...asaPaginationFlags, ...adGroupScopeFlags, ...statusFilter(['ACTIVE', 'PAUSED']) };
14
+ async run() {
15
+ const { flags } = await this.parse(AsaKeywordsList);
16
+ const client = await createAsaClient(this.config);
17
+ const result = await client.get('/keywords', {
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,21 @@
1
+ import { Command } from '@oclif/core';
2
+ import type { AsaKeywordMutationDTO } from '../../../lib/asa-schemas.js';
3
+ export default class AsaKeywordsUpdate extends Command {
4
+ static args: {
5
+ keyword_id: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
6
+ };
7
+ static description: string;
8
+ static enableJsonFlag: boolean;
9
+ static examples: string[];
10
+ static flags: {
11
+ bid: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
12
+ 'match-type': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
13
+ status: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
14
+ text: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
15
+ 'idempotency-key': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
16
+ yes: import("@oclif/core/interfaces").BooleanFlag<boolean>;
17
+ currency: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
18
+ };
19
+ static strict: boolean;
20
+ run(): Promise<AsaKeywordMutationDTO>;
21
+ }