@capawesome/cli 3.1.0 → 3.2.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.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,27 @@
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
+ ## [3.2.1](https://github.com/capawesome-team/cli/compare/v3.2.0...v3.2.1) (2025-09-26)
6
+
7
+
8
+ ### Bug Fixes
9
+
10
+ * add missing CI checks ([#84](https://github.com/capawesome-team/cli/issues/84)) ([617e89c](https://github.com/capawesome-team/cli/commit/617e89c6b69c907573b61b5e685a2f05cdab2d62))
11
+ * **apps:bundles:create:** add CI check for channel prompt ([5d89cff](https://github.com/capawesome-team/cli/commit/5d89cffad756b6d5b9017c8797dc8c2e8cd2d5f8))
12
+ * **apps:bundles:create:** add CI checks for path and app ID prompts ([e96f567](https://github.com/capawesome-team/cli/commit/e96f5674293a01a8d87eb293dffdf06b4a59f43f))
13
+
14
+ ## [3.2.0](https://github.com/capawesome-team/cli/compare/v3.1.0...v3.2.0) (2025-09-21)
15
+
16
+
17
+ ### Features
18
+
19
+ * **apps:channels:create:** add `--expires-in-days` option ([#69](https://github.com/capawesome-team/cli/issues/69)) ([ef2dd36](https://github.com/capawesome-team/cli/commit/ef2dd3638653e35a3ee3390ad336f39d368cd7ca))
20
+
21
+
22
+ ### Bug Fixes
23
+
24
+ * **apps:bundles:create:** add error handling for invalid path types ([1fae8c6](https://github.com/capawesome-team/cli/commit/1fae8c6a831bdc0f5bb1e80f2dea4fa3a981e625))
25
+
5
26
  ## [3.1.0](https://github.com/capawesome-team/cli/compare/v3.0.0...v3.1.0) (2025-09-17)
6
27
 
7
28
 
@@ -5,16 +5,17 @@ import appsService from '../../../services/apps.js';
5
5
  import authorizationService from '../../../services/authorization-service.js';
6
6
  import organizationsService from '../../../services/organizations.js';
7
7
  import { createBufferFromPath, createBufferFromReadStream, createBufferFromString, isPrivateKeyContent, } from '../../../utils/buffer.js';
8
- import { formatPrivateKey } from '../../../utils/private-key.js';
9
8
  import { fileExistsAtPath, getFilesInDirectoryAndSubdirectories, isDirectory } from '../../../utils/file.js';
10
9
  import { createHash } from '../../../utils/hash.js';
11
10
  import { generateManifestJson } from '../../../utils/manifest.js';
11
+ import { formatPrivateKey } from '../../../utils/private-key.js';
12
12
  import { prompt } from '../../../utils/prompt.js';
13
13
  import { createSignature } from '../../../utils/signature.js';
14
14
  import zip from '../../../utils/zip.js';
15
15
  import { defineCommand, defineOptions } from '@robingenz/zli';
16
16
  import consola from 'consola';
17
17
  import { createReadStream } from 'fs';
18
+ import { isCI } from 'std-env';
18
19
  import { z } from 'zod';
19
20
  export default defineCommand({
20
21
  description: 'Create a new app bundle.',
@@ -94,7 +95,7 @@ export default defineCommand({
94
95
  consola.error('You must be logged in to run this command.');
95
96
  process.exit(1);
96
97
  }
97
- // Validate the expiration days
98
+ // Calculate the expiration date
98
99
  let expiresAt;
99
100
  if (expiresInDays) {
100
101
  const expiresAtDate = new Date();
@@ -103,13 +104,19 @@ export default defineCommand({
103
104
  }
104
105
  // Check that either a path or a url is provided
105
106
  if (!path && !url) {
106
- path = await prompt('Enter the path to the app bundle:', {
107
- type: 'text',
108
- });
109
- if (!path) {
110
- consola.error('You must provide a path to the app bundle.');
107
+ if (isCI) {
108
+ consola.error('You must provide either a path or a url when running in CI mode.');
111
109
  process.exit(1);
112
110
  }
111
+ else {
112
+ path = await prompt('Enter the path to the app bundle:', {
113
+ type: 'text',
114
+ });
115
+ if (!path) {
116
+ consola.error('You must provide a path to the app bundle.');
117
+ process.exit(1);
118
+ }
119
+ }
113
120
  }
114
121
  if (path) {
115
122
  // Check if the path exists when a path is provided
@@ -128,6 +135,13 @@ export default defineCommand({
128
135
  process.exit(1);
129
136
  }
130
137
  }
138
+ else if (zip.isZipped(path)) {
139
+ // No-op
140
+ }
141
+ else {
142
+ consola.error('The path must be either a folder or a zip file.');
143
+ process.exit(1);
144
+ }
131
145
  }
132
146
  // Check that the path is a directory when creating a bundle with an artifact type
133
147
  if (artifactType === 'manifest' && path) {
@@ -143,6 +157,10 @@ export default defineCommand({
143
157
  process.exit(1);
144
158
  }
145
159
  if (!appId) {
160
+ if (isCI) {
161
+ consola.error('You must provide an app ID when running in CI mode.');
162
+ process.exit(1);
163
+ }
146
164
  const organizations = await organizationsService.findAll();
147
165
  if (organizations.length === 0) {
148
166
  consola.error('You must create an organization before creating a bundle.');
@@ -174,7 +192,7 @@ export default defineCommand({
174
192
  process.exit(1);
175
193
  }
176
194
  }
177
- if (!channel) {
195
+ if (!channel && !isCI) {
178
196
  const promptChannel = await prompt('Do you want to deploy to a specific channel?', {
179
197
  type: 'select',
180
198
  options: ['Yes', 'No'],
@@ -1,6 +1,6 @@
1
1
  import { DEFAULT_API_BASE_URL } from '../../../config/consts.js';
2
2
  import authorizationService from '../../../services/authorization-service.js';
3
- import { fileExistsAtPath, isDirectory } from '../../../utils/file.js';
3
+ import { fileExistsAtPath, getFilesInDirectoryAndSubdirectories, isDirectory } from '../../../utils/file.js';
4
4
  import userConfig from '../../../utils/user-config.js';
5
5
  import consola from 'consola';
6
6
  import nock from 'nock';
@@ -20,6 +20,7 @@ describe('apps-bundles-create', () => {
20
20
  const mockUserConfig = vi.mocked(userConfig);
21
21
  const mockAuthorizationService = vi.mocked(authorizationService);
22
22
  const mockFileExistsAtPath = vi.mocked(fileExistsAtPath);
23
+ const mockGetFilesInDirectoryAndSubdirectories = vi.mocked(getFilesInDirectoryAndSubdirectories);
23
24
  const mockIsDirectory = vi.mocked(isDirectory);
24
25
  const mockConsola = vi.mocked(consola);
25
26
  beforeEach(() => {
@@ -100,6 +101,9 @@ describe('apps-bundles-create', () => {
100
101
  };
101
102
  mockFileExistsAtPath.mockResolvedValue(true);
102
103
  mockIsDirectory.mockResolvedValue(false);
104
+ // Mock zip utility to return true so path validation passes
105
+ const mockZip = await import('../../../utils/zip.js');
106
+ vi.mocked(mockZip.default.isZipped).mockReturnValue(true);
103
107
  await expect(createBundleCommand.action(options, undefined)).rejects.toThrow('Process exited with code 1');
104
108
  expect(mockConsola.error).toHaveBeenCalledWith('The path must be a folder when creating a bundle with an artifact type of `manifest`.');
105
109
  });
@@ -249,6 +253,10 @@ describe('apps-bundles-create', () => {
249
253
  return Promise.resolve(false);
250
254
  return Promise.resolve(true);
251
255
  });
256
+ mockIsDirectory.mockResolvedValue(true);
257
+ mockGetFilesInDirectoryAndSubdirectories.mockResolvedValue([
258
+ { href: 'index.html', mimeType: 'text/html', name: 'index.html', path: 'index.html' },
259
+ ]);
252
260
  // Mock utility functions
253
261
  const mockBuffer = await import('../../../utils/buffer.js');
254
262
  vi.mocked(mockBuffer.isPrivateKeyContent).mockReturnValue(false);
@@ -267,6 +275,9 @@ describe('apps-bundles-create', () => {
267
275
  };
268
276
  mockFileExistsAtPath.mockResolvedValue(true);
269
277
  mockIsDirectory.mockResolvedValue(false);
278
+ // Mock zip utility to pass path validation
279
+ const mockZip = await import('../../../utils/zip.js');
280
+ vi.mocked(mockZip.default.isZipped).mockReturnValue(true);
270
281
  // Mock utility functions
271
282
  const mockBuffer = await import('../../../utils/buffer.js');
272
283
  vi.mocked(mockBuffer.isPrivateKeyContent).mockReturnValue(false);
@@ -5,6 +5,7 @@ import organizationsService from '../../../services/organizations.js';
5
5
  import { prompt } from '../../../utils/prompt.js';
6
6
  import { defineCommand, defineOptions } from '@robingenz/zli';
7
7
  import consola from 'consola';
8
+ import { isCI } from 'std-env';
8
9
  import { z } from 'zod';
9
10
  export default defineCommand({
10
11
  description: 'Delete an app bundle.',
@@ -20,6 +21,10 @@ export default defineCommand({
20
21
  }
21
22
  // Prompt for missing arguments
22
23
  if (!appId) {
24
+ if (isCI) {
25
+ consola.error('You must provide an app ID when running in CI mode.');
26
+ process.exit(1);
27
+ }
23
28
  const organizations = await organizationsService.findAll();
24
29
  if (organizations.length === 0) {
25
30
  consola.error('You must create an organization before deleting a bundle.');
@@ -48,16 +53,22 @@ export default defineCommand({
48
53
  });
49
54
  }
50
55
  if (!bundleId) {
56
+ if (isCI) {
57
+ consola.error('You must provide the bundle ID when running in CI mode.');
58
+ process.exit(1);
59
+ }
51
60
  bundleId = await prompt('Enter the bundle ID:', {
52
61
  type: 'text',
53
62
  });
54
63
  }
55
64
  // Confirm deletion
56
- const confirmed = await prompt('Are you sure you want to delete this bundle?', {
57
- type: 'confirm',
58
- });
59
- if (!confirmed) {
60
- return;
65
+ if (!isCI) {
66
+ const confirmed = await prompt('Are you sure you want to delete this bundle?', {
67
+ type: 'confirm',
68
+ });
69
+ if (!confirmed) {
70
+ return;
71
+ }
61
72
  }
62
73
  // Delete bundle
63
74
  await appBundlesService.delete({
@@ -11,6 +11,9 @@ vi.mock('@/utils/user-config.js');
11
11
  vi.mock('@/utils/prompt.js');
12
12
  vi.mock('@/services/authorization-service.js');
13
13
  vi.mock('consola');
14
+ vi.mock('std-env', () => ({
15
+ isCI: false,
16
+ }));
14
17
  describe('apps-bundles-delete', () => {
15
18
  const mockUserConfig = vi.mocked(userConfig);
16
19
  const mockPrompt = vi.mocked(prompt);
@@ -5,6 +5,7 @@ import organizationsService from '../../../services/organizations.js';
5
5
  import { prompt } from '../../../utils/prompt.js';
6
6
  import { defineCommand, defineOptions } from '@robingenz/zli';
7
7
  import consola from 'consola';
8
+ import { isCI } from 'std-env';
8
9
  import { z } from 'zod';
9
10
  export default defineCommand({
10
11
  description: 'Update an app bundle.',
@@ -44,6 +45,10 @@ export default defineCommand({
44
45
  }
45
46
  // Prompt for missing arguments
46
47
  if (!appId) {
48
+ if (isCI) {
49
+ consola.error('You must provide an app ID when running in CI mode.');
50
+ process.exit(1);
51
+ }
47
52
  const organizations = await organizationsService.findAll();
48
53
  if (organizations.length === 0) {
49
54
  consola.error('You must create an organization before updating a bundle.');
@@ -72,6 +77,10 @@ export default defineCommand({
72
77
  });
73
78
  }
74
79
  if (!bundleId) {
80
+ if (isCI) {
81
+ consola.error('You must provide the bundle ID when running in CI mode.');
82
+ process.exit(1);
83
+ }
75
84
  bundleId = await prompt('Enter the bundle ID:', {
76
85
  type: 'text',
77
86
  });
@@ -11,6 +11,9 @@ vi.mock('@/utils/user-config.js');
11
11
  vi.mock('@/utils/prompt.js');
12
12
  vi.mock('@/services/authorization-service.js');
13
13
  vi.mock('consola');
14
+ vi.mock('std-env', () => ({
15
+ isCI: false,
16
+ }));
14
17
  describe('apps-bundles-update', () => {
15
18
  const mockUserConfig = vi.mocked(userConfig);
16
19
  const mockPrompt = vi.mocked(prompt);
@@ -6,6 +6,7 @@ import { getMessageFromUnknownError } from '../../../utils/error.js';
6
6
  import { prompt } from '../../../utils/prompt.js';
7
7
  import { defineCommand, defineOptions } from '@robingenz/zli';
8
8
  import consola from 'consola';
9
+ import { isCI } from 'std-env';
9
10
  import { z } from 'zod';
10
11
  export default defineCommand({
11
12
  description: 'Create a new app channel.',
@@ -15,17 +16,37 @@ export default defineCommand({
15
16
  .number()
16
17
  .optional()
17
18
  .describe('Maximum number of bundles that can be assigned to the channel. If more bundles are assigned, the oldest bundles will be automatically deleted.'),
19
+ expiresInDays: z.coerce
20
+ .number({
21
+ message: 'Expiration days must be an integer.',
22
+ })
23
+ .int({
24
+ message: 'Expiration days must be an integer.',
25
+ })
26
+ .optional()
27
+ .describe('The number of days until the channel is automatically deleted.'),
18
28
  ignoreErrors: z.boolean().optional().describe('Whether to ignore errors or not.'),
19
29
  name: z.string().optional().describe('Name of the channel.'),
20
30
  })),
21
31
  action: async (options, args) => {
22
- let { appId, bundleLimit, ignoreErrors, name } = options;
32
+ let { appId, bundleLimit, expiresInDays, ignoreErrors, name } = options;
23
33
  if (!authorizationService.hasAuthorizationToken()) {
24
34
  consola.error('You must be logged in to run this command.');
25
35
  process.exit(1);
26
36
  }
37
+ // Calculate the expiration date
38
+ let expiresAt;
39
+ if (expiresInDays) {
40
+ const expiresAtDate = new Date();
41
+ expiresAtDate.setDate(expiresAtDate.getDate() + expiresInDays);
42
+ expiresAt = expiresAtDate.toISOString();
43
+ }
27
44
  // Validate the app ID
28
45
  if (!appId) {
46
+ if (isCI) {
47
+ consola.error('You must provide an app ID when running in CI mode.');
48
+ process.exit(1);
49
+ }
29
50
  const organizations = await organizationsService.findAll();
30
51
  if (organizations.length === 0) {
31
52
  consola.error('You must create an organization before creating a channel.');
@@ -55,6 +76,10 @@ export default defineCommand({
55
76
  }
56
77
  // Validate the channel name
57
78
  if (!name) {
79
+ if (isCI) {
80
+ consola.error('You must provide the channel name when running in CI mode.');
81
+ process.exit(1);
82
+ }
58
83
  name = await prompt('Enter the name of the channel:', { type: 'text' });
59
84
  }
60
85
  try {
@@ -62,6 +87,7 @@ export default defineCommand({
62
87
  appId,
63
88
  name,
64
89
  totalAppBundleLimit: bundleLimit,
90
+ expiresAt,
65
91
  });
66
92
  consola.success('Channel created successfully.');
67
93
  consola.info(`Channel ID: ${response.id}`);
@@ -11,6 +11,9 @@ vi.mock('@/utils/user-config.js');
11
11
  vi.mock('@/utils/prompt.js');
12
12
  vi.mock('@/services/authorization-service.js');
13
13
  vi.mock('consola');
14
+ vi.mock('std-env', () => ({
15
+ isCI: false,
16
+ }));
14
17
  describe('apps-channels-create', () => {
15
18
  const mockUserConfig = vi.mocked(userConfig);
16
19
  const mockPrompt = vi.mocked(prompt);
@@ -116,4 +119,32 @@ describe('apps-channels-create', () => {
116
119
  await expect(createChannelCommand.action(options, undefined)).rejects.toThrow();
117
120
  expect(scope.isDone()).toBe(true);
118
121
  });
122
+ it('should create channel with expiresInDays option', async () => {
123
+ const appId = 'app-123';
124
+ const channelName = 'production';
125
+ const expiresInDays = 30;
126
+ const channelId = 'channel-456';
127
+ const testToken = 'test-token';
128
+ const options = { appId, name: channelName, expiresInDays };
129
+ // Calculate expected expiration date
130
+ const expectedExpiresAt = new Date();
131
+ expectedExpiresAt.setDate(expectedExpiresAt.getDate() + expiresInDays);
132
+ const scope = nock(DEFAULT_API_BASE_URL)
133
+ .post(`/v1/apps/${appId}/channels`, (body) => {
134
+ // Verify the request includes expiresAt and it's approximately correct (within 1 minute)
135
+ const actualExpiresAt = new Date(body.expiresAt);
136
+ const timeDiff = Math.abs(actualExpiresAt.getTime() - expectedExpiresAt.getTime());
137
+ const oneMinute = 60 * 1000;
138
+ return (body.appId === appId &&
139
+ body.name === channelName &&
140
+ body.totalAppBundleLimit === undefined &&
141
+ timeDiff < oneMinute);
142
+ })
143
+ .matchHeader('Authorization', `Bearer ${testToken}`)
144
+ .reply(201, { id: channelId, name: channelName });
145
+ await createChannelCommand.action(options, undefined);
146
+ expect(scope.isDone()).toBe(true);
147
+ expect(mockConsola.success).toHaveBeenCalledWith('Channel created successfully.');
148
+ expect(mockConsola.info).toHaveBeenCalledWith(`Channel ID: ${channelId}`);
149
+ });
119
150
  });
@@ -27,6 +27,10 @@ export default defineCommand({
27
27
  process.exit(1);
28
28
  }
29
29
  if (!appId) {
30
+ if (isCI) {
31
+ consola.error('You must provide an app ID when running in CI mode.');
32
+ process.exit(1);
33
+ }
30
34
  const organizations = await organizationsService.findAll();
31
35
  if (organizations.length === 0) {
32
36
  consola.error('You must create an organization before deleting a channel.');
@@ -54,11 +58,17 @@ export default defineCommand({
54
58
  options: apps.map((app) => ({ label: app.name, value: app.id })),
55
59
  });
56
60
  }
61
+ // Prompt for channel ID or name if neither is provided
57
62
  if (!channelId && !name) {
63
+ if (isCI) {
64
+ consola.error('You must provide either the channel ID or name when running in CI mode.');
65
+ process.exit(1);
66
+ }
58
67
  name = await prompt('Enter the channel name:', {
59
68
  type: 'text',
60
69
  });
61
70
  }
71
+ // Confirm deletion
62
72
  if (!isCI) {
63
73
  const confirmed = await prompt('Are you sure you want to delete this channel?', {
64
74
  type: 'confirm',
@@ -67,6 +77,7 @@ export default defineCommand({
67
77
  return;
68
78
  }
69
79
  }
80
+ // Delete channel
70
81
  await appChannelsService.delete({
71
82
  appId,
72
83
  id: channelId,
@@ -5,6 +5,7 @@ import organizationsService from '../../../services/organizations.js';
5
5
  import { prompt } from '../../../utils/prompt.js';
6
6
  import { defineCommand, defineOptions } from '@robingenz/zli';
7
7
  import consola from 'consola';
8
+ import { isCI } from 'std-env';
8
9
  import { z } from 'zod';
9
10
  export default defineCommand({
10
11
  description: 'Update an existing app channel.',
@@ -23,7 +24,12 @@ export default defineCommand({
23
24
  consola.error('You must be logged in to run this command.');
24
25
  process.exit(1);
25
26
  }
27
+ // Prompt app ID if not provided
26
28
  if (!appId) {
29
+ if (isCI) {
30
+ consola.error('You must provide an app ID when running in CI mode.');
31
+ process.exit(1);
32
+ }
27
33
  const organizations = await organizationsService.findAll();
28
34
  if (organizations.length === 0) {
29
35
  consola.error('You must create an organization before updating a channel.');
@@ -51,7 +57,12 @@ export default defineCommand({
51
57
  options: apps.map((app) => ({ label: app.name, value: app.id })),
52
58
  });
53
59
  }
60
+ // Prompt for channel ID if not provided
54
61
  if (!channelId) {
62
+ if (isCI) {
63
+ consola.error('You must provide the channel ID when running in CI mode.');
64
+ process.exit(1);
65
+ }
55
66
  channelId = await prompt('Enter the channel ID:', {
56
67
  type: 'text',
57
68
  });
@@ -11,6 +11,9 @@ vi.mock('@/utils/user-config.js');
11
11
  vi.mock('@/utils/prompt.js');
12
12
  vi.mock('@/services/authorization-service.js');
13
13
  vi.mock('consola');
14
+ vi.mock('std-env', () => ({
15
+ isCI: false,
16
+ }));
14
17
  describe('apps-channels-update', () => {
15
18
  const mockUserConfig = vi.mocked(userConfig);
16
19
  const mockPrompt = vi.mocked(prompt);
@@ -4,6 +4,7 @@ import organizationsService from '../../services/organizations.js';
4
4
  import { prompt } from '../../utils/prompt.js';
5
5
  import { defineCommand, defineOptions } from '@robingenz/zli';
6
6
  import consola from 'consola';
7
+ import { isCI } from 'std-env';
7
8
  import { z } from 'zod';
8
9
  export default defineCommand({
9
10
  description: 'Create a new app.',
@@ -18,6 +19,10 @@ export default defineCommand({
18
19
  process.exit(1);
19
20
  }
20
21
  if (!organizationId) {
22
+ if (isCI) {
23
+ consola.error('You must provide the organization ID when running in CI mode.');
24
+ process.exit(1);
25
+ }
21
26
  const organizations = await organizationsService.findAll();
22
27
  if (organizations.length === 0) {
23
28
  consola.error('You must create an organization before creating an app.');
@@ -34,6 +39,10 @@ export default defineCommand({
34
39
  }
35
40
  }
36
41
  if (!name) {
42
+ if (isCI) {
43
+ consola.error('You must provide the app name when running in CI mode.');
44
+ process.exit(1);
45
+ }
37
46
  name = await prompt('Enter the name of the app:', { type: 'text' });
38
47
  }
39
48
  const response = await appsService.create({ name, organizationId });
@@ -11,6 +11,9 @@ vi.mock('@/utils/user-config.js');
11
11
  vi.mock('@/utils/prompt.js');
12
12
  vi.mock('@/services/authorization-service.js');
13
13
  vi.mock('consola');
14
+ vi.mock('std-env', () => ({
15
+ isCI: false,
16
+ }));
14
17
  describe('apps-create', () => {
15
18
  const mockUserConfig = vi.mocked(userConfig);
16
19
  const mockPrompt = vi.mocked(prompt);
@@ -4,6 +4,7 @@ import organizationsService from '../../services/organizations.js';
4
4
  import { prompt } from '../../utils/prompt.js';
5
5
  import { defineCommand, defineOptions } from '@robingenz/zli';
6
6
  import consola from 'consola';
7
+ import { isCI } from 'std-env';
7
8
  import { z } from 'zod';
8
9
  export default defineCommand({
9
10
  description: 'Delete an app.',
@@ -17,6 +18,10 @@ export default defineCommand({
17
18
  process.exit(1);
18
19
  }
19
20
  if (!appId) {
21
+ if (isCI) {
22
+ consola.error('You must provide the app ID when running in CI mode.');
23
+ process.exit(1);
24
+ }
20
25
  const organizations = await organizationsService.findAll();
21
26
  if (organizations.length === 0) {
22
27
  consola.error('You must create an organization before deleting an app.');
@@ -44,11 +49,13 @@ export default defineCommand({
44
49
  options: apps.map((app) => ({ label: app.name, value: app.id })),
45
50
  });
46
51
  }
47
- const confirmed = await prompt('Are you sure you want to delete this app?', {
48
- type: 'confirm',
49
- });
50
- if (!confirmed) {
51
- return;
52
+ if (!isCI) {
53
+ const confirmed = await prompt('Are you sure you want to delete this app?', {
54
+ type: 'confirm',
55
+ });
56
+ if (!confirmed) {
57
+ return;
58
+ }
52
59
  }
53
60
  await appsService.delete({ id: appId });
54
61
  consola.success('App deleted successfully.');
@@ -11,6 +11,9 @@ vi.mock('@/utils/user-config.js');
11
11
  vi.mock('@/utils/prompt.js');
12
12
  vi.mock('@/services/authorization-service.js');
13
13
  vi.mock('consola');
14
+ vi.mock('std-env', () => ({
15
+ isCI: false,
16
+ }));
14
17
  describe('apps-delete', () => {
15
18
  const mockUserConfig = vi.mocked(userConfig);
16
19
  const mockPrompt = vi.mocked(prompt);
@@ -5,6 +5,7 @@ import organizationsService from '../../../services/organizations.js';
5
5
  import { prompt } from '../../../utils/prompt.js';
6
6
  import { defineCommand, defineOptions } from '@robingenz/zli';
7
7
  import consola from 'consola';
8
+ import { isCI } from 'std-env';
8
9
  import { z } from 'zod';
9
10
  export default defineCommand({
10
11
  description: 'Delete an app device.',
@@ -18,7 +19,12 @@ export default defineCommand({
18
19
  consola.error('You must be logged in to run this command.');
19
20
  process.exit(1);
20
21
  }
22
+ // Prompt for app ID if not provided
21
23
  if (!appId) {
24
+ if (isCI) {
25
+ consola.error('You must provide an app ID when running in CI mode.');
26
+ process.exit(1);
27
+ }
22
28
  const organizations = await organizationsService.findAll();
23
29
  if (organizations.length === 0) {
24
30
  consola.error('You must create an organization before deleting a device.');
@@ -46,17 +52,26 @@ export default defineCommand({
46
52
  options: apps.map((app) => ({ label: app.name, value: app.id })),
47
53
  });
48
54
  }
55
+ // Prompt for device ID if not provided
49
56
  if (!deviceId) {
57
+ if (isCI) {
58
+ consola.error('You must provide the device ID when running in CI mode.');
59
+ process.exit(1);
60
+ }
50
61
  deviceId = await prompt('Enter the device ID:', {
51
62
  type: 'text',
52
63
  });
53
64
  }
54
- const confirmed = await prompt('Are you sure you want to delete this device?', {
55
- type: 'confirm',
56
- });
57
- if (!confirmed) {
58
- return;
65
+ // Confirm deletion
66
+ if (!isCI) {
67
+ const confirmed = await prompt('Are you sure you want to delete this device?', {
68
+ type: 'confirm',
69
+ });
70
+ if (!confirmed) {
71
+ return;
72
+ }
59
73
  }
74
+ // Delete device
60
75
  await appDevicesService.delete({
61
76
  appId,
62
77
  deviceId,
@@ -11,6 +11,9 @@ vi.mock('@/utils/user-config.js');
11
11
  vi.mock('@/utils/prompt.js');
12
12
  vi.mock('@/services/authorization-service.js');
13
13
  vi.mock('consola');
14
+ vi.mock('std-env', () => ({
15
+ isCI: false,
16
+ }));
14
17
  describe('apps-devices-delete', () => {
15
18
  const mockUserConfig = vi.mocked(userConfig);
16
19
  const mockPrompt = vi.mocked(prompt);
@@ -20,6 +20,9 @@ vi.mock('@/utils/prompt.js');
20
20
  vi.mock('std-env', () => ({
21
21
  isCI: false,
22
22
  }));
23
+ vi.mock('std-env', () => ({
24
+ isCI: false,
25
+ }));
23
26
  describe('login', () => {
24
27
  const mockUserConfig = vi.mocked(userConfig);
25
28
  const mockSessionCodesService = vi.mocked(sessionCodesService);
@@ -1,9 +1,10 @@
1
- import { defineCommand, defineOptions } from '@robingenz/zli';
2
- import consola from 'consola';
3
- import { z } from 'zod';
4
1
  import { fileExistsAtPath } from '../../utils/file.js';
5
2
  import { generateManifestJson } from '../../utils/manifest.js';
6
3
  import { prompt } from '../../utils/prompt.js';
4
+ import { defineCommand, defineOptions } from '@robingenz/zli';
5
+ import consola from 'consola';
6
+ import { isCI } from 'std-env';
7
+ import { z } from 'zod';
7
8
  export default defineCommand({
8
9
  description: 'Generate a manifest file.',
9
10
  options: defineOptions(z.object({
@@ -12,6 +13,10 @@ export default defineCommand({
12
13
  action: async (options, args) => {
13
14
  let path = options.path;
14
15
  if (!path) {
16
+ if (isCI) {
17
+ consola.error('You must provide the path to the web assets folder when running in CI mode.');
18
+ process.exit(1);
19
+ }
15
20
  path = await prompt('Enter the path to the web assets folder:', {
16
21
  type: 'text',
17
22
  });
@@ -9,6 +9,9 @@ vi.mock('@/utils/file.js');
9
9
  vi.mock('@/utils/manifest.js');
10
10
  vi.mock('@/utils/prompt.js');
11
11
  vi.mock('consola');
12
+ vi.mock('std-env', () => ({
13
+ isCI: false,
14
+ }));
12
15
  describe('manifests-generate', () => {
13
16
  const mockFileExistsAtPath = vi.mocked(fileExistsAtPath);
14
17
  const mockGenerateManifestJson = vi.mocked(generateManifestJson);
@@ -3,6 +3,7 @@ import organizationsService from '../../services/organizations.js';
3
3
  import { prompt } from '../../utils/prompt.js';
4
4
  import { defineCommand, defineOptions } from '@robingenz/zli';
5
5
  import consola from 'consola';
6
+ import { isCI } from 'std-env';
6
7
  import { z } from 'zod';
7
8
  export default defineCommand({
8
9
  description: 'Create a new organization.',
@@ -16,6 +17,10 @@ export default defineCommand({
16
17
  process.exit(1);
17
18
  }
18
19
  if (!name) {
20
+ if (isCI) {
21
+ consola.error('You must provide the organization name when running in CI mode.');
22
+ process.exit(1);
23
+ }
19
24
  name = await prompt('Enter the name of the organization:', { type: 'text' });
20
25
  }
21
26
  const response = await organizationsService.create({ name });
@@ -11,6 +11,9 @@ vi.mock('@/utils/user-config.js');
11
11
  vi.mock('@/utils/prompt.js');
12
12
  vi.mock('@/services/authorization-service.js');
13
13
  vi.mock('consola');
14
+ vi.mock('std-env', () => ({
15
+ isCI: false,
16
+ }));
14
17
  describe('organizations-create', () => {
15
18
  const mockUserConfig = vi.mocked(userConfig);
16
19
  const mockPrompt = vi.mocked(prompt);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@capawesome/cli",
3
- "version": "3.1.0",
3
+ "version": "3.2.1",
4
4
  "description": "The Capawesome Cloud Command Line Interface (CLI) to manage Live Updates and more.",
5
5
  "type": "module",
6
6
  "scripts": {