@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,612 @@
1
+ // ABOUTME: CLI commands for Contact.
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 createContactsCommand(
9
+ client: any,
10
+ formatter: any
11
+ ): Command {
12
+ const cmd = new Command('contacts')
13
+ .description('Manage contacts within lists. Contacts are individual email subscribers with an email address, subscription status, custom attributes, tags, and interests. Supports creating, importing, updating, unsubscribing, and deleting contacts. Bulk operations are available for tagging and interest management. Contact data can be exported to CSV files.');
14
+
15
+
16
+ cmd
17
+ .command('add-interests-to <list-id>')
18
+ .description('Add interest(s) to contact(s)')
19
+ .option('--account-id <value>', 'Optional Account ID to be used for the request')
20
+ .requiredOption('--data <value>', 'Request body as JSON string')
21
+ .action(async (list_id, options) => {
22
+ const spinner = createSpinner('Creating contact...').start();
23
+ try {
24
+ const body = JSON.parse(options.data);
25
+ const data = await client.addInterestsToContacts({
26
+ list_id: Number(list_id),
27
+ ...body,
28
+ });
29
+
30
+ spinner.stop();
31
+ formatter.success('Contact created successfully');
32
+ formatter.output(data);
33
+ } catch (error: any) {
34
+ spinner.stop();
35
+ formatter.error(error.message);
36
+ process.exit(1);
37
+ }
38
+ });
39
+
40
+
41
+ cmd
42
+ .command('list-contacts-of-list <list-id>')
43
+ .description('Show contacts of a list')
44
+ .option('--query <value>', 'SQL selection query (only use one of query, fiql and json)')
45
+ .option('--json <value>', 'JSON selection expression (only use one of query, fiql and json)')
46
+ .option('--cursor <value>', 'Cursor pagination')
47
+ .option('--account-id <value>', 'Optional Account ID to be used for the request')
48
+ .option('--page <value>', 'page')
49
+ .option('--per-page <value>', 'per_page')
50
+ .option('--with-count <value>', 'Include count in the response')
51
+ .option('--sort <value>', 'Sort term and direction, using syntax `[-|+]term`.
52
+
53
+ Valid terms:
54
+ - `id`
55
+ - `email`
56
+ - `status`
57
+ - `subscribed_on`
58
+ - `last_bounce_type`
59
+ - `bounces_count`')
60
+ .option('--filter <value>', 'Valid Terms:
61
+ - `status`
62
+ - `email`
63
+ - `tags`
64
+ - `interests`
65
+
66
+ Valid Operators:
67
+ - `==`
68
+
69
+ Query separator:
70
+ - `;`')
71
+ .option('--fiql <value>', 'fiql')
72
+ .action(async (list_id, options) => {
73
+ const spinner = createSpinner('Fetching contact...').start();
74
+ try {
75
+ const data = await client.listContactsOfList({
76
+ list_id: Number(list_id),
77
+ query: options.query,
78
+ json: options.json,
79
+ cursor: options.cursor,
80
+ accountId: options.accountId != null ? Number(options.accountId) : undefined,
81
+ page: options.page != null ? Number(options.page) : undefined,
82
+ perPage: options.perPage != null ? Number(options.perPage) : undefined,
83
+ withCount: options.withCount,
84
+ sort: options.sort,
85
+ filter: options.filter,
86
+ fiql: options.fiql,
87
+ });
88
+
89
+ spinner.stop();
90
+ formatter.output(data);
91
+ } catch (error: any) {
92
+ spinner.stop();
93
+ formatter.error(error.message);
94
+ process.exit(1);
95
+ }
96
+ });
97
+
98
+
99
+ cmd
100
+ .command('create <list-id>')
101
+ .description('Add a contact')
102
+ .option('--send-double-opt-in <value>', 'send_double_opt_in')
103
+ .option('--resubscribe <value>', 'resubscribe')
104
+ .option('--account-id <value>', 'Optional Account ID to be used for the request')
105
+ .requiredOption('--data <value>', 'Request body as JSON string')
106
+ .action(async (list_id, options) => {
107
+ const spinner = createSpinner('Creating contact...').start();
108
+ try {
109
+ const body = JSON.parse(options.data);
110
+ const data = await client.createContact({
111
+ list_id: Number(list_id),
112
+ ...body,
113
+ });
114
+
115
+ spinner.stop();
116
+ formatter.success('Contact created successfully');
117
+ formatter.output(data);
118
+ } catch (error: any) {
119
+ spinner.stop();
120
+ formatter.error(error.message);
121
+ process.exit(1);
122
+ }
123
+ });
124
+
125
+
126
+ cmd
127
+ .command('list-contacts-of-segment <segment-id> <list-id>')
128
+ .description('Show contacts of a segment')
129
+ .option('--query <value>', 'SQL selection query (only use one of query, fiql and json)')
130
+ .option('--json <value>', 'JSON selection expression (only use one of query, fiql and json)')
131
+ .option('--cursor <value>', 'Cursor pagination')
132
+ .option('--account-id <value>', 'Optional Account ID to be used for the request')
133
+ .option('--page <value>', 'page')
134
+ .option('--per-page <value>', 'per_page')
135
+ .option('--with-count <value>', 'Include count in the response')
136
+ .option('--sort <value>', 'Sort term and direction, using syntax `[-|+]term`.
137
+
138
+ Valid terms:
139
+ - `id`
140
+ - `email`
141
+ - `status`
142
+ - `subscribed_on`
143
+ - `last_bounce_type`
144
+ - `bounces_count`')
145
+ .option('--filter <value>', 'Valid Terms:
146
+ - `status`
147
+ - `email`
148
+ - `tags`
149
+ - `interests`
150
+
151
+ Valid Operators:
152
+ - `==`
153
+
154
+ Query separator:
155
+ - `;`')
156
+ .option('--fiql <value>', 'fiql')
157
+ .action(async (segment_id, list_id, options) => {
158
+ const spinner = createSpinner('Fetching contact...').start();
159
+ try {
160
+ const data = await client.listContactsOfSegment({
161
+ segment_id: Number(segment_id),
162
+ list_id: Number(list_id),
163
+ query: options.query,
164
+ json: options.json,
165
+ cursor: options.cursor,
166
+ accountId: options.accountId != null ? Number(options.accountId) : undefined,
167
+ page: options.page != null ? Number(options.page) : undefined,
168
+ perPage: options.perPage != null ? Number(options.perPage) : undefined,
169
+ withCount: options.withCount,
170
+ sort: options.sort,
171
+ filter: options.filter,
172
+ fiql: options.fiql,
173
+ });
174
+
175
+ spinner.stop();
176
+ formatter.output(data);
177
+ } catch (error: any) {
178
+ spinner.stop();
179
+ formatter.error(error.message);
180
+ process.exit(1);
181
+ }
182
+ });
183
+
184
+
185
+ cmd
186
+ .command('tag-multiple <list-id>')
187
+ .description('Tags multiple contacts')
188
+ .option('--account-id <value>', 'Optional Account ID to be used for the request')
189
+ .requiredOption('--data <value>', 'Request body as JSON string')
190
+ .action(async (list_id, options) => {
191
+ const spinner = createSpinner('Creating contact...').start();
192
+ try {
193
+ const body = JSON.parse(options.data);
194
+ const data = await client.tagMultipleContacts({
195
+ list_id: Number(list_id),
196
+ ...body,
197
+ });
198
+
199
+ spinner.stop();
200
+ formatter.success('Contact created successfully');
201
+ formatter.output(data);
202
+ } catch (error: any) {
203
+ spinner.stop();
204
+ formatter.error(error.message);
205
+ process.exit(1);
206
+ }
207
+ });
208
+
209
+
210
+ cmd
211
+ .command('untag-multiple <list-id>')
212
+ .description('Untags multiple contacts')
213
+ .option('--account-id <value>', 'Optional Account ID to be used for the request')
214
+ .requiredOption('--data <value>', 'Request body as JSON string')
215
+ .action(async (list_id, options) => {
216
+ const spinner = createSpinner('Creating contact...').start();
217
+ try {
218
+ const body = JSON.parse(options.data);
219
+ const data = await client.untagMultipleContacts({
220
+ list_id: Number(list_id),
221
+ ...body,
222
+ });
223
+
224
+ spinner.stop();
225
+ formatter.success('Contact created successfully');
226
+ formatter.output(data);
227
+ } catch (error: any) {
228
+ spinner.stop();
229
+ formatter.error(error.message);
230
+ process.exit(1);
231
+ }
232
+ });
233
+
234
+
235
+ cmd
236
+ .command('get <list-id> <contact-id>')
237
+ .description('Show a contact details')
238
+ .option('--account-id <value>', 'Optional Account ID to be used for the request')
239
+ .action(async (list_id, contact_id, options) => {
240
+ const spinner = createSpinner('Fetching contact...').start();
241
+ try {
242
+ const data = await client.getContact({
243
+ list_id: Number(list_id),
244
+ contact_id: Number(contact_id),
245
+ accountId: options.accountId != null ? Number(options.accountId) : undefined,
246
+ });
247
+
248
+ spinner.stop();
249
+ formatter.output(data);
250
+ } catch (error: any) {
251
+ spinner.stop();
252
+ formatter.error(error.message);
253
+ process.exit(1);
254
+ }
255
+ });
256
+
257
+
258
+ cmd
259
+ .command('patch <list-id> <contact-id>')
260
+ .description('Update a contact')
261
+ .option('--account-id <value>', 'Optional Account ID to be used for the request')
262
+ .requiredOption('--data <value>', 'Request body as JSON string')
263
+ .action(async (list_id, contact_id, options) => {
264
+ const spinner = createSpinner('Updating contact...').start();
265
+ try {
266
+ const body = JSON.parse(options.data);
267
+ const data = await client.patchContact({
268
+ list_id: Number(list_id),
269
+ contact_id: Number(contact_id),
270
+ ...body,
271
+ });
272
+
273
+ spinner.stop();
274
+ formatter.success('Contact updated successfully');
275
+ formatter.output(data);
276
+ } catch (error: any) {
277
+ spinner.stop();
278
+ formatter.error(error.message);
279
+ process.exit(1);
280
+ }
281
+ });
282
+
283
+
284
+ cmd
285
+ .command('delete <list-id> <contact-id>')
286
+ .description('Delete a contact')
287
+ .option('--account-id <value>', 'Optional Account ID to be used for the request')
288
+ .option('-f, --force', 'Skip confirmation prompt')
289
+ .action(async (list_id, contact_id, options) => {
290
+ if (!options.force) {
291
+ const confirmed = await confirmDelete('contact', list_id);
292
+ if (!confirmed) {
293
+ formatter.info('Deletion cancelled');
294
+ return;
295
+ }
296
+ }
297
+
298
+ const spinner = createSpinner('Deleting contact...').start();
299
+ try {
300
+ const data = await client.deleteContact({
301
+ list_id: Number(list_id),
302
+ contact_id: Number(contact_id),
303
+ accountId: options.accountId != null ? Number(options.accountId) : undefined,
304
+ });
305
+
306
+ spinner.stop();
307
+ formatter.success('Contact deleted successfully');
308
+ } catch (error: any) {
309
+ spinner.stop();
310
+ formatter.error(error.message);
311
+ process.exit(1);
312
+ }
313
+ });
314
+
315
+
316
+ cmd
317
+ .command('import <list-id>')
318
+ .description('Import contacts')
319
+ .option('--send-double-opt-in <value>', 'send_double_opt_in')
320
+ .option('--account-id <value>', 'Optional Account ID to be used for the request')
321
+ .requiredOption('--data <value>', 'Request body as JSON string')
322
+ .action(async (list_id, options) => {
323
+ const spinner = createSpinner('Creating contact...').start();
324
+ try {
325
+ const body = JSON.parse(options.data);
326
+ const data = await client.importContacts({
327
+ list_id: Number(list_id),
328
+ ...body,
329
+ });
330
+
331
+ spinner.stop();
332
+ formatter.success('Contact created successfully');
333
+ formatter.output(data);
334
+ } catch (error: any) {
335
+ spinner.stop();
336
+ formatter.error(error.message);
337
+ process.exit(1);
338
+ }
339
+ });
340
+
341
+
342
+ cmd
343
+ .command('unsubscribe <list-id> <contact-id>')
344
+ .description('Unsubscribe a contact from a list')
345
+ .option('--account-id <value>', 'Optional Account ID to be used for the request')
346
+ .action(async (list_id, contact_id, options) => {
347
+ const spinner = createSpinner('Creating contact...').start();
348
+ try {
349
+ const data = await client.unsubscribeContact({
350
+ list_id: Number(list_id),
351
+ contact_id: Number(contact_id),
352
+ accountId: options.accountId != null ? Number(options.accountId) : undefined,
353
+ });
354
+
355
+ spinner.stop();
356
+ formatter.success('Contact created successfully');
357
+ formatter.output(data);
358
+ } catch (error: any) {
359
+ spinner.stop();
360
+ formatter.error(error.message);
361
+ process.exit(1);
362
+ }
363
+ });
364
+
365
+
366
+ cmd
367
+ .command('tag <list-id> <contact-id>')
368
+ .description('Tags a contact')
369
+ .option('--account-id <value>', 'Optional Account ID to be used for the request')
370
+ .requiredOption('--data <value>', 'Request body as JSON string')
371
+ .action(async (list_id, contact_id, options) => {
372
+ const spinner = createSpinner('Creating contact...').start();
373
+ try {
374
+ const body = JSON.parse(options.data);
375
+ const data = await client.tagContact({
376
+ list_id: Number(list_id),
377
+ contact_id: Number(contact_id),
378
+ ...body,
379
+ });
380
+
381
+ spinner.stop();
382
+ formatter.success('Contact created successfully');
383
+ formatter.output(data);
384
+ } catch (error: any) {
385
+ spinner.stop();
386
+ formatter.error(error.message);
387
+ process.exit(1);
388
+ }
389
+ });
390
+
391
+
392
+ cmd
393
+ .command('untag <list-id> <contact-id>')
394
+ .description('Untags a contact')
395
+ .option('--account-id <value>', 'Optional Account ID to be used for the request')
396
+ .requiredOption('--data <value>', 'Request body as JSON string')
397
+ .action(async (list_id, contact_id, options) => {
398
+ const spinner = createSpinner('Creating contact...').start();
399
+ try {
400
+ const body = JSON.parse(options.data);
401
+ const data = await client.untagContact({
402
+ list_id: Number(list_id),
403
+ contact_id: Number(contact_id),
404
+ ...body,
405
+ });
406
+
407
+ spinner.stop();
408
+ formatter.success('Contact created successfully');
409
+ formatter.output(data);
410
+ } catch (error: any) {
411
+ spinner.stop();
412
+ formatter.error(error.message);
413
+ process.exit(1);
414
+ }
415
+ });
416
+
417
+
418
+ cmd
419
+ .command('remove-interests-from <list-id>')
420
+ .description('Remove interest(s) from contact(s)')
421
+ .option('--account-id <value>', 'Optional Account ID to be used for the request')
422
+ .requiredOption('--data <value>', 'Request body as JSON string')
423
+ .action(async (list_id, options) => {
424
+ const spinner = createSpinner('Creating contact...').start();
425
+ try {
426
+ const body = JSON.parse(options.data);
427
+ const data = await client.removeInterestsFromContacts({
428
+ list_id: Number(list_id),
429
+ ...body,
430
+ });
431
+
432
+ spinner.stop();
433
+ formatter.success('Contact created successfully');
434
+ formatter.output(data);
435
+ } catch (error: any) {
436
+ spinner.stop();
437
+ formatter.error(error.message);
438
+ process.exit(1);
439
+ }
440
+ });
441
+
442
+
443
+ cmd
444
+ .command('list-contacts-exports <list-id>')
445
+ .description('Show all Contacts Exports')
446
+ .option('--account-id <value>', 'Optional Account ID to be used for the request')
447
+ .option('--page <value>', 'page')
448
+ .option('--per-page <value>', 'per_page')
449
+ .option('--with-count <value>', 'Include count in the response')
450
+ .option('--sort <value>', 'Sort term and direction, using syntax `[-|+]term`.
451
+
452
+ Valid terms:
453
+ - `status`
454
+ - `created_on`
455
+ - `expires_on`')
456
+ .option('--filter <value>', 'Valid Terms:
457
+ - `status`
458
+ - `progress`
459
+
460
+ Valid Operators:
461
+ - `==`
462
+
463
+ Query separator:
464
+ - `;`')
465
+ .action(async (list_id, options) => {
466
+ const spinner = createSpinner('Fetching contact...').start();
467
+ try {
468
+ const data = await client.listContactsExports({
469
+ list_id: Number(list_id),
470
+ accountId: options.accountId != null ? Number(options.accountId) : undefined,
471
+ page: options.page != null ? Number(options.page) : undefined,
472
+ perPage: options.perPage != null ? Number(options.perPage) : undefined,
473
+ withCount: options.withCount,
474
+ sort: options.sort,
475
+ filter: options.filter,
476
+ });
477
+
478
+ spinner.stop();
479
+ formatter.output(data);
480
+ } catch (error: any) {
481
+ spinner.stop();
482
+ formatter.error(error.message);
483
+ process.exit(1);
484
+ }
485
+ });
486
+
487
+
488
+ cmd
489
+ .command('export <list-id>')
490
+ .description('Create a Contacts Export')
491
+ .option('--description <value>', 'description')
492
+ .option('--query <value>', 'query')
493
+ .option('--json <value>', 'json')
494
+ .option('--segment-id <value>', 'segment_id')
495
+ .option('--account-id <value>', 'Optional Account ID to be used for the request')
496
+ .option('--filter <value>', 'Valid Terms:
497
+ - `status`
498
+ - `email`
499
+ - `tags`
500
+ - `interests`
501
+
502
+ Valid Operators:
503
+ - `==`
504
+
505
+ Query separator:
506
+ - `;`')
507
+ .option('--fiql <value>', 'fiql')
508
+ .action(async (list_id, options) => {
509
+ const spinner = createSpinner('Creating contact...').start();
510
+ try {
511
+ const data = await client.exportContacts({
512
+ list_id: Number(list_id),
513
+ description: options.description,
514
+ query: options.query,
515
+ json: options.json,
516
+ segmentId: options.segmentId != null ? Number(options.segmentId) : undefined,
517
+ accountId: options.accountId != null ? Number(options.accountId) : undefined,
518
+ filter: options.filter,
519
+ fiql: options.fiql,
520
+ });
521
+
522
+ spinner.stop();
523
+ formatter.success('Contact created successfully');
524
+ formatter.output(data);
525
+ } catch (error: any) {
526
+ spinner.stop();
527
+ formatter.error(error.message);
528
+ process.exit(1);
529
+ }
530
+ });
531
+
532
+
533
+ cmd
534
+ .command('get-contacts-export <export-id> <list-id>')
535
+ .description('Show a Contacts Export')
536
+ .option('--account-id <value>', 'Optional Account ID to be used for the request')
537
+ .action(async (export_id, list_id, options) => {
538
+ const spinner = createSpinner('Fetching contact...').start();
539
+ try {
540
+ const data = await client.getContactsExport({
541
+ export_id,
542
+ list_id: Number(list_id),
543
+ accountId: options.accountId != null ? Number(options.accountId) : undefined,
544
+ });
545
+
546
+ spinner.stop();
547
+ formatter.output(data);
548
+ } catch (error: any) {
549
+ spinner.stop();
550
+ formatter.error(error.message);
551
+ process.exit(1);
552
+ }
553
+ });
554
+
555
+
556
+ cmd
557
+ .command('delete-contacts-export <export-id> <list-id>')
558
+ .description('Delete a Contacts Export')
559
+ .option('--account-id <value>', 'Optional Account ID to be used for the request')
560
+ .option('-f, --force', 'Skip confirmation prompt')
561
+ .action(async (export_id, list_id, options) => {
562
+ if (!options.force) {
563
+ const confirmed = await confirmDelete('contact', export_id);
564
+ if (!confirmed) {
565
+ formatter.info('Deletion cancelled');
566
+ return;
567
+ }
568
+ }
569
+
570
+ const spinner = createSpinner('Deleting contact...').start();
571
+ try {
572
+ const data = await client.deleteContactsExport({
573
+ export_id,
574
+ list_id: Number(list_id),
575
+ accountId: options.accountId != null ? Number(options.accountId) : undefined,
576
+ });
577
+
578
+ spinner.stop();
579
+ formatter.success('Contact deleted successfully');
580
+ } catch (error: any) {
581
+ spinner.stop();
582
+ formatter.error(error.message);
583
+ process.exit(1);
584
+ }
585
+ });
586
+
587
+
588
+ cmd
589
+ .command('download-contacts-export <export-id> <list-id>')
590
+ .description('Download a Contacts Export')
591
+ .option('--account-id <value>', 'Optional Account ID to be used for the request')
592
+ .action(async (export_id, list_id, options) => {
593
+ const spinner = createSpinner('Fetching contact...').start();
594
+ try {
595
+ const data = await client.downloadContactsExport({
596
+ export_id,
597
+ list_id: Number(list_id),
598
+ accountId: options.accountId != null ? Number(options.accountId) : undefined,
599
+ });
600
+
601
+ spinner.stop();
602
+ formatter.output(data);
603
+ } catch (error: any) {
604
+ spinner.stop();
605
+ formatter.error(error.message);
606
+ process.exit(1);
607
+ }
608
+ });
609
+
610
+
611
+ return cmd;
612
+ }