@capawesome/cli 3.11.0 → 4.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 (48) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/dist/commands/apps/builds/cancel.js +1 -1
  3. package/dist/commands/apps/builds/create.js +58 -50
  4. package/dist/commands/apps/builds/download.js +27 -3
  5. package/dist/commands/apps/bundles/create.js +5 -449
  6. package/dist/commands/apps/bundles/delete.js +3 -68
  7. package/dist/commands/apps/bundles/update.js +3 -66
  8. package/dist/commands/apps/channels/create.js +5 -8
  9. package/dist/commands/apps/channels/create.test.js +6 -9
  10. package/dist/commands/apps/channels/delete.js +3 -2
  11. package/dist/commands/apps/channels/get.js +2 -12
  12. package/dist/commands/apps/channels/get.test.js +1 -2
  13. package/dist/commands/apps/channels/list.js +2 -10
  14. package/dist/commands/apps/channels/list.test.js +2 -3
  15. package/dist/commands/apps/channels/pause.js +85 -0
  16. package/dist/commands/apps/channels/resume.js +85 -0
  17. package/dist/commands/apps/channels/update.js +4 -7
  18. package/dist/commands/apps/channels/update.test.js +2 -4
  19. package/dist/commands/apps/create.js +1 -1
  20. package/dist/commands/apps/delete.js +3 -2
  21. package/dist/commands/apps/deployments/cancel.js +1 -1
  22. package/dist/commands/apps/deployments/create.js +82 -31
  23. package/dist/commands/apps/devices/delete.js +3 -2
  24. package/dist/commands/apps/environments/create.js +1 -1
  25. package/dist/commands/apps/environments/delete.js +3 -2
  26. package/dist/commands/apps/liveupdates/bundle.js +117 -0
  27. package/dist/commands/apps/liveupdates/generate-manifest.js +39 -0
  28. package/dist/commands/{manifests/generate.test.js → apps/liveupdates/generate-manifest.test.js} +6 -6
  29. package/dist/commands/apps/liveupdates/register.js +291 -0
  30. package/dist/commands/apps/{bundles/create.test.js → liveupdates/register.test.js} +123 -111
  31. package/dist/commands/apps/liveupdates/rollback.js +171 -0
  32. package/dist/commands/apps/liveupdates/rollout.js +147 -0
  33. package/dist/commands/apps/liveupdates/upload.js +420 -0
  34. package/dist/commands/apps/liveupdates/upload.test.js +325 -0
  35. package/dist/commands/manifests/generate.js +2 -27
  36. package/dist/commands/organizations/create.js +1 -1
  37. package/dist/index.js +8 -0
  38. package/dist/services/app-builds.js +9 -2
  39. package/dist/services/app-channels.js +19 -0
  40. package/dist/services/app-deployments.js +24 -14
  41. package/dist/services/config.js +2 -0
  42. package/dist/utils/app-environments.js +2 -1
  43. package/dist/utils/time-format.js +26 -0
  44. package/package.json +3 -3
  45. package/dist/commands/apps/bundles/delete.test.js +0 -142
  46. package/dist/commands/apps/bundles/update.test.js +0 -144
  47. package/dist/utils/capacitor-config.js +0 -96
  48. package/dist/utils/package-json.js +0 -58
package/CHANGELOG.md CHANGED
@@ -2,6 +2,40 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file. See [commit-and-tag-version](https://github.com/absolute-version/commit-and-tag-version) for commit guidelines.
4
4
 
5
+ ## [4.0.1](https://github.com/capawesome-team/cli/compare/v4.0.0...v4.0.1) (2026-01-27)
6
+
7
+
8
+ ### Bug Fixes
9
+
10
+ * change exit code to indicate errors for deprecated commands ([78cd785](https://github.com/capawesome-team/cli/commit/78cd785714309f3acbba2ca19a17b8bd85194cc6))
11
+
12
+ ## [4.0.0](https://github.com/capawesome-team/cli/compare/v3.11.0...v4.0.0) (2026-01-24)
13
+
14
+
15
+ ### ⚠ BREAKING CHANGES
16
+
17
+ * Deprecate `apps:bundles:*` and `manifests:generate` commands.
18
+ * update node and npm engine requirements
19
+ * The `--bundle-limit` parameter has been removed from the `apps:channels:create` and `apps:channels:update` commands (see `BREAKING.md`).
20
+
21
+ * update node and npm engine requirements ([32bd55c](https://github.com/capawesome-team/cli/commit/32bd55c59833cda478f5302e99b47f4a9efd5416))
22
+
23
+
24
+ ### Features
25
+
26
+ * add `--yes` option for non-interactive environments ([8edda58](https://github.com/capawesome-team/cli/commit/8edda5830195b0d1a5b823ed49153c65748c203f))
27
+ * add `apps:channels:pause` command ([00318a9](https://github.com/capawesome-team/cli/commit/00318a985bbca29374437666cb750a0406ac5e79))
28
+ * add `apps:channels:resume` command ([6b9eb29](https://github.com/capawesome-team/cli/commit/6b9eb29a6f2e7a3452e63fab9a5075b0cc93f0c0))
29
+ * add `apps:liveupdates:bundle` command ([5985e13](https://github.com/capawesome-team/cli/commit/5985e13483f8dd1b2cfbb8d3ef833cf6d8b5df87))
30
+ * add `apps:liveupdates:rollback` command ([b4504d0](https://github.com/capawesome-team/cli/commit/b4504d0128211f179b1f057da4738f2a25932b32))
31
+ * add `apps:liveupdates:rollout` command ([#112](https://github.com/capawesome-team/cli/issues/112)) ([40a105f](https://github.com/capawesome-team/cli/commit/40a105fc3795d8ae210c322c22e6879789be809e))
32
+ * add `apps:liveupdates:upload`, `apps:liveupdates:register` and `apps:liveupdates:generatemanifest` commands ([#113](https://github.com/capawesome-team/cli/issues/113)) ([1cd339e](https://github.com/capawesome-team/cli/commit/1cd339e6c3b9bbac42efdccac35b3cb9e065de44))
33
+ * **apps:channels:create:** add `protect` option ([efc2910](https://github.com/capawesome-team/cli/commit/efc291083e4d081e87a44121de2a0c560af206c9))
34
+ * **apps:deployments:create:** add `--build-number` option ([cdc7f63](https://github.com/capawesome-team/cli/commit/cdc7f63abd9692b9160a46d8d15f58ead5266d07))
35
+ * **apps:deployments:create:** add platform selection prompt for deployment ([c4db421](https://github.com/capawesome-team/cli/commit/c4db42120fa12173ac6489ed41c985a2af7c072e))
36
+ * **apps:liveupdates:** add warnings for web asset paths ([962d5d3](https://github.com/capawesome-team/cli/commit/962d5d3e69ffe1c9640b39b0410ce5af696c4a91))
37
+ * support web builds ([#111](https://github.com/capawesome-team/cli/issues/111)) ([7405088](https://github.com/capawesome-team/cli/commit/7405088fecfb8b1a5d5301145f7002808d57878f))
38
+
5
39
  ## [3.11.0](https://github.com/capawesome-team/cli/compare/v3.10.2...v3.11.0) (2026-01-08)
6
40
 
7
41
 
@@ -3,10 +3,10 @@ import appsService from '../../../services/apps.js';
3
3
  import authorizationService from '../../../services/authorization-service.js';
4
4
  import jobsService from '../../../services/jobs.js';
5
5
  import organizationsService from '../../../services/organizations.js';
6
+ import { isInteractive } from '../../../utils/environment.js';
6
7
  import { prompt } from '../../../utils/prompt.js';
7
8
  import { defineCommand, defineOptions } from '@robingenz/zli';
8
9
  import consola from 'consola';
9
- import { isInteractive } from '../../../utils/environment.js';
10
10
  import { z } from 'zod';
11
11
  export default defineCommand({
12
12
  description: 'Cancel an app build.',
@@ -6,13 +6,13 @@ import appsService from '../../../services/apps.js';
6
6
  import authorizationService from '../../../services/authorization-service.js';
7
7
  import organizationsService from '../../../services/organizations.js';
8
8
  import { unescapeAnsi } from '../../../utils/ansi.js';
9
+ import { isInteractive } from '../../../utils/environment.js';
9
10
  import { prompt } from '../../../utils/prompt.js';
10
11
  import { wait } from '../../../utils/wait.js';
11
12
  import { defineCommand, defineOptions } from '@robingenz/zli';
12
13
  import consola from 'consola';
13
14
  import fs from 'fs/promises';
14
15
  import path from 'path';
15
- import { isInteractive } from '../../../utils/environment.js';
16
16
  import { z } from 'zod';
17
17
  const IOS_BUILD_TYPES = ['simulator', 'development', 'ad-hoc', 'app-store', 'enterprise'];
18
18
  const ANDROID_BUILD_TYPES = ['debug', 'release'];
@@ -46,11 +46,11 @@ export default defineCommand({
46
46
  .describe('Download the generated IPA file (iOS only). Optionally provide a file path.'),
47
47
  json: z.boolean().optional().describe('Output in JSON format.'),
48
48
  platform: z
49
- .enum(['ios', 'android'], {
50
- message: 'Platform must be either `ios` or `android`.',
49
+ .enum(['ios', 'android', 'web'], {
50
+ message: 'Platform must be either `ios`, `android`, or `web`.',
51
51
  })
52
52
  .optional()
53
- .describe('The platform for the build. Supported values are `ios` and `android`.'),
53
+ .describe('The platform for the build. Supported values are `ios`, `android`, and `web`.'),
54
54
  stack: z
55
55
  .enum(['macos-sequoia', 'macos-tahoe'], {
56
56
  message: 'Build stack must be either `macos-sequoia` or `macos-tahoe`.',
@@ -60,8 +60,13 @@ export default defineCommand({
60
60
  type: z
61
61
  .string()
62
62
  .optional()
63
- .describe('The type of build. For iOS, supported values are `simulator`, `development`, `ad-hoc`, `app-store`, and `enterprise`. For Android, supported values are `debug` and `release`.'),
64
- })),
63
+ .describe('The type of build. For iOS, supported values are `simulator`, `development`, `ad-hoc`, `app-store`, and `enterprise`. For Android, supported values are `debug` and `release`. For Web, no type is required.'),
64
+ zip: z
65
+ .union([z.boolean(), z.string()])
66
+ .optional()
67
+ .describe('Download the generated ZIP file (Web only). Optionally provide a file path.'),
68
+ yes: z.boolean().optional().describe('Skip confirmation prompts.'),
69
+ }), { y: 'yes' }),
65
70
  action: async (options) => {
66
71
  let { appId, platform, type, gitRef, environment, certificate, json, stack } = options;
67
72
  // Check if the user is logged in
@@ -70,8 +75,8 @@ export default defineCommand({
70
75
  process.exit(1);
71
76
  }
72
77
  // Validate that detached flag cannot be used with artifact flags
73
- if (options.detached && (options.apk || options.aab || options.ipa)) {
74
- consola.error('The --detached flag cannot be used with --apk, --aab, or --ipa flags.');
78
+ if (options.detached && (options.apk || options.aab || options.ipa || options.zip)) {
79
+ consola.error('The --detached flag cannot be used with --apk, --aab, --ipa, or --zip flags.');
75
80
  process.exit(1);
76
81
  }
77
82
  // Prompt for app ID if not provided
@@ -123,6 +128,7 @@ export default defineCommand({
123
128
  options: [
124
129
  { label: 'Android', value: 'android' },
125
130
  { label: 'iOS', value: 'ios' },
131
+ { label: 'Web', value: 'web' },
126
132
  ],
127
133
  });
128
134
  if (!platform) {
@@ -146,7 +152,12 @@ export default defineCommand({
146
152
  }
147
153
  // Set default type based on platform if not provided
148
154
  if (!type) {
149
- type = platform === 'android' ? 'debug' : 'simulator';
155
+ if (platform === 'android') {
156
+ type = 'debug';
157
+ }
158
+ else if (platform === 'ios') {
159
+ type = 'simulator';
160
+ }
150
161
  }
151
162
  // Validate type based on platform
152
163
  if (platform === 'ios' && !IOS_BUILD_TYPES.includes(type)) {
@@ -158,7 +169,7 @@ export default defineCommand({
158
169
  process.exit(1);
159
170
  }
160
171
  // Prompt for environment if not provided
161
- if (!environment && isInteractive()) {
172
+ if (!environment && !options.yes && isInteractive()) {
162
173
  // @ts-ignore wait till https://github.com/unjs/consola/pull/280 is merged
163
174
  const selectEnvironment = await prompt('Do you want to select an environment?', {
164
175
  type: 'confirm',
@@ -178,8 +189,8 @@ export default defineCommand({
178
189
  }
179
190
  }
180
191
  }
181
- // Prompt for certificate if not provided
182
- if (!certificate && isInteractive()) {
192
+ // Prompt for certificate if not provided (skip for web platform)
193
+ if (!certificate && !options.yes && isInteractive() && platform !== 'web') {
183
194
  // @ts-ignore wait till https://github.com/unjs/consola/pull/280 is merged
184
195
  const selectCertificate = await prompt('Do you want to select a certificate?', {
185
196
  type: 'confirm',
@@ -200,9 +211,7 @@ export default defineCommand({
200
211
  }
201
212
  }
202
213
  // Create the app build
203
- if (!json) {
204
- consola.start('Creating build...');
205
- }
214
+ consola.start('Creating build...');
206
215
  const response = await appBuildsService.create({
207
216
  appCertificateName: certificate,
208
217
  appEnvironmentName: environment,
@@ -212,12 +221,10 @@ export default defineCommand({
212
221
  platform,
213
222
  type,
214
223
  });
215
- if (!json) {
216
- consola.success(`Build created successfully.`);
217
- consola.info(`Build Number: ${response.numberAsString}`);
218
- consola.info(`Build ID: ${response.id}`);
219
- consola.info(`Build URL: ${DEFAULT_CONSOLE_BASE_URL}/apps/${appId}/builds/${response.id}`);
220
- }
224
+ consola.info(`Build ID: ${response.id}`);
225
+ consola.info(`Build Number: ${response.numberAsString}`);
226
+ consola.info(`Build URL: ${DEFAULT_CONSOLE_BASE_URL}/apps/${appId}/builds/${response.id}`);
227
+ consola.success('Build created successfully.');
221
228
  // Wait for build job to complete by default, unless --detached flag is set
222
229
  const shouldWait = !options.detached;
223
230
  if (shouldWait) {
@@ -238,7 +245,7 @@ export default defineCommand({
238
245
  const jobStatus = build.job.status;
239
246
  // Show spinner while queued or pending
240
247
  if (jobStatus === 'queued' || jobStatus === 'pending') {
241
- if (isWaitingForStart && !json) {
248
+ if (isWaitingForStart) {
242
249
  consola.start(`Waiting for build to start (status: ${jobStatus})...`);
243
250
  }
244
251
  await wait(3000);
@@ -247,12 +254,10 @@ export default defineCommand({
247
254
  // Stop spinner when job moves to in_progress
248
255
  if (isWaitingForStart && jobStatus === 'in_progress') {
249
256
  isWaitingForStart = false;
250
- if (!json) {
251
- consola.success('Build started...');
252
- }
257
+ consola.success('Build started...');
253
258
  }
254
259
  // Print new logs
255
- if (!json && build.job.jobLogs && build.job.jobLogs.length > 0) {
260
+ if (build.job.jobLogs && build.job.jobLogs.length > 0) {
256
261
  const newLogs = build.job.jobLogs
257
262
  .filter((log) => log.number > lastPrintedLogNumber)
258
263
  .sort((a, b) => a.number - b.number);
@@ -267,14 +272,13 @@ export default defineCommand({
267
272
  jobStatus === 'canceled' ||
268
273
  jobStatus === 'rejected' ||
269
274
  jobStatus === 'timed_out') {
270
- if (!json) {
271
- console.log(); // New line for better readability
272
- }
275
+ console.log(); // New line for better readability
273
276
  if (jobStatus === 'succeeded') {
274
- if (!json) {
275
- consola.success('Build completed successfully.');
276
- console.log(); // New line for better readability
277
- }
277
+ consola.info(`Build ID: ${response.id}`);
278
+ consola.info(`Build Number: ${response.numberAsString}`);
279
+ consola.info(`Build URL: ${DEFAULT_CONSOLE_BASE_URL}/apps/${appId}/builds/${response.id}`);
280
+ consola.success('Build completed successfully.');
281
+ console.log(); // New line for better readability
278
282
  // Download artifacts if flags are set
279
283
  if (options.apk && platform === 'android') {
280
284
  await handleArtifactDownload({
@@ -283,7 +287,6 @@ export default defineCommand({
283
287
  buildArtifacts: build.appBuildArtifacts,
284
288
  artifactType: 'apk',
285
289
  filePath: typeof options.apk === 'string' ? options.apk : undefined,
286
- json,
287
290
  });
288
291
  }
289
292
  if (options.aab && platform === 'android') {
@@ -293,7 +296,6 @@ export default defineCommand({
293
296
  buildArtifacts: build.appBuildArtifacts,
294
297
  artifactType: 'aab',
295
298
  filePath: typeof options.aab === 'string' ? options.aab : undefined,
296
- json,
297
299
  });
298
300
  }
299
301
  if (options.ipa && platform === 'ios') {
@@ -303,7 +305,15 @@ export default defineCommand({
303
305
  buildArtifacts: build.appBuildArtifacts,
304
306
  artifactType: 'ipa',
305
307
  filePath: typeof options.ipa === 'string' ? options.ipa : undefined,
306
- json,
308
+ });
309
+ }
310
+ if (options.zip && platform === 'web') {
311
+ await handleArtifactDownload({
312
+ appId,
313
+ buildId: response.id,
314
+ buildArtifacts: build.appBuildArtifacts,
315
+ artifactType: 'zip',
316
+ filePath: typeof options.zip === 'string' ? options.zip : undefined,
307
317
  });
308
318
  }
309
319
  // Output JSON if json flag is set
@@ -349,31 +359,31 @@ export default defineCommand({
349
359
  numberAsString: response.numberAsString,
350
360
  }, null, 2));
351
361
  }
362
+ else {
363
+ consola.info(`Build ID: ${response.id}`);
364
+ consola.info(`Build Number: ${response.numberAsString}`);
365
+ consola.info(`Build URL: ${DEFAULT_CONSOLE_BASE_URL}/apps/${appId}/builds/${response.id}`);
366
+ consola.success(`Build completed successfully.`);
367
+ }
352
368
  }
353
369
  },
354
370
  });
355
371
  /**
356
- * Download a build artifact (APK, AAB, or IPA).
372
+ * Download a build artifact (APK, AAB, IPA, or ZIP).
357
373
  */
358
374
  const handleArtifactDownload = async (options) => {
359
- const { appId, buildId, buildArtifacts, artifactType, filePath, json } = options;
375
+ const { appId, buildId, buildArtifacts, artifactType, filePath } = options;
360
376
  try {
361
377
  const artifactTypeUpper = artifactType.toUpperCase();
362
- if (!json) {
363
- consola.start(`Downloading ${artifactTypeUpper}...`);
364
- }
378
+ consola.start(`Downloading ${artifactTypeUpper}...`);
365
379
  // Find the artifact
366
380
  const artifact = buildArtifacts?.find((artifact) => artifact.type === artifactType);
367
381
  if (!artifact) {
368
- if (!json) {
369
- consola.warn(`No ${artifactTypeUpper} artifact found for this build.`);
370
- }
382
+ consola.warn(`No ${artifactTypeUpper} artifact found for this build.`);
371
383
  return;
372
384
  }
373
385
  if (artifact.status !== 'ready') {
374
- if (!json) {
375
- consola.warn(`${artifactTypeUpper} artifact is not ready (status: ${artifact.status}).`);
376
- }
386
+ consola.warn(`${artifactTypeUpper} artifact is not ready (status: ${artifact.status}).`);
377
387
  return;
378
388
  }
379
389
  // Download the artifact
@@ -394,9 +404,7 @@ const handleArtifactDownload = async (options) => {
394
404
  }
395
405
  // Save the file
396
406
  await fs.writeFile(outputPath, Buffer.from(artifactData));
397
- if (!json) {
398
- consola.success(`${artifactTypeUpper} downloaded successfully: ${outputPath}`);
399
- }
407
+ consola.success(`${artifactTypeUpper} downloaded successfully: ${outputPath}`);
400
408
  }
401
409
  catch (error) {
402
410
  consola.error(`Failed to download ${artifactType.toUpperCase()}:`, error);
@@ -36,6 +36,10 @@ export default defineCommand({
36
36
  .union([z.boolean(), z.string()])
37
37
  .optional()
38
38
  .describe('Download the IPA artifact. Optionally provide a file path.'),
39
+ zip: z
40
+ .union([z.boolean(), z.string()])
41
+ .optional()
42
+ .describe('Download the ZIP artifact. Optionally provide a file path.'),
39
43
  })),
40
44
  action: async (options) => {
41
45
  let { appId, buildId } = options;
@@ -120,14 +124,19 @@ export default defineCommand({
120
124
  consola.error('Cannot download APK or AAB artifacts for an iOS build.');
121
125
  process.exit(1);
122
126
  }
127
+ if (build.platform === 'web' && (options.apk || options.aab || options.ipa)) {
128
+ consola.error('Cannot download APK, AAB, or IPA artifacts for a Web build.');
129
+ process.exit(1);
130
+ }
123
131
  // Determine which artifacts to download
124
132
  let downloadApk = options.apk;
125
133
  let downloadAab = options.aab;
126
134
  let downloadIpa = options.ipa;
135
+ let downloadZip = options.zip;
127
136
  // Prompt for artifact types if none were provided
128
- if (!downloadApk && !downloadAab && !downloadIpa) {
137
+ if (!downloadApk && !downloadAab && !downloadIpa && !downloadZip) {
129
138
  if (!isInteractive()) {
130
- consola.error('You must specify at least one artifact type (--apk, --aab, or --ipa) when running in non-interactive environment.');
139
+ consola.error('You must specify at least one artifact type (--apk, --aab, --ipa, or --zip) when running in non-interactive environment.');
131
140
  process.exit(1);
132
141
  }
133
142
  // Get available artifact types from the build
@@ -152,6 +161,11 @@ export default defineCommand({
152
161
  artifactOptions.push({ label: 'IPA', value: 'ipa' });
153
162
  }
154
163
  }
164
+ else if (build.platform === 'web') {
165
+ if (availableArtifacts.includes('zip')) {
166
+ artifactOptions.push({ label: 'ZIP', value: 'zip' });
167
+ }
168
+ }
155
169
  // @ts-ignore wait till https://github.com/unjs/consola/pull/280 is merged
156
170
  const selectedArtifacts = await prompt('Which artifact type(s) do you want to download:', {
157
171
  type: 'multiselect',
@@ -165,6 +179,7 @@ export default defineCommand({
165
179
  downloadApk = selectedArtifacts.includes('apk');
166
180
  downloadAab = selectedArtifacts.includes('aab');
167
181
  downloadIpa = selectedArtifacts.includes('ipa');
182
+ downloadZip = selectedArtifacts.includes('zip');
168
183
  }
169
184
  // Download artifacts if flags are set
170
185
  if (downloadApk) {
@@ -194,10 +209,19 @@ export default defineCommand({
194
209
  filePath: typeof options.ipa === 'string' ? options.ipa : undefined,
195
210
  });
196
211
  }
212
+ if (downloadZip) {
213
+ await handleArtifactDownload({
214
+ appId,
215
+ buildId: buildId,
216
+ buildArtifacts: build.appBuildArtifacts,
217
+ artifactType: 'zip',
218
+ filePath: typeof options.zip === 'string' ? options.zip : undefined,
219
+ });
220
+ }
197
221
  },
198
222
  });
199
223
  /**
200
- * Download a build artifact (APK, AAB, or IPA).
224
+ * Download a build artifact (APK, AAB, IPA, or ZIP).
201
225
  */
202
226
  const handleArtifactDownload = async (options) => {
203
227
  const { appId, buildId, buildArtifacts, artifactType, filePath } = options;