salesprompter-cli 0.1.13 → 0.1.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +264 -49
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { spawn } from "node:child_process";
|
|
3
3
|
import { createRequire } from "node:module";
|
|
4
|
+
import { emitKeypressEvents } from "node:readline";
|
|
4
5
|
import { createInterface } from "node:readline/promises";
|
|
5
6
|
import { Command } from "commander";
|
|
6
7
|
import { z } from "zod";
|
|
@@ -175,30 +176,151 @@ function deriveCompanyNameFromDomain(domain) {
|
|
|
175
176
|
function writeWizardLine(message = "") {
|
|
176
177
|
process.stdout.write(`${message}\n`);
|
|
177
178
|
}
|
|
179
|
+
function isOpaqueOrgId(value) {
|
|
180
|
+
return /^org_[A-Za-z0-9]+$/.test(value);
|
|
181
|
+
}
|
|
178
182
|
function getOrgLabel(session) {
|
|
179
|
-
|
|
183
|
+
const label = session.user.orgName ?? session.user.orgSlug ?? session.user.orgId ?? null;
|
|
184
|
+
if (label && isOpaqueOrgId(label)) {
|
|
185
|
+
return null;
|
|
186
|
+
}
|
|
187
|
+
return label;
|
|
180
188
|
}
|
|
181
189
|
function writeSessionSummary(session) {
|
|
190
|
+
const identity = session.user.name?.trim()
|
|
191
|
+
? `${session.user.name} (${session.user.email})`
|
|
192
|
+
: session.user.email;
|
|
193
|
+
writeWizardLine(`Signed in as ${identity}.`);
|
|
182
194
|
const orgLabel = getOrgLabel(session);
|
|
183
195
|
if (orgLabel) {
|
|
184
|
-
writeWizardLine(`
|
|
185
|
-
return;
|
|
196
|
+
writeWizardLine(`Workspace: ${orgLabel}`);
|
|
186
197
|
}
|
|
187
|
-
|
|
188
|
-
|
|
198
|
+
}
|
|
199
|
+
function normalizeChoiceText(value) {
|
|
200
|
+
return value.trim().toLowerCase().replace(/[^a-z0-9]+/g, " ").replace(/\s+/g, " ").trim();
|
|
201
|
+
}
|
|
202
|
+
function supportsInteractiveChoice() {
|
|
203
|
+
return Boolean(process.stdin.isTTY && process.stdout.isTTY && typeof process.stdin.setRawMode === "function");
|
|
204
|
+
}
|
|
205
|
+
function renderInteractiveChoiceLines(prompt, options, activeIndex) {
|
|
206
|
+
const lines = [prompt];
|
|
207
|
+
for (const [index, option] of options.entries()) {
|
|
208
|
+
const description = option.description ? ` - ${option.description}` : "";
|
|
209
|
+
const text = `${index + 1}. ${option.label}${description}`;
|
|
210
|
+
if (index === activeIndex) {
|
|
211
|
+
lines.push(`\x1b[7m> ${text}\x1b[0m`);
|
|
212
|
+
}
|
|
213
|
+
else {
|
|
214
|
+
lines.push(` ${text}`);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
lines.push("\x1b[2mUse Up/Down or number keys. Press Enter to continue.\x1b[0m");
|
|
218
|
+
return lines;
|
|
219
|
+
}
|
|
220
|
+
function redrawInteractiveChoice(lines, previousLineCount) {
|
|
221
|
+
if (previousLineCount > 0) {
|
|
222
|
+
for (let index = 0; index < previousLineCount; index += 1) {
|
|
223
|
+
process.stdout.write("\x1b[1A\x1b[2K\r");
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
for (const line of lines) {
|
|
227
|
+
process.stdout.write(`${line}\n`);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
async function promptChoiceInteractive(prompt, options, defaultIndex) {
|
|
231
|
+
const stdin = process.stdin;
|
|
232
|
+
let activeIndex = defaultIndex;
|
|
233
|
+
let renderedLineCount = 0;
|
|
234
|
+
const render = () => {
|
|
235
|
+
const lines = renderInteractiveChoiceLines(prompt, options, activeIndex);
|
|
236
|
+
redrawInteractiveChoice(lines, renderedLineCount);
|
|
237
|
+
renderedLineCount = lines.length;
|
|
238
|
+
};
|
|
239
|
+
const cleanup = () => {
|
|
240
|
+
stdin.removeListener("keypress", onKeypress);
|
|
241
|
+
process.removeListener("SIGINT", onSigint);
|
|
242
|
+
if (stdin.isTTY) {
|
|
243
|
+
stdin.setRawMode(false);
|
|
244
|
+
}
|
|
245
|
+
stdin.pause();
|
|
246
|
+
};
|
|
247
|
+
const finalize = (index, resolve) => {
|
|
248
|
+
const selected = options[index];
|
|
249
|
+
if (!selected) {
|
|
250
|
+
throw new Error("wizard selection invariant violated");
|
|
251
|
+
}
|
|
252
|
+
const summary = `${prompt} ${selected.label}`;
|
|
253
|
+
redrawInteractiveChoice([summary], renderedLineCount);
|
|
254
|
+
renderedLineCount = 1;
|
|
255
|
+
process.stdout.write("\n");
|
|
256
|
+
cleanup();
|
|
257
|
+
resolve(selected.value);
|
|
258
|
+
};
|
|
259
|
+
const cancel = (reject) => {
|
|
260
|
+
cleanup();
|
|
261
|
+
process.stdout.write("\n");
|
|
262
|
+
reject(new Error("prompt cancelled"));
|
|
263
|
+
};
|
|
264
|
+
const onSigint = () => {
|
|
265
|
+
cancel(rejectPromise);
|
|
266
|
+
};
|
|
267
|
+
const onKeypress = (_character, key) => {
|
|
268
|
+
if (key.ctrl && key.name === "c") {
|
|
269
|
+
return cancel(rejectPromise);
|
|
270
|
+
}
|
|
271
|
+
if (key.name === "up") {
|
|
272
|
+
activeIndex = activeIndex === 0 ? options.length - 1 : activeIndex - 1;
|
|
273
|
+
render();
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
if (key.name === "down") {
|
|
277
|
+
activeIndex = activeIndex === options.length - 1 ? 0 : activeIndex + 1;
|
|
278
|
+
render();
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
if (key.name === "return" || key.name === "enter") {
|
|
282
|
+
finalize(activeIndex, resolvePromise);
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
const digit = Number(key.sequence ?? "");
|
|
286
|
+
if (Number.isInteger(digit) && digit >= 1 && digit <= options.length) {
|
|
287
|
+
finalize(digit - 1, resolvePromise);
|
|
288
|
+
}
|
|
289
|
+
};
|
|
290
|
+
let resolvePromise;
|
|
291
|
+
let rejectPromise;
|
|
292
|
+
return await new Promise((resolve, reject) => {
|
|
293
|
+
resolvePromise = resolve;
|
|
294
|
+
rejectPromise = reject;
|
|
295
|
+
emitKeypressEvents(stdin);
|
|
296
|
+
stdin.setRawMode(true);
|
|
297
|
+
stdin.resume();
|
|
298
|
+
stdin.on("keypress", onKeypress);
|
|
299
|
+
process.on("SIGINT", onSigint);
|
|
300
|
+
render();
|
|
301
|
+
});
|
|
189
302
|
}
|
|
190
303
|
async function promptChoice(rl, prompt, options, defaultValue) {
|
|
191
304
|
const defaultIndex = options.findIndex((option) => option.value === defaultValue);
|
|
192
305
|
if (defaultIndex === -1) {
|
|
193
306
|
throw new Error(`wizard default option is invalid for ${prompt}`);
|
|
194
307
|
}
|
|
308
|
+
if (supportsInteractiveChoice()) {
|
|
309
|
+
rl.pause?.();
|
|
310
|
+
try {
|
|
311
|
+
return await promptChoiceInteractive(prompt, options, defaultIndex);
|
|
312
|
+
}
|
|
313
|
+
finally {
|
|
314
|
+
rl.resume?.();
|
|
315
|
+
}
|
|
316
|
+
}
|
|
195
317
|
while (true) {
|
|
196
318
|
writeWizardLine(prompt);
|
|
197
319
|
for (const [index, option] of options.entries()) {
|
|
198
320
|
const description = option.description ? ` - ${option.description}` : "";
|
|
199
321
|
writeWizardLine(` ${index + 1}. ${option.label}${description}`);
|
|
200
322
|
}
|
|
201
|
-
const answer = (await rl.question(`
|
|
323
|
+
const answer = (await rl.question(`Select an option (press Enter for ${defaultIndex + 1}): `)).trim();
|
|
202
324
|
if (answer.length === 0) {
|
|
203
325
|
return defaultValue;
|
|
204
326
|
}
|
|
@@ -210,11 +332,25 @@ async function promptChoice(rl, prompt, options, defaultValue) {
|
|
|
210
332
|
}
|
|
211
333
|
return selected.value;
|
|
212
334
|
}
|
|
213
|
-
const
|
|
335
|
+
const normalizedAnswer = normalizeChoiceText(answer);
|
|
336
|
+
const matched = options.find((option) => {
|
|
337
|
+
if (normalizeChoiceText(option.value) === normalizedAnswer) {
|
|
338
|
+
return true;
|
|
339
|
+
}
|
|
340
|
+
if (normalizeChoiceText(option.label) === normalizedAnswer) {
|
|
341
|
+
return true;
|
|
342
|
+
}
|
|
343
|
+
return (option.aliases ?? []).some((alias) => normalizeChoiceText(alias) === normalizedAnswer);
|
|
344
|
+
});
|
|
214
345
|
if (matched) {
|
|
215
346
|
return matched.value;
|
|
216
347
|
}
|
|
217
|
-
|
|
348
|
+
if (/^(npx|salesprompter)\b/i.test(answer)) {
|
|
349
|
+
writeWizardLine("You are already in the wizard. Pick an option here, or press Ctrl-C to exit.");
|
|
350
|
+
}
|
|
351
|
+
else {
|
|
352
|
+
writeWizardLine("Please choose one of the options above.");
|
|
353
|
+
}
|
|
218
354
|
writeWizardLine();
|
|
219
355
|
}
|
|
220
356
|
}
|
|
@@ -265,7 +401,7 @@ async function ensureWizardSession(options) {
|
|
|
265
401
|
throw error;
|
|
266
402
|
}
|
|
267
403
|
}
|
|
268
|
-
writeWizardLine("
|
|
404
|
+
writeWizardLine("First, sign in to Salesprompter.");
|
|
269
405
|
writeWizardLine();
|
|
270
406
|
const result = await performLogin({
|
|
271
407
|
apiUrl: options?.apiUrl,
|
|
@@ -276,6 +412,90 @@ async function ensureWizardSession(options) {
|
|
|
276
412
|
return result.session;
|
|
277
413
|
}
|
|
278
414
|
async function runVendorIcpWizard(rl) {
|
|
415
|
+
const startPoint = await promptChoice(rl, "How do you want to start?", [
|
|
416
|
+
{
|
|
417
|
+
value: "custom",
|
|
418
|
+
label: "Create a custom profile",
|
|
419
|
+
description: "Answer a few questions about who you want to sell to",
|
|
420
|
+
aliases: ["custom", "from scratch", "new profile"]
|
|
421
|
+
},
|
|
422
|
+
{
|
|
423
|
+
value: "template",
|
|
424
|
+
label: "Use the Deel template",
|
|
425
|
+
description: "Quick start from the current built-in template",
|
|
426
|
+
aliases: ["template", "deel", "use template"]
|
|
427
|
+
}
|
|
428
|
+
], "custom");
|
|
429
|
+
writeWizardLine();
|
|
430
|
+
if (startPoint === "custom") {
|
|
431
|
+
const productName = await promptText(rl, "What are you selling?", { required: true });
|
|
432
|
+
const description = await promptText(rl, "Who is this for? (optional)");
|
|
433
|
+
const industries = await promptText(rl, "Target industries (optional, comma-separated)");
|
|
434
|
+
const companySizes = await promptText(rl, "Target company sizes (optional, comma-separated)");
|
|
435
|
+
const regions = await promptText(rl, "Target regions (optional, comma-separated)");
|
|
436
|
+
const countries = await promptText(rl, "Target countries (optional, comma-separated)");
|
|
437
|
+
const titles = await promptText(rl, "Target job titles (optional, comma-separated)");
|
|
438
|
+
const keywords = await promptText(rl, "Keywords or buying signals (optional, comma-separated)");
|
|
439
|
+
writeWizardLine();
|
|
440
|
+
const slug = slugify(productName) || "icp";
|
|
441
|
+
const outPath = await promptText(rl, "Where should I save the ICP JSON?", {
|
|
442
|
+
defaultValue: `./data/${slug}-icp.json`,
|
|
443
|
+
required: true
|
|
444
|
+
});
|
|
445
|
+
writeWizardLine();
|
|
446
|
+
const icp = IcpSchema.parse({
|
|
447
|
+
name: `${productName} ICP`,
|
|
448
|
+
description,
|
|
449
|
+
industries: splitCsv(industries),
|
|
450
|
+
companySizes: splitCsv(companySizes),
|
|
451
|
+
regions: splitCsv(regions),
|
|
452
|
+
countries: splitCsv(countries),
|
|
453
|
+
titles: splitCsv(titles),
|
|
454
|
+
keywords: splitCsv(keywords)
|
|
455
|
+
});
|
|
456
|
+
await writeJsonFile(outPath, icp);
|
|
457
|
+
writeWizardLine(`Created ${icp.name}.`);
|
|
458
|
+
writeWizardLine(`Saved ICP to ${outPath}.`);
|
|
459
|
+
writeWizardLine();
|
|
460
|
+
writeWizardLine("Equivalent raw command:");
|
|
461
|
+
const defineArgs = ["salesprompter", "icp:define", "--name", icp.name];
|
|
462
|
+
if (description.trim().length > 0) {
|
|
463
|
+
defineArgs.push("--description", description);
|
|
464
|
+
}
|
|
465
|
+
if (industries.trim().length > 0) {
|
|
466
|
+
defineArgs.push("--industries", industries);
|
|
467
|
+
}
|
|
468
|
+
if (companySizes.trim().length > 0) {
|
|
469
|
+
defineArgs.push("--company-sizes", companySizes);
|
|
470
|
+
}
|
|
471
|
+
if (regions.trim().length > 0) {
|
|
472
|
+
defineArgs.push("--regions", regions);
|
|
473
|
+
}
|
|
474
|
+
if (countries.trim().length > 0) {
|
|
475
|
+
defineArgs.push("--countries", countries);
|
|
476
|
+
}
|
|
477
|
+
if (titles.trim().length > 0) {
|
|
478
|
+
defineArgs.push("--titles", titles);
|
|
479
|
+
}
|
|
480
|
+
if (keywords.trim().length > 0) {
|
|
481
|
+
defineArgs.push("--keywords", keywords);
|
|
482
|
+
}
|
|
483
|
+
defineArgs.push("--out", outPath);
|
|
484
|
+
writeWizardLine(` ${buildCommandLine(defineArgs)}`);
|
|
485
|
+
writeWizardLine();
|
|
486
|
+
writeWizardLine("Next suggested command:");
|
|
487
|
+
writeWizardLine(` ${buildCommandLine([
|
|
488
|
+
"salesprompter",
|
|
489
|
+
"leads:generate",
|
|
490
|
+
"--icp",
|
|
491
|
+
outPath,
|
|
492
|
+
"--count",
|
|
493
|
+
"5",
|
|
494
|
+
"--out",
|
|
495
|
+
`./data/${slug}-leads.json`
|
|
496
|
+
])}`);
|
|
497
|
+
return;
|
|
498
|
+
}
|
|
279
499
|
const vendor = "deel";
|
|
280
500
|
writeWizardLine("Using the built-in Deel ICP template.");
|
|
281
501
|
writeWizardLine();
|
|
@@ -374,13 +594,15 @@ async function runLeadGenerationWizard(rl) {
|
|
|
374
594
|
const source = await promptChoice(rl, "How do you want to generate leads?", [
|
|
375
595
|
{
|
|
376
596
|
value: "target-account",
|
|
377
|
-
label: "At
|
|
378
|
-
description: "Example: find people at
|
|
597
|
+
label: "At one company",
|
|
598
|
+
description: "Example: find people at acme.com",
|
|
599
|
+
aliases: ["company", "one company", "specific company", "account"]
|
|
379
600
|
},
|
|
380
601
|
{
|
|
381
602
|
value: "vendor-lookup",
|
|
382
|
-
label: "From BigQuery",
|
|
383
|
-
description: "Use
|
|
603
|
+
label: "From my BigQuery data",
|
|
604
|
+
description: "Use a saved profile to search the lead data you already have",
|
|
605
|
+
aliases: ["bigquery", "warehouse", "my data", "from bigquery"]
|
|
384
606
|
}
|
|
385
607
|
], "target-account");
|
|
386
608
|
writeWizardLine();
|
|
@@ -391,41 +613,32 @@ async function runLeadGenerationWizard(rl) {
|
|
|
391
613
|
await runVendorLookupWizard(rl);
|
|
392
614
|
}
|
|
393
615
|
async function runVendorLookupWizard(rl) {
|
|
394
|
-
const
|
|
395
|
-
|
|
396
|
-
writeWizardLine();
|
|
397
|
-
const market = await promptChoice(rl, "Which market should the BigQuery lookup target?", [
|
|
398
|
-
{ value: "dach", label: "DACH", description: "Germany, Austria, Switzerland" },
|
|
399
|
-
{ value: "europe", label: "Europe" },
|
|
400
|
-
{ value: "global", label: "Global" }
|
|
401
|
-
], "dach");
|
|
402
|
-
const limit = z.coerce.number().int().min(1).max(5000).parse(await promptText(rl, "How many rows should the lookup return?", { defaultValue: "100", required: true }));
|
|
403
|
-
const execute = await promptYesNo(rl, "Execute the BigQuery lookup now?", false);
|
|
404
|
-
writeWizardLine();
|
|
405
|
-
const slug = slugify(vendor);
|
|
406
|
-
const icpPath = await promptText(rl, "Where should I save the ICP JSON?", {
|
|
407
|
-
defaultValue: `./data/${slug}-icp-${market}.json`,
|
|
616
|
+
const icpPath = await promptText(rl, "Where is your ICP JSON file?", {
|
|
617
|
+
defaultValue: "./data/icp.json",
|
|
408
618
|
required: true
|
|
409
619
|
});
|
|
620
|
+
const limit = z.coerce.number().int().min(1).max(5000).parse(await promptText(rl, "How many leads should I look up?", { defaultValue: "100", required: true }));
|
|
621
|
+
const execute = await promptYesNo(rl, "Execute the BigQuery lookup now?", false);
|
|
622
|
+
writeWizardLine();
|
|
623
|
+
const icp = await readJsonFile(icpPath, IcpSchema);
|
|
624
|
+
const slug = slugify(icp.name) || "icp";
|
|
410
625
|
const sqlPath = await promptText(rl, "Where should I save the generated SQL?", {
|
|
411
|
-
defaultValue: `./data/${slug}-lookup
|
|
626
|
+
defaultValue: `./data/${slug}-lookup.sql`,
|
|
412
627
|
required: true
|
|
413
628
|
});
|
|
414
629
|
const rawPath = execute
|
|
415
630
|
? await promptText(rl, "Where should I save raw BigQuery rows?", {
|
|
416
|
-
defaultValue: `./data/${slug}-leads-raw
|
|
631
|
+
defaultValue: `./data/${slug}-leads-raw.json`,
|
|
417
632
|
required: true
|
|
418
633
|
})
|
|
419
634
|
: "";
|
|
420
635
|
const leadPath = execute
|
|
421
636
|
? await promptText(rl, "Where should I save normalized leads?", {
|
|
422
|
-
defaultValue: `./data/${slug}-leads
|
|
637
|
+
defaultValue: `./data/${slug}-leads.json`,
|
|
423
638
|
required: true
|
|
424
639
|
})
|
|
425
640
|
: "";
|
|
426
641
|
writeWizardLine();
|
|
427
|
-
const icp = buildVendorIcp(vendor, market);
|
|
428
|
-
await writeJsonFile(icpPath, icp);
|
|
429
642
|
const sql = buildBigQueryLeadLookupSql(icp, {
|
|
430
643
|
table: "icpidentifier.SalesGPT.leadPool_new",
|
|
431
644
|
companyField: "companyName",
|
|
@@ -453,15 +666,14 @@ async function runVendorLookupWizard(rl) {
|
|
|
453
666
|
await writeJsonFile(leadPath, normalizedLeads);
|
|
454
667
|
executedRowCount = parsedRows.length;
|
|
455
668
|
}
|
|
456
|
-
writeWizardLine(`
|
|
669
|
+
writeWizardLine(`Using ICP from ${icpPath}.`);
|
|
457
670
|
writeWizardLine(`Saved lookup SQL to ${sqlPath}.`);
|
|
458
671
|
if (execute) {
|
|
459
672
|
writeWizardLine(`Saved ${executedRowCount ?? 0} raw rows to ${rawPath}.`);
|
|
460
673
|
writeWizardLine(`Saved normalized leads to ${leadPath}.`);
|
|
461
674
|
}
|
|
462
675
|
writeWizardLine();
|
|
463
|
-
writeWizardLine("Equivalent raw
|
|
464
|
-
writeWizardLine(` ${buildCommandLine(["salesprompter", "icp:vendor", "--vendor", vendor, "--market", market, "--out", icpPath])}`);
|
|
676
|
+
writeWizardLine("Equivalent raw command:");
|
|
465
677
|
const lookupArgs = ["salesprompter", "leads:lookup:bq", "--icp", icpPath, "--limit", String(limit), "--sql-out", sqlPath];
|
|
466
678
|
if (execute) {
|
|
467
679
|
lookupArgs.push("--execute", "--out", rawPath, "--lead-out", leadPath);
|
|
@@ -469,13 +681,8 @@ async function runVendorLookupWizard(rl) {
|
|
|
469
681
|
writeWizardLine(` ${buildCommandLine(lookupArgs)}`);
|
|
470
682
|
}
|
|
471
683
|
async function runOutreachSyncWizard(rl) {
|
|
472
|
-
const target =
|
|
473
|
-
|
|
474
|
-
value: "instantly",
|
|
475
|
-
label: "Instantly",
|
|
476
|
-
description: "Create campaign leads from a scored leads JSON file"
|
|
477
|
-
}
|
|
478
|
-
], "instantly");
|
|
684
|
+
const target = "instantly";
|
|
685
|
+
writeWizardLine("Using Instantly.");
|
|
479
686
|
writeWizardLine();
|
|
480
687
|
if (!process.env.INSTANTLY_API_KEY || process.env.INSTANTLY_API_KEY.trim().length === 0) {
|
|
481
688
|
throw new Error("INSTANTLY_API_KEY is required for the Instantly sync flow.");
|
|
@@ -530,8 +737,8 @@ async function runWizard(options) {
|
|
|
530
737
|
if (runtimeOutputOptions.json || runtimeOutputOptions.quiet) {
|
|
531
738
|
throw new Error("wizard does not support --json or --quiet.");
|
|
532
739
|
}
|
|
533
|
-
writeWizardLine("Salesprompter
|
|
534
|
-
writeWizardLine("
|
|
740
|
+
writeWizardLine("Salesprompter");
|
|
741
|
+
writeWizardLine("Tell me what you want to do, and I will guide you through it.");
|
|
535
742
|
writeWizardLine();
|
|
536
743
|
await ensureWizardSession(options);
|
|
537
744
|
const rl = createInterface({
|
|
@@ -542,18 +749,21 @@ async function runWizard(options) {
|
|
|
542
749
|
const flow = await promptChoice(rl, "What do you want help with?", [
|
|
543
750
|
{
|
|
544
751
|
value: "vendor-icp",
|
|
545
|
-
label: "
|
|
546
|
-
description: "
|
|
752
|
+
label: "Figure out who to target",
|
|
753
|
+
description: "Create an ideal customer profile for your product",
|
|
754
|
+
aliases: ["icp", "ideal customer", "who to target", "targeting", "profile"]
|
|
547
755
|
},
|
|
548
756
|
{
|
|
549
757
|
value: "lead-generation",
|
|
550
758
|
label: "Generate leads",
|
|
551
|
-
description: "Find people at
|
|
759
|
+
description: "Find people at one company or from your BigQuery data",
|
|
760
|
+
aliases: ["leads", "find leads", "lead generation", "find people"]
|
|
552
761
|
},
|
|
553
762
|
{
|
|
554
763
|
value: "outreach-sync",
|
|
555
|
-
label: "
|
|
556
|
-
description: "
|
|
764
|
+
label: "Add leads to Instantly",
|
|
765
|
+
description: "Send a scored leads file to an Instantly campaign",
|
|
766
|
+
aliases: ["instantly", "outreach", "send leads", "campaign"]
|
|
557
767
|
}
|
|
558
768
|
], "vendor-icp");
|
|
559
769
|
writeWizardLine();
|
|
@@ -1452,6 +1662,11 @@ async function main() {
|
|
|
1452
1662
|
await program.parseAsync(process.argv);
|
|
1453
1663
|
}
|
|
1454
1664
|
main().catch((error) => {
|
|
1665
|
+
if (error instanceof Error &&
|
|
1666
|
+
(error.message === "prompt cancelled" || error.message === "readline was closed")) {
|
|
1667
|
+
process.exitCode = 130;
|
|
1668
|
+
return;
|
|
1669
|
+
}
|
|
1455
1670
|
const cliError = buildCliError(error);
|
|
1456
1671
|
const space = runtimeOutputOptions.json ? undefined : 2;
|
|
1457
1672
|
if (runtimeOutputOptions.json) {
|
package/package.json
CHANGED