@ascendkit/cli 0.2.6 → 0.3.1
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 +1 -0
- package/dist/api/client.js +36 -9
- package/dist/cli.js +869 -474
- 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/journeys.d.ts +1 -0
- package/dist/commands/journeys.js +9 -6
- package/dist/commands/platform.d.ts +22 -2
- package/dist/commands/platform.js +211 -103
- package/dist/commands/surveys.js +5 -5
- package/dist/mcp.js +31 -3
- package/dist/tools/auth.js +32 -11
- package/dist/tools/content.js +24 -12
- package/dist/tools/email.js +47 -17
- package/dist/tools/import.js +9 -9
- package/dist/tools/journeys.js +21 -14
- package/dist/tools/platform.js +125 -10
- package/dist/tools/surveys.js +9 -9
- package/dist/utils/correlation.d.ts +11 -0
- package/dist/utils/correlation.js +67 -0
- package/dist/utils/exit.d.ts +23 -0
- package/dist/utils/exit.js +64 -0
- package/dist/utils/journey-format.d.ts +6 -0
- package/dist/utils/journey-format.js +6 -4
- package/dist/utils/redaction.d.ts +16 -0
- package/dist/utils/redaction.js +114 -0
- package/dist/utils/survey-format.js +2 -2
- package/dist/utils/telemetry.d.ts +32 -0
- package/dist/utils/telemetry.js +47 -0
- package/package.json +5 -5
package/dist/cli.js
CHANGED
|
@@ -14,6 +14,12 @@ import * as webhooks from "./commands/webhooks.js";
|
|
|
14
14
|
import * as campaigns from "./commands/campaigns.js";
|
|
15
15
|
import * as importCmd from "./commands/import.js";
|
|
16
16
|
import { parseDelay } from "./utils/duration.js";
|
|
17
|
+
import { exitCli, installGlobalHandlers, onExit } from "./utils/exit.js";
|
|
18
|
+
import { getInvocationId, getMachineId } from "./utils/correlation.js";
|
|
19
|
+
import { redactArgs } from "./utils/redaction.js";
|
|
20
|
+
import { captureTelemetry } from "./utils/telemetry.js";
|
|
21
|
+
import { hostname as osHostname, platform as osPlatform } from "node:os";
|
|
22
|
+
import { formatJourneyAnalytics, formatJourneyWithGuidance, formatNodeList, formatSingleNode, formatSingleTransition, formatTransitionList, } from "./utils/journey-format.js";
|
|
17
23
|
const require = createRequire(import.meta.url);
|
|
18
24
|
const { version: CLI_VERSION } = require("../package.json");
|
|
19
25
|
const HELP = `ascendkit v${CLI_VERSION} - AscendKit CLI
|
|
@@ -29,17 +35,18 @@ Getting Started:
|
|
|
29
35
|
|
|
30
36
|
Services:
|
|
31
37
|
auth Authentication, providers, OAuth, users
|
|
32
|
-
|
|
38
|
+
template Email templates and versioning
|
|
33
39
|
survey Surveys, questions, distribution, analytics
|
|
34
40
|
journey Lifecycle journeys, nodes, transitions
|
|
35
|
-
email
|
|
41
|
+
email-identity Email domains, sender identities, and DNS
|
|
42
|
+
keystore Environment runtime key-value settings
|
|
36
43
|
webhook Webhook endpoints and testing
|
|
37
44
|
campaign Email campaigns, scheduling, analytics
|
|
38
45
|
import Import users from external auth providers
|
|
39
46
|
|
|
40
47
|
Project Management:
|
|
41
|
-
|
|
42
|
-
|
|
48
|
+
project Projects and environment selection
|
|
49
|
+
environment Active environment operations
|
|
43
50
|
verify Check all services in the active environment
|
|
44
51
|
|
|
45
52
|
Run "ascendkit help <section>" for detailed command usage.
|
|
@@ -48,70 +55,79 @@ const HELP_SECTION = {
|
|
|
48
55
|
auth: `Usage: ascendkit auth <command>
|
|
49
56
|
|
|
50
57
|
Commands:
|
|
51
|
-
auth
|
|
52
|
-
auth
|
|
53
|
-
auth
|
|
54
|
-
auth
|
|
58
|
+
auth show
|
|
59
|
+
auth update [--providers <p1,p2,...>] [--email-verification <true|false>] [--waitlist <true|false>] [--password-reset <true|false>] [--session-duration <duration>]
|
|
60
|
+
auth provider list
|
|
61
|
+
auth provider set <p1,p2,...>
|
|
62
|
+
auth oauth open <provider>
|
|
55
63
|
auth oauth set <provider> --client-id <id> [--client-secret <secret> | --client-secret-stdin] [--callback-url <url>]
|
|
56
|
-
auth
|
|
57
|
-
|
|
64
|
+
auth oauth remove <provider>
|
|
65
|
+
auth user list
|
|
66
|
+
auth user remove <user-id>
|
|
67
|
+
auth user reactivate <user-id>`,
|
|
68
|
+
template: `Usage: ascendkit template <command>
|
|
58
69
|
|
|
59
70
|
Commands:
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
71
|
+
template create --name <name> --subject <subject> --body-html <html> --body-text <text> [--slug <slug>] [--description <description>]
|
|
72
|
+
template list [--query <search>] [--system true|--custom true]
|
|
73
|
+
template show <template-id>
|
|
74
|
+
template update <template-id> [--subject <subject>] [--body-html <html>] [--body-text <text>] [--change-note <note>]
|
|
75
|
+
template remove <template-id>
|
|
76
|
+
template version list <template-id>
|
|
77
|
+
template version show <template-id> <n>`,
|
|
67
78
|
survey: `Usage: ascendkit survey <command>
|
|
68
79
|
|
|
69
80
|
Commands:
|
|
70
81
|
survey create --name <name> [--type <nps|csat|custom>] [--definition <json>]
|
|
71
82
|
survey list
|
|
72
|
-
survey
|
|
83
|
+
survey show <survey-id>
|
|
73
84
|
survey update <survey-id> [--name <name>] [--status <draft|active|paused>] [--definition <json>]
|
|
74
|
-
survey
|
|
85
|
+
survey remove <survey-id>
|
|
75
86
|
survey distribute <survey-id> --users <usr_id1,usr_id2,...>
|
|
76
|
-
survey
|
|
87
|
+
survey invitation list <survey-id>
|
|
77
88
|
survey analytics <survey-id>
|
|
78
|
-
survey
|
|
79
|
-
survey
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
89
|
+
survey definition export <survey-id> [--out <file>]
|
|
90
|
+
survey definition import <survey-id> --in <file>
|
|
91
|
+
survey question list <survey-id>
|
|
92
|
+
survey question add <survey-id> --type <type> --title <title> [--name <name>] [--required <true|false>] [--choices <c1,c2,...>] [--position <n>]
|
|
93
|
+
survey question update <survey-id> <question-name> [--title <title>] [--required <true|false>] [--choices <c1,c2,...>]
|
|
94
|
+
survey question remove <survey-id> <question-name>
|
|
95
|
+
survey question reorder <survey-id> --order <name1,name2,...>`,
|
|
83
96
|
journey: `Usage: ascendkit journey <command>
|
|
84
97
|
|
|
85
98
|
Commands:
|
|
86
99
|
journey create --name <name> --entry-event <event> --entry-node <node> [--nodes <json>] [--transitions <json>] [--description <description>] [--entry-conditions <json>] [--re-entry-policy <skip|restart>]
|
|
87
100
|
journey list [--status <draft|active|paused|archived>]
|
|
88
|
-
journey
|
|
101
|
+
journey show <journey-id>
|
|
89
102
|
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>]
|
|
90
|
-
journey
|
|
103
|
+
journey remove <journey-id>
|
|
91
104
|
journey activate <journey-id>
|
|
92
105
|
journey pause <journey-id>
|
|
106
|
+
journey resume <journey-id>
|
|
93
107
|
journey archive <journey-id>
|
|
94
108
|
journey analytics <journey-id>
|
|
95
|
-
journey list
|
|
96
|
-
journey
|
|
97
|
-
journey
|
|
98
|
-
journey
|
|
99
|
-
journey list
|
|
100
|
-
journey
|
|
101
|
-
journey
|
|
102
|
-
journey
|
|
103
|
-
email: `Usage: ascendkit email <command>
|
|
109
|
+
journey node list <journey-id>
|
|
110
|
+
journey node add <journey-id> --name <node-name> [--action <json>] [--email-id <email>] [--terminal <true|false>]
|
|
111
|
+
journey node update <journey-id> <node-name> [--action <json>] [--email-id <email>] [--terminal <true|false>]
|
|
112
|
+
journey node remove <journey-id> <node-name>
|
|
113
|
+
journey transition list <journey-id> [--from <node-name>] [--to <node-name>]
|
|
114
|
+
journey transition add <journey-id> --from <node-name> --to <node-name> --trigger <json> [--priority <n>] [--name <transition-name>]
|
|
115
|
+
journey transition update <journey-id> <transition-name> [--trigger <json>] [--priority <n>]
|
|
116
|
+
journey transition remove <journey-id> <transition-name>`,
|
|
117
|
+
"email-identity": `Usage: ascendkit email-identity <command>
|
|
104
118
|
|
|
105
119
|
Commands:
|
|
106
|
-
email settings
|
|
107
|
-
email
|
|
108
|
-
email
|
|
109
|
-
email
|
|
110
|
-
email
|
|
111
|
-
email
|
|
112
|
-
email
|
|
113
|
-
email
|
|
114
|
-
email remove
|
|
120
|
+
email-identity settings [--json]
|
|
121
|
+
email-identity setup-domain <domain>
|
|
122
|
+
email-identity status [--watch] [--interval <seconds>]
|
|
123
|
+
email-identity remove-domain
|
|
124
|
+
email-identity list
|
|
125
|
+
email-identity add <email> [--display-name <name>]
|
|
126
|
+
email-identity resend <email>
|
|
127
|
+
email-identity set-default <email> [--display-name <name>]
|
|
128
|
+
email-identity remove <email>
|
|
129
|
+
email-identity test <email> --to <recipient>
|
|
130
|
+
email-identity open-dns [--domain <domain>] [--open]`,
|
|
115
131
|
webhook: `Usage: ascendkit webhook <command>
|
|
116
132
|
|
|
117
133
|
Commands:
|
|
@@ -137,50 +153,61 @@ Notes:
|
|
|
137
153
|
- --audience is a JSON filter object, e.g. '{"tags":{"$in":["premium"]}}'
|
|
138
154
|
- --scheduled-at / --at accepts ISO 8601 datetime, e.g. 2026-03-15T10:00:00Z
|
|
139
155
|
- cancel deletes a draft/failed campaign or cancels a scheduled/sending campaign`,
|
|
140
|
-
|
|
156
|
+
environment: `Usage: ascendkit environment <command>
|
|
157
|
+
|
|
158
|
+
Commands:
|
|
159
|
+
environment show
|
|
160
|
+
environment update [<env-id>] [--name <name>] [--description <desc>]
|
|
161
|
+
environment promote [<env-id>] --target <tier>`,
|
|
162
|
+
keystore: `Usage: ascendkit keystore <command>
|
|
141
163
|
|
|
142
164
|
Commands:
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
env set-var <key> <value>
|
|
148
|
-
env unset-var <key>
|
|
149
|
-
env list-vars`,
|
|
150
|
-
import: `Usage: ascendkit import <source> [options]
|
|
165
|
+
keystore list
|
|
166
|
+
keystore set <key> <value>
|
|
167
|
+
keystore remove <key>`,
|
|
168
|
+
import: `Usage: ascendkit import <source> <action> [options]
|
|
151
169
|
|
|
152
170
|
Sources:
|
|
153
|
-
clerk
|
|
171
|
+
clerk Import users from Clerk
|
|
172
|
+
migration-journey Create migration email templates and journeys
|
|
154
173
|
|
|
155
174
|
Commands:
|
|
156
|
-
import clerk --api-key <key> [options]
|
|
157
|
-
import clerk --file <path> [options]
|
|
158
|
-
import
|
|
175
|
+
import clerk preview --api-key <key> [options]
|
|
176
|
+
import clerk preview --file <path> [options]
|
|
177
|
+
import clerk run --api-key <key> [options]
|
|
178
|
+
import clerk run --file <path> [options]
|
|
179
|
+
import migration-journey create [--from-identity <email>]
|
|
159
180
|
|
|
160
181
|
Options:
|
|
161
182
|
--api-key <key> Clerk secret API key (fetches users from Clerk API)
|
|
162
183
|
--file <path> Path to Clerk dashboard export (JSON)
|
|
163
184
|
--instance-url <url> Custom Clerk API URL (default: https://api.clerk.com)
|
|
164
|
-
--execute Run the import for real (default is dry-run preview)
|
|
165
185
|
--users Import users (included by default; use to select only users)
|
|
166
186
|
--settings Import auth settings / OAuth providers (included by default)
|
|
167
187
|
--from-identity <email> Email identity for migration journey emails
|
|
168
188
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
projects: `Usage: ascendkit projects <command>
|
|
189
|
+
Use "preview" for dry-run mode and "run" to apply changes. Pass --users or --settings alone to select
|
|
190
|
+
only that phase (e.g. "import clerk run --users ..." imports only users, not settings).`,
|
|
191
|
+
project: `Usage: ascendkit project <command>
|
|
173
192
|
|
|
174
193
|
Commands:
|
|
175
|
-
|
|
176
|
-
|
|
194
|
+
project list
|
|
195
|
+
project create --name <name> [--description <description>] [--services <s1,s2,...>]
|
|
196
|
+
project show <project-id>
|
|
197
|
+
project env list <project-id>`,
|
|
177
198
|
};
|
|
178
199
|
function printSectionHelp(section) {
|
|
179
200
|
if (!section)
|
|
180
201
|
return false;
|
|
181
202
|
let key = section.toLowerCase();
|
|
182
|
-
if (key === "content")
|
|
183
|
-
key = "
|
|
203
|
+
if (key === "content" || key === "templates")
|
|
204
|
+
key = "template";
|
|
205
|
+
if (key === "email")
|
|
206
|
+
key = "email-identity";
|
|
207
|
+
if (key === "projects")
|
|
208
|
+
key = "project";
|
|
209
|
+
if (key === "env" || key === "environments")
|
|
210
|
+
key = "environment";
|
|
184
211
|
const text = HELP_SECTION[key];
|
|
185
212
|
if (!text)
|
|
186
213
|
return false;
|
|
@@ -188,14 +215,15 @@ function printSectionHelp(section) {
|
|
|
188
215
|
return true;
|
|
189
216
|
}
|
|
190
217
|
function getClient() {
|
|
191
|
-
let publicKey = process.env.
|
|
218
|
+
let publicKey = process.env.ASCENDKIT_ENV_KEY;
|
|
192
219
|
let apiUrl = process.env.ASCENDKIT_API_URL;
|
|
193
220
|
const auth = loadAuth();
|
|
194
221
|
const env = loadEnvContext();
|
|
195
222
|
// Require auth unless env var provides the public key (CI/CD escape hatch)
|
|
196
223
|
if (!auth?.token && !publicKey) {
|
|
197
224
|
console.error("Not initialized. Run: ascendkit init");
|
|
198
|
-
|
|
225
|
+
exitCli(1);
|
|
226
|
+
throw new Error("unreachable");
|
|
199
227
|
}
|
|
200
228
|
if (!publicKey && env?.publicKey) {
|
|
201
229
|
publicKey = env.publicKey;
|
|
@@ -205,7 +233,8 @@ function getClient() {
|
|
|
205
233
|
}
|
|
206
234
|
if (!publicKey) {
|
|
207
235
|
console.error("No environment set. Run: ascendkit set-env <public-key>");
|
|
208
|
-
|
|
236
|
+
exitCli(1);
|
|
237
|
+
throw new Error("unreachable");
|
|
209
238
|
}
|
|
210
239
|
const client = new AscendKitClient({
|
|
211
240
|
apiUrl: apiUrl ?? DEFAULT_API_URL,
|
|
@@ -241,6 +270,84 @@ async function readSecretFromStdin() {
|
|
|
241
270
|
function output(data) {
|
|
242
271
|
console.log(JSON.stringify(data, null, 2));
|
|
243
272
|
}
|
|
273
|
+
function printAuthSettingsSummary(data) {
|
|
274
|
+
const providers = Array.isArray(data.providers) ? data.providers : [];
|
|
275
|
+
const features = data.features ?? {};
|
|
276
|
+
const featureLines = [
|
|
277
|
+
["Email verification", features.emailVerification],
|
|
278
|
+
["Waitlist", features.waitlist],
|
|
279
|
+
["Password reset", features.passwordReset],
|
|
280
|
+
["Require username", features.requireUsername],
|
|
281
|
+
];
|
|
282
|
+
console.log(`Providers: ${providers.length > 0 ? providers.join(", ") : "none"}`);
|
|
283
|
+
for (const [label, enabled] of featureLines) {
|
|
284
|
+
console.log(`${label}: ${enabled ? "on" : "off"}`);
|
|
285
|
+
}
|
|
286
|
+
if (data.sessionDuration) {
|
|
287
|
+
console.log(`Session: ${data.sessionDuration}`);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
function printTemplateSummary(data, opts) {
|
|
291
|
+
console.log(`Template: ${data.name} (${data.id})`);
|
|
292
|
+
if (data.slug)
|
|
293
|
+
console.log(`Slug: ${data.slug}`);
|
|
294
|
+
console.log(`Subject: ${data.subject ?? "-"}`);
|
|
295
|
+
if (data.currentVersion != null)
|
|
296
|
+
console.log(`Version: ${data.currentVersion}`);
|
|
297
|
+
if (Array.isArray(data.variables) && data.variables.length > 0) {
|
|
298
|
+
console.log(`Variables: ${data.variables.join(", ")}`);
|
|
299
|
+
}
|
|
300
|
+
if (Array.isArray(data.unconfiguredVariables) && data.unconfiguredVariables.length > 0) {
|
|
301
|
+
console.log(`\n⚠ Unconfigured variables: ${data.unconfiguredVariables.join(", ")}`);
|
|
302
|
+
console.log(` Set values with: ascendkit keystore set <key> <value>`);
|
|
303
|
+
}
|
|
304
|
+
if (Array.isArray(data.relatedJourneys) && data.relatedJourneys.length > 0) {
|
|
305
|
+
const refs = data.relatedJourneys.map((j) => `${j.name} (${j.id})`).join(", ");
|
|
306
|
+
console.log(`Used in journeys: ${refs}`);
|
|
307
|
+
}
|
|
308
|
+
// Show body content on show/get (verbose) but not on create/update summaries
|
|
309
|
+
if (opts?.verbose) {
|
|
310
|
+
if (data.bodyHtml) {
|
|
311
|
+
console.log(`\n--- HTML Body ---\n${data.bodyHtml}`);
|
|
312
|
+
}
|
|
313
|
+
if (data.bodyText) {
|
|
314
|
+
console.log(`\n--- Plain Text Body ---\n${data.bodyText}`);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
// Always show warnings and variable diffs (relevant on update responses)
|
|
318
|
+
if (Array.isArray(data.droppedVariables) && data.droppedVariables.length > 0) {
|
|
319
|
+
console.log(`\n⚠ Variables removed: ${data.droppedVariables.join(", ")}`);
|
|
320
|
+
}
|
|
321
|
+
if (Array.isArray(data.addedVariables) && data.addedVariables.length > 0) {
|
|
322
|
+
console.log(`Variables added: ${data.addedVariables.join(", ")}`);
|
|
323
|
+
}
|
|
324
|
+
if (Array.isArray(data.warnings) && data.warnings.length > 0) {
|
|
325
|
+
for (const w of data.warnings) {
|
|
326
|
+
console.log(`⚠ ${w}`);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
function printSurveySummary(data) {
|
|
331
|
+
console.log(`Survey: ${data.name} (${data.id})`);
|
|
332
|
+
console.log(`Type: ${data.type ?? "custom"} | Status: ${data.status ?? "draft"}`);
|
|
333
|
+
if (data.slug)
|
|
334
|
+
console.log(`Slug: ${data.slug}`);
|
|
335
|
+
const questions = Array.isArray(data.definition?.pages)
|
|
336
|
+
? data.definition.pages.flatMap((page) => page.elements ?? []).length
|
|
337
|
+
: undefined;
|
|
338
|
+
if (questions != null)
|
|
339
|
+
console.log(`Questions: ${questions}`);
|
|
340
|
+
}
|
|
341
|
+
function printProjectSummary(data) {
|
|
342
|
+
console.log(`Project: ${data.id}`);
|
|
343
|
+
console.log(`Name: ${data.name}`);
|
|
344
|
+
if (Array.isArray(data.enabledServices) && data.enabledServices.length > 0) {
|
|
345
|
+
console.log(`Services: ${data.enabledServices.join(", ")}`);
|
|
346
|
+
}
|
|
347
|
+
if (data.environment?.publicKey) {
|
|
348
|
+
console.log(`Environment: ${data.environment.publicKey}`);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
244
351
|
function normalizeJourneyRows(data) {
|
|
245
352
|
if (Array.isArray(data)) {
|
|
246
353
|
return data;
|
|
@@ -277,7 +384,36 @@ function table(rows, columns) {
|
|
|
277
384
|
}
|
|
278
385
|
}
|
|
279
386
|
async function run() {
|
|
387
|
+
installGlobalHandlers();
|
|
280
388
|
const args = process.argv.slice(2);
|
|
389
|
+
const dtEntered = new Date();
|
|
390
|
+
// Extract command/domain/action for telemetry before any early returns
|
|
391
|
+
const teleDomain = args[0] ?? null;
|
|
392
|
+
const teleAction = args[1] ?? null;
|
|
393
|
+
const redactedArgs = redactArgs(args);
|
|
394
|
+
// Register telemetry hook — runs on every exit path (success or failure)
|
|
395
|
+
onExit(async (code, error) => {
|
|
396
|
+
const dtCompleted = new Date();
|
|
397
|
+
const record = {
|
|
398
|
+
invocationId: getInvocationId(),
|
|
399
|
+
machineId: getMachineId(),
|
|
400
|
+
clientType: "cli",
|
|
401
|
+
clientVersion: CLI_VERSION,
|
|
402
|
+
command: redactedArgs.join(" ") || "(empty)",
|
|
403
|
+
domain: teleDomain,
|
|
404
|
+
action: teleAction,
|
|
405
|
+
args: redactedArgs,
|
|
406
|
+
dtEntered: dtEntered.toISOString(),
|
|
407
|
+
dtCompleted: dtCompleted.toISOString(),
|
|
408
|
+
durationMs: dtCompleted.getTime() - dtEntered.getTime(),
|
|
409
|
+
success: code === 0,
|
|
410
|
+
errorMessage: error?.message ?? null,
|
|
411
|
+
hostname: osHostname(),
|
|
412
|
+
os: osPlatform(),
|
|
413
|
+
nodeVersion: process.version,
|
|
414
|
+
};
|
|
415
|
+
await captureTelemetry(record);
|
|
416
|
+
});
|
|
281
417
|
if (args[0] === "--version" || args[0] === "-v" || args[0] === "-V") {
|
|
282
418
|
console.log(CLI_VERSION);
|
|
283
419
|
return;
|
|
@@ -291,7 +427,7 @@ async function run() {
|
|
|
291
427
|
if (!printSectionHelp(args[1])) {
|
|
292
428
|
console.error(`Unknown help section: ${args[1]}`);
|
|
293
429
|
console.error('Run "ascendkit --help" for available sections.');
|
|
294
|
-
|
|
430
|
+
return await exitCli(1);
|
|
295
431
|
}
|
|
296
432
|
return;
|
|
297
433
|
}
|
|
@@ -307,7 +443,7 @@ async function run() {
|
|
|
307
443
|
if (!printSectionHelp(domain)) {
|
|
308
444
|
console.error(`Unknown command section: ${domain}`);
|
|
309
445
|
console.error('Run "ascendkit --help" for usage');
|
|
310
|
-
|
|
446
|
+
return await exitCli(1);
|
|
311
447
|
}
|
|
312
448
|
return;
|
|
313
449
|
}
|
|
@@ -321,7 +457,7 @@ async function run() {
|
|
|
321
457
|
case "logout":
|
|
322
458
|
platform.logout();
|
|
323
459
|
return;
|
|
324
|
-
case "
|
|
460
|
+
case "project":
|
|
325
461
|
if (action === "list") {
|
|
326
462
|
const projects = await platform.listProjects();
|
|
327
463
|
table(projects, [
|
|
@@ -330,18 +466,25 @@ async function run() {
|
|
|
330
466
|
{ key: "enabledServices", label: "Services", width: 30 },
|
|
331
467
|
]);
|
|
332
468
|
}
|
|
469
|
+
else if (action === "show") {
|
|
470
|
+
if (!args[2]) {
|
|
471
|
+
console.error("Usage: ascendkit project show <project-id>");
|
|
472
|
+
return await exitCli(1);
|
|
473
|
+
}
|
|
474
|
+
printProjectSummary(await platform.showProject(args[2]));
|
|
475
|
+
}
|
|
476
|
+
else if (action === "env") {
|
|
477
|
+
await runProjectEnvironment(args.slice(2));
|
|
478
|
+
}
|
|
333
479
|
else if (action === "create") {
|
|
334
480
|
const flags = parseFlags(args.slice(2));
|
|
335
481
|
if (!flags.name) {
|
|
336
|
-
console.error("Usage: ascendkit
|
|
337
|
-
|
|
482
|
+
console.error("Usage: ascendkit project create --name <name> [--description <description>] [--services <s1,s2,...>]");
|
|
483
|
+
return await exitCli(1);
|
|
338
484
|
}
|
|
339
485
|
try {
|
|
340
486
|
const proj = await platform.createProject(flags.name, flags.description, flags.services?.split(","));
|
|
341
|
-
|
|
342
|
-
console.log(`Project created: ${proj.id}`);
|
|
343
|
-
if (env)
|
|
344
|
-
console.log(`Environment: ${env.publicKey}`);
|
|
487
|
+
printProjectSummary(proj);
|
|
345
488
|
}
|
|
346
489
|
catch (err) {
|
|
347
490
|
let message = err instanceof Error ? err.message : String(err);
|
|
@@ -357,18 +500,18 @@ async function run() {
|
|
|
357
500
|
catch { /* use raw message */ }
|
|
358
501
|
}
|
|
359
502
|
console.error(message);
|
|
360
|
-
|
|
503
|
+
return await exitCli(1);
|
|
361
504
|
}
|
|
362
505
|
}
|
|
363
506
|
else {
|
|
364
|
-
console.error('Usage: ascendkit
|
|
365
|
-
|
|
507
|
+
console.error('Usage: ascendkit project list|create|show|env');
|
|
508
|
+
return await exitCli(1);
|
|
366
509
|
}
|
|
367
510
|
return;
|
|
368
511
|
case "set-env":
|
|
369
512
|
if (!action) {
|
|
370
513
|
console.error("Usage: ascendkit set-env <public-key>");
|
|
371
|
-
|
|
514
|
+
return await exitCli(1);
|
|
372
515
|
}
|
|
373
516
|
await platform.setEnv(action);
|
|
374
517
|
return;
|
|
@@ -378,8 +521,11 @@ async function run() {
|
|
|
378
521
|
case "verify":
|
|
379
522
|
await runVerify();
|
|
380
523
|
return;
|
|
381
|
-
case "
|
|
382
|
-
await
|
|
524
|
+
case "environment":
|
|
525
|
+
await runEnvironment(action, args.slice(2));
|
|
526
|
+
return;
|
|
527
|
+
case "keystore":
|
|
528
|
+
await runKeystore(action, args.slice(2));
|
|
383
529
|
return;
|
|
384
530
|
}
|
|
385
531
|
// Service commands (need environment key)
|
|
@@ -388,6 +534,7 @@ async function run() {
|
|
|
388
534
|
case "auth":
|
|
389
535
|
await runAuth(client, action, args.slice(2));
|
|
390
536
|
break;
|
|
537
|
+
case "template":
|
|
391
538
|
case "templates":
|
|
392
539
|
case "content":
|
|
393
540
|
await runContent(client, action, args.slice(2));
|
|
@@ -398,7 +545,7 @@ async function run() {
|
|
|
398
545
|
case "journey":
|
|
399
546
|
await runJourney(client, action, args.slice(2));
|
|
400
547
|
break;
|
|
401
|
-
case "email":
|
|
548
|
+
case "email-identity":
|
|
402
549
|
await runEmail(client, action, args.slice(2));
|
|
403
550
|
break;
|
|
404
551
|
case "webhook":
|
|
@@ -413,17 +560,33 @@ async function run() {
|
|
|
413
560
|
default:
|
|
414
561
|
console.error(`Unknown command: ${domain}`);
|
|
415
562
|
console.error('Run "ascendkit --help" for usage');
|
|
416
|
-
|
|
563
|
+
return await exitCli(1);
|
|
417
564
|
}
|
|
418
565
|
}
|
|
419
566
|
async function runImport(client, source, rest) {
|
|
420
|
-
|
|
567
|
+
if (!source) {
|
|
568
|
+
console.log(HELP_SECTION.import);
|
|
569
|
+
return;
|
|
570
|
+
}
|
|
571
|
+
let action = rest[0];
|
|
572
|
+
let args = rest;
|
|
421
573
|
if (source === "create-migration-journey") {
|
|
574
|
+
source = "migration-journey";
|
|
575
|
+
action = "create";
|
|
576
|
+
}
|
|
577
|
+
if (source === "clerk" && (action === "preview" || action === "run")) {
|
|
578
|
+
args = rest.slice(1);
|
|
579
|
+
}
|
|
580
|
+
else if (source === "clerk") {
|
|
581
|
+
action = flagsFromLegacy(rest).execute ? "run" : "preview";
|
|
582
|
+
}
|
|
583
|
+
const flags = parseFlags(args);
|
|
584
|
+
if (source === "migration-journey" && action === "create") {
|
|
422
585
|
const result = await importCmd.instantiateMigrationJourney(client, flags["from-identity"]);
|
|
423
586
|
output(result);
|
|
424
587
|
console.log("\nNext steps:");
|
|
425
588
|
console.log(" ascendkit journey list — review created journeys");
|
|
426
|
-
console.log(" ascendkit
|
|
589
|
+
console.log(" ascendkit template list — review migration email templates");
|
|
427
590
|
console.log(" ascendkit journey activate <journey-id> — activate when ready");
|
|
428
591
|
return;
|
|
429
592
|
}
|
|
@@ -431,10 +594,9 @@ async function runImport(client, source, rest) {
|
|
|
431
594
|
console.error(`Unsupported import source: ${source}`);
|
|
432
595
|
console.error("Supported sources: clerk");
|
|
433
596
|
console.error('Run "ascendkit help import" for usage');
|
|
434
|
-
|
|
597
|
+
return await exitCli(1);
|
|
435
598
|
}
|
|
436
|
-
const
|
|
437
|
-
const dryRun = !execute;
|
|
599
|
+
const dryRun = action !== "run";
|
|
438
600
|
const hasUsers = flags.users !== undefined;
|
|
439
601
|
const hasSettings = flags.settings !== undefined;
|
|
440
602
|
// If neither --users nor --settings is passed, both default to true.
|
|
@@ -444,8 +606,8 @@ async function runImport(client, source, rest) {
|
|
|
444
606
|
const apiKey = flags["api-key"];
|
|
445
607
|
const filePath = flags.file;
|
|
446
608
|
if (!apiKey && !filePath) {
|
|
447
|
-
console.error("Usage: ascendkit import clerk --api-key <key> | --file <path>
|
|
448
|
-
|
|
609
|
+
console.error("Usage: ascendkit import clerk preview|run --api-key <key> | --file <path>");
|
|
610
|
+
return await exitCli(1);
|
|
449
611
|
}
|
|
450
612
|
let clerkUsers = [];
|
|
451
613
|
if (filePath) {
|
|
@@ -558,36 +720,50 @@ async function runImport(client, source, rest) {
|
|
|
558
720
|
}
|
|
559
721
|
else if (totalImported > 0) {
|
|
560
722
|
console.log("\nTo set up migration emails, run:\n" +
|
|
561
|
-
" ascendkit import
|
|
723
|
+
" ascendkit import migration-journey create");
|
|
562
724
|
}
|
|
563
725
|
}
|
|
564
|
-
|
|
565
|
-
|
|
726
|
+
function flagsFromLegacy(args) {
|
|
727
|
+
return parseFlags(args);
|
|
728
|
+
}
|
|
729
|
+
async function runProjectEnvironment(rest) {
|
|
730
|
+
const action = rest[0];
|
|
731
|
+
const target = rest[1];
|
|
566
732
|
switch (action) {
|
|
567
733
|
case "list":
|
|
568
|
-
if (!
|
|
569
|
-
console.error("Usage: ascendkit env list
|
|
570
|
-
|
|
734
|
+
if (!target) {
|
|
735
|
+
console.error("Usage: ascendkit project env list <project-id>");
|
|
736
|
+
return await exitCli(1);
|
|
571
737
|
}
|
|
572
|
-
table(await platform.listEnvironments(
|
|
738
|
+
table(await platform.listEnvironments(target), [
|
|
739
|
+
{ key: "id", label: "ID" },
|
|
573
740
|
{ key: "name", label: "Name", width: 20 },
|
|
574
741
|
{ key: "tier", label: "Tier" },
|
|
575
742
|
{ key: "publicKey", label: "Public Key" },
|
|
576
743
|
]);
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
744
|
+
return;
|
|
745
|
+
default:
|
|
746
|
+
console.error("Usage: ascendkit project env list <project-id>");
|
|
747
|
+
return await exitCli(1);
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
async function runEnvironment(action, rest) {
|
|
751
|
+
const flags = parseFlags(rest);
|
|
752
|
+
const ctx = loadEnvContext();
|
|
753
|
+
if (!ctx) {
|
|
754
|
+
console.error("No environment set. Run: ascendkit set-env <public-key>");
|
|
755
|
+
return await exitCli(1);
|
|
756
|
+
}
|
|
757
|
+
switch (action) {
|
|
758
|
+
case "show":
|
|
759
|
+
output(await platform.getEnvironment(ctx.projectId, ctx.environmentId));
|
|
760
|
+
return;
|
|
585
761
|
case "promote": {
|
|
586
|
-
const envId = rest[0];
|
|
762
|
+
const envId = rest[0] && !rest[0].startsWith("--") ? rest[0] : ctx.environmentId;
|
|
587
763
|
const target = flags.target;
|
|
588
764
|
if (!envId || !target) {
|
|
589
|
-
console.error("Usage: ascendkit
|
|
590
|
-
|
|
765
|
+
console.error("Usage: ascendkit environment promote [<env-id>] --target <tier>");
|
|
766
|
+
return await exitCli(1);
|
|
591
767
|
}
|
|
592
768
|
try {
|
|
593
769
|
const result = await platform.promoteEnvironment(envId, target);
|
|
@@ -608,24 +784,20 @@ async function runEnv(action, rest) {
|
|
|
608
784
|
catch { /* use raw message */ }
|
|
609
785
|
}
|
|
610
786
|
console.error(message);
|
|
611
|
-
|
|
787
|
+
return await exitCli(1);
|
|
612
788
|
}
|
|
613
|
-
|
|
789
|
+
return;
|
|
614
790
|
}
|
|
615
791
|
case "update": {
|
|
616
|
-
const envId = rest[0];
|
|
617
|
-
if (!envId || !flags.project) {
|
|
618
|
-
console.error("Usage: ascendkit env update <env-id> --project <project-id> [--name <name>] [--description <desc>]");
|
|
619
|
-
process.exit(1);
|
|
620
|
-
}
|
|
792
|
+
const envId = rest[0] && !rest[0].startsWith("--") ? rest[0] : ctx.environmentId;
|
|
621
793
|
const name = flags.name;
|
|
622
794
|
const description = flags.description;
|
|
623
795
|
if (!name && description === undefined) {
|
|
624
796
|
console.error("Provide at least --name or --description to update.");
|
|
625
|
-
|
|
797
|
+
return await exitCli(1);
|
|
626
798
|
}
|
|
627
799
|
try {
|
|
628
|
-
const result = await platform.updateEnvironment(
|
|
800
|
+
const result = await platform.updateEnvironment(ctx.projectId, envId, name, description);
|
|
629
801
|
console.log("Environment updated:");
|
|
630
802
|
console.log(JSON.stringify(result, null, 2));
|
|
631
803
|
}
|
|
@@ -643,174 +815,234 @@ async function runEnv(action, rest) {
|
|
|
643
815
|
catch { /* use raw message */ }
|
|
644
816
|
}
|
|
645
817
|
console.error(message);
|
|
646
|
-
|
|
818
|
+
return await exitCli(1);
|
|
647
819
|
}
|
|
648
|
-
|
|
820
|
+
return;
|
|
649
821
|
}
|
|
650
|
-
|
|
822
|
+
default:
|
|
823
|
+
console.error(`Unknown environment command: ${action}`);
|
|
824
|
+
console.error("Usage: ascendkit environment show|update|promote");
|
|
825
|
+
return await exitCli(1);
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
async function runKeystore(action, rest) {
|
|
829
|
+
const ctx = loadEnvContext();
|
|
830
|
+
if (!ctx) {
|
|
831
|
+
console.error("No environment set. Run: ascendkit set-env <public-key>");
|
|
832
|
+
return await exitCli(1);
|
|
833
|
+
}
|
|
834
|
+
const current = await platform.getEnvironment(ctx.projectId, ctx.environmentId);
|
|
835
|
+
const vars = { ...(current.variables ?? {}) };
|
|
836
|
+
const systemVars = Array.isArray(current.systemVariables)
|
|
837
|
+
? current.systemVariables
|
|
838
|
+
: [];
|
|
839
|
+
switch (action) {
|
|
840
|
+
case "set": {
|
|
651
841
|
const key = rest[0];
|
|
652
842
|
const value = rest[1];
|
|
653
843
|
if (!key || value === undefined) {
|
|
654
|
-
console.error("Usage: ascendkit
|
|
655
|
-
|
|
656
|
-
}
|
|
657
|
-
const ctx = loadEnvContext();
|
|
658
|
-
if (!ctx) {
|
|
659
|
-
console.error("No environment set. Run: ascendkit env use <tier> --project <project-id>");
|
|
660
|
-
process.exit(1);
|
|
844
|
+
console.error("Usage: ascendkit keystore set <key> <value>");
|
|
845
|
+
return await exitCli(1);
|
|
661
846
|
}
|
|
662
|
-
const current = await platform.getEnvironment(ctx.projectId, ctx.environmentId);
|
|
663
|
-
const vars = { ...(current.variables ?? {}) };
|
|
664
847
|
vars[key] = value;
|
|
665
848
|
await platform.updateEnvironmentVariables(ctx.projectId, ctx.environmentId, vars);
|
|
666
|
-
console.log(`
|
|
667
|
-
|
|
849
|
+
console.log(`Saved ${key}=${value}`);
|
|
850
|
+
return;
|
|
668
851
|
}
|
|
669
|
-
case "
|
|
852
|
+
case "remove": {
|
|
670
853
|
const key = rest[0];
|
|
671
854
|
if (!key) {
|
|
672
|
-
console.error("Usage: ascendkit
|
|
673
|
-
|
|
855
|
+
console.error("Usage: ascendkit keystore remove <key>");
|
|
856
|
+
return await exitCli(1);
|
|
674
857
|
}
|
|
675
|
-
const ctx = loadEnvContext();
|
|
676
|
-
if (!ctx) {
|
|
677
|
-
console.error("No environment set. Run: ascendkit env use <tier> --project <project-id>");
|
|
678
|
-
process.exit(1);
|
|
679
|
-
}
|
|
680
|
-
const current = await platform.getEnvironment(ctx.projectId, ctx.environmentId);
|
|
681
|
-
const vars = { ...(current.variables ?? {}) };
|
|
682
858
|
if (!(key in vars)) {
|
|
683
859
|
console.error(`Variable "${key}" not found.`);
|
|
684
|
-
|
|
860
|
+
return await exitCli(1);
|
|
685
861
|
}
|
|
686
862
|
delete vars[key];
|
|
687
863
|
await platform.updateEnvironmentVariables(ctx.projectId, ctx.environmentId, vars);
|
|
688
|
-
console.log(`
|
|
689
|
-
|
|
864
|
+
console.log(`Removed ${key}`);
|
|
865
|
+
return;
|
|
690
866
|
}
|
|
691
|
-
case "list
|
|
692
|
-
const
|
|
693
|
-
if (!ctx) {
|
|
694
|
-
console.error("No environment set. Run: ascendkit env use <tier> --project <project-id>");
|
|
695
|
-
process.exit(1);
|
|
696
|
-
}
|
|
697
|
-
const current = await platform.getEnvironment(ctx.projectId, ctx.environmentId);
|
|
698
|
-
const vars = current.variables ?? {};
|
|
699
|
-
const entries = Object.entries(vars);
|
|
867
|
+
case "list": {
|
|
868
|
+
const entries = Object.entries(vars).map(([key, value]) => ({ key, value }));
|
|
700
869
|
if (entries.length === 0) {
|
|
701
|
-
console.log("No variables set.");
|
|
870
|
+
console.log("No custom variables set.");
|
|
702
871
|
}
|
|
703
872
|
else {
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
873
|
+
console.log("Custom variables:");
|
|
874
|
+
table(entries, [
|
|
875
|
+
{ key: "key", label: "Key", width: 32 },
|
|
876
|
+
{ key: "value", label: "Value", width: 60 },
|
|
877
|
+
]);
|
|
707
878
|
}
|
|
708
|
-
|
|
879
|
+
if (systemVars.length > 0) {
|
|
880
|
+
console.log(entries.length > 0 ? "\nSystem variables:" : "System variables:");
|
|
881
|
+
table(systemVars.map((item) => ({
|
|
882
|
+
key: item.key,
|
|
883
|
+
value: item.valuePreview,
|
|
884
|
+
availability: item.availability,
|
|
885
|
+
})), [
|
|
886
|
+
{ key: "key", label: "Key", width: 24 },
|
|
887
|
+
{ key: "value", label: "Value", width: 48 },
|
|
888
|
+
{ key: "availability", label: "Availability", width: 22 },
|
|
889
|
+
]);
|
|
890
|
+
}
|
|
891
|
+
return;
|
|
709
892
|
}
|
|
710
893
|
default:
|
|
711
|
-
console.error(`Unknown
|
|
712
|
-
console.error("Usage: ascendkit
|
|
713
|
-
|
|
894
|
+
console.error(`Unknown keystore command: ${action}`);
|
|
895
|
+
console.error("Usage: ascendkit keystore list|set|remove");
|
|
896
|
+
return await exitCli(1);
|
|
714
897
|
}
|
|
715
898
|
}
|
|
716
899
|
async function runAuth(client, action, rest) {
|
|
717
900
|
const flags = parseFlags(rest);
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
901
|
+
const normalizedAction = !action ? "show" :
|
|
902
|
+
action === "settings" ? (rest[0] === "update" ? "update" : "show") :
|
|
903
|
+
action === "show" ? "show" :
|
|
904
|
+
action === "update" ? "update" :
|
|
905
|
+
action;
|
|
906
|
+
switch (normalizedAction) {
|
|
907
|
+
case "show":
|
|
908
|
+
printAuthSettingsSummary(await auth.getSettings(client));
|
|
909
|
+
break;
|
|
910
|
+
case "update": {
|
|
911
|
+
const params = {};
|
|
912
|
+
if (flags.providers)
|
|
913
|
+
params.providers = flags.providers.split(",");
|
|
914
|
+
if (flags["email-verification"] || flags.waitlist || flags["password-reset"]) {
|
|
915
|
+
params.features = {};
|
|
916
|
+
if (flags["email-verification"])
|
|
917
|
+
params.features.emailVerification = flags["email-verification"] === "true";
|
|
918
|
+
if (flags.waitlist)
|
|
919
|
+
params.features.waitlist = flags.waitlist === "true";
|
|
920
|
+
if (flags["password-reset"])
|
|
921
|
+
params.features.passwordReset = flags["password-reset"] === "true";
|
|
922
|
+
}
|
|
923
|
+
if (flags["session-duration"])
|
|
924
|
+
params.sessionDuration = flags["session-duration"];
|
|
925
|
+
printAuthSettingsSummary(await auth.updateSettings(client, params));
|
|
926
|
+
break;
|
|
927
|
+
}
|
|
928
|
+
case "provider":
|
|
929
|
+
case "providers":
|
|
930
|
+
if (normalizedAction === "providers" || rest[0] === "set") {
|
|
931
|
+
const providersArg = normalizedAction === "providers" ? rest[0] : rest[1];
|
|
932
|
+
if (!providersArg) {
|
|
933
|
+
console.error("Usage: ascendkit auth provider set <p1,p2,...>");
|
|
934
|
+
return await exitCli(1);
|
|
732
935
|
}
|
|
733
|
-
|
|
734
|
-
params.sessionDuration = flags["session-duration"];
|
|
735
|
-
const s = await auth.updateSettings(client, params);
|
|
736
|
-
console.log(`Providers: ${Array.isArray(s.providers) ? s.providers.join(", ") : "none"}`);
|
|
737
|
-
const f = s.features ?? {};
|
|
738
|
-
const enabled = Object.entries(f).filter(([, v]) => v).map(([k]) => k);
|
|
739
|
-
if (enabled.length)
|
|
740
|
-
console.log(`Features: ${enabled.join(", ")}`);
|
|
741
|
-
if (s.sessionDuration)
|
|
742
|
-
console.log(`Session: ${s.sessionDuration}`);
|
|
936
|
+
printAuthSettingsSummary(await auth.updateProviders(client, providersArg.split(",")));
|
|
743
937
|
}
|
|
744
938
|
else {
|
|
745
|
-
|
|
939
|
+
const settings = await auth.getSettings(client);
|
|
940
|
+
const providers = Array.isArray(settings.providers) ? settings.providers.map((provider) => ({ provider })) : [];
|
|
941
|
+
if (providers.length === 0)
|
|
942
|
+
console.log("No providers configured.");
|
|
943
|
+
else
|
|
944
|
+
table(providers, [{ key: "provider", label: "Provider", width: 20 }]);
|
|
746
945
|
}
|
|
747
946
|
break;
|
|
748
|
-
case "providers":
|
|
749
|
-
if (!rest[0]) {
|
|
750
|
-
console.error("Usage: ascendkit auth providers <p1,p2,...>");
|
|
751
|
-
process.exit(1);
|
|
752
|
-
}
|
|
753
|
-
output(await auth.updateProviders(client, rest[0].split(",")));
|
|
754
|
-
break;
|
|
755
947
|
case "oauth": {
|
|
756
|
-
|
|
757
|
-
|
|
948
|
+
const oauthAction = rest[0] === "open" || rest[0] === "set" || rest[0] === "remove" ? rest[0] : "open";
|
|
949
|
+
const provider = oauthAction === "open" && rest[0] !== "open" ? rest[0] : rest[1];
|
|
950
|
+
if (oauthAction === "set") {
|
|
758
951
|
if (!provider || !flags["client-id"]) {
|
|
759
952
|
console.error("Usage: ascendkit auth oauth set <provider> --client-id <id> [--client-secret <secret> | --client-secret-stdin] [--callback-url <url>]");
|
|
760
|
-
|
|
953
|
+
return await exitCli(1);
|
|
761
954
|
}
|
|
762
955
|
const secretFromArg = flags["client-secret"];
|
|
763
956
|
const secretFromStdin = flags["client-secret-stdin"] === "true";
|
|
764
957
|
if (!secretFromArg && !secretFromStdin) {
|
|
765
958
|
console.error("Missing client secret. Use --client-secret-stdin (recommended) or --client-secret.");
|
|
766
|
-
|
|
959
|
+
return await exitCli(1);
|
|
767
960
|
}
|
|
768
961
|
if (secretFromArg && secretFromStdin) {
|
|
769
962
|
console.error("Use only one of --client-secret or --client-secret-stdin.");
|
|
770
|
-
|
|
963
|
+
return await exitCli(1);
|
|
771
964
|
}
|
|
772
965
|
const clientSecret = secretFromArg ?? await readSecretFromStdin();
|
|
773
966
|
if (!clientSecret) {
|
|
774
967
|
console.error("Client secret cannot be empty.");
|
|
775
|
-
|
|
968
|
+
return await exitCli(1);
|
|
969
|
+
}
|
|
970
|
+
await auth.updateOAuthCredentials(client, provider, flags["client-id"], clientSecret, flags["callback-url"]);
|
|
971
|
+
console.log(`Saved OAuth credentials for ${provider}.`);
|
|
972
|
+
}
|
|
973
|
+
else if (oauthAction === "remove") {
|
|
974
|
+
if (!provider) {
|
|
975
|
+
console.error("Usage: ascendkit auth oauth remove <provider>");
|
|
976
|
+
return await exitCli(1);
|
|
776
977
|
}
|
|
777
|
-
|
|
978
|
+
await auth.deleteOAuthCredentials(client, provider);
|
|
979
|
+
console.log(`Removed OAuth credentials for ${provider}.`);
|
|
778
980
|
}
|
|
779
981
|
else {
|
|
780
|
-
if (!
|
|
781
|
-
console.error("Usage: ascendkit auth oauth <provider>");
|
|
782
|
-
|
|
982
|
+
if (!provider) {
|
|
983
|
+
console.error("Usage: ascendkit auth oauth open <provider>");
|
|
984
|
+
return await exitCli(1);
|
|
783
985
|
}
|
|
784
986
|
const portalUrl = process.env.ASCENDKIT_PORTAL_URL ?? "http://localhost:3000";
|
|
785
|
-
const url = auth.getOAuthSetupUrl(portalUrl,
|
|
786
|
-
console.log(`Opening browser to configure ${
|
|
987
|
+
const url = auth.getOAuthSetupUrl(portalUrl, provider, client.currentPublicKey ?? undefined);
|
|
988
|
+
console.log(`Opening browser to configure ${provider} OAuth credentials...`);
|
|
787
989
|
console.log(url);
|
|
788
990
|
openBrowser(url);
|
|
789
991
|
}
|
|
790
992
|
break;
|
|
791
993
|
}
|
|
994
|
+
case "user":
|
|
792
995
|
case "users":
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
996
|
+
if (normalizedAction === "users" || !rest[0] || rest[0] === "list") {
|
|
997
|
+
table(await auth.listUsers(client), [
|
|
998
|
+
{ key: "id", label: "ID" },
|
|
999
|
+
{ key: "email", label: "Email", width: 35 },
|
|
1000
|
+
{ key: "name", label: "Name", width: 25 },
|
|
1001
|
+
{ key: "status", label: "Status" },
|
|
1002
|
+
]);
|
|
1003
|
+
}
|
|
1004
|
+
else if (rest[0] === "remove") {
|
|
1005
|
+
if (!rest[1]) {
|
|
1006
|
+
console.error("Usage: ascendkit auth user remove <user-id>");
|
|
1007
|
+
return await exitCli(1);
|
|
1008
|
+
}
|
|
1009
|
+
await auth.deleteUser(client, rest[1]);
|
|
1010
|
+
console.log(`Removed user ${rest[1]}.`);
|
|
1011
|
+
}
|
|
1012
|
+
else if (rest[0] === "reactivate") {
|
|
1013
|
+
if (!rest[1]) {
|
|
1014
|
+
console.error("Usage: ascendkit auth user reactivate <user-id>");
|
|
1015
|
+
return await exitCli(1);
|
|
1016
|
+
}
|
|
1017
|
+
await auth.reactivateUser(client, rest[1]);
|
|
1018
|
+
console.log(`Reactivated user ${rest[1]}.`);
|
|
1019
|
+
}
|
|
1020
|
+
else {
|
|
1021
|
+
console.error(`Unknown auth user command: ${rest[0]}`);
|
|
1022
|
+
return await exitCli(1);
|
|
1023
|
+
}
|
|
799
1024
|
break;
|
|
800
1025
|
default:
|
|
801
1026
|
console.error(`Unknown auth command: ${action}`);
|
|
802
|
-
|
|
1027
|
+
return await exitCli(1);
|
|
803
1028
|
}
|
|
804
1029
|
}
|
|
805
1030
|
async function runContent(client, action, rest) {
|
|
806
1031
|
const flags = parseFlags(rest);
|
|
807
|
-
|
|
1032
|
+
if (!action) {
|
|
1033
|
+
console.log(HELP_SECTION.template);
|
|
1034
|
+
return;
|
|
1035
|
+
}
|
|
1036
|
+
const normalizedAction = action === "show" || action === "get" ? "show" :
|
|
1037
|
+
action === "remove" || action === "delete" ? "remove" :
|
|
1038
|
+
action;
|
|
1039
|
+
switch (normalizedAction) {
|
|
808
1040
|
case "create":
|
|
809
1041
|
if (!flags.name || !flags.subject || !flags["body-html"] || !flags["body-text"]) {
|
|
810
|
-
console.error("Usage: ascendkit
|
|
811
|
-
|
|
1042
|
+
console.error("Usage: ascendkit template create --name <n> --subject <s> --body-html <h> --body-text <t> [--slug <slug>] [--description <desc>]");
|
|
1043
|
+
return await exitCli(1);
|
|
812
1044
|
}
|
|
813
|
-
|
|
1045
|
+
printTemplateSummary(await content.createTemplate(client, {
|
|
814
1046
|
name: flags.name, subject: flags.subject,
|
|
815
1047
|
bodyHtml: flags["body-html"], bodyText: flags["body-text"],
|
|
816
1048
|
slug: flags.slug, description: flags.description,
|
|
@@ -833,64 +1065,79 @@ async function runContent(client, action, rest) {
|
|
|
833
1065
|
]);
|
|
834
1066
|
break;
|
|
835
1067
|
}
|
|
836
|
-
case "
|
|
1068
|
+
case "show":
|
|
837
1069
|
if (!rest[0]) {
|
|
838
|
-
console.error("Usage: ascendkit
|
|
839
|
-
|
|
1070
|
+
console.error("Usage: ascendkit template show <template-id>");
|
|
1071
|
+
return await exitCli(1);
|
|
840
1072
|
}
|
|
841
|
-
|
|
1073
|
+
printTemplateSummary(await content.getTemplate(client, rest[0]), { verbose: true });
|
|
842
1074
|
break;
|
|
843
1075
|
case "update":
|
|
844
1076
|
if (!rest[0]) {
|
|
845
|
-
console.error("Usage: ascendkit
|
|
846
|
-
|
|
1077
|
+
console.error("Usage: ascendkit template update <template-id> [--flags]");
|
|
1078
|
+
return await exitCli(1);
|
|
847
1079
|
}
|
|
848
|
-
|
|
1080
|
+
printTemplateSummary(await content.updateTemplate(client, rest[0], {
|
|
849
1081
|
subject: flags.subject,
|
|
850
1082
|
bodyHtml: flags["body-html"],
|
|
851
1083
|
bodyText: flags["body-text"],
|
|
852
1084
|
changeNote: flags["change-note"],
|
|
853
1085
|
}));
|
|
854
1086
|
break;
|
|
855
|
-
case "
|
|
1087
|
+
case "remove":
|
|
856
1088
|
if (!rest[0]) {
|
|
857
|
-
console.error("Usage: ascendkit
|
|
858
|
-
|
|
1089
|
+
console.error("Usage: ascendkit template remove <template-id>");
|
|
1090
|
+
return await exitCli(1);
|
|
859
1091
|
}
|
|
860
|
-
|
|
1092
|
+
await content.deleteTemplate(client, rest[0]);
|
|
1093
|
+
console.log(`Removed template ${rest[0]}.`);
|
|
861
1094
|
break;
|
|
862
1095
|
case "versions":
|
|
863
|
-
if (!rest[0]) {
|
|
864
|
-
console.error("Usage: ascendkit templates versions <template-id>");
|
|
865
|
-
process.exit(1);
|
|
866
|
-
}
|
|
867
|
-
output(await content.listVersions(client, rest[0]));
|
|
868
|
-
break;
|
|
869
1096
|
case "version":
|
|
870
|
-
if (
|
|
871
|
-
|
|
872
|
-
|
|
1097
|
+
if (normalizedAction === "versions" || rest[0] === "list") {
|
|
1098
|
+
const templateId = normalizedAction === "versions" ? rest[0] : rest[1];
|
|
1099
|
+
if (!templateId) {
|
|
1100
|
+
console.error("Usage: ascendkit template version list <template-id>");
|
|
1101
|
+
return await exitCli(1);
|
|
1102
|
+
}
|
|
1103
|
+
const versions = await content.listVersions(client, templateId);
|
|
1104
|
+
table(versions, [
|
|
1105
|
+
{ key: "versionNumber", label: "Version" },
|
|
1106
|
+
{ key: "createdAt", label: "Created", width: 24 },
|
|
1107
|
+
{ key: "changeNote", label: "Change note", width: 40 },
|
|
1108
|
+
]);
|
|
1109
|
+
}
|
|
1110
|
+
else {
|
|
1111
|
+
const templateId = normalizedAction === "version" && rest[0] === "show" ? rest[1] : normalizedAction === "version" ? rest[0] : rest[1];
|
|
1112
|
+
const versionNumber = normalizedAction === "version" && rest[0] === "show" ? rest[2] : normalizedAction === "version" ? rest[1] : rest[2];
|
|
1113
|
+
if (!templateId || !versionNumber) {
|
|
1114
|
+
console.error("Usage: ascendkit template version show <template-id> <n>");
|
|
1115
|
+
return await exitCli(1);
|
|
1116
|
+
}
|
|
1117
|
+
output(await content.getVersion(client, templateId, parseInt(versionNumber, 10)));
|
|
873
1118
|
}
|
|
874
|
-
output(await content.getVersion(client, rest[0], parseInt(rest[1], 10)));
|
|
875
1119
|
break;
|
|
876
1120
|
default:
|
|
877
|
-
console.error(`Unknown
|
|
878
|
-
|
|
1121
|
+
console.error(`Unknown template command: ${action}`);
|
|
1122
|
+
return await exitCli(1);
|
|
879
1123
|
}
|
|
880
1124
|
}
|
|
881
1125
|
async function runSurvey(client, action, rest) {
|
|
882
1126
|
const flags = parseFlags(rest);
|
|
883
|
-
|
|
1127
|
+
const normalizedAction = action === "show" || action === "get" ? "show" :
|
|
1128
|
+
action === "remove" || action === "delete" ? "remove" :
|
|
1129
|
+
action;
|
|
1130
|
+
if (!normalizedAction) {
|
|
884
1131
|
console.log(HELP_SECTION.survey);
|
|
885
1132
|
return;
|
|
886
1133
|
}
|
|
887
|
-
switch (
|
|
1134
|
+
switch (normalizedAction) {
|
|
888
1135
|
case "create":
|
|
889
1136
|
if (!flags.name) {
|
|
890
1137
|
console.error("Usage: ascendkit survey create --name <n> [--type nps|csat|custom]");
|
|
891
|
-
|
|
1138
|
+
return await exitCli(1);
|
|
892
1139
|
}
|
|
893
|
-
|
|
1140
|
+
printSurveySummary(await surveys.createSurvey(client, {
|
|
894
1141
|
name: flags.name,
|
|
895
1142
|
type: flags.type ?? "custom",
|
|
896
1143
|
definition: flags.definition ? JSON.parse(flags.definition) : undefined,
|
|
@@ -904,146 +1151,180 @@ async function runSurvey(client, action, rest) {
|
|
|
904
1151
|
{ key: "status", label: "Status" },
|
|
905
1152
|
]);
|
|
906
1153
|
break;
|
|
907
|
-
case "
|
|
1154
|
+
case "show":
|
|
908
1155
|
if (!rest[0]) {
|
|
909
|
-
console.error("Usage: ascendkit survey
|
|
910
|
-
|
|
1156
|
+
console.error("Usage: ascendkit survey show <survey-id>");
|
|
1157
|
+
return await exitCli(1);
|
|
911
1158
|
}
|
|
912
|
-
|
|
1159
|
+
printSurveySummary(await surveys.getSurvey(client, rest[0]));
|
|
913
1160
|
break;
|
|
914
1161
|
case "update":
|
|
915
1162
|
if (!rest[0]) {
|
|
916
1163
|
console.error("Usage: ascendkit survey update <survey-id> [--flags]");
|
|
917
|
-
|
|
1164
|
+
return await exitCli(1);
|
|
918
1165
|
}
|
|
919
|
-
|
|
1166
|
+
printSurveySummary(await surveys.updateSurvey(client, rest[0], {
|
|
920
1167
|
name: flags.name,
|
|
921
1168
|
status: flags.status,
|
|
922
1169
|
definition: flags.definition ? JSON.parse(flags.definition) : undefined,
|
|
923
1170
|
}));
|
|
924
1171
|
break;
|
|
925
|
-
case "
|
|
1172
|
+
case "remove":
|
|
926
1173
|
if (!rest[0]) {
|
|
927
|
-
console.error("Usage: ascendkit survey
|
|
928
|
-
|
|
1174
|
+
console.error("Usage: ascendkit survey remove <survey-id>");
|
|
1175
|
+
return await exitCli(1);
|
|
929
1176
|
}
|
|
930
|
-
|
|
1177
|
+
await surveys.deleteSurvey(client, rest[0]);
|
|
1178
|
+
console.log(`Removed survey ${rest[0]}.`);
|
|
931
1179
|
break;
|
|
932
1180
|
case "distribute":
|
|
933
1181
|
if (!rest[0] || !flags.users) {
|
|
934
1182
|
console.error("Usage: ascendkit survey distribute <survey-id> --users <usr_id1,usr_id2,...>");
|
|
935
|
-
|
|
1183
|
+
return await exitCli(1);
|
|
936
1184
|
}
|
|
937
1185
|
output(await surveys.distributeSurvey(client, rest[0], flags.users.split(",")));
|
|
938
1186
|
break;
|
|
939
1187
|
case "invitations":
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
1188
|
+
case "invitation":
|
|
1189
|
+
if (normalizedAction === "invitations" || rest[0] === "list") {
|
|
1190
|
+
const surveyId = normalizedAction === "invitations" ? rest[0] : rest[1];
|
|
1191
|
+
if (!surveyId) {
|
|
1192
|
+
console.error("Usage: ascendkit survey invitation list <survey-id>");
|
|
1193
|
+
return await exitCli(1);
|
|
1194
|
+
}
|
|
1195
|
+
output(await surveys.listInvitations(client, surveyId));
|
|
1196
|
+
}
|
|
1197
|
+
else {
|
|
1198
|
+
console.error(`Unknown survey invitation command: ${rest[0]}`);
|
|
1199
|
+
return await exitCli(1);
|
|
943
1200
|
}
|
|
944
|
-
output(await surveys.listInvitations(client, rest[0]));
|
|
945
1201
|
break;
|
|
946
1202
|
case "analytics":
|
|
947
1203
|
if (!rest[0]) {
|
|
948
1204
|
console.error("Usage: ascendkit survey analytics <survey-id>");
|
|
949
|
-
|
|
1205
|
+
return await exitCli(1);
|
|
950
1206
|
}
|
|
951
1207
|
output(await surveys.getAnalytics(client, rest[0]));
|
|
952
1208
|
break;
|
|
953
|
-
case "export-definition":
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
const
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
1209
|
+
case "export-definition":
|
|
1210
|
+
case "import-definition":
|
|
1211
|
+
case "definition": {
|
|
1212
|
+
const definitionAction = normalizedAction === "definition" ? rest[0] :
|
|
1213
|
+
normalizedAction === "export-definition" ? "export" :
|
|
1214
|
+
"import";
|
|
1215
|
+
const surveyId = normalizedAction === "definition" ? rest[1] : rest[0];
|
|
1216
|
+
if (definitionAction === "export") {
|
|
1217
|
+
if (!surveyId) {
|
|
1218
|
+
console.error("Usage: ascendkit survey definition export <survey-id> [--out <file>]");
|
|
1219
|
+
return await exitCli(1);
|
|
1220
|
+
}
|
|
1221
|
+
const survey = await surveys.getSurvey(client, surveyId);
|
|
1222
|
+
const text = `${JSON.stringify(survey.definition ?? {}, null, 2)}\n`;
|
|
1223
|
+
const outPath = flags.out || flags.file;
|
|
1224
|
+
if (outPath) {
|
|
1225
|
+
writeFileSync(outPath, text, "utf8");
|
|
1226
|
+
console.log(`Wrote survey definition to ${outPath}`);
|
|
1227
|
+
}
|
|
1228
|
+
else {
|
|
1229
|
+
process.stdout.write(text);
|
|
1230
|
+
}
|
|
964
1231
|
}
|
|
965
1232
|
else {
|
|
966
|
-
|
|
1233
|
+
if (!surveyId || !(flags.in || flags.file)) {
|
|
1234
|
+
console.error("Usage: ascendkit survey definition import <survey-id> --in <file>");
|
|
1235
|
+
return await exitCli(1);
|
|
1236
|
+
}
|
|
1237
|
+
const inPath = flags.in || flags.file;
|
|
1238
|
+
const raw = readFileSync(inPath, "utf8");
|
|
1239
|
+
const parsed = JSON.parse(raw);
|
|
1240
|
+
const candidate = parsed && typeof parsed === "object" && "definition" in parsed
|
|
1241
|
+
? parsed.definition
|
|
1242
|
+
: parsed;
|
|
1243
|
+
if (!candidate || typeof candidate !== "object" || Array.isArray(candidate)) {
|
|
1244
|
+
console.error("Invalid definition JSON: expected an object or { definition: object }");
|
|
1245
|
+
return await exitCli(1);
|
|
1246
|
+
}
|
|
1247
|
+
printSurveySummary(await surveys.updateSurvey(client, surveyId, {
|
|
1248
|
+
definition: candidate,
|
|
1249
|
+
}));
|
|
967
1250
|
}
|
|
968
1251
|
break;
|
|
969
1252
|
}
|
|
970
|
-
case "import-definition": {
|
|
971
|
-
if (!rest[0] || !(flags.in || flags.file)) {
|
|
972
|
-
console.error("Usage: ascendkit survey import-definition <survey-id> --in <file>");
|
|
973
|
-
process.exit(1);
|
|
974
|
-
}
|
|
975
|
-
const inPath = flags.in || flags.file;
|
|
976
|
-
const raw = readFileSync(inPath, "utf8");
|
|
977
|
-
const parsed = JSON.parse(raw);
|
|
978
|
-
const candidate = parsed && typeof parsed === "object" && "definition" in parsed
|
|
979
|
-
? parsed.definition
|
|
980
|
-
: parsed;
|
|
981
|
-
if (!candidate || typeof candidate !== "object" || Array.isArray(candidate)) {
|
|
982
|
-
console.error("Invalid definition JSON: expected an object or { definition: object }");
|
|
983
|
-
process.exit(1);
|
|
984
|
-
}
|
|
985
|
-
output(await surveys.updateSurvey(client, rest[0], {
|
|
986
|
-
definition: candidate,
|
|
987
|
-
}));
|
|
988
|
-
break;
|
|
989
|
-
}
|
|
990
1253
|
case "list-questions":
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
1254
|
+
case "add-question":
|
|
1255
|
+
case "edit-question":
|
|
1256
|
+
case "remove-question":
|
|
1257
|
+
case "reorder-questions":
|
|
1258
|
+
case "question": {
|
|
1259
|
+
const questionAction = normalizedAction === "question" ? rest[0] :
|
|
1260
|
+
normalizedAction === "list-questions" ? "list" :
|
|
1261
|
+
normalizedAction === "add-question" ? "add" :
|
|
1262
|
+
normalizedAction === "edit-question" ? "update" :
|
|
1263
|
+
normalizedAction === "remove-question" ? "remove" :
|
|
1264
|
+
"reorder";
|
|
1265
|
+
const surveyId = normalizedAction === "question" ? rest[1] : rest[0];
|
|
1266
|
+
const questionName = normalizedAction === "question" ? rest[2] : rest[1];
|
|
1267
|
+
if (!surveyId) {
|
|
1268
|
+
console.error("Usage: ascendkit survey question list <survey-id>");
|
|
1269
|
+
return await exitCli(1);
|
|
1270
|
+
}
|
|
1271
|
+
if (questionAction === "list") {
|
|
1272
|
+
output(await surveys.listQuestions(client, surveyId));
|
|
1273
|
+
}
|
|
1274
|
+
else if (questionAction === "add") {
|
|
1275
|
+
if (!flags.type || !flags.title) {
|
|
1276
|
+
console.error("Usage: ascendkit survey question add <survey-id> --type <type> --title <title> [--name <name>] [--required <true|false>] [--choices <c1,c2,...>] [--position <n>]");
|
|
1277
|
+
return await exitCli(1);
|
|
1278
|
+
}
|
|
1279
|
+
const params = { type: flags.type, title: flags.title };
|
|
1280
|
+
if (flags.name)
|
|
1281
|
+
params.name = flags.name;
|
|
1282
|
+
if (flags.required)
|
|
1283
|
+
params.isRequired = flags.required === "true";
|
|
1284
|
+
if (flags.choices)
|
|
1285
|
+
params.choices = flags.choices.split(",");
|
|
1286
|
+
if (flags.position != null)
|
|
1287
|
+
params.position = Number(flags.position);
|
|
1288
|
+
output(await surveys.addQuestion(client, surveyId, params));
|
|
1289
|
+
}
|
|
1290
|
+
else if (questionAction === "update") {
|
|
1291
|
+
if (!questionName) {
|
|
1292
|
+
console.error("Usage: ascendkit survey question update <survey-id> <question-name> [--title <title>] [--required <true|false>] [--choices <c1,c2,...>]");
|
|
1293
|
+
return await exitCli(1);
|
|
1294
|
+
}
|
|
1295
|
+
const params = {};
|
|
1296
|
+
if (flags.title)
|
|
1297
|
+
params.title = flags.title;
|
|
1298
|
+
if (flags.required)
|
|
1299
|
+
params.isRequired = flags.required === "true";
|
|
1300
|
+
if (flags.choices)
|
|
1301
|
+
params.choices = flags.choices.split(",");
|
|
1302
|
+
output(await surveys.editQuestion(client, surveyId, questionName, params));
|
|
1303
|
+
}
|
|
1304
|
+
else if (questionAction === "remove") {
|
|
1305
|
+
if (!questionName) {
|
|
1306
|
+
console.error("Usage: ascendkit survey question remove <survey-id> <question-name>");
|
|
1307
|
+
return await exitCli(1);
|
|
1308
|
+
}
|
|
1309
|
+
output(await surveys.removeQuestion(client, surveyId, questionName));
|
|
994
1310
|
}
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1311
|
+
else if (questionAction === "reorder") {
|
|
1312
|
+
if (!flags.order) {
|
|
1313
|
+
console.error("Usage: ascendkit survey question reorder <survey-id> --order <name1,name2,...>");
|
|
1314
|
+
return await exitCli(1);
|
|
1315
|
+
}
|
|
1316
|
+
output(await surveys.reorderQuestions(client, surveyId, flags.order.split(",")));
|
|
1001
1317
|
}
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
if (flags.required)
|
|
1006
|
-
params.isRequired = flags.required === "true";
|
|
1007
|
-
if (flags.choices)
|
|
1008
|
-
params.choices = flags.choices.split(",");
|
|
1009
|
-
if (flags.position != null)
|
|
1010
|
-
params.position = Number(flags.position);
|
|
1011
|
-
output(await surveys.addQuestion(client, rest[0], params));
|
|
1012
|
-
break;
|
|
1013
|
-
}
|
|
1014
|
-
case "edit-question": {
|
|
1015
|
-
if (!rest[0] || !rest[1]) {
|
|
1016
|
-
console.error("Usage: ascendkit survey edit-question <survey-id> <question-name> [--title <title>] [--required <true|false>] [--choices <c1,c2,...>]");
|
|
1017
|
-
process.exit(1);
|
|
1318
|
+
else {
|
|
1319
|
+
console.error(`Unknown survey question command: ${questionAction}`);
|
|
1320
|
+
return await exitCli(1);
|
|
1018
1321
|
}
|
|
1019
|
-
const params = {};
|
|
1020
|
-
if (flags.title)
|
|
1021
|
-
params.title = flags.title;
|
|
1022
|
-
if (flags.required)
|
|
1023
|
-
params.isRequired = flags.required === "true";
|
|
1024
|
-
if (flags.choices)
|
|
1025
|
-
params.choices = flags.choices.split(",");
|
|
1026
|
-
output(await surveys.editQuestion(client, rest[0], rest[1], params));
|
|
1027
1322
|
break;
|
|
1028
1323
|
}
|
|
1029
|
-
case "remove-question":
|
|
1030
|
-
if (!rest[0] || !rest[1]) {
|
|
1031
|
-
console.error("Usage: ascendkit survey remove-question <survey-id> <question-name>");
|
|
1032
|
-
process.exit(1);
|
|
1033
|
-
}
|
|
1034
|
-
output(await surveys.removeQuestion(client, rest[0], rest[1]));
|
|
1035
|
-
break;
|
|
1036
|
-
case "reorder-questions":
|
|
1037
|
-
if (!rest[0] || !flags.order) {
|
|
1038
|
-
console.error("Usage: ascendkit survey reorder-questions <survey-id> --order <name1,name2,...>");
|
|
1039
|
-
process.exit(1);
|
|
1040
|
-
}
|
|
1041
|
-
output(await surveys.reorderQuestions(client, rest[0], flags.order.split(",")));
|
|
1042
|
-
break;
|
|
1043
1324
|
default:
|
|
1044
1325
|
console.error(`Unknown survey command: ${action}`);
|
|
1045
1326
|
console.error('Run "ascendkit survey --help" for usage');
|
|
1046
|
-
|
|
1327
|
+
return await exitCli(1);
|
|
1047
1328
|
}
|
|
1048
1329
|
}
|
|
1049
1330
|
function runStatus() {
|
|
@@ -1064,7 +1345,7 @@ function runStatus() {
|
|
|
1064
1345
|
}
|
|
1065
1346
|
else {
|
|
1066
1347
|
console.log(" No environment set. Run: ascendkit set-env <public-key>");
|
|
1067
|
-
console.log(" List environments: ascendkit env list
|
|
1348
|
+
console.log(" List environments: ascendkit project env list <project-id>");
|
|
1068
1349
|
}
|
|
1069
1350
|
console.log();
|
|
1070
1351
|
}
|
|
@@ -1128,14 +1409,42 @@ async function runVerify() {
|
|
|
1128
1409
|
console.log();
|
|
1129
1410
|
}
|
|
1130
1411
|
async function runJourney(client, action, rest) {
|
|
1412
|
+
if (!action) {
|
|
1413
|
+
console.log(HELP_SECTION.journey);
|
|
1414
|
+
return;
|
|
1415
|
+
}
|
|
1416
|
+
if (action === "show")
|
|
1417
|
+
action = "get";
|
|
1418
|
+
if (action === "remove")
|
|
1419
|
+
action = "delete";
|
|
1420
|
+
if (action === "node") {
|
|
1421
|
+
const nodeAction = rest[0];
|
|
1422
|
+
action =
|
|
1423
|
+
nodeAction === "list" ? "list-nodes" :
|
|
1424
|
+
nodeAction === "add" ? "add-node" :
|
|
1425
|
+
nodeAction === "update" ? "edit-node" :
|
|
1426
|
+
nodeAction === "remove" ? "remove-node" :
|
|
1427
|
+
action;
|
|
1428
|
+
rest = rest.slice(1);
|
|
1429
|
+
}
|
|
1430
|
+
if (action === "transition") {
|
|
1431
|
+
const transitionAction = rest[0];
|
|
1432
|
+
action =
|
|
1433
|
+
transitionAction === "list" ? "list-transitions" :
|
|
1434
|
+
transitionAction === "add" ? "add-transition" :
|
|
1435
|
+
transitionAction === "update" ? "edit-transition" :
|
|
1436
|
+
transitionAction === "remove" ? "remove-transition" :
|
|
1437
|
+
action;
|
|
1438
|
+
rest = rest.slice(1);
|
|
1439
|
+
}
|
|
1131
1440
|
const flags = parseFlags(rest);
|
|
1132
1441
|
switch (action) {
|
|
1133
1442
|
case "create":
|
|
1134
1443
|
if (!flags.name || !flags["entry-event"] || !flags["entry-node"]) {
|
|
1135
1444
|
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>]");
|
|
1136
|
-
|
|
1445
|
+
return await exitCli(1);
|
|
1137
1446
|
}
|
|
1138
|
-
|
|
1447
|
+
console.log(formatJourneyWithGuidance((await journeys.createJourney(client, {
|
|
1139
1448
|
name: flags.name,
|
|
1140
1449
|
entryEvent: flags["entry-event"],
|
|
1141
1450
|
entryNode: flags["entry-node"],
|
|
@@ -1144,7 +1453,7 @@ async function runJourney(client, action, rest) {
|
|
|
1144
1453
|
description: flags.description,
|
|
1145
1454
|
entryConditions: flags["entry-conditions"] ? JSON.parse(flags["entry-conditions"]) : undefined,
|
|
1146
1455
|
reEntryPolicy: flags["re-entry-policy"],
|
|
1147
|
-
}));
|
|
1456
|
+
}))));
|
|
1148
1457
|
break;
|
|
1149
1458
|
case "list": {
|
|
1150
1459
|
const opts = {};
|
|
@@ -1162,17 +1471,17 @@ async function runJourney(client, action, rest) {
|
|
|
1162
1471
|
}
|
|
1163
1472
|
case "get":
|
|
1164
1473
|
if (!rest[0]) {
|
|
1165
|
-
console.error("Usage: ascendkit journey
|
|
1166
|
-
|
|
1474
|
+
console.error("Usage: ascendkit journey show <journey-id>");
|
|
1475
|
+
return await exitCli(1);
|
|
1167
1476
|
}
|
|
1168
|
-
|
|
1477
|
+
console.log(formatJourneyWithGuidance(await journeys.getJourney(client, rest[0])));
|
|
1169
1478
|
break;
|
|
1170
1479
|
case "update":
|
|
1171
1480
|
if (!rest[0]) {
|
|
1172
1481
|
console.error("Usage: ascendkit journey update <journey-id> [--flags]");
|
|
1173
|
-
|
|
1482
|
+
return await exitCli(1);
|
|
1174
1483
|
}
|
|
1175
|
-
|
|
1484
|
+
console.log(formatJourneyWithGuidance((await journeys.updateJourney(client, rest[0], {
|
|
1176
1485
|
name: flags.name,
|
|
1177
1486
|
description: flags.description,
|
|
1178
1487
|
entryEvent: flags["entry-event"],
|
|
@@ -1181,54 +1490,62 @@ async function runJourney(client, action, rest) {
|
|
|
1181
1490
|
reEntryPolicy: flags["re-entry-policy"],
|
|
1182
1491
|
nodes: flags.nodes ? JSON.parse(flags.nodes) : undefined,
|
|
1183
1492
|
transitions: flags.transitions ? JSON.parse(flags.transitions) : undefined,
|
|
1184
|
-
}));
|
|
1493
|
+
}))));
|
|
1185
1494
|
break;
|
|
1186
1495
|
case "delete":
|
|
1187
1496
|
if (!rest[0]) {
|
|
1188
|
-
console.error("Usage: ascendkit journey
|
|
1189
|
-
|
|
1497
|
+
console.error("Usage: ascendkit journey remove <journey-id>");
|
|
1498
|
+
return await exitCli(1);
|
|
1190
1499
|
}
|
|
1191
|
-
|
|
1500
|
+
await journeys.deleteJourney(client, rest[0]);
|
|
1501
|
+
console.log(`Deleted journey ${rest[0]}.`);
|
|
1192
1502
|
break;
|
|
1193
1503
|
case "activate":
|
|
1194
1504
|
if (!rest[0]) {
|
|
1195
1505
|
console.error("Usage: ascendkit journey activate <journey-id>");
|
|
1196
|
-
|
|
1506
|
+
return await exitCli(1);
|
|
1197
1507
|
}
|
|
1198
|
-
|
|
1508
|
+
console.log(formatJourneyWithGuidance(await journeys.activateJourney(client, rest[0])));
|
|
1199
1509
|
break;
|
|
1200
1510
|
case "pause":
|
|
1201
1511
|
if (!rest[0]) {
|
|
1202
1512
|
console.error("Usage: ascendkit journey pause <journey-id>");
|
|
1203
|
-
|
|
1513
|
+
return await exitCli(1);
|
|
1204
1514
|
}
|
|
1205
|
-
|
|
1515
|
+
console.log(formatJourneyWithGuidance(await journeys.pauseJourney(client, rest[0])));
|
|
1516
|
+
break;
|
|
1517
|
+
case "resume":
|
|
1518
|
+
if (!rest[0]) {
|
|
1519
|
+
console.error("Usage: ascendkit journey resume <journey-id>");
|
|
1520
|
+
return await exitCli(1);
|
|
1521
|
+
}
|
|
1522
|
+
console.log(formatJourneyWithGuidance(await journeys.resumeJourney(client, rest[0])));
|
|
1206
1523
|
break;
|
|
1207
1524
|
case "archive":
|
|
1208
1525
|
if (!rest[0]) {
|
|
1209
1526
|
console.error("Usage: ascendkit journey archive <journey-id>");
|
|
1210
|
-
|
|
1527
|
+
return await exitCli(1);
|
|
1211
1528
|
}
|
|
1212
|
-
|
|
1529
|
+
console.log(formatJourneyWithGuidance(await journeys.archiveJourney(client, rest[0])));
|
|
1213
1530
|
break;
|
|
1214
1531
|
case "analytics":
|
|
1215
1532
|
if (!rest[0]) {
|
|
1216
1533
|
console.error("Usage: ascendkit journey analytics <journey-id>");
|
|
1217
|
-
|
|
1534
|
+
return await exitCli(1);
|
|
1218
1535
|
}
|
|
1219
|
-
|
|
1536
|
+
console.log(formatJourneyAnalytics(await journeys.getJourneyAnalytics(client, rest[0])));
|
|
1220
1537
|
break;
|
|
1221
1538
|
case "list-nodes":
|
|
1222
1539
|
if (!rest[0]) {
|
|
1223
|
-
console.error("Usage: ascendkit journey list
|
|
1224
|
-
|
|
1540
|
+
console.error("Usage: ascendkit journey node list <journey-id>");
|
|
1541
|
+
return await exitCli(1);
|
|
1225
1542
|
}
|
|
1226
|
-
|
|
1543
|
+
console.log(formatNodeList(await journeys.listNodes(client, rest[0])));
|
|
1227
1544
|
break;
|
|
1228
1545
|
case "add-node": {
|
|
1229
1546
|
if (!rest[0] || !flags.name) {
|
|
1230
|
-
console.error("Usage: ascendkit journey
|
|
1231
|
-
|
|
1547
|
+
console.error("Usage: ascendkit journey node add <journey-id> --name <node-name> [--action <json>] [--email-id <email>] [--terminal <true|false>]");
|
|
1548
|
+
return await exitCli(1);
|
|
1232
1549
|
}
|
|
1233
1550
|
const params = { name: flags.name };
|
|
1234
1551
|
if (flags.action)
|
|
@@ -1236,19 +1553,19 @@ async function runJourney(client, action, rest) {
|
|
|
1236
1553
|
if (flags["email-id"]) {
|
|
1237
1554
|
if (!params.action || params.action.type !== "send_email") {
|
|
1238
1555
|
console.error("--email-id requires a send_email action (use --action '{\"type\": \"send_email\", \"templateSlug\": \"...\"}')");
|
|
1239
|
-
|
|
1556
|
+
return await exitCli(1);
|
|
1240
1557
|
}
|
|
1241
1558
|
params.action.fromIdentityEmail = flags["email-id"];
|
|
1242
1559
|
}
|
|
1243
1560
|
if (flags.terminal)
|
|
1244
1561
|
params.terminal = flags.terminal === "true";
|
|
1245
|
-
|
|
1562
|
+
console.log(formatSingleNode(await journeys.addNode(client, rest[0], params), "Added", flags.name));
|
|
1246
1563
|
break;
|
|
1247
1564
|
}
|
|
1248
1565
|
case "edit-node": {
|
|
1249
1566
|
if (!rest[0] || !rest[1]) {
|
|
1250
|
-
console.error("Usage: ascendkit journey
|
|
1251
|
-
|
|
1567
|
+
console.error("Usage: ascendkit journey node update <journey-id> <node-name> [--action <json>] [--email-id <email>] [--terminal <true|false>]");
|
|
1568
|
+
return await exitCli(1);
|
|
1252
1569
|
}
|
|
1253
1570
|
const params = {};
|
|
1254
1571
|
if (flags.action)
|
|
@@ -1259,43 +1576,43 @@ async function runJourney(client, action, rest) {
|
|
|
1259
1576
|
const current = nodesData.nodes?.find((n) => n.name === rest[1]);
|
|
1260
1577
|
if (!current?.action || current.action.type !== "send_email") {
|
|
1261
1578
|
console.error("--email-id can only be set on send_email nodes");
|
|
1262
|
-
|
|
1579
|
+
return await exitCli(1);
|
|
1263
1580
|
}
|
|
1264
1581
|
params.action = current.action;
|
|
1265
1582
|
}
|
|
1266
1583
|
else if (params.action.type !== "send_email") {
|
|
1267
1584
|
console.error("--email-id requires a send_email action");
|
|
1268
|
-
|
|
1585
|
+
return await exitCli(1);
|
|
1269
1586
|
}
|
|
1270
1587
|
params.action.fromIdentityEmail = flags["email-id"];
|
|
1271
1588
|
}
|
|
1272
1589
|
if (flags.terminal)
|
|
1273
1590
|
params.terminal = flags.terminal === "true";
|
|
1274
|
-
|
|
1591
|
+
console.log(formatSingleNode(await journeys.editNode(client, rest[0], rest[1], params), "Updated", rest[1]));
|
|
1275
1592
|
break;
|
|
1276
1593
|
}
|
|
1277
1594
|
case "remove-node":
|
|
1278
1595
|
if (!rest[0] || !rest[1]) {
|
|
1279
|
-
console.error("Usage: ascendkit journey
|
|
1280
|
-
|
|
1596
|
+
console.error("Usage: ascendkit journey node remove <journey-id> <node-name>");
|
|
1597
|
+
return await exitCli(1);
|
|
1281
1598
|
}
|
|
1282
|
-
|
|
1599
|
+
console.log(formatSingleNode(await journeys.removeNode(client, rest[0], rest[1]), "Removed", rest[1]));
|
|
1283
1600
|
break;
|
|
1284
1601
|
case "list-transitions": {
|
|
1285
1602
|
if (!rest[0]) {
|
|
1286
|
-
console.error("Usage: ascendkit journey list
|
|
1287
|
-
|
|
1603
|
+
console.error("Usage: ascendkit journey transition list <journey-id> [--from <node-name>] [--to <node-name>]");
|
|
1604
|
+
return await exitCli(1);
|
|
1288
1605
|
}
|
|
1289
|
-
|
|
1606
|
+
console.log(formatTransitionList(await journeys.listTransitions(client, rest[0], {
|
|
1290
1607
|
from_node: flags.from,
|
|
1291
1608
|
to_node: flags.to,
|
|
1292
|
-
}));
|
|
1609
|
+
})));
|
|
1293
1610
|
break;
|
|
1294
1611
|
}
|
|
1295
1612
|
case "add-transition": {
|
|
1296
1613
|
if (!rest[0] || !flags.from || !flags.to || !(flags.on || flags.after || flags.trigger)) {
|
|
1297
|
-
console.error("Usage: ascendkit journey
|
|
1298
|
-
|
|
1614
|
+
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>]");
|
|
1615
|
+
return await exitCli(1);
|
|
1299
1616
|
}
|
|
1300
1617
|
let trigger;
|
|
1301
1618
|
if (flags.on) {
|
|
@@ -1316,13 +1633,14 @@ async function runJourney(client, action, rest) {
|
|
|
1316
1633
|
params.priority = Number(flags.priority);
|
|
1317
1634
|
if (flags.name)
|
|
1318
1635
|
params.name = flags.name;
|
|
1319
|
-
|
|
1636
|
+
const transitionName = flags.name || `${flags.from}-to-${flags.to}`;
|
|
1637
|
+
console.log(formatSingleTransition(await journeys.addTransition(client, rest[0], params), "Added", transitionName));
|
|
1320
1638
|
break;
|
|
1321
1639
|
}
|
|
1322
1640
|
case "edit-transition": {
|
|
1323
1641
|
if (!rest[0] || !rest[1]) {
|
|
1324
|
-
console.error("Usage: ascendkit journey
|
|
1325
|
-
|
|
1642
|
+
console.error("Usage: ascendkit journey transition update <journey-id> <transition-name> [--on <event>] [--after <delay>] [--trigger <json>] [--priority <n>]");
|
|
1643
|
+
return await exitCli(1);
|
|
1326
1644
|
}
|
|
1327
1645
|
const params = {};
|
|
1328
1646
|
if (flags.on) {
|
|
@@ -1336,19 +1654,19 @@ async function runJourney(client, action, rest) {
|
|
|
1336
1654
|
}
|
|
1337
1655
|
if (flags.priority != null)
|
|
1338
1656
|
params.priority = Number(flags.priority);
|
|
1339
|
-
|
|
1657
|
+
console.log(formatSingleTransition(await journeys.editTransition(client, rest[0], rest[1], params), "Updated", rest[1]));
|
|
1340
1658
|
break;
|
|
1341
1659
|
}
|
|
1342
1660
|
case "remove-transition":
|
|
1343
1661
|
if (!rest[0] || !rest[1]) {
|
|
1344
|
-
console.error("Usage: ascendkit journey
|
|
1345
|
-
|
|
1662
|
+
console.error("Usage: ascendkit journey transition remove <journey-id> <transition-name>");
|
|
1663
|
+
return await exitCli(1);
|
|
1346
1664
|
}
|
|
1347
|
-
|
|
1665
|
+
console.log(formatSingleTransition(await journeys.removeTransition(client, rest[0], rest[1]), "Removed", rest[1]));
|
|
1348
1666
|
break;
|
|
1349
1667
|
default:
|
|
1350
1668
|
console.error(`Unknown journey command: ${action}`);
|
|
1351
|
-
|
|
1669
|
+
return await exitCli(1);
|
|
1352
1670
|
}
|
|
1353
1671
|
}
|
|
1354
1672
|
async function runWebhook(client, action, rest) {
|
|
@@ -1357,7 +1675,7 @@ async function runWebhook(client, action, rest) {
|
|
|
1357
1675
|
case "create":
|
|
1358
1676
|
if (!flags.url) {
|
|
1359
1677
|
console.error("Usage: ascendkit webhook create --url <url> [--events <e1,e2,...>]");
|
|
1360
|
-
|
|
1678
|
+
return await exitCli(1);
|
|
1361
1679
|
}
|
|
1362
1680
|
output(await webhooks.createWebhook(client, {
|
|
1363
1681
|
url: flags.url,
|
|
@@ -1375,14 +1693,14 @@ async function runWebhook(client, action, rest) {
|
|
|
1375
1693
|
case "get":
|
|
1376
1694
|
if (!rest[0]) {
|
|
1377
1695
|
console.error("Usage: ascendkit webhook get <webhook-id>");
|
|
1378
|
-
|
|
1696
|
+
return await exitCli(1);
|
|
1379
1697
|
}
|
|
1380
1698
|
output(await webhooks.getWebhook(client, rest[0]));
|
|
1381
1699
|
break;
|
|
1382
1700
|
case "update": {
|
|
1383
1701
|
if (!rest[0]) {
|
|
1384
1702
|
console.error("Usage: ascendkit webhook update <webhook-id> [--flags]");
|
|
1385
|
-
|
|
1703
|
+
return await exitCli(1);
|
|
1386
1704
|
}
|
|
1387
1705
|
const updated = await webhooks.updateWebhook(client, rest[0], {
|
|
1388
1706
|
url: flags.url,
|
|
@@ -1398,21 +1716,21 @@ async function runWebhook(client, action, rest) {
|
|
|
1398
1716
|
case "delete":
|
|
1399
1717
|
if (!rest[0]) {
|
|
1400
1718
|
console.error("Usage: ascendkit webhook delete <webhook-id>");
|
|
1401
|
-
|
|
1719
|
+
return await exitCli(1);
|
|
1402
1720
|
}
|
|
1403
1721
|
output(await webhooks.deleteWebhook(client, rest[0]));
|
|
1404
1722
|
break;
|
|
1405
1723
|
case "test":
|
|
1406
1724
|
if (!rest[0]) {
|
|
1407
1725
|
console.error("Usage: ascendkit webhook test <webhook-id> [--event <event-type>]");
|
|
1408
|
-
|
|
1726
|
+
return await exitCli(1);
|
|
1409
1727
|
}
|
|
1410
1728
|
output(await webhooks.testWebhook(client, rest[0], flags.event));
|
|
1411
1729
|
break;
|
|
1412
1730
|
default:
|
|
1413
1731
|
console.error(`Unknown webhook command: ${action}`);
|
|
1414
1732
|
console.error('Run "ascendkit webhook --help" for usage');
|
|
1415
|
-
|
|
1733
|
+
return await exitCli(1);
|
|
1416
1734
|
}
|
|
1417
1735
|
}
|
|
1418
1736
|
async function runCampaign(client, action, rest) {
|
|
@@ -1422,10 +1740,10 @@ async function runCampaign(client, action, rest) {
|
|
|
1422
1740
|
return;
|
|
1423
1741
|
}
|
|
1424
1742
|
switch (action) {
|
|
1425
|
-
case "create":
|
|
1743
|
+
case "create": {
|
|
1426
1744
|
if (!flags.name || !flags.template || !flags.audience) {
|
|
1427
1745
|
console.error("Usage: ascendkit campaign create --name <name> --template <template-id> --audience <json> [--scheduled-at <datetime>]");
|
|
1428
|
-
|
|
1746
|
+
return await exitCli(1);
|
|
1429
1747
|
}
|
|
1430
1748
|
let createFilter;
|
|
1431
1749
|
try {
|
|
@@ -1433,7 +1751,7 @@ async function runCampaign(client, action, rest) {
|
|
|
1433
1751
|
}
|
|
1434
1752
|
catch {
|
|
1435
1753
|
console.error("Invalid JSON for --audience flag. Provide a valid JSON object.");
|
|
1436
|
-
|
|
1754
|
+
return await exitCli(1);
|
|
1437
1755
|
}
|
|
1438
1756
|
output(await campaigns.createCampaign(client, {
|
|
1439
1757
|
name: flags.name,
|
|
@@ -1442,6 +1760,7 @@ async function runCampaign(client, action, rest) {
|
|
|
1442
1760
|
scheduledAt: flags["scheduled-at"],
|
|
1443
1761
|
}));
|
|
1444
1762
|
break;
|
|
1763
|
+
}
|
|
1445
1764
|
case "list": {
|
|
1446
1765
|
const items = await campaigns.listCampaigns(client, flags.status);
|
|
1447
1766
|
table(items, [
|
|
@@ -1457,14 +1776,14 @@ async function runCampaign(client, action, rest) {
|
|
|
1457
1776
|
case "get":
|
|
1458
1777
|
if (!rest[0]) {
|
|
1459
1778
|
console.error("Usage: ascendkit campaign show <campaign-id>");
|
|
1460
|
-
|
|
1779
|
+
return await exitCli(1);
|
|
1461
1780
|
}
|
|
1462
1781
|
output(await campaigns.getCampaign(client, rest[0]));
|
|
1463
1782
|
break;
|
|
1464
|
-
case "update":
|
|
1783
|
+
case "update": {
|
|
1465
1784
|
if (!rest[0]) {
|
|
1466
1785
|
console.error("Usage: ascendkit campaign update <campaign-id> [--flags]");
|
|
1467
|
-
|
|
1786
|
+
return await exitCli(1);
|
|
1468
1787
|
}
|
|
1469
1788
|
let updateFilter;
|
|
1470
1789
|
if (flags.audience) {
|
|
@@ -1473,7 +1792,7 @@ async function runCampaign(client, action, rest) {
|
|
|
1473
1792
|
}
|
|
1474
1793
|
catch {
|
|
1475
1794
|
console.error("Invalid JSON for --audience flag. Provide a valid JSON object.");
|
|
1476
|
-
|
|
1795
|
+
return await exitCli(1);
|
|
1477
1796
|
}
|
|
1478
1797
|
}
|
|
1479
1798
|
output(await campaigns.updateCampaign(client, rest[0], {
|
|
@@ -1483,99 +1802,82 @@ async function runCampaign(client, action, rest) {
|
|
|
1483
1802
|
scheduledAt: flags["scheduled-at"],
|
|
1484
1803
|
}));
|
|
1485
1804
|
break;
|
|
1486
|
-
|
|
1805
|
+
}
|
|
1806
|
+
case "preview": {
|
|
1487
1807
|
if (!rest[0]) {
|
|
1488
1808
|
console.error("Usage: ascendkit campaign preview <campaign-id>");
|
|
1489
|
-
|
|
1809
|
+
return await exitCli(1);
|
|
1490
1810
|
}
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
process.exit(1);
|
|
1496
|
-
}
|
|
1497
|
-
output(await campaigns.previewAudience(client, detail.audienceFilter));
|
|
1811
|
+
const detail = await campaigns.getCampaign(client, rest[0]);
|
|
1812
|
+
if (!detail?.audienceFilter) {
|
|
1813
|
+
console.error("Campaign has no audience filter set.");
|
|
1814
|
+
return await exitCli(1);
|
|
1498
1815
|
}
|
|
1816
|
+
output(await campaigns.previewAudience(client, detail.audienceFilter));
|
|
1499
1817
|
break;
|
|
1818
|
+
}
|
|
1500
1819
|
case "schedule":
|
|
1501
1820
|
if (!rest[0] || !flags.at) {
|
|
1502
1821
|
console.error("Usage: ascendkit campaign schedule <campaign-id> --at <datetime>");
|
|
1503
|
-
|
|
1822
|
+
return await exitCli(1);
|
|
1504
1823
|
}
|
|
1505
1824
|
output(await campaigns.updateCampaign(client, rest[0], { scheduledAt: flags.at }));
|
|
1506
1825
|
break;
|
|
1507
1826
|
case "cancel":
|
|
1508
1827
|
if (!rest[0]) {
|
|
1509
1828
|
console.error("Usage: ascendkit campaign cancel <campaign-id>");
|
|
1510
|
-
|
|
1829
|
+
return await exitCli(1);
|
|
1511
1830
|
}
|
|
1512
1831
|
output(await campaigns.deleteCampaign(client, rest[0]));
|
|
1513
1832
|
break;
|
|
1514
1833
|
case "analytics":
|
|
1515
1834
|
if (!rest[0]) {
|
|
1516
1835
|
console.error("Usage: ascendkit campaign analytics <campaign-id>");
|
|
1517
|
-
|
|
1836
|
+
return await exitCli(1);
|
|
1518
1837
|
}
|
|
1519
1838
|
output(await campaigns.getCampaignAnalytics(client, rest[0]));
|
|
1520
1839
|
break;
|
|
1521
1840
|
default:
|
|
1522
1841
|
console.error(`Unknown campaign command: ${action}`);
|
|
1523
1842
|
console.error('Run "ascendkit campaign --help" for usage');
|
|
1524
|
-
|
|
1843
|
+
return await exitCli(1);
|
|
1525
1844
|
}
|
|
1526
1845
|
}
|
|
1527
1846
|
async function runEmail(client, action, rest) {
|
|
1528
1847
|
const flags = parseFlags(rest);
|
|
1848
|
+
if (!action) {
|
|
1849
|
+
console.log(HELP_SECTION["email-identity"]);
|
|
1850
|
+
return;
|
|
1851
|
+
}
|
|
1529
1852
|
switch (action) {
|
|
1530
|
-
case "
|
|
1531
|
-
const
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
}
|
|
1535
|
-
case "use-default": {
|
|
1536
|
-
const s = await email.useDefaultIdentity(client);
|
|
1537
|
-
printIdentityStatus(s);
|
|
1538
|
-
break;
|
|
1539
|
-
}
|
|
1540
|
-
case "use-custom": {
|
|
1541
|
-
if (!rest[0]) {
|
|
1542
|
-
console.error("Usage: ascendkit email use-custom <domain> [--from-email <email>] [--from-name <name>]");
|
|
1543
|
-
process.exit(1);
|
|
1544
|
-
}
|
|
1545
|
-
const s = await email.useCustomIdentity(client, rest[0], {
|
|
1546
|
-
fromEmail: flags["from-email"],
|
|
1547
|
-
fromName: flags["from-name"],
|
|
1548
|
-
});
|
|
1549
|
-
await printEmailSetup(s);
|
|
1550
|
-
break;
|
|
1551
|
-
}
|
|
1552
|
-
case "settings":
|
|
1553
|
-
if (rest[0] === "update") {
|
|
1554
|
-
const params = {};
|
|
1555
|
-
if (flags["from-email"])
|
|
1556
|
-
params.fromEmail = flags["from-email"];
|
|
1557
|
-
if (flags["from-name"])
|
|
1558
|
-
params.fromName = flags["from-name"];
|
|
1559
|
-
output(await email.updateSettings(client, params));
|
|
1853
|
+
case "settings": {
|
|
1854
|
+
const settings = await email.getSettings(client);
|
|
1855
|
+
if (flags.json === "true") {
|
|
1856
|
+
output(settings);
|
|
1560
1857
|
}
|
|
1561
1858
|
else {
|
|
1562
|
-
|
|
1859
|
+
printEmailSettingsSummary(settings);
|
|
1563
1860
|
}
|
|
1564
1861
|
break;
|
|
1862
|
+
}
|
|
1565
1863
|
case "setup-domain":
|
|
1566
1864
|
if (!rest[0]) {
|
|
1567
|
-
console.error("Usage: ascendkit email setup-domain <domain>");
|
|
1568
|
-
|
|
1865
|
+
console.error("Usage: ascendkit email-identity setup-domain <domain>");
|
|
1866
|
+
return await exitCli(1);
|
|
1569
1867
|
}
|
|
1570
1868
|
await printEmailSetup(await email.setupDomain(client, rest[0]));
|
|
1571
1869
|
break;
|
|
1572
|
-
case "
|
|
1870
|
+
case "status": {
|
|
1573
1871
|
const interval = Math.max(5, Number.parseInt(flags.interval || "15", 10) || 15);
|
|
1574
1872
|
if (flags.watch === "true") {
|
|
1575
1873
|
await watchDomainStatus(client, interval);
|
|
1576
1874
|
}
|
|
1577
1875
|
else {
|
|
1578
|
-
|
|
1876
|
+
const settings = await email.getSettings(client);
|
|
1877
|
+
const domainStatus = await email.checkDomainStatus(client);
|
|
1878
|
+
const dnsCheck = settings.domain ? await email.checkDnsRecords(client) : null;
|
|
1879
|
+
const identities = await email.listIdentities(client);
|
|
1880
|
+
printEmailStatusSummary(settings, domainStatus, dnsCheck, identities.identities ?? []);
|
|
1579
1881
|
}
|
|
1580
1882
|
break;
|
|
1581
1883
|
}
|
|
@@ -1584,7 +1886,7 @@ async function runEmail(client, action, rest) {
|
|
|
1584
1886
|
const url = provider?.portalUrl;
|
|
1585
1887
|
if (!url) {
|
|
1586
1888
|
console.error("Could not determine DNS provider URL for this domain.");
|
|
1587
|
-
|
|
1889
|
+
return await exitCli(1);
|
|
1588
1890
|
}
|
|
1589
1891
|
console.log(url);
|
|
1590
1892
|
if (flags.open === "true") {
|
|
@@ -1595,14 +1897,74 @@ async function runEmail(client, action, rest) {
|
|
|
1595
1897
|
case "remove-domain":
|
|
1596
1898
|
output(await email.removeDomain(client));
|
|
1597
1899
|
break;
|
|
1900
|
+
case "list": {
|
|
1901
|
+
const result = await email.listIdentities(client);
|
|
1902
|
+
printEmailIdentities(result.identities ?? []);
|
|
1903
|
+
break;
|
|
1904
|
+
}
|
|
1905
|
+
case "add": {
|
|
1906
|
+
const identityEmail = rest[0];
|
|
1907
|
+
if (!identityEmail) {
|
|
1908
|
+
console.error("Usage: ascendkit email-identity add <email> [--display-name <name>]");
|
|
1909
|
+
return await exitCli(1);
|
|
1910
|
+
}
|
|
1911
|
+
output(await email.createIdentity(client, {
|
|
1912
|
+
email: identityEmail,
|
|
1913
|
+
displayName: flags["display-name"],
|
|
1914
|
+
}));
|
|
1915
|
+
break;
|
|
1916
|
+
}
|
|
1917
|
+
case "resend": {
|
|
1918
|
+
const identityEmail = rest[0];
|
|
1919
|
+
if (!identityEmail) {
|
|
1920
|
+
console.error("Usage: ascendkit email-identity resend <email>");
|
|
1921
|
+
return await exitCli(1);
|
|
1922
|
+
}
|
|
1923
|
+
output(await email.resendIdentityVerification(client, identityEmail));
|
|
1924
|
+
break;
|
|
1925
|
+
}
|
|
1926
|
+
case "set-default": {
|
|
1927
|
+
const identityEmail = rest[0];
|
|
1928
|
+
if (!identityEmail) {
|
|
1929
|
+
console.error("Usage: ascendkit email-identity set-default <email> [--display-name <name>]");
|
|
1930
|
+
return await exitCli(1);
|
|
1931
|
+
}
|
|
1932
|
+
output(await email.setDefaultIdentity(client, {
|
|
1933
|
+
email: identityEmail,
|
|
1934
|
+
displayName: flags["display-name"],
|
|
1935
|
+
}));
|
|
1936
|
+
break;
|
|
1937
|
+
}
|
|
1938
|
+
case "remove":
|
|
1939
|
+
case "delete": {
|
|
1940
|
+
const identityEmail = rest[0];
|
|
1941
|
+
if (!identityEmail) {
|
|
1942
|
+
console.error("Usage: ascendkit email-identity remove <email>");
|
|
1943
|
+
return await exitCli(1);
|
|
1944
|
+
}
|
|
1945
|
+
output(await email.removeIdentity(client, identityEmail));
|
|
1946
|
+
break;
|
|
1947
|
+
}
|
|
1948
|
+
case "test": {
|
|
1949
|
+
const identityEmail = rest[0];
|
|
1950
|
+
if (!identityEmail || !flags.to) {
|
|
1951
|
+
console.error("Usage: ascendkit email-identity test <email> --to <recipient>");
|
|
1952
|
+
return await exitCli(1);
|
|
1953
|
+
}
|
|
1954
|
+
output(await email.sendTestEmail(client, {
|
|
1955
|
+
to: flags.to,
|
|
1956
|
+
fromIdentityEmail: identityEmail,
|
|
1957
|
+
}));
|
|
1958
|
+
break;
|
|
1959
|
+
}
|
|
1598
1960
|
default:
|
|
1599
|
-
console.error(`Unknown email command: ${action}`);
|
|
1600
|
-
|
|
1961
|
+
console.error(`Unknown email-identity command: ${action}`);
|
|
1962
|
+
return await exitCli(1);
|
|
1601
1963
|
}
|
|
1602
1964
|
}
|
|
1603
1965
|
run().catch((err) => {
|
|
1604
1966
|
console.error(err.message);
|
|
1605
|
-
|
|
1967
|
+
exitCli(1, err instanceof Error ? err : new Error(String(err)));
|
|
1606
1968
|
});
|
|
1607
1969
|
async function printEmailSetup(settings) {
|
|
1608
1970
|
output(settings);
|
|
@@ -1625,28 +1987,61 @@ async function printEmailSetup(settings) {
|
|
|
1625
1987
|
}
|
|
1626
1988
|
}
|
|
1627
1989
|
}
|
|
1628
|
-
function
|
|
1629
|
-
const
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
console.log("Next step: add DNS records and run `ascendkit email domain-status --watch`");
|
|
1990
|
+
function printEmailSettingsSummary(settings) {
|
|
1991
|
+
const identities = Array.isArray(settings.identities) ? settings.identities : [];
|
|
1992
|
+
if (!settings.domain) {
|
|
1993
|
+
console.log("Sender: AscendKit default");
|
|
1994
|
+
console.log(`From: ${settings.fromEmail || "noreply@ascendkit.dev"}`);
|
|
1995
|
+
console.log(`Display name: ${settings.fromName || "not set"}`);
|
|
1996
|
+
console.log("Domain: not configured");
|
|
1997
|
+
if (identities.length > 0) {
|
|
1998
|
+
console.log("");
|
|
1999
|
+
printEmailIdentities(identities);
|
|
1639
2000
|
}
|
|
2001
|
+
return;
|
|
1640
2002
|
}
|
|
1641
|
-
|
|
1642
|
-
|
|
2003
|
+
console.log(`Domain: ${settings.domain} (${settings.verificationStatus || "unknown"})`);
|
|
2004
|
+
console.log("");
|
|
2005
|
+
printEmailIdentities(identities);
|
|
2006
|
+
}
|
|
2007
|
+
function printEmailStatusSummary(settings, domainStatus, dnsCheck, identities) {
|
|
2008
|
+
if (!settings.domain) {
|
|
2009
|
+
console.log("Domain: not configured");
|
|
2010
|
+
console.log("Sender: AscendKit default");
|
|
2011
|
+
if (identities.length > 0) {
|
|
2012
|
+
console.log("");
|
|
2013
|
+
printEmailIdentities(identities);
|
|
2014
|
+
}
|
|
2015
|
+
return;
|
|
2016
|
+
}
|
|
2017
|
+
console.log(`Domain: ${settings.domain} (${domainStatus.status || settings.verificationStatus || "unknown"})`);
|
|
2018
|
+
if (dnsCheck?.summary) {
|
|
2019
|
+
console.log(`DNS: ${dnsCheck.summary.found}/${dnsCheck.summary.total} verified`);
|
|
1643
2020
|
}
|
|
2021
|
+
console.log("");
|
|
2022
|
+
printEmailIdentities(identities);
|
|
2023
|
+
}
|
|
2024
|
+
function printEmailIdentities(identities) {
|
|
2025
|
+
table(identities.map((identity) => ({
|
|
2026
|
+
email: identity.email,
|
|
2027
|
+
displayName: identity.displayName || "—",
|
|
2028
|
+
verificationStatus: identity.verificationStatus,
|
|
2029
|
+
isDefault: identity.isDefault ? "yes" : "",
|
|
2030
|
+
})), [
|
|
2031
|
+
{ key: "email", label: "Email", width: 36 },
|
|
2032
|
+
{ key: "displayName", label: "Display Name", width: 24 },
|
|
2033
|
+
{ key: "verificationStatus", label: "Status", width: 12 },
|
|
2034
|
+
{ key: "isDefault", label: "Default", width: 7 },
|
|
2035
|
+
]);
|
|
1644
2036
|
}
|
|
1645
2037
|
async function watchDomainStatus(client, intervalSeconds) {
|
|
1646
2038
|
while (true) {
|
|
1647
|
-
const
|
|
1648
|
-
|
|
1649
|
-
|
|
2039
|
+
const settings = await email.getSettings(client);
|
|
2040
|
+
const domainStatus = await email.checkDomainStatus(client);
|
|
2041
|
+
const dnsCheck = settings.domain ? await email.checkDnsRecords(client) : null;
|
|
2042
|
+
const identities = await email.listIdentities(client);
|
|
2043
|
+
printEmailStatusSummary(settings, domainStatus, dnsCheck, identities.identities ?? []);
|
|
2044
|
+
if (domainStatus?.status === "verified" || domainStatus?.status === "failed" || domainStatus?.status === "none") {
|
|
1650
2045
|
return;
|
|
1651
2046
|
}
|
|
1652
2047
|
await new Promise((resolve) => setTimeout(resolve, intervalSeconds * 1000));
|