adapty 0.8.4-beta.1 → 0.8.5
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 +133 -3
- package/bin/dev.js +2 -2
- package/bin/run.js +2 -2
- package/dist/commands/access-levels/create.js +1 -1
- package/dist/commands/access-levels/list.d.ts +1 -1
- package/dist/commands/apps/create.js +9 -6
- package/dist/commands/apps/list.d.ts +1 -1
- package/dist/commands/apps/update.js +7 -4
- package/dist/commands/asa/ad-groups/create.js +9 -7
- package/dist/commands/asa/ad-groups/get.js +3 -2
- package/dist/commands/asa/ad-groups/list.d.ts +1 -1
- package/dist/commands/asa/ad-groups/list.js +1 -1
- package/dist/commands/asa/ad-groups/update.js +22 -12
- package/dist/commands/asa/ads/create.js +7 -5
- package/dist/commands/asa/ads/get.js +3 -2
- package/dist/commands/asa/ads/list.d.ts +1 -1
- package/dist/commands/asa/ads/list.js +1 -1
- package/dist/commands/asa/ads/update.js +9 -5
- package/dist/commands/asa/apps/list.d.ts +1 -1
- package/dist/commands/asa/apps/list.js +1 -1
- package/dist/commands/asa/automations/create.d.ts +9 -0
- package/dist/commands/asa/automations/create.js +26 -7
- package/dist/commands/asa/automations/get.js +3 -2
- package/dist/commands/asa/automations/list.d.ts +1 -1
- package/dist/commands/asa/automations/list.js +1 -1
- package/dist/commands/asa/automations/run.js +5 -3
- package/dist/commands/asa/automations/runs.d.ts +1 -1
- package/dist/commands/asa/automations/runs.js +4 -4
- package/dist/commands/asa/automations/update.d.ts +9 -0
- package/dist/commands/asa/automations/update.js +36 -12
- package/dist/commands/asa/campaigns/bulk-create.js +31 -18
- package/dist/commands/asa/campaigns/bulk-list.js +7 -5
- package/dist/commands/asa/campaigns/bulk-status.js +3 -2
- package/dist/commands/asa/campaigns/create.js +10 -8
- package/dist/commands/asa/campaigns/get.js +3 -2
- package/dist/commands/asa/campaigns/list.d.ts +1 -1
- package/dist/commands/asa/campaigns/list.js +1 -1
- package/dist/commands/asa/campaigns/update.js +25 -15
- package/dist/commands/asa/competitors/summary.js +5 -5
- package/dist/commands/asa/connect.js +5 -3
- package/dist/commands/asa/creatives/list.d.ts +1 -1
- package/dist/commands/asa/creatives/list.js +1 -1
- package/dist/commands/asa/keywords/add.js +13 -10
- package/dist/commands/asa/keywords/list.d.ts +1 -1
- package/dist/commands/asa/keywords/list.js +1 -1
- package/dist/commands/asa/keywords/update.js +15 -10
- package/dist/commands/asa/metrics/index.d.ts +6 -3
- package/dist/commands/asa/metrics/index.js +38 -14
- package/dist/commands/asa/metrics/overview.js +3 -3
- package/dist/commands/asa/negative-keywords/add.js +10 -7
- package/dist/commands/asa/negative-keywords/list.d.ts +1 -1
- package/dist/commands/asa/negative-keywords/list.js +1 -1
- package/dist/commands/asa/orgs/list.d.ts +1 -1
- package/dist/commands/asa/orgs/list.js +1 -1
- package/dist/commands/asa/product-pages/list.d.ts +1 -1
- package/dist/commands/asa/product-pages/list.js +1 -1
- package/dist/commands/asa/product-pages/sync.js +1 -1
- package/dist/commands/asa/search-terms/list.d.ts +1 -1
- package/dist/commands/asa/search-terms/list.js +1 -1
- package/dist/commands/asa/whoami.js +7 -2
- package/dist/commands/auth/login.js +3 -1
- package/dist/commands/flows/config/get.js +2 -2
- package/dist/commands/flows/config/preview.d.ts +2 -2
- package/dist/commands/flows/config/preview.js +6 -4
- package/dist/commands/flows/config/update.js +5 -3
- package/dist/commands/flows/config/validate.js +5 -3
- package/dist/commands/flows/list.d.ts +1 -1
- package/dist/commands/flows/media/upload.js +1 -1
- package/dist/commands/flows/publish.js +5 -5
- package/dist/commands/paywalls/create.js +1 -1
- package/dist/commands/paywalls/list.d.ts +1 -1
- package/dist/commands/paywalls/placements.d.ts +2 -2
- package/dist/commands/paywalls/update.js +1 -1
- package/dist/commands/placements/create.js +17 -12
- package/dist/commands/placements/list.d.ts +1 -1
- package/dist/commands/placements/update.js +20 -15
- package/dist/commands/products/create.js +20 -13
- package/dist/commands/products/list.d.ts +1 -1
- package/dist/commands/products/update.js +1 -1
- package/dist/commands/segments/list.d.ts +1 -1
- package/dist/lib/api-client.d.ts +10 -6
- package/dist/lib/api-client.js +56 -35
- package/dist/lib/api-schemas.d.ts +56 -56
- package/dist/lib/app-url.js +2 -1
- package/dist/lib/asa-client.d.ts +13 -8
- package/dist/lib/asa-client.js +9 -4
- package/dist/lib/asa-flags.d.ts +37 -13
- package/dist/lib/asa-flags.js +87 -20
- package/dist/lib/asa-keyword-action.d.ts +18 -0
- package/dist/lib/asa-keyword-action.js +175 -0
- package/dist/lib/asa-schemas.d.ts +100 -80
- package/dist/lib/auth.js +2 -1
- package/dist/lib/client-from-config.d.ts +1 -1
- package/dist/lib/client-from-config.js +2 -1
- package/dist/lib/config.d.ts +2 -2
- package/dist/lib/confirm.d.ts +4 -4
- package/dist/lib/confirm.js +12 -7
- package/dist/lib/errors.d.ts +18 -18
- package/dist/lib/errors.js +14 -10
- package/dist/lib/flags.d.ts +3 -3
- package/dist/lib/flags.js +2 -1
- package/dist/lib/flow-help.js +11 -6
- package/dist/lib/output.js +23 -12
- package/dist/lib/preview.d.ts +5 -5
- package/dist/lib/preview.js +2 -1
- package/oclif.manifest.json +372 -200
- package/package.json +10 -8
|
@@ -13,7 +13,7 @@ export default class AsaCreativesList extends Command {
|
|
|
13
13
|
static flags = { ...asaPaginationFlags, ...assetScopeFlags };
|
|
14
14
|
async run() {
|
|
15
15
|
const { flags } = await this.parse(AsaCreativesList);
|
|
16
|
-
const client = await createAsaClient(this
|
|
16
|
+
const client = await createAsaClient(this);
|
|
17
17
|
const result = await client.get('/creatives', {
|
|
18
18
|
...paginationParams(flags),
|
|
19
19
|
...scopeParams(flags),
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Command, Flags } from '@oclif/core';
|
|
2
1
|
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { Command, Flags } from '@oclif/core';
|
|
3
3
|
import { asaWrite, createAsaClient, noteReplay } from '../../../lib/asa-client.js';
|
|
4
4
|
import { currencyFlag, idempotencyFlags, MAX_BULK_ITEMS, money, moneyFlag, reportBulkOutcome } from '../../../lib/asa-flags.js';
|
|
5
5
|
import { confirmFlags, confirmMutation } from '../../../lib/confirm.js';
|
|
@@ -16,24 +16,26 @@ export default class AsaKeywordsAdd extends Command {
|
|
|
16
16
|
...confirmFlags,
|
|
17
17
|
...idempotencyFlags,
|
|
18
18
|
'ad-group': Flags.string({ description: 'Ad group ID (UUID) — the campaign is resolved from it', required: true }),
|
|
19
|
-
bid: moneyFlag('Bid per keyword'),
|
|
19
|
+
'bid': moneyFlag('Bid per keyword'),
|
|
20
20
|
'from-file': Flags.string({ description: 'File with one keyword per line, combined with any --text values' }),
|
|
21
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 }),
|
|
22
|
+
'status': Flags.string({ default: 'ACTIVE', description: 'Keyword status', options: ['ACTIVE', 'PAUSED'] }),
|
|
23
|
+
'text': Flags.string({ description: 'Keyword text, repeatable', multiple: true }),
|
|
24
24
|
};
|
|
25
25
|
async run() {
|
|
26
26
|
const { flags } = await this.parse(AsaKeywordsAdd);
|
|
27
|
-
if (!isValidUuid(flags['ad-group']))
|
|
27
|
+
if (!isValidUuid(flags['ad-group'])) {
|
|
28
28
|
this.error('Invalid ad group ID format.', { exit: 2 });
|
|
29
|
+
}
|
|
29
30
|
const texts = [...(flags.text ?? []), ...(await this.readTexts(flags['from-file']))];
|
|
30
|
-
if (texts.length === 0)
|
|
31
|
+
if (texts.length === 0) {
|
|
31
32
|
this.error('Pass at least one --text or a --from-file with keywords.', { exit: 2 });
|
|
33
|
+
}
|
|
32
34
|
if (texts.length > MAX_BULK_ITEMS) {
|
|
33
35
|
this.error(`A single call takes at most ${MAX_BULK_ITEMS} keywords, got ${texts.length}.`, { exit: 2 });
|
|
34
36
|
}
|
|
35
37
|
const body = {
|
|
36
|
-
keywords: texts.map(
|
|
38
|
+
keywords: texts.map(text => ({
|
|
37
39
|
ad_group_id: flags['ad-group'],
|
|
38
40
|
bid_amount: money(flags.bid, flags.currency),
|
|
39
41
|
match_type: flags['match-type'],
|
|
@@ -42,7 +44,7 @@ export default class AsaKeywordsAdd extends Command {
|
|
|
42
44
|
})),
|
|
43
45
|
};
|
|
44
46
|
await confirmMutation(this, { body, method: 'POST', path: '/keywords/', summary: `Add ${texts.length} keyword(s)` }, flags.yes);
|
|
45
|
-
const client = await createAsaClient(this
|
|
47
|
+
const client = await createAsaClient(this);
|
|
46
48
|
const { replayed, result } = await asaWrite(client, 'post', '/keywords', {
|
|
47
49
|
body,
|
|
48
50
|
idempotencyKey: flags['idempotency-key'],
|
|
@@ -57,13 +59,14 @@ export default class AsaKeywordsAdd extends Command {
|
|
|
57
59
|
return result;
|
|
58
60
|
}
|
|
59
61
|
async readTexts(path) {
|
|
60
|
-
if (!path)
|
|
62
|
+
if (!path) {
|
|
61
63
|
return [];
|
|
64
|
+
}
|
|
62
65
|
try {
|
|
63
66
|
const raw = await readFile(path, 'utf8');
|
|
64
67
|
return raw
|
|
65
68
|
.split('\n')
|
|
66
|
-
.map(
|
|
69
|
+
.map(line => line.trim())
|
|
67
70
|
.filter(Boolean);
|
|
68
71
|
}
|
|
69
72
|
catch {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Command } from '@oclif/core';
|
|
2
2
|
import type { AsaKeywordDTO } from '../../../lib/asa-schemas.js';
|
|
3
|
-
import {
|
|
3
|
+
import type { PaginatedResponse } from '../../../lib/flags.js';
|
|
4
4
|
export default class AsaKeywordsList extends Command {
|
|
5
5
|
static description: string;
|
|
6
6
|
static enableJsonFlag: boolean;
|
|
@@ -13,7 +13,7 @@ export default class AsaKeywordsList extends Command {
|
|
|
13
13
|
static flags = { ...asaPaginationFlags, ...adGroupScopeFlags, ...statusFilter(['ACTIVE', 'PAUSED']) };
|
|
14
14
|
async run() {
|
|
15
15
|
const { flags } = await this.parse(AsaKeywordsList);
|
|
16
|
-
const client = await createAsaClient(this
|
|
16
|
+
const client = await createAsaClient(this);
|
|
17
17
|
const result = await client.get('/keywords', {
|
|
18
18
|
...paginationParams(flags),
|
|
19
19
|
...scopeParams(flags),
|
|
@@ -17,37 +17,42 @@ export default class AsaKeywordsUpdate extends Command {
|
|
|
17
17
|
...currencyFlag,
|
|
18
18
|
...confirmFlags,
|
|
19
19
|
...idempotencyFlags,
|
|
20
|
-
bid: moneyFlag('Bid'),
|
|
20
|
+
'bid': moneyFlag('Bid'),
|
|
21
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)' }),
|
|
22
|
+
'status': Flags.string({ description: 'Keyword status', options: ['ACTIVE', 'PAUSED'] }),
|
|
23
|
+
'text': Flags.string({ description: 'Keyword text (only meaningful for a single keyword)' }),
|
|
24
24
|
};
|
|
25
25
|
static strict = false;
|
|
26
26
|
async run() {
|
|
27
27
|
const { argv, flags } = await this.parse(AsaKeywordsUpdate);
|
|
28
28
|
const keywordIds = argv;
|
|
29
29
|
for (const id of keywordIds) {
|
|
30
|
-
if (!isValidUuid(id))
|
|
30
|
+
if (!isValidUuid(id)) {
|
|
31
31
|
this.error(`Invalid keyword ID format: ${id}`, { exit: 2 });
|
|
32
|
+
}
|
|
32
33
|
}
|
|
33
34
|
const change = {};
|
|
34
|
-
if (flags.bid !== undefined)
|
|
35
|
+
if (flags.bid !== undefined) {
|
|
35
36
|
change.bid_amount = money(flags.bid, flags.currency);
|
|
36
|
-
|
|
37
|
+
}
|
|
38
|
+
if (flags.status !== undefined) {
|
|
37
39
|
change.status = flags.status;
|
|
38
|
-
|
|
40
|
+
}
|
|
41
|
+
if (flags['match-type'] !== undefined) {
|
|
39
42
|
change.match_type = flags['match-type'];
|
|
40
|
-
|
|
43
|
+
}
|
|
44
|
+
if (flags.text !== undefined) {
|
|
41
45
|
change.text = flags.text;
|
|
46
|
+
}
|
|
42
47
|
if (Object.keys(change).length === 0) {
|
|
43
48
|
this.error('Nothing to change. Pass at least one field, e.g. --bid 2.00.', { exit: 2 });
|
|
44
49
|
}
|
|
45
50
|
if (flags.text !== undefined && keywordIds.length > 1) {
|
|
46
51
|
this.error('--text would give every keyword the same text. Update them one at a time.', { exit: 2 });
|
|
47
52
|
}
|
|
48
|
-
const body = { keywords: keywordIds.map(
|
|
53
|
+
const body = { keywords: keywordIds.map(id => ({ internal_id: id, ...change })) };
|
|
49
54
|
await confirmMutation(this, { body, method: 'PUT', path: '/keywords/', summary: `Update ${keywordIds.length} keyword(s)` }, flags.yes);
|
|
50
|
-
const client = await createAsaClient(this
|
|
55
|
+
const client = await createAsaClient(this);
|
|
51
56
|
const { replayed, result } = await asaWrite(client, 'put', '/keywords', {
|
|
52
57
|
body,
|
|
53
58
|
idempotencyKey: flags['idempotency-key'],
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Command } from '@oclif/core';
|
|
2
|
-
import {
|
|
2
|
+
import type { AsaMetricsResponse } from '../../../lib/asa-schemas.js';
|
|
3
3
|
export default class AsaMetrics extends Command {
|
|
4
4
|
static description: string;
|
|
5
5
|
static enableJsonFlag: boolean;
|
|
@@ -9,13 +9,16 @@ export default class AsaMetrics extends Command {
|
|
|
9
9
|
'date-to': import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
10
|
entity: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
11
|
'group-by': import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
|
-
metric: import("@oclif/core/interfaces").OptionFlag<string[]
|
|
12
|
+
metric: import("@oclif/core/interfaces").OptionFlag<string[], import("@oclif/core/interfaces").CustomOptions>;
|
|
13
13
|
order: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
14
14
|
'order-by': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
15
15
|
'order-by-day': import("@oclif/core/interfaces").OptionFlag<number | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
16
|
+
'ad-group': import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
17
|
+
app: import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
18
|
+
campaign: import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
16
19
|
'by-days': import("@oclif/core/interfaces").OptionFlag<number[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
17
20
|
page: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
|
|
18
21
|
'page-size': import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
|
|
19
22
|
};
|
|
20
|
-
run(): Promise<
|
|
23
|
+
run(): Promise<AsaMetricsResponse>;
|
|
21
24
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Command, Flags } from '@oclif/core';
|
|
2
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';
|
|
3
|
+
import { ASA_GROUP_BY_DIMENSIONS, ASA_METRIC_ENTITIES, asaPaginationFlags, byDaysFlag, MAX_BY_DAYS, metricsScopeBody, metricsScopeFlags, } from '../../../lib/asa-flags.js';
|
|
4
4
|
import { paginationParams } from '../../../lib/flags.js';
|
|
5
5
|
import { printList } from '../../../lib/output.js';
|
|
6
6
|
export default class AsaMetrics extends Command {
|
|
@@ -10,34 +10,50 @@ One row per entity, already aggregated server-side and sorted by --order-by, so
|
|
|
10
10
|
call with --order-by and --page-size N — never sum pages yourself. Account-level totals are one call to
|
|
11
11
|
asa metrics overview instead. The date window is capped by the finest --group-by period: 28 days when
|
|
12
12
|
day is grouped, 90 with no period grouping, 180 by week, 365 by month and coarser — widen the window by
|
|
13
|
-
coarsening the grouping, not by splitting into more calls. Each page is also capped at
|
|
14
|
-
|
|
15
|
-
the grouping, narrow the window, or reduce page[size].
|
|
16
|
-
|
|
13
|
+
coarsening the grouping, not by splitting into more calls. Each page is also capped at a breakdown-row
|
|
14
|
+
count (entities × countries × periods); over it the call fails with 422 cli_response_too_large naming the
|
|
15
|
+
page[size] that fits — coarsen the grouping, narrow the window, or reduce page[size].
|
|
16
|
+
|
|
17
|
+
Money columns are in the campaign group currency, not USD, and spend and local_spend carry the same
|
|
18
|
+
figure; the currency itself comes from asa orgs list. Rate budgets and the breakdown-row cap are set per
|
|
19
|
+
company: read the current ones with asa whoami rather than assuming a default. meta.pagination.count is
|
|
20
|
+
the full entity count behind the filters, so take inventory from there instead of counting the rows on a
|
|
21
|
+
page. meta.max_valid_day says how many days the youngest cohort in the window has lived — any --by-days
|
|
22
|
+
window above it repeats the last real figure because that time has not passed yet, so treat those as
|
|
23
|
+
unreached rather than as a plateau.
|
|
24
|
+
|
|
25
|
+
--metric is required and every metric named is computed over the whole entity set, so ask for the
|
|
26
|
+
columns you actually read. subscribers and paid_subscribers (and arppu / arpas, which derive from them)
|
|
27
|
+
count unique profiles per entity and cost roughly seventeen times the rest; whatever the --entity, they
|
|
28
|
+
are refused unless --campaign or --ad-group scopes the call. Those scope flags are also the cheapest way
|
|
29
|
+
to make any call fast, since cost follows the number of entities aggregated, not the page size.`;
|
|
17
30
|
static enableJsonFlag = true;
|
|
18
31
|
static examples = [
|
|
19
|
-
'<%= config.bin %> asa metrics --entity campaign --date-from 2026-07-01 --date-to 2026-07-31',
|
|
20
|
-
'<%= config.bin %> asa metrics --entity campaign --date-from 2026-07-01 --date-to 2026-07-31 --order-by spend --page-size 5',
|
|
21
|
-
'<%= config.bin %> asa metrics --entity campaign --date-from 2026-07-01 --date-to 2026-07-31 --group-by country --page-size 1000',
|
|
32
|
+
'<%= config.bin %> asa metrics --entity campaign --date-from 2026-07-01 --date-to 2026-07-31 --metric spend --metric adapty_installs',
|
|
33
|
+
'<%= config.bin %> asa metrics --entity campaign --date-from 2026-07-01 --date-to 2026-07-31 --metric spend --order-by spend --page-size 5',
|
|
34
|
+
'<%= config.bin %> asa metrics --entity campaign --date-from 2026-07-01 --date-to 2026-07-31 --metric spend --group-by country --page-size 1000',
|
|
22
35
|
'<%= config.bin %> asa metrics --entity keyword --date-from 2026-07-01 --date-to 2026-07-31 --metric spend --metric roas',
|
|
23
36
|
'<%= config.bin %> asa metrics --entity campaign --date-from 2026-07-01 --date-to 2026-07-31 --metric roas --by-days 7 --by-days 90',
|
|
37
|
+
'<%= config.bin %> asa metrics --entity keyword --date-from 2026-07-01 --date-to 2026-07-31 --metric arpas --campaign 0f0e...',
|
|
24
38
|
];
|
|
25
39
|
static flags = {
|
|
26
40
|
...asaPaginationFlags,
|
|
27
41
|
...byDaysFlag,
|
|
42
|
+
...metricsScopeFlags,
|
|
28
43
|
'date-from': Flags.string({ description: 'Start of the period (YYYY-MM-DD)', required: true }),
|
|
29
44
|
'date-to': Flags.string({ description: 'End of the period (YYYY-MM-DD)', required: true }),
|
|
30
|
-
entity: Flags.string({ description: 'What to report on', options: ASA_METRIC_ENTITIES, required: true }),
|
|
45
|
+
'entity': Flags.string({ description: 'What to report on', options: ASA_METRIC_ENTITIES, required: true }),
|
|
31
46
|
'group-by': Flags.string({
|
|
32
47
|
description: 'Break the rows down by a dimension, repeatable',
|
|
33
48
|
multiple: true,
|
|
34
49
|
options: ASA_GROUP_BY_DIMENSIONS,
|
|
35
50
|
}),
|
|
36
|
-
metric: Flags.string({
|
|
37
|
-
description: 'Metric name (dashboard nomenclature, e.g. spend, taps, gross_roas), repeatable;
|
|
51
|
+
'metric': Flags.string({
|
|
52
|
+
description: 'Metric name (dashboard nomenclature, e.g. spend, taps, gross_roas), repeatable and required; every metric asked for is computed over the whole entity set, so list only what you read; a wrong name fails listing all valid ones',
|
|
38
53
|
multiple: true,
|
|
54
|
+
required: true,
|
|
39
55
|
}),
|
|
40
|
-
order: Flags.string({ default: 'desc', description: 'Sort direction', options: ['asc', 'desc'] }),
|
|
56
|
+
'order': Flags.string({ default: 'desc', description: 'Sort direction', options: ['asc', 'desc'] }),
|
|
41
57
|
'order-by': Flags.string({
|
|
42
58
|
description: 'Metric or field to sort by; cohort metrics rank via their gross_/proceeds_/net_ names',
|
|
43
59
|
}),
|
|
@@ -50,14 +66,15 @@ the grouping, narrow the window, or reduce page[size]. Budget: 5 metrics calls p
|
|
|
50
66
|
if (flags['by-days'] && flags['by-days'].length > MAX_BY_DAYS) {
|
|
51
67
|
this.error(`At most ${MAX_BY_DAYS} renewal windows per call, got ${flags['by-days'].length}.`, { exit: 2 });
|
|
52
68
|
}
|
|
53
|
-
const client = await createAsaClient(this
|
|
69
|
+
const client = await createAsaClient(this);
|
|
54
70
|
const { result } = await asaWrite(client, 'post', '/metrics', {
|
|
55
71
|
body: {
|
|
56
72
|
date_from: flags['date-from'],
|
|
57
73
|
date_to: flags['date-to'],
|
|
58
74
|
entity: flags.entity,
|
|
75
|
+
metrics: flags.metric,
|
|
59
76
|
order: flags.order,
|
|
60
|
-
...(flags
|
|
77
|
+
...metricsScopeBody(flags),
|
|
61
78
|
...(flags['by-days'] === undefined ? {} : { by_days: flags['by-days'] }),
|
|
62
79
|
...(flags['group-by'] === undefined ? {} : { group_by: flags['group-by'] }),
|
|
63
80
|
...(flags['order-by'] === undefined ? {} : { order_by: flags['order-by'] }),
|
|
@@ -65,7 +82,14 @@ the grouping, narrow the window, or reduce page[size]. Budget: 5 metrics calls p
|
|
|
65
82
|
},
|
|
66
83
|
params: paginationParams(flags),
|
|
67
84
|
});
|
|
85
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- FIXME if you see this
|
|
68
86
|
printList(result.data, this.log.bind(this), result.meta?.pagination);
|
|
87
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- FIXME if you see this
|
|
88
|
+
const horizon = result.meta?.max_valid_day;
|
|
89
|
+
if (horizon !== undefined && flags['by-days']?.some(day => day > horizon)) {
|
|
90
|
+
this.log(`\nCohort windows past day ${horizon} have not been reached by this date range yet; they repeat the last `
|
|
91
|
+
+ 'figure actually observed rather than projecting it.');
|
|
92
|
+
}
|
|
69
93
|
return result;
|
|
70
94
|
}
|
|
71
95
|
}
|
|
@@ -18,8 +18,8 @@ the 5-per-minute metrics budget with asa metrics.`;
|
|
|
18
18
|
...byDaysFlag,
|
|
19
19
|
'date-from': Flags.string({ description: 'Start of the period (YYYY-MM-DD)', required: true }),
|
|
20
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({
|
|
21
|
+
'entity': Flags.string({ description: 'What to report on', options: ASA_METRIC_ENTITIES, required: true }),
|
|
22
|
+
'metric': Flags.string({
|
|
23
23
|
description: 'Metric name, repeatable; cohort roots only here (revenue, roas, arpu), not their gross_/proceeds_/net_ variants; omit for every metric',
|
|
24
24
|
multiple: true,
|
|
25
25
|
}),
|
|
@@ -34,7 +34,7 @@ the 5-per-minute metrics budget with asa metrics.`;
|
|
|
34
34
|
if (flags['by-days'] && flags['by-days'].length > MAX_BY_DAYS) {
|
|
35
35
|
this.error(`At most ${MAX_BY_DAYS} renewal windows per call, got ${flags['by-days'].length}.`, { exit: 2 });
|
|
36
36
|
}
|
|
37
|
-
const client = await createAsaClient(this
|
|
37
|
+
const client = await createAsaClient(this);
|
|
38
38
|
const { result } = await asaWrite(client, 'post', '/metrics/overview', {
|
|
39
39
|
body: {
|
|
40
40
|
date_from: flags['date-from'],
|
|
@@ -19,25 +19,28 @@ export default class AsaNegativeKeywordsAdd extends Command {
|
|
|
19
19
|
dependsOn: ['campaign'],
|
|
20
20
|
description: 'Apply to every ad group of the campaign instead of the campaign itself',
|
|
21
21
|
}),
|
|
22
|
-
campaign: Flags.string({ description: 'Campaign ID (UUID)', exclusive: ['ad-group'] }),
|
|
22
|
+
'campaign': Flags.string({ description: 'Campaign ID (UUID)', exclusive: ['ad-group'] }),
|
|
23
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 }),
|
|
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
26
|
};
|
|
27
27
|
async run() {
|
|
28
28
|
const { flags } = await this.parse(AsaNegativeKeywordsAdd);
|
|
29
29
|
const target = flags['ad-group'] ?? flags.campaign;
|
|
30
|
-
if (!target)
|
|
30
|
+
if (!target) {
|
|
31
31
|
this.error('Pass either --ad-group or --campaign.', { exit: 2 });
|
|
32
|
-
|
|
32
|
+
}
|
|
33
|
+
if (!isValidUuid(target)) {
|
|
33
34
|
this.error('Invalid ID format.', { exit: 2 });
|
|
35
|
+
}
|
|
34
36
|
if (flags.text.length > MAX_BULK_ITEMS) {
|
|
35
37
|
this.error(`A single call takes at most ${MAX_BULK_ITEMS} keywords, got ${flags.text.length}.`, { exit: 2 });
|
|
36
38
|
}
|
|
39
|
+
// eslint-disable-next-line no-nested-ternary -- FIXME if you see this
|
|
37
40
|
const scope = flags['ad-group'] ? 'AD_GROUP' : flags['all-ad-groups'] ? 'ALL_CAMPAIGN_AD_GROUPS' : 'CAMPAIGN';
|
|
38
41
|
const parent = flags['ad-group'] ? { ad_group_id: flags['ad-group'] } : { campaign_id: flags.campaign };
|
|
39
42
|
const body = {
|
|
40
|
-
negative_keywords: flags.text.map(
|
|
43
|
+
negative_keywords: flags.text.map(text => ({
|
|
41
44
|
...parent,
|
|
42
45
|
match_type: flags['match-type'],
|
|
43
46
|
status: flags.status,
|
|
@@ -53,7 +56,7 @@ export default class AsaNegativeKeywordsAdd extends Command {
|
|
|
53
56
|
? `Add ${flags.text.length} negative keyword(s) to every ad group of the campaign`
|
|
54
57
|
: `Add ${flags.text.length} negative keyword(s) in ${scope} scope`,
|
|
55
58
|
}, flags.yes);
|
|
56
|
-
const client = await createAsaClient(this
|
|
59
|
+
const client = await createAsaClient(this);
|
|
57
60
|
const { replayed, result } = await asaWrite(client, 'post', '/negative-keywords', {
|
|
58
61
|
body,
|
|
59
62
|
idempotencyKey: flags['idempotency-key'],
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Command } from '@oclif/core';
|
|
2
2
|
import type { AsaNegativeKeywordDTO } from '../../../lib/asa-schemas.js';
|
|
3
|
-
import {
|
|
3
|
+
import type { PaginatedResponse } from '../../../lib/flags.js';
|
|
4
4
|
export default class AsaNegativeKeywordsList extends Command {
|
|
5
5
|
static description: string;
|
|
6
6
|
static enableJsonFlag: boolean;
|
|
@@ -17,7 +17,7 @@ export default class AsaNegativeKeywordsList extends Command {
|
|
|
17
17
|
};
|
|
18
18
|
async run() {
|
|
19
19
|
const { flags } = await this.parse(AsaNegativeKeywordsList);
|
|
20
|
-
const client = await createAsaClient(this
|
|
20
|
+
const client = await createAsaClient(this);
|
|
21
21
|
const result = await client.get('/negative-keywords', {
|
|
22
22
|
...paginationParams(flags),
|
|
23
23
|
...scopeParams(flags),
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Command } from '@oclif/core';
|
|
2
2
|
import type { AsaCampaignGroupDTO } from '../../../lib/asa-schemas.js';
|
|
3
|
-
import {
|
|
3
|
+
import type { PaginatedResponse } from '../../../lib/flags.js';
|
|
4
4
|
export default class AsaOrgsList extends Command {
|
|
5
5
|
static description: string;
|
|
6
6
|
static enableJsonFlag: boolean;
|
|
@@ -10,7 +10,7 @@ export default class AsaOrgsList extends Command {
|
|
|
10
10
|
static flags = { ...asaPaginationFlags };
|
|
11
11
|
async run() {
|
|
12
12
|
const { flags } = await this.parse(AsaOrgsList);
|
|
13
|
-
const client = await createAsaClient(this
|
|
13
|
+
const client = await createAsaClient(this);
|
|
14
14
|
const result = await client.get('/campaign-groups', paginationParams(flags));
|
|
15
15
|
printList(result.data, this.log.bind(this), result.meta.pagination);
|
|
16
16
|
return result;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Command } from '@oclif/core';
|
|
2
2
|
import type { AsaProductPageDTO } from '../../../lib/asa-schemas.js';
|
|
3
|
-
import {
|
|
3
|
+
import type { PaginatedResponse } from '../../../lib/flags.js';
|
|
4
4
|
export default class AsaProductPagesList extends Command {
|
|
5
5
|
static description: string;
|
|
6
6
|
static enableJsonFlag: boolean;
|
|
@@ -13,7 +13,7 @@ export default class AsaProductPagesList extends Command {
|
|
|
13
13
|
static flags = { ...asaPaginationFlags, ...assetScopeFlags };
|
|
14
14
|
async run() {
|
|
15
15
|
const { flags } = await this.parse(AsaProductPagesList);
|
|
16
|
-
const client = await createAsaClient(this
|
|
16
|
+
const client = await createAsaClient(this);
|
|
17
17
|
const result = await client.get('/product-pages', {
|
|
18
18
|
...paginationParams(flags),
|
|
19
19
|
...scopeParams(flags),
|
|
@@ -16,7 +16,7 @@ export default class AsaProductPagesSync extends Command {
|
|
|
16
16
|
const { flags } = await this.parse(AsaProductPagesSync);
|
|
17
17
|
const body = { ...(flags['adam-id'] === undefined ? {} : { adam_id: flags['adam-id'] }) };
|
|
18
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
|
|
19
|
+
const client = await createAsaClient(this);
|
|
20
20
|
const { result } = await asaWrite(client, 'post', '/product-pages/sync', {
|
|
21
21
|
body,
|
|
22
22
|
idempotencyKey: flags['idempotency-key'],
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Command } from '@oclif/core';
|
|
2
2
|
import type { AsaSearchTermDTO } from '../../../lib/asa-schemas.js';
|
|
3
|
-
import {
|
|
3
|
+
import type { PaginatedResponse } from '../../../lib/flags.js';
|
|
4
4
|
export default class AsaSearchTermsList extends Command {
|
|
5
5
|
static description: string;
|
|
6
6
|
static enableJsonFlag: boolean;
|
|
@@ -13,7 +13,7 @@ export default class AsaSearchTermsList extends Command {
|
|
|
13
13
|
static flags = { ...asaPaginationFlags, ...periodFlags, ...adGroupScopeFlags };
|
|
14
14
|
async run() {
|
|
15
15
|
const { flags } = await this.parse(AsaSearchTermsList);
|
|
16
|
-
const client = await createAsaClient(this
|
|
16
|
+
const client = await createAsaClient(this);
|
|
17
17
|
const result = await client.get('/search-terms', {
|
|
18
18
|
...paginationParams(flags),
|
|
19
19
|
...periodParams(flags),
|
|
@@ -2,12 +2,17 @@ import { Command } from '@oclif/core';
|
|
|
2
2
|
import { createAsaClient } from '../../lib/asa-client.js';
|
|
3
3
|
import { printResponse } from '../../lib/output.js';
|
|
4
4
|
export default class AsaWhoami extends Command {
|
|
5
|
-
static description =
|
|
5
|
+
static description = `Show which company the token unlocks, whether Apple Ads is connected, and the request budgets it gets
|
|
6
|
+
|
|
7
|
+
Limits are raised per company, so read them here rather than assuming the defaults: metrics_limit_per_minute
|
|
8
|
+
and metrics_burst_limit govern asa metrics, keywords_read_limit_per_minute the keyword lists,
|
|
9
|
+
metrics_inflight_limit how many analytics calls may overlap, and max_breakdown_rows_per_page the size a
|
|
10
|
+
grouped page may project to before it is refused.`;
|
|
6
11
|
static enableJsonFlag = true;
|
|
7
12
|
static examples = ['<%= config.bin %> asa whoami'];
|
|
8
13
|
async run() {
|
|
9
14
|
await this.parse(AsaWhoami);
|
|
10
|
-
const client = await createAsaClient(this
|
|
15
|
+
const client = await createAsaClient(this);
|
|
11
16
|
const result = await client.get('/me');
|
|
12
17
|
printResponse(result, this.log.bind(this));
|
|
13
18
|
if (result.apple_credentials_status !== 'active') {
|
|
@@ -14,6 +14,7 @@ export default class AuthLogin extends Command {
|
|
|
14
14
|
static description = 'Authenticate with Adapty via browser';
|
|
15
15
|
static examples = ['<%= config.bin %> auth login'];
|
|
16
16
|
async run() {
|
|
17
|
+
await this.parse(AuthLogin);
|
|
17
18
|
const config = await readConfig(this.config.configDir);
|
|
18
19
|
if (config.access_token && config.user) {
|
|
19
20
|
this.log(`Already authenticated as ${config.user.email}. Re-authenticating...`);
|
|
@@ -31,7 +32,7 @@ export default class AuthLogin extends Command {
|
|
|
31
32
|
const verificationUrl = this.verificationUrl(device.verification_uri_complete);
|
|
32
33
|
this.log(`\nYour code: ${device.user_code}\n`);
|
|
33
34
|
this.log(`If browser doesn't open, visit: ${verificationUrl}\n`);
|
|
34
|
-
if (process.stdin.isTTY
|
|
35
|
+
if (process.stdin.isTTY) {
|
|
35
36
|
try {
|
|
36
37
|
await open(verificationUrl);
|
|
37
38
|
}
|
|
@@ -46,6 +47,7 @@ export default class AuthLogin extends Command {
|
|
|
46
47
|
// Handle Ctrl+C
|
|
47
48
|
const onSignal = () => {
|
|
48
49
|
this.log('\nLogin cancelled.');
|
|
50
|
+
// eslint-disable-next-line n/no-process-exit -- FIXME if you see this
|
|
49
51
|
process.exit(0);
|
|
50
52
|
};
|
|
51
53
|
process.on('SIGINT', onSignal);
|
|
@@ -6,8 +6,8 @@ export default class FlowsConfigGet extends Command {
|
|
|
6
6
|
static args = {
|
|
7
7
|
flow_id: Args.string({ description: 'Flow ID (UUID)', required: true }),
|
|
8
8
|
};
|
|
9
|
-
static description = 'Read the flow builder config (404 until the config has been written at least once). '
|
|
10
|
-
'On a failed publish, `publication_status`, `transform_error` and `publication_error` show why.';
|
|
9
|
+
static description = 'Read the flow builder config (404 until the config has been written at least once). '
|
|
10
|
+
+ 'On a failed publish, `publication_status`, `transform_error` and `publication_error` show why.';
|
|
11
11
|
static enableJsonFlag = true;
|
|
12
12
|
static examples = ['<%= config.bin %> flows config get --app UUID 550e8400-e29b-41d4-a716-446655440000'];
|
|
13
13
|
static flags = {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Command } from '@oclif/core';
|
|
2
|
-
export
|
|
2
|
+
export type PreviewResult = {
|
|
3
3
|
render_url: string;
|
|
4
|
-
}
|
|
4
|
+
};
|
|
5
5
|
export default class FlowsConfigPreview extends Command {
|
|
6
6
|
static args: {
|
|
7
7
|
config_file: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { Args, Command, Flags } from '@oclif/core';
|
|
2
1
|
import { readFile } from 'node:fs/promises';
|
|
3
2
|
import { resolve } from 'node:path';
|
|
3
|
+
import { Args, Command, Flags } from '@oclif/core';
|
|
4
4
|
import open from 'open';
|
|
5
5
|
import { APP_URL_ENV_VAR } from '../../../lib/app-url.js';
|
|
6
6
|
import { buildRenderUrl, DEFAULT_DEVICE_ID, DEFAULT_ORIENTATION, normalizePreviewConfig, ORIENTATIONS, } from '../../../lib/preview.js';
|
|
@@ -26,7 +26,7 @@ export default class FlowsConfigPreview extends Command {
|
|
|
26
26
|
description: 'Device orientation to render in',
|
|
27
27
|
options: [...ORIENTATIONS],
|
|
28
28
|
}),
|
|
29
|
-
screen: Flags.string({ description:
|
|
29
|
+
screen: Flags.string({ description: 'Screen ID to render (default: the flow\'s first screen)' }),
|
|
30
30
|
};
|
|
31
31
|
async run() {
|
|
32
32
|
const { args, flags } = await this.parse(FlowsConfigPreview);
|
|
@@ -47,17 +47,19 @@ export default class FlowsConfigPreview extends Command {
|
|
|
47
47
|
}
|
|
48
48
|
let renderUrl;
|
|
49
49
|
try {
|
|
50
|
+
// eslint-disable-next-line @stylistic/max-len -- FIXME if you see this
|
|
50
51
|
renderUrl = buildRenderUrl({ device: flags.device, orientation: flags.orientation, screen: flags.screen }, payload);
|
|
51
52
|
}
|
|
52
53
|
catch (error) {
|
|
53
54
|
this.error(describeError(error), { exit: 2 });
|
|
54
55
|
}
|
|
55
56
|
const result = { render_url: renderUrl };
|
|
56
|
-
if (this.jsonEnabled())
|
|
57
|
+
if (this.jsonEnabled()) {
|
|
57
58
|
return result;
|
|
59
|
+
}
|
|
58
60
|
// Piped output stays a bare URL so it composes — the whole config rides in the fragment, which
|
|
59
61
|
// is why a TTY gets the browser opened instead of a screenful of base64.
|
|
60
|
-
if (process.stdout.isTTY
|
|
62
|
+
if (!process.stdout.isTTY) {
|
|
61
63
|
this.log(renderUrl);
|
|
62
64
|
return result;
|
|
63
65
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Args, Command, Flags } from '@oclif/core';
|
|
2
1
|
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { Args, Command, Flags } from '@oclif/core';
|
|
3
3
|
import { createAuthenticatedClient } from '../../../lib/client-from-config.js';
|
|
4
4
|
import { appFlag, isValidUuid } from '../../../lib/flags.js';
|
|
5
5
|
import { printResponse } from '../../../lib/output.js';
|
|
@@ -16,7 +16,7 @@ export default class FlowsConfigUpdate extends Command {
|
|
|
16
16
|
];
|
|
17
17
|
static flags = {
|
|
18
18
|
...appFlag,
|
|
19
|
-
config: Flags.string({
|
|
19
|
+
'config': Flags.string({
|
|
20
20
|
description: 'Builder config as a JSON string',
|
|
21
21
|
exactlyOne: ['config', 'config-file'],
|
|
22
22
|
}),
|
|
@@ -50,6 +50,7 @@ export default class FlowsConfigUpdate extends Command {
|
|
|
50
50
|
printResponse(result, this.log.bind(this));
|
|
51
51
|
return result;
|
|
52
52
|
}
|
|
53
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-parameters -- FIXME if you see this
|
|
53
54
|
parseJson(raw, label) {
|
|
54
55
|
try {
|
|
55
56
|
return JSON.parse(raw);
|
|
@@ -77,8 +78,9 @@ export default class FlowsConfigUpdate extends Command {
|
|
|
77
78
|
}
|
|
78
79
|
async readStdin() {
|
|
79
80
|
const chunks = [];
|
|
80
|
-
for await (const chunk of process.stdin)
|
|
81
|
+
for await (const chunk of process.stdin) {
|
|
81
82
|
chunks.push(chunk);
|
|
83
|
+
}
|
|
82
84
|
return Buffer.concat(chunks).toString('utf8');
|
|
83
85
|
}
|
|
84
86
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Args, Command, Flags } from '@oclif/core';
|
|
2
1
|
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { Args, Command, Flags } from '@oclif/core';
|
|
3
3
|
import { createAuthenticatedClient } from '../../../lib/client-from-config.js';
|
|
4
4
|
import { appFlag, isValidUuid } from '../../../lib/flags.js';
|
|
5
5
|
import { printResponse } from '../../../lib/output.js';
|
|
@@ -16,7 +16,7 @@ export default class FlowsConfigValidate extends Command {
|
|
|
16
16
|
];
|
|
17
17
|
static flags = {
|
|
18
18
|
...appFlag,
|
|
19
|
-
config: Flags.string({
|
|
19
|
+
'config': Flags.string({
|
|
20
20
|
description: 'Builder config as a JSON string',
|
|
21
21
|
exactlyOne: ['config', 'config-file'],
|
|
22
22
|
}),
|
|
@@ -43,6 +43,7 @@ export default class FlowsConfigValidate extends Command {
|
|
|
43
43
|
}
|
|
44
44
|
return result;
|
|
45
45
|
}
|
|
46
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-parameters -- FIXME if you see this
|
|
46
47
|
parseJson(raw, label) {
|
|
47
48
|
try {
|
|
48
49
|
return JSON.parse(raw);
|
|
@@ -70,8 +71,9 @@ export default class FlowsConfigValidate extends Command {
|
|
|
70
71
|
}
|
|
71
72
|
async readStdin() {
|
|
72
73
|
const chunks = [];
|
|
73
|
-
for await (const chunk of process.stdin)
|
|
74
|
+
for await (const chunk of process.stdin) {
|
|
74
75
|
chunks.push(chunk);
|
|
76
|
+
}
|
|
75
77
|
return Buffer.concat(chunks).toString('utf8');
|
|
76
78
|
}
|
|
77
79
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Command } from '@oclif/core';
|
|
2
2
|
import type { FlowDTO } from '../../lib/api-schemas.js';
|
|
3
|
-
import {
|
|
3
|
+
import type { PaginatedResponse } from '../../lib/flags.js';
|
|
4
4
|
export default class FlowsList extends Command {
|
|
5
5
|
static description: string;
|
|
6
6
|
static enableJsonFlag: boolean;
|