@cakemail-org/cakemail-cli 1.7.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.
- package/.claude/settings.local.json +12 -0
- package/.env.example +40 -0
- package/.env.test.example +45 -0
- package/CHANGELOG.md +1031 -0
- package/README.md +41 -37
- package/audit-formats.js +128 -0
- package/cakemail.rb +20 -0
- package/dist/client.js +1 -1
- package/dist/client.js.map +1 -1
- package/dist/commands/account.js +1 -1
- package/dist/commands/account.js.map +1 -1
- package/dist/commands/attributes.js +1 -1
- package/dist/commands/attributes.js.map +1 -1
- package/dist/commands/campaigns.js +1 -1
- package/dist/commands/campaigns.js.map +1 -1
- package/dist/commands/contacts.js +1 -1
- package/dist/commands/contacts.js.map +1 -1
- package/dist/commands/emails.js +1 -1
- package/dist/commands/emails.js.map +1 -1
- package/dist/commands/interests.js +1 -1
- package/dist/commands/interests.js.map +1 -1
- package/dist/commands/lists.js +1 -1
- package/dist/commands/lists.js.map +1 -1
- package/dist/commands/logs.js +1 -1
- package/dist/commands/logs.js.map +1 -1
- package/dist/commands/reports.js +1 -1
- package/dist/commands/reports.js.map +1 -1
- package/dist/commands/segments.js +1 -1
- package/dist/commands/segments.js.map +1 -1
- package/dist/commands/senders.js +1 -1
- package/dist/commands/senders.js.map +1 -1
- package/dist/commands/suppressed.js +1 -1
- package/dist/commands/suppressed.js.map +1 -1
- package/dist/commands/tags.js +1 -1
- package/dist/commands/tags.js.map +1 -1
- package/dist/commands/templates.js +1 -1
- package/dist/commands/templates.js.map +1 -1
- package/dist/commands/transactional-templates.js +1 -1
- package/dist/commands/transactional-templates.js.map +1 -1
- package/dist/commands/webhooks.js +1 -1
- package/dist/commands/webhooks.js.map +1 -1
- package/dist/utils/config.js +2 -2
- package/dist/utils/config.js.map +1 -1
- package/dist/utils/errors.js +1 -1
- package/dist/utils/errors.js.map +1 -1
- package/dist/utils/progress.d.ts.map +1 -1
- package/dist/utils/progress.js +32 -4
- package/dist/utils/progress.js.map +1 -1
- package/dist/utils/spinner.d.ts +17 -0
- package/dist/utils/spinner.d.ts.map +1 -0
- package/dist/utils/spinner.js +43 -0
- package/dist/utils/spinner.js.map +1 -0
- package/docs/DOCUMENTATION-STANDARD.md +1068 -0
- package/docs/README.md +161 -0
- package/docs/developer/ARCHITECTURE.md +516 -0
- package/docs/developer/AUTH.md +204 -0
- package/docs/developer/CONTRIBUTING.md +227 -0
- package/docs/developer/DOCUMENTATION_SUMMARY.md +346 -0
- package/docs/developer/PROJECT_INDEX.md +365 -0
- package/docs/planning/API_COVERAGE.md +1045 -0
- package/docs/planning/BACKLOG.md +1159 -0
- package/docs/planning/PROFILE_SYSTEM_TASKS.md +287 -0
- package/docs/planning/UX_IMPLEMENTATION_PLAN.md +691 -0
- package/docs/planning/archive/RELEASE_CHECKLIST_v1.3.0.md +332 -0
- package/docs/planning/archive/RELEASE_v1.3.0.md +428 -0
- package/docs/planning/archive/cakemail-cli-ux-improvements.md +438 -0
- package/docs/planning/cakemail-profile-system-plan.md +1121 -0
- package/docs/testing/AI_USER_SIMULATION_DESIGN.md +1342 -0
- package/docs/testing/KENOGAMI_BIDIRECTIONAL_FLOW.md +1517 -0
- package/docs/testing/KENOGAMI_TRUTH_RECONCILIATION_SYSTEM.md +1369 -0
- package/docs/user-manual/.obsidian/app.json +1 -0
- package/docs/user-manual/.obsidian/appearance.json +1 -0
- package/docs/user-manual/.obsidian/core-plugins.json +33 -0
- package/docs/user-manual/.obsidian/workspace.json +167 -0
- package/docs/user-manual/01-getting-started/01-installation.md +214 -0
- package/docs/user-manual/01-getting-started/02-quick-start.md +432 -0
- package/docs/user-manual/01-getting-started/03-authentication.md +448 -0
- package/docs/user-manual/01-getting-started/04-configuration.md +430 -0
- package/docs/user-manual/01-getting-started/05-output-formats.md +447 -0
- package/docs/user-manual/02-core-concepts/01-accounts.md +514 -0
- package/docs/user-manual/02-core-concepts/02-profile-system.md +771 -0
- package/docs/user-manual/02-core-concepts/03-smart-defaults.md +485 -0
- package/docs/user-manual/02-core-concepts/04-authentication-methods.md +435 -0
- package/docs/user-manual/02-core-concepts/05-pagination-filtering.md +600 -0
- package/docs/user-manual/02-core-concepts/06-error-handling.md +718 -0
- package/docs/user-manual/02-core-concepts/07-api-coverage.md +483 -0
- package/docs/user-manual/03-email-operations/01-senders.md +490 -0
- package/docs/user-manual/03-email-operations/02-templates.md +444 -0
- package/docs/user-manual/03-email-operations/03-transactional-emails.md +706 -0
- package/docs/user-manual/03-email-operations/04-email-tracking.md +407 -0
- package/docs/user-manual/04-campaign-management/01-campaigns-basics.md +394 -0
- package/docs/user-manual/04-campaign-management/02-campaign-scheduling.md +630 -0
- package/docs/user-manual/04-campaign-management/03-campaign-testing.md +997 -0
- package/docs/user-manual/04-campaign-management/04-campaign-lifecycle.md +709 -0
- package/docs/user-manual/04-campaign-management/05-campaign-links.md +934 -0
- package/docs/user-manual/05-contact-management/01-lists.md +836 -0
- package/docs/user-manual/05-contact-management/02-contacts.md +1035 -0
- package/docs/user-manual/05-contact-management/03-custom-attributes.md +788 -0
- package/docs/user-manual/05-contact-management/04-segments.md +1028 -0
- package/docs/user-manual/05-contact-management/05-contact-import-export.md +1031 -0
- package/docs/user-manual/06-analytics-reporting/01-campaign-analytics.md +867 -0
- package/docs/user-manual/06-analytics-reporting/02-account-reports.md +227 -0
- package/docs/user-manual/07-integrations/01-webhooks-integration.md +259 -0
- package/docs/user-manual/07-integrations/02-automation.md +326 -0
- package/docs/user-manual/08-advanced-usage/01-scripting-patterns.md +672 -0
- package/docs/user-manual/08-advanced-usage/02-bulk-operations.md +932 -0
- package/docs/user-manual/08-advanced-usage/03-ci-cd-integration.md +892 -0
- package/docs/user-manual/08-advanced-usage/04-performance-optimization.md +766 -0
- package/docs/user-manual/09-command-reference/01-config.md +776 -0
- package/docs/user-manual/09-command-reference/02-account.md +652 -0
- package/docs/user-manual/09-command-reference/03-lists.md +958 -0
- package/docs/user-manual/09-command-reference/04-contacts.md +1408 -0
- package/docs/user-manual/09-command-reference/05-attributes.md +617 -0
- package/docs/user-manual/09-command-reference/06-segments.md +894 -0
- package/docs/user-manual/09-command-reference/07-senders.md +803 -0
- package/docs/user-manual/09-command-reference/08-templates.md +818 -0
- package/docs/user-manual/09-command-reference/09-campaigns.md +1250 -0
- package/docs/user-manual/09-command-reference/10-emails.md +807 -0
- package/docs/user-manual/09-command-reference/11-reports.md +1135 -0
- package/docs/user-manual/09-command-reference/12-webhooks.md +773 -0
- package/docs/user-manual/09-command-reference/13-suppressed.md +797 -0
- package/docs/user-manual/09-command-reference/14-interests.md +630 -0
- package/docs/user-manual/09-command-reference/15-tags.md +584 -0
- package/docs/user-manual/09-command-reference/16-logs.md +656 -0
- package/docs/user-manual/09-command-reference/17-transactional-templates.md +850 -0
- package/docs/user-manual/10-troubleshooting/01-common-errors.md +457 -0
- package/docs/user-manual/10-troubleshooting/02-authentication-issues.md +558 -0
- package/docs/user-manual/10-troubleshooting/03-connection-problems.md +634 -0
- package/docs/user-manual/10-troubleshooting/04-debugging.md +725 -0
- package/docs/user-manual/11-appendix/04-faq.md +484 -0
- package/docs/user-manual/11-appendix/05-glossary.md +250 -0
- package/docs/user-manual/README.md +0 -0
- package/package.json +13 -61
- package/src/cli.ts +125 -0
- package/src/client.ts +16 -0
- package/src/commands/account.ts +267 -0
- package/src/commands/accounts.ts +78 -0
- package/src/commands/actions.ts +249 -0
- package/src/commands/attributes.ts +139 -0
- package/src/commands/campaign-blueprints.ts +106 -0
- package/src/commands/campaigns.ts +469 -0
- package/src/commands/config.ts +77 -0
- package/src/commands/contacts.ts +612 -0
- package/src/commands/custom-attributes.ts +127 -0
- package/src/commands/dkims.ts +117 -0
- package/src/commands/domains.ts +82 -0
- package/src/commands/email-apis.ts +569 -0
- package/src/commands/emails.ts +197 -0
- package/src/commands/forms.ts +283 -0
- package/src/commands/interests.ts +155 -0
- package/src/commands/links.ts +38 -0
- package/src/commands/lists.ts +406 -0
- package/src/commands/logos.ts +71 -0
- package/src/commands/logs.ts +386 -0
- package/src/commands/reports.ts +306 -0
- package/src/commands/segments.ts +158 -0
- package/src/commands/senders.ts +204 -0
- package/src/commands/sub-accounts.ts +271 -0
- package/src/commands/suppressed-emails.ts +234 -0
- package/src/commands/suppressed.ts +198 -0
- package/src/commands/system-emails.ts +85 -0
- package/src/commands/tags.ts +146 -0
- package/src/commands/tasks.ts +116 -0
- package/src/commands/templates.ts +189 -0
- package/src/commands/tokens.ts +83 -0
- package/src/commands/transactional-emails.ts +374 -0
- package/src/commands/transactional-templates.ts +385 -0
- package/src/commands/users.ts +506 -0
- package/src/commands/webhooks.ts +172 -0
- package/src/commands/workflow-blueprints.ts +123 -0
- package/src/commands/workflows.ts +265 -0
- package/src/types/profile.ts +93 -0
- package/src/utils/auth.ts +272 -0
- package/src/utils/config-file.ts +96 -0
- package/src/utils/config.ts +134 -0
- package/src/utils/confirm.ts +32 -0
- package/src/utils/defaults.ts +99 -0
- package/src/utils/errors.ts +116 -0
- package/src/utils/interactive.ts +91 -0
- package/src/utils/list-defaults.ts +74 -0
- package/src/utils/output.ts +190 -0
- package/src/utils/progress.ts +320 -0
- package/src/utils/spinner.ts +22 -0
- package/tests/IMPLEMENTATION_STATUS.md +258 -0
- package/tests/PTY_SETUP.md +118 -0
- package/tests/PTY_TESTING_GUIDE.md +507 -0
- package/tests/README.md +244 -0
- package/tests/fixtures/api-responses/campaigns.json +34 -0
- package/tests/fixtures/test-config.json +13 -0
- package/tests/helpers/cli-runner.ts +128 -0
- package/tests/helpers/mock-server.ts +301 -0
- package/tests/helpers/pty-runner.ts +181 -0
- package/tests/integration/campaigns-real-api.test.ts +196 -0
- package/tests/integration/setup-integration.ts +50 -0
- package/tests/pty/campaigns.test.ts +241 -0
- package/tests/setup.ts +34 -0
- package/tsconfig.json +15 -0
- 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
|
+
}
|