@audienti/cli 0.1.75 → 0.1.77
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/CHANGELOG.md +15 -0
- package/README.md +30 -3
- package/install +22 -1
- package/package.json +1 -1
- package/skills/audienti/SKILL.md +15 -2
- package/src/api-client.js +95 -0
- package/src/cli.js +1128 -39
package/src/cli.js
CHANGED
|
@@ -22,6 +22,12 @@ const API_MAX_LIST_LIMIT = 100;
|
|
|
22
22
|
const DEFAULT_LOOKUP_TIMEOUT_SECONDS = 60;
|
|
23
23
|
const DEFAULT_LOOKUP_POLL_INTERVAL_SECONDS = 2;
|
|
24
24
|
const DEFAULT_AUTH_LOGIN_TIMEOUT_SECONDS = 180;
|
|
25
|
+
// Start covers signup plus email confirmation, which takes longer than a sign-in.
|
|
26
|
+
const DEFAULT_START_LOGIN_TIMEOUT_SECONDS = 900;
|
|
27
|
+
const DEFAULT_SETUP_TIMEOUT_SECONDS = 300;
|
|
28
|
+
const START_USAGE = "Usage: audienti start [--host https://app.audienti.com] [--no-open] [--json]";
|
|
29
|
+
const SETUP_USAGE = "Usage: audienti setup [--url <company_url>] [--sell-to <who you sell to>] [--ask <what the prospect says yes to>] [--yes] [--json]";
|
|
30
|
+
const SETUP_FLAGS_EXAMPLE = "audienti setup --url https://acme.com --sell-to \"<who you sell to>\" --ask \"<what they say yes to>\" --yes";
|
|
25
31
|
const DEFAULT_WRITER_TEST_RUN_TIMEOUT_SECONDS = 180;
|
|
26
32
|
const DEFAULT_WRITER_TEST_RUN_POLL_INTERVAL_SECONDS = 2;
|
|
27
33
|
const PACKAGE_NAME = "@audienti/cli";
|
|
@@ -207,6 +213,21 @@ const TOOLS_LINKEDIN_REVIEW_USAGE = "Usage: audienti tools linkedin-review --url
|
|
|
207
213
|
const TOOLS_LINKEDIN_REVIEW_REPORTS_USAGE = "Usage: audienti tools linkedin-review reports [--limit <n>] [--json] [--account <acct_id>]";
|
|
208
214
|
const TOOLS_LINKEDIN_REVIEW_SHOW_USAGE = "Usage: audienti tools linkedin-review show <rprt_id> [--json] [--account <acct_id>]";
|
|
209
215
|
const TOOLS_LINKEDIN_REVIEW_STATUS_USAGE = "Usage: audienti tools linkedin-review status <rprt_id> [--json] [--account <acct_id>]";
|
|
216
|
+
const TOOLS_LINKEDIN_STRATEGY_REVIEW_DELETE_USAGE = "Usage: audienti tools linkedin-strategy-review delete <rprt_id> --confirm <yes|true|Y|y> [--json] [--account <acct_id>]";
|
|
217
|
+
const LISTS_BULK_ADD_TAG_USAGE = "Usage: audienti lists bulk-add-tag --tag <tag> <list_id> [list_id...] [--json] [--account <acct_id>]";
|
|
218
|
+
const LISTS_MERGE_USAGE = "Usage: audienti lists merge <list_id> <list_id> [--json] [--account <acct_id>]";
|
|
219
|
+
const LISTS_EXPORT_USAGE = "Usage: audienti lists export <list_id> [--output <file.csv>] [--inactive-reason <reason>] [--json] [--account <acct_id>]";
|
|
220
|
+
const LIST_ROUTING_RULES_TOGGLE_USAGE = "Usage: audienti lists routing-rules <list_id> toggle <rule_id> [--json] [--account <acct_id>]";
|
|
221
|
+
const ICPS_BULK_ADD_TAG_USAGE = "Usage: audienti icps bulk-add-tag --tag <tag> <icp_id> [icp_id...] [--json] [--account <acct_id>]";
|
|
222
|
+
const ICPS_CLONE_USAGE = "Usage: audienti icps clone <icp_id> [--json] [--account <acct_id>]";
|
|
223
|
+
const ICPS_DELETE_USAGE = "Usage: audienti icps delete <icp_id> --confirm <yes|true|Y|y> [--json] [--account <acct_id>]";
|
|
224
|
+
const ICPS_PROSPECTS_USAGE = "Usage: audienti icps prospects <icp_id> [--query <text>] [--limit <n>] [--offset <n> | --page <n>] [--profiles] [--json] [--account <acct_id>]";
|
|
225
|
+
const OFFERS_REGENERATE_RESEARCH_USAGE = "Usage: audienti offers regenerate-research <offr_id> [--guidance <text>] [--json] [--account <acct_id>]";
|
|
226
|
+
const OFFERS_UPDATE_WRITEUP_USAGE = "Usage: audienti offers update-writeup <offr_id> --description <text> [--json] [--account <acct_id>]";
|
|
227
|
+
const OFFERS_ADD_ARTIFACTS_USAGE = "Usage: audienti offers add-artifacts <offr_id> <file> [file...] [--json] [--account <acct_id>]";
|
|
228
|
+
const OFFERS_REMOVE_ARTIFACT_USAGE = "Usage: audienti offers remove-artifact <offr_id> <artifact_id> [--json] [--account <acct_id>]";
|
|
229
|
+
const TASKS_UPDATE_USAGE = "Usage: audienti tasks update <ptsk_id> [--title <text>] [--due <time>] [--notes <text>] [--prospect <prsp_id|\"\">] [--list <list_id|\"\">] [--assigned-user <id|me>] [--json] [--account <acct_id>]";
|
|
230
|
+
const TASKS_BULK_UPDATE_USAGE = "Usage: audienti tasks bulk-update --action <complete|reassign> [--assigned-user <id|me>] <ptsk_id> [ptsk_id...] [--json] [--account <acct_id>]";
|
|
210
231
|
const SOCIAL_COOKIES_SYNC_MESSAGES_USAGE = "Usage: audienti social-cookies sync-messages <scok_id> [--folder <folder>] [--retry] [--json] [--account <acct_id>]";
|
|
211
232
|
const COHORT_STAGE_ORDER = [
|
|
212
233
|
"identified",
|
|
@@ -272,6 +293,7 @@ export async function run(argv = process.argv.slice(2), deps = {}) {
|
|
|
272
293
|
|
|
273
294
|
async function dispatch(argv, context) {
|
|
274
295
|
const { args, accountOverride } = extractGlobalOptions(argv);
|
|
296
|
+
if (args.length === 0 && !(await readConfig({ env: context.env })).token) return start([], context);
|
|
275
297
|
const helpTopic = helpTopicFromArgs(args);
|
|
276
298
|
|
|
277
299
|
if (helpTopic) {
|
|
@@ -302,6 +324,8 @@ async function dispatch(argv, context) {
|
|
|
302
324
|
if (normalizedResource === "users" && action === "select") return usersSelect(rest, context, { accountOverride });
|
|
303
325
|
if (normalizedResource === "users" && action === "activity") return usersActivity(rest, context, { accountOverride });
|
|
304
326
|
if (normalizedResource === "users" && action === "automation") return usersAutomation(rest, context, { accountOverride });
|
|
327
|
+
if (normalizedResource === "start") return start(args.slice(1), context);
|
|
328
|
+
if (normalizedResource === "setup" && (action === undefined || action.startsWith("--"))) return setupWizard(args.slice(1), context, { accountOverride });
|
|
305
329
|
if (normalizedResource === "setup" && action === "play" && rest[0] === "preflight") return setupPlayPreflight(rest.slice(1), context, { accountOverride });
|
|
306
330
|
if (normalizedResource === "social-cookies" && action === "sync-messages") return socialCookiesSyncMessages(rest, context, { accountOverride });
|
|
307
331
|
if (normalizedResource === "offers" && action === "list") return offersList(rest, context, { accountOverride });
|
|
@@ -309,6 +333,10 @@ async function dispatch(argv, context) {
|
|
|
309
333
|
if (normalizedResource === "offers" && action === "create") return offersCreate(rest, context, { accountOverride });
|
|
310
334
|
if (normalizedResource === "offers" && action === "update") return offersUpdate(rest, context, { accountOverride });
|
|
311
335
|
if (normalizedResource === "offers" && action === "delete") return offersDelete(rest, context, { accountOverride });
|
|
336
|
+
if (normalizedResource === "offers" && action === "regenerate-research") return offersRegenerateResearch(rest, context, { accountOverride });
|
|
337
|
+
if (normalizedResource === "offers" && action === "update-writeup") return offersUpdateWriteup(rest, context, { accountOverride });
|
|
338
|
+
if (normalizedResource === "offers" && action === "add-artifacts") return offersAddArtifacts(rest, context, { accountOverride });
|
|
339
|
+
if (normalizedResource === "offers" && action === "remove-artifact") return offersRemoveArtifact(rest, context, { accountOverride });
|
|
312
340
|
if (normalizedResource === "icps" && action === "list") return icpsList(rest, context, { accountOverride });
|
|
313
341
|
if (normalizedResource === "icps" && action === "show") return icpsShow(rest, context, { accountOverride });
|
|
314
342
|
if (normalizedResource === "icps" && action === "analytics") return analyticsIcps(rest, context, { accountOverride });
|
|
@@ -318,6 +346,10 @@ async function dispatch(argv, context) {
|
|
|
318
346
|
if (normalizedResource === "icps" && action === "restore") return icpsLifecycleMutation("restore", rest, context, { accountOverride });
|
|
319
347
|
if (normalizedResource === "icps" && action === "add-tag") return icpsTagMutation("add", rest, context, { accountOverride });
|
|
320
348
|
if (normalizedResource === "icps" && action === "remove-tag") return icpsTagMutation("remove", rest, context, { accountOverride });
|
|
349
|
+
if (normalizedResource === "icps" && action === "bulk-add-tag") return icpsBulkAddTag(rest, context, { accountOverride });
|
|
350
|
+
if (normalizedResource === "icps" && action === "clone") return icpsClone(rest, context, { accountOverride });
|
|
351
|
+
if (normalizedResource === "icps" && action === "delete") return icpsDelete(rest, context, { accountOverride });
|
|
352
|
+
if (normalizedResource === "icps" && action === "prospects") return icpsProspects(rest, context, { accountOverride });
|
|
321
353
|
if (normalizedResource === "companies" && action === "search") return companiesSearch(rest, context, { accountOverride });
|
|
322
354
|
if (normalizedResource === "dnc" && action === "list") return dncList(rest, context, { accountOverride });
|
|
323
355
|
if (normalizedResource === "dnc" && action === "add") return dncAdd(rest, context, { accountOverride });
|
|
@@ -339,6 +371,8 @@ async function dispatch(argv, context) {
|
|
|
339
371
|
if (normalizedResource === "tasks" && ["list", "manage"].includes(action)) return tasksList(rest, context, { accountOverride });
|
|
340
372
|
if (normalizedResource === "tasks" && action === "add") return tasksAdd(rest, context, { accountOverride });
|
|
341
373
|
if (normalizedResource === "tasks" && action === "complete") return tasksComplete(rest, context, { accountOverride });
|
|
374
|
+
if (normalizedResource === "tasks" && action === "update") return tasksUpdate(rest, context, { accountOverride });
|
|
375
|
+
if (normalizedResource === "tasks" && action === "bulk-update") return tasksBulkUpdate(rest, context, { accountOverride });
|
|
342
376
|
if (normalizedResource === "lists" && action === "list") return listsList(rest, context, { accountOverride });
|
|
343
377
|
if (normalizedResource === "lists" && action === "create") return listsCreate(rest, context, { accountOverride });
|
|
344
378
|
if (normalizedResource === "lists" && action === "show") return listsShow(rest, context, { accountOverride });
|
|
@@ -346,6 +380,9 @@ async function dispatch(argv, context) {
|
|
|
346
380
|
if (normalizedResource === "lists" && action === "add-tag") return listsTagMutation("add", rest, context, { accountOverride });
|
|
347
381
|
if (normalizedResource === "lists" && action === "remove-tag") return listsTagMutation("remove", rest, context, { accountOverride });
|
|
348
382
|
if (normalizedResource === "lists" && action === "delete") return listsDelete(rest, context, { accountOverride });
|
|
383
|
+
if (normalizedResource === "lists" && action === "bulk-add-tag") return listsBulkAddTag(rest, context, { accountOverride });
|
|
384
|
+
if (normalizedResource === "lists" && action === "merge") return listsMerge(rest, context, { accountOverride });
|
|
385
|
+
if (normalizedResource === "lists" && action === "export") return listsExport(rest, context, { accountOverride });
|
|
349
386
|
if (normalizedResource === "lists" && action === "prospects") return listProspects(rest, context, { accountOverride });
|
|
350
387
|
if (normalizedResource === "lists" && action === "add-prospects") return listsAddProspects(rest, context, { accountOverride });
|
|
351
388
|
if (normalizedResource === "lists" && action === "remove-prospects") return listsRemoveProspects(rest, context, { accountOverride });
|
|
@@ -409,6 +446,7 @@ async function dispatch(argv, context) {
|
|
|
409
446
|
if (normalizedResource === "tools" && action === "get") return toolsGet(rest, context, { accountOverride });
|
|
410
447
|
if (normalizedResource === "tools" && action === "humanize") return toolsHumanize(rest, context, { accountOverride });
|
|
411
448
|
if (normalizedResource === "tools" && action === "linkedin-review") return toolsLinkedinReview(rest, context, { accountOverride });
|
|
449
|
+
if (normalizedResource === "tools" && action === "linkedin-strategy-review") return toolsLinkedinStrategyReview(rest, context, { accountOverride });
|
|
412
450
|
if (normalizedResource === "operator" && action === "failed-drafts") return operatorFailedDrafts(rest, context, { accountOverride });
|
|
413
451
|
if (normalizedResource === "operator" && action === "queue") return operatorQueue(rest, context, { accountOverride });
|
|
414
452
|
if (normalizedResource === "operator" && action === "next") return operatorNext(rest, context, { accountOverride });
|
|
@@ -567,25 +605,50 @@ async function authLogin(args, context) {
|
|
|
567
605
|
|
|
568
606
|
const host = normalizeHost(values.host || DEFAULT_HOST);
|
|
569
607
|
const timeoutSeconds = normalizeOptionalPositiveInteger(values["timeout-seconds"], "--timeout-seconds") || DEFAULT_AUTH_LOGIN_TIMEOUT_SECONDS;
|
|
608
|
+
const { client, user, payload } = await browserSignIn({
|
|
609
|
+
host,
|
|
610
|
+
timeoutSeconds,
|
|
611
|
+
open: !values["no-open"],
|
|
612
|
+
context,
|
|
613
|
+
announce(authUrl, callbackUrl) {
|
|
614
|
+
if (values.json) {
|
|
615
|
+
writeJson(context.stdout, {
|
|
616
|
+
kind: "auth_login_start",
|
|
617
|
+
auth_url: authUrl,
|
|
618
|
+
callback_url: callbackUrl,
|
|
619
|
+
timeout_seconds: timeoutSeconds
|
|
620
|
+
});
|
|
621
|
+
} else {
|
|
622
|
+
writeLine(context.stdout, "Open this URL to authenticate Audienti CLI:");
|
|
623
|
+
writeLine(context.stdout, authUrl);
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
});
|
|
627
|
+
|
|
628
|
+
const result = {
|
|
629
|
+
kind: "auth_login_complete",
|
|
630
|
+
host: client.host,
|
|
631
|
+
user: user?.name || payload.user_name || payload.user_email || user?.email || null,
|
|
632
|
+
account_id: payload.account_id || null,
|
|
633
|
+
account_name: payload.account_name || null
|
|
634
|
+
};
|
|
635
|
+
|
|
636
|
+
if (values.json) return writeJson(context.stdout, result);
|
|
637
|
+
|
|
638
|
+
writeLine(context.stdout, `Authenticated to ${client.host} as ${result.user || "current user"}.`);
|
|
639
|
+
if (result.account_id) writeLine(context.stdout, `Selected account ${result.account_name || result.account_id} (${result.account_id}).`);
|
|
640
|
+
if (!result.account_id) writeLine(context.stdout, "Run `audienti accounts list` to choose an account.");
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
async function browserSignIn({ host, timeoutSeconds, open, context, announce }) {
|
|
570
644
|
const state = randomBytes(24).toString("hex");
|
|
571
645
|
const callback = await createAuthCallbackServer({ state, context });
|
|
572
646
|
const authUrl = new URL("/cli/auth", host);
|
|
573
647
|
authUrl.searchParams.set("redirect_uri", callback.redirectUri);
|
|
574
648
|
authUrl.searchParams.set("state", state);
|
|
575
649
|
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
kind: "auth_login_start",
|
|
579
|
-
auth_url: authUrl.toString(),
|
|
580
|
-
callback_url: callback.redirectUri,
|
|
581
|
-
timeout_seconds: timeoutSeconds
|
|
582
|
-
});
|
|
583
|
-
} else {
|
|
584
|
-
writeLine(context.stdout, "Open this URL to authenticate Audienti CLI:");
|
|
585
|
-
writeLine(context.stdout, authUrl.toString());
|
|
586
|
-
}
|
|
587
|
-
|
|
588
|
-
if (!values["no-open"]) openBrowser(authUrl.toString(), context);
|
|
650
|
+
announce(authUrl.toString(), callback.redirectUri);
|
|
651
|
+
if (open) openBrowser(authUrl.toString(), context);
|
|
589
652
|
|
|
590
653
|
let payload;
|
|
591
654
|
try {
|
|
@@ -603,19 +666,473 @@ async function authLogin(args, context) {
|
|
|
603
666
|
accountName: payload.account_name || undefined
|
|
604
667
|
}, { env: context.env });
|
|
605
668
|
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
669
|
+
return { client, user, payload };
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
async function start(args, context) {
|
|
673
|
+
const { values, positionals } = parseCommandArgs(args, {
|
|
674
|
+
...jsonOptions(),
|
|
675
|
+
host: { type: "string" },
|
|
676
|
+
"no-open": { type: "boolean" }
|
|
677
|
+
});
|
|
678
|
+
if (positionals.length > 0) throw new CommandError(START_USAGE);
|
|
679
|
+
|
|
680
|
+
const interactive = stdinIsInteractive(context) && !values.json;
|
|
681
|
+
const say = (line = "") => {
|
|
682
|
+
if (!values.json) writeLine(context.stdout, line);
|
|
612
683
|
};
|
|
684
|
+
let config = await readConfig({ env: context.env });
|
|
685
|
+
const host = normalizeHost(values.host || config.host || DEFAULT_HOST);
|
|
686
|
+
|
|
687
|
+
say("Welcome to Audienti.");
|
|
688
|
+
say("Audienti finds the people who fit your business, spots when they're ready to talk, and drafts the outreach.");
|
|
689
|
+
say("Nothing sends without your approval.");
|
|
690
|
+
say();
|
|
691
|
+
|
|
692
|
+
let session = await savedSession(config, context);
|
|
693
|
+
if (!session && !interactive) {
|
|
694
|
+
const payload = {
|
|
695
|
+
kind: "start",
|
|
696
|
+
status: "sign_in_required",
|
|
697
|
+
signed_in: false,
|
|
698
|
+
host,
|
|
699
|
+
sign_up_url: new URL("/users/sign_up", host).toString(),
|
|
700
|
+
sign_in_command: "audienti auth login",
|
|
701
|
+
next_command: "audienti start"
|
|
702
|
+
};
|
|
703
|
+
if (values.json) return writeJson(context.stdout, payload);
|
|
613
704
|
|
|
614
|
-
|
|
705
|
+
say("Sign in or create an account to continue.");
|
|
706
|
+
say(` New here? Create an account at ${payload.sign_up_url}`);
|
|
707
|
+
say(" Then run `audienti auth login` in a terminal (it opens your browser),");
|
|
708
|
+
say(" or save an API token with `audienti auth token <token>`.");
|
|
709
|
+
say("Next: run `audienti start` again.");
|
|
710
|
+
return 0;
|
|
711
|
+
}
|
|
615
712
|
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
713
|
+
const prompter = createPrompter(context);
|
|
714
|
+
try {
|
|
715
|
+
if (session) {
|
|
716
|
+
say(`You're signed in as ${session.label}.`);
|
|
717
|
+
} else {
|
|
718
|
+
say("First, sign in or create an account.");
|
|
719
|
+
const { client, user, payload } = await browserSignIn({
|
|
720
|
+
host,
|
|
721
|
+
timeoutSeconds: DEFAULT_START_LOGIN_TIMEOUT_SECONDS,
|
|
722
|
+
open: !values["no-open"],
|
|
723
|
+
context,
|
|
724
|
+
announce(authUrl) {
|
|
725
|
+
say("We'll open Audienti in your browser. New here? Choose \"sign up for an account\" on that page.");
|
|
726
|
+
say("If the browser doesn't open, visit:");
|
|
727
|
+
say(` ${authUrl}`);
|
|
728
|
+
say("Waiting for you to finish in the browser (confirm your email if asked)...");
|
|
729
|
+
}
|
|
730
|
+
});
|
|
731
|
+
config = await readConfig({ env: context.env });
|
|
732
|
+
session = { client, label: user?.name || payload.user_name || user?.email || payload.user_email || "you" };
|
|
733
|
+
say(`Signed in as ${session.label}.`);
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
const account = await chooseStartAccount(session.client, config, { interactive, prompter, say });
|
|
737
|
+
const accountUser = account ? await chooseStartAccountUser(session.client, account, { interactive, prompter, say }) : null;
|
|
738
|
+
if (account) {
|
|
739
|
+
const sameAccount = account.prefix_id === config.accountId;
|
|
740
|
+
await writeConfig({
|
|
741
|
+
...config,
|
|
742
|
+
accountId: account.prefix_id,
|
|
743
|
+
accountName: account.name,
|
|
744
|
+
accountUserId: accountUser ? String(accountUser.id) : sameAccount ? config.accountUserId : undefined,
|
|
745
|
+
accountUserName: accountUser ? accountUser.name : sameAccount ? config.accountUserName : undefined,
|
|
746
|
+
accountUserEmail: accountUser ? accountUser.email : sameAccount ? config.accountUserEmail : undefined
|
|
747
|
+
}, { env: context.env });
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
const status = !account ? "account_selection_required" : !accountUser ? "account_user_selection_required" : "ready";
|
|
751
|
+
const nextCommand = status === "account_selection_required"
|
|
752
|
+
? "audienti accounts select <acct_id>"
|
|
753
|
+
: status === "account_user_selection_required"
|
|
754
|
+
? "audienti users select <account_user_id|email|name|me>"
|
|
755
|
+
: interactive ? "audienti setup" : SETUP_FLAGS_EXAMPLE;
|
|
756
|
+
|
|
757
|
+
if (values.json) {
|
|
758
|
+
return writeJson(context.stdout, {
|
|
759
|
+
kind: "start",
|
|
760
|
+
status,
|
|
761
|
+
signed_in: true,
|
|
762
|
+
host: session.client.host,
|
|
763
|
+
user: session.label,
|
|
764
|
+
account: account ? { id: account.prefix_id, name: account.name } : null,
|
|
765
|
+
account_user: accountUser ? { id: accountUser.id, name: accountUser.name || null, email: accountUser.email || null } : null,
|
|
766
|
+
next_command: nextCommand
|
|
767
|
+
});
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
if (status !== "ready" || !interactive) {
|
|
771
|
+
say(`Next: ${nextCommand}`);
|
|
772
|
+
return 0;
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
say();
|
|
776
|
+
if (await askYesNo(prompter, "Set up your first motion now? [Y/n] ", { defaultYes: true })) {
|
|
777
|
+
say();
|
|
778
|
+
return setupWizard([], context, { prompter });
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
say("No problem. When you're ready, run: audienti setup");
|
|
782
|
+
return 0;
|
|
783
|
+
} finally {
|
|
784
|
+
prompter.close();
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
async function savedSession(config, context) {
|
|
789
|
+
if (!config.token) return null;
|
|
790
|
+
|
|
791
|
+
const client = clientFromConfig(config, context);
|
|
792
|
+
try {
|
|
793
|
+
const user = await client.me();
|
|
794
|
+
return { client, label: user?.name || user?.email || "you" };
|
|
795
|
+
} catch (error) {
|
|
796
|
+
if (error instanceof ApiError && error.status === 401) return null;
|
|
797
|
+
throw error;
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
async function chooseStartAccount(client, config, { interactive, prompter, say }) {
|
|
802
|
+
const accounts = await client.accounts();
|
|
803
|
+
if (!Array.isArray(accounts) || accounts.length === 0) {
|
|
804
|
+
throw new CommandError("This login has no Audienti account yet. Finish creating one in the browser, then run `audienti start` again.");
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
const saved = accounts.find((account) => account.prefix_id === config.accountId);
|
|
808
|
+
if (accounts.length === 1) {
|
|
809
|
+
say(`Using account ${accounts[0].name} (${accounts[0].prefix_id}).`);
|
|
810
|
+
return accounts[0];
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
if (!interactive) {
|
|
814
|
+
if (saved) {
|
|
815
|
+
say(`Using account ${saved.name} (${saved.prefix_id}).`);
|
|
816
|
+
return saved;
|
|
817
|
+
}
|
|
818
|
+
say("You have several accounts:");
|
|
819
|
+
for (const account of accounts) say(` ${account.prefix_id}\t${account.name}`);
|
|
820
|
+
return null;
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
say("Which account should the CLI use?");
|
|
824
|
+
accounts.forEach((account, index) => {
|
|
825
|
+
say(` ${index + 1}. ${account.name} (${account.prefix_id})${account === saved ? " - current" : ""}`);
|
|
826
|
+
});
|
|
827
|
+
const defaultIndex = saved ? accounts.indexOf(saved) + 1 : null;
|
|
828
|
+
const account = await askChoice(prompter, accounts, {
|
|
829
|
+
question: defaultIndex ? `Account [${defaultIndex}]: ` : "Account: ",
|
|
830
|
+
defaultChoice: saved,
|
|
831
|
+
resolve: (answer) => resolveAccountSelection(accounts, answer)
|
|
832
|
+
});
|
|
833
|
+
say(`Using account ${account.name} (${account.prefix_id}).`);
|
|
834
|
+
return account;
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
async function chooseStartAccountUser(client, account, { interactive, prompter, say }) {
|
|
838
|
+
const users = await client.users(account.prefix_id);
|
|
839
|
+
const rows = Array.isArray(users) ? users : [];
|
|
840
|
+
const currentUsers = rows.filter((candidate) => candidate.current);
|
|
841
|
+
const automatic = currentUsers.length === 1 ? currentUsers[0] : rows.length === 1 ? rows[0] : null;
|
|
842
|
+
if (automatic) {
|
|
843
|
+
say(`Working as ${accountUserLabel(automatic)}.`);
|
|
844
|
+
return automatic;
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
if (!interactive || rows.length === 0) {
|
|
848
|
+
say("Pick which account user you are:");
|
|
849
|
+
for (const accountUser of rows) say(` ${accountUserLabel(accountUser)}`);
|
|
850
|
+
return null;
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
say("Which account user are you?");
|
|
854
|
+
rows.forEach((accountUser, index) => say(` ${index + 1}. ${accountUserLabel(accountUser)}`));
|
|
855
|
+
const accountUser = await askChoice(prompter, rows, {
|
|
856
|
+
question: "Account user: ",
|
|
857
|
+
resolve: (answer) => resolveAccountUserSelection(rows, answer)
|
|
858
|
+
});
|
|
859
|
+
say(`Working as ${accountUserLabel(accountUser)}.`);
|
|
860
|
+
return accountUser;
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
async function askChoice(prompter, choices, { question, defaultChoice = null, resolve }) {
|
|
864
|
+
for (;;) {
|
|
865
|
+
const answer = await prompter.ask(question);
|
|
866
|
+
if (!answer && defaultChoice) return defaultChoice;
|
|
867
|
+
|
|
868
|
+
const number = Number(answer);
|
|
869
|
+
if (Number.isInteger(number) && number >= 1 && number <= choices.length) return choices[number - 1];
|
|
870
|
+
|
|
871
|
+
try {
|
|
872
|
+
const match = answer ? resolve(answer) : null;
|
|
873
|
+
if (match) return match;
|
|
874
|
+
} catch (error) {
|
|
875
|
+
if (!(error instanceof CommandError)) throw error;
|
|
876
|
+
}
|
|
877
|
+
prompter.say(`Type a number from 1 to ${choices.length}.`);
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
async function askYesNo(prompter, question, { defaultYes = false } = {}) {
|
|
882
|
+
for (;;) {
|
|
883
|
+
const answer = (await prompter.ask(question)).toLowerCase();
|
|
884
|
+
if (!answer) return defaultYes;
|
|
885
|
+
if (/^y(es)?$/.test(answer)) return true;
|
|
886
|
+
if (/^no?$/.test(answer)) return false;
|
|
887
|
+
prompter.say("Please answer y or n.");
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
function stdinIsInteractive(context) {
|
|
892
|
+
return Boolean(context.stdin && context.stdin.isTTY);
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
// One line reader per command run. The async iterator buffers lines typed
|
|
896
|
+
// ahead of a question, so several questions can share one input stream.
|
|
897
|
+
function createPrompter(context) {
|
|
898
|
+
let reader;
|
|
899
|
+
let lines;
|
|
900
|
+
|
|
901
|
+
return {
|
|
902
|
+
async ask(question) {
|
|
903
|
+
if (!lines) {
|
|
904
|
+
reader = createInterface({ input: context.stdin, terminal: false });
|
|
905
|
+
lines = reader[Symbol.asyncIterator]();
|
|
906
|
+
}
|
|
907
|
+
context.stdout.write(question);
|
|
908
|
+
const { value, done } = await lines.next();
|
|
909
|
+
if (done) throw new CommandError("Input ended before the questions were answered. Run the command again when you're ready.");
|
|
910
|
+
return String(value).trim();
|
|
911
|
+
},
|
|
912
|
+
say(line = "") {
|
|
913
|
+
writeLine(context.stdout, line);
|
|
914
|
+
},
|
|
915
|
+
close() {
|
|
916
|
+
reader?.close();
|
|
917
|
+
}
|
|
918
|
+
};
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
const SETUP_QUESTIONS = [
|
|
922
|
+
{
|
|
923
|
+
key: "url",
|
|
924
|
+
title: "What's your company website?",
|
|
925
|
+
good: "https://acme.com",
|
|
926
|
+
bad: "acme (not a full web address)",
|
|
927
|
+
skip: "Press Enter to use the website saved on your account."
|
|
928
|
+
},
|
|
929
|
+
{
|
|
930
|
+
key: "sell-to",
|
|
931
|
+
title: "Who do you sell to? One sentence.",
|
|
932
|
+
good: "Heads of finance at US software companies with 50-500 people.",
|
|
933
|
+
bad: "Everyone who could use our product.",
|
|
934
|
+
skip: "Press Enter to skip."
|
|
935
|
+
},
|
|
936
|
+
{
|
|
937
|
+
key: "ask",
|
|
938
|
+
title: "What do you want the prospect to say yes to?",
|
|
939
|
+
good: "A 20-minute call to see if our audit fits their month-end close.",
|
|
940
|
+
bad: "Buy now.",
|
|
941
|
+
skip: "Press Enter to skip."
|
|
942
|
+
}
|
|
943
|
+
];
|
|
944
|
+
|
|
945
|
+
async function setupWizard(args, context, { accountOverride, prompter: sharedPrompter } = {}) {
|
|
946
|
+
const { values, positionals } = parseCommandArgs(args, {
|
|
947
|
+
...jsonOptions(),
|
|
948
|
+
url: { type: "string" },
|
|
949
|
+
"sell-to": { type: "string" },
|
|
950
|
+
ask: { type: "string" },
|
|
951
|
+
yes: { type: "boolean" }
|
|
952
|
+
});
|
|
953
|
+
if (positionals.length > 0) throw new CommandError(SETUP_USAGE);
|
|
954
|
+
|
|
955
|
+
const interactive = stdinIsInteractive(context) && !values.json;
|
|
956
|
+
const say = (line = "") => {
|
|
957
|
+
if (!values.json) writeLine(context.stdout, line);
|
|
958
|
+
};
|
|
959
|
+
|
|
960
|
+
if (!interactive && !values.url) {
|
|
961
|
+
throw new CommandError([
|
|
962
|
+
"No terminal is attached, so `audienti setup` can't ask questions.",
|
|
963
|
+
"Pass the answers as flags instead:",
|
|
964
|
+
` ${SETUP_FLAGS_EXAMPLE}`,
|
|
965
|
+
SETUP_USAGE
|
|
966
|
+
].join("\n"));
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
const config = await readConfig({ env: context.env });
|
|
970
|
+
if (!config.token) {
|
|
971
|
+
throw new CommandError("You're not signed in yet. Run `audienti start` to sign in or create an account, then run `audienti setup`.");
|
|
972
|
+
}
|
|
973
|
+
const accountId = accountOverride || config.accountId;
|
|
974
|
+
if (!accountId) {
|
|
975
|
+
throw new CommandError("No account is picked yet. Run `audienti start` to choose one, then run `audienti setup`.");
|
|
976
|
+
}
|
|
977
|
+
const client = clientFromConfig(config, context);
|
|
978
|
+
const prompter = sharedPrompter || createPrompter(context);
|
|
979
|
+
|
|
980
|
+
try {
|
|
981
|
+
const answers = { url: values.url, "sell-to": values["sell-to"], ask: values.ask };
|
|
982
|
+
if (interactive) {
|
|
983
|
+
say("Let's set up your first motion. Three quick questions.");
|
|
984
|
+
for (const [index, question] of SETUP_QUESTIONS.entries()) {
|
|
985
|
+
if (answers[question.key] !== undefined) continue;
|
|
986
|
+
|
|
987
|
+
say();
|
|
988
|
+
say(`${index + 1}/${SETUP_QUESTIONS.length} ${question.title}`);
|
|
989
|
+
say(` Good: ${question.good}`);
|
|
990
|
+
say(` Bad: ${question.bad}`);
|
|
991
|
+
say(` ${question.skip}`);
|
|
992
|
+
answers[question.key] = await prompter.ask("> ");
|
|
993
|
+
}
|
|
994
|
+
say();
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
const sellTo = cleanSentence(answers["sell-to"]);
|
|
998
|
+
const ask = cleanSentence(answers.ask);
|
|
999
|
+
const requestBody = {
|
|
1000
|
+
quick_start: compactObject({
|
|
1001
|
+
company_url: String(answers.url || "").trim() || undefined,
|
|
1002
|
+
feedback: setupFeedback({ sellTo, ask })
|
|
1003
|
+
})
|
|
1004
|
+
};
|
|
1005
|
+
|
|
1006
|
+
say("Reading your website and drafting your first motion. This usually takes a minute or two.");
|
|
1007
|
+
let draft;
|
|
1008
|
+
try {
|
|
1009
|
+
draft = await client.createQuickStart(accountId, requestBody);
|
|
1010
|
+
} catch (error) {
|
|
1011
|
+
throw setupCreateError(error);
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
let polls = 0;
|
|
1015
|
+
draft = await waitForQuickStartDraft(client, accountId, draft, context, {
|
|
1016
|
+
timeoutSeconds: DEFAULT_SETUP_TIMEOUT_SECONDS,
|
|
1017
|
+
pollIntervalSeconds: DEFAULT_LOOKUP_POLL_INTERVAL_SECONDS,
|
|
1018
|
+
onPoll() {
|
|
1019
|
+
polls += 1;
|
|
1020
|
+
if (polls % 5 === 1) say(SETUP_PROGRESS[Math.min(Math.floor(polls / 5), SETUP_PROGRESS.length - 1)]);
|
|
1021
|
+
}
|
|
1022
|
+
});
|
|
1023
|
+
|
|
1024
|
+
if (draft?.status === "failed") throw setupDraftFailedError(draft?.error);
|
|
1025
|
+
if (!quickStartReady(draft)) {
|
|
1026
|
+
throw new CommandError("The draft is taking longer than usual. Nothing was created. Run `audienti setup` again in a few minutes.");
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
renderSetupDraft(draft, { ask }, say);
|
|
1030
|
+
|
|
1031
|
+
let confirmed = Boolean(values.yes);
|
|
1032
|
+
if (!confirmed && interactive) confirmed = await askYesNo(prompter, "Create this? [y/N] ");
|
|
1033
|
+
|
|
1034
|
+
if (!confirmed) {
|
|
1035
|
+
const payload = {
|
|
1036
|
+
kind: "setup",
|
|
1037
|
+
status: "draft_ready",
|
|
1038
|
+
created: false,
|
|
1039
|
+
draft,
|
|
1040
|
+
next_command: interactive ? "audienti setup" : "Re-run with --yes to create it."
|
|
1041
|
+
};
|
|
1042
|
+
if (values.json) return writeJson(context.stdout, payload);
|
|
1043
|
+
|
|
1044
|
+
say("Nothing was created.");
|
|
1045
|
+
say(interactive
|
|
1046
|
+
? "Run `audienti setup` again whenever you're ready. Changing your answers changes the draft."
|
|
1047
|
+
: "Re-run the same command with --yes to create it.");
|
|
1048
|
+
return 0;
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
const confirmation = await client.confirmQuickStart(accountId, draft.id, {});
|
|
1052
|
+
const connectUrl = new URL("/user/social_cookies", client.host).toString();
|
|
1053
|
+
if (values.json) {
|
|
1054
|
+
return writeJson(context.stdout, {
|
|
1055
|
+
kind: "setup",
|
|
1056
|
+
status: "created",
|
|
1057
|
+
created: true,
|
|
1058
|
+
draft,
|
|
1059
|
+
motion: confirmation?.motion || null,
|
|
1060
|
+
reservation: confirmation?.reservation || null,
|
|
1061
|
+
next_step: {
|
|
1062
|
+
connect_linkedin_url: connectUrl,
|
|
1063
|
+
check_command: "audienti setup play preflight"
|
|
1064
|
+
}
|
|
1065
|
+
});
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1068
|
+
const motion = confirmation?.motion || {};
|
|
1069
|
+
say(`Created your first motion: ${display(motion.name)} (${display(motion.prefix_id)}).`);
|
|
1070
|
+
say();
|
|
1071
|
+
say("Next: connect your LinkedIn account so Audienti can reach people for you.");
|
|
1072
|
+
say(` Connect it here: ${connectUrl}`);
|
|
1073
|
+
say(" Then check it with: audienti setup play preflight");
|
|
1074
|
+
say("Nothing is sent until LinkedIn is connected, and nothing sends without your approval.");
|
|
1075
|
+
return 0;
|
|
1076
|
+
} finally {
|
|
1077
|
+
if (!sharedPrompter) prompter.close();
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
const SETUP_PROGRESS = [
|
|
1082
|
+
"Reading your website...",
|
|
1083
|
+
"Working out who buys from you...",
|
|
1084
|
+
"Drafting your offer and the first buying signals...",
|
|
1085
|
+
"Still working. Thanks for waiting..."
|
|
1086
|
+
];
|
|
1087
|
+
|
|
1088
|
+
function cleanSentence(value) {
|
|
1089
|
+
return String(value || "").trim().replace(/[\s.]+$/, "");
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
function setupFeedback({ sellTo, ask }) {
|
|
1093
|
+
const parts = [];
|
|
1094
|
+
if (sellTo) parts.push(`We sell to: ${sellTo}.`);
|
|
1095
|
+
if (ask) parts.push(`What we want the prospect to say yes to: ${ask}.`);
|
|
1096
|
+
return parts.join(" ") || undefined;
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
function setupCreateError(error) {
|
|
1100
|
+
if (!(error instanceof ApiError)) return error;
|
|
1101
|
+
|
|
1102
|
+
const reason = typeof error.body?.error === "string" ? cleanSentence(error.body.error) : null;
|
|
1103
|
+
if (error.status === 401) {
|
|
1104
|
+
return new CommandError("Your saved login no longer works. Run `audienti start` to sign in again.");
|
|
1105
|
+
}
|
|
1106
|
+
if (error.status === 403) {
|
|
1107
|
+
return new CommandError("Only an account admin can replace the website already saved on this account. Run `audienti setup` again and press Enter at the website question to use the saved one.");
|
|
1108
|
+
}
|
|
1109
|
+
if (error.status === 422 && error.body?.status === "failed") return setupDraftFailedError(reason);
|
|
1110
|
+
if (error.status === 422) {
|
|
1111
|
+
return new CommandError(`That website didn't work: ${reason || "it was rejected"}. Use a full public address like https://acme.com, then run \`audienti setup\` again.`);
|
|
1112
|
+
}
|
|
1113
|
+
return error;
|
|
1114
|
+
}
|
|
1115
|
+
|
|
1116
|
+
function setupDraftFailedError(reason) {
|
|
1117
|
+
const detail = reason ? ` (${cleanSentence(reason)})` : "";
|
|
1118
|
+
return new CommandError(`We couldn't draft a motion from that website${detail}. Nothing was created. Check the address or add a sentence about who you sell to, then run \`audienti setup\` again.`);
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1121
|
+
function renderSetupDraft(draft, { ask }, say) {
|
|
1122
|
+
const preview = draft?.preview || {};
|
|
1123
|
+
const icp = preview.icp || {};
|
|
1124
|
+
const offer = preview.offer || {};
|
|
1125
|
+
const titles = Array.isArray(icp.job_titles) && icp.job_titles.length ? ` (${icp.job_titles.join(", ")})` : "";
|
|
1126
|
+
|
|
1127
|
+
say("Here's the draft:");
|
|
1128
|
+
if (preview.company_name || preview.product_or_service) {
|
|
1129
|
+
say(` Your business: ${[preview.company_name, preview.product_or_service].filter(Boolean).join(" - ")}`);
|
|
1130
|
+
}
|
|
1131
|
+
say(` Who you'll reach: ${display(icp.name, "people who fit your website")}${titles}`);
|
|
1132
|
+
say(` Your offer: ${[offer.title, offer.description].filter(Boolean).join(" - ") || "drafted from your website"}`);
|
|
1133
|
+
say(` The ask: ${ask || "not set; the writer will suggest one"}`);
|
|
1134
|
+
if (preview.premise) say(` Why now: ${preview.premise}`);
|
|
1135
|
+
say();
|
|
619
1136
|
}
|
|
620
1137
|
|
|
621
1138
|
async function createAuthCallbackServer({ state, context }) {
|
|
@@ -1250,6 +1767,65 @@ async function offersDelete(args, context, { accountOverride } = {}) {
|
|
|
1250
1767
|
writeLine(context.stdout, `Deleted offer ${display(payload?.name)} (${display(payload?.prefix_id)}).`);
|
|
1251
1768
|
}
|
|
1252
1769
|
|
|
1770
|
+
async function offersRegenerateResearch(args, context, { accountOverride } = {}) {
|
|
1771
|
+
const { values, positionals } = parseCommandArgs(args, { ...jsonOptions(), guidance: { type: "string" } });
|
|
1772
|
+
if (positionals.length !== 1) throw new CommandError(OFFERS_REGENERATE_RESEARCH_USAGE);
|
|
1773
|
+
|
|
1774
|
+
const { client, accountId } = await requireAccountContext(context, { accountOverride });
|
|
1775
|
+
const offer = await client.regenerateOfferResearch(accountId, positionals[0], compactObject({ guidance: values.guidance }));
|
|
1776
|
+
if (values.json) return writeJson(context.stdout, offer);
|
|
1777
|
+
|
|
1778
|
+
writeLine(context.stdout, `Queued a new research write-up for offer ${display(offer?.name)} (${display(offer?.prefix_id)}).`);
|
|
1779
|
+
}
|
|
1780
|
+
|
|
1781
|
+
async function offersUpdateWriteup(args, context, { accountOverride } = {}) {
|
|
1782
|
+
const { values, positionals } = parseCommandArgs(args, { ...jsonOptions(), description: { type: "string" } });
|
|
1783
|
+
if (positionals.length !== 1 || values.description === undefined) throw new CommandError(OFFERS_UPDATE_WRITEUP_USAGE);
|
|
1784
|
+
|
|
1785
|
+
const { client, accountId } = await requireAccountContext(context, { accountOverride });
|
|
1786
|
+
const offer = await client.updateOfferWriteup(accountId, positionals[0], { offer: { description: values.description } });
|
|
1787
|
+
if (values.json) return writeJson(context.stdout, offer);
|
|
1788
|
+
|
|
1789
|
+
writeLine(context.stdout, `Updated the write-up for offer ${display(offer?.name)} (${display(offer?.prefix_id)}).`);
|
|
1790
|
+
}
|
|
1791
|
+
|
|
1792
|
+
async function offersAddArtifacts(args, context, { accountOverride } = {}) {
|
|
1793
|
+
const { values, positionals } = parseCommandArgs(args, jsonOptions());
|
|
1794
|
+
if (positionals.length < 2) throw new CommandError(OFFERS_ADD_ARTIFACTS_USAGE);
|
|
1795
|
+
|
|
1796
|
+
const [offerId, ...paths] = positionals;
|
|
1797
|
+
const artifacts = [];
|
|
1798
|
+
for (const path of paths) {
|
|
1799
|
+
let data;
|
|
1800
|
+
try {
|
|
1801
|
+
data = await readFile(path);
|
|
1802
|
+
} catch (error) {
|
|
1803
|
+
throw new CommandError(`Cannot read ${path}: ${error.message}`);
|
|
1804
|
+
}
|
|
1805
|
+
artifacts.push({ filename: basename(path), data: data.toString("base64") });
|
|
1806
|
+
}
|
|
1807
|
+
|
|
1808
|
+
const { client, accountId } = await requireAccountContext(context, { accountOverride });
|
|
1809
|
+
const payload = await client.addOfferArtifacts(accountId, offerId, { artifacts });
|
|
1810
|
+
if (values.json) return writeJson(context.stdout, payload);
|
|
1811
|
+
|
|
1812
|
+
const count = payload?.attached_count ?? artifacts.length;
|
|
1813
|
+
writeLine(context.stdout, `Attached ${count} ${count === 1 ? "artifact" : "artifacts"} to offer ${display(payload?.offer_id || offerId)}.`);
|
|
1814
|
+
renderOfferArtifacts(payload?.artifacts, context);
|
|
1815
|
+
}
|
|
1816
|
+
|
|
1817
|
+
async function offersRemoveArtifact(args, context, { accountOverride } = {}) {
|
|
1818
|
+
const { values, positionals } = parseCommandArgs(args, jsonOptions());
|
|
1819
|
+
if (positionals.length !== 2) throw new CommandError(OFFERS_REMOVE_ARTIFACT_USAGE);
|
|
1820
|
+
|
|
1821
|
+
const [offerId, artifactId] = positionals;
|
|
1822
|
+
const { client, accountId } = await requireAccountContext(context, { accountOverride });
|
|
1823
|
+
const payload = await client.removeOfferArtifact(accountId, offerId, artifactId);
|
|
1824
|
+
if (values.json) return writeJson(context.stdout, payload);
|
|
1825
|
+
|
|
1826
|
+
writeLine(context.stdout, `Removed artifact ${display(artifactId)} from offer ${display(payload?.offer_id || offerId)}.`);
|
|
1827
|
+
}
|
|
1828
|
+
|
|
1253
1829
|
async function icpsList(args, context, { accountOverride } = {}) {
|
|
1254
1830
|
const { values, positionals } = parseCommandArgs(args, {
|
|
1255
1831
|
...jsonOptions(),
|
|
@@ -1359,6 +1935,68 @@ async function icpsTagMutation(action, args, context, { accountOverride } = {})
|
|
|
1359
1935
|
if (Array.isArray(icp?.tags)) writeLine(context.stdout, `Tags: ${display(icp.tags.join(", "), "-")}`);
|
|
1360
1936
|
}
|
|
1361
1937
|
|
|
1938
|
+
async function icpsBulkAddTag(args, context, { accountOverride } = {}) {
|
|
1939
|
+
const { values, positionals } = parseCommandArgs(args, { ...jsonOptions(), tag: { type: "string" } });
|
|
1940
|
+
if (positionals.length === 0 || !String(values.tag || "").trim()) throw new CommandError(ICPS_BULK_ADD_TAG_USAGE);
|
|
1941
|
+
|
|
1942
|
+
const { client, accountId } = await requireAccountContext(context, { accountOverride });
|
|
1943
|
+
const icps = await client.bulkAddIcpTag(accountId, { icp_ids: positionals, tag: values.tag });
|
|
1944
|
+
if (values.json) return writeJson(context.stdout, icps);
|
|
1945
|
+
|
|
1946
|
+
const count = Array.isArray(icps) ? icps.length : 0;
|
|
1947
|
+
writeLine(context.stdout, `Added tag ${display(values.tag)} to ${count} ${count === 1 ? "ICP" : "ICPs"}.`);
|
|
1948
|
+
}
|
|
1949
|
+
|
|
1950
|
+
async function icpsClone(args, context, { accountOverride } = {}) {
|
|
1951
|
+
const { values, positionals } = parseCommandArgs(args, jsonOptions());
|
|
1952
|
+
if (positionals.length !== 1) throw new CommandError(ICPS_CLONE_USAGE);
|
|
1953
|
+
|
|
1954
|
+
const { client, accountId } = await requireAccountContext(context, { accountOverride });
|
|
1955
|
+
const icp = await client.cloneIcp(accountId, positionals[0]);
|
|
1956
|
+
if (values.json) return writeJson(context.stdout, icp);
|
|
1957
|
+
|
|
1958
|
+
writeLine(context.stdout, `Cloned ICP as ${display(icp?.name)} (${display(icp?.prefix_id)}).`);
|
|
1959
|
+
}
|
|
1960
|
+
|
|
1961
|
+
async function icpsDelete(args, context, { accountOverride } = {}) {
|
|
1962
|
+
const { values, positionals } = parseCommandArgs(args, { ...jsonOptions(), confirm: { type: "string" } });
|
|
1963
|
+
const normalizedConfirm = String(values.confirm || "").trim().toLowerCase();
|
|
1964
|
+
if (positionals.length !== 1 || !DELETE_CONFIRMATION_VALUES.has(normalizedConfirm)) throw new CommandError(ICPS_DELETE_USAGE);
|
|
1965
|
+
|
|
1966
|
+
const { client, accountId } = await requireAccountContext(context, { accountOverride });
|
|
1967
|
+
const payload = await client.deleteIcp(accountId, positionals[0]);
|
|
1968
|
+
if (values.json) return writeJson(context.stdout, payload);
|
|
1969
|
+
|
|
1970
|
+
writeLine(context.stdout, `Deleted ICP ${display(payload?.name)} (${display(payload?.prefix_id)}).`);
|
|
1971
|
+
}
|
|
1972
|
+
|
|
1973
|
+
async function icpsProspects(args, context, { accountOverride } = {}) {
|
|
1974
|
+
const { values, positionals } = parseCommandArgs(args, {
|
|
1975
|
+
...jsonOptions(),
|
|
1976
|
+
query: { type: "string" },
|
|
1977
|
+
limit: { type: "string" },
|
|
1978
|
+
offset: { type: "string" },
|
|
1979
|
+
page: { type: "string" },
|
|
1980
|
+
profiles: { type: "boolean" }
|
|
1981
|
+
});
|
|
1982
|
+
if (positionals.length !== 1) throw new CommandError(ICPS_PROSPECTS_USAGE);
|
|
1983
|
+
if (values.page && values.offset) throw new CommandError("Choose one pagination mode: use either --page or --offset.");
|
|
1984
|
+
|
|
1985
|
+
const { client, accountId } = await requireAccountContext(context, { accountOverride });
|
|
1986
|
+
const payload = await client.icpProspects(accountId, positionals[0], compactObject({
|
|
1987
|
+
query: values.query,
|
|
1988
|
+
limit: values.limit,
|
|
1989
|
+
offset: values.offset,
|
|
1990
|
+
page: values.page,
|
|
1991
|
+
include_profiles: values.profiles
|
|
1992
|
+
}));
|
|
1993
|
+
if (values.json) return writeJson(context.stdout, payload);
|
|
1994
|
+
|
|
1995
|
+
renderProspects(payload, context, { profiles: values.profiles });
|
|
1996
|
+
const meta = payload?.meta || {};
|
|
1997
|
+
writeLine(context.stdout, `Showing ${display(meta.returned_count, 0)} of ${display(meta.total_count, 0)} matched prospects.`);
|
|
1998
|
+
}
|
|
1999
|
+
|
|
1362
2000
|
async function companiesSearch(args, context, { accountOverride } = {}) {
|
|
1363
2001
|
const { values, positionals } = parseCommandArgs(args, {
|
|
1364
2002
|
...jsonOptions(),
|
|
@@ -1952,21 +2590,71 @@ async function tasksAdd(args, context, { accountOverride } = {}) {
|
|
|
1952
2590
|
const payload = await client.createTask(accountId, { task });
|
|
1953
2591
|
if (values.json) return writeJson(context.stdout, payload);
|
|
1954
2592
|
|
|
1955
|
-
writeLine(context.stdout, `Created task ${display(payload?.prefix_id || payload?.id)}.`);
|
|
2593
|
+
writeLine(context.stdout, `Created task ${display(payload?.prefix_id || payload?.id)}.`);
|
|
2594
|
+
writeLine(context.stdout, `Title: ${display(payload?.title)}`);
|
|
2595
|
+
if (payload?.notes) writeLine(context.stdout, `Description: ${payload.notes}`);
|
|
2596
|
+
writeLine(context.stdout, `Due: ${display(payload?.due_at, "-")} | Association: ${taskAssociationLabel(payload)}`);
|
|
2597
|
+
}
|
|
2598
|
+
|
|
2599
|
+
async function tasksComplete(args, context, { accountOverride } = {}) {
|
|
2600
|
+
const { values, positionals } = parseCommandArgs(args, jsonOptions());
|
|
2601
|
+
if (positionals.length !== 1) throw new CommandError(TASKS_COMPLETE_USAGE);
|
|
2602
|
+
|
|
2603
|
+
const { client, accountId } = await requireAccountContext(context, { accountOverride });
|
|
2604
|
+
const payload = await client.completeTask(accountId, positionals[0]);
|
|
2605
|
+
if (values.json) return writeJson(context.stdout, payload);
|
|
2606
|
+
|
|
2607
|
+
writeLine(context.stdout, `Completed task ${display(payload?.prefix_id || payload?.id || positionals[0])}.`);
|
|
2608
|
+
}
|
|
2609
|
+
|
|
2610
|
+
async function tasksUpdate(args, context, { accountOverride } = {}) {
|
|
2611
|
+
const { values, positionals } = parseCommandArgs(args, taskAddOptions());
|
|
2612
|
+
const fields = {
|
|
2613
|
+
title: values.title,
|
|
2614
|
+
due_at: values.due,
|
|
2615
|
+
notes: values.notes,
|
|
2616
|
+
prospect_id: values.prospect,
|
|
2617
|
+
list_id: values.list,
|
|
2618
|
+
assignee_account_user_id: values["assigned-user"]
|
|
2619
|
+
};
|
|
2620
|
+
const sentFields = Object.entries(fields).filter(([, value]) => value !== undefined);
|
|
2621
|
+
if (positionals.length !== 1 || sentFields.length === 0) throw new CommandError(TASKS_UPDATE_USAGE);
|
|
2622
|
+
if (values.prospect && values.list) throw new CommandError("Use either --prospect or --list, not both.");
|
|
2623
|
+
|
|
2624
|
+
const { client, accountId, config } = await requireAccountContext(context, { accountOverride });
|
|
2625
|
+
const task = Object.fromEntries(sentFields);
|
|
2626
|
+
if (task.assignee_account_user_id !== undefined) {
|
|
2627
|
+
task.assignee_account_user_id = resolveAccountUserId(task.assignee_account_user_id, config, { accountOverride }) ?? "";
|
|
2628
|
+
}
|
|
2629
|
+
const payload = await client.updateTask(accountId, positionals[0], { task });
|
|
2630
|
+
if (values.json) return writeJson(context.stdout, payload);
|
|
2631
|
+
|
|
2632
|
+
writeLine(context.stdout, `Updated task ${display(payload?.prefix_id || payload?.id || positionals[0])}.`);
|
|
1956
2633
|
writeLine(context.stdout, `Title: ${display(payload?.title)}`);
|
|
1957
|
-
if (payload?.notes) writeLine(context.stdout, `Description: ${payload.notes}`);
|
|
1958
2634
|
writeLine(context.stdout, `Due: ${display(payload?.due_at, "-")} | Association: ${taskAssociationLabel(payload)}`);
|
|
1959
2635
|
}
|
|
1960
2636
|
|
|
1961
|
-
async function
|
|
1962
|
-
const { values, positionals } = parseCommandArgs(args,
|
|
1963
|
-
|
|
2637
|
+
async function tasksBulkUpdate(args, context, { accountOverride } = {}) {
|
|
2638
|
+
const { values, positionals } = parseCommandArgs(args, {
|
|
2639
|
+
...jsonOptions(),
|
|
2640
|
+
action: { type: "string" },
|
|
2641
|
+
"assigned-user": { type: "string" }
|
|
2642
|
+
});
|
|
2643
|
+
const bulkAction = String(values.action || "").trim();
|
|
2644
|
+
if (positionals.length === 0 || !["complete", "reassign"].includes(bulkAction)) throw new CommandError(TASKS_BULK_UPDATE_USAGE);
|
|
2645
|
+
if (bulkAction === "reassign" && !values["assigned-user"]) throw new CommandError("--assigned-user is required with --action reassign.");
|
|
1964
2646
|
|
|
1965
|
-
const { client, accountId } = await requireAccountContext(context, { accountOverride });
|
|
1966
|
-
const payload = await client.
|
|
2647
|
+
const { client, accountId, config } = await requireAccountContext(context, { accountOverride });
|
|
2648
|
+
const payload = await client.bulkUpdateTasks(accountId, compactObject({
|
|
2649
|
+
task_ids: positionals,
|
|
2650
|
+
bulk_action: bulkAction,
|
|
2651
|
+
assignee_account_user_id: bulkAction === "reassign" ? resolveAccountUserId(values["assigned-user"], config, { accountOverride }) : undefined
|
|
2652
|
+
}));
|
|
1967
2653
|
if (values.json) return writeJson(context.stdout, payload);
|
|
1968
2654
|
|
|
1969
|
-
|
|
2655
|
+
const count = payload?.count ?? 0;
|
|
2656
|
+
const noun = count === 1 ? "task" : "tasks";
|
|
2657
|
+
writeLine(context.stdout, bulkAction === "complete" ? `Completed ${count} ${noun}.` : `Queued reassignment of ${count} ${noun}.`);
|
|
1970
2658
|
}
|
|
1971
2659
|
|
|
1972
2660
|
async function listsCreate(args, context, { accountOverride } = {}) {
|
|
@@ -2086,6 +2774,52 @@ async function listsDelete(args, context, { accountOverride } = {}) {
|
|
|
2086
2774
|
}
|
|
2087
2775
|
}
|
|
2088
2776
|
|
|
2777
|
+
async function listsBulkAddTag(args, context, { accountOverride } = {}) {
|
|
2778
|
+
const { values, positionals } = parseCommandArgs(args, { ...jsonOptions(), tag: { type: "string" } });
|
|
2779
|
+
if (positionals.length === 0 || !String(values.tag || "").trim()) throw new CommandError(LISTS_BULK_ADD_TAG_USAGE);
|
|
2780
|
+
|
|
2781
|
+
const { client, accountId } = await requireAccountContext(context, { accountOverride });
|
|
2782
|
+
const lists = await client.bulkAddListTag(accountId, { list_ids: positionals, tag: values.tag });
|
|
2783
|
+
if (values.json) return writeJson(context.stdout, lists);
|
|
2784
|
+
|
|
2785
|
+
const count = Array.isArray(lists) ? lists.length : 0;
|
|
2786
|
+
writeLine(context.stdout, `Added tag ${display(values.tag)} to ${count} ${count === 1 ? "list" : "lists"}.`);
|
|
2787
|
+
}
|
|
2788
|
+
|
|
2789
|
+
async function listsMerge(args, context, { accountOverride } = {}) {
|
|
2790
|
+
const { values, positionals } = parseCommandArgs(args, jsonOptions());
|
|
2791
|
+
if (positionals.length !== 2) throw new CommandError(LISTS_MERGE_USAGE);
|
|
2792
|
+
|
|
2793
|
+
const { client, accountId } = await requireAccountContext(context, { accountOverride });
|
|
2794
|
+
const list = await client.mergeLists(accountId, { list_ids: positionals });
|
|
2795
|
+
if (values.json) return writeJson(context.stdout, list);
|
|
2796
|
+
|
|
2797
|
+
writeLine(context.stdout, `Merged into list ${display(list?.name)} (${display(list?.prefix_id)}); the newer list was deleted.`);
|
|
2798
|
+
writeLine(context.stdout, `Prospects: ${display(list?.prospect_count, 0)}`);
|
|
2799
|
+
}
|
|
2800
|
+
|
|
2801
|
+
async function listsExport(args, context, { accountOverride } = {}) {
|
|
2802
|
+
const { values, positionals } = parseCommandArgs(args, {
|
|
2803
|
+
...jsonOptions(),
|
|
2804
|
+
output: { type: "string" },
|
|
2805
|
+
"inactive-reason": { type: "string" }
|
|
2806
|
+
});
|
|
2807
|
+
if (positionals.length !== 1) throw new CommandError(LISTS_EXPORT_USAGE);
|
|
2808
|
+
|
|
2809
|
+
const listId = positionals[0];
|
|
2810
|
+
const { client, accountId } = await requireAccountContext(context, { accountOverride });
|
|
2811
|
+
const csv = String(await client.exportList(accountId, listId, compactObject({ inactive_reason: values["inactive-reason"] })) ?? "");
|
|
2812
|
+
|
|
2813
|
+
if (values.output) {
|
|
2814
|
+
await writeFile(values.output, csv);
|
|
2815
|
+
if (values.json) return writeJson(context.stdout, { list_id: listId, output: values.output, bytes: Buffer.byteLength(csv) });
|
|
2816
|
+
return writeLine(context.stdout, `Wrote list ${display(listId)} CSV to ${values.output}.`);
|
|
2817
|
+
}
|
|
2818
|
+
if (values.json) return writeJson(context.stdout, { list_id: listId, csv });
|
|
2819
|
+
|
|
2820
|
+
context.stdout.write(csv);
|
|
2821
|
+
}
|
|
2822
|
+
|
|
2089
2823
|
async function listProspects(args, context, { accountOverride } = {}) {
|
|
2090
2824
|
const { values, positionals } = parseCommandArgs(args, {
|
|
2091
2825
|
...jsonOptions(),
|
|
@@ -2167,7 +2901,7 @@ async function listsRemoveProspects(args, context, { accountOverride } = {}) {
|
|
|
2167
2901
|
async function listsRoutingRules(args, context, { accountOverride } = {}) {
|
|
2168
2902
|
const [listId, subaction, ...rest] = args;
|
|
2169
2903
|
if (!listId || !subaction) {
|
|
2170
|
-
throw new CommandError("Usage: audienti lists routing-rules <list_id> <list|create|update|remove|move|apply> [args] [--json] [--account <acct_id>]");
|
|
2904
|
+
throw new CommandError("Usage: audienti lists routing-rules <list_id> <list|create|update|remove|move|toggle|apply> [args] [--json] [--account <acct_id>]");
|
|
2171
2905
|
}
|
|
2172
2906
|
|
|
2173
2907
|
if (subaction === "list") return listRoutingRulesList(listId, rest, context, { accountOverride });
|
|
@@ -2175,9 +2909,10 @@ async function listsRoutingRules(args, context, { accountOverride } = {}) {
|
|
|
2175
2909
|
if (subaction === "update") return listRoutingRulesUpdate(listId, rest, context, { accountOverride });
|
|
2176
2910
|
if (["remove", "delete"].includes(subaction)) return listRoutingRulesRemove(listId, rest, context, { accountOverride });
|
|
2177
2911
|
if (subaction === "move") return listRoutingRulesMove(listId, rest, context, { accountOverride });
|
|
2912
|
+
if (subaction === "toggle") return listRoutingRulesToggle(listId, rest, context, { accountOverride });
|
|
2178
2913
|
if (subaction === "apply") return listRoutingRulesApply(listId, rest, context, { accountOverride });
|
|
2179
2914
|
|
|
2180
|
-
throw new CommandError("Usage: audienti lists routing-rules <list_id> <list|create|update|remove|move|apply> [args] [--json] [--account <acct_id>]");
|
|
2915
|
+
throw new CommandError("Usage: audienti lists routing-rules <list_id> <list|create|update|remove|move|toggle|apply> [args] [--json] [--account <acct_id>]");
|
|
2181
2916
|
}
|
|
2182
2917
|
|
|
2183
2918
|
async function listRoutingRulesList(listId, args, context, { accountOverride } = {}) {
|
|
@@ -2251,6 +2986,19 @@ async function listRoutingRulesMove(listId, args, context, { accountOverride } =
|
|
|
2251
2986
|
renderListRoutingRules(payload, context);
|
|
2252
2987
|
}
|
|
2253
2988
|
|
|
2989
|
+
async function listRoutingRulesToggle(listId, args, context, { accountOverride } = {}) {
|
|
2990
|
+
const { values, positionals } = parseCommandArgs(args, jsonOptions());
|
|
2991
|
+
if (positionals.length !== 1) throw new CommandError(LIST_ROUTING_RULES_TOGGLE_USAGE);
|
|
2992
|
+
|
|
2993
|
+
const { client, accountId } = await requireAccountContext(context, { accountOverride });
|
|
2994
|
+
const payload = await client.toggleListRoutingRule(accountId, listId, positionals[0]);
|
|
2995
|
+
if (values.json) return writeJson(context.stdout, payload);
|
|
2996
|
+
|
|
2997
|
+
const rule = payload?.routing_rule;
|
|
2998
|
+
const state = rule?.enabled ? "Enabled" : "Disabled";
|
|
2999
|
+
writeLine(context.stdout, `${state} routing rule ${display(rule?.name)} (${display(rule?.id || positionals[0])}) on list ${display(payload?.list_id || listId)}.`);
|
|
3000
|
+
}
|
|
3001
|
+
|
|
2254
3002
|
async function listRoutingRulesApply(listId, args, context, { accountOverride } = {}) {
|
|
2255
3003
|
const { values, positionals } = parseCommandArgs(args, jsonOptions());
|
|
2256
3004
|
if (positionals.length > 0) throw new CommandError(LIST_ROUTING_RULES_APPLY_USAGE);
|
|
@@ -3803,6 +4551,23 @@ async function toolsLinkedinReviewStatus(args, context, { accountOverride } = {}
|
|
|
3803
4551
|
renderLinkedinReviewStatus(payload, context);
|
|
3804
4552
|
}
|
|
3805
4553
|
|
|
4554
|
+
async function toolsLinkedinStrategyReview(args, context, { accountOverride } = {}) {
|
|
4555
|
+
const [subaction, ...rest] = args;
|
|
4556
|
+
if (subaction !== "delete") throw new CommandError(TOOLS_LINKEDIN_STRATEGY_REVIEW_DELETE_USAGE);
|
|
4557
|
+
|
|
4558
|
+
const { values, positionals } = parseCommandArgs(rest, { ...jsonOptions(), confirm: { type: "string" } });
|
|
4559
|
+
const normalizedConfirm = String(values.confirm || "").trim().toLowerCase();
|
|
4560
|
+
if (positionals.length !== 1 || !DELETE_CONFIRMATION_VALUES.has(normalizedConfirm)) {
|
|
4561
|
+
throw new CommandError(TOOLS_LINKEDIN_STRATEGY_REVIEW_DELETE_USAGE);
|
|
4562
|
+
}
|
|
4563
|
+
|
|
4564
|
+
const { client, accountId } = await requireAccountContext(context, { accountOverride });
|
|
4565
|
+
const payload = await client.deleteLinkedinStrategyReview(accountId, positionals[0]);
|
|
4566
|
+
if (values.json) return writeJson(context.stdout, payload);
|
|
4567
|
+
|
|
4568
|
+
writeLine(context.stdout, `Deleted LinkedIn strategy review report ${display(payload?.id || positionals[0])}.`);
|
|
4569
|
+
}
|
|
4570
|
+
|
|
3806
4571
|
async function operatorQueue(args, context, { accountOverride } = {}) {
|
|
3807
4572
|
const { values, positionals } = parseCommandArgs(args, operatorFilterOptions(operatorPaginationOptions()));
|
|
3808
4573
|
if (positionals.length > 0) throw new CommandError("Usage: audienti operator queue [--json] [filters] [--account <acct_id>]");
|
|
@@ -4140,9 +4905,8 @@ async function inboxOpsActionsInBatches(client, accountId, { operation, rows, dr
|
|
|
4140
4905
|
}
|
|
4141
4906
|
|
|
4142
4907
|
async function confirmInboxOpsAction(context) {
|
|
4143
|
-
|
|
4144
|
-
|
|
4145
|
-
const prompt = createInterface({ input, output: context.stdout });
|
|
4908
|
+
if (!stdinIsInteractive(context)) return false;
|
|
4909
|
+
const prompt = createInterface({ input: context.stdin, output: context.stdout });
|
|
4146
4910
|
try {
|
|
4147
4911
|
const answer = await prompt.question("Apply? [y/N] ");
|
|
4148
4912
|
return /^y(es)?$/i.test(String(answer).trim());
|
|
@@ -6128,6 +6892,16 @@ function renderOffer(offer, context) {
|
|
|
6128
6892
|
writeLine(context.stdout, `Offer: ${display(offer?.name)} (${display(offer?.prefix_id)})`);
|
|
6129
6893
|
if (offer?.description) writeLine(context.stdout, `Description: ${offer.description}`);
|
|
6130
6894
|
if (offer?.url) writeLine(context.stdout, `URL: ${offer.url}`);
|
|
6895
|
+
if (Array.isArray(offer?.artifacts) && offer.artifacts.length > 0) renderOfferArtifacts(offer.artifacts, context);
|
|
6896
|
+
}
|
|
6897
|
+
|
|
6898
|
+
function renderOfferArtifacts(artifacts, context) {
|
|
6899
|
+
if (!Array.isArray(artifacts) || artifacts.length === 0) return writeLine(context.stdout, "No artifacts attached.");
|
|
6900
|
+
|
|
6901
|
+
writeLine(context.stdout, "ARTIFACT ID\tFILENAME\tTYPE\tBYTES");
|
|
6902
|
+
for (const artifact of artifacts) {
|
|
6903
|
+
writeLine(context.stdout, [display(artifact.id), display(artifact.filename), display(artifact.content_type, "-"), display(artifact.byte_size, 0)].join("\t"));
|
|
6904
|
+
}
|
|
6131
6905
|
}
|
|
6132
6906
|
|
|
6133
6907
|
function renderIcps(icps, context) {
|
|
@@ -7825,13 +8599,14 @@ function quickStartTerminal(draft) {
|
|
|
7825
8599
|
return quickStartReady(draft) || draft?.status === "failed";
|
|
7826
8600
|
}
|
|
7827
8601
|
|
|
7828
|
-
async function waitForQuickStartDraft(client, accountId, initialDraft, context, { timeoutSeconds, pollIntervalSeconds }) {
|
|
8602
|
+
async function waitForQuickStartDraft(client, accountId, initialDraft, context, { timeoutSeconds, pollIntervalSeconds, onPoll }) {
|
|
7829
8603
|
let draft = initialDraft;
|
|
7830
8604
|
const deadline = context.now().getTime() + timeoutSeconds * 1000;
|
|
7831
8605
|
|
|
7832
8606
|
while (!quickStartTerminal(draft)) {
|
|
7833
8607
|
if (context.now().getTime() >= deadline) return draft;
|
|
7834
8608
|
|
|
8609
|
+
onPoll?.();
|
|
7835
8610
|
await context.sleep(pollIntervalSeconds * 1000);
|
|
7836
8611
|
draft = await client.quickStart(accountId, draft.id);
|
|
7837
8612
|
}
|
|
@@ -8889,6 +9664,8 @@ const HELP_TOPICS = new Map([
|
|
|
8889
9664
|
" audienti <command> [options]",
|
|
8890
9665
|
"",
|
|
8891
9666
|
"Start:",
|
|
9667
|
+
" audienti start Guided first run: sign in, pick your account",
|
|
9668
|
+
" audienti setup Guided setup of your first motion",
|
|
8892
9669
|
" audienti auth login Sign in through the browser",
|
|
8893
9670
|
" audienti auth token <token> Save an API token",
|
|
8894
9671
|
" audienti accounts list See accounts available to this token",
|
|
@@ -8971,15 +9748,23 @@ const HELP_TOPICS = new Map([
|
|
|
8971
9748
|
" audienti lists routing-rules <list_id> list",
|
|
8972
9749
|
" audienti lists routing-rules <list_id> create --payload <file.json>",
|
|
8973
9750
|
" audienti lists routing-rules <list_id> apply",
|
|
9751
|
+
" audienti lists bulk-add-tag --tag <tag> <list_id> [list_id...]",
|
|
9752
|
+
" audienti lists merge <list_id> <list_id>",
|
|
9753
|
+
" audienti lists export <list_id> [--output <file.csv>]",
|
|
8974
9754
|
" audienti tags list",
|
|
8975
9755
|
" audienti tags show <tag>",
|
|
8976
9756
|
" audienti tasks list [--status open]",
|
|
8977
9757
|
" audienti tasks add --title <text> --due <time>",
|
|
8978
9758
|
" audienti tasks complete <ptsk_id>",
|
|
9759
|
+
" audienti tasks update <ptsk_id> [--title <text>]",
|
|
9760
|
+
" audienti tasks bulk-update --action <complete|reassign> <ptsk_id> [ptsk_id...]",
|
|
8979
9761
|
" audienti offers list",
|
|
8980
9762
|
" audienti offers show <offr_id>",
|
|
8981
9763
|
" audienti offers update <offr_id> [--name <text>]",
|
|
8982
9764
|
" audienti offers delete <offr_id> --confirm <yes|true|Y|y>",
|
|
9765
|
+
" audienti offers regenerate-research <offr_id> [--guidance <text>]",
|
|
9766
|
+
" audienti offers update-writeup <offr_id> --description <text>",
|
|
9767
|
+
" audienti offers add-artifacts <offr_id> <file> [file...]",
|
|
8983
9768
|
" audienti icps list [--status <active|archived|all>] [--tag <tag>]",
|
|
8984
9769
|
" audienti icps show <icp_id>",
|
|
8985
9770
|
" audienti icps update <icp_id> [--tags <tag[,tag...]> | --payload <file.json>]",
|
|
@@ -8987,6 +9772,10 @@ const HELP_TOPICS = new Map([
|
|
|
8987
9772
|
" audienti icps restore <icp_id>",
|
|
8988
9773
|
" audienti icps add-tag <icp_id> <tag>",
|
|
8989
9774
|
" audienti icps remove-tag <icp_id> <tag>",
|
|
9775
|
+
" audienti icps bulk-add-tag --tag <tag> <icp_id> [icp_id...]",
|
|
9776
|
+
" audienti icps clone <icp_id>",
|
|
9777
|
+
" audienti icps delete <icp_id> --confirm <yes|true|Y|y>",
|
|
9778
|
+
" audienti icps prospects <icp_id> [--query <text>]",
|
|
8990
9779
|
" audienti companies search --query <text>",
|
|
8991
9780
|
" audienti linkedin-lookups <kind> [--query <text>]",
|
|
8992
9781
|
" audienti dnc list",
|
|
@@ -9338,16 +10127,47 @@ const HELP_TOPICS = new Map([
|
|
|
9338
10127
|
" Saves accountId and accountName in local CLI config."
|
|
9339
10128
|
].join("\n")],
|
|
9340
10129
|
|
|
10130
|
+
["start", [
|
|
10131
|
+
"Usage:",
|
|
10132
|
+
` ${START_USAGE.slice("Usage: ".length)}`,
|
|
10133
|
+
"",
|
|
10134
|
+
"Status: implemented",
|
|
10135
|
+
"",
|
|
10136
|
+
"Purpose:",
|
|
10137
|
+
" Guided first run. Signs you in (or sends you to create an account) through the browser,",
|
|
10138
|
+
" picks your account and account user, then offers to run `audienti setup`.",
|
|
10139
|
+
" Running `audienti` with no arguments and no saved login starts here.",
|
|
10140
|
+
"",
|
|
10141
|
+
"Without a terminal:",
|
|
10142
|
+
" Never asks questions. Prints the sign-in command and the next step; `--json` returns them.",
|
|
10143
|
+
"",
|
|
10144
|
+
"Effect:",
|
|
10145
|
+
" Saves the login, accountId and accountUserId in local CLI config. Sends nothing."
|
|
10146
|
+
].join("\n")],
|
|
10147
|
+
|
|
9341
10148
|
["setup", [
|
|
9342
10149
|
"Usage:",
|
|
10150
|
+
` ${SETUP_USAGE.slice("Usage: ".length)}`,
|
|
9343
10151
|
" audienti setup play preflight [--principal <account_user_id|email|name|me>] [--platform linkedin] [--json]",
|
|
9344
10152
|
"",
|
|
9345
10153
|
"Status: implemented",
|
|
9346
10154
|
"",
|
|
9347
10155
|
"Purpose:",
|
|
10156
|
+
" `audienti setup` asks three questions (company website, who you sell to, what the prospect",
|
|
10157
|
+
" should say yes to), drafts an ICP, offer and motion with Quick Start, reads the draft back,",
|
|
10158
|
+
" and creates it only when you say yes. Nothing sends until LinkedIn is connected and approved.",
|
|
9348
10159
|
" Preflight account setup before an agent creates or activates an Audienti play.",
|
|
9349
10160
|
"",
|
|
10161
|
+
"Without a terminal:",
|
|
10162
|
+
" Pass --url (required) and optionally --sell-to and --ask. Add --yes to create the draft.",
|
|
10163
|
+
"",
|
|
10164
|
+
"API:",
|
|
10165
|
+
" POST /api/v1/accounts/:account_id/quick_start.json",
|
|
10166
|
+
" GET /api/v1/accounts/:account_id/quick_start/:draft_id.json",
|
|
10167
|
+
" POST /api/v1/accounts/:account_id/quick_start/:draft_id/confirm.json",
|
|
10168
|
+
"",
|
|
9350
10169
|
"Commands:",
|
|
10170
|
+
" audienti setup Guided setup of your first motion",
|
|
9351
10171
|
" audienti setup play preflight Check connected-account readiness and direct setup URLs"
|
|
9352
10172
|
].join("\n")],
|
|
9353
10173
|
|
|
@@ -9528,6 +10348,10 @@ const HELP_TOPICS = new Map([
|
|
|
9528
10348
|
" audienti offers create --name <text> [--json]",
|
|
9529
10349
|
" audienti offers update <offr_id> [--name <text>] [--json]",
|
|
9530
10350
|
" audienti offers delete <offr_id> --confirm <yes|true|Y|y> [--json]",
|
|
10351
|
+
" audienti offers regenerate-research <offr_id> [--guidance <text>] [--json]",
|
|
10352
|
+
" audienti offers update-writeup <offr_id> --description <text> [--json]",
|
|
10353
|
+
" audienti offers add-artifacts <offr_id> <file> [file...] [--json]",
|
|
10354
|
+
" audienti offers remove-artifact <offr_id> <artifact_id> [--json]",
|
|
9531
10355
|
"",
|
|
9532
10356
|
"Status: implemented",
|
|
9533
10357
|
"",
|
|
@@ -9639,6 +10463,10 @@ const HELP_TOPICS = new Map([
|
|
|
9639
10463
|
" audienti icps remove-tag <icp_id> <tag> [--json]",
|
|
9640
10464
|
" audienti icps archive <icp_id> [--json]",
|
|
9641
10465
|
" audienti icps restore <icp_id> [--json]",
|
|
10466
|
+
" audienti icps bulk-add-tag --tag <tag> <icp_id> [icp_id...] [--json]",
|
|
10467
|
+
" audienti icps clone <icp_id> [--json]",
|
|
10468
|
+
" audienti icps delete <icp_id> --confirm <yes|true|Y|y> [--json]",
|
|
10469
|
+
" audienti icps prospects <icp_id> [--query <text>] [--json]",
|
|
9642
10470
|
"",
|
|
9643
10471
|
"Status: implemented",
|
|
9644
10472
|
"",
|
|
@@ -10337,6 +11165,8 @@ const HELP_TOPICS = new Map([
|
|
|
10337
11165
|
` ${TASKS_LIST_USAGE.slice("Usage: ".length)}`,
|
|
10338
11166
|
` ${TASKS_ADD_USAGE.slice("Usage: ".length)}`,
|
|
10339
11167
|
` ${TASKS_COMPLETE_USAGE.slice("Usage: ".length)}`,
|
|
11168
|
+
` ${TASKS_UPDATE_USAGE.slice("Usage: ".length)}`,
|
|
11169
|
+
` ${TASKS_BULK_UPDATE_USAGE.slice("Usage: ".length)}`,
|
|
10340
11170
|
"",
|
|
10341
11171
|
"Status: implemented",
|
|
10342
11172
|
"",
|
|
@@ -10417,10 +11247,13 @@ const HELP_TOPICS = new Map([
|
|
|
10417
11247
|
" audienti lists add-tag <list_id> <tag> [--json]",
|
|
10418
11248
|
" audienti lists remove-tag <list_id> <tag> [--json]",
|
|
10419
11249
|
" audienti lists delete <list_id> --confirm <yes|true|Y|y> [--json]",
|
|
11250
|
+
" audienti lists bulk-add-tag --tag <tag> <list_id> [list_id...] [--json]",
|
|
11251
|
+
" audienti lists merge <list_id> <list_id> [--json]",
|
|
11252
|
+
" audienti lists export <list_id> [--output <file.csv>] [--json]",
|
|
10420
11253
|
" audienti lists prospects <list_id> [--json]",
|
|
10421
11254
|
" audienti lists add-prospects <list_id> <prsp_id> [prsp_id...] [--json]",
|
|
10422
11255
|
" audienti lists remove-prospects <list_id> <prsp_id> [prsp_id...] [--json]",
|
|
10423
|
-
" audienti lists routing-rules <list_id> <list|create|update|remove|move|apply> [args] [--json]",
|
|
11256
|
+
" audienti lists routing-rules <list_id> <list|create|update|remove|move|toggle|apply> [args] [--json]",
|
|
10424
11257
|
"",
|
|
10425
11258
|
"Status: read, create, update, delete, membership, and routing-rule commands implemented",
|
|
10426
11259
|
"",
|
|
@@ -10435,6 +11268,7 @@ const HELP_TOPICS = new Map([
|
|
|
10435
11268
|
" audienti lists routing-rules <list_id> update <rule_id> --payload <file.json> [--json] [--account <acct_id>]",
|
|
10436
11269
|
" audienti lists routing-rules <list_id> remove <rule_id> [--json] [--account <acct_id>]",
|
|
10437
11270
|
" audienti lists routing-rules <list_id> move <rule_id> <up|down> [--json] [--account <acct_id>]",
|
|
11271
|
+
" audienti lists routing-rules <list_id> toggle <rule_id> [--json] [--account <acct_id>]",
|
|
10438
11272
|
" audienti lists routing-rules <list_id> apply [--json] [--account <acct_id>]",
|
|
10439
11273
|
"",
|
|
10440
11274
|
"Status: implemented",
|
|
@@ -10576,6 +11410,259 @@ const HELP_TOPICS = new Map([
|
|
|
10576
11410
|
" PATCH /api/v1/accounts/:account_id/lists/:id.json"
|
|
10577
11411
|
].join("\n")],
|
|
10578
11412
|
|
|
11413
|
+
["lists bulk-add-tag", [
|
|
11414
|
+
"Usage:",
|
|
11415
|
+
` ${LISTS_BULK_ADD_TAG_USAGE.slice("Usage: ".length)}`,
|
|
11416
|
+
"",
|
|
11417
|
+
"Status: implemented",
|
|
11418
|
+
"",
|
|
11419
|
+
"Purpose:",
|
|
11420
|
+
" Add one normalized tag to several lists at once. All lists change or none do.",
|
|
11421
|
+
"",
|
|
11422
|
+
"Input shape:",
|
|
11423
|
+
" list_id: list_ prefix id or integer id; every id must belong to the account",
|
|
11424
|
+
" tag: string",
|
|
11425
|
+
"",
|
|
11426
|
+
"API:",
|
|
11427
|
+
" POST /api/v1/accounts/:account_id/lists/bulk_add_tag.json"
|
|
11428
|
+
].join("\n")],
|
|
11429
|
+
|
|
11430
|
+
["lists merge", [
|
|
11431
|
+
"Usage:",
|
|
11432
|
+
` ${LISTS_MERGE_USAGE.slice("Usage: ".length)}`,
|
|
11433
|
+
"",
|
|
11434
|
+
"Status: implemented",
|
|
11435
|
+
"",
|
|
11436
|
+
"Purpose:",
|
|
11437
|
+
" Merge two lists. The older list keeps its name and gains the newer list's tags, prospects, agents, and routing targets; the newer list is deleted.",
|
|
11438
|
+
" System and HubSpot-synced lists cannot be merged.",
|
|
11439
|
+
"",
|
|
11440
|
+
"Input shape:",
|
|
11441
|
+
" list_id: exactly two different list ids in the account",
|
|
11442
|
+
"",
|
|
11443
|
+
"API:",
|
|
11444
|
+
" POST /api/v1/accounts/:account_id/lists/merge_selected.json"
|
|
11445
|
+
].join("\n")],
|
|
11446
|
+
|
|
11447
|
+
["lists export", [
|
|
11448
|
+
"Usage:",
|
|
11449
|
+
` ${LISTS_EXPORT_USAGE.slice("Usage: ".length)}`,
|
|
11450
|
+
"",
|
|
11451
|
+
"Status: implemented",
|
|
11452
|
+
"",
|
|
11453
|
+
"Purpose:",
|
|
11454
|
+
" Download the list's prospects as CSV with the same columns as the web export.",
|
|
11455
|
+
" Prints to stdout unless --output is given.",
|
|
11456
|
+
"",
|
|
11457
|
+
"Input shape:",
|
|
11458
|
+
" list_id: list_ prefix id",
|
|
11459
|
+
" inactive-reason: optional filter for the inactive system list",
|
|
11460
|
+
"",
|
|
11461
|
+
"API:",
|
|
11462
|
+
" GET /api/v1/accounts/:account_id/lists/:id/export.json (returns text/csv)"
|
|
11463
|
+
].join("\n")],
|
|
11464
|
+
|
|
11465
|
+
["lists routing-rules toggle", [
|
|
11466
|
+
"Usage:",
|
|
11467
|
+
` ${LIST_ROUTING_RULES_TOGGLE_USAGE.slice("Usage: ".length)}`,
|
|
11468
|
+
"",
|
|
11469
|
+
"Status: implemented",
|
|
11470
|
+
"",
|
|
11471
|
+
"Purpose:",
|
|
11472
|
+
" Turn one routing rule on or off.",
|
|
11473
|
+
"",
|
|
11474
|
+
"Input shape:",
|
|
11475
|
+
" rule_id: routing rule id on the list",
|
|
11476
|
+
"",
|
|
11477
|
+
"API:",
|
|
11478
|
+
" PATCH /api/v1/accounts/:account_id/lists/:list_id/routing_rules/:id/toggle.json"
|
|
11479
|
+
].join("\n")],
|
|
11480
|
+
|
|
11481
|
+
["icps bulk-add-tag", [
|
|
11482
|
+
"Usage:",
|
|
11483
|
+
` ${ICPS_BULK_ADD_TAG_USAGE.slice("Usage: ".length)}`,
|
|
11484
|
+
"",
|
|
11485
|
+
"Status: implemented",
|
|
11486
|
+
"",
|
|
11487
|
+
"Purpose:",
|
|
11488
|
+
" Add one normalized tag to several ICPs at once. All ICPs change or none do.",
|
|
11489
|
+
"",
|
|
11490
|
+
"Input shape:",
|
|
11491
|
+
" icp_id: icpp_ prefix id or integer id; every id must belong to the account",
|
|
11492
|
+
" tag: string",
|
|
11493
|
+
"",
|
|
11494
|
+
"API:",
|
|
11495
|
+
" POST /api/v1/accounts/:account_id/icps/bulk_add_tag.json"
|
|
11496
|
+
].join("\n")],
|
|
11497
|
+
|
|
11498
|
+
["icps clone", [
|
|
11499
|
+
"Usage:",
|
|
11500
|
+
` ${ICPS_CLONE_USAGE.slice("Usage: ".length)}`,
|
|
11501
|
+
"",
|
|
11502
|
+
"Status: implemented",
|
|
11503
|
+
"",
|
|
11504
|
+
"Purpose:",
|
|
11505
|
+
" Copy an ICP and its audience rules under a new \"(Copy)\" name.",
|
|
11506
|
+
"",
|
|
11507
|
+
"Input shape:",
|
|
11508
|
+
" icp_id: icpp_ prefix id or integer id",
|
|
11509
|
+
"",
|
|
11510
|
+
"API:",
|
|
11511
|
+
" POST /api/v1/accounts/:account_id/icps/:id/clone.json"
|
|
11512
|
+
].join("\n")],
|
|
11513
|
+
|
|
11514
|
+
["icps delete", [
|
|
11515
|
+
"Usage:",
|
|
11516
|
+
` ${ICPS_DELETE_USAGE.slice("Usage: ".length)}`,
|
|
11517
|
+
"",
|
|
11518
|
+
"Status: implemented",
|
|
11519
|
+
"",
|
|
11520
|
+
"Purpose:",
|
|
11521
|
+
" Delete an ICP. ICPs still used by motions or agents are refused; archive them instead.",
|
|
11522
|
+
"",
|
|
11523
|
+
"Input shape:",
|
|
11524
|
+
" icp_id: icpp_ prefix id or integer id",
|
|
11525
|
+
" confirm: one of yes, true, Y, y",
|
|
11526
|
+
"",
|
|
11527
|
+
"API:",
|
|
11528
|
+
" DELETE /api/v1/accounts/:account_id/icps/:id.json"
|
|
11529
|
+
].join("\n")],
|
|
11530
|
+
|
|
11531
|
+
["icps prospects", [
|
|
11532
|
+
"Usage:",
|
|
11533
|
+
` ${ICPS_PROSPECTS_USAGE.slice("Usage: ".length)}`,
|
|
11534
|
+
"",
|
|
11535
|
+
"Status: implemented",
|
|
11536
|
+
"",
|
|
11537
|
+
"Purpose:",
|
|
11538
|
+
" List prospects matched to an ICP, newest first, optionally filtered by a text query.",
|
|
11539
|
+
"",
|
|
11540
|
+
"Input shape:",
|
|
11541
|
+
" icp_id: icpp_ prefix id or integer id",
|
|
11542
|
+
" limit: 1-100 (default 25)",
|
|
11543
|
+
"",
|
|
11544
|
+
"API:",
|
|
11545
|
+
" GET /api/v1/accounts/:account_id/icps/:id/prospects.json"
|
|
11546
|
+
].join("\n")],
|
|
11547
|
+
|
|
11548
|
+
["offers regenerate-research", [
|
|
11549
|
+
"Usage:",
|
|
11550
|
+
` ${OFFERS_REGENERATE_RESEARCH_USAGE.slice("Usage: ".length)}`,
|
|
11551
|
+
"",
|
|
11552
|
+
"Status: implemented",
|
|
11553
|
+
"",
|
|
11554
|
+
"Purpose:",
|
|
11555
|
+
" Queue a new research write-up for an offer, optionally steered by guidance. Returns once queued.",
|
|
11556
|
+
"",
|
|
11557
|
+
"Input shape:",
|
|
11558
|
+
" offr_id: offr_ prefixed id or integer id",
|
|
11559
|
+
" guidance: optional text",
|
|
11560
|
+
"",
|
|
11561
|
+
"API:",
|
|
11562
|
+
" POST /api/v1/accounts/:account_id/offers/:id/regenerate_research.json"
|
|
11563
|
+
].join("\n")],
|
|
11564
|
+
|
|
11565
|
+
["offers update-writeup", [
|
|
11566
|
+
"Usage:",
|
|
11567
|
+
` ${OFFERS_UPDATE_WRITEUP_USAGE.slice("Usage: ".length)}`,
|
|
11568
|
+
"",
|
|
11569
|
+
"Status: implemented",
|
|
11570
|
+
"",
|
|
11571
|
+
"Purpose:",
|
|
11572
|
+
" Replace an offer's write-up without queueing new research.",
|
|
11573
|
+
"",
|
|
11574
|
+
"Input shape:",
|
|
11575
|
+
" offr_id: offr_ prefixed id or integer id",
|
|
11576
|
+
" description: text",
|
|
11577
|
+
"",
|
|
11578
|
+
"API:",
|
|
11579
|
+
" PATCH /api/v1/accounts/:account_id/offers/:id/writeup.json"
|
|
11580
|
+
].join("\n")],
|
|
11581
|
+
|
|
11582
|
+
["offers add-artifacts", [
|
|
11583
|
+
"Usage:",
|
|
11584
|
+
` ${OFFERS_ADD_ARTIFACTS_USAGE.slice("Usage: ".length)}`,
|
|
11585
|
+
"",
|
|
11586
|
+
"Status: implemented",
|
|
11587
|
+
"",
|
|
11588
|
+
"Purpose:",
|
|
11589
|
+
" Attach local files to an offer as artifacts. `audienti offers show` lists attached artifacts.",
|
|
11590
|
+
"",
|
|
11591
|
+
"Input shape:",
|
|
11592
|
+
" offr_id: offr_ prefixed id or integer id",
|
|
11593
|
+
" file: local file path; sent as base64",
|
|
11594
|
+
"",
|
|
11595
|
+
"API:",
|
|
11596
|
+
" POST /api/v1/accounts/:account_id/offers/:offer_id/artifacts.json"
|
|
11597
|
+
].join("\n")],
|
|
11598
|
+
|
|
11599
|
+
["offers remove-artifact", [
|
|
11600
|
+
"Usage:",
|
|
11601
|
+
` ${OFFERS_REMOVE_ARTIFACT_USAGE.slice("Usage: ".length)}`,
|
|
11602
|
+
"",
|
|
11603
|
+
"Status: implemented",
|
|
11604
|
+
"",
|
|
11605
|
+
"Purpose:",
|
|
11606
|
+
" Remove one artifact from an offer.",
|
|
11607
|
+
"",
|
|
11608
|
+
"Input shape:",
|
|
11609
|
+
" artifact_id: id from `audienti offers show <offr_id>`",
|
|
11610
|
+
"",
|
|
11611
|
+
"API:",
|
|
11612
|
+
" DELETE /api/v1/accounts/:account_id/offers/:offer_id/artifacts/:id.json"
|
|
11613
|
+
].join("\n")],
|
|
11614
|
+
|
|
11615
|
+
["tasks update", [
|
|
11616
|
+
"Usage:",
|
|
11617
|
+
` ${TASKS_UPDATE_USAGE.slice("Usage: ".length)}`,
|
|
11618
|
+
"",
|
|
11619
|
+
"Status: implemented",
|
|
11620
|
+
"",
|
|
11621
|
+
"Purpose:",
|
|
11622
|
+
" Edit any task in the account. Only the options you pass change; pass an empty --prospect or --list to clear it.",
|
|
11623
|
+
"",
|
|
11624
|
+
"Input shape:",
|
|
11625
|
+
" task_id: ptsk_ prefix id or numeric id",
|
|
11626
|
+
" assigned-user: account user id or me",
|
|
11627
|
+
"",
|
|
11628
|
+
"API:",
|
|
11629
|
+
" PATCH /api/v1/accounts/:account_id/tasks/:id.json"
|
|
11630
|
+
].join("\n")],
|
|
11631
|
+
|
|
11632
|
+
["tasks bulk-update", [
|
|
11633
|
+
"Usage:",
|
|
11634
|
+
` ${TASKS_BULK_UPDATE_USAGE.slice("Usage: ".length)}`,
|
|
11635
|
+
"",
|
|
11636
|
+
"Status: implemented",
|
|
11637
|
+
"",
|
|
11638
|
+
"Purpose:",
|
|
11639
|
+
" Complete several tasks now, or queue their reassignment to one account user.",
|
|
11640
|
+
"",
|
|
11641
|
+
"Input shape:",
|
|
11642
|
+
" task_id: ptsk_ prefix ids; every id must belong to the account",
|
|
11643
|
+
" action: complete or reassign (reassign needs --assigned-user)",
|
|
11644
|
+
"",
|
|
11645
|
+
"API:",
|
|
11646
|
+
" PATCH /api/v1/accounts/:account_id/tasks/bulk_update.json"
|
|
11647
|
+
].join("\n")],
|
|
11648
|
+
|
|
11649
|
+
["tools linkedin-strategy-review delete", [
|
|
11650
|
+
"Usage:",
|
|
11651
|
+
` ${TOOLS_LINKEDIN_STRATEGY_REVIEW_DELETE_USAGE.slice("Usage: ".length)}`,
|
|
11652
|
+
"",
|
|
11653
|
+
"Status: implemented",
|
|
11654
|
+
"",
|
|
11655
|
+
"Purpose:",
|
|
11656
|
+
" Delete one LinkedIn strategy review report.",
|
|
11657
|
+
"",
|
|
11658
|
+
"Input shape:",
|
|
11659
|
+
" rprt_id: rprt_ report id of a strategy review",
|
|
11660
|
+
" confirm: one of yes, true, Y, y",
|
|
11661
|
+
"",
|
|
11662
|
+
"API:",
|
|
11663
|
+
" DELETE /api/v1/accounts/:account_id/tools/linkedin-strategy-review/reports/:id.json"
|
|
11664
|
+
].join("\n")],
|
|
11665
|
+
|
|
10579
11666
|
["lists add-tag", [
|
|
10580
11667
|
"Usage:",
|
|
10581
11668
|
` ${LISTS_ADD_TAG_USAGE.slice("Usage: ".length)}`,
|
|
@@ -12037,6 +13124,7 @@ const HELP_TOPICS = new Map([
|
|
|
12037
13124
|
" audienti tools linkedin-review reports [--limit <n>] [--json]",
|
|
12038
13125
|
" audienti tools linkedin-review show <rprt_id> [--json]",
|
|
12039
13126
|
" audienti tools linkedin-review status <rprt_id> [--json]",
|
|
13127
|
+
" audienti tools linkedin-strategy-review delete <rprt_id> --confirm <yes|true|Y|y> [--json]",
|
|
12040
13128
|
"",
|
|
12041
13129
|
"Status: implemented",
|
|
12042
13130
|
"",
|
|
@@ -12047,7 +13135,8 @@ const HELP_TOPICS = new Map([
|
|
|
12047
13135
|
" audienti tools linkedin-review Queue a LinkedIn personal profile authority review and ICP-fit positioning blueprint.",
|
|
12048
13136
|
" audienti tools linkedin-review reports List recent LinkedIn Review reports for the active account.",
|
|
12049
13137
|
" audienti tools linkedin-review show View the completed report content in the terminal.",
|
|
12050
|
-
" audienti tools linkedin-review status Show the current report stage and run status."
|
|
13138
|
+
" audienti tools linkedin-review status Show the current report stage and run status.",
|
|
13139
|
+
" audienti tools linkedin-strategy-review delete Delete one LinkedIn strategy review report."
|
|
12051
13140
|
].join("\n")],
|
|
12052
13141
|
|
|
12053
13142
|
["tools list", [
|