eas-cli 16.31.0 → 18.0.1

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 (59) hide show
  1. package/README.md +116 -91
  2. package/build/branch/queries.js +6 -1
  3. package/build/build/createContext.js +5 -3
  4. package/build/build/queries.js +13 -1
  5. package/build/build/runBuildAndSubmit.js +2 -0
  6. package/build/build/utils/devClient.d.ts +1 -0
  7. package/build/build/utils/devClient.js +3 -2
  8. package/build/build/utils/repository.js +3 -3
  9. package/build/build/utils/resourceClass.d.ts +1 -1
  10. package/build/build/utils/resourceClass.js +2 -2
  11. package/build/channel/queries.js +10 -1
  12. package/build/commandUtils/context/contextUtils/createGraphqlClient.d.ts +1 -5
  13. package/build/commandUtils/new/templates/AGENTS.md +0 -1
  14. package/build/commandUtils/usageUtils.d.ts +58 -0
  15. package/build/commandUtils/usageUtils.js +204 -0
  16. package/build/commands/account/login.d.ts +1 -0
  17. package/build/commands/account/login.js +7 -2
  18. package/build/commands/account/usage.d.ts +16 -0
  19. package/build/commands/account/usage.js +326 -0
  20. package/build/commands/env/create.js +5 -2
  21. package/build/commands/env/list.js +1 -5
  22. package/build/commands/update/index.js +2 -2
  23. package/build/commands/upload.js +3 -3
  24. package/build/credentials/ios/api/graphql/mutations/AppleDistributionCertificateMutation.js +1 -3
  25. package/build/devices/queries.js +23 -2
  26. package/build/graphql/generated.d.ts +1063 -39
  27. package/build/graphql/generated.js +78 -11
  28. package/build/graphql/mutations/EnvironmentVariableMutation.js +1 -4
  29. package/build/graphql/mutations/PublishMutation.js +1 -4
  30. package/build/graphql/mutations/UserPreferencesMutation.js +1 -3
  31. package/build/graphql/mutations/WorkflowRevisionMutation.js +2 -10
  32. package/build/graphql/queries/AccountQuery.d.ts +15 -0
  33. package/build/graphql/queries/AccountQuery.js +197 -0
  34. package/build/graphql/queries/AppVersionQuery.js +1 -5
  35. package/build/graphql/types/Account.d.ts +11 -0
  36. package/build/graphql/types/Account.js +142 -1
  37. package/build/log.js +1 -1
  38. package/build/ora.js +1 -1
  39. package/build/project/discourageExpoGoForProdAsync.d.ts +4 -0
  40. package/build/project/discourageExpoGoForProdAsync.js +46 -0
  41. package/build/rollout/actions/ManageRollout.js +5 -1
  42. package/build/rollout/actions/SelectRuntime.js +10 -1
  43. package/build/update/queries.js +28 -2
  44. package/build/user/SessionManager.d.ts +3 -2
  45. package/build/user/SessionManager.js +6 -6
  46. package/build/user/expoBrowserAuthFlowLauncher.d.ts +3 -0
  47. package/build/user/{expoSsoLauncher.js → expoBrowserAuthFlowLauncher.js} +21 -47
  48. package/build/user/fetchSessionSecretAndUserFromBrowserAuthFlow.d.ts +7 -0
  49. package/build/user/fetchSessionSecretAndUserFromBrowserAuthFlow.js +14 -0
  50. package/build/utils/prompts.js +3 -3
  51. package/build/utils/usage/checkForOverages.js +6 -4
  52. package/build/worker/mutations.js +2 -8
  53. package/oclif.manifest.json +40 -1
  54. package/package.json +60 -63
  55. package/build/graphql/queries/AccountUsageQuery.d.ts +0 -5
  56. package/build/graphql/queries/AccountUsageQuery.js +0 -40
  57. package/build/user/expoSsoLauncher.d.ts +0 -3
  58. package/build/user/fetchSessionSecretAndSsoUser.d.ts +0 -5
  59. package/build/user/fetchSessionSecretAndSsoUser.js +0 -17
@@ -0,0 +1,326 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
4
+ const chalk_1 = tslib_1.__importDefault(require("chalk"));
5
+ const EasCommand_1 = tslib_1.__importDefault(require("../../commandUtils/EasCommand"));
6
+ const flags_1 = require("../../commandUtils/flags");
7
+ const usageUtils_1 = require("../../commandUtils/usageUtils");
8
+ const generated_1 = require("../../graphql/generated");
9
+ const AccountQuery_1 = require("../../graphql/queries/AccountQuery");
10
+ const log_1 = tslib_1.__importStar(require("../../log"));
11
+ const ora_1 = require("../../ora");
12
+ const prompts_1 = require("../../prompts");
13
+ const json_1 = require("../../utils/json");
14
+ const checkForOverages_1 = require("../../utils/usage/checkForOverages");
15
+ const MiB = 1024 * 1024;
16
+ const GiB = 1024 * MiB;
17
+ const TiB = 1024 * GiB;
18
+ function formatBytes(bytes) {
19
+ if (bytes >= TiB) {
20
+ return { value: bytes / TiB, unit: 'TiB' };
21
+ }
22
+ else if (bytes >= GiB) {
23
+ return { value: bytes / GiB, unit: 'GiB' };
24
+ }
25
+ else {
26
+ return { value: bytes / MiB, unit: 'MiB' };
27
+ }
28
+ }
29
+ function formatBytesDisplay(bytes) {
30
+ const { value, unit } = formatBytes(bytes);
31
+ return `${(0, usageUtils_1.formatNumber)(value, value < 10 ? 2 : value < 100 ? 1 : 0)} ${unit}`;
32
+ }
33
+ function displayBandwidthMetric(metric, indent = ' ') {
34
+ const progressBar = (0, checkForOverages_1.createProgressBar)(metric.percentUsed, 20);
35
+ const percentStr = `${metric.percentUsed}%`;
36
+ const planUsageStr = `${formatBytesDisplay(metric.planValue)}/${formatBytesDisplay(metric.limit)}`;
37
+ let color = chalk_1.default.green;
38
+ if (metric.percentUsed >= 100) {
39
+ color = chalk_1.default.red;
40
+ }
41
+ else if (metric.percentUsed >= 85) {
42
+ color = chalk_1.default.yellow;
43
+ }
44
+ log_1.default.log(`${indent}${metric.name} (plan): ${color(planUsageStr)}`);
45
+ log_1.default.log(`${indent}${progressBar} ${color(percentStr)}`);
46
+ if (metric.overageValue > 0) {
47
+ log_1.default.log(`${indent}${metric.name} (overage): ${chalk_1.default.red(formatBytesDisplay(metric.overageValue))} (${(0, usageUtils_1.formatCurrency)(metric.overageCost)})`);
48
+ }
49
+ }
50
+ function displayMetric(metric, indent = ' ') {
51
+ const progressBar = (0, checkForOverages_1.createProgressBar)(metric.percentUsed, 20);
52
+ const percentStr = `${metric.percentUsed}%`;
53
+ const planUsageStr = `${(0, usageUtils_1.formatNumber)(metric.planValue)}/${(0, usageUtils_1.formatNumber)(metric.limit)} ${metric.unit ?? ''}`;
54
+ let color = chalk_1.default.green;
55
+ if (metric.percentUsed >= 100) {
56
+ color = chalk_1.default.red;
57
+ }
58
+ else if (metric.percentUsed >= 85) {
59
+ color = chalk_1.default.yellow;
60
+ }
61
+ log_1.default.log(`${indent}${metric.name} (plan): ${color(planUsageStr)}`);
62
+ log_1.default.log(`${indent}${progressBar} ${color(percentStr)}`);
63
+ if (metric.overageValue > 0) {
64
+ log_1.default.log(`${indent}${metric.name} (overage): ${chalk_1.default.red(`${(0, usageUtils_1.formatNumber)(metric.overageValue)} ${metric.unit ?? ''}`)} (${(0, usageUtils_1.formatCurrency)(metric.overageCost)})`);
65
+ }
66
+ }
67
+ function billingUrl(accountName) {
68
+ return `https://expo.dev/accounts/${accountName}/settings/billing`;
69
+ }
70
+ function displayUsage(data, usageData) {
71
+ const subscription = usageData.subscription;
72
+ log_1.default.newLine();
73
+ log_1.default.log(chalk_1.default.bold(`Account: ${data.accountName}`));
74
+ log_1.default.log(`Plan: ${data.subscriptionPlan}`);
75
+ if (subscription?.concurrencies) {
76
+ log_1.default.log(`Concurrencies: ${subscription.concurrencies.total} total (iOS: ${subscription.concurrencies.ios}, Android: ${subscription.concurrencies.android})`);
77
+ }
78
+ log_1.default.log(`Billing Period: ${(0, usageUtils_1.formatDate)(data.billingPeriod.start)} - ${(0, usageUtils_1.formatDate)(data.billingPeriod.end)}`);
79
+ const periodInfo = (0, usageUtils_1.calculateBillingPeriodInfo)(data);
80
+ log_1.default.log(`Days: ${periodInfo.daysElapsed} elapsed / ${periodInfo.totalDays} total (${periodInfo.daysRemaining} remaining)`);
81
+ log_1.default.newLine();
82
+ log_1.default.log(chalk_1.default.bold.underline('EAS Build'));
83
+ displayMetric(data.builds.total);
84
+ if (data.builds.ios) {
85
+ displayMetric(data.builds.ios, ' ');
86
+ }
87
+ if (data.builds.android) {
88
+ displayMetric(data.builds.android, ' ');
89
+ }
90
+ // Show build counts by platform and worker size
91
+ if (data.builds.countsByPlatformAndSize.length > 0) {
92
+ log_1.default.log(' Breakdown by platform/worker:');
93
+ for (const item of data.builds.countsByPlatformAndSize) {
94
+ const platformName = item.platform === 'ios' ? 'iOS' : 'Android';
95
+ log_1.default.log(` ${platformName} ${item.resourceClass}: ${(0, usageUtils_1.formatNumber)(item.count)} builds`);
96
+ }
97
+ }
98
+ log_1.default.newLine();
99
+ log_1.default.log(chalk_1.default.bold.underline('EAS Update'));
100
+ displayMetric(data.updates.mau);
101
+ displayBandwidthMetric(data.updates.bandwidth);
102
+ log_1.default.newLine();
103
+ log_1.default.log(chalk_1.default.bold.underline('Billing'));
104
+ const upcomingInvoice = subscription?.upcomingInvoice;
105
+ // Show subscription addons
106
+ if (subscription?.addons && subscription.addons.length > 0) {
107
+ log_1.default.log(' Addons:');
108
+ for (const addon of subscription.addons) {
109
+ log_1.default.log(` ${addon.name}: ${addon.quantity}`);
110
+ }
111
+ }
112
+ // Show upcoming invoice line items
113
+ if (upcomingInvoice?.lineItems && upcomingInvoice.lineItems.length > 0) {
114
+ log_1.default.newLine();
115
+ log_1.default.log(' Upcoming invoice:');
116
+ for (const lineItem of upcomingInvoice.lineItems) {
117
+ const amount = lineItem.amount;
118
+ const amountStr = amount < 0 ? chalk_1.default.green((0, usageUtils_1.formatCurrency)(amount)) : (0, usageUtils_1.formatCurrency)(amount);
119
+ log_1.default.log(` ${lineItem.description}: ${amountStr}`);
120
+ }
121
+ log_1.default.newLine();
122
+ log_1.default.log(` Invoice total: ${chalk_1.default.bold((0, usageUtils_1.formatCurrency)(upcomingInvoice.total))}`);
123
+ }
124
+ else {
125
+ // Fallback to the calculated estimate if no upcoming invoice
126
+ if (data.recurringCents !== null && data.recurringCents > 0) {
127
+ log_1.default.log(` Base subscription: ${(0, usageUtils_1.formatCurrency)(data.recurringCents)}`);
128
+ }
129
+ if (data.totalOverageCostCents > 0) {
130
+ log_1.default.log(` Current overages: ${chalk_1.default.yellow((0, usageUtils_1.formatCurrency)(data.totalOverageCostCents))}`);
131
+ if (data.builds.overageCostCents > 0) {
132
+ log_1.default.log(` Build overages: ${(0, usageUtils_1.formatCurrency)(data.builds.overageCostCents)}`);
133
+ // Show breakdown by worker size
134
+ for (const overage of data.builds.overagesByWorkerSize) {
135
+ const platformName = overage.platform === generated_1.AppPlatform.Ios ? 'iOS' : 'Android';
136
+ const sizeName = overage.resourceClass === generated_1.EasBuildBillingResourceClass.Large ? 'large' : 'medium';
137
+ log_1.default.log(` ${platformName} ${sizeName}: ${(0, usageUtils_1.formatNumber)(overage.count)} builds (${(0, usageUtils_1.formatCurrency)(overage.costCents)})`);
138
+ }
139
+ }
140
+ if (data.updates.overageCostCents > 0) {
141
+ log_1.default.log(` Update overages: ${(0, usageUtils_1.formatCurrency)(data.updates.overageCostCents)}`);
142
+ }
143
+ }
144
+ log_1.default.log(` Estimated bill: ${chalk_1.default.bold((0, usageUtils_1.formatCurrency)(data.estimatedBillCents))}`);
145
+ }
146
+ log_1.default.newLine();
147
+ log_1.default.log(chalk_1.default.dim(`View detailed billing: ${(0, log_1.link)(billingUrl(data.accountName))}`));
148
+ }
149
+ class AccountUsage extends EasCommand_1.default {
150
+ static description = 'view account usage and billing for the current cycle';
151
+ static args = [
152
+ {
153
+ name: 'ACCOUNT_NAME',
154
+ description: 'Account name to view usage for. If not provided, the account will be selected interactively (or defaults to the only account if there is just one)',
155
+ },
156
+ ];
157
+ static flags = {
158
+ ...flags_1.EasJsonOnlyFlag,
159
+ ...flags_1.EASNonInteractiveFlag,
160
+ };
161
+ static contextDefinition = {
162
+ ...this.ContextOptions.LoggedIn,
163
+ };
164
+ async runAsync() {
165
+ const { args: { ACCOUNT_NAME: accountName }, flags: { json: jsonFlag, 'non-interactive': nonInteractive }, } = await this.parse(AccountUsage);
166
+ // Enable JSON output if either --json or --non-interactive is passed
167
+ const json = jsonFlag || nonInteractive;
168
+ if (json) {
169
+ (0, json_1.enableJsonOutput)();
170
+ }
171
+ const { loggedIn: { graphqlClient, actor }, } = await this.getContextAsync(AccountUsage, {
172
+ nonInteractive,
173
+ });
174
+ // Find the target account
175
+ const defaultAccount = actor.accounts[0];
176
+ let targetAccount;
177
+ const availableAccounts = actor.accounts.map(a => a.name).join(', ');
178
+ if (accountName) {
179
+ // First check if it's one of the user's accounts
180
+ const found = actor.accounts.find(a => a.name === accountName);
181
+ if (found) {
182
+ targetAccount = found;
183
+ }
184
+ else {
185
+ // Try to look up the account by name (user may have access via organization)
186
+ try {
187
+ const account = await AccountQuery_1.AccountQuery.getByNameAsync(graphqlClient, accountName);
188
+ if (!account) {
189
+ throw new Error(`Account "${accountName}" not found. Available accounts: ${availableAccounts}`);
190
+ }
191
+ targetAccount = account;
192
+ }
193
+ catch {
194
+ throw new Error(`Account "${accountName}" not found or you don't have access. Available accounts: ${availableAccounts}`);
195
+ }
196
+ }
197
+ }
198
+ else if (nonInteractive) {
199
+ throw new Error('ACCOUNT_NAME argument must be provided when running in `--non-interactive` mode.');
200
+ }
201
+ else if (actor.accounts.length === 1) {
202
+ // Only one account, use it directly
203
+ targetAccount = defaultAccount;
204
+ }
205
+ else {
206
+ // Prompt user to select an account
207
+ targetAccount = await (0, prompts_1.selectAsync)('Select account to view usage for:', actor.accounts.map(account => ({
208
+ title: account.name,
209
+ value: account,
210
+ })), { initial: defaultAccount });
211
+ }
212
+ const spinner = (0, ora_1.ora)(`Fetching usage data for account ${targetAccount.name}`).start();
213
+ try {
214
+ const currentDate = new Date();
215
+ const { start, end } = await AccountQuery_1.AccountQuery.getBillingPeriodAsync(graphqlClient, targetAccount.id, currentDate);
216
+ const usageData = await AccountQuery_1.AccountQuery.getFullUsageAsync(graphqlClient, targetAccount.id, currentDate, start, end);
217
+ log_1.default.debug(JSON.stringify(usageData, null, 2));
218
+ spinner.succeed(`Usage data loaded for ${targetAccount.name}`);
219
+ const displayData = (0, usageUtils_1.extractUsageData)(usageData);
220
+ const periodInfo = (0, usageUtils_1.calculateBillingPeriodInfo)(displayData);
221
+ if (json) {
222
+ const subscription = usageData.subscription;
223
+ (0, json_1.printJsonOnlyOutput)({
224
+ account: {
225
+ name: displayData.accountName,
226
+ plan: displayData.subscriptionPlan,
227
+ concurrencies: subscription?.concurrencies ?? null,
228
+ billingPeriod: {
229
+ start: displayData.billingPeriod.start,
230
+ end: displayData.billingPeriod.end,
231
+ daysElapsed: periodInfo.daysElapsed,
232
+ daysRemaining: periodInfo.daysRemaining,
233
+ totalDays: periodInfo.totalDays,
234
+ },
235
+ },
236
+ builds: {
237
+ plan: {
238
+ used: displayData.builds.total.planValue,
239
+ limit: displayData.builds.total.limit,
240
+ percentUsed: displayData.builds.total.percentUsed,
241
+ },
242
+ overage: {
243
+ count: displayData.builds.total.overageValue,
244
+ costCents: displayData.builds.total.overageCost,
245
+ byWorkerSize: displayData.builds.overagesByWorkerSize.map(o => ({
246
+ platform: o.platform.toLowerCase(),
247
+ resourceClass: o.resourceClass.toLowerCase(),
248
+ count: o.count,
249
+ costCents: o.costCents,
250
+ })),
251
+ },
252
+ byPlatformAndSize: displayData.builds.countsByPlatformAndSize.map(item => ({
253
+ platform: item.platform,
254
+ resourceClass: item.resourceClass,
255
+ count: item.count,
256
+ })),
257
+ ios: displayData.builds.ios
258
+ ? {
259
+ plan: {
260
+ used: displayData.builds.ios.planValue,
261
+ limit: displayData.builds.ios.limit,
262
+ percentUsed: displayData.builds.ios.percentUsed,
263
+ },
264
+ }
265
+ : null,
266
+ android: displayData.builds.android
267
+ ? {
268
+ plan: {
269
+ used: displayData.builds.android.planValue,
270
+ limit: displayData.builds.android.limit,
271
+ percentUsed: displayData.builds.android.percentUsed,
272
+ },
273
+ }
274
+ : null,
275
+ },
276
+ updates: {
277
+ uniqueUpdaters: {
278
+ plan: {
279
+ used: displayData.updates.mau.planValue,
280
+ limit: displayData.updates.mau.limit,
281
+ percentUsed: displayData.updates.mau.percentUsed,
282
+ },
283
+ overage: {
284
+ count: displayData.updates.mau.overageValue,
285
+ costCents: displayData.updates.mau.overageCost,
286
+ },
287
+ },
288
+ bandwidth: {
289
+ plan: {
290
+ usedBytes: displayData.updates.bandwidth.planValue,
291
+ usedFormatted: formatBytesDisplay(displayData.updates.bandwidth.planValue),
292
+ limitBytes: displayData.updates.bandwidth.limit,
293
+ limitFormatted: formatBytesDisplay(displayData.updates.bandwidth.limit),
294
+ percentUsed: displayData.updates.bandwidth.percentUsed,
295
+ },
296
+ overage: {
297
+ usedBytes: displayData.updates.bandwidth.overageValue,
298
+ usedFormatted: formatBytesDisplay(displayData.updates.bandwidth.overageValue),
299
+ costCents: displayData.updates.bandwidth.overageCost,
300
+ },
301
+ },
302
+ overageCostCents: displayData.updates.overageCostCents,
303
+ },
304
+ billing: {
305
+ addons: subscription?.addons ?? [],
306
+ upcomingInvoice: subscription?.upcomingInvoice ?? null,
307
+ estimated: {
308
+ recurringCents: displayData.recurringCents,
309
+ overageCents: displayData.totalOverageCostCents,
310
+ totalCents: displayData.estimatedBillCents,
311
+ },
312
+ },
313
+ billingUrl: billingUrl(displayData.accountName),
314
+ });
315
+ }
316
+ else {
317
+ displayUsage(displayData, usageData);
318
+ }
319
+ }
320
+ catch (error) {
321
+ spinner.fail('Failed to fetch usage data');
322
+ throw error;
323
+ }
324
+ }
325
+ }
326
+ exports.default = AccountUsage;
@@ -54,7 +54,10 @@ class EnvCreate extends EasCommand_1.default {
54
54
  const { projectId, loggedIn: { graphqlClient }, } = await this.getContextAsync(EnvCreate, {
55
55
  nonInteractive: validatedFlags['non-interactive'],
56
56
  });
57
- const { name, value, scope, 'non-interactive': nonInteractive, environment: environments, visibility, force, type, fileName, } = await this.promptForMissingFlagsAsync(validatedFlags, args, { graphqlClient, projectId });
57
+ const { name, value, scope, 'non-interactive': nonInteractive, environment: environments, visibility, force, type, fileName, } = await this.promptForMissingFlagsAsync(validatedFlags, args, {
58
+ graphqlClient,
59
+ projectId,
60
+ });
58
61
  const [projectDisplayName, ownerAccount] = await Promise.all([
59
62
  (0, projectUtils_1.getDisplayNameForProjectIdAsync)(graphqlClient, projectId),
60
63
  (0, projectUtils_1.getOwnerAccountForProjectIdAsync)(graphqlClient, projectId),
@@ -186,7 +189,7 @@ class EnvCreate extends EasCommand_1.default {
186
189
  fileName = path_1.default.basename(environmentFilePath);
187
190
  }
188
191
  value = environmentFilePath ? await fs_extra_1.default.readFile(environmentFilePath, 'base64') : value;
189
- let newEnvironments = environments ? environments : environment ? [environment] : undefined;
192
+ let newEnvironments = environments ?? (environment ? [environment] : undefined);
190
193
  if (!newEnvironments) {
191
194
  newEnvironments = await (0, prompts_2.promptVariableEnvironmentAsync)({
192
195
  nonInteractive,
@@ -110,11 +110,7 @@ class EnvList extends EasCommand_1.default {
110
110
  });
111
111
  }
112
112
  sanitizeInputs(flags, { environment }) {
113
- const environments = flags.environment
114
- ? flags.environment
115
- : environment
116
- ? [environment]
117
- : undefined;
113
+ const environments = flags.environment ?? (environment ? [environment] : undefined);
118
114
  return {
119
115
  ...flags,
120
116
  'non-interactive': flags['non-interactive'] ?? false,
@@ -313,11 +313,11 @@ class UpdatePublish extends EasCommand_1.default {
313
313
  return {
314
314
  ...info,
315
315
  expoUpdatesRuntimeFingerprintSource: info.expoUpdatesRuntimeFingerprint
316
- ? (await (0, maybeUploadFingerprintAsync_1.maybeUploadFingerprintAsync)({
316
+ ? ((await (0, maybeUploadFingerprintAsync_1.maybeUploadFingerprintAsync)({
317
317
  hash: info.runtimeVersion,
318
318
  fingerprint: info.expoUpdatesRuntimeFingerprint,
319
319
  graphqlClient,
320
- })).fingerprintSource ?? null
320
+ })).fingerprintSource ?? null)
321
321
  : null,
322
322
  };
323
323
  }));
@@ -8,7 +8,7 @@ const fast_glob_1 = tslib_1.__importDefault(require("fast-glob"));
8
8
  const fs_extra_1 = tslib_1.__importDefault(require("fs-extra"));
9
9
  const node_stream_zip_1 = tslib_1.__importDefault(require("node-stream-zip"));
10
10
  const path_1 = tslib_1.__importDefault(require("path"));
11
- const tar_1 = tslib_1.__importDefault(require("tar"));
11
+ const tar = tslib_1.__importStar(require("tar"));
12
12
  const uuid_1 = require("uuid");
13
13
  const url_1 = require("../build/utils/url");
14
14
  const EasCommand_1 = tslib_1.__importDefault(require("../commandUtils/EasCommand"));
@@ -214,7 +214,7 @@ async function uploadAppArchiveAsync(graphqlClient, originalPath) {
214
214
  const tarPath = path_1.default.join((0, paths_1.getTmpDirectory)(), `${(0, uuid_1.v4)()}.tar.gz`);
215
215
  const parentPath = path_1.default.dirname(originalPath);
216
216
  const folderName = path_1.default.basename(originalPath);
217
- await tar_1.default.create({ cwd: parentPath, file: tarPath, gzip: true }, [folderName]);
217
+ await tar.create({ cwd: parentPath, file: tarPath, gzip: true }, [folderName]);
218
218
  filePath = tarPath;
219
219
  }
220
220
  const fileSize = (await fs_extra_1.default.stat(filePath)).size;
@@ -306,7 +306,7 @@ async function extractAppMetadataAsync(buildPath, platform) {
306
306
  try {
307
307
  let fingerprintHashPromise;
308
308
  let infoPlistPromise;
309
- await tar_1.default.list({
309
+ await tar.list({
310
310
  file: buildPath,
311
311
  // eslint-disable-next-line async-protect/async-suffix
312
312
  onentry: entry => {
@@ -43,9 +43,7 @@ exports.AppleDistributionCertificateMutation = {
43
43
  async deleteAppleDistributionCertificateAsync(graphqlClient, appleDistributionCertificateId) {
44
44
  await (0, client_1.withErrorHandlingAsync)(graphqlClient
45
45
  .mutation((0, graphql_tag_1.default) `
46
- mutation DeleteAppleDistributionCertificateMutation(
47
- $appleDistributionCertificateId: ID!
48
- ) {
46
+ mutation DeleteAppleDistributionCertificateMutation($appleDistributionCertificateId: ID!) {
49
47
  appleDistributionCertificate {
50
48
  deleteAppleDistributionCertificate(id: $appleDistributionCertificateId) {
51
49
  id
@@ -18,7 +18,18 @@ exports.TEAMS_LIMIT = 50;
18
18
  exports.DEVICES_LIMIT = 50;
19
19
  async function selectAppleTeamOnAccountAsync(graphqlClient, { accountName, selectionPromptTitle, paginatedQueryOptions, }) {
20
20
  if (paginatedQueryOptions.nonInteractive) {
21
- throw new Error('Unable to select an Apple team in non-interactive mode.');
21
+ const teams = await AppleTeamQuery_1.AppleTeamQuery.getAllForAccountAsync(graphqlClient, {
22
+ accountName,
23
+ limit: paginatedQueryOptions.limit ?? exports.TEAMS_LIMIT,
24
+ offset: paginatedQueryOptions.offset,
25
+ });
26
+ if (teams.length === 0) {
27
+ throw new Error(`No Apple teams found for account ${accountName}.`);
28
+ }
29
+ const teamList = teams
30
+ .map(t => t.appleTeamName ? `${t.appleTeamName} (${t.appleTeamIdentifier})` : t.appleTeamIdentifier)
31
+ .join('\n ');
32
+ throw new Error(`Unable to select an Apple team in non-interactive mode. Use the --apple-team-id flag to specify the team. Available Apple teams for account ${accountName}:\n ${teamList}`);
22
33
  }
23
34
  else {
24
35
  const selectedAppleTeam = await (0, queries_1.paginatedQueryWithSelectPromptAsync)({
@@ -47,7 +58,17 @@ async function selectAppleTeamOnAccountAsync(graphqlClient, { accountName, selec
47
58
  }
48
59
  async function selectAppleDeviceOnAppleTeamAsync(graphqlClient, { accountName, appleTeamIdentifier, selectionPromptTitle, paginatedQueryOptions, }) {
49
60
  if (paginatedQueryOptions.nonInteractive) {
50
- throw new Error('Unable to select an Apple device in non-interactive mode.');
61
+ const devices = await AppleDeviceQuery_1.AppleDeviceQuery.getAllForAppleTeamAsync(graphqlClient, {
62
+ accountName,
63
+ appleTeamIdentifier,
64
+ limit: paginatedQueryOptions.limit ?? exports.DEVICES_LIMIT,
65
+ offset: paginatedQueryOptions.offset,
66
+ });
67
+ if (devices.length === 0) {
68
+ throw new Error(`No devices found on Apple team ${appleTeamIdentifier} for account ${accountName}.`);
69
+ }
70
+ const deviceList = devices.map(d => (0, DeviceUtils_1.formatDeviceLabel)(d)).join('\n ');
71
+ throw new Error(`Unable to select an Apple device in non-interactive mode. Available devices on Apple team ${appleTeamIdentifier}:\n ${deviceList}`);
51
72
  }
52
73
  else {
53
74
  const selectedAppleDevice = await (0, queries_1.paginatedQueryWithSelectPromptAsync)({