@ascendkit/cli 0.2.0 → 0.3.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/dist/api/client.d.ts +2 -0
- package/dist/api/client.js +34 -8
- package/dist/cli.js +1047 -314
- package/dist/commands/auth.d.ts +4 -0
- package/dist/commands/auth.js +12 -0
- package/dist/commands/campaigns.d.ts +22 -0
- package/dist/commands/campaigns.js +25 -0
- package/dist/commands/content.js +4 -4
- package/dist/commands/email.d.ts +62 -6
- package/dist/commands/email.js +26 -17
- package/dist/commands/import.d.ts +75 -0
- package/dist/commands/import.js +97 -0
- package/dist/commands/journeys.d.ts +3 -0
- package/dist/commands/journeys.js +9 -6
- package/dist/commands/platform.d.ts +28 -0
- package/dist/commands/platform.js +219 -101
- package/dist/commands/surveys.js +5 -5
- package/dist/mcp.js +35 -3
- package/dist/tools/auth.js +66 -8
- package/dist/tools/campaigns.d.ts +3 -0
- package/dist/tools/campaigns.js +78 -0
- package/dist/tools/content.js +24 -12
- package/dist/tools/email.js +47 -17
- package/dist/tools/import.d.ts +3 -0
- package/dist/tools/import.js +116 -0
- package/dist/tools/journeys.js +25 -14
- package/dist/tools/platform.js +151 -6
- package/dist/tools/surveys.js +9 -9
- package/dist/utils/journey-format.d.ts +6 -0
- package/dist/utils/journey-format.js +6 -4
- package/dist/utils/survey-format.js +2 -2
- package/package.json +5 -5
package/dist/cli.js
CHANGED
|
@@ -11,7 +11,10 @@ import * as platform from "./commands/platform.js";
|
|
|
11
11
|
import * as journeys from "./commands/journeys.js";
|
|
12
12
|
import * as email from "./commands/email.js";
|
|
13
13
|
import * as webhooks from "./commands/webhooks.js";
|
|
14
|
+
import * as campaigns from "./commands/campaigns.js";
|
|
15
|
+
import * as importCmd from "./commands/import.js";
|
|
14
16
|
import { parseDelay } from "./utils/duration.js";
|
|
17
|
+
import { formatJourneyAnalytics, formatJourneyWithGuidance, formatNodeList, formatSingleNode, formatSingleTransition, formatTransitionList, } from "./utils/journey-format.js";
|
|
15
18
|
const require = createRequire(import.meta.url);
|
|
16
19
|
const { version: CLI_VERSION } = require("../package.json");
|
|
17
20
|
const HELP = `ascendkit v${CLI_VERSION} - AscendKit CLI
|
|
@@ -27,15 +30,18 @@ Getting Started:
|
|
|
27
30
|
|
|
28
31
|
Services:
|
|
29
32
|
auth Authentication, providers, OAuth, users
|
|
30
|
-
|
|
33
|
+
template Email templates and versioning
|
|
31
34
|
survey Surveys, questions, distribution, analytics
|
|
32
35
|
journey Lifecycle journeys, nodes, transitions
|
|
33
|
-
email
|
|
36
|
+
email-identity Email domains, sender identities, and DNS
|
|
37
|
+
keystore Environment runtime key-value settings
|
|
34
38
|
webhook Webhook endpoints and testing
|
|
39
|
+
campaign Email campaigns, scheduling, analytics
|
|
40
|
+
import Import users from external auth providers
|
|
35
41
|
|
|
36
42
|
Project Management:
|
|
37
|
-
|
|
38
|
-
|
|
43
|
+
project Projects and environment selection
|
|
44
|
+
environment Active environment operations
|
|
39
45
|
verify Check all services in the active environment
|
|
40
46
|
|
|
41
47
|
Run "ascendkit help <section>" for detailed command usage.
|
|
@@ -44,70 +50,79 @@ const HELP_SECTION = {
|
|
|
44
50
|
auth: `Usage: ascendkit auth <command>
|
|
45
51
|
|
|
46
52
|
Commands:
|
|
47
|
-
auth
|
|
48
|
-
auth
|
|
49
|
-
auth
|
|
50
|
-
auth
|
|
53
|
+
auth show
|
|
54
|
+
auth update [--providers <p1,p2,...>] [--email-verification <true|false>] [--waitlist <true|false>] [--password-reset <true|false>] [--session-duration <duration>]
|
|
55
|
+
auth provider list
|
|
56
|
+
auth provider set <p1,p2,...>
|
|
57
|
+
auth oauth open <provider>
|
|
51
58
|
auth oauth set <provider> --client-id <id> [--client-secret <secret> | --client-secret-stdin] [--callback-url <url>]
|
|
52
|
-
auth
|
|
53
|
-
|
|
59
|
+
auth oauth remove <provider>
|
|
60
|
+
auth user list
|
|
61
|
+
auth user remove <user-id>
|
|
62
|
+
auth user reactivate <user-id>`,
|
|
63
|
+
template: `Usage: ascendkit template <command>
|
|
54
64
|
|
|
55
65
|
Commands:
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
66
|
+
template create --name <name> --subject <subject> --body-html <html> --body-text <text> [--slug <slug>] [--description <description>]
|
|
67
|
+
template list [--query <search>] [--system true|--custom true]
|
|
68
|
+
template show <template-id>
|
|
69
|
+
template update <template-id> [--subject <subject>] [--body-html <html>] [--body-text <text>] [--change-note <note>]
|
|
70
|
+
template remove <template-id>
|
|
71
|
+
template version list <template-id>
|
|
72
|
+
template version show <template-id> <n>`,
|
|
63
73
|
survey: `Usage: ascendkit survey <command>
|
|
64
74
|
|
|
65
75
|
Commands:
|
|
66
76
|
survey create --name <name> [--type <nps|csat|custom>] [--definition <json>]
|
|
67
77
|
survey list
|
|
68
|
-
survey
|
|
78
|
+
survey show <survey-id>
|
|
69
79
|
survey update <survey-id> [--name <name>] [--status <draft|active|paused>] [--definition <json>]
|
|
70
|
-
survey
|
|
80
|
+
survey remove <survey-id>
|
|
71
81
|
survey distribute <survey-id> --users <usr_id1,usr_id2,...>
|
|
72
|
-
survey
|
|
82
|
+
survey invitation list <survey-id>
|
|
73
83
|
survey analytics <survey-id>
|
|
74
|
-
survey
|
|
75
|
-
survey
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
84
|
+
survey definition export <survey-id> [--out <file>]
|
|
85
|
+
survey definition import <survey-id> --in <file>
|
|
86
|
+
survey question list <survey-id>
|
|
87
|
+
survey question add <survey-id> --type <type> --title <title> [--name <name>] [--required <true|false>] [--choices <c1,c2,...>] [--position <n>]
|
|
88
|
+
survey question update <survey-id> <question-name> [--title <title>] [--required <true|false>] [--choices <c1,c2,...>]
|
|
89
|
+
survey question remove <survey-id> <question-name>
|
|
90
|
+
survey question reorder <survey-id> --order <name1,name2,...>`,
|
|
79
91
|
journey: `Usage: ascendkit journey <command>
|
|
80
92
|
|
|
81
93
|
Commands:
|
|
82
94
|
journey create --name <name> --entry-event <event> --entry-node <node> [--nodes <json>] [--transitions <json>] [--description <description>] [--entry-conditions <json>] [--re-entry-policy <skip|restart>]
|
|
83
95
|
journey list [--status <draft|active|paused|archived>]
|
|
84
|
-
journey
|
|
96
|
+
journey show <journey-id>
|
|
85
97
|
journey update <journey-id> [--name <name>] [--nodes <json>] [--transitions <json>] [--description <description>] [--entry-event <event>] [--entry-node <node>] [--entry-conditions <json>] [--re-entry-policy <skip|restart>]
|
|
86
|
-
journey
|
|
98
|
+
journey remove <journey-id>
|
|
87
99
|
journey activate <journey-id>
|
|
88
100
|
journey pause <journey-id>
|
|
101
|
+
journey resume <journey-id>
|
|
89
102
|
journey archive <journey-id>
|
|
90
103
|
journey analytics <journey-id>
|
|
91
|
-
journey list
|
|
92
|
-
journey
|
|
93
|
-
journey
|
|
94
|
-
journey
|
|
95
|
-
journey list
|
|
96
|
-
journey
|
|
97
|
-
journey
|
|
98
|
-
journey
|
|
99
|
-
email: `Usage: ascendkit email <command>
|
|
104
|
+
journey node list <journey-id>
|
|
105
|
+
journey node add <journey-id> --name <node-name> [--action <json>] [--email-id <email>] [--terminal <true|false>]
|
|
106
|
+
journey node update <journey-id> <node-name> [--action <json>] [--email-id <email>] [--terminal <true|false>]
|
|
107
|
+
journey node remove <journey-id> <node-name>
|
|
108
|
+
journey transition list <journey-id> [--from <node-name>] [--to <node-name>]
|
|
109
|
+
journey transition add <journey-id> --from <node-name> --to <node-name> --trigger <json> [--priority <n>] [--name <transition-name>]
|
|
110
|
+
journey transition update <journey-id> <transition-name> [--trigger <json>] [--priority <n>]
|
|
111
|
+
journey transition remove <journey-id> <transition-name>`,
|
|
112
|
+
"email-identity": `Usage: ascendkit email-identity <command>
|
|
100
113
|
|
|
101
114
|
Commands:
|
|
102
|
-
email settings
|
|
103
|
-
email
|
|
104
|
-
email
|
|
105
|
-
email
|
|
106
|
-
email
|
|
107
|
-
email
|
|
108
|
-
email
|
|
109
|
-
email
|
|
110
|
-
email remove
|
|
115
|
+
email-identity settings [--json]
|
|
116
|
+
email-identity setup-domain <domain>
|
|
117
|
+
email-identity status [--watch] [--interval <seconds>]
|
|
118
|
+
email-identity remove-domain
|
|
119
|
+
email-identity list
|
|
120
|
+
email-identity add <email> [--display-name <name>]
|
|
121
|
+
email-identity resend <email>
|
|
122
|
+
email-identity set-default <email> [--display-name <name>]
|
|
123
|
+
email-identity remove <email>
|
|
124
|
+
email-identity test <email> --to <recipient>
|
|
125
|
+
email-identity open-dns [--domain <domain>] [--open]`,
|
|
111
126
|
webhook: `Usage: ascendkit webhook <command>
|
|
112
127
|
|
|
113
128
|
Commands:
|
|
@@ -117,25 +132,77 @@ Commands:
|
|
|
117
132
|
webhook update <webhook-id> [--url <url>] [--events <e1,e2,...>] [--status <active|inactive>]
|
|
118
133
|
webhook delete <webhook-id>
|
|
119
134
|
webhook test <webhook-id> [--event <event-type>]`,
|
|
120
|
-
|
|
135
|
+
campaign: `Usage: ascendkit campaign <command>
|
|
136
|
+
|
|
137
|
+
Commands:
|
|
138
|
+
campaign create --name <name> --template <template-id> --audience <json> [--scheduled-at <datetime>]
|
|
139
|
+
campaign list [--status <draft|scheduled|sending|sent|failed|cancelled>]
|
|
140
|
+
campaign show <campaign-id>
|
|
141
|
+
campaign update <campaign-id> [--name <name>] [--template <template-id>] [--audience <json>] [--scheduled-at <datetime>]
|
|
142
|
+
campaign preview <campaign-id>
|
|
143
|
+
campaign schedule <campaign-id> --at <datetime>
|
|
144
|
+
campaign cancel <campaign-id>
|
|
145
|
+
campaign analytics <campaign-id>
|
|
146
|
+
|
|
147
|
+
Notes:
|
|
148
|
+
- --audience is a JSON filter object, e.g. '{"tags":{"$in":["premium"]}}'
|
|
149
|
+
- --scheduled-at / --at accepts ISO 8601 datetime, e.g. 2026-03-15T10:00:00Z
|
|
150
|
+
- cancel deletes a draft/failed campaign or cancels a scheduled/sending campaign`,
|
|
151
|
+
environment: `Usage: ascendkit environment <command>
|
|
152
|
+
|
|
153
|
+
Commands:
|
|
154
|
+
environment show
|
|
155
|
+
environment update [<env-id>] [--name <name>] [--description <desc>]
|
|
156
|
+
environment promote [<env-id>] --target <tier>`,
|
|
157
|
+
keystore: `Usage: ascendkit keystore <command>
|
|
158
|
+
|
|
159
|
+
Commands:
|
|
160
|
+
keystore list
|
|
161
|
+
keystore set <key> <value>
|
|
162
|
+
keystore remove <key>`,
|
|
163
|
+
import: `Usage: ascendkit import <source> <action> [options]
|
|
164
|
+
|
|
165
|
+
Sources:
|
|
166
|
+
clerk Import users from Clerk
|
|
167
|
+
migration-journey Create migration email templates and journeys
|
|
121
168
|
|
|
122
169
|
Commands:
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
170
|
+
import clerk preview --api-key <key> [options]
|
|
171
|
+
import clerk preview --file <path> [options]
|
|
172
|
+
import clerk run --api-key <key> [options]
|
|
173
|
+
import clerk run --file <path> [options]
|
|
174
|
+
import migration-journey create [--from-identity <email>]
|
|
175
|
+
|
|
176
|
+
Options:
|
|
177
|
+
--api-key <key> Clerk secret API key (fetches users from Clerk API)
|
|
178
|
+
--file <path> Path to Clerk dashboard export (JSON)
|
|
179
|
+
--instance-url <url> Custom Clerk API URL (default: https://api.clerk.com)
|
|
180
|
+
--users Import users (included by default; use to select only users)
|
|
181
|
+
--settings Import auth settings / OAuth providers (included by default)
|
|
182
|
+
--from-identity <email> Email identity for migration journey emails
|
|
183
|
+
|
|
184
|
+
Use "preview" for dry-run mode and "run" to apply changes. Pass --users or --settings alone to select
|
|
185
|
+
only that phase (e.g. "import clerk run --users ..." imports only users, not settings).`,
|
|
186
|
+
project: `Usage: ascendkit project <command>
|
|
128
187
|
|
|
129
188
|
Commands:
|
|
130
|
-
|
|
131
|
-
|
|
189
|
+
project list
|
|
190
|
+
project create --name <name> [--description <description>] [--services <s1,s2,...>]
|
|
191
|
+
project show <project-id>
|
|
192
|
+
project env list <project-id>`,
|
|
132
193
|
};
|
|
133
194
|
function printSectionHelp(section) {
|
|
134
195
|
if (!section)
|
|
135
196
|
return false;
|
|
136
197
|
let key = section.toLowerCase();
|
|
137
|
-
if (key === "content")
|
|
138
|
-
key = "
|
|
198
|
+
if (key === "content" || key === "templates")
|
|
199
|
+
key = "template";
|
|
200
|
+
if (key === "email")
|
|
201
|
+
key = "email-identity";
|
|
202
|
+
if (key === "projects")
|
|
203
|
+
key = "project";
|
|
204
|
+
if (key === "env" || key === "environments")
|
|
205
|
+
key = "environment";
|
|
139
206
|
const text = HELP_SECTION[key];
|
|
140
207
|
if (!text)
|
|
141
208
|
return false;
|
|
@@ -143,7 +210,7 @@ function printSectionHelp(section) {
|
|
|
143
210
|
return true;
|
|
144
211
|
}
|
|
145
212
|
function getClient() {
|
|
146
|
-
let publicKey = process.env.
|
|
213
|
+
let publicKey = process.env.ASCENDKIT_ENV_KEY;
|
|
147
214
|
let apiUrl = process.env.ASCENDKIT_API_URL;
|
|
148
215
|
const auth = loadAuth();
|
|
149
216
|
const env = loadEnvContext();
|
|
@@ -196,6 +263,55 @@ async function readSecretFromStdin() {
|
|
|
196
263
|
function output(data) {
|
|
197
264
|
console.log(JSON.stringify(data, null, 2));
|
|
198
265
|
}
|
|
266
|
+
function printAuthSettingsSummary(data) {
|
|
267
|
+
const providers = Array.isArray(data.providers) ? data.providers : [];
|
|
268
|
+
const features = data.features ?? {};
|
|
269
|
+
const featureLines = [
|
|
270
|
+
["Email verification", features.emailVerification],
|
|
271
|
+
["Waitlist", features.waitlist],
|
|
272
|
+
["Password reset", features.passwordReset],
|
|
273
|
+
["Require username", features.requireUsername],
|
|
274
|
+
];
|
|
275
|
+
console.log(`Providers: ${providers.length > 0 ? providers.join(", ") : "none"}`);
|
|
276
|
+
for (const [label, enabled] of featureLines) {
|
|
277
|
+
console.log(`${label}: ${enabled ? "on" : "off"}`);
|
|
278
|
+
}
|
|
279
|
+
if (data.sessionDuration) {
|
|
280
|
+
console.log(`Session: ${data.sessionDuration}`);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
function printTemplateSummary(data) {
|
|
284
|
+
console.log(`Template: ${data.name} (${data.id})`);
|
|
285
|
+
if (data.slug)
|
|
286
|
+
console.log(`Slug: ${data.slug}`);
|
|
287
|
+
console.log(`Subject: ${data.subject ?? "-"}`);
|
|
288
|
+
if (data.currentVersion != null)
|
|
289
|
+
console.log(`Version: ${data.currentVersion}`);
|
|
290
|
+
if (Array.isArray(data.variables) && data.variables.length > 0) {
|
|
291
|
+
console.log(`Variables: ${data.variables.join(", ")}`);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
function printSurveySummary(data) {
|
|
295
|
+
console.log(`Survey: ${data.name} (${data.id})`);
|
|
296
|
+
console.log(`Type: ${data.type ?? "custom"} | Status: ${data.status ?? "draft"}`);
|
|
297
|
+
if (data.slug)
|
|
298
|
+
console.log(`Slug: ${data.slug}`);
|
|
299
|
+
const questions = Array.isArray(data.definition?.pages)
|
|
300
|
+
? data.definition.pages.flatMap((page) => page.elements ?? []).length
|
|
301
|
+
: undefined;
|
|
302
|
+
if (questions != null)
|
|
303
|
+
console.log(`Questions: ${questions}`);
|
|
304
|
+
}
|
|
305
|
+
function printProjectSummary(data) {
|
|
306
|
+
console.log(`Project: ${data.id}`);
|
|
307
|
+
console.log(`Name: ${data.name}`);
|
|
308
|
+
if (Array.isArray(data.enabledServices) && data.enabledServices.length > 0) {
|
|
309
|
+
console.log(`Services: ${data.enabledServices.join(", ")}`);
|
|
310
|
+
}
|
|
311
|
+
if (data.environment?.publicKey) {
|
|
312
|
+
console.log(`Environment: ${data.environment.publicKey}`);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
199
315
|
function normalizeJourneyRows(data) {
|
|
200
316
|
if (Array.isArray(data)) {
|
|
201
317
|
return data;
|
|
@@ -276,7 +392,7 @@ async function run() {
|
|
|
276
392
|
case "logout":
|
|
277
393
|
platform.logout();
|
|
278
394
|
return;
|
|
279
|
-
case "
|
|
395
|
+
case "project":
|
|
280
396
|
if (action === "list") {
|
|
281
397
|
const projects = await platform.listProjects();
|
|
282
398
|
table(projects, [
|
|
@@ -285,14 +401,25 @@ async function run() {
|
|
|
285
401
|
{ key: "enabledServices", label: "Services", width: 30 },
|
|
286
402
|
]);
|
|
287
403
|
}
|
|
404
|
+
else if (action === "show") {
|
|
405
|
+
if (!args[2]) {
|
|
406
|
+
console.error("Usage: ascendkit project show <project-id>");
|
|
407
|
+
process.exit(1);
|
|
408
|
+
}
|
|
409
|
+
printProjectSummary(await platform.showProject(args[2]));
|
|
410
|
+
}
|
|
411
|
+
else if (action === "env") {
|
|
412
|
+
await runProjectEnvironment(args.slice(2));
|
|
413
|
+
}
|
|
288
414
|
else if (action === "create") {
|
|
289
415
|
const flags = parseFlags(args.slice(2));
|
|
290
416
|
if (!flags.name) {
|
|
291
|
-
console.error("Usage: ascendkit
|
|
417
|
+
console.error("Usage: ascendkit project create --name <name> [--description <description>] [--services <s1,s2,...>]");
|
|
292
418
|
process.exit(1);
|
|
293
419
|
}
|
|
294
420
|
try {
|
|
295
|
-
|
|
421
|
+
const proj = await platform.createProject(flags.name, flags.description, flags.services?.split(","));
|
|
422
|
+
printProjectSummary(proj);
|
|
296
423
|
}
|
|
297
424
|
catch (err) {
|
|
298
425
|
let message = err instanceof Error ? err.message : String(err);
|
|
@@ -312,7 +439,7 @@ async function run() {
|
|
|
312
439
|
}
|
|
313
440
|
}
|
|
314
441
|
else {
|
|
315
|
-
console.error('Usage: ascendkit
|
|
442
|
+
console.error('Usage: ascendkit project list|create|show|env');
|
|
316
443
|
process.exit(1);
|
|
317
444
|
}
|
|
318
445
|
return;
|
|
@@ -329,8 +456,11 @@ async function run() {
|
|
|
329
456
|
case "verify":
|
|
330
457
|
await runVerify();
|
|
331
458
|
return;
|
|
332
|
-
case "
|
|
333
|
-
await
|
|
459
|
+
case "environment":
|
|
460
|
+
await runEnvironment(action, args.slice(2));
|
|
461
|
+
return;
|
|
462
|
+
case "keystore":
|
|
463
|
+
await runKeystore(action, args.slice(2));
|
|
334
464
|
return;
|
|
335
465
|
}
|
|
336
466
|
// Service commands (need environment key)
|
|
@@ -339,6 +469,7 @@ async function run() {
|
|
|
339
469
|
case "auth":
|
|
340
470
|
await runAuth(client, action, args.slice(2));
|
|
341
471
|
break;
|
|
472
|
+
case "template":
|
|
342
473
|
case "templates":
|
|
343
474
|
case "content":
|
|
344
475
|
await runContent(client, action, args.slice(2));
|
|
@@ -349,44 +480,224 @@ async function run() {
|
|
|
349
480
|
case "journey":
|
|
350
481
|
await runJourney(client, action, args.slice(2));
|
|
351
482
|
break;
|
|
352
|
-
case "email":
|
|
483
|
+
case "email-identity":
|
|
353
484
|
await runEmail(client, action, args.slice(2));
|
|
354
485
|
break;
|
|
355
486
|
case "webhook":
|
|
356
487
|
await runWebhook(client, action, args.slice(2));
|
|
357
488
|
break;
|
|
489
|
+
case "campaign":
|
|
490
|
+
await runCampaign(client, action, args.slice(2));
|
|
491
|
+
break;
|
|
492
|
+
case "import":
|
|
493
|
+
await runImport(client, action, args.slice(2));
|
|
494
|
+
break;
|
|
358
495
|
default:
|
|
359
496
|
console.error(`Unknown command: ${domain}`);
|
|
360
497
|
console.error('Run "ascendkit --help" for usage');
|
|
361
498
|
process.exit(1);
|
|
362
499
|
}
|
|
363
500
|
}
|
|
364
|
-
async function
|
|
365
|
-
|
|
501
|
+
async function runImport(client, source, rest) {
|
|
502
|
+
if (!source) {
|
|
503
|
+
console.log(HELP_SECTION.import);
|
|
504
|
+
return;
|
|
505
|
+
}
|
|
506
|
+
let action = rest[0];
|
|
507
|
+
let args = rest;
|
|
508
|
+
if (source === "create-migration-journey") {
|
|
509
|
+
source = "migration-journey";
|
|
510
|
+
action = "create";
|
|
511
|
+
}
|
|
512
|
+
if (source === "clerk" && (action === "preview" || action === "run")) {
|
|
513
|
+
args = rest.slice(1);
|
|
514
|
+
}
|
|
515
|
+
else if (source === "clerk") {
|
|
516
|
+
action = flagsFromLegacy(rest).execute ? "run" : "preview";
|
|
517
|
+
}
|
|
518
|
+
const flags = parseFlags(args);
|
|
519
|
+
if (source === "migration-journey" && action === "create") {
|
|
520
|
+
const result = await importCmd.instantiateMigrationJourney(client, flags["from-identity"]);
|
|
521
|
+
output(result);
|
|
522
|
+
console.log("\nNext steps:");
|
|
523
|
+
console.log(" ascendkit journey list — review created journeys");
|
|
524
|
+
console.log(" ascendkit template list — review migration email templates");
|
|
525
|
+
console.log(" ascendkit journey activate <journey-id> — activate when ready");
|
|
526
|
+
return;
|
|
527
|
+
}
|
|
528
|
+
if (source !== "clerk") {
|
|
529
|
+
console.error(`Unsupported import source: ${source}`);
|
|
530
|
+
console.error("Supported sources: clerk");
|
|
531
|
+
console.error('Run "ascendkit help import" for usage');
|
|
532
|
+
process.exit(1);
|
|
533
|
+
}
|
|
534
|
+
const dryRun = action !== "run";
|
|
535
|
+
const hasUsers = flags.users !== undefined;
|
|
536
|
+
const hasSettings = flags.settings !== undefined;
|
|
537
|
+
// If neither --users nor --settings is passed, both default to true.
|
|
538
|
+
// If either is passed, only the specified phases run.
|
|
539
|
+
const importUsers = (!hasUsers && !hasSettings) || hasUsers;
|
|
540
|
+
const importSettings = (!hasUsers && !hasSettings) || hasSettings;
|
|
541
|
+
const apiKey = flags["api-key"];
|
|
542
|
+
const filePath = flags.file;
|
|
543
|
+
if (!apiKey && !filePath) {
|
|
544
|
+
console.error("Usage: ascendkit import clerk preview|run --api-key <key> | --file <path>");
|
|
545
|
+
process.exit(1);
|
|
546
|
+
}
|
|
547
|
+
let clerkUsers = [];
|
|
548
|
+
if (filePath) {
|
|
549
|
+
console.log(`Reading Clerk export from ${filePath}...`);
|
|
550
|
+
const rawUsers = importCmd.parseClerkExport(filePath);
|
|
551
|
+
clerkUsers = rawUsers.map(importCmd.transformClerkUser);
|
|
552
|
+
console.log(`Parsed ${clerkUsers.length} users from file.`);
|
|
553
|
+
}
|
|
554
|
+
else {
|
|
555
|
+
console.log("Fetching users from Clerk API...");
|
|
556
|
+
const rawUsers = await importCmd.fetchClerkUsers(apiKey, flags["instance-url"]);
|
|
557
|
+
clerkUsers = rawUsers.map(importCmd.transformClerkUser);
|
|
558
|
+
console.log(`Fetched ${clerkUsers.length} users from Clerk.`);
|
|
559
|
+
}
|
|
560
|
+
if (clerkUsers.length === 0 && importUsers) {
|
|
561
|
+
console.log("No users to import.");
|
|
562
|
+
return;
|
|
563
|
+
}
|
|
564
|
+
if (dryRun) {
|
|
565
|
+
console.log("\n--- DRY RUN (pass --execute to apply changes) ---");
|
|
566
|
+
}
|
|
567
|
+
// Batch in chunks of 500 (backend max)
|
|
568
|
+
const batchSize = 500;
|
|
569
|
+
let totalImported = 0;
|
|
570
|
+
const allDuplicates = [];
|
|
571
|
+
const allErrors = [];
|
|
572
|
+
const allWarnings = [];
|
|
573
|
+
if (importUsers) {
|
|
574
|
+
for (let i = 0; i < clerkUsers.length; i += batchSize) {
|
|
575
|
+
const batch = clerkUsers.slice(i, i + batchSize);
|
|
576
|
+
const batchNum = Math.floor(i / batchSize) + 1;
|
|
577
|
+
const totalBatches = Math.ceil(clerkUsers.length / batchSize);
|
|
578
|
+
if (totalBatches > 1) {
|
|
579
|
+
console.log(`\nBatch ${batchNum}/${totalBatches} (${batch.length} users)...`);
|
|
580
|
+
}
|
|
581
|
+
const result = await importCmd.importUsers(client, {
|
|
582
|
+
source: "clerk",
|
|
583
|
+
users: batch,
|
|
584
|
+
dryRun,
|
|
585
|
+
});
|
|
586
|
+
totalImported += result.imported;
|
|
587
|
+
allDuplicates.push(...result.duplicates);
|
|
588
|
+
allErrors.push(...result.errors);
|
|
589
|
+
allWarnings.push(...result.warnings);
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
else {
|
|
593
|
+
console.log("Skipping user import (--settings only).");
|
|
594
|
+
}
|
|
595
|
+
if (importSettings) {
|
|
596
|
+
const derived = importCmd.deriveSettingsFromUsers(clerkUsers);
|
|
597
|
+
// --providers override lets the user pick exactly which SSO to enable
|
|
598
|
+
const providerOverride = flags.providers;
|
|
599
|
+
const ssoProviders = providerOverride
|
|
600
|
+
? providerOverride.split(",").map((s) => s.trim()).filter(Boolean)
|
|
601
|
+
: derived.providers;
|
|
602
|
+
const providers = [];
|
|
603
|
+
if (derived.hasCredentials)
|
|
604
|
+
providers.push("credentials");
|
|
605
|
+
providers.push(...ssoProviders);
|
|
606
|
+
console.log("\nAuth settings from user data:");
|
|
607
|
+
console.log(` Credentials (email/password): ${derived.hasCredentials ? "yes" : "no"}`);
|
|
608
|
+
console.log(` SSO providers: ${ssoProviders.length > 0 ? ssoProviders.join(", ") : "none"}`);
|
|
609
|
+
if (providerOverride) {
|
|
610
|
+
console.log(` (overridden via --providers)`);
|
|
611
|
+
}
|
|
612
|
+
if (providers.length > 0) {
|
|
613
|
+
const settingsPayload = {
|
|
614
|
+
source: "clerk",
|
|
615
|
+
users: [],
|
|
616
|
+
authSettings: { enabledProviders: providers },
|
|
617
|
+
dryRun,
|
|
618
|
+
};
|
|
619
|
+
const settingsResult = await importCmd.importUsers(client, settingsPayload);
|
|
620
|
+
allWarnings.push(...settingsResult.warnings);
|
|
621
|
+
if (!dryRun) {
|
|
622
|
+
console.log(`\nAuth settings applied: ${providers.join(", ")}`);
|
|
623
|
+
console.log("Configure OAuth secrets in the portal under Auth → OAuth settings.");
|
|
624
|
+
}
|
|
625
|
+
else {
|
|
626
|
+
console.log(`\nWill enable: ${providers.join(", ")}`);
|
|
627
|
+
console.log("Use --providers linkedin to pick specific SSO providers.");
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
else {
|
|
632
|
+
console.log("Skipping auth settings import (--users only).");
|
|
633
|
+
}
|
|
634
|
+
console.log(`\n--- Import Summary ---`);
|
|
635
|
+
if (importUsers) {
|
|
636
|
+
console.log(`Imported: ${totalImported}`);
|
|
637
|
+
if (allDuplicates.length > 0) {
|
|
638
|
+
console.log(`Duplicates: ${allDuplicates.length}`);
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
if (allErrors.length > 0) {
|
|
642
|
+
console.log(`Errors: ${allErrors.length}`);
|
|
643
|
+
for (const err of allErrors.slice(0, 10)) {
|
|
644
|
+
console.log(` - ${err.email}: ${err.reason}`);
|
|
645
|
+
}
|
|
646
|
+
if (allErrors.length > 10) {
|
|
647
|
+
console.log(` ... and ${allErrors.length - 10} more`);
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
for (const warning of allWarnings) {
|
|
651
|
+
console.log(`Warning: ${warning}`);
|
|
652
|
+
}
|
|
653
|
+
if (dryRun) {
|
|
654
|
+
console.log("\nThis was a dry run. Pass --execute to apply changes.");
|
|
655
|
+
}
|
|
656
|
+
else if (totalImported > 0) {
|
|
657
|
+
console.log("\nTo set up migration emails, run:\n" +
|
|
658
|
+
" ascendkit import migration-journey create");
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
function flagsFromLegacy(args) {
|
|
662
|
+
return parseFlags(args);
|
|
663
|
+
}
|
|
664
|
+
async function runProjectEnvironment(rest) {
|
|
665
|
+
const action = rest[0];
|
|
666
|
+
const target = rest[1];
|
|
366
667
|
switch (action) {
|
|
367
668
|
case "list":
|
|
368
|
-
if (!
|
|
369
|
-
console.error("Usage: ascendkit env list
|
|
669
|
+
if (!target) {
|
|
670
|
+
console.error("Usage: ascendkit project env list <project-id>");
|
|
370
671
|
process.exit(1);
|
|
371
672
|
}
|
|
372
|
-
table(await platform.listEnvironments(
|
|
673
|
+
table(await platform.listEnvironments(target), [
|
|
674
|
+
{ key: "id", label: "ID" },
|
|
373
675
|
{ key: "name", label: "Name", width: 20 },
|
|
374
676
|
{ key: "tier", label: "Tier" },
|
|
375
677
|
{ key: "publicKey", label: "Public Key" },
|
|
376
678
|
]);
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
679
|
+
return;
|
|
680
|
+
default:
|
|
681
|
+
console.error("Usage: ascendkit project env list <project-id>");
|
|
682
|
+
process.exit(1);
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
async function runEnvironment(action, rest) {
|
|
686
|
+
const flags = parseFlags(rest);
|
|
687
|
+
const ctx = loadEnvContext();
|
|
688
|
+
if (!ctx) {
|
|
689
|
+
console.error("No environment set. Run: ascendkit set-env <public-key>");
|
|
690
|
+
process.exit(1);
|
|
691
|
+
}
|
|
692
|
+
switch (action) {
|
|
693
|
+
case "show":
|
|
694
|
+
output(await platform.getEnvironment(ctx.projectId, ctx.environmentId));
|
|
695
|
+
return;
|
|
385
696
|
case "promote": {
|
|
386
|
-
const envId = rest[0];
|
|
697
|
+
const envId = rest[0] && !rest[0].startsWith("--") ? rest[0] : ctx.environmentId;
|
|
387
698
|
const target = flags.target;
|
|
388
699
|
if (!envId || !target) {
|
|
389
|
-
console.error("Usage: ascendkit
|
|
700
|
+
console.error("Usage: ascendkit environment promote [<env-id>] --target <tier>");
|
|
390
701
|
process.exit(1);
|
|
391
702
|
}
|
|
392
703
|
try {
|
|
@@ -410,14 +721,10 @@ async function runEnv(action, rest) {
|
|
|
410
721
|
console.error(message);
|
|
411
722
|
process.exit(1);
|
|
412
723
|
}
|
|
413
|
-
|
|
724
|
+
return;
|
|
414
725
|
}
|
|
415
726
|
case "update": {
|
|
416
|
-
const envId = rest[0];
|
|
417
|
-
if (!envId || !flags.project) {
|
|
418
|
-
console.error("Usage: ascendkit env update <env-id> --project <project-id> [--name <name>] [--description <desc>]");
|
|
419
|
-
process.exit(1);
|
|
420
|
-
}
|
|
727
|
+
const envId = rest[0] && !rest[0].startsWith("--") ? rest[0] : ctx.environmentId;
|
|
421
728
|
const name = flags.name;
|
|
422
729
|
const description = flags.description;
|
|
423
730
|
if (!name && description === undefined) {
|
|
@@ -425,7 +732,7 @@ async function runEnv(action, rest) {
|
|
|
425
732
|
process.exit(1);
|
|
426
733
|
}
|
|
427
734
|
try {
|
|
428
|
-
const result = await platform.updateEnvironment(
|
|
735
|
+
const result = await platform.updateEnvironment(ctx.projectId, envId, name, description);
|
|
429
736
|
console.log("Environment updated:");
|
|
430
737
|
console.log(JSON.stringify(result, null, 2));
|
|
431
738
|
}
|
|
@@ -445,49 +752,137 @@ async function runEnv(action, rest) {
|
|
|
445
752
|
console.error(message);
|
|
446
753
|
process.exit(1);
|
|
447
754
|
}
|
|
448
|
-
|
|
755
|
+
return;
|
|
449
756
|
}
|
|
450
757
|
default:
|
|
451
|
-
console.error(`Unknown
|
|
452
|
-
console.error("Usage: ascendkit
|
|
758
|
+
console.error(`Unknown environment command: ${action}`);
|
|
759
|
+
console.error("Usage: ascendkit environment show|update|promote");
|
|
453
760
|
process.exit(1);
|
|
454
761
|
}
|
|
455
762
|
}
|
|
456
|
-
async function
|
|
457
|
-
const
|
|
763
|
+
async function runKeystore(action, rest) {
|
|
764
|
+
const ctx = loadEnvContext();
|
|
765
|
+
if (!ctx) {
|
|
766
|
+
console.error("No environment set. Run: ascendkit set-env <public-key>");
|
|
767
|
+
process.exit(1);
|
|
768
|
+
}
|
|
769
|
+
const current = await platform.getEnvironment(ctx.projectId, ctx.environmentId);
|
|
770
|
+
const vars = { ...(current.variables ?? {}) };
|
|
771
|
+
const systemVars = Array.isArray(current.systemVariables)
|
|
772
|
+
? current.systemVariables
|
|
773
|
+
: [];
|
|
458
774
|
switch (action) {
|
|
459
|
-
case "
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
775
|
+
case "set": {
|
|
776
|
+
const key = rest[0];
|
|
777
|
+
const value = rest[1];
|
|
778
|
+
if (!key || value === undefined) {
|
|
779
|
+
console.error("Usage: ascendkit keystore set <key> <value>");
|
|
780
|
+
process.exit(1);
|
|
781
|
+
}
|
|
782
|
+
vars[key] = value;
|
|
783
|
+
await platform.updateEnvironmentVariables(ctx.projectId, ctx.environmentId, vars);
|
|
784
|
+
console.log(`Saved ${key}=${value}`);
|
|
785
|
+
return;
|
|
786
|
+
}
|
|
787
|
+
case "remove": {
|
|
788
|
+
const key = rest[0];
|
|
789
|
+
if (!key) {
|
|
790
|
+
console.error("Usage: ascendkit keystore remove <key>");
|
|
791
|
+
process.exit(1);
|
|
792
|
+
}
|
|
793
|
+
if (!(key in vars)) {
|
|
794
|
+
console.error(`Variable "${key}" not found.`);
|
|
795
|
+
process.exit(1);
|
|
796
|
+
}
|
|
797
|
+
delete vars[key];
|
|
798
|
+
await platform.updateEnvironmentVariables(ctx.projectId, ctx.environmentId, vars);
|
|
799
|
+
console.log(`Removed ${key}`);
|
|
800
|
+
return;
|
|
801
|
+
}
|
|
802
|
+
case "list": {
|
|
803
|
+
const entries = Object.entries(vars).map(([key, value]) => ({ key, value }));
|
|
804
|
+
if (entries.length === 0) {
|
|
805
|
+
console.log("No custom variables set.");
|
|
476
806
|
}
|
|
477
807
|
else {
|
|
478
|
-
|
|
808
|
+
console.log("Custom variables:");
|
|
809
|
+
table(entries, [
|
|
810
|
+
{ key: "key", label: "Key", width: 32 },
|
|
811
|
+
{ key: "value", label: "Value", width: 60 },
|
|
812
|
+
]);
|
|
479
813
|
}
|
|
814
|
+
if (systemVars.length > 0) {
|
|
815
|
+
console.log(entries.length > 0 ? "\nSystem variables:" : "System variables:");
|
|
816
|
+
table(systemVars.map((item) => ({
|
|
817
|
+
key: item.key,
|
|
818
|
+
value: item.valuePreview,
|
|
819
|
+
availability: item.availability,
|
|
820
|
+
})), [
|
|
821
|
+
{ key: "key", label: "Key", width: 24 },
|
|
822
|
+
{ key: "value", label: "Value", width: 48 },
|
|
823
|
+
{ key: "availability", label: "Availability", width: 22 },
|
|
824
|
+
]);
|
|
825
|
+
}
|
|
826
|
+
return;
|
|
827
|
+
}
|
|
828
|
+
default:
|
|
829
|
+
console.error(`Unknown keystore command: ${action}`);
|
|
830
|
+
console.error("Usage: ascendkit keystore list|set|remove");
|
|
831
|
+
process.exit(1);
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
async function runAuth(client, action, rest) {
|
|
835
|
+
const flags = parseFlags(rest);
|
|
836
|
+
const normalizedAction = !action ? "show" :
|
|
837
|
+
action === "settings" ? (rest[0] === "update" ? "update" : "show") :
|
|
838
|
+
action === "show" ? "show" :
|
|
839
|
+
action === "update" ? "update" :
|
|
840
|
+
action;
|
|
841
|
+
switch (normalizedAction) {
|
|
842
|
+
case "show":
|
|
843
|
+
printAuthSettingsSummary(await auth.getSettings(client));
|
|
844
|
+
break;
|
|
845
|
+
case "update": {
|
|
846
|
+
const params = {};
|
|
847
|
+
if (flags.providers)
|
|
848
|
+
params.providers = flags.providers.split(",");
|
|
849
|
+
if (flags["email-verification"] || flags.waitlist || flags["password-reset"]) {
|
|
850
|
+
params.features = {};
|
|
851
|
+
if (flags["email-verification"])
|
|
852
|
+
params.features.emailVerification = flags["email-verification"] === "true";
|
|
853
|
+
if (flags.waitlist)
|
|
854
|
+
params.features.waitlist = flags.waitlist === "true";
|
|
855
|
+
if (flags["password-reset"])
|
|
856
|
+
params.features.passwordReset = flags["password-reset"] === "true";
|
|
857
|
+
}
|
|
858
|
+
if (flags["session-duration"])
|
|
859
|
+
params.sessionDuration = flags["session-duration"];
|
|
860
|
+
printAuthSettingsSummary(await auth.updateSettings(client, params));
|
|
480
861
|
break;
|
|
862
|
+
}
|
|
863
|
+
case "provider":
|
|
481
864
|
case "providers":
|
|
482
|
-
if (
|
|
483
|
-
|
|
484
|
-
|
|
865
|
+
if (normalizedAction === "providers" || rest[0] === "set") {
|
|
866
|
+
const providersArg = normalizedAction === "providers" ? rest[0] : rest[1];
|
|
867
|
+
if (!providersArg) {
|
|
868
|
+
console.error("Usage: ascendkit auth provider set <p1,p2,...>");
|
|
869
|
+
process.exit(1);
|
|
870
|
+
}
|
|
871
|
+
printAuthSettingsSummary(await auth.updateProviders(client, providersArg.split(",")));
|
|
872
|
+
}
|
|
873
|
+
else {
|
|
874
|
+
const settings = await auth.getSettings(client);
|
|
875
|
+
const providers = Array.isArray(settings.providers) ? settings.providers.map((provider) => ({ provider })) : [];
|
|
876
|
+
if (providers.length === 0)
|
|
877
|
+
console.log("No providers configured.");
|
|
878
|
+
else
|
|
879
|
+
table(providers, [{ key: "provider", label: "Provider", width: 20 }]);
|
|
485
880
|
}
|
|
486
|
-
output(await auth.updateProviders(client, rest[0].split(",")));
|
|
487
881
|
break;
|
|
488
882
|
case "oauth": {
|
|
489
|
-
|
|
490
|
-
|
|
883
|
+
const oauthAction = rest[0] === "open" || rest[0] === "set" || rest[0] === "remove" ? rest[0] : "open";
|
|
884
|
+
const provider = oauthAction === "open" && rest[0] !== "open" ? rest[0] : rest[1];
|
|
885
|
+
if (oauthAction === "set") {
|
|
491
886
|
if (!provider || !flags["client-id"]) {
|
|
492
887
|
console.error("Usage: ascendkit auth oauth set <provider> --client-id <id> [--client-secret <secret> | --client-secret-stdin] [--callback-url <url>]");
|
|
493
888
|
process.exit(1);
|
|
@@ -507,28 +902,60 @@ async function runAuth(client, action, rest) {
|
|
|
507
902
|
console.error("Client secret cannot be empty.");
|
|
508
903
|
process.exit(1);
|
|
509
904
|
}
|
|
510
|
-
|
|
905
|
+
await auth.updateOAuthCredentials(client, provider, flags["client-id"], clientSecret, flags["callback-url"]);
|
|
906
|
+
console.log(`Saved OAuth credentials for ${provider}.`);
|
|
907
|
+
}
|
|
908
|
+
else if (oauthAction === "remove") {
|
|
909
|
+
if (!provider) {
|
|
910
|
+
console.error("Usage: ascendkit auth oauth remove <provider>");
|
|
911
|
+
process.exit(1);
|
|
912
|
+
}
|
|
913
|
+
await auth.deleteOAuthCredentials(client, provider);
|
|
914
|
+
console.log(`Removed OAuth credentials for ${provider}.`);
|
|
511
915
|
}
|
|
512
916
|
else {
|
|
513
|
-
if (!
|
|
514
|
-
console.error("Usage: ascendkit auth oauth <provider>");
|
|
917
|
+
if (!provider) {
|
|
918
|
+
console.error("Usage: ascendkit auth oauth open <provider>");
|
|
515
919
|
process.exit(1);
|
|
516
920
|
}
|
|
517
921
|
const portalUrl = process.env.ASCENDKIT_PORTAL_URL ?? "http://localhost:3000";
|
|
518
|
-
const url = auth.getOAuthSetupUrl(portalUrl,
|
|
519
|
-
console.log(`Opening browser to configure ${
|
|
922
|
+
const url = auth.getOAuthSetupUrl(portalUrl, provider, client.currentPublicKey ?? undefined);
|
|
923
|
+
console.log(`Opening browser to configure ${provider} OAuth credentials...`);
|
|
520
924
|
console.log(url);
|
|
521
925
|
openBrowser(url);
|
|
522
926
|
}
|
|
523
927
|
break;
|
|
524
928
|
}
|
|
929
|
+
case "user":
|
|
525
930
|
case "users":
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
931
|
+
if (normalizedAction === "users" || !rest[0] || rest[0] === "list") {
|
|
932
|
+
table(await auth.listUsers(client), [
|
|
933
|
+
{ key: "id", label: "ID" },
|
|
934
|
+
{ key: "email", label: "Email", width: 35 },
|
|
935
|
+
{ key: "name", label: "Name", width: 25 },
|
|
936
|
+
{ key: "status", label: "Status" },
|
|
937
|
+
]);
|
|
938
|
+
}
|
|
939
|
+
else if (rest[0] === "remove") {
|
|
940
|
+
if (!rest[1]) {
|
|
941
|
+
console.error("Usage: ascendkit auth user remove <user-id>");
|
|
942
|
+
process.exit(1);
|
|
943
|
+
}
|
|
944
|
+
await auth.deleteUser(client, rest[1]);
|
|
945
|
+
console.log(`Removed user ${rest[1]}.`);
|
|
946
|
+
}
|
|
947
|
+
else if (rest[0] === "reactivate") {
|
|
948
|
+
if (!rest[1]) {
|
|
949
|
+
console.error("Usage: ascendkit auth user reactivate <user-id>");
|
|
950
|
+
process.exit(1);
|
|
951
|
+
}
|
|
952
|
+
await auth.reactivateUser(client, rest[1]);
|
|
953
|
+
console.log(`Reactivated user ${rest[1]}.`);
|
|
954
|
+
}
|
|
955
|
+
else {
|
|
956
|
+
console.error(`Unknown auth user command: ${rest[0]}`);
|
|
957
|
+
process.exit(1);
|
|
958
|
+
}
|
|
532
959
|
break;
|
|
533
960
|
default:
|
|
534
961
|
console.error(`Unknown auth command: ${action}`);
|
|
@@ -537,13 +964,20 @@ async function runAuth(client, action, rest) {
|
|
|
537
964
|
}
|
|
538
965
|
async function runContent(client, action, rest) {
|
|
539
966
|
const flags = parseFlags(rest);
|
|
540
|
-
|
|
967
|
+
if (!action) {
|
|
968
|
+
console.log(HELP_SECTION.template);
|
|
969
|
+
return;
|
|
970
|
+
}
|
|
971
|
+
const normalizedAction = action === "show" || action === "get" ? "show" :
|
|
972
|
+
action === "remove" || action === "delete" ? "remove" :
|
|
973
|
+
action;
|
|
974
|
+
switch (normalizedAction) {
|
|
541
975
|
case "create":
|
|
542
976
|
if (!flags.name || !flags.subject || !flags["body-html"] || !flags["body-text"]) {
|
|
543
|
-
console.error("Usage: ascendkit
|
|
977
|
+
console.error("Usage: ascendkit template create --name <n> --subject <s> --body-html <h> --body-text <t> [--slug <slug>] [--description <desc>]");
|
|
544
978
|
process.exit(1);
|
|
545
979
|
}
|
|
546
|
-
|
|
980
|
+
printTemplateSummary(await content.createTemplate(client, {
|
|
547
981
|
name: flags.name, subject: flags.subject,
|
|
548
982
|
bodyHtml: flags["body-html"], bodyText: flags["body-text"],
|
|
549
983
|
slug: flags.slug, description: flags.description,
|
|
@@ -566,64 +1000,79 @@ async function runContent(client, action, rest) {
|
|
|
566
1000
|
]);
|
|
567
1001
|
break;
|
|
568
1002
|
}
|
|
569
|
-
case "
|
|
1003
|
+
case "show":
|
|
570
1004
|
if (!rest[0]) {
|
|
571
|
-
console.error("Usage: ascendkit
|
|
1005
|
+
console.error("Usage: ascendkit template show <template-id>");
|
|
572
1006
|
process.exit(1);
|
|
573
1007
|
}
|
|
574
|
-
|
|
1008
|
+
printTemplateSummary(await content.getTemplate(client, rest[0]));
|
|
575
1009
|
break;
|
|
576
1010
|
case "update":
|
|
577
1011
|
if (!rest[0]) {
|
|
578
|
-
console.error("Usage: ascendkit
|
|
1012
|
+
console.error("Usage: ascendkit template update <template-id> [--flags]");
|
|
579
1013
|
process.exit(1);
|
|
580
1014
|
}
|
|
581
|
-
|
|
1015
|
+
printTemplateSummary(await content.updateTemplate(client, rest[0], {
|
|
582
1016
|
subject: flags.subject,
|
|
583
1017
|
bodyHtml: flags["body-html"],
|
|
584
1018
|
bodyText: flags["body-text"],
|
|
585
1019
|
changeNote: flags["change-note"],
|
|
586
1020
|
}));
|
|
587
1021
|
break;
|
|
588
|
-
case "
|
|
1022
|
+
case "remove":
|
|
589
1023
|
if (!rest[0]) {
|
|
590
|
-
console.error("Usage: ascendkit
|
|
1024
|
+
console.error("Usage: ascendkit template remove <template-id>");
|
|
591
1025
|
process.exit(1);
|
|
592
1026
|
}
|
|
593
|
-
|
|
1027
|
+
await content.deleteTemplate(client, rest[0]);
|
|
1028
|
+
console.log(`Removed template ${rest[0]}.`);
|
|
594
1029
|
break;
|
|
595
1030
|
case "versions":
|
|
596
|
-
if (!rest[0]) {
|
|
597
|
-
console.error("Usage: ascendkit templates versions <template-id>");
|
|
598
|
-
process.exit(1);
|
|
599
|
-
}
|
|
600
|
-
output(await content.listVersions(client, rest[0]));
|
|
601
|
-
break;
|
|
602
1031
|
case "version":
|
|
603
|
-
if (
|
|
604
|
-
|
|
605
|
-
|
|
1032
|
+
if (normalizedAction === "versions" || rest[0] === "list") {
|
|
1033
|
+
const templateId = normalizedAction === "versions" ? rest[0] : rest[1];
|
|
1034
|
+
if (!templateId) {
|
|
1035
|
+
console.error("Usage: ascendkit template version list <template-id>");
|
|
1036
|
+
process.exit(1);
|
|
1037
|
+
}
|
|
1038
|
+
const versions = await content.listVersions(client, templateId);
|
|
1039
|
+
table(versions, [
|
|
1040
|
+
{ key: "versionNumber", label: "Version" },
|
|
1041
|
+
{ key: "createdAt", label: "Created", width: 24 },
|
|
1042
|
+
{ key: "changeNote", label: "Change note", width: 40 },
|
|
1043
|
+
]);
|
|
1044
|
+
}
|
|
1045
|
+
else {
|
|
1046
|
+
const templateId = normalizedAction === "version" && rest[0] === "show" ? rest[1] : normalizedAction === "version" ? rest[0] : rest[1];
|
|
1047
|
+
const versionNumber = normalizedAction === "version" && rest[0] === "show" ? rest[2] : normalizedAction === "version" ? rest[1] : rest[2];
|
|
1048
|
+
if (!templateId || !versionNumber) {
|
|
1049
|
+
console.error("Usage: ascendkit template version show <template-id> <n>");
|
|
1050
|
+
process.exit(1);
|
|
1051
|
+
}
|
|
1052
|
+
output(await content.getVersion(client, templateId, parseInt(versionNumber, 10)));
|
|
606
1053
|
}
|
|
607
|
-
output(await content.getVersion(client, rest[0], parseInt(rest[1], 10)));
|
|
608
1054
|
break;
|
|
609
1055
|
default:
|
|
610
|
-
console.error(`Unknown
|
|
1056
|
+
console.error(`Unknown template command: ${action}`);
|
|
611
1057
|
process.exit(1);
|
|
612
1058
|
}
|
|
613
1059
|
}
|
|
614
1060
|
async function runSurvey(client, action, rest) {
|
|
615
1061
|
const flags = parseFlags(rest);
|
|
616
|
-
|
|
1062
|
+
const normalizedAction = action === "show" || action === "get" ? "show" :
|
|
1063
|
+
action === "remove" || action === "delete" ? "remove" :
|
|
1064
|
+
action;
|
|
1065
|
+
if (!normalizedAction) {
|
|
617
1066
|
console.log(HELP_SECTION.survey);
|
|
618
1067
|
return;
|
|
619
1068
|
}
|
|
620
|
-
switch (
|
|
1069
|
+
switch (normalizedAction) {
|
|
621
1070
|
case "create":
|
|
622
1071
|
if (!flags.name) {
|
|
623
1072
|
console.error("Usage: ascendkit survey create --name <n> [--type nps|csat|custom]");
|
|
624
1073
|
process.exit(1);
|
|
625
1074
|
}
|
|
626
|
-
|
|
1075
|
+
printSurveySummary(await surveys.createSurvey(client, {
|
|
627
1076
|
name: flags.name,
|
|
628
1077
|
type: flags.type ?? "custom",
|
|
629
1078
|
definition: flags.definition ? JSON.parse(flags.definition) : undefined,
|
|
@@ -637,30 +1086,31 @@ async function runSurvey(client, action, rest) {
|
|
|
637
1086
|
{ key: "status", label: "Status" },
|
|
638
1087
|
]);
|
|
639
1088
|
break;
|
|
640
|
-
case "
|
|
1089
|
+
case "show":
|
|
641
1090
|
if (!rest[0]) {
|
|
642
|
-
console.error("Usage: ascendkit survey
|
|
1091
|
+
console.error("Usage: ascendkit survey show <survey-id>");
|
|
643
1092
|
process.exit(1);
|
|
644
1093
|
}
|
|
645
|
-
|
|
1094
|
+
printSurveySummary(await surveys.getSurvey(client, rest[0]));
|
|
646
1095
|
break;
|
|
647
1096
|
case "update":
|
|
648
1097
|
if (!rest[0]) {
|
|
649
1098
|
console.error("Usage: ascendkit survey update <survey-id> [--flags]");
|
|
650
1099
|
process.exit(1);
|
|
651
1100
|
}
|
|
652
|
-
|
|
1101
|
+
printSurveySummary(await surveys.updateSurvey(client, rest[0], {
|
|
653
1102
|
name: flags.name,
|
|
654
1103
|
status: flags.status,
|
|
655
1104
|
definition: flags.definition ? JSON.parse(flags.definition) : undefined,
|
|
656
1105
|
}));
|
|
657
1106
|
break;
|
|
658
|
-
case "
|
|
1107
|
+
case "remove":
|
|
659
1108
|
if (!rest[0]) {
|
|
660
|
-
console.error("Usage: ascendkit survey
|
|
1109
|
+
console.error("Usage: ascendkit survey remove <survey-id>");
|
|
661
1110
|
process.exit(1);
|
|
662
1111
|
}
|
|
663
|
-
|
|
1112
|
+
await surveys.deleteSurvey(client, rest[0]);
|
|
1113
|
+
console.log(`Removed survey ${rest[0]}.`);
|
|
664
1114
|
break;
|
|
665
1115
|
case "distribute":
|
|
666
1116
|
if (!rest[0] || !flags.users) {
|
|
@@ -670,11 +1120,19 @@ async function runSurvey(client, action, rest) {
|
|
|
670
1120
|
output(await surveys.distributeSurvey(client, rest[0], flags.users.split(",")));
|
|
671
1121
|
break;
|
|
672
1122
|
case "invitations":
|
|
673
|
-
|
|
674
|
-
|
|
1123
|
+
case "invitation":
|
|
1124
|
+
if (normalizedAction === "invitations" || rest[0] === "list") {
|
|
1125
|
+
const surveyId = normalizedAction === "invitations" ? rest[0] : rest[1];
|
|
1126
|
+
if (!surveyId) {
|
|
1127
|
+
console.error("Usage: ascendkit survey invitation list <survey-id>");
|
|
1128
|
+
process.exit(1);
|
|
1129
|
+
}
|
|
1130
|
+
output(await surveys.listInvitations(client, surveyId));
|
|
1131
|
+
}
|
|
1132
|
+
else {
|
|
1133
|
+
console.error(`Unknown survey invitation command: ${rest[0]}`);
|
|
675
1134
|
process.exit(1);
|
|
676
1135
|
}
|
|
677
|
-
output(await surveys.listInvitations(client, rest[0]));
|
|
678
1136
|
break;
|
|
679
1137
|
case "analytics":
|
|
680
1138
|
if (!rest[0]) {
|
|
@@ -683,96 +1141,121 @@ async function runSurvey(client, action, rest) {
|
|
|
683
1141
|
}
|
|
684
1142
|
output(await surveys.getAnalytics(client, rest[0]));
|
|
685
1143
|
break;
|
|
686
|
-
case "export-definition":
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
const
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
1144
|
+
case "export-definition":
|
|
1145
|
+
case "import-definition":
|
|
1146
|
+
case "definition": {
|
|
1147
|
+
const definitionAction = normalizedAction === "definition" ? rest[0] :
|
|
1148
|
+
normalizedAction === "export-definition" ? "export" :
|
|
1149
|
+
"import";
|
|
1150
|
+
const surveyId = normalizedAction === "definition" ? rest[1] : rest[0];
|
|
1151
|
+
if (definitionAction === "export") {
|
|
1152
|
+
if (!surveyId) {
|
|
1153
|
+
console.error("Usage: ascendkit survey definition export <survey-id> [--out <file>]");
|
|
1154
|
+
process.exit(1);
|
|
1155
|
+
}
|
|
1156
|
+
const survey = await surveys.getSurvey(client, surveyId);
|
|
1157
|
+
const text = `${JSON.stringify(survey.definition ?? {}, null, 2)}\n`;
|
|
1158
|
+
const outPath = flags.out || flags.file;
|
|
1159
|
+
if (outPath) {
|
|
1160
|
+
writeFileSync(outPath, text, "utf8");
|
|
1161
|
+
console.log(`Wrote survey definition to ${outPath}`);
|
|
1162
|
+
}
|
|
1163
|
+
else {
|
|
1164
|
+
process.stdout.write(text);
|
|
1165
|
+
}
|
|
697
1166
|
}
|
|
698
1167
|
else {
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
process.exit(1);
|
|
1168
|
+
if (!surveyId || !(flags.in || flags.file)) {
|
|
1169
|
+
console.error("Usage: ascendkit survey definition import <survey-id> --in <file>");
|
|
1170
|
+
process.exit(1);
|
|
1171
|
+
}
|
|
1172
|
+
const inPath = flags.in || flags.file;
|
|
1173
|
+
const raw = readFileSync(inPath, "utf8");
|
|
1174
|
+
const parsed = JSON.parse(raw);
|
|
1175
|
+
const candidate = parsed && typeof parsed === "object" && "definition" in parsed
|
|
1176
|
+
? parsed.definition
|
|
1177
|
+
: parsed;
|
|
1178
|
+
if (!candidate || typeof candidate !== "object" || Array.isArray(candidate)) {
|
|
1179
|
+
console.error("Invalid definition JSON: expected an object or { definition: object }");
|
|
1180
|
+
process.exit(1);
|
|
1181
|
+
}
|
|
1182
|
+
printSurveySummary(await surveys.updateSurvey(client, surveyId, {
|
|
1183
|
+
definition: candidate,
|
|
1184
|
+
}));
|
|
717
1185
|
}
|
|
718
|
-
output(await surveys.updateSurvey(client, rest[0], {
|
|
719
|
-
definition: candidate,
|
|
720
|
-
}));
|
|
721
1186
|
break;
|
|
722
1187
|
}
|
|
723
1188
|
case "list-questions":
|
|
724
|
-
|
|
725
|
-
|
|
1189
|
+
case "add-question":
|
|
1190
|
+
case "edit-question":
|
|
1191
|
+
case "remove-question":
|
|
1192
|
+
case "reorder-questions":
|
|
1193
|
+
case "question": {
|
|
1194
|
+
const questionAction = normalizedAction === "question" ? rest[0] :
|
|
1195
|
+
normalizedAction === "list-questions" ? "list" :
|
|
1196
|
+
normalizedAction === "add-question" ? "add" :
|
|
1197
|
+
normalizedAction === "edit-question" ? "update" :
|
|
1198
|
+
normalizedAction === "remove-question" ? "remove" :
|
|
1199
|
+
"reorder";
|
|
1200
|
+
const surveyId = normalizedAction === "question" ? rest[1] : rest[0];
|
|
1201
|
+
const questionName = normalizedAction === "question" ? rest[2] : rest[1];
|
|
1202
|
+
if (!surveyId) {
|
|
1203
|
+
console.error("Usage: ascendkit survey question list <survey-id>");
|
|
726
1204
|
process.exit(1);
|
|
727
1205
|
}
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
case "add-question": {
|
|
731
|
-
if (!rest[0] || !flags.type || !flags.title) {
|
|
732
|
-
console.error("Usage: ascendkit survey add-question <survey-id> --type <type> --title <title> [--name <name>] [--required <true|false>] [--choices <c1,c2,...>] [--position <n>]");
|
|
733
|
-
process.exit(1);
|
|
1206
|
+
if (questionAction === "list") {
|
|
1207
|
+
output(await surveys.listQuestions(client, surveyId));
|
|
734
1208
|
}
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
1209
|
+
else if (questionAction === "add") {
|
|
1210
|
+
if (!flags.type || !flags.title) {
|
|
1211
|
+
console.error("Usage: ascendkit survey question add <survey-id> --type <type> --title <title> [--name <name>] [--required <true|false>] [--choices <c1,c2,...>] [--position <n>]");
|
|
1212
|
+
process.exit(1);
|
|
1213
|
+
}
|
|
1214
|
+
const params = { type: flags.type, title: flags.title };
|
|
1215
|
+
if (flags.name)
|
|
1216
|
+
params.name = flags.name;
|
|
1217
|
+
if (flags.required)
|
|
1218
|
+
params.isRequired = flags.required === "true";
|
|
1219
|
+
if (flags.choices)
|
|
1220
|
+
params.choices = flags.choices.split(",");
|
|
1221
|
+
if (flags.position != null)
|
|
1222
|
+
params.position = Number(flags.position);
|
|
1223
|
+
output(await surveys.addQuestion(client, surveyId, params));
|
|
1224
|
+
}
|
|
1225
|
+
else if (questionAction === "update") {
|
|
1226
|
+
if (!questionName) {
|
|
1227
|
+
console.error("Usage: ascendkit survey question update <survey-id> <question-name> [--title <title>] [--required <true|false>] [--choices <c1,c2,...>]");
|
|
1228
|
+
process.exit(1);
|
|
1229
|
+
}
|
|
1230
|
+
const params = {};
|
|
1231
|
+
if (flags.title)
|
|
1232
|
+
params.title = flags.title;
|
|
1233
|
+
if (flags.required)
|
|
1234
|
+
params.isRequired = flags.required === "true";
|
|
1235
|
+
if (flags.choices)
|
|
1236
|
+
params.choices = flags.choices.split(",");
|
|
1237
|
+
output(await surveys.editQuestion(client, surveyId, questionName, params));
|
|
1238
|
+
}
|
|
1239
|
+
else if (questionAction === "remove") {
|
|
1240
|
+
if (!questionName) {
|
|
1241
|
+
console.error("Usage: ascendkit survey question remove <survey-id> <question-name>");
|
|
1242
|
+
process.exit(1);
|
|
1243
|
+
}
|
|
1244
|
+
output(await surveys.removeQuestion(client, surveyId, questionName));
|
|
751
1245
|
}
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
params.choices = flags.choices.split(",");
|
|
759
|
-
output(await surveys.editQuestion(client, rest[0], rest[1], params));
|
|
760
|
-
break;
|
|
761
|
-
}
|
|
762
|
-
case "remove-question":
|
|
763
|
-
if (!rest[0] || !rest[1]) {
|
|
764
|
-
console.error("Usage: ascendkit survey remove-question <survey-id> <question-name>");
|
|
765
|
-
process.exit(1);
|
|
1246
|
+
else if (questionAction === "reorder") {
|
|
1247
|
+
if (!flags.order) {
|
|
1248
|
+
console.error("Usage: ascendkit survey question reorder <survey-id> --order <name1,name2,...>");
|
|
1249
|
+
process.exit(1);
|
|
1250
|
+
}
|
|
1251
|
+
output(await surveys.reorderQuestions(client, surveyId, flags.order.split(",")));
|
|
766
1252
|
}
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
case "reorder-questions":
|
|
770
|
-
if (!rest[0] || !flags.order) {
|
|
771
|
-
console.error("Usage: ascendkit survey reorder-questions <survey-id> --order <name1,name2,...>");
|
|
1253
|
+
else {
|
|
1254
|
+
console.error(`Unknown survey question command: ${questionAction}`);
|
|
772
1255
|
process.exit(1);
|
|
773
1256
|
}
|
|
774
|
-
output(await surveys.reorderQuestions(client, rest[0], flags.order.split(",")));
|
|
775
1257
|
break;
|
|
1258
|
+
}
|
|
776
1259
|
default:
|
|
777
1260
|
console.error(`Unknown survey command: ${action}`);
|
|
778
1261
|
console.error('Run "ascendkit survey --help" for usage');
|
|
@@ -797,7 +1280,7 @@ function runStatus() {
|
|
|
797
1280
|
}
|
|
798
1281
|
else {
|
|
799
1282
|
console.log(" No environment set. Run: ascendkit set-env <public-key>");
|
|
800
|
-
console.log(" List environments: ascendkit env list
|
|
1283
|
+
console.log(" List environments: ascendkit project env list <project-id>");
|
|
801
1284
|
}
|
|
802
1285
|
console.log();
|
|
803
1286
|
}
|
|
@@ -861,6 +1344,34 @@ async function runVerify() {
|
|
|
861
1344
|
console.log();
|
|
862
1345
|
}
|
|
863
1346
|
async function runJourney(client, action, rest) {
|
|
1347
|
+
if (!action) {
|
|
1348
|
+
console.log(HELP_SECTION.journey);
|
|
1349
|
+
return;
|
|
1350
|
+
}
|
|
1351
|
+
if (action === "show")
|
|
1352
|
+
action = "get";
|
|
1353
|
+
if (action === "remove")
|
|
1354
|
+
action = "delete";
|
|
1355
|
+
if (action === "node") {
|
|
1356
|
+
const nodeAction = rest[0];
|
|
1357
|
+
action =
|
|
1358
|
+
nodeAction === "list" ? "list-nodes" :
|
|
1359
|
+
nodeAction === "add" ? "add-node" :
|
|
1360
|
+
nodeAction === "update" ? "edit-node" :
|
|
1361
|
+
nodeAction === "remove" ? "remove-node" :
|
|
1362
|
+
action;
|
|
1363
|
+
rest = rest.slice(1);
|
|
1364
|
+
}
|
|
1365
|
+
if (action === "transition") {
|
|
1366
|
+
const transitionAction = rest[0];
|
|
1367
|
+
action =
|
|
1368
|
+
transitionAction === "list" ? "list-transitions" :
|
|
1369
|
+
transitionAction === "add" ? "add-transition" :
|
|
1370
|
+
transitionAction === "update" ? "edit-transition" :
|
|
1371
|
+
transitionAction === "remove" ? "remove-transition" :
|
|
1372
|
+
action;
|
|
1373
|
+
rest = rest.slice(1);
|
|
1374
|
+
}
|
|
864
1375
|
const flags = parseFlags(rest);
|
|
865
1376
|
switch (action) {
|
|
866
1377
|
case "create":
|
|
@@ -868,7 +1379,7 @@ async function runJourney(client, action, rest) {
|
|
|
868
1379
|
console.error("Usage: ascendkit journey create --name <n> --entry-event <e> --entry-node <n> [--nodes <json>] [--transitions <json>] [--description <d>] [--entry-conditions <json>] [--re-entry-policy <skip|restart>]");
|
|
869
1380
|
process.exit(1);
|
|
870
1381
|
}
|
|
871
|
-
|
|
1382
|
+
console.log(formatJourneyWithGuidance((await journeys.createJourney(client, {
|
|
872
1383
|
name: flags.name,
|
|
873
1384
|
entryEvent: flags["entry-event"],
|
|
874
1385
|
entryNode: flags["entry-node"],
|
|
@@ -877,7 +1388,7 @@ async function runJourney(client, action, rest) {
|
|
|
877
1388
|
description: flags.description,
|
|
878
1389
|
entryConditions: flags["entry-conditions"] ? JSON.parse(flags["entry-conditions"]) : undefined,
|
|
879
1390
|
reEntryPolicy: flags["re-entry-policy"],
|
|
880
|
-
}));
|
|
1391
|
+
}))));
|
|
881
1392
|
break;
|
|
882
1393
|
case "list": {
|
|
883
1394
|
const opts = {};
|
|
@@ -895,17 +1406,17 @@ async function runJourney(client, action, rest) {
|
|
|
895
1406
|
}
|
|
896
1407
|
case "get":
|
|
897
1408
|
if (!rest[0]) {
|
|
898
|
-
console.error("Usage: ascendkit journey
|
|
1409
|
+
console.error("Usage: ascendkit journey show <journey-id>");
|
|
899
1410
|
process.exit(1);
|
|
900
1411
|
}
|
|
901
|
-
|
|
1412
|
+
console.log(formatJourneyWithGuidance(await journeys.getJourney(client, rest[0])));
|
|
902
1413
|
break;
|
|
903
1414
|
case "update":
|
|
904
1415
|
if (!rest[0]) {
|
|
905
1416
|
console.error("Usage: ascendkit journey update <journey-id> [--flags]");
|
|
906
1417
|
process.exit(1);
|
|
907
1418
|
}
|
|
908
|
-
|
|
1419
|
+
console.log(formatJourneyWithGuidance((await journeys.updateJourney(client, rest[0], {
|
|
909
1420
|
name: flags.name,
|
|
910
1421
|
description: flags.description,
|
|
911
1422
|
entryEvent: flags["entry-event"],
|
|
@@ -914,97 +1425,128 @@ async function runJourney(client, action, rest) {
|
|
|
914
1425
|
reEntryPolicy: flags["re-entry-policy"],
|
|
915
1426
|
nodes: flags.nodes ? JSON.parse(flags.nodes) : undefined,
|
|
916
1427
|
transitions: flags.transitions ? JSON.parse(flags.transitions) : undefined,
|
|
917
|
-
}));
|
|
1428
|
+
}))));
|
|
918
1429
|
break;
|
|
919
1430
|
case "delete":
|
|
920
1431
|
if (!rest[0]) {
|
|
921
|
-
console.error("Usage: ascendkit journey
|
|
1432
|
+
console.error("Usage: ascendkit journey remove <journey-id>");
|
|
922
1433
|
process.exit(1);
|
|
923
1434
|
}
|
|
924
|
-
|
|
1435
|
+
await journeys.deleteJourney(client, rest[0]);
|
|
1436
|
+
console.log(`Deleted journey ${rest[0]}.`);
|
|
925
1437
|
break;
|
|
926
1438
|
case "activate":
|
|
927
1439
|
if (!rest[0]) {
|
|
928
1440
|
console.error("Usage: ascendkit journey activate <journey-id>");
|
|
929
1441
|
process.exit(1);
|
|
930
1442
|
}
|
|
931
|
-
|
|
1443
|
+
console.log(formatJourneyWithGuidance(await journeys.activateJourney(client, rest[0])));
|
|
932
1444
|
break;
|
|
933
1445
|
case "pause":
|
|
934
1446
|
if (!rest[0]) {
|
|
935
1447
|
console.error("Usage: ascendkit journey pause <journey-id>");
|
|
936
1448
|
process.exit(1);
|
|
937
1449
|
}
|
|
938
|
-
|
|
1450
|
+
console.log(formatJourneyWithGuidance(await journeys.pauseJourney(client, rest[0])));
|
|
1451
|
+
break;
|
|
1452
|
+
case "resume":
|
|
1453
|
+
if (!rest[0]) {
|
|
1454
|
+
console.error("Usage: ascendkit journey resume <journey-id>");
|
|
1455
|
+
process.exit(1);
|
|
1456
|
+
}
|
|
1457
|
+
console.log(formatJourneyWithGuidance(await journeys.resumeJourney(client, rest[0])));
|
|
939
1458
|
break;
|
|
940
1459
|
case "archive":
|
|
941
1460
|
if (!rest[0]) {
|
|
942
1461
|
console.error("Usage: ascendkit journey archive <journey-id>");
|
|
943
1462
|
process.exit(1);
|
|
944
1463
|
}
|
|
945
|
-
|
|
1464
|
+
console.log(formatJourneyWithGuidance(await journeys.archiveJourney(client, rest[0])));
|
|
946
1465
|
break;
|
|
947
1466
|
case "analytics":
|
|
948
1467
|
if (!rest[0]) {
|
|
949
1468
|
console.error("Usage: ascendkit journey analytics <journey-id>");
|
|
950
1469
|
process.exit(1);
|
|
951
1470
|
}
|
|
952
|
-
|
|
1471
|
+
console.log(formatJourneyAnalytics(await journeys.getJourneyAnalytics(client, rest[0])));
|
|
953
1472
|
break;
|
|
954
1473
|
case "list-nodes":
|
|
955
1474
|
if (!rest[0]) {
|
|
956
|
-
console.error("Usage: ascendkit journey list
|
|
1475
|
+
console.error("Usage: ascendkit journey node list <journey-id>");
|
|
957
1476
|
process.exit(1);
|
|
958
1477
|
}
|
|
959
|
-
|
|
1478
|
+
console.log(formatNodeList(await journeys.listNodes(client, rest[0])));
|
|
960
1479
|
break;
|
|
961
1480
|
case "add-node": {
|
|
962
1481
|
if (!rest[0] || !flags.name) {
|
|
963
|
-
console.error("Usage: ascendkit journey
|
|
1482
|
+
console.error("Usage: ascendkit journey node add <journey-id> --name <node-name> [--action <json>] [--email-id <email>] [--terminal <true|false>]");
|
|
964
1483
|
process.exit(1);
|
|
965
1484
|
}
|
|
966
1485
|
const params = { name: flags.name };
|
|
967
1486
|
if (flags.action)
|
|
968
1487
|
params.action = JSON.parse(flags.action);
|
|
1488
|
+
if (flags["email-id"]) {
|
|
1489
|
+
if (!params.action || params.action.type !== "send_email") {
|
|
1490
|
+
console.error("--email-id requires a send_email action (use --action '{\"type\": \"send_email\", \"templateSlug\": \"...\"}')");
|
|
1491
|
+
process.exit(1);
|
|
1492
|
+
}
|
|
1493
|
+
params.action.fromIdentityEmail = flags["email-id"];
|
|
1494
|
+
}
|
|
969
1495
|
if (flags.terminal)
|
|
970
1496
|
params.terminal = flags.terminal === "true";
|
|
971
|
-
|
|
1497
|
+
console.log(formatSingleNode(await journeys.addNode(client, rest[0], params), "Added", flags.name));
|
|
972
1498
|
break;
|
|
973
1499
|
}
|
|
974
1500
|
case "edit-node": {
|
|
975
1501
|
if (!rest[0] || !rest[1]) {
|
|
976
|
-
console.error("Usage: ascendkit journey
|
|
1502
|
+
console.error("Usage: ascendkit journey node update <journey-id> <node-name> [--action <json>] [--email-id <email>] [--terminal <true|false>]");
|
|
977
1503
|
process.exit(1);
|
|
978
1504
|
}
|
|
979
1505
|
const params = {};
|
|
980
1506
|
if (flags.action)
|
|
981
1507
|
params.action = JSON.parse(flags.action);
|
|
1508
|
+
if (flags["email-id"]) {
|
|
1509
|
+
if (!params.action) {
|
|
1510
|
+
const nodesData = await journeys.listNodes(client, rest[0]);
|
|
1511
|
+
const current = nodesData.nodes?.find((n) => n.name === rest[1]);
|
|
1512
|
+
if (!current?.action || current.action.type !== "send_email") {
|
|
1513
|
+
console.error("--email-id can only be set on send_email nodes");
|
|
1514
|
+
process.exit(1);
|
|
1515
|
+
}
|
|
1516
|
+
params.action = current.action;
|
|
1517
|
+
}
|
|
1518
|
+
else if (params.action.type !== "send_email") {
|
|
1519
|
+
console.error("--email-id requires a send_email action");
|
|
1520
|
+
process.exit(1);
|
|
1521
|
+
}
|
|
1522
|
+
params.action.fromIdentityEmail = flags["email-id"];
|
|
1523
|
+
}
|
|
982
1524
|
if (flags.terminal)
|
|
983
1525
|
params.terminal = flags.terminal === "true";
|
|
984
|
-
|
|
1526
|
+
console.log(formatSingleNode(await journeys.editNode(client, rest[0], rest[1], params), "Updated", rest[1]));
|
|
985
1527
|
break;
|
|
986
1528
|
}
|
|
987
1529
|
case "remove-node":
|
|
988
1530
|
if (!rest[0] || !rest[1]) {
|
|
989
|
-
console.error("Usage: ascendkit journey
|
|
1531
|
+
console.error("Usage: ascendkit journey node remove <journey-id> <node-name>");
|
|
990
1532
|
process.exit(1);
|
|
991
1533
|
}
|
|
992
|
-
|
|
1534
|
+
console.log(formatSingleNode(await journeys.removeNode(client, rest[0], rest[1]), "Removed", rest[1]));
|
|
993
1535
|
break;
|
|
994
1536
|
case "list-transitions": {
|
|
995
1537
|
if (!rest[0]) {
|
|
996
|
-
console.error("Usage: ascendkit journey list
|
|
1538
|
+
console.error("Usage: ascendkit journey transition list <journey-id> [--from <node-name>] [--to <node-name>]");
|
|
997
1539
|
process.exit(1);
|
|
998
1540
|
}
|
|
999
|
-
|
|
1541
|
+
console.log(formatTransitionList(await journeys.listTransitions(client, rest[0], {
|
|
1000
1542
|
from_node: flags.from,
|
|
1001
1543
|
to_node: flags.to,
|
|
1002
|
-
}));
|
|
1544
|
+
})));
|
|
1003
1545
|
break;
|
|
1004
1546
|
}
|
|
1005
1547
|
case "add-transition": {
|
|
1006
1548
|
if (!rest[0] || !flags.from || !flags.to || !(flags.on || flags.after || flags.trigger)) {
|
|
1007
|
-
console.error("Usage: ascendkit journey
|
|
1549
|
+
console.error("Usage: ascendkit journey transition add <journey-id> --from <node-name> --to <node-name> --on <event> | --after <delay> | --trigger <json> [--priority <n>] [--name <transition-name>]");
|
|
1008
1550
|
process.exit(1);
|
|
1009
1551
|
}
|
|
1010
1552
|
let trigger;
|
|
@@ -1026,12 +1568,13 @@ async function runJourney(client, action, rest) {
|
|
|
1026
1568
|
params.priority = Number(flags.priority);
|
|
1027
1569
|
if (flags.name)
|
|
1028
1570
|
params.name = flags.name;
|
|
1029
|
-
|
|
1571
|
+
const transitionName = flags.name || `${flags.from}-to-${flags.to}`;
|
|
1572
|
+
console.log(formatSingleTransition(await journeys.addTransition(client, rest[0], params), "Added", transitionName));
|
|
1030
1573
|
break;
|
|
1031
1574
|
}
|
|
1032
1575
|
case "edit-transition": {
|
|
1033
1576
|
if (!rest[0] || !rest[1]) {
|
|
1034
|
-
console.error("Usage: ascendkit journey
|
|
1577
|
+
console.error("Usage: ascendkit journey transition update <journey-id> <transition-name> [--on <event>] [--after <delay>] [--trigger <json>] [--priority <n>]");
|
|
1035
1578
|
process.exit(1);
|
|
1036
1579
|
}
|
|
1037
1580
|
const params = {};
|
|
@@ -1046,15 +1589,15 @@ async function runJourney(client, action, rest) {
|
|
|
1046
1589
|
}
|
|
1047
1590
|
if (flags.priority != null)
|
|
1048
1591
|
params.priority = Number(flags.priority);
|
|
1049
|
-
|
|
1592
|
+
console.log(formatSingleTransition(await journeys.editTransition(client, rest[0], rest[1], params), "Updated", rest[1]));
|
|
1050
1593
|
break;
|
|
1051
1594
|
}
|
|
1052
1595
|
case "remove-transition":
|
|
1053
1596
|
if (!rest[0] || !rest[1]) {
|
|
1054
|
-
console.error("Usage: ascendkit journey
|
|
1597
|
+
console.error("Usage: ascendkit journey transition remove <journey-id> <transition-name>");
|
|
1055
1598
|
process.exit(1);
|
|
1056
1599
|
}
|
|
1057
|
-
|
|
1600
|
+
console.log(formatSingleTransition(await journeys.removeTransition(client, rest[0], rest[1]), "Removed", rest[1]));
|
|
1058
1601
|
break;
|
|
1059
1602
|
default:
|
|
1060
1603
|
console.error(`Unknown journey command: ${action}`);
|
|
@@ -1089,17 +1632,22 @@ async function runWebhook(client, action, rest) {
|
|
|
1089
1632
|
}
|
|
1090
1633
|
output(await webhooks.getWebhook(client, rest[0]));
|
|
1091
1634
|
break;
|
|
1092
|
-
case "update":
|
|
1635
|
+
case "update": {
|
|
1093
1636
|
if (!rest[0]) {
|
|
1094
1637
|
console.error("Usage: ascendkit webhook update <webhook-id> [--flags]");
|
|
1095
1638
|
process.exit(1);
|
|
1096
1639
|
}
|
|
1097
|
-
|
|
1640
|
+
const updated = await webhooks.updateWebhook(client, rest[0], {
|
|
1098
1641
|
url: flags.url,
|
|
1099
1642
|
events: flags.events ? flags.events.split(",") : undefined,
|
|
1100
1643
|
status: flags.status,
|
|
1101
|
-
})
|
|
1644
|
+
});
|
|
1645
|
+
console.log(`Updated: ${updated.id}`);
|
|
1646
|
+
console.log(`URL: ${updated.url}`);
|
|
1647
|
+
console.log(`Status: ${updated.status}`);
|
|
1648
|
+
console.log(`Events: ${Array.isArray(updated.events) ? updated.events.join(", ") : updated.events}`);
|
|
1102
1649
|
break;
|
|
1650
|
+
}
|
|
1103
1651
|
case "delete":
|
|
1104
1652
|
if (!rest[0]) {
|
|
1105
1653
|
console.error("Usage: ascendkit webhook delete <webhook-id>");
|
|
@@ -1120,58 +1668,150 @@ async function runWebhook(client, action, rest) {
|
|
|
1120
1668
|
process.exit(1);
|
|
1121
1669
|
}
|
|
1122
1670
|
}
|
|
1123
|
-
async function
|
|
1671
|
+
async function runCampaign(client, action, rest) {
|
|
1124
1672
|
const flags = parseFlags(rest);
|
|
1673
|
+
if (!action) {
|
|
1674
|
+
console.log(HELP_SECTION.campaign);
|
|
1675
|
+
return;
|
|
1676
|
+
}
|
|
1125
1677
|
switch (action) {
|
|
1126
|
-
case "
|
|
1127
|
-
|
|
1128
|
-
|
|
1678
|
+
case "create":
|
|
1679
|
+
if (!flags.name || !flags.template || !flags.audience) {
|
|
1680
|
+
console.error("Usage: ascendkit campaign create --name <name> --template <template-id> --audience <json> [--scheduled-at <datetime>]");
|
|
1681
|
+
process.exit(1);
|
|
1682
|
+
}
|
|
1683
|
+
let createFilter;
|
|
1684
|
+
try {
|
|
1685
|
+
createFilter = JSON.parse(flags.audience);
|
|
1686
|
+
}
|
|
1687
|
+
catch {
|
|
1688
|
+
console.error("Invalid JSON for --audience flag. Provide a valid JSON object.");
|
|
1689
|
+
process.exit(1);
|
|
1690
|
+
}
|
|
1691
|
+
output(await campaigns.createCampaign(client, {
|
|
1692
|
+
name: flags.name,
|
|
1693
|
+
templateId: flags.template,
|
|
1694
|
+
audienceFilter: createFilter,
|
|
1695
|
+
scheduledAt: flags["scheduled-at"],
|
|
1696
|
+
}));
|
|
1129
1697
|
break;
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1698
|
+
case "list": {
|
|
1699
|
+
const items = await campaigns.listCampaigns(client, flags.status);
|
|
1700
|
+
table(items, [
|
|
1701
|
+
{ key: "id", label: "ID" },
|
|
1702
|
+
{ key: "name", label: "Name", width: 30 },
|
|
1703
|
+
{ key: "status", label: "Status" },
|
|
1704
|
+
{ key: "scheduledAt", label: "Scheduled", width: 22 },
|
|
1705
|
+
{ key: "templateId", label: "Template" },
|
|
1706
|
+
]);
|
|
1134
1707
|
break;
|
|
1135
1708
|
}
|
|
1136
|
-
case "
|
|
1709
|
+
case "show":
|
|
1710
|
+
case "get":
|
|
1137
1711
|
if (!rest[0]) {
|
|
1138
|
-
console.error("Usage: ascendkit
|
|
1712
|
+
console.error("Usage: ascendkit campaign show <campaign-id>");
|
|
1139
1713
|
process.exit(1);
|
|
1140
1714
|
}
|
|
1141
|
-
|
|
1142
|
-
fromEmail: flags["from-email"],
|
|
1143
|
-
fromName: flags["from-name"],
|
|
1144
|
-
});
|
|
1145
|
-
await printEmailSetup(s);
|
|
1715
|
+
output(await campaigns.getCampaign(client, rest[0]));
|
|
1146
1716
|
break;
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1717
|
+
case "update":
|
|
1718
|
+
if (!rest[0]) {
|
|
1719
|
+
console.error("Usage: ascendkit campaign update <campaign-id> [--flags]");
|
|
1720
|
+
process.exit(1);
|
|
1721
|
+
}
|
|
1722
|
+
let updateFilter;
|
|
1723
|
+
if (flags.audience) {
|
|
1724
|
+
try {
|
|
1725
|
+
updateFilter = JSON.parse(flags.audience);
|
|
1726
|
+
}
|
|
1727
|
+
catch {
|
|
1728
|
+
console.error("Invalid JSON for --audience flag. Provide a valid JSON object.");
|
|
1729
|
+
process.exit(1);
|
|
1730
|
+
}
|
|
1731
|
+
}
|
|
1732
|
+
output(await campaigns.updateCampaign(client, rest[0], {
|
|
1733
|
+
name: flags.name,
|
|
1734
|
+
templateId: flags.template,
|
|
1735
|
+
audienceFilter: updateFilter,
|
|
1736
|
+
scheduledAt: flags["scheduled-at"],
|
|
1737
|
+
}));
|
|
1738
|
+
break;
|
|
1739
|
+
case "preview":
|
|
1740
|
+
if (!rest[0]) {
|
|
1741
|
+
console.error("Usage: ascendkit campaign preview <campaign-id>");
|
|
1742
|
+
process.exit(1);
|
|
1743
|
+
}
|
|
1744
|
+
{
|
|
1745
|
+
const detail = await campaigns.getCampaign(client, rest[0]);
|
|
1746
|
+
if (!detail?.audienceFilter) {
|
|
1747
|
+
console.error("Campaign has no audience filter set.");
|
|
1748
|
+
process.exit(1);
|
|
1749
|
+
}
|
|
1750
|
+
output(await campaigns.previewAudience(client, detail.audienceFilter));
|
|
1751
|
+
}
|
|
1752
|
+
break;
|
|
1753
|
+
case "schedule":
|
|
1754
|
+
if (!rest[0] || !flags.at) {
|
|
1755
|
+
console.error("Usage: ascendkit campaign schedule <campaign-id> --at <datetime>");
|
|
1756
|
+
process.exit(1);
|
|
1757
|
+
}
|
|
1758
|
+
output(await campaigns.updateCampaign(client, rest[0], { scheduledAt: flags.at }));
|
|
1759
|
+
break;
|
|
1760
|
+
case "cancel":
|
|
1761
|
+
if (!rest[0]) {
|
|
1762
|
+
console.error("Usage: ascendkit campaign cancel <campaign-id>");
|
|
1763
|
+
process.exit(1);
|
|
1764
|
+
}
|
|
1765
|
+
output(await campaigns.deleteCampaign(client, rest[0]));
|
|
1766
|
+
break;
|
|
1767
|
+
case "analytics":
|
|
1768
|
+
if (!rest[0]) {
|
|
1769
|
+
console.error("Usage: ascendkit campaign analytics <campaign-id>");
|
|
1770
|
+
process.exit(1);
|
|
1771
|
+
}
|
|
1772
|
+
output(await campaigns.getCampaignAnalytics(client, rest[0]));
|
|
1773
|
+
break;
|
|
1774
|
+
default:
|
|
1775
|
+
console.error(`Unknown campaign command: ${action}`);
|
|
1776
|
+
console.error('Run "ascendkit campaign --help" for usage');
|
|
1777
|
+
process.exit(1);
|
|
1778
|
+
}
|
|
1779
|
+
}
|
|
1780
|
+
async function runEmail(client, action, rest) {
|
|
1781
|
+
const flags = parseFlags(rest);
|
|
1782
|
+
if (!action) {
|
|
1783
|
+
console.log(HELP_SECTION["email-identity"]);
|
|
1784
|
+
return;
|
|
1785
|
+
}
|
|
1786
|
+
switch (action) {
|
|
1787
|
+
case "settings": {
|
|
1788
|
+
const settings = await email.getSettings(client);
|
|
1789
|
+
if (flags.json === "true") {
|
|
1790
|
+
output(settings);
|
|
1156
1791
|
}
|
|
1157
1792
|
else {
|
|
1158
|
-
|
|
1793
|
+
printEmailSettingsSummary(settings);
|
|
1159
1794
|
}
|
|
1160
1795
|
break;
|
|
1796
|
+
}
|
|
1161
1797
|
case "setup-domain":
|
|
1162
1798
|
if (!rest[0]) {
|
|
1163
|
-
console.error("Usage: ascendkit email setup-domain <domain>");
|
|
1799
|
+
console.error("Usage: ascendkit email-identity setup-domain <domain>");
|
|
1164
1800
|
process.exit(1);
|
|
1165
1801
|
}
|
|
1166
1802
|
await printEmailSetup(await email.setupDomain(client, rest[0]));
|
|
1167
1803
|
break;
|
|
1168
|
-
case "
|
|
1804
|
+
case "status": {
|
|
1169
1805
|
const interval = Math.max(5, Number.parseInt(flags.interval || "15", 10) || 15);
|
|
1170
1806
|
if (flags.watch === "true") {
|
|
1171
1807
|
await watchDomainStatus(client, interval);
|
|
1172
1808
|
}
|
|
1173
1809
|
else {
|
|
1174
|
-
|
|
1810
|
+
const settings = await email.getSettings(client);
|
|
1811
|
+
const domainStatus = await email.checkDomainStatus(client);
|
|
1812
|
+
const dnsCheck = settings.domain ? await email.checkDnsRecords(client) : null;
|
|
1813
|
+
const identities = await email.listIdentities(client);
|
|
1814
|
+
printEmailStatusSummary(settings, domainStatus, dnsCheck, identities.identities ?? []);
|
|
1175
1815
|
}
|
|
1176
1816
|
break;
|
|
1177
1817
|
}
|
|
@@ -1191,8 +1831,68 @@ async function runEmail(client, action, rest) {
|
|
|
1191
1831
|
case "remove-domain":
|
|
1192
1832
|
output(await email.removeDomain(client));
|
|
1193
1833
|
break;
|
|
1834
|
+
case "list": {
|
|
1835
|
+
const result = await email.listIdentities(client);
|
|
1836
|
+
printEmailIdentities(result.identities ?? []);
|
|
1837
|
+
break;
|
|
1838
|
+
}
|
|
1839
|
+
case "add": {
|
|
1840
|
+
const identityEmail = rest[0];
|
|
1841
|
+
if (!identityEmail) {
|
|
1842
|
+
console.error("Usage: ascendkit email-identity add <email> [--display-name <name>]");
|
|
1843
|
+
process.exit(1);
|
|
1844
|
+
}
|
|
1845
|
+
output(await email.createIdentity(client, {
|
|
1846
|
+
email: identityEmail,
|
|
1847
|
+
displayName: flags["display-name"],
|
|
1848
|
+
}));
|
|
1849
|
+
break;
|
|
1850
|
+
}
|
|
1851
|
+
case "resend": {
|
|
1852
|
+
const identityEmail = rest[0];
|
|
1853
|
+
if (!identityEmail) {
|
|
1854
|
+
console.error("Usage: ascendkit email-identity resend <email>");
|
|
1855
|
+
process.exit(1);
|
|
1856
|
+
}
|
|
1857
|
+
output(await email.resendIdentityVerification(client, identityEmail));
|
|
1858
|
+
break;
|
|
1859
|
+
}
|
|
1860
|
+
case "set-default": {
|
|
1861
|
+
const identityEmail = rest[0];
|
|
1862
|
+
if (!identityEmail) {
|
|
1863
|
+
console.error("Usage: ascendkit email-identity set-default <email> [--display-name <name>]");
|
|
1864
|
+
process.exit(1);
|
|
1865
|
+
}
|
|
1866
|
+
output(await email.setDefaultIdentity(client, {
|
|
1867
|
+
email: identityEmail,
|
|
1868
|
+
displayName: flags["display-name"],
|
|
1869
|
+
}));
|
|
1870
|
+
break;
|
|
1871
|
+
}
|
|
1872
|
+
case "remove":
|
|
1873
|
+
case "delete": {
|
|
1874
|
+
const identityEmail = rest[0];
|
|
1875
|
+
if (!identityEmail) {
|
|
1876
|
+
console.error("Usage: ascendkit email-identity remove <email>");
|
|
1877
|
+
process.exit(1);
|
|
1878
|
+
}
|
|
1879
|
+
output(await email.removeIdentity(client, identityEmail));
|
|
1880
|
+
break;
|
|
1881
|
+
}
|
|
1882
|
+
case "test": {
|
|
1883
|
+
const identityEmail = rest[0];
|
|
1884
|
+
if (!identityEmail || !flags.to) {
|
|
1885
|
+
console.error("Usage: ascendkit email-identity test <email> --to <recipient>");
|
|
1886
|
+
process.exit(1);
|
|
1887
|
+
}
|
|
1888
|
+
output(await email.sendTestEmail(client, {
|
|
1889
|
+
to: flags.to,
|
|
1890
|
+
fromIdentityEmail: identityEmail,
|
|
1891
|
+
}));
|
|
1892
|
+
break;
|
|
1893
|
+
}
|
|
1194
1894
|
default:
|
|
1195
|
-
console.error(`Unknown email command: ${action}`);
|
|
1895
|
+
console.error(`Unknown email-identity command: ${action}`);
|
|
1196
1896
|
process.exit(1);
|
|
1197
1897
|
}
|
|
1198
1898
|
}
|
|
@@ -1221,28 +1921,61 @@ async function printEmailSetup(settings) {
|
|
|
1221
1921
|
}
|
|
1222
1922
|
}
|
|
1223
1923
|
}
|
|
1224
|
-
function
|
|
1225
|
-
const
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
console.log("Next step: add DNS records and run `ascendkit email domain-status --watch`");
|
|
1924
|
+
function printEmailSettingsSummary(settings) {
|
|
1925
|
+
const identities = Array.isArray(settings.identities) ? settings.identities : [];
|
|
1926
|
+
if (!settings.domain) {
|
|
1927
|
+
console.log("Sender: AscendKit default");
|
|
1928
|
+
console.log(`From: ${settings.fromEmail || "noreply@ascendkit.dev"}`);
|
|
1929
|
+
console.log(`Display name: ${settings.fromName || "not set"}`);
|
|
1930
|
+
console.log("Domain: not configured");
|
|
1931
|
+
if (identities.length > 0) {
|
|
1932
|
+
console.log("");
|
|
1933
|
+
printEmailIdentities(identities);
|
|
1235
1934
|
}
|
|
1935
|
+
return;
|
|
1236
1936
|
}
|
|
1237
|
-
|
|
1238
|
-
|
|
1937
|
+
console.log(`Domain: ${settings.domain} (${settings.verificationStatus || "unknown"})`);
|
|
1938
|
+
console.log("");
|
|
1939
|
+
printEmailIdentities(identities);
|
|
1940
|
+
}
|
|
1941
|
+
function printEmailStatusSummary(settings, domainStatus, dnsCheck, identities) {
|
|
1942
|
+
if (!settings.domain) {
|
|
1943
|
+
console.log("Domain: not configured");
|
|
1944
|
+
console.log("Sender: AscendKit default");
|
|
1945
|
+
if (identities.length > 0) {
|
|
1946
|
+
console.log("");
|
|
1947
|
+
printEmailIdentities(identities);
|
|
1948
|
+
}
|
|
1949
|
+
return;
|
|
1239
1950
|
}
|
|
1951
|
+
console.log(`Domain: ${settings.domain} (${domainStatus.status || settings.verificationStatus || "unknown"})`);
|
|
1952
|
+
if (dnsCheck?.summary) {
|
|
1953
|
+
console.log(`DNS: ${dnsCheck.summary.found}/${dnsCheck.summary.total} verified`);
|
|
1954
|
+
}
|
|
1955
|
+
console.log("");
|
|
1956
|
+
printEmailIdentities(identities);
|
|
1957
|
+
}
|
|
1958
|
+
function printEmailIdentities(identities) {
|
|
1959
|
+
table(identities.map((identity) => ({
|
|
1960
|
+
email: identity.email,
|
|
1961
|
+
displayName: identity.displayName || "—",
|
|
1962
|
+
verificationStatus: identity.verificationStatus,
|
|
1963
|
+
isDefault: identity.isDefault ? "yes" : "",
|
|
1964
|
+
})), [
|
|
1965
|
+
{ key: "email", label: "Email", width: 36 },
|
|
1966
|
+
{ key: "displayName", label: "Display Name", width: 24 },
|
|
1967
|
+
{ key: "verificationStatus", label: "Status", width: 12 },
|
|
1968
|
+
{ key: "isDefault", label: "Default", width: 7 },
|
|
1969
|
+
]);
|
|
1240
1970
|
}
|
|
1241
1971
|
async function watchDomainStatus(client, intervalSeconds) {
|
|
1242
1972
|
while (true) {
|
|
1243
|
-
const
|
|
1244
|
-
|
|
1245
|
-
|
|
1973
|
+
const settings = await email.getSettings(client);
|
|
1974
|
+
const domainStatus = await email.checkDomainStatus(client);
|
|
1975
|
+
const dnsCheck = settings.domain ? await email.checkDnsRecords(client) : null;
|
|
1976
|
+
const identities = await email.listIdentities(client);
|
|
1977
|
+
printEmailStatusSummary(settings, domainStatus, dnsCheck, identities.identities ?? []);
|
|
1978
|
+
if (domainStatus?.status === "verified" || domainStatus?.status === "failed" || domainStatus?.status === "none") {
|
|
1246
1979
|
return;
|
|
1247
1980
|
}
|
|
1248
1981
|
await new Promise((resolve) => setTimeout(resolve, intervalSeconds * 1000));
|