@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.
- package/dist/api/client.d.ts +1 -0
- package/dist/api/client.js +3 -0
- package/dist/cli.js +452 -12
- package/dist/commands/auth.d.ts +4 -0
- package/dist/commands/auth.js +12 -0
- package/dist/commands/campaigns.d.ts +22 -0
- package/dist/commands/campaigns.js +25 -0
- package/dist/commands/import.d.ts +75 -0
- package/dist/commands/import.js +97 -0
- package/dist/commands/journeys.d.ts +2 -0
- package/dist/commands/platform.d.ts +17 -1
- package/dist/commands/platform.js +34 -1
- package/dist/mcp.js +4 -0
- package/dist/tools/auth.js +38 -1
- package/dist/tools/campaigns.d.ts +3 -0
- package/dist/tools/campaigns.js +78 -0
- package/dist/tools/import.d.ts +3 -0
- package/dist/tools/import.js +116 -0
- package/dist/tools/journeys.js +5 -1
- package/dist/tools/platform.js +69 -1
- package/package.json +1 -1
- package/dist/index.d.ts +0 -2
- package/dist/index.js +0 -29
package/dist/api/client.d.ts
CHANGED
|
@@ -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>;
|
package/dist/api/client.js
CHANGED
|
@@ -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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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) {
|
package/dist/commands/auth.d.ts
CHANGED
|
@@ -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>;
|
package/dist/commands/auth.js
CHANGED
|
@@ -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>;
|