@capawesome/cli 4.1.0-dev.f5ef8ba.1771047437 → 4.2.0

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 +14 -0
  2. package/dist/commands/apps/builds/cancel.js +6 -41
  3. package/dist/commands/apps/builds/create.js +10 -55
  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 +6 -2
package/CHANGELOG.md CHANGED
@@ -2,6 +2,20 @@
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.0](https://github.com/capawesome-team/cli/compare/v4.1.0...v4.2.0) (2026-02-20)
6
+
7
+
8
+ ### Features
9
+
10
+ * 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))
11
+ * add interactive org/app selection prompt ([#117](https://github.com/capawesome-team/cli/issues/117)) ([15e7813](https://github.com/capawesome-team/cli/commit/15e781363e4a8d5eeb25ca39e9d52bc5300e3a35))
12
+
13
+
14
+ ### Bug Fixes
15
+
16
+ * **deps:** add overrides for glob and minimatch versions ([abf0888](https://github.com/capawesome-team/cli/commit/abf08882a6c8938db8d1e9dc2b132d1f44e65888))
17
+ * 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))
18
+
5
19
  ## [4.1.0](https://github.com/capawesome-team/cli/compare/v4.0.5...v4.1.0) (2026-02-12)
6
20
 
7
21
 
@@ -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';
@@ -40,10 +38,6 @@ export default defineCommand({
40
38
  .describe('Exit immediately after creating the build without waiting for completion.'),
41
39
  environment: z.string().optional().describe('The name of the environment to use for the build.'),
42
40
  gitRef: z.string().optional().describe('The Git reference (branch, tag, or commit SHA) to build.'),
43
- sourceUrl: z
44
- .string()
45
- .optional()
46
- .describe('The URL to a hosted zip file containing the source code for the build.'),
47
41
  ipa: z
48
42
  .union([z.boolean(), z.string()])
49
43
  .optional()
@@ -71,59 +65,21 @@ export default defineCommand({
71
65
  .describe('Download the generated ZIP file (Web only). Optionally provide a file path.'),
72
66
  yes: z.boolean().optional().describe('Skip confirmation prompts.'),
73
67
  }), { y: 'yes' }),
74
- action: async (options) => {
75
- let { appId, platform, type, gitRef, sourceUrl, environment, certificate, json, stack } = options;
76
- // Check if the user is logged in
77
- if (!authorizationService.hasAuthorizationToken()) {
78
- consola.error('You must be logged in to run this command. Please run the `login` command first.');
79
- process.exit(1);
80
- }
68
+ action: withAuth(async (options) => {
69
+ let { appId, platform, type, gitRef, environment, certificate, json, stack } = options;
81
70
  // Validate that detached flag cannot be used with artifact flags
82
71
  if (options.detached && (options.apk || options.aab || options.ipa || options.zip)) {
83
72
  consola.error('The --detached flag cannot be used with --apk, --aab, --ipa, or --zip flags.');
84
73
  process.exit(1);
85
74
  }
86
- // Validate that gitRef and sourceUrl are mutually exclusive
87
- if (gitRef && sourceUrl) {
88
- consola.error('The --gitRef and --sourceUrl flags are mutually exclusive. Please provide only one.');
89
- process.exit(1);
90
- }
91
75
  // Prompt for app ID if not provided
92
76
  if (!appId) {
93
77
  if (!isInteractive()) {
94
78
  consola.error('You must provide an app ID when running in non-interactive environment.');
95
79
  process.exit(1);
96
80
  }
97
- const organizations = await organizationsService.findAll();
98
- if (organizations.length === 0) {
99
- consola.error('You must create an organization before creating a build.');
100
- process.exit(1);
101
- }
102
- // @ts-ignore wait till https://github.com/unjs/consola/pull/280 is merged
103
- const organizationId = await prompt('Select the organization of the app for which you want to create a build.', {
104
- type: 'select',
105
- options: organizations.map((organization) => ({ label: organization.name, value: organization.id })),
106
- });
107
- if (!organizationId) {
108
- consola.error('You must select the organization of an app for which you want to create a build.');
109
- process.exit(1);
110
- }
111
- const apps = await appsService.findAll({
112
- organizationId,
113
- });
114
- if (apps.length === 0) {
115
- consola.error('You must create an app before creating a build.');
116
- process.exit(1);
117
- }
118
- // @ts-ignore wait till https://github.com/unjs/consola/pull/280 is merged
119
- appId = await prompt('Which app do you want to create a build for:', {
120
- type: 'select',
121
- options: apps.map((app) => ({ label: app.name, value: app.id })),
122
- });
123
- if (!appId) {
124
- consola.error('You must select an app to create a build for.');
125
- process.exit(1);
126
- }
81
+ const organizationId = await promptOrganizationSelection({ allowCreate: true });
82
+ appId = await promptAppSelection(organizationId, { allowCreate: true });
127
83
  }
128
84
  // Prompt for platform if not provided
129
85
  if (!platform) {
@@ -145,10 +101,10 @@ export default defineCommand({
145
101
  process.exit(1);
146
102
  }
147
103
  }
148
- // Prompt for git ref if not provided and sourceUrl is not set
149
- if (!gitRef && !sourceUrl) {
104
+ // Prompt for git ref if not provided
105
+ if (!gitRef) {
150
106
  if (!isInteractive()) {
151
- consola.error('You must provide a git ref or source URL when running in non-interactive environment.');
107
+ consola.error('You must provide a git ref when running in non-interactive environment.');
152
108
  process.exit(1);
153
109
  }
154
110
  gitRef = await prompt('Enter the Git reference (branch, tag, or commit SHA):', {
@@ -228,7 +184,6 @@ export default defineCommand({
228
184
  stack,
229
185
  gitRef,
230
186
  platform,
231
- sourceUrl,
232
187
  type,
233
188
  });
234
189
  consola.info(`Build ID: ${response.id}`);
@@ -376,7 +331,7 @@ export default defineCommand({
376
331
  consola.success(`Build completed successfully.`);
377
332
  }
378
333
  }
379
- },
334
+ }),
380
335
  });
381
336
  /**
382
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
  });