salesprompter-cli 0.1.59 → 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.
Files changed (3) hide show
  1. package/README.md +2 -1
  2. package/dist/cli.js +36 -13
  3. 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
- # Oversized or pagination-short searches are split by company headcount and deduplicated.
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 = 1600;
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);
@@ -8808,7 +8809,9 @@ async function fetchCompleteLocalSalesNavigatorPeople(sourceQueryUrl, requestHea
8808
8809
  collected.people.length >= options.maxResultsPerSearch) ||
8809
8810
  (attempt.depth === 0 &&
8810
8811
  collected?.totalResults != null &&
8811
- collected.people.length < collected.totalResults);
8812
+ collected.people.length < collected.totalResults &&
8813
+ collected.totalResults - collected.people.length >
8814
+ LOCAL_SALES_NAVIGATOR_REPORTED_COUNT_DRIFT_LIMIT);
8812
8815
  if (needsSplit) {
8813
8816
  const usedDimensionKeys = new Set(attempt.splitTrail.map((entry) => entry.key.replace(/-residual$/, "")));
8814
8817
  const nextDimension = dimensions.find((dimension) => !usedDimensionKeys.has(dimension.key));
@@ -8855,7 +8858,16 @@ async function fetchCompleteLocalSalesNavigatorPeople(sourceQueryUrl, requestHea
8855
8858
  splitDimension: null,
8856
8859
  });
8857
8860
  }
8858
- if (rootTotalResults == null || peopleByKey.size >= rootTotalResults) {
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) {
8859
8871
  break;
8860
8872
  }
8861
8873
  const nextRecoveryDimension = dimensions.find((dimension) => !rootRecoveryDimensionKeys.has(dimension.key));
@@ -8884,13 +8896,18 @@ async function fetchCompleteLocalSalesNavigatorPeople(sourceQueryUrl, requestHea
8884
8896
  });
8885
8897
  }
8886
8898
  const people = [...peopleByKey.values()];
8887
- if (rootTotalResults != null && people.length < rootTotalResults) {
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) {
8888
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.`);
8889
8905
  }
8890
8906
  return {
8891
8907
  people,
8892
8908
  totalResults: rootTotalResults,
8893
8909
  rootTotalResults,
8910
+ reportedCountDrift,
8894
8911
  fetchedPages,
8895
8912
  sourceQueryUrl,
8896
8913
  collectionMode: "adaptive",
@@ -13679,6 +13696,7 @@ async function runSalesNavigatorPeopleCollectCommand(options) {
13679
13696
  ? {
13680
13697
  collectionMode: adaptiveCollection.collectionMode,
13681
13698
  accessibleResultLimit,
13699
+ reportedCountDrift: adaptiveCollection.reportedCountDrift,
13682
13700
  sliceCount: adaptiveCollection.slices.filter((slice) => slice.status === "collected").length,
13683
13701
  slices: adaptiveCollection.slices,
13684
13702
  }
@@ -13692,14 +13710,19 @@ async function runSalesNavigatorPeopleCollectCommand(options) {
13692
13710
  dryRun: false,
13693
13711
  collected: collected.people.length,
13694
13712
  totalResults: collected.totalResults,
13695
- collectionComplete: collected.totalResults != null &&
13696
- collected.people.length >= collected.totalResults,
13697
- truncated: collected.totalResults != null &&
13713
+ collectionComplete: adaptiveCollection != null ||
13714
+ (collected.totalResults != null &&
13715
+ collected.people.length >= collected.totalResults),
13716
+ truncated: adaptiveCollection == null &&
13717
+ collected.totalResults != null &&
13698
13718
  collected.totalResults > collected.people.length &&
13699
13719
  collected.people.length >= maxResults,
13700
- remainingResults: collected.totalResults == null
13701
- ? null
13702
- : Math.max(0, collected.totalResults - collected.people.length),
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,
13703
13726
  fetchedPages: collected.fetchedPages,
13704
13727
  accessibleResultLimit,
13705
13728
  collectionMode: adaptiveCollection ? "adaptive" : "bounded",
@@ -15597,10 +15620,10 @@ program
15597
15620
  if (normalizedCheckpointUrl !== normalizedSourceQueryUrl) {
15598
15621
  throw new Error("The checkpoint belongs to a different Sales Navigator account search.");
15599
15622
  }
15600
- if (checkpoint.pageSize !== pageSize || checkpoint.cap > cap) {
15601
- throw new Error(`Checkpoint paging does not match this invocation. Use --max-results-per-search ${checkpoint.cap} --page-size ${checkpoint.pageSize}.`);
15623
+ if (checkpoint.pageSize !== pageSize) {
15624
+ throw new Error(`Checkpoint page size does not match this invocation. Use --page-size ${checkpoint.pageSize}.`);
15602
15625
  }
15603
- if (checkpoint.cap < cap) {
15626
+ if (checkpoint.cap !== cap) {
15604
15627
  checkpoint.cap = cap;
15605
15628
  }
15606
15629
  checkpoint.delayMinMs = delayMinMs;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "salesprompter-cli",
3
- "version": "0.1.59",
3
+ "version": "0.1.60",
4
4
  "description": "Sales workflow CLI for guided lead generation, enrichment, scoring, and sync.",
5
5
  "author": "Daniel Sinewe <hello@danielsinewe.com>",
6
6
  "type": "module",