salesprompter-cli 0.1.54 → 0.1.56
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/README.md +12 -1
- package/dist/cli.js +81 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -68,7 +68,7 @@ salesprompter contacts:resolve-emails --in ./contacts.tsv --out-dir ./email-run
|
|
|
68
68
|
# Collect a Sales Navigator people search into the active workspace
|
|
69
69
|
salesprompter leads:collect \
|
|
70
70
|
--linkedin-url "$SALES_NAV_PEOPLE_URL" \
|
|
71
|
-
--max-results
|
|
71
|
+
--max-results 2000
|
|
72
72
|
|
|
73
73
|
# Enrich one or more stored imports: Sales Navigator company -> official domain -> Hunter email
|
|
74
74
|
# This updates workspace data only. It does not create or start outreach.
|
|
@@ -110,6 +110,17 @@ salesprompter packs:list
|
|
|
110
110
|
salesprompter --help
|
|
111
111
|
```
|
|
112
112
|
|
|
113
|
+
Download stored workspace leads without starting a new Sales Navigator scrape:
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
salesprompter leads:download --out ./salesprompter-leads.csv
|
|
117
|
+
salesprompter leads:download --run-id <uuid> --emails-only --out ./run-leads.csv
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Use `--search <value>` to match a person, title, or company. The command uses the
|
|
121
|
+
active Salesprompter workspace and downloads the same filtered data available in
|
|
122
|
+
the app's CLI imports view.
|
|
123
|
+
|
|
113
124
|
## Output modes
|
|
114
125
|
|
|
115
126
|
- `--json` for machine-readable output
|
package/dist/cli.js
CHANGED
|
@@ -526,6 +526,7 @@ const cliPacks = [
|
|
|
526
526
|
commands: [
|
|
527
527
|
"leads:discover",
|
|
528
528
|
"leads:collect",
|
|
529
|
+
"leads:download",
|
|
529
530
|
"leads:enrich-import",
|
|
530
531
|
"search:run",
|
|
531
532
|
"search:status",
|
|
@@ -587,6 +588,7 @@ const helpVisibleCommandNames = new Set([
|
|
|
587
588
|
"wizard",
|
|
588
589
|
"auth:whoami",
|
|
589
590
|
"llm:ready",
|
|
591
|
+
"leads:download",
|
|
590
592
|
"contacts:find-linkedin-urls",
|
|
591
593
|
"companies:find-linkedin-urls",
|
|
592
594
|
"contacts:process-emails",
|
|
@@ -6757,6 +6759,59 @@ async function runCliImportEnrichmentAction(session, body, schema) {
|
|
|
6757
6759
|
}), schema);
|
|
6758
6760
|
return value;
|
|
6759
6761
|
}
|
|
6762
|
+
function parseDownloadFilename(contentDisposition) {
|
|
6763
|
+
const match = contentDisposition?.match(/filename="?([^";]+)"?/i);
|
|
6764
|
+
return match?.[1]?.trim() || `salesprompter-leads-${new Date().toISOString().slice(0, 10)}.csv`;
|
|
6765
|
+
}
|
|
6766
|
+
async function runLeadsDownloadCommand(options) {
|
|
6767
|
+
const session = await requireAuthSession();
|
|
6768
|
+
const runId = options.runId ? z.string().uuid().parse(options.runId) : null;
|
|
6769
|
+
const search = options.search?.trim() || null;
|
|
6770
|
+
const { value } = await withRefreshableAuthSession(session, async (currentSession) => {
|
|
6771
|
+
const url = new URL('/api/cli/leads/imports/export', currentSession.apiBaseUrl);
|
|
6772
|
+
if (runId)
|
|
6773
|
+
url.searchParams.set('runId', runId);
|
|
6774
|
+
if (search)
|
|
6775
|
+
url.searchParams.set('q', search);
|
|
6776
|
+
if (options.emailsOnly)
|
|
6777
|
+
url.searchParams.set('emailsOnly', '1');
|
|
6778
|
+
const response = await fetch(url, {
|
|
6779
|
+
headers: { Authorization: `Bearer ${currentSession.accessToken}` },
|
|
6780
|
+
});
|
|
6781
|
+
if (!response.ok) {
|
|
6782
|
+
const raw = await response.text();
|
|
6783
|
+
let message = `lead download failed (${response.status})`;
|
|
6784
|
+
try {
|
|
6785
|
+
const payload = JSON.parse(raw);
|
|
6786
|
+
if (typeof payload.error === 'string')
|
|
6787
|
+
message = payload.error;
|
|
6788
|
+
}
|
|
6789
|
+
catch {
|
|
6790
|
+
if (raw.trim())
|
|
6791
|
+
message = raw.trim();
|
|
6792
|
+
}
|
|
6793
|
+
throw new CliApiRequestError(message, { statusCode: response.status });
|
|
6794
|
+
}
|
|
6795
|
+
const countHeader = response.headers.get('x-salesprompter-lead-count');
|
|
6796
|
+
return {
|
|
6797
|
+
bytes: Buffer.from(await response.arrayBuffer()),
|
|
6798
|
+
count: countHeader == null ? null : Number.parseInt(countHeader, 10),
|
|
6799
|
+
filename: parseDownloadFilename(response.headers.get('content-disposition')),
|
|
6800
|
+
};
|
|
6801
|
+
});
|
|
6802
|
+
const outputPath = path.resolve(options.out?.trim() || value.filename);
|
|
6803
|
+
await mkdir(path.dirname(outputPath), { recursive: true });
|
|
6804
|
+
await writeFile(outputPath, value.bytes);
|
|
6805
|
+
return {
|
|
6806
|
+
status: 'ok',
|
|
6807
|
+
output: outputPath,
|
|
6808
|
+
leads: Number.isFinite(value.count) ? value.count : null,
|
|
6809
|
+
bytes: value.bytes.length,
|
|
6810
|
+
runId,
|
|
6811
|
+
search,
|
|
6812
|
+
emailsOnly: options.emailsOnly,
|
|
6813
|
+
};
|
|
6814
|
+
}
|
|
6760
6815
|
async function getCliImportCompanyCandidatesViaApp(session, input) {
|
|
6761
6816
|
return runCliImportEnrichmentAction(session, {
|
|
6762
6817
|
action: "company-candidates",
|
|
@@ -13328,9 +13383,15 @@ program
|
|
|
13328
13383
|
}
|
|
13329
13384
|
});
|
|
13330
13385
|
});
|
|
13386
|
+
const SALES_NAVIGATOR_PEOPLE_MAX_RESULTS = 2_000;
|
|
13331
13387
|
async function runSalesNavigatorPeopleCollectCommand(options) {
|
|
13332
13388
|
const linkedInUrl = z.string().url().parse(options.linkedinUrl);
|
|
13333
|
-
const maxResults = z.coerce
|
|
13389
|
+
const maxResults = z.coerce
|
|
13390
|
+
.number()
|
|
13391
|
+
.int()
|
|
13392
|
+
.min(1)
|
|
13393
|
+
.max(SALES_NAVIGATOR_PEOPLE_MAX_RESULTS)
|
|
13394
|
+
.parse(options.maxResults);
|
|
13334
13395
|
const pageSize = z.coerce.number().int().min(1).max(100).parse(options.pageSize);
|
|
13335
13396
|
const normalizedSourceUrl = (() => {
|
|
13336
13397
|
const source = new URL(linkedInUrl);
|
|
@@ -14186,7 +14247,12 @@ async function runSalesNavigatorImportEnrichmentCommand(options) {
|
|
|
14186
14247
|
async function runAffiliateLaunchCommand(options) {
|
|
14187
14248
|
const affiliateLink = z.string().url().parse(options.affiliateLink);
|
|
14188
14249
|
const linkedInUrl = z.string().url().parse(options.linkedinUrl);
|
|
14189
|
-
const maxResults = z.coerce
|
|
14250
|
+
const maxResults = z.coerce
|
|
14251
|
+
.number()
|
|
14252
|
+
.int()
|
|
14253
|
+
.min(1)
|
|
14254
|
+
.max(SALES_NAVIGATOR_PEOPLE_MAX_RESULTS)
|
|
14255
|
+
.parse(options.maxResults);
|
|
14190
14256
|
const session = await requireAuthSession();
|
|
14191
14257
|
const isSalesNavigatorPeopleSearch = new URL(linkedInUrl).pathname.includes("/sales/search/people");
|
|
14192
14258
|
let localResults;
|
|
@@ -14259,7 +14325,7 @@ function addAffiliateAudienceOptions(command) {
|
|
|
14259
14325
|
return command
|
|
14260
14326
|
.requiredOption("--affiliate-link <url>", "Affiliate link to promote")
|
|
14261
14327
|
.requiredOption("--linkedin-url <url>", "Sales Navigator people or LinkedIn content search URL")
|
|
14262
|
-
.option("--max-results <number>", "Maximum audience results",
|
|
14328
|
+
.option("--max-results <number>", "Maximum audience results (people searches: 2000)", String(SALES_NAVIGATOR_PEOPLE_MAX_RESULTS))
|
|
14263
14329
|
.option("--curl-file <path>", "Optional copied Sales Navigator Lead Search curl request")
|
|
14264
14330
|
.option("--browser-relay-port <number>", "Use the signed-in browser through a loopback relay")
|
|
14265
14331
|
.option("--page-size <number>", "Direct Sales Navigator page size", "100")
|
|
@@ -14273,7 +14339,7 @@ program
|
|
|
14273
14339
|
.alias("leads:collect")
|
|
14274
14340
|
.description("Collect a Sales Navigator people search into the active workspace.")
|
|
14275
14341
|
.requiredOption("--linkedin-url <url>", "Sales Navigator people search URL")
|
|
14276
|
-
.option("--max-results <number>", "Maximum people to collect",
|
|
14342
|
+
.option("--max-results <number>", "Maximum people to collect", String(SALES_NAVIGATOR_PEOPLE_MAX_RESULTS))
|
|
14277
14343
|
.option("--curl-file <path>", "Optional copied Sales Navigator Lead Search curl request")
|
|
14278
14344
|
.option("--browser-relay-port <number>", "Use the signed-in browser through a loopback relay")
|
|
14279
14345
|
.option("--page-size <number>", "Direct Sales Navigator page size", "100")
|
|
@@ -14284,6 +14350,17 @@ program
|
|
|
14284
14350
|
.action(async (options) => {
|
|
14285
14351
|
printOutput(await runSalesNavigatorPeopleCollectCommand(options));
|
|
14286
14352
|
});
|
|
14353
|
+
program
|
|
14354
|
+
.command("leads:download")
|
|
14355
|
+
.alias("leads:export-csv")
|
|
14356
|
+
.description("Download stored leads from the active Salesprompter workspace as CSV.")
|
|
14357
|
+
.option("--run-id <uuid>", "Limit the CSV to one CLI import run")
|
|
14358
|
+
.option("--search <value>", "Filter by person, title, or company")
|
|
14359
|
+
.option("--emails-only", "Include only leads with a found email", false)
|
|
14360
|
+
.option("--out <path>", "CSV output path; defaults to a dated filename")
|
|
14361
|
+
.action(async (options) => {
|
|
14362
|
+
printOutput(await runLeadsDownloadCommand(options));
|
|
14363
|
+
});
|
|
14287
14364
|
program
|
|
14288
14365
|
.command("salesnav:people:enrich")
|
|
14289
14366
|
.alias("leads:enrich-import")
|
package/package.json
CHANGED