salesprompter-cli 0.1.58 → 0.1.60
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 +2 -1
- package/dist/cli.js +144 -87
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -66,7 +66,8 @@ salesprompter companies:resolve-linkedin-urls --in ./companies.txt --out ./compa
|
|
|
66
66
|
salesprompter contacts:resolve-emails --in ./contacts.tsv --out-dir ./email-run --dry-run
|
|
67
67
|
|
|
68
68
|
# Collect a Sales Navigator people search into the active workspace
|
|
69
|
-
#
|
|
69
|
+
# Normal people searches use 2,500-result slices; Connections-of searches use 1,000.
|
|
70
|
+
# Oversized searches split by company headcount and deduplicate by canonical profile URL.
|
|
70
71
|
salesprompter leads:collect \
|
|
71
72
|
--linkedin-url "$SALES_NAV_PEOPLE_URL"
|
|
72
73
|
|
package/dist/cli.js
CHANGED
|
@@ -8019,7 +8019,7 @@ async function createLocalAccountSearchBrowserRelay(requestedPort) {
|
|
|
8019
8019
|
},
|
|
8020
8020
|
};
|
|
8021
8021
|
}
|
|
8022
|
-
const LOCAL_ACCOUNT_SEARCH_COLLECTOR_RESULT_WINDOW =
|
|
8022
|
+
const LOCAL_ACCOUNT_SEARCH_COLLECTOR_RESULT_WINDOW = 1000;
|
|
8023
8023
|
const LOCAL_ACCOUNT_SEARCH_COLLECTOR_COVERAGE_PASS_VERSION = 3;
|
|
8024
8024
|
const LOCAL_ACCOUNT_SEARCH_COLLECTOR_COVERAGE_PASS = "coverage-complete";
|
|
8025
8025
|
const LOCAL_ACCOUNT_SEARCH_COLLECTOR_PASSES = [
|
|
@@ -8724,6 +8724,7 @@ async function fetchAllLocalSalesNavigatorPeople(parsedRequest, options) {
|
|
|
8724
8724
|
},
|
|
8725
8725
|
};
|
|
8726
8726
|
}
|
|
8727
|
+
const LOCAL_SALES_NAVIGATOR_REPORTED_COUNT_DRIFT_LIMIT = 5;
|
|
8727
8728
|
function canonicalLocalSalesNavigatorLeadKey(person) {
|
|
8728
8729
|
try {
|
|
8729
8730
|
const url = new URL(person.profileUrl);
|
|
@@ -8752,111 +8753,161 @@ async function fetchCompleteLocalSalesNavigatorPeople(sourceQueryUrl, requestHea
|
|
|
8752
8753
|
const dimensions = localPeopleSplitDimensions(root.sourceQueryUrl);
|
|
8753
8754
|
const queue = [root];
|
|
8754
8755
|
const queuedUrls = new Set([root.slicedQueryUrl]);
|
|
8756
|
+
const rootRecoveryDimensionKeys = new Set();
|
|
8755
8757
|
const peopleByKey = new Map();
|
|
8756
8758
|
const slices = [];
|
|
8757
8759
|
let rootTotalResults = null;
|
|
8758
8760
|
let fetchedPages = 0;
|
|
8759
8761
|
let totalDelayMs = 0;
|
|
8760
8762
|
let retryCount = 0;
|
|
8761
|
-
while (
|
|
8762
|
-
|
|
8763
|
-
|
|
8764
|
-
|
|
8765
|
-
|
|
8766
|
-
|
|
8767
|
-
|
|
8768
|
-
|
|
8769
|
-
|
|
8770
|
-
|
|
8771
|
-
|
|
8772
|
-
|
|
8773
|
-
|
|
8774
|
-
|
|
8775
|
-
|
|
8776
|
-
|
|
8777
|
-
|
|
8778
|
-
|
|
8779
|
-
|
|
8780
|
-
|
|
8781
|
-
|
|
8782
|
-
|
|
8783
|
-
|
|
8784
|
-
|
|
8785
|
-
|
|
8786
|
-
|
|
8787
|
-
catch (error) {
|
|
8788
|
-
if (!(error instanceof SalesNavigatorSliceTooBroadError)) {
|
|
8789
|
-
throw error;
|
|
8763
|
+
while (true) {
|
|
8764
|
+
while (queue.length > 0) {
|
|
8765
|
+
const attempt = queue.shift();
|
|
8766
|
+
const parsedRequest = {
|
|
8767
|
+
url: buildSalesNavigatorLeadApiUrlFromSearchUrl(attempt.slicedQueryUrl, options.pageSize),
|
|
8768
|
+
headers: requestHeaders,
|
|
8769
|
+
};
|
|
8770
|
+
let collected = null;
|
|
8771
|
+
let tooBroadTotal = null;
|
|
8772
|
+
let probeFetchedPages = 0;
|
|
8773
|
+
try {
|
|
8774
|
+
collected = await fetchAllLocalSalesNavigatorPeople(parsedRequest, {
|
|
8775
|
+
requestedProfiles: options.maxResultsPerSearch,
|
|
8776
|
+
maxAllowedResults: options.maxResultsPerSearch,
|
|
8777
|
+
pageSize: options.pageSize,
|
|
8778
|
+
pageDelayMinMs: options.pageDelayMinMs,
|
|
8779
|
+
pageDelayMaxMs: options.pageDelayMaxMs,
|
|
8780
|
+
retry: options.retry,
|
|
8781
|
+
executeRequest: options.executeRequest,
|
|
8782
|
+
});
|
|
8783
|
+
fetchedPages += collected.fetchedPages;
|
|
8784
|
+
totalDelayMs += collected.pacing.totalDelayMs;
|
|
8785
|
+
retryCount += collected.pacing.retryCount;
|
|
8786
|
+
if (attempt.depth === 0) {
|
|
8787
|
+
rootTotalResults = collected.totalResults;
|
|
8788
|
+
}
|
|
8790
8789
|
}
|
|
8791
|
-
|
|
8792
|
-
|
|
8793
|
-
|
|
8794
|
-
|
|
8795
|
-
|
|
8796
|
-
|
|
8797
|
-
|
|
8798
|
-
|
|
8799
|
-
|
|
8800
|
-
|
|
8790
|
+
catch (error) {
|
|
8791
|
+
if (!(error instanceof SalesNavigatorSliceTooBroadError)) {
|
|
8792
|
+
throw error;
|
|
8793
|
+
}
|
|
8794
|
+
tooBroadTotal = error.totalResults;
|
|
8795
|
+
const details = error.details && typeof error.details === "object"
|
|
8796
|
+
? error.details
|
|
8797
|
+
: {};
|
|
8798
|
+
probeFetchedPages = Number(details.fetchedPages ?? 1);
|
|
8799
|
+
fetchedPages += probeFetchedPages;
|
|
8800
|
+
retryCount += Number(details.retryCount ?? 0);
|
|
8801
|
+
totalDelayMs += Number(details.totalDelayMs ?? 0);
|
|
8802
|
+
if (attempt.depth === 0) {
|
|
8803
|
+
rootTotalResults = tooBroadTotal;
|
|
8804
|
+
}
|
|
8801
8805
|
}
|
|
8802
|
-
|
|
8803
|
-
|
|
8804
|
-
|
|
8805
|
-
|
|
8806
|
-
|
|
8807
|
-
|
|
8808
|
-
|
|
8809
|
-
|
|
8810
|
-
|
|
8811
|
-
|
|
8812
|
-
|
|
8813
|
-
|
|
8806
|
+
const needsSplit = tooBroadTotal != null ||
|
|
8807
|
+
(collected?.totalResults == null &&
|
|
8808
|
+
collected != null &&
|
|
8809
|
+
collected.people.length >= options.maxResultsPerSearch) ||
|
|
8810
|
+
(attempt.depth === 0 &&
|
|
8811
|
+
collected?.totalResults != null &&
|
|
8812
|
+
collected.people.length < collected.totalResults &&
|
|
8813
|
+
collected.totalResults - collected.people.length >
|
|
8814
|
+
LOCAL_SALES_NAVIGATOR_REPORTED_COUNT_DRIFT_LIMIT);
|
|
8815
|
+
if (needsSplit) {
|
|
8816
|
+
const usedDimensionKeys = new Set(attempt.splitTrail.map((entry) => entry.key.replace(/-residual$/, "")));
|
|
8817
|
+
const nextDimension = dimensions.find((dimension) => !usedDimensionKeys.has(dimension.key));
|
|
8818
|
+
if (!nextDimension) {
|
|
8819
|
+
throw new Error(`Sales Navigator search could not be completed within the ${options.maxResultsPerSearch}-person window after all safe split dimensions were used. Narrow the source search further.`);
|
|
8820
|
+
}
|
|
8821
|
+
if (attempt.depth === 0) {
|
|
8822
|
+
rootRecoveryDimensionKeys.add(nextDimension.key);
|
|
8823
|
+
}
|
|
8824
|
+
const children = expandSalesNavigatorCrawlAttempt(attempt, nextDimension, "people");
|
|
8825
|
+
if (nextDimension.supportsResidualExclusion !== false) {
|
|
8826
|
+
children.push(buildSalesNavigatorResidualCrawlAttempt(attempt, nextDimension, "people"));
|
|
8827
|
+
}
|
|
8828
|
+
for (const child of children) {
|
|
8829
|
+
if (queuedUrls.has(child.slicedQueryUrl))
|
|
8830
|
+
continue;
|
|
8831
|
+
queuedUrls.add(child.slicedQueryUrl);
|
|
8832
|
+
queue.push(child);
|
|
8833
|
+
}
|
|
8834
|
+
slices.push({
|
|
8835
|
+
depth: attempt.depth,
|
|
8836
|
+
splitTrail: attempt.splitTrail,
|
|
8837
|
+
totalResults: tooBroadTotal ?? collected?.totalResults ?? null,
|
|
8838
|
+
collected: 0,
|
|
8839
|
+
fetchedPages: probeFetchedPages || collected?.fetchedPages || 0,
|
|
8840
|
+
status: "split",
|
|
8841
|
+
splitDimension: nextDimension.key,
|
|
8842
|
+
});
|
|
8843
|
+
continue;
|
|
8814
8844
|
}
|
|
8815
|
-
|
|
8816
|
-
|
|
8817
|
-
children.push(buildSalesNavigatorResidualCrawlAttempt(attempt, nextDimension, "people"));
|
|
8845
|
+
if (!collected) {
|
|
8846
|
+
throw new Error("Sales Navigator slice did not return a collection result.");
|
|
8818
8847
|
}
|
|
8819
|
-
for (const
|
|
8820
|
-
|
|
8821
|
-
continue;
|
|
8822
|
-
queuedUrls.add(child.slicedQueryUrl);
|
|
8823
|
-
queue.push(child);
|
|
8848
|
+
for (const person of collected.people) {
|
|
8849
|
+
peopleByKey.set(canonicalLocalSalesNavigatorLeadKey(person), person);
|
|
8824
8850
|
}
|
|
8825
8851
|
slices.push({
|
|
8826
8852
|
depth: attempt.depth,
|
|
8827
8853
|
splitTrail: attempt.splitTrail,
|
|
8828
|
-
totalResults:
|
|
8829
|
-
collected:
|
|
8830
|
-
fetchedPages:
|
|
8831
|
-
status: "
|
|
8832
|
-
splitDimension:
|
|
8854
|
+
totalResults: collected.totalResults,
|
|
8855
|
+
collected: collected.people.length,
|
|
8856
|
+
fetchedPages: collected.fetchedPages,
|
|
8857
|
+
status: "collected",
|
|
8858
|
+
splitDimension: null,
|
|
8833
8859
|
});
|
|
8834
|
-
continue;
|
|
8835
8860
|
}
|
|
8836
|
-
|
|
8837
|
-
|
|
8861
|
+
const currentReportedCountDrift = rootTotalResults == null
|
|
8862
|
+
? 0
|
|
8863
|
+
: Math.max(0, rootTotalResults - peopleByKey.size);
|
|
8864
|
+
const currentReportedCountDriftAccepted = rootTotalResults != null &&
|
|
8865
|
+
rootTotalResults <= options.maxResultsPerSearch &&
|
|
8866
|
+
currentReportedCountDrift <=
|
|
8867
|
+
LOCAL_SALES_NAVIGATOR_REPORTED_COUNT_DRIFT_LIMIT;
|
|
8868
|
+
if (rootTotalResults == null ||
|
|
8869
|
+
peopleByKey.size >= rootTotalResults ||
|
|
8870
|
+
currentReportedCountDriftAccepted) {
|
|
8871
|
+
break;
|
|
8838
8872
|
}
|
|
8839
|
-
|
|
8840
|
-
|
|
8873
|
+
const nextRecoveryDimension = dimensions.find((dimension) => !rootRecoveryDimensionKeys.has(dimension.key));
|
|
8874
|
+
if (!nextRecoveryDimension) {
|
|
8875
|
+
break;
|
|
8876
|
+
}
|
|
8877
|
+
rootRecoveryDimensionKeys.add(nextRecoveryDimension.key);
|
|
8878
|
+
const recoveryChildren = expandSalesNavigatorCrawlAttempt(root, nextRecoveryDimension, "people");
|
|
8879
|
+
if (nextRecoveryDimension.supportsResidualExclusion !== false) {
|
|
8880
|
+
recoveryChildren.push(buildSalesNavigatorResidualCrawlAttempt(root, nextRecoveryDimension, "people"));
|
|
8881
|
+
}
|
|
8882
|
+
for (const child of recoveryChildren) {
|
|
8883
|
+
if (queuedUrls.has(child.slicedQueryUrl))
|
|
8884
|
+
continue;
|
|
8885
|
+
queuedUrls.add(child.slicedQueryUrl);
|
|
8886
|
+
queue.push(child);
|
|
8841
8887
|
}
|
|
8842
8888
|
slices.push({
|
|
8843
|
-
depth:
|
|
8844
|
-
splitTrail:
|
|
8845
|
-
totalResults:
|
|
8846
|
-
collected:
|
|
8847
|
-
fetchedPages:
|
|
8848
|
-
status: "
|
|
8849
|
-
splitDimension:
|
|
8889
|
+
depth: 0,
|
|
8890
|
+
splitTrail: [],
|
|
8891
|
+
totalResults: rootTotalResults,
|
|
8892
|
+
collected: 0,
|
|
8893
|
+
fetchedPages: 0,
|
|
8894
|
+
status: "split",
|
|
8895
|
+
splitDimension: nextRecoveryDimension.key,
|
|
8850
8896
|
});
|
|
8851
8897
|
}
|
|
8852
8898
|
const people = [...peopleByKey.values()];
|
|
8853
|
-
|
|
8899
|
+
const reportedCountDrift = rootTotalResults == null ? 0 : Math.max(0, rootTotalResults - people.length);
|
|
8900
|
+
const acceptedReportedCountDrift = rootTotalResults != null &&
|
|
8901
|
+
rootTotalResults <= options.maxResultsPerSearch &&
|
|
8902
|
+
reportedCountDrift <= LOCAL_SALES_NAVIGATOR_REPORTED_COUNT_DRIFT_LIMIT;
|
|
8903
|
+
if (reportedCountDrift > 0 && !acceptedReportedCountDrift) {
|
|
8854
8904
|
throw new Error(`Adaptive collection found ${people.length} unique people but the root search reported ${rootTotalResults}. No import was written because coverage is incomplete.`);
|
|
8855
8905
|
}
|
|
8856
8906
|
return {
|
|
8857
8907
|
people,
|
|
8858
8908
|
totalResults: rootTotalResults,
|
|
8859
8909
|
rootTotalResults,
|
|
8910
|
+
reportedCountDrift,
|
|
8860
8911
|
fetchedPages,
|
|
8861
8912
|
sourceQueryUrl,
|
|
8862
8913
|
collectionMode: "adaptive",
|
|
@@ -13645,6 +13696,7 @@ async function runSalesNavigatorPeopleCollectCommand(options) {
|
|
|
13645
13696
|
? {
|
|
13646
13697
|
collectionMode: adaptiveCollection.collectionMode,
|
|
13647
13698
|
accessibleResultLimit,
|
|
13699
|
+
reportedCountDrift: adaptiveCollection.reportedCountDrift,
|
|
13648
13700
|
sliceCount: adaptiveCollection.slices.filter((slice) => slice.status === "collected").length,
|
|
13649
13701
|
slices: adaptiveCollection.slices,
|
|
13650
13702
|
}
|
|
@@ -13658,14 +13710,19 @@ async function runSalesNavigatorPeopleCollectCommand(options) {
|
|
|
13658
13710
|
dryRun: false,
|
|
13659
13711
|
collected: collected.people.length,
|
|
13660
13712
|
totalResults: collected.totalResults,
|
|
13661
|
-
collectionComplete:
|
|
13662
|
-
collected.
|
|
13663
|
-
|
|
13713
|
+
collectionComplete: adaptiveCollection != null ||
|
|
13714
|
+
(collected.totalResults != null &&
|
|
13715
|
+
collected.people.length >= collected.totalResults),
|
|
13716
|
+
truncated: adaptiveCollection == null &&
|
|
13717
|
+
collected.totalResults != null &&
|
|
13664
13718
|
collected.totalResults > collected.people.length &&
|
|
13665
13719
|
collected.people.length >= maxResults,
|
|
13666
|
-
remainingResults:
|
|
13667
|
-
?
|
|
13668
|
-
:
|
|
13720
|
+
remainingResults: adaptiveCollection != null
|
|
13721
|
+
? 0
|
|
13722
|
+
: collected.totalResults == null
|
|
13723
|
+
? null
|
|
13724
|
+
: Math.max(0, collected.totalResults - collected.people.length),
|
|
13725
|
+
reportedCountDrift: adaptiveCollection?.reportedCountDrift ?? 0,
|
|
13669
13726
|
fetchedPages: collected.fetchedPages,
|
|
13670
13727
|
accessibleResultLimit,
|
|
13671
13728
|
collectionMode: adaptiveCollection ? "adaptive" : "bounded",
|
|
@@ -15563,10 +15620,10 @@ program
|
|
|
15563
15620
|
if (normalizedCheckpointUrl !== normalizedSourceQueryUrl) {
|
|
15564
15621
|
throw new Error("The checkpoint belongs to a different Sales Navigator account search.");
|
|
15565
15622
|
}
|
|
15566
|
-
if (checkpoint.pageSize !== pageSize
|
|
15567
|
-
throw new Error(`Checkpoint
|
|
15623
|
+
if (checkpoint.pageSize !== pageSize) {
|
|
15624
|
+
throw new Error(`Checkpoint page size does not match this invocation. Use --page-size ${checkpoint.pageSize}.`);
|
|
15568
15625
|
}
|
|
15569
|
-
if (checkpoint.cap
|
|
15626
|
+
if (checkpoint.cap !== cap) {
|
|
15570
15627
|
checkpoint.cap = cap;
|
|
15571
15628
|
}
|
|
15572
15629
|
checkpoint.delayMinMs = delayMinMs;
|
package/package.json
CHANGED