@ascendkit/cli 0.1.11 → 0.2.6

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.
@@ -32,6 +32,7 @@ export declare class AscendKitClient {
32
32
  delete<T = unknown>(path: string): Promise<T>;
33
33
  /** Write operation requiring both public key and platform auth. */
34
34
  managedRequest<T = unknown>(method: string, path: string, body?: unknown): Promise<T>;
35
+ managedGet<T = unknown>(path: string): Promise<T>;
35
36
  managedPut<T = unknown>(path: string, body?: unknown): Promise<T>;
36
37
  managedPost<T = unknown>(path: string, body?: unknown): Promise<T>;
37
38
  managedDelete<T = unknown>(path: string): Promise<T>;
@@ -179,6 +179,9 @@ export class AscendKitClient {
179
179
  const json = (await response.json());
180
180
  return json.data;
181
181
  }
182
+ managedGet(path) {
183
+ return this.managedRequest("GET", path);
184
+ }
182
185
  managedPut(path, body) {
183
186
  return this.managedRequest("PUT", path, body);
184
187
  }
package/dist/cli.js CHANGED
@@ -11,6 +11,8 @@ import * as platform from "./commands/platform.js";
11
11
  import * as journeys from "./commands/journeys.js";
12
12
  import * as email from "./commands/email.js";
13
13
  import * as webhooks from "./commands/webhooks.js";
14
+ import * as campaigns from "./commands/campaigns.js";
15
+ import * as importCmd from "./commands/import.js";
14
16
  import { parseDelay } from "./utils/duration.js";
15
17
  const require = createRequire(import.meta.url);
16
18
  const { version: CLI_VERSION } = require("../package.json");
@@ -32,10 +34,12 @@ Services:
32
34
  journey Lifecycle journeys, nodes, transitions
33
35
  email Email settings, domain verification, DNS
34
36
  webhook Webhook endpoints and testing
37
+ campaign Email campaigns, scheduling, analytics
38
+ import Import users from external auth providers
35
39
 
36
40
  Project Management:
37
41
  projects List and create projects
38
- env List, switch, and promote environments
42
+ env List, switch, update, and promote environments
39
43
  verify Check all services in the active environment
40
44
 
41
45
  Run "ascendkit help <section>" for detailed command usage.
@@ -89,8 +93,8 @@ Commands:
89
93
  journey archive <journey-id>
90
94
  journey analytics <journey-id>
91
95
  journey list-nodes <journey-id>
92
- journey add-node <journey-id> --name <node-name> [--action <json>] [--terminal <true|false>]
93
- journey edit-node <journey-id> <node-name> [--action <json>] [--terminal <true|false>]
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>]
94
98
  journey remove-node <journey-id> <node-name>
95
99
  journey list-transitions <journey-id> [--from <node-name>] [--to <node-name>]
96
100
  journey add-transition <journey-id> --from <node-name> --to <node-name> --trigger <json> [--priority <n>] [--name <transition-name>]
@@ -117,12 +121,54 @@ Commands:
117
121
  webhook update <webhook-id> [--url <url>] [--events <e1,e2,...>] [--status <active|inactive>]
118
122
  webhook delete <webhook-id>
119
123
  webhook test <webhook-id> [--event <event-type>]`,
124
+ campaign: `Usage: ascendkit campaign <command>
125
+
126
+ Commands:
127
+ campaign create --name <name> --template <template-id> --audience <json> [--scheduled-at <datetime>]
128
+ campaign list [--status <draft|scheduled|sending|sent|failed|cancelled>]
129
+ campaign show <campaign-id>
130
+ campaign update <campaign-id> [--name <name>] [--template <template-id>] [--audience <json>] [--scheduled-at <datetime>]
131
+ campaign preview <campaign-id>
132
+ campaign schedule <campaign-id> --at <datetime>
133
+ campaign cancel <campaign-id>
134
+ campaign analytics <campaign-id>
135
+
136
+ Notes:
137
+ - --audience is a JSON filter object, e.g. '{"tags":{"$in":["premium"]}}'
138
+ - --scheduled-at / --at accepts ISO 8601 datetime, e.g. 2026-03-15T10:00:00Z
139
+ - cancel deletes a draft/failed campaign or cancels a scheduled/sending campaign`,
120
140
  env: `Usage: ascendkit env <command>
121
141
 
122
142
  Commands:
123
143
  env list --project <project-id>
124
144
  env use <tier> --project <project-id>
125
- env promote <env-id> --target <tier>`,
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]
151
+
152
+ Sources:
153
+ clerk Import users from Clerk
154
+
155
+ Commands:
156
+ import clerk --api-key <key> [options]
157
+ import clerk --file <path> [options]
158
+ import create-migration-journey [--from-identity <email>]
159
+
160
+ Options:
161
+ --api-key <key> Clerk secret API key (fetches users from Clerk API)
162
+ --file <path> Path to Clerk dashboard export (JSON)
163
+ --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
+ --users Import users (included by default; use to select only users)
166
+ --settings Import auth settings / OAuth providers (included by default)
167
+ --from-identity <email> Email identity for migration journey emails
168
+
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).`,
126
172
  projects: `Usage: ascendkit projects <command>
127
173
 
128
174
  Commands:
@@ -291,7 +337,11 @@ async function run() {
291
337
  process.exit(1);
292
338
  }
293
339
  try {
294
- output(await platform.createProject(flags.name, flags.description, flags.services?.split(",")));
340
+ 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}`);
295
345
  }
296
346
  catch (err) {
297
347
  let message = err instanceof Error ? err.message : String(err);
@@ -354,12 +404,163 @@ async function run() {
354
404
  case "webhook":
355
405
  await runWebhook(client, action, args.slice(2));
356
406
  break;
407
+ case "campaign":
408
+ await runCampaign(client, action, args.slice(2));
409
+ break;
410
+ case "import":
411
+ await runImport(client, action, args.slice(2));
412
+ break;
357
413
  default:
358
414
  console.error(`Unknown command: ${domain}`);
359
415
  console.error('Run "ascendkit --help" for usage');
360
416
  process.exit(1);
361
417
  }
362
418
  }
419
+ async function runImport(client, source, rest) {
420
+ const flags = parseFlags(rest);
421
+ if (source === "create-migration-journey") {
422
+ const result = await importCmd.instantiateMigrationJourney(client, flags["from-identity"]);
423
+ output(result);
424
+ console.log("\nNext steps:");
425
+ console.log(" ascendkit journey list — review created journeys");
426
+ console.log(" ascendkit templates list — review migration email templates");
427
+ console.log(" ascendkit journey activate <journey-id> — activate when ready");
428
+ return;
429
+ }
430
+ if (source !== "clerk") {
431
+ console.error(`Unsupported import source: ${source}`);
432
+ console.error("Supported sources: clerk");
433
+ console.error('Run "ascendkit help import" for usage');
434
+ process.exit(1);
435
+ }
436
+ const execute = flags.execute === "true" || flags.execute === "";
437
+ const dryRun = !execute;
438
+ const hasUsers = flags.users !== undefined;
439
+ const hasSettings = flags.settings !== undefined;
440
+ // If neither --users nor --settings is passed, both default to true.
441
+ // If either is passed, only the specified phases run.
442
+ const importUsers = (!hasUsers && !hasSettings) || hasUsers;
443
+ const importSettings = (!hasUsers && !hasSettings) || hasSettings;
444
+ const apiKey = flags["api-key"];
445
+ const filePath = flags.file;
446
+ if (!apiKey && !filePath) {
447
+ console.error("Usage: ascendkit import clerk --api-key <key> | --file <path> [--execute]");
448
+ process.exit(1);
449
+ }
450
+ let clerkUsers = [];
451
+ if (filePath) {
452
+ console.log(`Reading Clerk export from ${filePath}...`);
453
+ const rawUsers = importCmd.parseClerkExport(filePath);
454
+ clerkUsers = rawUsers.map(importCmd.transformClerkUser);
455
+ console.log(`Parsed ${clerkUsers.length} users from file.`);
456
+ }
457
+ else {
458
+ console.log("Fetching users from Clerk API...");
459
+ const rawUsers = await importCmd.fetchClerkUsers(apiKey, flags["instance-url"]);
460
+ clerkUsers = rawUsers.map(importCmd.transformClerkUser);
461
+ console.log(`Fetched ${clerkUsers.length} users from Clerk.`);
462
+ }
463
+ if (clerkUsers.length === 0 && importUsers) {
464
+ console.log("No users to import.");
465
+ return;
466
+ }
467
+ if (dryRun) {
468
+ console.log("\n--- DRY RUN (pass --execute to apply changes) ---");
469
+ }
470
+ // Batch in chunks of 500 (backend max)
471
+ const batchSize = 500;
472
+ let totalImported = 0;
473
+ const allDuplicates = [];
474
+ const allErrors = [];
475
+ const allWarnings = [];
476
+ if (importUsers) {
477
+ for (let i = 0; i < clerkUsers.length; i += batchSize) {
478
+ const batch = clerkUsers.slice(i, i + batchSize);
479
+ const batchNum = Math.floor(i / batchSize) + 1;
480
+ const totalBatches = Math.ceil(clerkUsers.length / batchSize);
481
+ if (totalBatches > 1) {
482
+ console.log(`\nBatch ${batchNum}/${totalBatches} (${batch.length} users)...`);
483
+ }
484
+ const result = await importCmd.importUsers(client, {
485
+ source: "clerk",
486
+ users: batch,
487
+ dryRun,
488
+ });
489
+ totalImported += result.imported;
490
+ allDuplicates.push(...result.duplicates);
491
+ allErrors.push(...result.errors);
492
+ allWarnings.push(...result.warnings);
493
+ }
494
+ }
495
+ else {
496
+ console.log("Skipping user import (--settings only).");
497
+ }
498
+ if (importSettings) {
499
+ const derived = importCmd.deriveSettingsFromUsers(clerkUsers);
500
+ // --providers override lets the user pick exactly which SSO to enable
501
+ const providerOverride = flags.providers;
502
+ const ssoProviders = providerOverride
503
+ ? providerOverride.split(",").map((s) => s.trim()).filter(Boolean)
504
+ : derived.providers;
505
+ const providers = [];
506
+ if (derived.hasCredentials)
507
+ providers.push("credentials");
508
+ providers.push(...ssoProviders);
509
+ console.log("\nAuth settings from user data:");
510
+ console.log(` Credentials (email/password): ${derived.hasCredentials ? "yes" : "no"}`);
511
+ console.log(` SSO providers: ${ssoProviders.length > 0 ? ssoProviders.join(", ") : "none"}`);
512
+ if (providerOverride) {
513
+ console.log(` (overridden via --providers)`);
514
+ }
515
+ if (providers.length > 0) {
516
+ const settingsPayload = {
517
+ source: "clerk",
518
+ users: [],
519
+ authSettings: { enabledProviders: providers },
520
+ dryRun,
521
+ };
522
+ const settingsResult = await importCmd.importUsers(client, settingsPayload);
523
+ allWarnings.push(...settingsResult.warnings);
524
+ if (!dryRun) {
525
+ console.log(`\nAuth settings applied: ${providers.join(", ")}`);
526
+ console.log("Configure OAuth secrets in the portal under Auth → OAuth settings.");
527
+ }
528
+ else {
529
+ console.log(`\nWill enable: ${providers.join(", ")}`);
530
+ console.log("Use --providers linkedin to pick specific SSO providers.");
531
+ }
532
+ }
533
+ }
534
+ else {
535
+ console.log("Skipping auth settings import (--users only).");
536
+ }
537
+ console.log(`\n--- Import Summary ---`);
538
+ if (importUsers) {
539
+ console.log(`Imported: ${totalImported}`);
540
+ if (allDuplicates.length > 0) {
541
+ console.log(`Duplicates: ${allDuplicates.length}`);
542
+ }
543
+ }
544
+ if (allErrors.length > 0) {
545
+ console.log(`Errors: ${allErrors.length}`);
546
+ for (const err of allErrors.slice(0, 10)) {
547
+ console.log(` - ${err.email}: ${err.reason}`);
548
+ }
549
+ if (allErrors.length > 10) {
550
+ console.log(` ... and ${allErrors.length - 10} more`);
551
+ }
552
+ }
553
+ for (const warning of allWarnings) {
554
+ console.log(`Warning: ${warning}`);
555
+ }
556
+ if (dryRun) {
557
+ console.log("\nThis was a dry run. Pass --execute to apply changes.");
558
+ }
559
+ else if (totalImported > 0) {
560
+ console.log("\nTo set up migration emails, run:\n" +
561
+ " ascendkit import create-migration-journey");
562
+ }
563
+ }
363
564
  async function runEnv(action, rest) {
364
565
  const flags = parseFlags(rest);
365
566
  switch (action) {
@@ -411,9 +612,104 @@ async function runEnv(action, rest) {
411
612
  }
412
613
  break;
413
614
  }
615
+ 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
+ }
621
+ const name = flags.name;
622
+ const description = flags.description;
623
+ if (!name && description === undefined) {
624
+ console.error("Provide at least --name or --description to update.");
625
+ process.exit(1);
626
+ }
627
+ try {
628
+ const result = await platform.updateEnvironment(flags.project, envId, name, description);
629
+ console.log("Environment updated:");
630
+ console.log(JSON.stringify(result, null, 2));
631
+ }
632
+ catch (err) {
633
+ let message = err instanceof Error ? err.message : String(err);
634
+ const jsonMatch = message.match(/\{.*\}/s);
635
+ if (jsonMatch) {
636
+ try {
637
+ const parsed = JSON.parse(jsonMatch[0]);
638
+ if (parsed.error)
639
+ message = parsed.error;
640
+ else if (parsed.detail)
641
+ message = parsed.detail;
642
+ }
643
+ catch { /* use raw message */ }
644
+ }
645
+ console.error(message);
646
+ process.exit(1);
647
+ }
648
+ break;
649
+ }
650
+ case "set-var": {
651
+ const key = rest[0];
652
+ const value = rest[1];
653
+ if (!key || value === undefined) {
654
+ console.error("Usage: ascendkit env set-var <key> <value>");
655
+ process.exit(1);
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);
661
+ }
662
+ const current = await platform.getEnvironment(ctx.projectId, ctx.environmentId);
663
+ const vars = { ...(current.variables ?? {}) };
664
+ vars[key] = value;
665
+ await platform.updateEnvironmentVariables(ctx.projectId, ctx.environmentId, vars);
666
+ console.log(`Set ${key}=${value}`);
667
+ break;
668
+ }
669
+ case "unset-var": {
670
+ const key = rest[0];
671
+ if (!key) {
672
+ console.error("Usage: ascendkit env unset-var <key>");
673
+ process.exit(1);
674
+ }
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
+ if (!(key in vars)) {
683
+ console.error(`Variable "${key}" not found.`);
684
+ process.exit(1);
685
+ }
686
+ delete vars[key];
687
+ await platform.updateEnvironmentVariables(ctx.projectId, ctx.environmentId, vars);
688
+ console.log(`Unset ${key}`);
689
+ break;
690
+ }
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);
700
+ if (entries.length === 0) {
701
+ console.log("No variables set.");
702
+ }
703
+ else {
704
+ for (const [k, v] of entries) {
705
+ console.log(`${k}=${v || "(empty)"}`);
706
+ }
707
+ }
708
+ break;
709
+ }
414
710
  default:
415
711
  console.error(`Unknown env command: ${action}`);
416
- console.error("Usage: ascendkit env list|use|promote");
712
+ console.error("Usage: ascendkit env list|use|update|promote|set-var|unset-var|list-vars");
417
713
  process.exit(1);
418
714
  }
419
715
  }
@@ -436,7 +732,14 @@ async function runAuth(client, action, rest) {
436
732
  }
437
733
  if (flags["session-duration"])
438
734
  params.sessionDuration = flags["session-duration"];
439
- output(await auth.updateSettings(client, params));
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}`);
440
743
  }
441
744
  else {
442
745
  output(await auth.getSettings(client));
@@ -924,12 +1227,19 @@ async function runJourney(client, action, rest) {
924
1227
  break;
925
1228
  case "add-node": {
926
1229
  if (!rest[0] || !flags.name) {
927
- console.error("Usage: ascendkit journey add-node <journey-id> --name <node-name> [--action <json>] [--terminal <true|false>]");
1230
+ console.error("Usage: ascendkit journey add-node <journey-id> --name <node-name> [--action <json>] [--email-id <email>] [--terminal <true|false>]");
928
1231
  process.exit(1);
929
1232
  }
930
1233
  const params = { name: flags.name };
931
1234
  if (flags.action)
932
1235
  params.action = JSON.parse(flags.action);
1236
+ if (flags["email-id"]) {
1237
+ if (!params.action || params.action.type !== "send_email") {
1238
+ console.error("--email-id requires a send_email action (use --action '{\"type\": \"send_email\", \"templateSlug\": \"...\"}')");
1239
+ process.exit(1);
1240
+ }
1241
+ params.action.fromIdentityEmail = flags["email-id"];
1242
+ }
933
1243
  if (flags.terminal)
934
1244
  params.terminal = flags.terminal === "true";
935
1245
  output(await journeys.addNode(client, rest[0], params));
@@ -937,12 +1247,28 @@ async function runJourney(client, action, rest) {
937
1247
  }
938
1248
  case "edit-node": {
939
1249
  if (!rest[0] || !rest[1]) {
940
- console.error("Usage: ascendkit journey edit-node <journey-id> <node-name> [--action <json>] [--terminal <true|false>]");
1250
+ console.error("Usage: ascendkit journey edit-node <journey-id> <node-name> [--action <json>] [--email-id <email>] [--terminal <true|false>]");
941
1251
  process.exit(1);
942
1252
  }
943
1253
  const params = {};
944
1254
  if (flags.action)
945
1255
  params.action = JSON.parse(flags.action);
1256
+ if (flags["email-id"]) {
1257
+ if (!params.action) {
1258
+ const nodesData = await journeys.listNodes(client, rest[0]);
1259
+ const current = nodesData.nodes?.find((n) => n.name === rest[1]);
1260
+ if (!current?.action || current.action.type !== "send_email") {
1261
+ console.error("--email-id can only be set on send_email nodes");
1262
+ process.exit(1);
1263
+ }
1264
+ params.action = current.action;
1265
+ }
1266
+ else if (params.action.type !== "send_email") {
1267
+ console.error("--email-id requires a send_email action");
1268
+ process.exit(1);
1269
+ }
1270
+ params.action.fromIdentityEmail = flags["email-id"];
1271
+ }
946
1272
  if (flags.terminal)
947
1273
  params.terminal = flags.terminal === "true";
948
1274
  output(await journeys.editNode(client, rest[0], rest[1], params));
@@ -1053,17 +1379,22 @@ async function runWebhook(client, action, rest) {
1053
1379
  }
1054
1380
  output(await webhooks.getWebhook(client, rest[0]));
1055
1381
  break;
1056
- case "update":
1382
+ case "update": {
1057
1383
  if (!rest[0]) {
1058
1384
  console.error("Usage: ascendkit webhook update <webhook-id> [--flags]");
1059
1385
  process.exit(1);
1060
1386
  }
1061
- output(await webhooks.updateWebhook(client, rest[0], {
1387
+ const updated = await webhooks.updateWebhook(client, rest[0], {
1062
1388
  url: flags.url,
1063
1389
  events: flags.events ? flags.events.split(",") : undefined,
1064
1390
  status: flags.status,
1065
- }));
1391
+ });
1392
+ console.log(`Updated: ${updated.id}`);
1393
+ console.log(`URL: ${updated.url}`);
1394
+ console.log(`Status: ${updated.status}`);
1395
+ console.log(`Events: ${Array.isArray(updated.events) ? updated.events.join(", ") : updated.events}`);
1066
1396
  break;
1397
+ }
1067
1398
  case "delete":
1068
1399
  if (!rest[0]) {
1069
1400
  console.error("Usage: ascendkit webhook delete <webhook-id>");
@@ -1084,6 +1415,115 @@ async function runWebhook(client, action, rest) {
1084
1415
  process.exit(1);
1085
1416
  }
1086
1417
  }
1418
+ async function runCampaign(client, action, rest) {
1419
+ const flags = parseFlags(rest);
1420
+ if (!action) {
1421
+ console.log(HELP_SECTION.campaign);
1422
+ return;
1423
+ }
1424
+ switch (action) {
1425
+ case "create":
1426
+ if (!flags.name || !flags.template || !flags.audience) {
1427
+ console.error("Usage: ascendkit campaign create --name <name> --template <template-id> --audience <json> [--scheduled-at <datetime>]");
1428
+ process.exit(1);
1429
+ }
1430
+ let createFilter;
1431
+ try {
1432
+ createFilter = JSON.parse(flags.audience);
1433
+ }
1434
+ catch {
1435
+ console.error("Invalid JSON for --audience flag. Provide a valid JSON object.");
1436
+ process.exit(1);
1437
+ }
1438
+ output(await campaigns.createCampaign(client, {
1439
+ name: flags.name,
1440
+ templateId: flags.template,
1441
+ audienceFilter: createFilter,
1442
+ scheduledAt: flags["scheduled-at"],
1443
+ }));
1444
+ break;
1445
+ case "list": {
1446
+ const items = await campaigns.listCampaigns(client, flags.status);
1447
+ table(items, [
1448
+ { key: "id", label: "ID" },
1449
+ { key: "name", label: "Name", width: 30 },
1450
+ { key: "status", label: "Status" },
1451
+ { key: "scheduledAt", label: "Scheduled", width: 22 },
1452
+ { key: "templateId", label: "Template" },
1453
+ ]);
1454
+ break;
1455
+ }
1456
+ case "show":
1457
+ case "get":
1458
+ if (!rest[0]) {
1459
+ console.error("Usage: ascendkit campaign show <campaign-id>");
1460
+ process.exit(1);
1461
+ }
1462
+ output(await campaigns.getCampaign(client, rest[0]));
1463
+ break;
1464
+ case "update":
1465
+ if (!rest[0]) {
1466
+ console.error("Usage: ascendkit campaign update <campaign-id> [--flags]");
1467
+ process.exit(1);
1468
+ }
1469
+ let updateFilter;
1470
+ if (flags.audience) {
1471
+ try {
1472
+ updateFilter = JSON.parse(flags.audience);
1473
+ }
1474
+ catch {
1475
+ console.error("Invalid JSON for --audience flag. Provide a valid JSON object.");
1476
+ process.exit(1);
1477
+ }
1478
+ }
1479
+ output(await campaigns.updateCampaign(client, rest[0], {
1480
+ name: flags.name,
1481
+ templateId: flags.template,
1482
+ audienceFilter: updateFilter,
1483
+ scheduledAt: flags["scheduled-at"],
1484
+ }));
1485
+ break;
1486
+ case "preview":
1487
+ if (!rest[0]) {
1488
+ console.error("Usage: ascendkit campaign preview <campaign-id>");
1489
+ process.exit(1);
1490
+ }
1491
+ {
1492
+ const detail = await campaigns.getCampaign(client, rest[0]);
1493
+ if (!detail?.audienceFilter) {
1494
+ console.error("Campaign has no audience filter set.");
1495
+ process.exit(1);
1496
+ }
1497
+ output(await campaigns.previewAudience(client, detail.audienceFilter));
1498
+ }
1499
+ break;
1500
+ case "schedule":
1501
+ if (!rest[0] || !flags.at) {
1502
+ console.error("Usage: ascendkit campaign schedule <campaign-id> --at <datetime>");
1503
+ process.exit(1);
1504
+ }
1505
+ output(await campaigns.updateCampaign(client, rest[0], { scheduledAt: flags.at }));
1506
+ break;
1507
+ case "cancel":
1508
+ if (!rest[0]) {
1509
+ console.error("Usage: ascendkit campaign cancel <campaign-id>");
1510
+ process.exit(1);
1511
+ }
1512
+ output(await campaigns.deleteCampaign(client, rest[0]));
1513
+ break;
1514
+ case "analytics":
1515
+ if (!rest[0]) {
1516
+ console.error("Usage: ascendkit campaign analytics <campaign-id>");
1517
+ process.exit(1);
1518
+ }
1519
+ output(await campaigns.getCampaignAnalytics(client, rest[0]));
1520
+ break;
1521
+ default:
1522
+ console.error(`Unknown campaign command: ${action}`);
1523
+ console.error('Run "ascendkit campaign --help" for usage');
1524
+ process.exit(1);
1525
+ }
1526
+ }
1087
1527
  async function runEmail(client, action, rest) {
1088
1528
  const flags = parseFlags(rest);
1089
1529
  switch (action) {
@@ -13,5 +13,9 @@ export declare function getSettings(client: AscendKitClient): Promise<unknown>;
13
13
  export declare function updateSettings(client: AscendKitClient, params: UpdateSettingsParams): Promise<unknown>;
14
14
  export declare function updateProviders(client: AscendKitClient, providers: string[]): Promise<unknown>;
15
15
  export declare function updateOAuthCredentials(client: AscendKitClient, provider: string, clientId: string, clientSecret: string, callbackUrl?: string): Promise<unknown>;
16
+ export declare function deleteOAuthCredentials(client: AscendKitClient, provider: string): Promise<unknown>;
16
17
  export declare function getOAuthSetupUrl(portalUrl: string, provider: string, publicKey?: string): string;
17
18
  export declare function listUsers(client: AscendKitClient): Promise<unknown>;
19
+ export declare function deleteUser(client: AscendKitClient, userId: string): Promise<unknown>;
20
+ export declare function bulkDeleteUsers(client: AscendKitClient, userIds: string[]): Promise<unknown>;
21
+ export declare function reactivateUser(client: AscendKitClient, userId: string): Promise<unknown>;
@@ -20,6 +20,9 @@ export async function updateOAuthCredentials(client, provider, clientId, clientS
20
20
  body.callbackUrl = callbackUrl;
21
21
  return client.managedPut(`/api/auth/settings/oauth/${provider}`, body);
22
22
  }
23
+ export async function deleteOAuthCredentials(client, provider) {
24
+ return client.managedDelete(`/api/auth/settings/oauth/${provider}`);
25
+ }
23
26
  export function getOAuthSetupUrl(portalUrl, provider, publicKey) {
24
27
  const base = `${portalUrl}/settings/oauth/${provider}`;
25
28
  return publicKey ? `${base}?pk=${encodeURIComponent(publicKey)}` : base;
@@ -27,3 +30,12 @@ export function getOAuthSetupUrl(portalUrl, provider, publicKey) {
27
30
  export async function listUsers(client) {
28
31
  return client.managedRequest("GET", "/api/users");
29
32
  }
33
+ export async function deleteUser(client, userId) {
34
+ return client.managedDelete(`/api/users/${userId}`);
35
+ }
36
+ export async function bulkDeleteUsers(client, userIds) {
37
+ return client.managedPost("/api/users/bulk-deactivate", { userIds });
38
+ }
39
+ export async function reactivateUser(client, userId) {
40
+ return client.managedPost(`/api/users/${userId}/reactivate`);
41
+ }
@@ -0,0 +1,22 @@
1
+ import { AscendKitClient } from "../api/client.js";
2
+ export interface CreateCampaignParams {
3
+ name: string;
4
+ templateId: string;
5
+ audienceFilter: Record<string, unknown>;
6
+ scheduledAt?: string;
7
+ fromIdentityEmail?: string;
8
+ }
9
+ export interface UpdateCampaignParams {
10
+ name?: string;
11
+ templateId?: string;
12
+ audienceFilter?: Record<string, unknown>;
13
+ scheduledAt?: string;
14
+ fromIdentityEmail?: string;
15
+ }
16
+ export declare function createCampaign(client: AscendKitClient, params: CreateCampaignParams): Promise<unknown>;
17
+ export declare function listCampaigns(client: AscendKitClient, status?: string, limit?: number): Promise<unknown>;
18
+ export declare function getCampaign(client: AscendKitClient, campaignId: string): Promise<unknown>;
19
+ export declare function updateCampaign(client: AscendKitClient, campaignId: string, params: UpdateCampaignParams): Promise<unknown>;
20
+ export declare function deleteCampaign(client: AscendKitClient, campaignId: string): Promise<unknown>;
21
+ export declare function previewAudience(client: AscendKitClient, audienceFilter: Record<string, unknown>): Promise<unknown>;
22
+ export declare function getCampaignAnalytics(client: AscendKitClient, campaignId: string): Promise<unknown>;