@capawesome/cli 4.6.0-dev.a84b1ba.1774341927 → 4.6.0-dev.bda9e9d.1774345600

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.
@@ -2,11 +2,10 @@ import { DEFAULT_CONSOLE_BASE_URL } from '../../../config/consts.js';
2
2
  import appBuildsService from '../../../services/app-builds.js';
3
3
  import appCertificatesService from '../../../services/app-certificates.js';
4
4
  import appEnvironmentsService from '../../../services/app-environments.js';
5
- import { unescapeAnsi } from '../../../utils/ansi.js';
5
+ import { waitForBuildCompletion } from '../../../utils/build.js';
6
6
  import { withAuth } from '../../../utils/auth.js';
7
7
  import { isInteractive } from '../../../utils/environment.js';
8
8
  import { prompt, promptAppSelection, promptOrganizationSelection } from '../../../utils/prompt.js';
9
- import { wait } from '../../../utils/wait.js';
10
9
  import { defineCommand, defineOptions } from '@robingenz/zli';
11
10
  import consola from 'consola';
12
11
  import fs from 'fs/promises';
@@ -215,127 +214,59 @@ export default defineCommand({
215
214
  // Wait for build job to complete by default, unless --detached flag is set
216
215
  const shouldWait = !options.detached;
217
216
  if (shouldWait) {
218
- let lastPrintedLogNumber = 0;
219
- let isWaitingForStart = true;
220
- // Poll build status until completion
221
- while (true) {
222
- try {
223
- const build = await appBuildsService.findOne({
224
- appId,
225
- appBuildId: response.id,
226
- relations: 'appBuildArtifacts,job,job.jobLogs',
227
- });
228
- if (!build.job) {
229
- await wait(3000);
230
- continue;
231
- }
232
- const jobStatus = build.job.status;
233
- // Show spinner while queued or pending
234
- if (jobStatus === 'queued' || jobStatus === 'pending') {
235
- if (isWaitingForStart) {
236
- consola.start(`Waiting for build to start (status: ${jobStatus})...`);
237
- }
238
- await wait(3000);
239
- continue;
240
- }
241
- // Stop spinner when job moves to in_progress
242
- if (isWaitingForStart && jobStatus === 'in_progress') {
243
- isWaitingForStart = false;
244
- consola.success('Build started...');
245
- }
246
- // Print new logs
247
- if (build.job.jobLogs && build.job.jobLogs.length > 0) {
248
- const newLogs = build.job.jobLogs
249
- .filter((log) => log.number > lastPrintedLogNumber)
250
- .sort((a, b) => a.number - b.number);
251
- for (const log of newLogs) {
252
- console.log(unescapeAnsi(log.payload));
253
- lastPrintedLogNumber = log.number;
254
- }
255
- }
256
- // Handle terminal states
257
- if (jobStatus === 'succeeded' ||
258
- jobStatus === 'failed' ||
259
- jobStatus === 'canceled' ||
260
- jobStatus === 'rejected' ||
261
- jobStatus === 'timed_out') {
262
- console.log(); // New line for better readability
263
- if (jobStatus === 'succeeded') {
264
- consola.info(`Build ID: ${response.id}`);
265
- consola.info(`Build Number: ${response.numberAsString}`);
266
- consola.info(`Build URL: ${DEFAULT_CONSOLE_BASE_URL}/apps/${appId}/builds/${response.id}`);
267
- consola.success('Build completed successfully.');
268
- console.log(); // New line for better readability
269
- // Download artifacts if flags are set
270
- if (options.apk && platform === 'android') {
271
- await handleArtifactDownload({
272
- appId,
273
- buildId: response.id,
274
- buildArtifacts: build.appBuildArtifacts,
275
- artifactType: 'apk',
276
- filePath: typeof options.apk === 'string' ? options.apk : undefined,
277
- });
278
- }
279
- if (options.aab && platform === 'android') {
280
- await handleArtifactDownload({
281
- appId,
282
- buildId: response.id,
283
- buildArtifacts: build.appBuildArtifacts,
284
- artifactType: 'aab',
285
- filePath: typeof options.aab === 'string' ? options.aab : undefined,
286
- });
287
- }
288
- if (options.ipa && platform === 'ios') {
289
- await handleArtifactDownload({
290
- appId,
291
- buildId: response.id,
292
- buildArtifacts: build.appBuildArtifacts,
293
- artifactType: 'ipa',
294
- filePath: typeof options.ipa === 'string' ? options.ipa : undefined,
295
- });
296
- }
297
- if (options.zip && platform === 'web') {
298
- await handleArtifactDownload({
299
- appId,
300
- buildId: response.id,
301
- buildArtifacts: build.appBuildArtifacts,
302
- artifactType: 'zip',
303
- filePath: typeof options.zip === 'string' ? options.zip : undefined,
304
- });
305
- }
306
- // Output JSON if json flag is set
307
- if (json) {
308
- console.log(JSON.stringify({
309
- id: response.id,
310
- numberAsString: response.numberAsString,
311
- }, null, 2));
312
- }
313
- break;
314
- }
315
- else if (jobStatus === 'failed') {
316
- consola.error('Build failed.');
317
- process.exit(1);
318
- }
319
- else if (jobStatus === 'canceled') {
320
- consola.warn('Build was canceled.');
321
- process.exit(1);
322
- }
323
- else if (jobStatus === 'rejected') {
324
- consola.error('Build was rejected.');
325
- process.exit(1);
326
- }
327
- else if (jobStatus === 'timed_out') {
328
- consola.error('Build timed out.');
329
- process.exit(1);
330
- }
331
- }
332
- // Wait before next poll (3 seconds)
333
- await wait(3000);
334
- }
335
- catch (error) {
336
- consola.error('Error polling build status:', error);
337
- process.exit(1);
338
- }
217
+ const build = await waitForBuildCompletion({
218
+ appId,
219
+ appBuildId: response.id,
220
+ relations: 'appBuildArtifacts,job,job.jobLogs',
221
+ });
222
+ consola.info(`Build ID: ${response.id}`);
223
+ consola.info(`Build Number: ${response.numberAsString}`);
224
+ consola.info(`Build URL: ${DEFAULT_CONSOLE_BASE_URL}/apps/${appId}/builds/${response.id}`);
225
+ consola.success('Build completed successfully.');
226
+ console.log();
227
+ // Download artifacts if flags are set
228
+ if (options.apk && platform === 'android') {
229
+ await handleArtifactDownload({
230
+ appId,
231
+ buildId: response.id,
232
+ buildArtifacts: build.appBuildArtifacts,
233
+ artifactType: 'apk',
234
+ filePath: typeof options.apk === 'string' ? options.apk : undefined,
235
+ });
236
+ }
237
+ if (options.aab && platform === 'android') {
238
+ await handleArtifactDownload({
239
+ appId,
240
+ buildId: response.id,
241
+ buildArtifacts: build.appBuildArtifacts,
242
+ artifactType: 'aab',
243
+ filePath: typeof options.aab === 'string' ? options.aab : undefined,
244
+ });
245
+ }
246
+ if (options.ipa && platform === 'ios') {
247
+ await handleArtifactDownload({
248
+ appId,
249
+ buildId: response.id,
250
+ buildArtifacts: build.appBuildArtifacts,
251
+ artifactType: 'ipa',
252
+ filePath: typeof options.ipa === 'string' ? options.ipa : undefined,
253
+ });
254
+ }
255
+ if (options.zip && platform === 'web') {
256
+ await handleArtifactDownload({
257
+ appId,
258
+ buildId: response.id,
259
+ buildArtifacts: build.appBuildArtifacts,
260
+ artifactType: 'zip',
261
+ filePath: typeof options.zip === 'string' ? options.zip : undefined,
262
+ });
263
+ }
264
+ // Output JSON if json flag is set
265
+ if (json) {
266
+ console.log(JSON.stringify({
267
+ id: response.id,
268
+ numberAsString: response.numberAsString,
269
+ }, null, 2));
339
270
  }
340
271
  }
341
272
  else {
@@ -10,11 +10,11 @@ export default defineCommand({
10
10
  description: 'Force a device to use a specific channel.',
11
11
  options: defineOptions(z.object({
12
12
  appId: z.string().uuid({ message: 'App ID must be a UUID.' }).optional().describe('ID of the app.'),
13
- deviceId: z.array(z.string()).optional().describe('ID of the device. Can be specified multiple times.'),
13
+ deviceId: z.string().optional().describe('ID of the device.'),
14
14
  channel: z.string().optional().describe('Name of the channel to force.'),
15
15
  })),
16
16
  action: withAuth(async (options, args) => {
17
- let { appId, deviceId: deviceIds, channel } = options;
17
+ let { appId, deviceId, channel } = options;
18
18
  if (!appId) {
19
19
  if (!isInteractive()) {
20
20
  consola.error('You must provide an app ID when running in non-interactive environment.');
@@ -23,15 +23,14 @@ export default defineCommand({
23
23
  const organizationId = await promptOrganizationSelection();
24
24
  appId = await promptAppSelection(organizationId);
25
25
  }
26
- if (!deviceIds || deviceIds.length === 0) {
26
+ if (!deviceId) {
27
27
  if (!isInteractive()) {
28
28
  consola.error('You must provide the device ID when running in non-interactive environment.');
29
29
  process.exit(1);
30
30
  }
31
- const deviceId = await prompt('Enter the device ID:', {
31
+ deviceId = await prompt('Enter the device ID:', {
32
32
  type: 'text',
33
33
  });
34
- deviceIds = [deviceId];
35
34
  }
36
35
  if (!channel) {
37
36
  if (!isInteractive()) {
@@ -57,12 +56,11 @@ export default defineCommand({
57
56
  consola.error('Channel ID not found.');
58
57
  process.exit(1);
59
58
  }
60
- await appDevicesService.updateMany({
59
+ await appDevicesService.update({
61
60
  appId,
62
- deviceIds,
61
+ deviceId,
63
62
  forcedAppChannelId: channelId,
64
63
  });
65
- const deviceCount = deviceIds.length;
66
- consola.success(`${deviceCount === 1 ? 'Device' : `${deviceCount} devices`} forced to channel successfully.`);
64
+ consola.success('Device forced to channel successfully.');
67
65
  }),
68
66
  });
@@ -9,10 +9,10 @@ export default defineCommand({
9
9
  description: 'Remove the forced channel from a device.',
10
10
  options: defineOptions(z.object({
11
11
  appId: z.string().uuid({ message: 'App ID must be a UUID.' }).optional().describe('ID of the app.'),
12
- deviceId: z.array(z.string()).optional().describe('ID of the device. Can be specified multiple times.'),
12
+ deviceId: z.string().optional().describe('ID of the device.'),
13
13
  })),
14
14
  action: withAuth(async (options, args) => {
15
- let { appId, deviceId: deviceIds } = options;
15
+ let { appId, deviceId } = options;
16
16
  if (!appId) {
17
17
  if (!isInteractive()) {
18
18
  consola.error('You must provide an app ID when running in non-interactive environment.');
@@ -21,22 +21,20 @@ export default defineCommand({
21
21
  const organizationId = await promptOrganizationSelection();
22
22
  appId = await promptAppSelection(organizationId);
23
23
  }
24
- if (!deviceIds || deviceIds.length === 0) {
24
+ if (!deviceId) {
25
25
  if (!isInteractive()) {
26
26
  consola.error('You must provide the device ID when running in non-interactive environment.');
27
27
  process.exit(1);
28
28
  }
29
- const deviceId = await prompt('Enter the device ID:', {
29
+ deviceId = await prompt('Enter the device ID:', {
30
30
  type: 'text',
31
31
  });
32
- deviceIds = [deviceId];
33
32
  }
34
- await appDevicesService.updateMany({
33
+ await appDevicesService.update({
35
34
  appId,
36
- deviceIds,
35
+ deviceId,
37
36
  forcedAppChannelId: null,
38
37
  });
39
- const deviceCount = deviceIds.length;
40
- consola.success(`Forced channel removed from ${deviceCount === 1 ? 'device' : `${deviceCount} devices`} successfully.`);
38
+ consola.success('Forced channel removed from device successfully.');
41
39
  }),
42
40
  });
@@ -0,0 +1,189 @@
1
+ import { DEFAULT_CONSOLE_BASE_URL } from '../../../config/consts.js';
2
+ import appBuildsService from '../../../services/app-builds.js';
3
+ import appDeploymentsService from '../../../services/app-deployments.js';
4
+ import appCertificatesService from '../../../services/app-certificates.js';
5
+ import appEnvironmentsService from '../../../services/app-environments.js';
6
+ import { withAuth } from '../../../utils/auth.js';
7
+ import { waitForBuildCompletion } from '../../../utils/build.js';
8
+ import { isInteractive } from '../../../utils/environment.js';
9
+ import { prompt, promptAppSelection, promptOrganizationSelection } from '../../../utils/prompt.js';
10
+ import { defineCommand, defineOptions } from '@robingenz/zli';
11
+ import consola from 'consola';
12
+ import { z } from 'zod';
13
+ export default defineCommand({
14
+ description: 'Create a new live update.',
15
+ options: defineOptions(z.object({
16
+ appId: z
17
+ .uuid({
18
+ message: 'App ID must be a UUID.',
19
+ })
20
+ .optional()
21
+ .describe('App ID to create the live update for.'),
22
+ certificate: z.string().optional().describe('The name of the certificate to use for the build.'),
23
+ channel: z.string().optional().describe('The name of the channel to deploy to.'),
24
+ gitRef: z.string().optional().describe('The Git reference (branch, tag, or commit SHA) to build.'),
25
+ environment: z.string().optional().describe('The name of the environment to use for the build.'),
26
+ stack: z
27
+ .enum(['macos-sequoia', 'macos-tahoe'], {
28
+ message: 'Build stack must be either `macos-sequoia` or `macos-tahoe`.',
29
+ })
30
+ .optional()
31
+ .describe('The build stack to use for the build process.'),
32
+ androidMin: z.string().optional().describe('The minimum Android versionCode for the live update.'),
33
+ androidMax: z.string().optional().describe('The maximum Android versionCode for the live update.'),
34
+ androidEq: z.string().optional().describe('The exact Android versionCode for the live update.'),
35
+ iosMin: z.string().optional().describe('The minimum iOS CFBundleVersion for the live update.'),
36
+ iosMax: z.string().optional().describe('The maximum iOS CFBundleVersion for the live update.'),
37
+ iosEq: z.string().optional().describe('The exact iOS CFBundleVersion for the live update.'),
38
+ rolloutPercentage: z
39
+ .number()
40
+ .int()
41
+ .min(0)
42
+ .max(100)
43
+ .optional()
44
+ .describe('The rollout percentage for the deployment (0-100). Default: 100.'),
45
+ json: z.boolean().optional().describe('Output in JSON format.'),
46
+ yes: z.boolean().optional().describe('Skip confirmation prompts.'),
47
+ }), { y: 'yes' }),
48
+ action: withAuth(async (options) => {
49
+ let { appId, certificate, channel, gitRef, environment, json, stack } = options;
50
+ // Prompt for app ID if not provided
51
+ if (!appId) {
52
+ if (!isInteractive()) {
53
+ consola.error('You must provide an app ID when running in non-interactive environment.');
54
+ process.exit(1);
55
+ }
56
+ const organizationId = await promptOrganizationSelection({ allowCreate: true });
57
+ appId = await promptAppSelection(organizationId, { allowCreate: true });
58
+ }
59
+ // Prompt for git ref if not provided
60
+ if (!gitRef) {
61
+ if (!isInteractive()) {
62
+ consola.error('You must provide a git ref when running in non-interactive environment.');
63
+ process.exit(1);
64
+ }
65
+ gitRef = await prompt('Enter the Git reference (branch, tag, or commit SHA):', {
66
+ type: 'text',
67
+ });
68
+ if (!gitRef) {
69
+ consola.error('You must provide a git ref.');
70
+ process.exit(1);
71
+ }
72
+ }
73
+ // Prompt for channel if not provided
74
+ if (!channel) {
75
+ if (!isInteractive()) {
76
+ consola.error('You must provide a channel when running in non-interactive environment.');
77
+ process.exit(1);
78
+ }
79
+ channel = await prompt('Enter the channel name to deploy to:', {
80
+ type: 'text',
81
+ });
82
+ if (!channel) {
83
+ consola.error('You must provide a channel.');
84
+ process.exit(1);
85
+ }
86
+ }
87
+ // Prompt for environment if not provided
88
+ if (!environment && !options.yes && isInteractive()) {
89
+ // @ts-ignore wait till https://github.com/unjs/consola/pull/280 is merged
90
+ const selectEnvironment = await prompt('Do you want to select an environment?', {
91
+ type: 'confirm',
92
+ initial: false,
93
+ });
94
+ if (selectEnvironment) {
95
+ const environments = await appEnvironmentsService.findAll({ appId });
96
+ if (environments.length === 0) {
97
+ consola.warn('No environments found for this app.');
98
+ }
99
+ else {
100
+ // @ts-ignore wait till https://github.com/unjs/consola/pull/280 is merged
101
+ environment = await prompt('Select the environment for the build:', {
102
+ type: 'select',
103
+ options: environments.map((env) => ({ label: env.name, value: env.name })),
104
+ });
105
+ }
106
+ }
107
+ }
108
+ // Prompt for certificate if not provided
109
+ if (!certificate && !options.yes && isInteractive()) {
110
+ // @ts-ignore wait till https://github.com/unjs/consola/pull/280 is merged
111
+ const selectCertificate = await prompt('Do you want to select a certificate?', {
112
+ type: 'confirm',
113
+ initial: false,
114
+ });
115
+ if (selectCertificate) {
116
+ const certificates = await appCertificatesService.findAll({ appId, platform: 'web' });
117
+ if (certificates.length === 0) {
118
+ consola.warn('No certificates found for this app.');
119
+ }
120
+ else {
121
+ // @ts-ignore wait till https://github.com/unjs/consola/pull/280 is merged
122
+ certificate = await prompt('Select the certificate for the build:', {
123
+ type: 'select',
124
+ options: certificates.map((cert) => ({ label: cert.name, value: cert.name })),
125
+ });
126
+ }
127
+ }
128
+ }
129
+ // Create the web build
130
+ consola.start('Creating build...');
131
+ const response = await appBuildsService.create({
132
+ appCertificateName: certificate,
133
+ appEnvironmentName: environment,
134
+ appId,
135
+ stack,
136
+ gitRef,
137
+ platform: 'web',
138
+ });
139
+ consola.info(`Build ID: ${response.id}`);
140
+ consola.info(`Build Number: ${response.numberAsString}`);
141
+ consola.info(`Build URL: ${DEFAULT_CONSOLE_BASE_URL}/apps/${appId}/builds/${response.id}`);
142
+ consola.success('Build created successfully.');
143
+ // Wait for build to complete
144
+ await waitForBuildCompletion({ appId, appBuildId: response.id });
145
+ consola.success('Build completed successfully.');
146
+ console.log();
147
+ // Update build with version constraints if any are provided
148
+ const hasVersionConstraints = options.androidMin ||
149
+ options.androidMax ||
150
+ options.androidEq ||
151
+ options.iosMin ||
152
+ options.iosMax ||
153
+ options.iosEq;
154
+ if (hasVersionConstraints) {
155
+ consola.start('Updating version constraints...');
156
+ await appBuildsService.update({
157
+ appId,
158
+ appBuildId: response.id,
159
+ minAndroidAppVersionCode: options.androidMin,
160
+ maxAndroidAppVersionCode: options.androidMax,
161
+ eqAndroidAppVersionCode: options.androidEq,
162
+ minIosAppVersionCode: options.iosMin,
163
+ maxIosAppVersionCode: options.iosMax,
164
+ eqIosAppVersionCode: options.iosEq,
165
+ });
166
+ consola.success('Version constraints updated successfully.');
167
+ }
168
+ // Deploy to channel
169
+ consola.start('Creating deployment...');
170
+ const rolloutPercentage = (options.rolloutPercentage ?? 100) / 100;
171
+ const deployment = await appDeploymentsService.create({
172
+ appId,
173
+ appBuildId: response.id,
174
+ appChannelName: channel,
175
+ rolloutPercentage,
176
+ });
177
+ consola.info(`Deployment ID: ${deployment.id}`);
178
+ consola.info(`Deployment URL: ${DEFAULT_CONSOLE_BASE_URL}/apps/${appId}/deployments/${deployment.id}`);
179
+ consola.success('Deployment created successfully.');
180
+ // Output JSON if json flag is set
181
+ if (json) {
182
+ console.log(JSON.stringify({
183
+ buildId: response.id,
184
+ buildNumberAsString: response.numberAsString,
185
+ deploymentId: deployment.id,
186
+ }, null, 2));
187
+ }
188
+ }),
189
+ });
package/dist/index.js CHANGED
@@ -60,6 +60,7 @@ const config = defineConfig({
60
60
  'apps:environments:set': await import('./commands/apps/environments/set.js').then((mod) => mod.default),
61
61
  'apps:environments:unset': await import('./commands/apps/environments/unset.js').then((mod) => mod.default),
62
62
  'apps:liveupdates:bundle': await import('./commands/apps/liveupdates/bundle.js').then((mod) => mod.default),
63
+ 'apps:liveupdates:create': await import('./commands/apps/liveupdates/create.js').then((mod) => mod.default),
63
64
  'apps:liveupdates:generatesigningkey': await import('./commands/apps/liveupdates/generate-signing-key.js').then((mod) => mod.default),
64
65
  'apps:liveupdates:rollback': await import('./commands/apps/liveupdates/rollback.js').then((mod) => mod.default),
65
66
  'apps:liveupdates:rollout': await import('./commands/apps/liveupdates/rollout.js').then((mod) => mod.default),
@@ -55,14 +55,6 @@ class AppDevicesServiceImpl {
55
55
  },
56
56
  });
57
57
  }
58
- async updateMany(data) {
59
- const ids = data.deviceIds.join(',');
60
- await this.httpClient.patch(`/v1/apps/${data.appId}/devices?ids=${ids}`, { forcedAppChannelId: data.forcedAppChannelId }, {
61
- headers: {
62
- Authorization: `Bearer ${authorizationService.getCurrentAuthorizationToken()}`,
63
- },
64
- });
65
- }
66
58
  }
67
59
  const appDevicesService = new AppDevicesServiceImpl(httpClient);
68
60
  export default appDevicesService;
@@ -0,0 +1,74 @@
1
+ import appBuildsService from '../services/app-builds.js';
2
+ import { unescapeAnsi } from '../utils/ansi.js';
3
+ import { wait } from '../utils/wait.js';
4
+ import consola from 'consola';
5
+ export const waitForBuildCompletion = async (options) => {
6
+ const { appId, appBuildId, relations = 'job,job.jobLogs' } = options;
7
+ let lastPrintedLogNumber = 0;
8
+ let isWaitingForStart = true;
9
+ while (true) {
10
+ try {
11
+ const build = await appBuildsService.findOne({
12
+ appId,
13
+ appBuildId,
14
+ relations,
15
+ });
16
+ if (!build.job) {
17
+ await wait(3000);
18
+ continue;
19
+ }
20
+ const jobStatus = build.job.status;
21
+ if (jobStatus === 'queued' || jobStatus === 'pending') {
22
+ if (isWaitingForStart) {
23
+ consola.start(`Waiting for build to start (status: ${jobStatus})...`);
24
+ }
25
+ await wait(3000);
26
+ continue;
27
+ }
28
+ if (isWaitingForStart && jobStatus === 'in_progress') {
29
+ isWaitingForStart = false;
30
+ consola.success('Build started...');
31
+ }
32
+ if (build.job.jobLogs && build.job.jobLogs.length > 0) {
33
+ const newLogs = build.job.jobLogs
34
+ .filter((log) => log.number > lastPrintedLogNumber)
35
+ .sort((a, b) => a.number - b.number);
36
+ for (const log of newLogs) {
37
+ console.log(unescapeAnsi(log.payload));
38
+ lastPrintedLogNumber = log.number;
39
+ }
40
+ }
41
+ if (jobStatus === 'succeeded' ||
42
+ jobStatus === 'failed' ||
43
+ jobStatus === 'canceled' ||
44
+ jobStatus === 'rejected' ||
45
+ jobStatus === 'timed_out') {
46
+ console.log();
47
+ if (jobStatus === 'succeeded') {
48
+ return build;
49
+ }
50
+ else if (jobStatus === 'failed') {
51
+ consola.error('Build failed.');
52
+ process.exit(1);
53
+ }
54
+ else if (jobStatus === 'canceled') {
55
+ consola.warn('Build was canceled.');
56
+ process.exit(1);
57
+ }
58
+ else if (jobStatus === 'rejected') {
59
+ consola.error('Build was rejected.');
60
+ process.exit(1);
61
+ }
62
+ else if (jobStatus === 'timed_out') {
63
+ consola.error('Build timed out.');
64
+ process.exit(1);
65
+ }
66
+ }
67
+ await wait(3000);
68
+ }
69
+ catch (error) {
70
+ consola.error('Error polling build status:', error);
71
+ process.exit(1);
72
+ }
73
+ }
74
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@capawesome/cli",
3
- "version": "4.6.0-dev.a84b1ba.1774341927",
3
+ "version": "4.6.0-dev.bda9e9d.1774345600",
4
4
  "description": "The Capawesome Cloud Command Line Interface (CLI) to manage Live Updates and more.",
5
5
  "type": "module",
6
6
  "scripts": {