@ascendkit/cli 0.2.6 → 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/cli.js CHANGED
@@ -14,6 +14,7 @@ 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 { formatJourneyAnalytics, formatJourneyWithGuidance, formatNodeList, formatSingleNode, formatSingleTransition, formatTransitionList, } from "./utils/journey-format.js";
17
18
  const require = createRequire(import.meta.url);
18
19
  const { version: CLI_VERSION } = require("../package.json");
19
20
  const HELP = `ascendkit v${CLI_VERSION} - AscendKit CLI
@@ -29,17 +30,18 @@ Getting Started:
29
30
 
30
31
  Services:
31
32
  auth Authentication, providers, OAuth, users
32
- templates Email templates and versioning
33
+ template Email templates and versioning
33
34
  survey Surveys, questions, distribution, analytics
34
35
  journey Lifecycle journeys, nodes, transitions
35
- email Email settings, domain verification, DNS
36
+ email-identity Email domains, sender identities, and DNS
37
+ keystore Environment runtime key-value settings
36
38
  webhook Webhook endpoints and testing
37
39
  campaign Email campaigns, scheduling, analytics
38
40
  import Import users from external auth providers
39
41
 
40
42
  Project Management:
41
- projects List and create projects
42
- env List, switch, update, and promote environments
43
+ project Projects and environment selection
44
+ environment Active environment operations
43
45
  verify Check all services in the active environment
44
46
 
45
47
  Run "ascendkit help <section>" for detailed command usage.
@@ -48,70 +50,79 @@ const HELP_SECTION = {
48
50
  auth: `Usage: ascendkit auth <command>
49
51
 
50
52
  Commands:
51
- auth settings
52
- auth settings update --providers <p1,p2,...> [--email-verification <true|false>] [--waitlist <true|false>] [--password-reset <true|false>] [--session-duration <duration>]
53
- auth providers <p1,p2,...>
54
- auth oauth <provider>
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>
55
58
  auth oauth set <provider> --client-id <id> [--client-secret <secret> | --client-secret-stdin] [--callback-url <url>]
56
- auth users`,
57
- templates: `Usage: ascendkit templates <command>
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>
58
64
 
59
65
  Commands:
60
- templates create --name <name> --subject <subject> --body-html <html> --body-text <text> [--slug <slug>] [--description <description>]
61
- templates list [--query <search>] [--system true|--custom true]
62
- templates get <template-id>
63
- templates update <template-id> [--subject <subject>] [--body-html <html>] [--body-text <text>] [--change-note <note>]
64
- templates delete <template-id>
65
- templates versions <template-id>
66
- templates version <template-id> <n>`,
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>`,
67
73
  survey: `Usage: ascendkit survey <command>
68
74
 
69
75
  Commands:
70
76
  survey create --name <name> [--type <nps|csat|custom>] [--definition <json>]
71
77
  survey list
72
- survey get <survey-id>
78
+ survey show <survey-id>
73
79
  survey update <survey-id> [--name <name>] [--status <draft|active|paused>] [--definition <json>]
74
- survey delete <survey-id>
80
+ survey remove <survey-id>
75
81
  survey distribute <survey-id> --users <usr_id1,usr_id2,...>
76
- survey invitations <survey-id>
82
+ survey invitation list <survey-id>
77
83
  survey analytics <survey-id>
78
- survey export-definition <survey-id> [--out <file>]
79
- survey import-definition <survey-id> --in <file>
80
-
81
- Notes:
82
- - import-definition only updates the survey definition. It does not mutate slug/name/status.`,
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,...>`,
83
91
  journey: `Usage: ascendkit journey <command>
84
92
 
85
93
  Commands:
86
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>]
87
95
  journey list [--status <draft|active|paused|archived>]
88
- journey get <journey-id>
96
+ journey show <journey-id>
89
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>]
90
- journey delete <journey-id>
98
+ journey remove <journey-id>
91
99
  journey activate <journey-id>
92
100
  journey pause <journey-id>
101
+ journey resume <journey-id>
93
102
  journey archive <journey-id>
94
103
  journey analytics <journey-id>
95
- journey list-nodes <journey-id>
96
- journey add-node <journey-id> --name <node-name> [--action <json>] [--email-id <email>] [--terminal <true|false>]
97
- journey edit-node <journey-id> <node-name> [--action <json>] [--email-id <email>] [--terminal <true|false>]
98
- journey remove-node <journey-id> <node-name>
99
- journey list-transitions <journey-id> [--from <node-name>] [--to <node-name>]
100
- journey add-transition <journey-id> --from <node-name> --to <node-name> --trigger <json> [--priority <n>] [--name <transition-name>]
101
- journey edit-transition <journey-id> <transition-name> [--trigger <json>] [--priority <n>]
102
- journey remove-transition <journey-id> <transition-name>`,
103
- 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>
104
113
 
105
114
  Commands:
106
- email settings
107
- email settings update [--from-email <email>] [--from-name <name>]
108
- email identity
109
- email use-default
110
- email use-custom <domain> [--from-email <email>] [--from-name <name>]
111
- email setup-domain <domain>
112
- email domain-status [--watch] [--interval <seconds>]
113
- email open-dns [--domain <domain>] [--open]
114
- email remove-domain`,
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]`,
115
126
  webhook: `Usage: ascendkit webhook <command>
116
127
 
117
128
  Commands:
@@ -137,50 +148,61 @@ Notes:
137
148
  - --audience is a JSON filter object, e.g. '{"tags":{"$in":["premium"]}}'
138
149
  - --scheduled-at / --at accepts ISO 8601 datetime, e.g. 2026-03-15T10:00:00Z
139
150
  - cancel deletes a draft/failed campaign or cancels a scheduled/sending campaign`,
140
- env: `Usage: ascendkit env <command>
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>
141
158
 
142
159
  Commands:
143
- env list --project <project-id>
144
- env use <tier> --project <project-id>
145
- env update <env-id> --project <project-id> [--name <name>] [--description <desc>]
146
- env promote <env-id> --target <tier>
147
- env set-var <key> <value>
148
- env unset-var <key>
149
- env list-vars`,
150
- import: `Usage: ascendkit import <source> [options]
160
+ keystore list
161
+ keystore set <key> <value>
162
+ keystore remove <key>`,
163
+ import: `Usage: ascendkit import <source> <action> [options]
151
164
 
152
165
  Sources:
153
- clerk Import users from Clerk
166
+ clerk Import users from Clerk
167
+ migration-journey Create migration email templates and journeys
154
168
 
155
169
  Commands:
156
- import clerk --api-key <key> [options]
157
- import clerk --file <path> [options]
158
- import create-migration-journey [--from-identity <email>]
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>]
159
175
 
160
176
  Options:
161
177
  --api-key <key> Clerk secret API key (fetches users from Clerk API)
162
178
  --file <path> Path to Clerk dashboard export (JSON)
163
179
  --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
180
  --users Import users (included by default; use to select only users)
166
181
  --settings Import auth settings / OAuth providers (included by default)
167
182
  --from-identity <email> Email identity for migration journey emails
168
183
 
169
- By default, import runs in dry-run mode and includes both users and settings.
170
- Pass --execute to apply changes. Pass --users or --settings alone to select
171
- only that phase (e.g. --users --execute imports only users, not settings).`,
172
- projects: `Usage: ascendkit projects <command>
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>
173
187
 
174
188
  Commands:
175
- projects list
176
- projects create --name <name> [--description <description>] [--services <s1,s2,...>]`,
189
+ project list
190
+ project create --name <name> [--description <description>] [--services <s1,s2,...>]
191
+ project show <project-id>
192
+ project env list <project-id>`,
177
193
  };
178
194
  function printSectionHelp(section) {
179
195
  if (!section)
180
196
  return false;
181
197
  let key = section.toLowerCase();
182
- if (key === "content")
183
- key = "templates";
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";
184
206
  const text = HELP_SECTION[key];
185
207
  if (!text)
186
208
  return false;
@@ -188,7 +210,7 @@ function printSectionHelp(section) {
188
210
  return true;
189
211
  }
190
212
  function getClient() {
191
- let publicKey = process.env.ASCENDKIT_PUBLIC_KEY;
213
+ let publicKey = process.env.ASCENDKIT_ENV_KEY;
192
214
  let apiUrl = process.env.ASCENDKIT_API_URL;
193
215
  const auth = loadAuth();
194
216
  const env = loadEnvContext();
@@ -241,6 +263,55 @@ async function readSecretFromStdin() {
241
263
  function output(data) {
242
264
  console.log(JSON.stringify(data, null, 2));
243
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
+ }
244
315
  function normalizeJourneyRows(data) {
245
316
  if (Array.isArray(data)) {
246
317
  return data;
@@ -321,7 +392,7 @@ async function run() {
321
392
  case "logout":
322
393
  platform.logout();
323
394
  return;
324
- case "projects":
395
+ case "project":
325
396
  if (action === "list") {
326
397
  const projects = await platform.listProjects();
327
398
  table(projects, [
@@ -330,18 +401,25 @@ async function run() {
330
401
  { key: "enabledServices", label: "Services", width: 30 },
331
402
  ]);
332
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
+ }
333
414
  else if (action === "create") {
334
415
  const flags = parseFlags(args.slice(2));
335
416
  if (!flags.name) {
336
- console.error("Usage: ascendkit projects create --name <name> [--description <description>] [--services <s1,s2,...>]");
417
+ console.error("Usage: ascendkit project create --name <name> [--description <description>] [--services <s1,s2,...>]");
337
418
  process.exit(1);
338
419
  }
339
420
  try {
340
421
  const proj = await platform.createProject(flags.name, flags.description, flags.services?.split(","));
341
- const env = proj.environment;
342
- console.log(`Project created: ${proj.id}`);
343
- if (env)
344
- console.log(`Environment: ${env.publicKey}`);
422
+ printProjectSummary(proj);
345
423
  }
346
424
  catch (err) {
347
425
  let message = err instanceof Error ? err.message : String(err);
@@ -361,7 +439,7 @@ async function run() {
361
439
  }
362
440
  }
363
441
  else {
364
- console.error('Usage: ascendkit projects list|create');
442
+ console.error('Usage: ascendkit project list|create|show|env');
365
443
  process.exit(1);
366
444
  }
367
445
  return;
@@ -378,8 +456,11 @@ async function run() {
378
456
  case "verify":
379
457
  await runVerify();
380
458
  return;
381
- case "env":
382
- await runEnv(action, args.slice(2));
459
+ case "environment":
460
+ await runEnvironment(action, args.slice(2));
461
+ return;
462
+ case "keystore":
463
+ await runKeystore(action, args.slice(2));
383
464
  return;
384
465
  }
385
466
  // Service commands (need environment key)
@@ -388,6 +469,7 @@ async function run() {
388
469
  case "auth":
389
470
  await runAuth(client, action, args.slice(2));
390
471
  break;
472
+ case "template":
391
473
  case "templates":
392
474
  case "content":
393
475
  await runContent(client, action, args.slice(2));
@@ -398,7 +480,7 @@ async function run() {
398
480
  case "journey":
399
481
  await runJourney(client, action, args.slice(2));
400
482
  break;
401
- case "email":
483
+ case "email-identity":
402
484
  await runEmail(client, action, args.slice(2));
403
485
  break;
404
486
  case "webhook":
@@ -417,13 +499,29 @@ async function run() {
417
499
  }
418
500
  }
419
501
  async function runImport(client, source, rest) {
420
- const flags = parseFlags(rest);
502
+ if (!source) {
503
+ console.log(HELP_SECTION.import);
504
+ return;
505
+ }
506
+ let action = rest[0];
507
+ let args = rest;
421
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") {
422
520
  const result = await importCmd.instantiateMigrationJourney(client, flags["from-identity"]);
423
521
  output(result);
424
522
  console.log("\nNext steps:");
425
523
  console.log(" ascendkit journey list — review created journeys");
426
- console.log(" ascendkit templates list — review migration email templates");
524
+ console.log(" ascendkit template list — review migration email templates");
427
525
  console.log(" ascendkit journey activate <journey-id> — activate when ready");
428
526
  return;
429
527
  }
@@ -433,8 +531,7 @@ async function runImport(client, source, rest) {
433
531
  console.error('Run "ascendkit help import" for usage');
434
532
  process.exit(1);
435
533
  }
436
- const execute = flags.execute === "true" || flags.execute === "";
437
- const dryRun = !execute;
534
+ const dryRun = action !== "run";
438
535
  const hasUsers = flags.users !== undefined;
439
536
  const hasSettings = flags.settings !== undefined;
440
537
  // If neither --users nor --settings is passed, both default to true.
@@ -444,7 +541,7 @@ async function runImport(client, source, rest) {
444
541
  const apiKey = flags["api-key"];
445
542
  const filePath = flags.file;
446
543
  if (!apiKey && !filePath) {
447
- console.error("Usage: ascendkit import clerk --api-key <key> | --file <path> [--execute]");
544
+ console.error("Usage: ascendkit import clerk preview|run --api-key <key> | --file <path>");
448
545
  process.exit(1);
449
546
  }
450
547
  let clerkUsers = [];
@@ -558,35 +655,49 @@ async function runImport(client, source, rest) {
558
655
  }
559
656
  else if (totalImported > 0) {
560
657
  console.log("\nTo set up migration emails, run:\n" +
561
- " ascendkit import create-migration-journey");
658
+ " ascendkit import migration-journey create");
562
659
  }
563
660
  }
564
- async function runEnv(action, rest) {
565
- const flags = parseFlags(rest);
661
+ function flagsFromLegacy(args) {
662
+ return parseFlags(args);
663
+ }
664
+ async function runProjectEnvironment(rest) {
665
+ const action = rest[0];
666
+ const target = rest[1];
566
667
  switch (action) {
567
668
  case "list":
568
- if (!flags.project) {
569
- console.error("Usage: ascendkit env list --project <project-id>");
669
+ if (!target) {
670
+ console.error("Usage: ascendkit project env list <project-id>");
570
671
  process.exit(1);
571
672
  }
572
- table(await platform.listEnvironments(flags.project), [
673
+ table(await platform.listEnvironments(target), [
674
+ { key: "id", label: "ID" },
573
675
  { key: "name", label: "Name", width: 20 },
574
676
  { key: "tier", label: "Tier" },
575
677
  { key: "publicKey", label: "Public Key" },
576
678
  ]);
577
- break;
578
- case "use":
579
- if (!rest[0] || !flags.project) {
580
- console.error("Usage: ascendkit env use <tier> --project <project-id>");
581
- process.exit(1);
582
- }
583
- await platform.useEnvironment(rest[0], flags.project);
584
- break;
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;
585
696
  case "promote": {
586
- const envId = rest[0];
697
+ const envId = rest[0] && !rest[0].startsWith("--") ? rest[0] : ctx.environmentId;
587
698
  const target = flags.target;
588
699
  if (!envId || !target) {
589
- console.error("Usage: ascendkit env promote <env-id> --target <tier>");
700
+ console.error("Usage: ascendkit environment promote [<env-id>] --target <tier>");
590
701
  process.exit(1);
591
702
  }
592
703
  try {
@@ -610,14 +721,10 @@ async function runEnv(action, rest) {
610
721
  console.error(message);
611
722
  process.exit(1);
612
723
  }
613
- break;
724
+ return;
614
725
  }
615
726
  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
- }
727
+ const envId = rest[0] && !rest[0].startsWith("--") ? rest[0] : ctx.environmentId;
621
728
  const name = flags.name;
622
729
  const description = flags.description;
623
730
  if (!name && description === undefined) {
@@ -625,7 +732,7 @@ async function runEnv(action, rest) {
625
732
  process.exit(1);
626
733
  }
627
734
  try {
628
- const result = await platform.updateEnvironment(flags.project, envId, name, description);
735
+ const result = await platform.updateEnvironment(ctx.projectId, envId, name, description);
629
736
  console.log("Environment updated:");
630
737
  console.log(JSON.stringify(result, null, 2));
631
738
  }
@@ -645,116 +752,137 @@ async function runEnv(action, rest) {
645
752
  console.error(message);
646
753
  process.exit(1);
647
754
  }
648
- break;
755
+ return;
649
756
  }
650
- case "set-var": {
757
+ default:
758
+ console.error(`Unknown environment command: ${action}`);
759
+ console.error("Usage: ascendkit environment show|update|promote");
760
+ process.exit(1);
761
+ }
762
+ }
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
+ : [];
774
+ switch (action) {
775
+ case "set": {
651
776
  const key = rest[0];
652
777
  const value = rest[1];
653
778
  if (!key || value === undefined) {
654
- console.error("Usage: ascendkit env set-var <key> <value>");
779
+ console.error("Usage: ascendkit keystore set <key> <value>");
655
780
  process.exit(1);
656
781
  }
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);
661
- }
662
- const current = await platform.getEnvironment(ctx.projectId, ctx.environmentId);
663
- const vars = { ...(current.variables ?? {}) };
664
782
  vars[key] = value;
665
783
  await platform.updateEnvironmentVariables(ctx.projectId, ctx.environmentId, vars);
666
- console.log(`Set ${key}=${value}`);
667
- break;
784
+ console.log(`Saved ${key}=${value}`);
785
+ return;
668
786
  }
669
- case "unset-var": {
787
+ case "remove": {
670
788
  const key = rest[0];
671
789
  if (!key) {
672
- console.error("Usage: ascendkit env unset-var <key>");
790
+ console.error("Usage: ascendkit keystore remove <key>");
673
791
  process.exit(1);
674
792
  }
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
793
  if (!(key in vars)) {
683
794
  console.error(`Variable "${key}" not found.`);
684
795
  process.exit(1);
685
796
  }
686
797
  delete vars[key];
687
798
  await platform.updateEnvironmentVariables(ctx.projectId, ctx.environmentId, vars);
688
- console.log(`Unset ${key}`);
689
- break;
799
+ console.log(`Removed ${key}`);
800
+ return;
690
801
  }
691
- case "list-vars": {
692
- const ctx = loadEnvContext();
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);
802
+ case "list": {
803
+ const entries = Object.entries(vars).map(([key, value]) => ({ key, value }));
700
804
  if (entries.length === 0) {
701
- console.log("No variables set.");
805
+ console.log("No custom variables set.");
702
806
  }
703
807
  else {
704
- for (const [k, v] of entries) {
705
- console.log(`${k}=${v || "(empty)"}`);
706
- }
808
+ console.log("Custom variables:");
809
+ table(entries, [
810
+ { key: "key", label: "Key", width: 32 },
811
+ { key: "value", label: "Value", width: 60 },
812
+ ]);
707
813
  }
708
- break;
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;
709
827
  }
710
828
  default:
711
- console.error(`Unknown env command: ${action}`);
712
- console.error("Usage: ascendkit env list|use|update|promote|set-var|unset-var|list-vars");
829
+ console.error(`Unknown keystore command: ${action}`);
830
+ console.error("Usage: ascendkit keystore list|set|remove");
713
831
  process.exit(1);
714
832
  }
715
833
  }
716
834
  async function runAuth(client, action, rest) {
717
835
  const flags = parseFlags(rest);
718
- switch (action) {
719
- case "settings":
720
- if (rest[0] === "update") {
721
- const params = {};
722
- if (flags.providers)
723
- params.providers = flags.providers.split(",");
724
- if (flags["email-verification"] || flags.waitlist || flags["password-reset"]) {
725
- params.features = {};
726
- if (flags["email-verification"])
727
- params.features.emailVerification = flags["email-verification"] === "true";
728
- if (flags.waitlist)
729
- params.features.waitlist = flags.waitlist === "true";
730
- if (flags["password-reset"])
731
- params.features.passwordReset = flags["password-reset"] === "true";
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));
861
+ break;
862
+ }
863
+ case "provider":
864
+ case "providers":
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);
732
870
  }
733
- if (flags["session-duration"])
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}`);
871
+ printAuthSettingsSummary(await auth.updateProviders(client, providersArg.split(",")));
743
872
  }
744
873
  else {
745
- output(await auth.getSettings(client));
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 }]);
746
880
  }
747
881
  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
882
  case "oauth": {
756
- if (rest[0] === "set") {
757
- const provider = rest[1];
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") {
758
886
  if (!provider || !flags["client-id"]) {
759
887
  console.error("Usage: ascendkit auth oauth set <provider> --client-id <id> [--client-secret <secret> | --client-secret-stdin] [--callback-url <url>]");
760
888
  process.exit(1);
@@ -774,28 +902,60 @@ async function runAuth(client, action, rest) {
774
902
  console.error("Client secret cannot be empty.");
775
903
  process.exit(1);
776
904
  }
777
- output(await auth.updateOAuthCredentials(client, provider, flags["client-id"], clientSecret, flags["callback-url"]));
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}.`);
778
915
  }
779
916
  else {
780
- if (!rest[0]) {
781
- console.error("Usage: ascendkit auth oauth <provider>");
917
+ if (!provider) {
918
+ console.error("Usage: ascendkit auth oauth open <provider>");
782
919
  process.exit(1);
783
920
  }
784
921
  const portalUrl = process.env.ASCENDKIT_PORTAL_URL ?? "http://localhost:3000";
785
- const url = auth.getOAuthSetupUrl(portalUrl, rest[0], client.currentPublicKey ?? undefined);
786
- console.log(`Opening browser to configure ${rest[0]} OAuth credentials...`);
922
+ const url = auth.getOAuthSetupUrl(portalUrl, provider, client.currentPublicKey ?? undefined);
923
+ console.log(`Opening browser to configure ${provider} OAuth credentials...`);
787
924
  console.log(url);
788
925
  openBrowser(url);
789
926
  }
790
927
  break;
791
928
  }
929
+ case "user":
792
930
  case "users":
793
- table(await auth.listUsers(client), [
794
- { key: "id", label: "ID" },
795
- { key: "email", label: "Email", width: 35 },
796
- { key: "name", label: "Name", width: 25 },
797
- { key: "status", label: "Status" },
798
- ]);
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
+ }
799
959
  break;
800
960
  default:
801
961
  console.error(`Unknown auth command: ${action}`);
@@ -804,13 +964,20 @@ async function runAuth(client, action, rest) {
804
964
  }
805
965
  async function runContent(client, action, rest) {
806
966
  const flags = parseFlags(rest);
807
- switch (action) {
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) {
808
975
  case "create":
809
976
  if (!flags.name || !flags.subject || !flags["body-html"] || !flags["body-text"]) {
810
- console.error("Usage: ascendkit templates create --name <n> --subject <s> --body-html <h> --body-text <t> [--slug <slug>] [--description <desc>]");
977
+ console.error("Usage: ascendkit template create --name <n> --subject <s> --body-html <h> --body-text <t> [--slug <slug>] [--description <desc>]");
811
978
  process.exit(1);
812
979
  }
813
- output(await content.createTemplate(client, {
980
+ printTemplateSummary(await content.createTemplate(client, {
814
981
  name: flags.name, subject: flags.subject,
815
982
  bodyHtml: flags["body-html"], bodyText: flags["body-text"],
816
983
  slug: flags.slug, description: flags.description,
@@ -833,64 +1000,79 @@ async function runContent(client, action, rest) {
833
1000
  ]);
834
1001
  break;
835
1002
  }
836
- case "get":
1003
+ case "show":
837
1004
  if (!rest[0]) {
838
- console.error("Usage: ascendkit templates get <template-id>");
1005
+ console.error("Usage: ascendkit template show <template-id>");
839
1006
  process.exit(1);
840
1007
  }
841
- output(await content.getTemplate(client, rest[0]));
1008
+ printTemplateSummary(await content.getTemplate(client, rest[0]));
842
1009
  break;
843
1010
  case "update":
844
1011
  if (!rest[0]) {
845
- console.error("Usage: ascendkit templates update <template-id> [--flags]");
1012
+ console.error("Usage: ascendkit template update <template-id> [--flags]");
846
1013
  process.exit(1);
847
1014
  }
848
- output(await content.updateTemplate(client, rest[0], {
1015
+ printTemplateSummary(await content.updateTemplate(client, rest[0], {
849
1016
  subject: flags.subject,
850
1017
  bodyHtml: flags["body-html"],
851
1018
  bodyText: flags["body-text"],
852
1019
  changeNote: flags["change-note"],
853
1020
  }));
854
1021
  break;
855
- case "delete":
1022
+ case "remove":
856
1023
  if (!rest[0]) {
857
- console.error("Usage: ascendkit templates delete <template-id>");
1024
+ console.error("Usage: ascendkit template remove <template-id>");
858
1025
  process.exit(1);
859
1026
  }
860
- output(await content.deleteTemplate(client, rest[0]));
1027
+ await content.deleteTemplate(client, rest[0]);
1028
+ console.log(`Removed template ${rest[0]}.`);
861
1029
  break;
862
1030
  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
1031
  case "version":
870
- if (!rest[0] || !rest[1]) {
871
- console.error("Usage: ascendkit templates version <template-id> <n>");
872
- process.exit(1);
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)));
873
1053
  }
874
- output(await content.getVersion(client, rest[0], parseInt(rest[1], 10)));
875
1054
  break;
876
1055
  default:
877
- console.error(`Unknown templates command: ${action}`);
1056
+ console.error(`Unknown template command: ${action}`);
878
1057
  process.exit(1);
879
1058
  }
880
1059
  }
881
1060
  async function runSurvey(client, action, rest) {
882
1061
  const flags = parseFlags(rest);
883
- if (!action) {
1062
+ const normalizedAction = action === "show" || action === "get" ? "show" :
1063
+ action === "remove" || action === "delete" ? "remove" :
1064
+ action;
1065
+ if (!normalizedAction) {
884
1066
  console.log(HELP_SECTION.survey);
885
1067
  return;
886
1068
  }
887
- switch (action) {
1069
+ switch (normalizedAction) {
888
1070
  case "create":
889
1071
  if (!flags.name) {
890
1072
  console.error("Usage: ascendkit survey create --name <n> [--type nps|csat|custom]");
891
1073
  process.exit(1);
892
1074
  }
893
- output(await surveys.createSurvey(client, {
1075
+ printSurveySummary(await surveys.createSurvey(client, {
894
1076
  name: flags.name,
895
1077
  type: flags.type ?? "custom",
896
1078
  definition: flags.definition ? JSON.parse(flags.definition) : undefined,
@@ -904,30 +1086,31 @@ async function runSurvey(client, action, rest) {
904
1086
  { key: "status", label: "Status" },
905
1087
  ]);
906
1088
  break;
907
- case "get":
1089
+ case "show":
908
1090
  if (!rest[0]) {
909
- console.error("Usage: ascendkit survey get <survey-id>");
1091
+ console.error("Usage: ascendkit survey show <survey-id>");
910
1092
  process.exit(1);
911
1093
  }
912
- output(await surveys.getSurvey(client, rest[0]));
1094
+ printSurveySummary(await surveys.getSurvey(client, rest[0]));
913
1095
  break;
914
1096
  case "update":
915
1097
  if (!rest[0]) {
916
1098
  console.error("Usage: ascendkit survey update <survey-id> [--flags]");
917
1099
  process.exit(1);
918
1100
  }
919
- output(await surveys.updateSurvey(client, rest[0], {
1101
+ printSurveySummary(await surveys.updateSurvey(client, rest[0], {
920
1102
  name: flags.name,
921
1103
  status: flags.status,
922
1104
  definition: flags.definition ? JSON.parse(flags.definition) : undefined,
923
1105
  }));
924
1106
  break;
925
- case "delete":
1107
+ case "remove":
926
1108
  if (!rest[0]) {
927
- console.error("Usage: ascendkit survey delete <survey-id>");
1109
+ console.error("Usage: ascendkit survey remove <survey-id>");
928
1110
  process.exit(1);
929
1111
  }
930
- output(await surveys.deleteSurvey(client, rest[0]));
1112
+ await surveys.deleteSurvey(client, rest[0]);
1113
+ console.log(`Removed survey ${rest[0]}.`);
931
1114
  break;
932
1115
  case "distribute":
933
1116
  if (!rest[0] || !flags.users) {
@@ -937,11 +1120,19 @@ async function runSurvey(client, action, rest) {
937
1120
  output(await surveys.distributeSurvey(client, rest[0], flags.users.split(",")));
938
1121
  break;
939
1122
  case "invitations":
940
- if (!rest[0]) {
941
- console.error("Usage: ascendkit survey invitations <survey-id>");
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]}`);
942
1134
  process.exit(1);
943
1135
  }
944
- output(await surveys.listInvitations(client, rest[0]));
945
1136
  break;
946
1137
  case "analytics":
947
1138
  if (!rest[0]) {
@@ -950,96 +1141,121 @@ async function runSurvey(client, action, rest) {
950
1141
  }
951
1142
  output(await surveys.getAnalytics(client, rest[0]));
952
1143
  break;
953
- case "export-definition": {
954
- if (!rest[0]) {
955
- console.error("Usage: ascendkit survey export-definition <survey-id> [--out <file>]");
956
- process.exit(1);
957
- }
958
- const survey = await surveys.getSurvey(client, rest[0]);
959
- const text = `${JSON.stringify(survey.definition ?? {}, null, 2)}\n`;
960
- const outPath = flags.out || flags.file;
961
- if (outPath) {
962
- writeFileSync(outPath, text, "utf8");
963
- console.log(`Wrote survey definition to ${outPath}`);
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
+ }
964
1166
  }
965
1167
  else {
966
- process.stdout.write(text);
967
- }
968
- break;
969
- }
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);
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
+ }));
984
1185
  }
985
- output(await surveys.updateSurvey(client, rest[0], {
986
- definition: candidate,
987
- }));
988
1186
  break;
989
1187
  }
990
1188
  case "list-questions":
991
- if (!rest[0]) {
992
- console.error("Usage: ascendkit survey list-questions <survey-id>");
993
- process.exit(1);
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>");
1204
+ process.exit(1);
1205
+ }
1206
+ if (questionAction === "list") {
1207
+ output(await surveys.listQuestions(client, surveyId));
1208
+ }
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));
994
1245
  }
995
- output(await surveys.listQuestions(client, rest[0]));
996
- break;
997
- case "add-question": {
998
- if (!rest[0] || !flags.type || !flags.title) {
999
- console.error("Usage: ascendkit survey add-question <survey-id> --type <type> --title <title> [--name <name>] [--required <true|false>] [--choices <c1,c2,...>] [--position <n>]");
1000
- 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(",")));
1001
1252
  }
1002
- const params = { type: flags.type, title: flags.title };
1003
- if (flags.name)
1004
- params.name = flags.name;
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,...>]");
1253
+ else {
1254
+ console.error(`Unknown survey question command: ${questionAction}`);
1017
1255
  process.exit(1);
1018
1256
  }
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
1257
  break;
1028
1258
  }
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
1259
  default:
1044
1260
  console.error(`Unknown survey command: ${action}`);
1045
1261
  console.error('Run "ascendkit survey --help" for usage');
@@ -1064,7 +1280,7 @@ function runStatus() {
1064
1280
  }
1065
1281
  else {
1066
1282
  console.log(" No environment set. Run: ascendkit set-env <public-key>");
1067
- console.log(" List environments: ascendkit env list --project <project-id>");
1283
+ console.log(" List environments: ascendkit project env list <project-id>");
1068
1284
  }
1069
1285
  console.log();
1070
1286
  }
@@ -1128,6 +1344,34 @@ async function runVerify() {
1128
1344
  console.log();
1129
1345
  }
1130
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
+ }
1131
1375
  const flags = parseFlags(rest);
1132
1376
  switch (action) {
1133
1377
  case "create":
@@ -1135,7 +1379,7 @@ async function runJourney(client, action, rest) {
1135
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>]");
1136
1380
  process.exit(1);
1137
1381
  }
1138
- output(await journeys.createJourney(client, {
1382
+ console.log(formatJourneyWithGuidance((await journeys.createJourney(client, {
1139
1383
  name: flags.name,
1140
1384
  entryEvent: flags["entry-event"],
1141
1385
  entryNode: flags["entry-node"],
@@ -1144,7 +1388,7 @@ async function runJourney(client, action, rest) {
1144
1388
  description: flags.description,
1145
1389
  entryConditions: flags["entry-conditions"] ? JSON.parse(flags["entry-conditions"]) : undefined,
1146
1390
  reEntryPolicy: flags["re-entry-policy"],
1147
- }));
1391
+ }))));
1148
1392
  break;
1149
1393
  case "list": {
1150
1394
  const opts = {};
@@ -1162,17 +1406,17 @@ async function runJourney(client, action, rest) {
1162
1406
  }
1163
1407
  case "get":
1164
1408
  if (!rest[0]) {
1165
- console.error("Usage: ascendkit journey get <journey-id>");
1409
+ console.error("Usage: ascendkit journey show <journey-id>");
1166
1410
  process.exit(1);
1167
1411
  }
1168
- output(await journeys.getJourney(client, rest[0]));
1412
+ console.log(formatJourneyWithGuidance(await journeys.getJourney(client, rest[0])));
1169
1413
  break;
1170
1414
  case "update":
1171
1415
  if (!rest[0]) {
1172
1416
  console.error("Usage: ascendkit journey update <journey-id> [--flags]");
1173
1417
  process.exit(1);
1174
1418
  }
1175
- output(await journeys.updateJourney(client, rest[0], {
1419
+ console.log(formatJourneyWithGuidance((await journeys.updateJourney(client, rest[0], {
1176
1420
  name: flags.name,
1177
1421
  description: flags.description,
1178
1422
  entryEvent: flags["entry-event"],
@@ -1181,53 +1425,61 @@ async function runJourney(client, action, rest) {
1181
1425
  reEntryPolicy: flags["re-entry-policy"],
1182
1426
  nodes: flags.nodes ? JSON.parse(flags.nodes) : undefined,
1183
1427
  transitions: flags.transitions ? JSON.parse(flags.transitions) : undefined,
1184
- }));
1428
+ }))));
1185
1429
  break;
1186
1430
  case "delete":
1187
1431
  if (!rest[0]) {
1188
- console.error("Usage: ascendkit journey delete <journey-id>");
1432
+ console.error("Usage: ascendkit journey remove <journey-id>");
1189
1433
  process.exit(1);
1190
1434
  }
1191
- output(await journeys.deleteJourney(client, rest[0]));
1435
+ await journeys.deleteJourney(client, rest[0]);
1436
+ console.log(`Deleted journey ${rest[0]}.`);
1192
1437
  break;
1193
1438
  case "activate":
1194
1439
  if (!rest[0]) {
1195
1440
  console.error("Usage: ascendkit journey activate <journey-id>");
1196
1441
  process.exit(1);
1197
1442
  }
1198
- output(await journeys.activateJourney(client, rest[0]));
1443
+ console.log(formatJourneyWithGuidance(await journeys.activateJourney(client, rest[0])));
1199
1444
  break;
1200
1445
  case "pause":
1201
1446
  if (!rest[0]) {
1202
1447
  console.error("Usage: ascendkit journey pause <journey-id>");
1203
1448
  process.exit(1);
1204
1449
  }
1205
- output(await journeys.pauseJourney(client, rest[0]));
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])));
1206
1458
  break;
1207
1459
  case "archive":
1208
1460
  if (!rest[0]) {
1209
1461
  console.error("Usage: ascendkit journey archive <journey-id>");
1210
1462
  process.exit(1);
1211
1463
  }
1212
- output(await journeys.archiveJourney(client, rest[0]));
1464
+ console.log(formatJourneyWithGuidance(await journeys.archiveJourney(client, rest[0])));
1213
1465
  break;
1214
1466
  case "analytics":
1215
1467
  if (!rest[0]) {
1216
1468
  console.error("Usage: ascendkit journey analytics <journey-id>");
1217
1469
  process.exit(1);
1218
1470
  }
1219
- output(await journeys.getJourneyAnalytics(client, rest[0]));
1471
+ console.log(formatJourneyAnalytics(await journeys.getJourneyAnalytics(client, rest[0])));
1220
1472
  break;
1221
1473
  case "list-nodes":
1222
1474
  if (!rest[0]) {
1223
- console.error("Usage: ascendkit journey list-nodes <journey-id>");
1475
+ console.error("Usage: ascendkit journey node list <journey-id>");
1224
1476
  process.exit(1);
1225
1477
  }
1226
- output(await journeys.listNodes(client, rest[0]));
1478
+ console.log(formatNodeList(await journeys.listNodes(client, rest[0])));
1227
1479
  break;
1228
1480
  case "add-node": {
1229
1481
  if (!rest[0] || !flags.name) {
1230
- console.error("Usage: ascendkit journey add-node <journey-id> --name <node-name> [--action <json>] [--email-id <email>] [--terminal <true|false>]");
1482
+ console.error("Usage: ascendkit journey node add <journey-id> --name <node-name> [--action <json>] [--email-id <email>] [--terminal <true|false>]");
1231
1483
  process.exit(1);
1232
1484
  }
1233
1485
  const params = { name: flags.name };
@@ -1242,12 +1494,12 @@ async function runJourney(client, action, rest) {
1242
1494
  }
1243
1495
  if (flags.terminal)
1244
1496
  params.terminal = flags.terminal === "true";
1245
- output(await journeys.addNode(client, rest[0], params));
1497
+ console.log(formatSingleNode(await journeys.addNode(client, rest[0], params), "Added", flags.name));
1246
1498
  break;
1247
1499
  }
1248
1500
  case "edit-node": {
1249
1501
  if (!rest[0] || !rest[1]) {
1250
- console.error("Usage: ascendkit journey edit-node <journey-id> <node-name> [--action <json>] [--email-id <email>] [--terminal <true|false>]");
1502
+ console.error("Usage: ascendkit journey node update <journey-id> <node-name> [--action <json>] [--email-id <email>] [--terminal <true|false>]");
1251
1503
  process.exit(1);
1252
1504
  }
1253
1505
  const params = {};
@@ -1271,30 +1523,30 @@ async function runJourney(client, action, rest) {
1271
1523
  }
1272
1524
  if (flags.terminal)
1273
1525
  params.terminal = flags.terminal === "true";
1274
- output(await journeys.editNode(client, rest[0], rest[1], params));
1526
+ console.log(formatSingleNode(await journeys.editNode(client, rest[0], rest[1], params), "Updated", rest[1]));
1275
1527
  break;
1276
1528
  }
1277
1529
  case "remove-node":
1278
1530
  if (!rest[0] || !rest[1]) {
1279
- console.error("Usage: ascendkit journey remove-node <journey-id> <node-name>");
1531
+ console.error("Usage: ascendkit journey node remove <journey-id> <node-name>");
1280
1532
  process.exit(1);
1281
1533
  }
1282
- output(await journeys.removeNode(client, rest[0], rest[1]));
1534
+ console.log(formatSingleNode(await journeys.removeNode(client, rest[0], rest[1]), "Removed", rest[1]));
1283
1535
  break;
1284
1536
  case "list-transitions": {
1285
1537
  if (!rest[0]) {
1286
- console.error("Usage: ascendkit journey list-transitions <journey-id> [--from <node-name>] [--to <node-name>]");
1538
+ console.error("Usage: ascendkit journey transition list <journey-id> [--from <node-name>] [--to <node-name>]");
1287
1539
  process.exit(1);
1288
1540
  }
1289
- output(await journeys.listTransitions(client, rest[0], {
1541
+ console.log(formatTransitionList(await journeys.listTransitions(client, rest[0], {
1290
1542
  from_node: flags.from,
1291
1543
  to_node: flags.to,
1292
- }));
1544
+ })));
1293
1545
  break;
1294
1546
  }
1295
1547
  case "add-transition": {
1296
1548
  if (!rest[0] || !flags.from || !flags.to || !(flags.on || flags.after || flags.trigger)) {
1297
- console.error("Usage: ascendkit journey add-transition <journey-id> --from <node-name> --to <node-name> --on <event> | --after <delay> | --trigger <json> [--priority <n>] [--name <transition-name>]");
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>]");
1298
1550
  process.exit(1);
1299
1551
  }
1300
1552
  let trigger;
@@ -1316,12 +1568,13 @@ async function runJourney(client, action, rest) {
1316
1568
  params.priority = Number(flags.priority);
1317
1569
  if (flags.name)
1318
1570
  params.name = flags.name;
1319
- output(await journeys.addTransition(client, rest[0], params));
1571
+ const transitionName = flags.name || `${flags.from}-to-${flags.to}`;
1572
+ console.log(formatSingleTransition(await journeys.addTransition(client, rest[0], params), "Added", transitionName));
1320
1573
  break;
1321
1574
  }
1322
1575
  case "edit-transition": {
1323
1576
  if (!rest[0] || !rest[1]) {
1324
- console.error("Usage: ascendkit journey edit-transition <journey-id> <transition-name> [--on <event>] [--after <delay>] [--trigger <json>] [--priority <n>]");
1577
+ console.error("Usage: ascendkit journey transition update <journey-id> <transition-name> [--on <event>] [--after <delay>] [--trigger <json>] [--priority <n>]");
1325
1578
  process.exit(1);
1326
1579
  }
1327
1580
  const params = {};
@@ -1336,15 +1589,15 @@ async function runJourney(client, action, rest) {
1336
1589
  }
1337
1590
  if (flags.priority != null)
1338
1591
  params.priority = Number(flags.priority);
1339
- output(await journeys.editTransition(client, rest[0], rest[1], params));
1592
+ console.log(formatSingleTransition(await journeys.editTransition(client, rest[0], rest[1], params), "Updated", rest[1]));
1340
1593
  break;
1341
1594
  }
1342
1595
  case "remove-transition":
1343
1596
  if (!rest[0] || !rest[1]) {
1344
- console.error("Usage: ascendkit journey remove-transition <journey-id> <transition-name>");
1597
+ console.error("Usage: ascendkit journey transition remove <journey-id> <transition-name>");
1345
1598
  process.exit(1);
1346
1599
  }
1347
- output(await journeys.removeTransition(client, rest[0], rest[1]));
1600
+ console.log(formatSingleTransition(await journeys.removeTransition(client, rest[0], rest[1]), "Removed", rest[1]));
1348
1601
  break;
1349
1602
  default:
1350
1603
  console.error(`Unknown journey command: ${action}`);
@@ -1526,56 +1779,39 @@ async function runCampaign(client, action, rest) {
1526
1779
  }
1527
1780
  async function runEmail(client, action, rest) {
1528
1781
  const flags = parseFlags(rest);
1782
+ if (!action) {
1783
+ console.log(HELP_SECTION["email-identity"]);
1784
+ return;
1785
+ }
1529
1786
  switch (action) {
1530
- case "identity": {
1531
- const s = await email.getSettings(client);
1532
- printIdentityStatus(s);
1533
- break;
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));
1787
+ case "settings": {
1788
+ const settings = await email.getSettings(client);
1789
+ if (flags.json === "true") {
1790
+ output(settings);
1560
1791
  }
1561
1792
  else {
1562
- output(await email.getSettings(client));
1793
+ printEmailSettingsSummary(settings);
1563
1794
  }
1564
1795
  break;
1796
+ }
1565
1797
  case "setup-domain":
1566
1798
  if (!rest[0]) {
1567
- console.error("Usage: ascendkit email setup-domain <domain>");
1799
+ console.error("Usage: ascendkit email-identity setup-domain <domain>");
1568
1800
  process.exit(1);
1569
1801
  }
1570
1802
  await printEmailSetup(await email.setupDomain(client, rest[0]));
1571
1803
  break;
1572
- case "domain-status": {
1804
+ case "status": {
1573
1805
  const interval = Math.max(5, Number.parseInt(flags.interval || "15", 10) || 15);
1574
1806
  if (flags.watch === "true") {
1575
1807
  await watchDomainStatus(client, interval);
1576
1808
  }
1577
1809
  else {
1578
- output(await email.checkDomainStatus(client));
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 ?? []);
1579
1815
  }
1580
1816
  break;
1581
1817
  }
@@ -1595,8 +1831,68 @@ async function runEmail(client, action, rest) {
1595
1831
  case "remove-domain":
1596
1832
  output(await email.removeDomain(client));
1597
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
+ }
1598
1894
  default:
1599
- console.error(`Unknown email command: ${action}`);
1895
+ console.error(`Unknown email-identity command: ${action}`);
1600
1896
  process.exit(1);
1601
1897
  }
1602
1898
  }
@@ -1625,28 +1921,61 @@ async function printEmailSetup(settings) {
1625
1921
  }
1626
1922
  }
1627
1923
  }
1628
- function printIdentityStatus(settings) {
1629
- const mode = settings.domain ? "customer-owned" : "ascendkit-default";
1630
- const from = settings.fromEmail || "noreply@ascendkit.dev";
1631
- const status = settings.verificationStatus || "none";
1632
- console.log(`Identity mode: ${mode}`);
1633
- console.log(`From email: ${from}`);
1634
- console.log(`From name: ${settings.fromName || "not set"}`);
1635
- if (settings.domain) {
1636
- console.log(`Domain: ${settings.domain} (${status})`);
1637
- if (status !== "verified") {
1638
- 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);
1639
1934
  }
1935
+ return;
1640
1936
  }
1641
- else {
1642
- console.log("Next step: run `ascendkit email use-custom <domain>` to configure customer-owned identity.");
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;
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`);
1643
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
+ ]);
1644
1970
  }
1645
1971
  async function watchDomainStatus(client, intervalSeconds) {
1646
1972
  while (true) {
1647
- const data = await email.checkDomainStatus(client);
1648
- output(data);
1649
- if (data?.status === "verified" || data?.status === "failed" || data?.status === "none") {
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") {
1650
1979
  return;
1651
1980
  }
1652
1981
  await new Promise((resolve) => setTimeout(resolve, intervalSeconds * 1000));