@cakemail-org/cakemail-cli 1.5.0 → 2.0.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 (234) hide show
  1. package/.claude/settings.local.json +12 -0
  2. package/.env.example +40 -0
  3. package/.env.test.example +45 -0
  4. package/CHANGELOG.md +1031 -0
  5. package/README.md +319 -15
  6. package/audit-formats.js +128 -0
  7. package/cakemail.rb +20 -0
  8. package/dist/cli.js +27 -10
  9. package/dist/cli.js.map +1 -1
  10. package/dist/client.d.ts +2 -0
  11. package/dist/client.d.ts.map +1 -1
  12. package/dist/client.js +16 -6
  13. package/dist/client.js.map +1 -1
  14. package/dist/commands/account.js +1 -1
  15. package/dist/commands/account.js.map +1 -1
  16. package/dist/commands/attributes.js +1 -1
  17. package/dist/commands/attributes.js.map +1 -1
  18. package/dist/commands/campaigns.d.ts.map +1 -1
  19. package/dist/commands/campaigns.js +103 -8
  20. package/dist/commands/campaigns.js.map +1 -1
  21. package/dist/commands/config.d.ts.map +1 -1
  22. package/dist/commands/config.js +63 -4
  23. package/dist/commands/config.js.map +1 -1
  24. package/dist/commands/contacts.d.ts.map +1 -1
  25. package/dist/commands/contacts.js +91 -12
  26. package/dist/commands/contacts.js.map +1 -1
  27. package/dist/commands/emails.js +1 -1
  28. package/dist/commands/emails.js.map +1 -1
  29. package/dist/commands/interests.d.ts +5 -0
  30. package/dist/commands/interests.d.ts.map +1 -0
  31. package/dist/commands/interests.js +172 -0
  32. package/dist/commands/interests.js.map +1 -0
  33. package/dist/commands/lists.d.ts.map +1 -1
  34. package/dist/commands/lists.js +6 -8
  35. package/dist/commands/lists.js.map +1 -1
  36. package/dist/commands/logs.d.ts +5 -0
  37. package/dist/commands/logs.d.ts.map +1 -0
  38. package/dist/commands/logs.js +237 -0
  39. package/dist/commands/logs.js.map +1 -0
  40. package/dist/commands/reports.js +1 -1
  41. package/dist/commands/reports.js.map +1 -1
  42. package/dist/commands/segments.js +1 -1
  43. package/dist/commands/segments.js.map +1 -1
  44. package/dist/commands/senders.d.ts.map +1 -1
  45. package/dist/commands/senders.js +11 -8
  46. package/dist/commands/senders.js.map +1 -1
  47. package/dist/commands/suppressed.js +1 -1
  48. package/dist/commands/suppressed.js.map +1 -1
  49. package/dist/commands/tags.d.ts +5 -0
  50. package/dist/commands/tags.d.ts.map +1 -0
  51. package/dist/commands/tags.js +124 -0
  52. package/dist/commands/tags.js.map +1 -0
  53. package/dist/commands/templates.js +1 -1
  54. package/dist/commands/templates.js.map +1 -1
  55. package/dist/commands/transactional-templates.d.ts +5 -0
  56. package/dist/commands/transactional-templates.d.ts.map +1 -0
  57. package/dist/commands/transactional-templates.js +354 -0
  58. package/dist/commands/transactional-templates.js.map +1 -0
  59. package/dist/commands/webhooks.js +1 -1
  60. package/dist/commands/webhooks.js.map +1 -1
  61. package/dist/utils/auth.d.ts +8 -1
  62. package/dist/utils/auth.d.ts.map +1 -1
  63. package/dist/utils/auth.js +39 -11
  64. package/dist/utils/auth.js.map +1 -1
  65. package/dist/utils/config-file.d.ts +7 -0
  66. package/dist/utils/config-file.d.ts.map +1 -1
  67. package/dist/utils/config-file.js +15 -0
  68. package/dist/utils/config-file.js.map +1 -1
  69. package/dist/utils/config.d.ts +2 -0
  70. package/dist/utils/config.d.ts.map +1 -1
  71. package/dist/utils/config.js +12 -4
  72. package/dist/utils/config.js.map +1 -1
  73. package/dist/utils/errors.js +1 -1
  74. package/dist/utils/errors.js.map +1 -1
  75. package/dist/utils/list-defaults.d.ts +33 -0
  76. package/dist/utils/list-defaults.d.ts.map +1 -0
  77. package/dist/utils/list-defaults.js +52 -0
  78. package/dist/utils/list-defaults.js.map +1 -0
  79. package/dist/utils/output.d.ts.map +1 -1
  80. package/dist/utils/output.js +36 -13
  81. package/dist/utils/output.js.map +1 -1
  82. package/dist/utils/progress.d.ts.map +1 -1
  83. package/dist/utils/progress.js +32 -4
  84. package/dist/utils/progress.js.map +1 -1
  85. package/dist/utils/spinner.d.ts +17 -0
  86. package/dist/utils/spinner.d.ts.map +1 -0
  87. package/dist/utils/spinner.js +43 -0
  88. package/dist/utils/spinner.js.map +1 -0
  89. package/docs/DOCUMENTATION-STANDARD.md +1068 -0
  90. package/docs/README.md +161 -0
  91. package/docs/developer/ARCHITECTURE.md +516 -0
  92. package/docs/developer/AUTH.md +204 -0
  93. package/docs/developer/CONTRIBUTING.md +227 -0
  94. package/docs/developer/DOCUMENTATION_SUMMARY.md +346 -0
  95. package/docs/developer/PROJECT_INDEX.md +365 -0
  96. package/docs/planning/API_COVERAGE.md +1045 -0
  97. package/docs/planning/BACKLOG.md +1159 -0
  98. package/docs/planning/PROFILE_SYSTEM_TASKS.md +287 -0
  99. package/docs/planning/UX_IMPLEMENTATION_PLAN.md +691 -0
  100. package/docs/planning/archive/RELEASE_CHECKLIST_v1.3.0.md +332 -0
  101. package/docs/planning/archive/RELEASE_v1.3.0.md +428 -0
  102. package/docs/planning/archive/cakemail-cli-ux-improvements.md +438 -0
  103. package/docs/planning/cakemail-profile-system-plan.md +1121 -0
  104. package/docs/testing/AI_USER_SIMULATION_DESIGN.md +1342 -0
  105. package/docs/testing/KENOGAMI_BIDIRECTIONAL_FLOW.md +1517 -0
  106. package/docs/testing/KENOGAMI_TRUTH_RECONCILIATION_SYSTEM.md +1369 -0
  107. package/docs/user-manual/.obsidian/app.json +1 -0
  108. package/docs/user-manual/.obsidian/appearance.json +1 -0
  109. package/docs/user-manual/.obsidian/core-plugins.json +33 -0
  110. package/docs/user-manual/.obsidian/workspace.json +167 -0
  111. package/docs/user-manual/01-getting-started/01-installation.md +214 -0
  112. package/docs/user-manual/01-getting-started/02-quick-start.md +432 -0
  113. package/docs/user-manual/01-getting-started/03-authentication.md +448 -0
  114. package/docs/user-manual/01-getting-started/04-configuration.md +430 -0
  115. package/docs/user-manual/01-getting-started/05-output-formats.md +447 -0
  116. package/docs/user-manual/02-core-concepts/01-accounts.md +514 -0
  117. package/docs/user-manual/02-core-concepts/02-profile-system.md +771 -0
  118. package/docs/user-manual/02-core-concepts/03-smart-defaults.md +485 -0
  119. package/docs/user-manual/02-core-concepts/04-authentication-methods.md +435 -0
  120. package/docs/user-manual/02-core-concepts/05-pagination-filtering.md +600 -0
  121. package/docs/user-manual/02-core-concepts/06-error-handling.md +718 -0
  122. package/docs/user-manual/02-core-concepts/07-api-coverage.md +483 -0
  123. package/docs/user-manual/03-email-operations/01-senders.md +490 -0
  124. package/docs/user-manual/03-email-operations/02-templates.md +444 -0
  125. package/docs/user-manual/03-email-operations/03-transactional-emails.md +706 -0
  126. package/docs/user-manual/03-email-operations/04-email-tracking.md +407 -0
  127. package/docs/user-manual/04-campaign-management/01-campaigns-basics.md +394 -0
  128. package/docs/user-manual/04-campaign-management/02-campaign-scheduling.md +630 -0
  129. package/docs/user-manual/04-campaign-management/03-campaign-testing.md +997 -0
  130. package/docs/user-manual/04-campaign-management/04-campaign-lifecycle.md +709 -0
  131. package/docs/user-manual/04-campaign-management/05-campaign-links.md +934 -0
  132. package/docs/user-manual/05-contact-management/01-lists.md +836 -0
  133. package/docs/user-manual/05-contact-management/02-contacts.md +1035 -0
  134. package/docs/user-manual/05-contact-management/03-custom-attributes.md +788 -0
  135. package/docs/user-manual/05-contact-management/04-segments.md +1028 -0
  136. package/docs/user-manual/05-contact-management/05-contact-import-export.md +1031 -0
  137. package/docs/user-manual/06-analytics-reporting/01-campaign-analytics.md +867 -0
  138. package/docs/user-manual/06-analytics-reporting/02-account-reports.md +227 -0
  139. package/docs/user-manual/07-integrations/01-webhooks-integration.md +259 -0
  140. package/docs/user-manual/07-integrations/02-automation.md +326 -0
  141. package/docs/user-manual/08-advanced-usage/01-scripting-patterns.md +672 -0
  142. package/docs/user-manual/08-advanced-usage/02-bulk-operations.md +932 -0
  143. package/docs/user-manual/08-advanced-usage/03-ci-cd-integration.md +892 -0
  144. package/docs/user-manual/08-advanced-usage/04-performance-optimization.md +766 -0
  145. package/docs/user-manual/09-command-reference/01-config.md +776 -0
  146. package/docs/user-manual/09-command-reference/02-account.md +652 -0
  147. package/docs/user-manual/09-command-reference/03-lists.md +958 -0
  148. package/docs/user-manual/09-command-reference/04-contacts.md +1408 -0
  149. package/docs/user-manual/09-command-reference/05-attributes.md +617 -0
  150. package/docs/user-manual/09-command-reference/06-segments.md +894 -0
  151. package/docs/user-manual/09-command-reference/07-senders.md +803 -0
  152. package/docs/user-manual/09-command-reference/08-templates.md +818 -0
  153. package/docs/user-manual/09-command-reference/09-campaigns.md +1250 -0
  154. package/docs/user-manual/09-command-reference/10-emails.md +807 -0
  155. package/docs/user-manual/09-command-reference/11-reports.md +1135 -0
  156. package/docs/user-manual/09-command-reference/12-webhooks.md +773 -0
  157. package/docs/user-manual/09-command-reference/13-suppressed.md +797 -0
  158. package/docs/user-manual/09-command-reference/14-interests.md +630 -0
  159. package/docs/user-manual/09-command-reference/15-tags.md +584 -0
  160. package/docs/user-manual/09-command-reference/16-logs.md +656 -0
  161. package/docs/user-manual/09-command-reference/17-transactional-templates.md +850 -0
  162. package/docs/user-manual/10-troubleshooting/01-common-errors.md +457 -0
  163. package/docs/user-manual/10-troubleshooting/02-authentication-issues.md +558 -0
  164. package/docs/user-manual/10-troubleshooting/03-connection-problems.md +634 -0
  165. package/docs/user-manual/10-troubleshooting/04-debugging.md +725 -0
  166. package/docs/user-manual/11-appendix/04-faq.md +484 -0
  167. package/docs/user-manual/11-appendix/05-glossary.md +250 -0
  168. package/docs/user-manual/README.md +0 -0
  169. package/package.json +13 -47
  170. package/src/cli.ts +125 -0
  171. package/src/client.ts +16 -0
  172. package/src/commands/account.ts +267 -0
  173. package/src/commands/accounts.ts +78 -0
  174. package/src/commands/actions.ts +249 -0
  175. package/src/commands/attributes.ts +139 -0
  176. package/src/commands/campaign-blueprints.ts +106 -0
  177. package/src/commands/campaigns.ts +469 -0
  178. package/src/commands/config.ts +77 -0
  179. package/src/commands/contacts.ts +612 -0
  180. package/src/commands/custom-attributes.ts +127 -0
  181. package/src/commands/dkims.ts +117 -0
  182. package/src/commands/domains.ts +82 -0
  183. package/src/commands/email-apis.ts +569 -0
  184. package/src/commands/emails.ts +197 -0
  185. package/src/commands/forms.ts +283 -0
  186. package/src/commands/interests.ts +155 -0
  187. package/src/commands/links.ts +38 -0
  188. package/src/commands/lists.ts +406 -0
  189. package/src/commands/logos.ts +71 -0
  190. package/src/commands/logs.ts +386 -0
  191. package/src/commands/reports.ts +306 -0
  192. package/src/commands/segments.ts +158 -0
  193. package/src/commands/senders.ts +204 -0
  194. package/src/commands/sub-accounts.ts +271 -0
  195. package/src/commands/suppressed-emails.ts +234 -0
  196. package/src/commands/suppressed.ts +198 -0
  197. package/src/commands/system-emails.ts +85 -0
  198. package/src/commands/tags.ts +146 -0
  199. package/src/commands/tasks.ts +116 -0
  200. package/src/commands/templates.ts +189 -0
  201. package/src/commands/tokens.ts +83 -0
  202. package/src/commands/transactional-emails.ts +374 -0
  203. package/src/commands/transactional-templates.ts +385 -0
  204. package/src/commands/users.ts +506 -0
  205. package/src/commands/webhooks.ts +172 -0
  206. package/src/commands/workflow-blueprints.ts +123 -0
  207. package/src/commands/workflows.ts +265 -0
  208. package/src/types/profile.ts +93 -0
  209. package/src/utils/auth.ts +272 -0
  210. package/src/utils/config-file.ts +96 -0
  211. package/src/utils/config.ts +134 -0
  212. package/src/utils/confirm.ts +32 -0
  213. package/src/utils/defaults.ts +99 -0
  214. package/src/utils/errors.ts +116 -0
  215. package/src/utils/interactive.ts +91 -0
  216. package/src/utils/list-defaults.ts +74 -0
  217. package/src/utils/output.ts +190 -0
  218. package/src/utils/progress.ts +320 -0
  219. package/src/utils/spinner.ts +22 -0
  220. package/tests/IMPLEMENTATION_STATUS.md +258 -0
  221. package/tests/PTY_SETUP.md +118 -0
  222. package/tests/PTY_TESTING_GUIDE.md +507 -0
  223. package/tests/README.md +244 -0
  224. package/tests/fixtures/api-responses/campaigns.json +34 -0
  225. package/tests/fixtures/test-config.json +13 -0
  226. package/tests/helpers/cli-runner.ts +128 -0
  227. package/tests/helpers/mock-server.ts +301 -0
  228. package/tests/helpers/pty-runner.ts +181 -0
  229. package/tests/integration/campaigns-real-api.test.ts +196 -0
  230. package/tests/integration/setup-integration.ts +50 -0
  231. package/tests/pty/campaigns.test.ts +241 -0
  232. package/tests/setup.ts +34 -0
  233. package/tsconfig.json +15 -0
  234. package/vitest.config.ts +28 -0
@@ -0,0 +1,249 @@
1
+ // ABOUTME: CLI commands for Action.
2
+ // ABOUTME: Generated by api-kit from the OpenAPI spec.
3
+
4
+ import { Command } from 'commander';
5
+ import { createSpinner } from '../utils/spinner.js';
6
+ import { confirmDelete } from '../utils/confirm.js';
7
+
8
+ export function createActionsCommand(
9
+ client: any,
10
+ formatter: any
11
+ ): Command {
12
+ const cmd = new Command('actions')
13
+ .description('Action');
14
+
15
+
16
+ cmd
17
+ .command('list <workflow-id>')
18
+ .description('List actions')
19
+ .option('--account-id <value>', 'Optional Account ID to be used for the request')
20
+ .option('--page <value>', 'page')
21
+ .option('--per-page <value>', 'per_page')
22
+ .option('--with-count <value>', 'Include count in the response')
23
+ .option('--sort <value>', 'Sort term and direction, using syntax `[-|+]term`.
24
+
25
+ Valid terms:
26
+ - `name`
27
+ - `created_on`')
28
+ .action(async (workflow_id, options) => {
29
+ const spinner = createSpinner('Fetching action...').start();
30
+ try {
31
+ const data = await client.listActions({
32
+ workflow_id,
33
+ accountId: options.accountId != null ? Number(options.accountId) : undefined,
34
+ page: options.page != null ? Number(options.page) : undefined,
35
+ perPage: options.perPage != null ? Number(options.perPage) : undefined,
36
+ withCount: options.withCount,
37
+ sort: options.sort,
38
+ });
39
+
40
+ spinner.stop();
41
+ formatter.output(data);
42
+ } catch (error: any) {
43
+ spinner.stop();
44
+ formatter.error(error.message);
45
+ process.exit(1);
46
+ }
47
+ });
48
+
49
+
50
+ cmd
51
+ .command('create <workflow-id>')
52
+ .description('Create an action')
53
+ .option('--account-id <value>', 'Optional Account ID to be used for the request')
54
+ .option('--lock-key <value>', 'Locking key, preventing other users from modifying this workflow for a short period.')
55
+ .requiredOption('--data <value>', 'Request body as JSON string')
56
+ .action(async (workflow_id, options) => {
57
+ const spinner = createSpinner('Creating action...').start();
58
+ try {
59
+ const body = JSON.parse(options.data);
60
+ const data = await client.createAction({
61
+ workflow_id,
62
+ ...body,
63
+ });
64
+
65
+ spinner.stop();
66
+ formatter.success('Action created successfully');
67
+ formatter.output(data);
68
+ } catch (error: any) {
69
+ spinner.stop();
70
+ formatter.error(error.message);
71
+ process.exit(1);
72
+ }
73
+ });
74
+
75
+
76
+ cmd
77
+ .command('get <workflow-id> <action-id>')
78
+ .description('Get an action')
79
+ .option('--account-id <value>', 'Optional Account ID to be used for the request')
80
+ .option('--lock-key <value>', 'Locking key, preventing other users from modifying this workflow for a short period.')
81
+ .action(async (workflow_id, action_id, options) => {
82
+ const spinner = createSpinner('Fetching action...').start();
83
+ try {
84
+ const data = await client.getAction({
85
+ workflow_id,
86
+ action_id,
87
+ accountId: options.accountId != null ? Number(options.accountId) : undefined,
88
+ lockKey: options.lockKey,
89
+ });
90
+
91
+ spinner.stop();
92
+ formatter.output(data);
93
+ } catch (error: any) {
94
+ spinner.stop();
95
+ formatter.error(error.message);
96
+ process.exit(1);
97
+ }
98
+ });
99
+
100
+
101
+ cmd
102
+ .command('patch <action-id> <workflow-id>')
103
+ .description('Update an action')
104
+ .option('--account-id <value>', 'Optional Account ID to be used for the request')
105
+ .option('--lock-key <value>', 'Locking key, preventing other users from modifying this workflow for a short period.')
106
+ .requiredOption('--data <value>', 'Request body as JSON string')
107
+ .action(async (action_id, workflow_id, options) => {
108
+ const spinner = createSpinner('Updating action...').start();
109
+ try {
110
+ const body = JSON.parse(options.data);
111
+ const data = await client.patchAction({
112
+ action_id,
113
+ workflow_id,
114
+ ...body,
115
+ });
116
+
117
+ spinner.stop();
118
+ formatter.success('Action updated successfully');
119
+ formatter.output(data);
120
+ } catch (error: any) {
121
+ spinner.stop();
122
+ formatter.error(error.message);
123
+ process.exit(1);
124
+ }
125
+ });
126
+
127
+
128
+ cmd
129
+ .command('delete <action-id> <workflow-id>')
130
+ .description('Delete an action')
131
+ .option('--account-id <value>', 'Optional Account ID to be used for the request')
132
+ .option('--lock-key <value>', 'Locking key, preventing other users from modifying this workflow for a short period.')
133
+ .option('-f, --force', 'Skip confirmation prompt')
134
+ .action(async (action_id, workflow_id, options) => {
135
+ if (!options.force) {
136
+ const confirmed = await confirmDelete('action', action_id);
137
+ if (!confirmed) {
138
+ formatter.info('Deletion cancelled');
139
+ return;
140
+ }
141
+ }
142
+
143
+ const spinner = createSpinner('Deleting action...').start();
144
+ try {
145
+ const data = await client.deleteAction({
146
+ action_id,
147
+ workflow_id,
148
+ accountId: options.accountId != null ? Number(options.accountId) : undefined,
149
+ lockKey: options.lockKey,
150
+ });
151
+
152
+ spinner.stop();
153
+ formatter.success('Action deleted successfully');
154
+ } catch (error: any) {
155
+ spinner.stop();
156
+ formatter.error(error.message);
157
+ process.exit(1);
158
+ }
159
+ });
160
+
161
+
162
+ cmd
163
+ .command('render <workflow-id> <action-id>')
164
+ .description('Render an action')
165
+ .option('--contact-id <value>', 'contact_id')
166
+ .option('--account-id <value>', 'account_id')
167
+ .option('--lock-key <value>', 'Locking key, preventing other users from modifying this workflow for a short period.')
168
+ .action(async (workflow_id, action_id, options) => {
169
+ const spinner = createSpinner('Fetching action...').start();
170
+ try {
171
+ const data = await client.renderAction({
172
+ workflow_id,
173
+ action_id,
174
+ contactId: options.contactId != null ? Number(options.contactId) : undefined,
175
+ accountId: options.accountId != null ? Number(options.accountId) : undefined,
176
+ lockKey: options.lockKey,
177
+ });
178
+
179
+ spinner.stop();
180
+ formatter.output(data);
181
+ } catch (error: any) {
182
+ spinner.stop();
183
+ formatter.error(error.message);
184
+ process.exit(1);
185
+ }
186
+ });
187
+
188
+
189
+ cmd
190
+ .command('send-test <workflow-id> <action-id>')
191
+ .description('Send a test of an action')
192
+ .option('--account-id <value>', 'Optional Account ID to be used for the request')
193
+ .option('--lock-key <value>', 'Locking key, preventing other users from modifying this workflow for a short period.')
194
+ .requiredOption('--email <value>', 'email')
195
+ .option('--type <value>', 'type')
196
+ .action(async (workflow_id, action_id, options) => {
197
+ const spinner = createSpinner('Creating action...').start();
198
+ try {
199
+ const data = await client.sendTestAction({
200
+ workflow_id,
201
+ action_id,
202
+ accountId: options.accountId != null ? Number(options.accountId) : undefined,
203
+ lockKey: options.lockKey,
204
+ email: options.email,
205
+ type: options.type,
206
+ });
207
+
208
+ spinner.stop();
209
+ formatter.success('Action created successfully');
210
+ formatter.output(data);
211
+ } catch (error: any) {
212
+ spinner.stop();
213
+ formatter.error(error.message);
214
+ process.exit(1);
215
+ }
216
+ });
217
+
218
+
219
+ cmd
220
+ .command('list-action-links <action-id> <workflow-id>')
221
+ .description('Show action links')
222
+ .option('--account-id <value>', 'Optional Account ID to be used for the request')
223
+ .option('--page <value>', 'page')
224
+ .option('--per-page <value>', 'per_page')
225
+ .option('--with-count <value>', 'Include count in the response')
226
+ .action(async (action_id, workflow_id, options) => {
227
+ const spinner = createSpinner('Fetching action...').start();
228
+ try {
229
+ const data = await client.listActionLinks({
230
+ action_id,
231
+ workflow_id,
232
+ accountId: options.accountId != null ? Number(options.accountId) : undefined,
233
+ page: options.page != null ? Number(options.page) : undefined,
234
+ perPage: options.perPage != null ? Number(options.perPage) : undefined,
235
+ withCount: options.withCount,
236
+ });
237
+
238
+ spinner.stop();
239
+ formatter.output(data);
240
+ } catch (error: any) {
241
+ spinner.stop();
242
+ formatter.error(error.message);
243
+ process.exit(1);
244
+ }
245
+ });
246
+
247
+
248
+ return cmd;
249
+ }
@@ -0,0 +1,139 @@
1
+ import { Command } from 'commander';
2
+ import { CakemailClient } from '../client.js';
3
+ import { OutputFormatter } from '../utils/output.js';
4
+ import ora from '../utils/spinner.js';
5
+ import { confirmDelete } from '../utils/confirm.js';
6
+ import { autoDetectList } from '../utils/defaults.js';
7
+
8
+ export function createAttributesCommand(client: CakemailClient, formatter: OutputFormatter): Command {
9
+ const attributes = new Command('attributes')
10
+ .description('Manage custom attributes (custom fields)');
11
+
12
+ // List attributes
13
+ attributes
14
+ .command('list [list-id]')
15
+ .description('List all custom attributes for a list (auto-detects if only one list exists)')
16
+ .action(async (listId) => {
17
+ // Auto-detect list ID if not provided
18
+ const detectedListId = await autoDetectList(client, formatter, listId, { useCache: true });
19
+
20
+ if (!detectedListId) {
21
+ process.exit(1);
22
+ }
23
+
24
+ const spinner = ora('Fetching custom attributes...').start();
25
+ try {
26
+ const data = await client.sdk.customAttributeService.listCustomAttributes({
27
+ listId: detectedListId
28
+ });
29
+ spinner.stop();
30
+ formatter.output(data);
31
+ } catch (error: any) {
32
+ spinner.stop();
33
+ formatter.error(error.message);
34
+ process.exit(1);
35
+ }
36
+ });
37
+
38
+ // Get attribute
39
+ attributes
40
+ .command('get [list-id] <name>')
41
+ .description('Get custom attribute details (auto-detects if only one list exists)')
42
+ .action(async (listId, name) => {
43
+ // Auto-detect list ID if not provided
44
+ const detectedListId = await autoDetectList(client, formatter, listId, { useCache: true });
45
+
46
+ if (!detectedListId) {
47
+ process.exit(1);
48
+ }
49
+
50
+ const spinner = ora(`Fetching attribute ${name}...`).start();
51
+ try {
52
+ const data = await client.sdk.customAttributeService.getCustomAttribute({
53
+ listId: detectedListId,
54
+ name
55
+ });
56
+ spinner.stop();
57
+ formatter.output(data);
58
+ } catch (error: any) {
59
+ spinner.stop();
60
+ formatter.error(error.message);
61
+ process.exit(1);
62
+ }
63
+ });
64
+
65
+ // Create attribute
66
+ attributes
67
+ .command('create [list-id]')
68
+ .description('Create a new custom attribute (auto-detects if only one list exists)')
69
+ .requiredOption('-n, --name <name>', 'Attribute name')
70
+ .requiredOption('-t, --type <type>', 'Attribute type (text, number, date, boolean)')
71
+ .action(async (listId, options) => {
72
+ // Auto-detect list ID if not provided
73
+ const detectedListId = await autoDetectList(client, formatter, listId, { useCache: true });
74
+
75
+ if (!detectedListId) {
76
+ process.exit(1);
77
+ }
78
+
79
+ const spinner = ora('Creating custom attribute...').start();
80
+ try {
81
+ const data = await client.sdk.customAttributeService.createCustomAttribute({
82
+ listId: detectedListId,
83
+ requestBody: {
84
+ name: options.name,
85
+ type: options.type
86
+ }
87
+ });
88
+ spinner.stop();
89
+ formatter.success(`Custom attribute created: ${options.name}`);
90
+ formatter.output(data);
91
+ } catch (error: any) {
92
+ spinner.stop();
93
+ formatter.error(error.message);
94
+ process.exit(1);
95
+ }
96
+ });
97
+
98
+ // Delete attribute
99
+ attributes
100
+ .command('delete [list-id] <name>')
101
+ .description('Delete a custom attribute (auto-detects if only one list exists)')
102
+ .option('-f, --force', 'Skip confirmation prompt')
103
+ .action(async (listId, name, options) => {
104
+ // Auto-detect list ID if not provided
105
+ const detectedListId = await autoDetectList(client, formatter, listId, { useCache: true });
106
+
107
+ if (!detectedListId) {
108
+ process.exit(1);
109
+ }
110
+
111
+ // Interactive confirmation (unless --force is used)
112
+ if (!options.force) {
113
+ const confirmed = await confirmDelete('custom attribute', name, [
114
+ 'Attribute and all its data will be deleted from all contacts'
115
+ ]);
116
+
117
+ if (!confirmed) {
118
+ formatter.info('Deletion cancelled');
119
+ return;
120
+ }
121
+ }
122
+
123
+ const spinner = ora(`Deleting attribute ${name}...`).start();
124
+ try {
125
+ await client.sdk.customAttributeService.deleteCustomAttribute({
126
+ listId: detectedListId,
127
+ name
128
+ });
129
+ spinner.stop();
130
+ formatter.success(`Attribute ${name} deleted`);
131
+ } catch (error: any) {
132
+ spinner.stop();
133
+ formatter.error(error.message);
134
+ process.exit(1);
135
+ }
136
+ });
137
+
138
+ return attributes;
139
+ }
@@ -0,0 +1,106 @@
1
+ // ABOUTME: CLI commands for Campaign Blueprint.
2
+ // ABOUTME: Generated by api-kit from the OpenAPI spec.
3
+
4
+ import { Command } from 'commander';
5
+ import { createSpinner } from '../utils/spinner.js';
6
+
7
+ export function createCampaignBlueprintsCommand(
8
+ client: any,
9
+ formatter: any
10
+ ): Command {
11
+ const cmd = new Command('campaign-blueprints')
12
+ .description('Browse pre-built campaign blueprints. Blueprints are ready-made campaign designs that can be used as starting points for new campaigns.');
13
+
14
+
15
+ cmd
16
+ .command('list-campaign-blueprints')
17
+ .description('Show all campaign blueprints')
18
+ .option('--account-id <value>', 'account_id')
19
+ .option('--page <value>', 'page')
20
+ .option('--per-page <value>', 'per_page')
21
+ .option('--with-count <value>', 'Include count in the response')
22
+ .option('--filter <value>', 'Valid Terms:
23
+ - `tag`
24
+ - `not_tag`
25
+ - `name`
26
+ - `is_owner`
27
+ - `is_not_owner`
28
+
29
+ Valid Operators:
30
+ - `==`
31
+
32
+ Query separator:
33
+ - `;`')
34
+ .option('--sort <value>', 'Sort term and direction, using syntax `[-|+]term`.
35
+
36
+ Valid terms:
37
+ - `id`
38
+ - `created_on`
39
+ - `updated_on`
40
+ - `name`')
41
+ .action(async (options) => {
42
+ const spinner = createSpinner('Fetching campaign blueprint...').start();
43
+ try {
44
+ const data = await client.listCampaignBlueprints({
45
+ accountId: options.accountId != null ? Number(options.accountId) : undefined,
46
+ page: options.page != null ? Number(options.page) : undefined,
47
+ perPage: options.perPage != null ? Number(options.perPage) : undefined,
48
+ withCount: options.withCount,
49
+ filter: options.filter,
50
+ sort: options.sort,
51
+ });
52
+
53
+ spinner.stop();
54
+ formatter.output(data);
55
+ } catch (error: any) {
56
+ spinner.stop();
57
+ formatter.error(error.message);
58
+ process.exit(1);
59
+ }
60
+ });
61
+
62
+
63
+ cmd
64
+ .command('get-campaign-blueprint <blueprint-id>')
65
+ .description('Get a campaign blueprint')
66
+ .option('--account-id <value>', 'account_id')
67
+ .action(async (blueprint_id, options) => {
68
+ const spinner = createSpinner('Fetching campaign blueprint...').start();
69
+ try {
70
+ const data = await client.getCampaignBlueprint({
71
+ blueprint_id: Number(blueprint_id),
72
+ accountId: options.accountId != null ? Number(options.accountId) : undefined,
73
+ });
74
+
75
+ spinner.stop();
76
+ formatter.output(data);
77
+ } catch (error: any) {
78
+ spinner.stop();
79
+ formatter.error(error.message);
80
+ process.exit(1);
81
+ }
82
+ });
83
+
84
+
85
+ cmd
86
+ .command('render-campaign-blueprint <blueprint-id>')
87
+ .description('Render campaign blueprint')
88
+ .action(async (blueprint_id) => {
89
+ const spinner = createSpinner('Fetching campaign blueprint...').start();
90
+ try {
91
+ const data = await client.renderCampaignBlueprint({
92
+ blueprint_id: Number(blueprint_id),
93
+ });
94
+
95
+ spinner.stop();
96
+ formatter.output(data);
97
+ } catch (error: any) {
98
+ spinner.stop();
99
+ formatter.error(error.message);
100
+ process.exit(1);
101
+ }
102
+ });
103
+
104
+
105
+ return cmd;
106
+ }