@capawesome/cli 4.1.0 → 4.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.
Files changed (43) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/commands/apps/builds/cancel.js +6 -41
  3. package/dist/commands/apps/builds/create.js +6 -41
  4. package/dist/commands/apps/builds/download.js +6 -41
  5. package/dist/commands/apps/builds/logs.js +6 -41
  6. package/dist/commands/apps/channels/create.js +6 -36
  7. package/dist/commands/apps/channels/create.test.js +5 -15
  8. package/dist/commands/apps/channels/delete.js +6 -36
  9. package/dist/commands/apps/channels/delete.test.js +6 -18
  10. package/dist/commands/apps/channels/get.js +3 -7
  11. package/dist/commands/apps/channels/list.js +6 -36
  12. package/dist/commands/apps/channels/list.test.js +4 -1
  13. package/dist/commands/apps/channels/pause.js +6 -40
  14. package/dist/commands/apps/channels/resume.js +6 -40
  15. package/dist/commands/apps/channels/update.js +6 -36
  16. package/dist/commands/apps/channels/update.test.js +14 -31
  17. package/dist/commands/apps/create.js +5 -23
  18. package/dist/commands/apps/create.test.js +13 -31
  19. package/dist/commands/apps/delete.js +6 -35
  20. package/dist/commands/apps/delete.test.js +11 -35
  21. package/dist/commands/apps/deployments/cancel.js +6 -41
  22. package/dist/commands/apps/deployments/create.js +6 -41
  23. package/dist/commands/apps/deployments/logs.js +6 -41
  24. package/dist/commands/apps/devices/delete.js +6 -36
  25. package/dist/commands/apps/devices/delete.test.js +14 -36
  26. package/dist/commands/apps/environments/create.js +6 -36
  27. package/dist/commands/apps/environments/delete.js +6 -36
  28. package/dist/commands/apps/environments/list.js +6 -36
  29. package/dist/commands/apps/environments/set.js +6 -36
  30. package/dist/commands/apps/environments/unset.js +6 -36
  31. package/dist/commands/apps/liveupdates/register.js +6 -40
  32. package/dist/commands/apps/liveupdates/register.test.js +2 -1
  33. package/dist/commands/apps/liveupdates/rollback.js +6 -41
  34. package/dist/commands/apps/liveupdates/rollout.js +6 -41
  35. package/dist/commands/apps/liveupdates/set-native-versions.js +3 -7
  36. package/dist/commands/apps/liveupdates/upload.js +6 -40
  37. package/dist/commands/apps/liveupdates/upload.test.js +2 -1
  38. package/dist/commands/organizations/create.js +3 -7
  39. package/dist/commands/organizations/create.test.js +3 -1
  40. package/dist/utils/auth.js +27 -0
  41. package/dist/utils/prompt.js +56 -11
  42. package/dist/utils/signature.js +2 -1
  43. package/package.json +7 -3
package/CHANGELOG.md CHANGED
@@ -2,6 +2,22 @@
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.2.1](https://github.com/capawesome-team/cli/compare/v4.2.0...v4.2.1) (2026-03-01)
6
+
7
+ ## [4.2.0](https://github.com/capawesome-team/cli/compare/v4.1.0...v4.2.0) (2026-02-20)
8
+
9
+
10
+ ### Features
11
+
12
+ * add interactive login prompt to all authenticated commands ([#116](https://github.com/capawesome-team/cli/issues/116)) ([4360b2f](https://github.com/capawesome-team/cli/commit/4360b2fca0e4d49eaa14b0d09b5702a0e9675be8))
13
+ * add interactive org/app selection prompt ([#117](https://github.com/capawesome-team/cli/issues/117)) ([15e7813](https://github.com/capawesome-team/cli/commit/15e781363e4a8d5eeb25ca39e9d52bc5300e3a35))
14
+
15
+
16
+ ### Bug Fixes
17
+
18
+ * **deps:** add overrides for glob and minimatch versions ([abf0888](https://github.com/capawesome-team/cli/commit/abf08882a6c8938db8d1e9dc2b132d1f44e65888))
19
+ * parse private key before signing for OpenSSL 3.x compatibility ([#115](https://github.com/capawesome-team/cli/issues/115)) ([1f8e26f](https://github.com/capawesome-team/cli/commit/1f8e26f0453d9360ff0664c76098e92c5a709a98))
20
+
5
21
  ## [4.1.0](https://github.com/capawesome-team/cli/compare/v4.0.5...v4.1.0) (2026-02-12)
6
22
 
7
23
 
@@ -1,10 +1,8 @@
1
1
  import appBuildsService from '../../../services/app-builds.js';
2
- import appsService from '../../../services/apps.js';
3
- import authorizationService from '../../../services/authorization-service.js';
4
2
  import jobsService from '../../../services/jobs.js';
5
- import organizationsService from '../../../services/organizations.js';
3
+ import { withAuth } from '../../../utils/auth.js';
6
4
  import { isInteractive } from '../../../utils/environment.js';
7
- import { prompt } from '../../../utils/prompt.js';
5
+ import { prompt, promptAppSelection, promptOrganizationSelection } from '../../../utils/prompt.js';
8
6
  import { defineCommand, defineOptions } from '@robingenz/zli';
9
7
  import consola from 'consola';
10
8
  import { z } from 'zod';
@@ -24,49 +22,16 @@ export default defineCommand({
24
22
  .optional()
25
23
  .describe('Build ID to cancel.'),
26
24
  })),
27
- action: async (options) => {
25
+ action: withAuth(async (options) => {
28
26
  let { appId, buildId } = options;
29
- // Check if the user is logged in
30
- if (!authorizationService.hasAuthorizationToken()) {
31
- consola.error('You must be logged in to run this command. Please run the `login` command first.');
32
- process.exit(1);
33
- }
34
27
  // Prompt for app ID if not provided
35
28
  if (!appId) {
36
29
  if (!isInteractive()) {
37
30
  consola.error('You must provide an app ID when running in non-interactive environment.');
38
31
  process.exit(1);
39
32
  }
40
- const organizations = await organizationsService.findAll();
41
- if (organizations.length === 0) {
42
- consola.error('You must create an organization before canceling a build.');
43
- process.exit(1);
44
- }
45
- // @ts-ignore wait till https://github.com/unjs/consola/pull/280 is merged
46
- const organizationId = await prompt('Select the organization of the app for which you want to cancel a build.', {
47
- type: 'select',
48
- options: organizations.map((organization) => ({ label: organization.name, value: organization.id })),
49
- });
50
- if (!organizationId) {
51
- consola.error('You must select the organization of an app for which you want to cancel a build.');
52
- process.exit(1);
53
- }
54
- const apps = await appsService.findAll({
55
- organizationId,
56
- });
57
- if (apps.length === 0) {
58
- consola.error('You must create an app before canceling a build.');
59
- process.exit(1);
60
- }
61
- // @ts-ignore wait till https://github.com/unjs/consola/pull/280 is merged
62
- appId = await prompt('Which app do you want to cancel a build for:', {
63
- type: 'select',
64
- options: apps.map((app) => ({ label: app.name, value: app.id })),
65
- });
66
- if (!appId) {
67
- consola.error('You must select an app to cancel a build for.');
68
- process.exit(1);
69
- }
33
+ const organizationId = await promptOrganizationSelection();
34
+ appId = await promptAppSelection(organizationId);
70
35
  }
71
36
  // Prompt for build ID if not provided
72
37
  if (!buildId) {
@@ -102,5 +67,5 @@ export default defineCommand({
102
67
  dto: { status: 'canceled' },
103
68
  });
104
69
  consola.success('Build successfully canceled.');
105
- },
70
+ }),
106
71
  });
@@ -2,12 +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 appsService from '../../../services/apps.js';
6
- import authorizationService from '../../../services/authorization-service.js';
7
- import organizationsService from '../../../services/organizations.js';
8
5
  import { unescapeAnsi } from '../../../utils/ansi.js';
6
+ import { withAuth } from '../../../utils/auth.js';
9
7
  import { isInteractive } from '../../../utils/environment.js';
10
- import { prompt } from '../../../utils/prompt.js';
8
+ import { prompt, promptAppSelection, promptOrganizationSelection } from '../../../utils/prompt.js';
11
9
  import { wait } from '../../../utils/wait.js';
12
10
  import { defineCommand, defineOptions } from '@robingenz/zli';
13
11
  import consola from 'consola';
@@ -67,13 +65,8 @@ export default defineCommand({
67
65
  .describe('Download the generated ZIP file (Web only). Optionally provide a file path.'),
68
66
  yes: z.boolean().optional().describe('Skip confirmation prompts.'),
69
67
  }), { y: 'yes' }),
70
- action: async (options) => {
68
+ action: withAuth(async (options) => {
71
69
  let { appId, platform, type, gitRef, environment, certificate, json, stack } = options;
72
- // Check if the user is logged in
73
- if (!authorizationService.hasAuthorizationToken()) {
74
- consola.error('You must be logged in to run this command. Please run the `login` command first.');
75
- process.exit(1);
76
- }
77
70
  // Validate that detached flag cannot be used with artifact flags
78
71
  if (options.detached && (options.apk || options.aab || options.ipa || options.zip)) {
79
72
  consola.error('The --detached flag cannot be used with --apk, --aab, --ipa, or --zip flags.');
@@ -85,36 +78,8 @@ export default defineCommand({
85
78
  consola.error('You must provide an app ID when running in non-interactive environment.');
86
79
  process.exit(1);
87
80
  }
88
- const organizations = await organizationsService.findAll();
89
- if (organizations.length === 0) {
90
- consola.error('You must create an organization before creating a build.');
91
- process.exit(1);
92
- }
93
- // @ts-ignore wait till https://github.com/unjs/consola/pull/280 is merged
94
- const organizationId = await prompt('Select the organization of the app for which you want to create a build.', {
95
- type: 'select',
96
- options: organizations.map((organization) => ({ label: organization.name, value: organization.id })),
97
- });
98
- if (!organizationId) {
99
- consola.error('You must select the organization of an app for which you want to create a build.');
100
- process.exit(1);
101
- }
102
- const apps = await appsService.findAll({
103
- organizationId,
104
- });
105
- if (apps.length === 0) {
106
- consola.error('You must create an app before creating a build.');
107
- process.exit(1);
108
- }
109
- // @ts-ignore wait till https://github.com/unjs/consola/pull/280 is merged
110
- appId = await prompt('Which app do you want to create a build for:', {
111
- type: 'select',
112
- options: apps.map((app) => ({ label: app.name, value: app.id })),
113
- });
114
- if (!appId) {
115
- consola.error('You must select an app to create a build for.');
116
- process.exit(1);
117
- }
81
+ const organizationId = await promptOrganizationSelection({ allowCreate: true });
82
+ appId = await promptAppSelection(organizationId, { allowCreate: true });
118
83
  }
119
84
  // Prompt for platform if not provided
120
85
  if (!platform) {
@@ -366,7 +331,7 @@ export default defineCommand({
366
331
  consola.success(`Build completed successfully.`);
367
332
  }
368
333
  }
369
- },
334
+ }),
370
335
  });
371
336
  /**
372
337
  * Download a build artifact (APK, AAB, IPA, or ZIP).
@@ -1,8 +1,6 @@
1
1
  import appBuildsService from '../../../services/app-builds.js';
2
- import appsService from '../../../services/apps.js';
3
- import authorizationService from '../../../services/authorization-service.js';
4
- import organizationsService from '../../../services/organizations.js';
5
- import { prompt } from '../../../utils/prompt.js';
2
+ import { withAuth } from '../../../utils/auth.js';
3
+ import { prompt, promptAppSelection, promptOrganizationSelection } from '../../../utils/prompt.js';
6
4
  import { defineCommand, defineOptions } from '@robingenz/zli';
7
5
  import consola from 'consola';
8
6
  import fs from 'fs/promises';
@@ -41,49 +39,16 @@ export default defineCommand({
41
39
  .optional()
42
40
  .describe('Download the ZIP artifact. Optionally provide a file path.'),
43
41
  })),
44
- action: async (options) => {
42
+ action: withAuth(async (options) => {
45
43
  let { appId, buildId } = options;
46
- // Check if the user is logged in
47
- if (!authorizationService.hasAuthorizationToken()) {
48
- consola.error('You must be logged in to run this command. Please run the `login` command first.');
49
- process.exit(1);
50
- }
51
44
  // Prompt for app ID if not provided
52
45
  if (!appId) {
53
46
  if (!isInteractive()) {
54
47
  consola.error('You must provide an app ID when running in non-interactive environment.');
55
48
  process.exit(1);
56
49
  }
57
- const organizations = await organizationsService.findAll();
58
- if (organizations.length === 0) {
59
- consola.error('You must create an organization before downloading a build.');
60
- process.exit(1);
61
- }
62
- // @ts-ignore wait till https://github.com/unjs/consola/pull/280 is merged
63
- const organizationId = await prompt('Select the organization of the app for which you want to download a build:', {
64
- type: 'select',
65
- options: organizations.map((organization) => ({ label: organization.name, value: organization.id })),
66
- });
67
- if (!organizationId) {
68
- consola.error('You must select the organization of an app for which you want to download a build.');
69
- process.exit(1);
70
- }
71
- const apps = await appsService.findAll({
72
- organizationId,
73
- });
74
- if (apps.length === 0) {
75
- consola.error('You must create an app before downloading a build.');
76
- process.exit(1);
77
- }
78
- // @ts-ignore wait till https://github.com/unjs/consola/pull/280 is merged
79
- appId = await prompt('Select the app for which you want to download a build:', {
80
- type: 'select',
81
- options: apps.map((app) => ({ label: app.name, value: app.id })),
82
- });
83
- if (!appId) {
84
- consola.error('You must select an app to download a build for.');
85
- process.exit(1);
86
- }
50
+ const organizationId = await promptOrganizationSelection();
51
+ appId = await promptAppSelection(organizationId);
87
52
  }
88
53
  // Prompt for build ID if not provided
89
54
  if (!buildId) {
@@ -218,7 +183,7 @@ export default defineCommand({
218
183
  filePath: typeof options.zip === 'string' ? options.zip : undefined,
219
184
  });
220
185
  }
221
- },
186
+ }),
222
187
  });
223
188
  /**
224
189
  * Download a build artifact (APK, AAB, IPA, or ZIP).
@@ -1,9 +1,7 @@
1
1
  import appBuildsService from '../../../services/app-builds.js';
2
- import appsService from '../../../services/apps.js';
3
- import authorizationService from '../../../services/authorization-service.js';
4
- import organizationsService from '../../../services/organizations.js';
5
2
  import { unescapeAnsi } from '../../../utils/ansi.js';
6
- import { prompt } from '../../../utils/prompt.js';
3
+ import { withAuth } from '../../../utils/auth.js';
4
+ import { prompt, promptAppSelection, promptOrganizationSelection } from '../../../utils/prompt.js';
7
5
  import { wait } from '../../../utils/wait.js';
8
6
  import { defineCommand, defineOptions } from '@robingenz/zli';
9
7
  import consola from 'consola';
@@ -25,49 +23,16 @@ export default defineCommand({
25
23
  .optional()
26
24
  .describe('Build ID to display the build logs for.'),
27
25
  })),
28
- action: async (options) => {
26
+ action: withAuth(async (options) => {
29
27
  let { appId, buildId } = options;
30
- // Check if the user is logged in
31
- if (!authorizationService.hasAuthorizationToken()) {
32
- consola.error('You must be logged in to run this command. Please run the `login` command first.');
33
- process.exit(1);
34
- }
35
28
  // Prompt for app ID if not provided
36
29
  if (!appId) {
37
30
  if (!isInteractive()) {
38
31
  consola.error('You must provide an app ID when running in non-interactive environment.');
39
32
  process.exit(1);
40
33
  }
41
- const organizations = await organizationsService.findAll();
42
- if (organizations.length === 0) {
43
- consola.error('You must create an organization before viewing build logs.');
44
- process.exit(1);
45
- }
46
- // @ts-ignore wait till https://github.com/unjs/consola/pull/280 is merged
47
- const organizationId = await prompt('Select the organization of the app for which you want to view the build logs.', {
48
- type: 'select',
49
- options: organizations.map((organization) => ({ label: organization.name, value: organization.id })),
50
- });
51
- if (!organizationId) {
52
- consola.error('You must select the organization of an app for which you want to view the build logs.');
53
- process.exit(1);
54
- }
55
- const apps = await appsService.findAll({
56
- organizationId,
57
- });
58
- if (apps.length === 0) {
59
- consola.error('You must create an app before viewing build logs.');
60
- process.exit(1);
61
- }
62
- // @ts-ignore wait till https://github.com/unjs/consola/pull/280 is merged
63
- appId = await prompt('Which app do you want to view the build logs for:', {
64
- type: 'select',
65
- options: apps.map((app) => ({ label: app.name, value: app.id })),
66
- });
67
- if (!appId) {
68
- consola.error('You must select an app to view the build logs.');
69
- process.exit(1);
70
- }
34
+ const organizationId = await promptOrganizationSelection();
35
+ appId = await promptAppSelection(organizationId);
71
36
  }
72
37
  // Prompt for platform if not provided
73
38
  if (!buildId) {
@@ -116,5 +81,5 @@ export default defineCommand({
116
81
  }
117
82
  }
118
83
  }
119
- },
84
+ }),
120
85
  });
@@ -1,10 +1,8 @@
1
1
  import appChannelsService from '../../../services/app-channels.js';
2
- import appsService from '../../../services/apps.js';
3
- import authorizationService from '../../../services/authorization-service.js';
4
- import organizationsService from '../../../services/organizations.js';
2
+ import { withAuth } from '../../../utils/auth.js';
5
3
  import { isInteractive } from '../../../utils/environment.js';
6
4
  import { getMessageFromUnknownError } from '../../../utils/error.js';
7
- import { prompt } from '../../../utils/prompt.js';
5
+ import { prompt, promptAppSelection, promptOrganizationSelection } from '../../../utils/prompt.js';
8
6
  import { defineCommand, defineOptions } from '@robingenz/zli';
9
7
  import consola from 'consola';
10
8
  import { z } from 'zod';
@@ -25,12 +23,8 @@ export default defineCommand({
25
23
  name: z.string().optional().describe('Name of the channel.'),
26
24
  protected: z.boolean().optional().describe('Whether to protect the channel or not. Default is `false`.'),
27
25
  })),
28
- action: async (options, args) => {
26
+ action: withAuth(async (options, args) => {
29
27
  let { appId, expiresInDays, ignoreErrors, name, protected: _protected } = options;
30
- if (!authorizationService.hasAuthorizationToken()) {
31
- consola.error('You must be logged in to run this command. Please run the `login` command first.');
32
- process.exit(1);
33
- }
34
28
  // Calculate the expiration date
35
29
  let expiresAt;
36
30
  if (expiresInDays) {
@@ -44,32 +38,8 @@ export default defineCommand({
44
38
  consola.error('You must provide an app ID when running in non-interactive environment.');
45
39
  process.exit(1);
46
40
  }
47
- const organizations = await organizationsService.findAll();
48
- if (organizations.length === 0) {
49
- consola.error('You must create an organization before creating a channel.');
50
- process.exit(1);
51
- }
52
- // @ts-ignore wait till https://github.com/unjs/consola/pull/280 is merged
53
- const organizationId = await prompt('Select the organization of the app for which you want to create a channel.', {
54
- type: 'select',
55
- options: organizations.map((organization) => ({ label: organization.name, value: organization.id })),
56
- });
57
- if (!organizationId) {
58
- consola.error('You must select the organization of an app for which you want to create a channel.');
59
- process.exit(1);
60
- }
61
- const apps = await appsService.findAll({
62
- organizationId,
63
- });
64
- if (!apps.length) {
65
- consola.error('You must create an app before creating a channel.');
66
- process.exit(1);
67
- }
68
- // @ts-ignore wait till https://github.com/unjs/consola/pull/280 is merged
69
- appId = await prompt('Which app do you want to create the channel for?', {
70
- type: 'select',
71
- options: apps.map((app) => ({ label: app.name, value: app.id })),
72
- });
41
+ const organizationId = await promptOrganizationSelection({ allowCreate: true });
42
+ appId = await promptAppSelection(organizationId, { allowCreate: true });
73
43
  }
74
44
  // Validate the channel name
75
45
  if (!name) {
@@ -99,5 +69,5 @@ export default defineCommand({
99
69
  throw error;
100
70
  }
101
71
  }
102
- },
72
+ }),
103
73
  });
@@ -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 { prompt } from '../../../utils/prompt.js';
3
+ import { prompt, promptAppSelection, promptOrganizationSelection } from '../../../utils/prompt.js';
4
4
  import userConfig from '../../../utils/user-config.js';
5
5
  import consola from 'consola';
6
6
  import nock from 'nock';
@@ -17,6 +17,8 @@ vi.mock('@/utils/environment.js', () => ({
17
17
  describe('apps-channels-create', () => {
18
18
  const mockUserConfig = vi.mocked(userConfig);
19
19
  const mockPrompt = vi.mocked(prompt);
20
+ const mockPromptOrganizationSelection = vi.mocked(promptOrganizationSelection);
21
+ const mockPromptAppSelection = vi.mocked(promptAppSelection);
20
22
  const mockConsola = vi.mocked(consola);
21
23
  const mockAuthorizationService = vi.mocked(authorizationService);
22
24
  beforeEach(() => {
@@ -59,15 +61,6 @@ describe('apps-channels-create', () => {
59
61
  const channelId = 'channel-456';
60
62
  const testToken = 'test-token';
61
63
  const options = { name: channelName };
62
- const orgsScope = nock(DEFAULT_API_BASE_URL)
63
- .get('/v1/organizations')
64
- .matchHeader('Authorization', `Bearer ${testToken}`)
65
- .reply(200, [{ id: orgId, name: 'Org 1' }]);
66
- const appsScope = nock(DEFAULT_API_BASE_URL)
67
- .get('/v1/apps')
68
- .query({ organizationId: orgId })
69
- .matchHeader('Authorization', `Bearer ${testToken}`)
70
- .reply(200, [{ id: appId, name: 'App 1' }]);
71
64
  const createScope = nock(DEFAULT_API_BASE_URL)
72
65
  .post(`/v1/apps/${appId}/channels`, {
73
66
  appId,
@@ -76,12 +69,9 @@ describe('apps-channels-create', () => {
76
69
  })
77
70
  .matchHeader('Authorization', `Bearer ${testToken}`)
78
71
  .reply(201, { id: channelId, name: channelName });
79
- mockPrompt
80
- .mockResolvedValueOnce(orgId) // organization selection
81
- .mockResolvedValueOnce(appId); // app selection
72
+ mockPromptOrganizationSelection.mockResolvedValueOnce(orgId);
73
+ mockPromptAppSelection.mockResolvedValueOnce(appId);
82
74
  await createChannelCommand.action(options, undefined);
83
- expect(orgsScope.isDone()).toBe(true);
84
- expect(appsScope.isDone()).toBe(true);
85
75
  expect(createScope.isDone()).toBe(true);
86
76
  expect(mockConsola.success).toHaveBeenCalledWith('Channel created successfully.');
87
77
  });
@@ -1,8 +1,6 @@
1
1
  import appChannelsService from '../../../services/app-channels.js';
2
- import appsService from '../../../services/apps.js';
3
- import authorizationService from '../../../services/authorization-service.js';
4
- import organizationsService from '../../../services/organizations.js';
5
- import { prompt } from '../../../utils/prompt.js';
2
+ import { withAuth } from '../../../utils/auth.js';
3
+ import { prompt, promptAppSelection, promptOrganizationSelection } from '../../../utils/prompt.js';
6
4
  import { defineCommand, defineOptions } from '@robingenz/zli';
7
5
  import consola from 'consola';
8
6
  import { isInteractive } from '../../../utils/environment.js';
@@ -21,43 +19,15 @@ export default defineCommand({
21
19
  .describe('Name of the channel. Either the ID or name of the channel must be provided.'),
22
20
  yes: z.boolean().optional().describe('Skip confirmation prompt.'),
23
21
  }), { y: 'yes' }),
24
- action: async (options, args) => {
22
+ action: withAuth(async (options, args) => {
25
23
  let { appId, channelId, name } = options;
26
- if (!authorizationService.hasAuthorizationToken()) {
27
- consola.error('You must be logged in to run this command. Please run the `login` command first.');
28
- process.exit(1);
29
- }
30
24
  if (!appId) {
31
25
  if (!isInteractive()) {
32
26
  consola.error('You must provide an app ID when running in non-interactive environment.');
33
27
  process.exit(1);
34
28
  }
35
- const organizations = await organizationsService.findAll();
36
- if (organizations.length === 0) {
37
- consola.error('You must create an organization before deleting a channel.');
38
- process.exit(1);
39
- }
40
- // @ts-ignore wait till https://github.com/unjs/consola/pull/280 is merged
41
- const organizationId = await prompt('Select the organization of the app from which you want to delete a channel.', {
42
- type: 'select',
43
- options: organizations.map((organization) => ({ label: organization.name, value: organization.id })),
44
- });
45
- if (!organizationId) {
46
- consola.error('You must select the organization of an app from which you want to delete a channel.');
47
- process.exit(1);
48
- }
49
- const apps = await appsService.findAll({
50
- organizationId,
51
- });
52
- if (!apps.length) {
53
- consola.error('You must create an app before deleting a channel.');
54
- process.exit(1);
55
- }
56
- // @ts-ignore wait till https://github.com/unjs/consola/pull/280 is merged
57
- appId = await prompt('Which app do you want to delete the channel from?', {
58
- type: 'select',
59
- options: apps.map((app) => ({ label: app.name, value: app.id })),
60
- });
29
+ const organizationId = await promptOrganizationSelection();
30
+ appId = await promptAppSelection(organizationId);
61
31
  }
62
32
  // Prompt for channel ID or name if neither is provided
63
33
  if (!channelId && !name) {
@@ -93,5 +63,5 @@ export default defineCommand({
93
63
  name,
94
64
  });
95
65
  consola.success('Channel deleted successfully.');
96
- },
66
+ }),
97
67
  });
@@ -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 { prompt } from '../../../utils/prompt.js';
3
+ import { prompt, promptAppSelection, promptOrganizationSelection } from '../../../utils/prompt.js';
4
4
  import userConfig from '../../../utils/user-config.js';
5
5
  import consola from 'consola';
6
6
  import nock from 'nock';
@@ -17,6 +17,8 @@ vi.mock('@/utils/environment.js', () => ({
17
17
  describe('apps-channels-delete', () => {
18
18
  const mockUserConfig = vi.mocked(userConfig);
19
19
  const mockPrompt = vi.mocked(prompt);
20
+ const mockPromptOrganizationSelection = vi.mocked(promptOrganizationSelection);
21
+ const mockPromptAppSelection = vi.mocked(promptAppSelection);
20
22
  const mockConsola = vi.mocked(consola);
21
23
  const mockAuthorizationService = vi.mocked(authorizationService);
22
24
  beforeEach(() => {
@@ -80,30 +82,16 @@ describe('apps-channels-delete', () => {
80
82
  const appId = 'app-1';
81
83
  const channelName = 'staging';
82
84
  const testToken = 'test-token';
83
- const organization = { id: orgId, name: 'Org 1' };
84
- const app = { id: appId, name: 'App 1' };
85
85
  const options = { name: channelName };
86
- const orgsScope = nock(DEFAULT_API_BASE_URL)
87
- .get('/v1/organizations')
88
- .matchHeader('Authorization', `Bearer ${testToken}`)
89
- .reply(200, [organization]);
90
- const appsScope = nock(DEFAULT_API_BASE_URL)
91
- .get('/v1/apps')
92
- .query({ organizationId: orgId })
93
- .matchHeader('Authorization', `Bearer ${testToken}`)
94
- .reply(200, [app]);
95
86
  const deleteScope = nock(DEFAULT_API_BASE_URL)
96
87
  .delete(`/v1/apps/${appId}/channels`)
97
88
  .query({ name: channelName })
98
89
  .matchHeader('Authorization', `Bearer ${testToken}`)
99
90
  .reply(200);
100
- mockPrompt
101
- .mockResolvedValueOnce(orgId) // organization selection
102
- .mockResolvedValueOnce(appId) // app selection
103
- .mockResolvedValueOnce(true); // confirmation
91
+ mockPromptOrganizationSelection.mockResolvedValueOnce(orgId);
92
+ mockPromptAppSelection.mockResolvedValueOnce(appId);
93
+ mockPrompt.mockResolvedValueOnce(true); // confirmation
104
94
  await deleteChannelCommand.action(options, undefined);
105
- expect(orgsScope.isDone()).toBe(true);
106
- expect(appsScope.isDone()).toBe(true);
107
95
  expect(deleteScope.isDone()).toBe(true);
108
96
  expect(mockConsola.success).toHaveBeenCalledWith('Channel deleted successfully.');
109
97
  });
@@ -1,5 +1,5 @@
1
1
  import appChannelsService from '../../../services/app-channels.js';
2
- import authorizationService from '../../../services/authorization-service.js';
2
+ import { withAuth } from '../../../utils/auth.js';
3
3
  import { defineCommand, defineOptions } from '@robingenz/zli';
4
4
  import consola from 'consola';
5
5
  import { z } from 'zod';
@@ -11,12 +11,8 @@ export default defineCommand({
11
11
  json: z.boolean().optional().describe('Output in JSON format.'),
12
12
  name: z.string().optional().describe('Name of the channel.'),
13
13
  })),
14
- action: async (options, args) => {
14
+ action: withAuth(async (options, args) => {
15
15
  let { appId, channelId, json, name } = options;
16
- if (!authorizationService.hasAuthorizationToken()) {
17
- consola.error('You must be logged in to run this command. Please run the `login` command first.');
18
- process.exit(1);
19
- }
20
16
  if (!appId) {
21
17
  consola.error('You must provide an app ID.');
22
18
  process.exit(1);
@@ -50,5 +46,5 @@ export default defineCommand({
50
46
  console.table(channel);
51
47
  consola.success('Channel retrieved successfully.');
52
48
  }
53
- },
49
+ }),
54
50
  });
@@ -1,9 +1,7 @@
1
1
  import appChannelsService from '../../../services/app-channels.js';
2
- import appsService from '../../../services/apps.js';
3
- import authorizationService from '../../../services/authorization-service.js';
4
- import organizationsService from '../../../services/organizations.js';
2
+ import { withAuth } from '../../../utils/auth.js';
5
3
  import { isInteractive } from '../../../utils/environment.js';
6
- import { prompt } from '../../../utils/prompt.js';
4
+ import { promptAppSelection, promptOrganizationSelection } from '../../../utils/prompt.js';
7
5
  import { defineCommand, defineOptions } from '@robingenz/zli';
8
6
  import consola from 'consola';
9
7
  import { z } from 'zod';
@@ -15,43 +13,15 @@ export default defineCommand({
15
13
  limit: z.coerce.number().optional().describe('Limit for pagination.'),
16
14
  offset: z.coerce.number().optional().describe('Offset for pagination.'),
17
15
  })),
18
- action: async (options, args) => {
16
+ action: withAuth(async (options, args) => {
19
17
  let { appId, json, limit, offset } = options;
20
- if (!authorizationService.hasAuthorizationToken()) {
21
- consola.error('You must be logged in to run this command. Please run the `login` command first.');
22
- process.exit(1);
23
- }
24
18
  if (!appId) {
25
19
  if (!isInteractive()) {
26
20
  consola.error('You must provide an app ID when running in non-interactive environment.');
27
21
  process.exit(1);
28
22
  }
29
- const organizations = await organizationsService.findAll();
30
- if (organizations.length === 0) {
31
- consola.error('You must create an organization before listing channels.');
32
- process.exit(1);
33
- }
34
- // @ts-ignore wait till https://github.com/unjs/consola/pull/280 is merged
35
- const organizationId = await prompt('Select the organization of the app for which you want to list channels.', {
36
- type: 'select',
37
- options: organizations.map((organization) => ({ label: organization.name, value: organization.id })),
38
- });
39
- if (!organizationId) {
40
- consola.error('You must select the organization of an app for which you want to list channels.');
41
- process.exit(1);
42
- }
43
- const apps = await appsService.findAll({
44
- organizationId,
45
- });
46
- if (!apps.length) {
47
- consola.error('You must create an app before listing channels.');
48
- process.exit(1);
49
- }
50
- // @ts-ignore wait till https://github.com/unjs/consola/pull/280 is merged
51
- appId = await prompt('Which app do you want to list the channels for?', {
52
- type: 'select',
53
- options: apps.map((app) => ({ label: app.name, value: app.id })),
54
- });
23
+ const organizationId = await promptOrganizationSelection();
24
+ appId = await promptAppSelection(organizationId);
55
25
  }
56
26
  const foundChannels = await appChannelsService.findAll({
57
27
  appId,
@@ -65,5 +35,5 @@ export default defineCommand({
65
35
  console.table(foundChannels);
66
36
  consola.success('Channels retrieved successfully.');
67
37
  }
68
- },
38
+ }),
69
39
  });