salesprompter-cli 0.1.65 → 0.1.66

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 +5 -0
  2. package/dist/cli.js +56 -1
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -73,6 +73,11 @@ salesprompter contacts:resolve-emails --in ./contacts.tsv --out-dir ./email-run
73
73
  salesprompter leads:collect \
74
74
  --linkedin-url "$SALES_NAV_PEOPLE_URL"
75
75
 
76
+ # For large discovery runs, store the leads now and enrich selected imports later
77
+ salesprompter leads:collect \
78
+ --linkedin-url "$SALES_NAV_PEOPLE_URL" \
79
+ --defer-email-enrichment
80
+
76
81
  # Enrich one or more stored imports: Sales Navigator company -> official domain -> Hunter email
77
82
  # This updates workspace data only. It does not create or start outreach.
78
83
  # Stale ids and raw legal-name misses use conservative exact-name recovery.
package/dist/cli.js CHANGED
@@ -296,6 +296,23 @@ const SalesNavigatorLocalImportResponseSchema = z.object({
296
296
  })
297
297
  .nullable()
298
298
  .optional(),
299
+ emailEnrichment: z
300
+ .object({
301
+ scheduled: z.boolean(),
302
+ deferred: z.boolean().optional(),
303
+ provider: z.string().min(1).optional(),
304
+ fallbackProvider: z.string().min(1).optional(),
305
+ })
306
+ .optional(),
307
+ enrichmentDeferred: z.boolean().optional(),
308
+ outreachStarted: z.literal(false).optional(),
309
+ });
310
+ const SalesNavigatorLocalImportCapabilitiesResponseSchema = z.object({
311
+ status: z.literal("ok"),
312
+ capabilities: z.object({
313
+ deferredEmailEnrichment: z.literal(true),
314
+ }),
315
+ outreachStarted: z.literal(false),
299
316
  });
300
317
  const PhantombusterContainersSyncResponseSchema = z.object({
301
318
  status: z.literal("ok"),
@@ -9275,6 +9292,9 @@ async function importLocalSalesNavigatorPeopleViaApp(session, payload) {
9275
9292
  totalResults: payload.totalResults,
9276
9293
  fetchedPages: payload.fetchedPages,
9277
9294
  people: payload.people,
9295
+ ...(payload.startEmailEnrichment === false
9296
+ ? { startEmailEnrichment: false }
9297
+ : {}),
9278
9298
  rawPayload: {
9279
9299
  ...payload.rawPayload,
9280
9300
  clientId: workspaceClientId ?? undefined,
@@ -9305,6 +9325,20 @@ async function importLocalSalesNavigatorPeopleViaApp(session, payload) {
9305
9325
  }, SalesNavigatorLocalImportResponseSchema);
9306
9326
  return value;
9307
9327
  }
9328
+ async function requireDeferredEmailEnrichmentCapability(session) {
9329
+ try {
9330
+ const result = await fetchCliJson(session, (currentSession) => fetch(`${currentSession.apiBaseUrl}/api/cli/salesnav/local-import`, {
9331
+ method: "GET",
9332
+ headers: {
9333
+ Authorization: `Bearer ${currentSession.accessToken}`,
9334
+ },
9335
+ }), SalesNavigatorLocalImportCapabilitiesResponseSchema);
9336
+ return result.session;
9337
+ }
9338
+ catch {
9339
+ throw new Error("This Salesprompter workspace cannot safely defer email enrichment yet. Update the app before collecting leads with --defer-email-enrichment.");
9340
+ }
9341
+ }
9308
9342
  const SALESPROMPTER_SUPABASE_TABLES = {
9309
9343
  accountSearchRuns: "salesprompter_linkedin_sales_nav_account_search_runs",
9310
9344
  accountSearchSlices: "salesprompter_linkedin_sales_nav_account_search_slices",
@@ -13813,6 +13847,7 @@ async function runSalesNavigatorPeopleCollectCommand(options) {
13813
13847
  .max(accessibleResultLimit)
13814
13848
  .parse(options.maxResults);
13815
13849
  const complete = Boolean(options.complete || options.maxResults == null);
13850
+ const emailEnrichmentDeferred = Boolean(options.deferEmailEnrichment);
13816
13851
  const checkpointPath = complete
13817
13852
  ? options.checkpoint
13818
13853
  ? path.resolve(options.checkpoint)
@@ -13827,13 +13862,18 @@ async function runSalesNavigatorPeopleCollectCommand(options) {
13827
13862
  accessibleResultLimit,
13828
13863
  complete,
13829
13864
  resumable: complete,
13865
+ emailEnrichmentDeferred,
13866
+ outreachStarted: false,
13830
13867
  destination: "/leads/cli-imports"
13831
13868
  };
13832
13869
  if (options.out)
13833
13870
  await writeJsonFile(options.out, payload);
13834
13871
  return payload;
13835
13872
  }
13836
- const session = await requireAuthSession();
13873
+ let session = await requireAuthSession();
13874
+ if (emailEnrichmentDeferred) {
13875
+ session = await requireDeferredEmailEnrichmentCapability(session);
13876
+ }
13837
13877
  const pageDelayMinMs = z.coerce.number().int().min(0).parse(options.pageDelayMinMs);
13838
13878
  const pageDelayMaxMs = z.coerce.number().int().min(pageDelayMinMs).parse(options.pageDelayMaxMs);
13839
13879
  const browserRelayPort = options.browserRelayPort == null
@@ -13899,6 +13939,9 @@ async function runSalesNavigatorPeopleCollectCommand(options) {
13899
13939
  slicePreset: adaptiveCollection
13900
13940
  ? "local-salesnav-people-adaptive"
13901
13941
  : "local-salesnav-people",
13942
+ ...(emailEnrichmentDeferred
13943
+ ? { startEmailEnrichment: false }
13944
+ : {}),
13902
13945
  rawPayload: adaptiveCollection
13903
13946
  ? {
13904
13947
  collectionMode: adaptiveCollection.collectionMode,
@@ -13914,6 +13957,13 @@ async function runSalesNavigatorPeopleCollectCommand(options) {
13914
13957
  accessibleResultLimit,
13915
13958
  },
13916
13959
  });
13960
+ if (emailEnrichmentDeferred &&
13961
+ !(imported.enrichmentDeferred === true &&
13962
+ imported.emailEnrichment?.deferred === true &&
13963
+ imported.emailEnrichment.scheduled === false &&
13964
+ imported.outreachStarted === false)) {
13965
+ throw new Error(`Import ${imported.runId} was stored, but the workspace did not confirm that email enrichment was deferred. Do not repeat the collection until that run's enrichment state is verified.`);
13966
+ }
13917
13967
  const payload = {
13918
13968
  status: "ok",
13919
13969
  dryRun: false,
@@ -13942,6 +13992,10 @@ async function runSalesNavigatorPeopleCollectCommand(options) {
13942
13992
  runId: imported.runId,
13943
13993
  imported: imported.imported,
13944
13994
  upserted: imported.upserted,
13995
+ emailEnrichmentDeferred: imported.enrichmentDeferred ??
13996
+ imported.emailEnrichment?.deferred ??
13997
+ false,
13998
+ outreachStarted: imported.outreachStarted ?? false,
13945
13999
  workspaceUrl: `${session.apiBaseUrl}/leads/cli-imports?runId=` +
13946
14000
  encodeURIComponent(imported.runId)
13947
14001
  };
@@ -14807,6 +14861,7 @@ program
14807
14861
  .option("--max-results <number>", "Bounded collection size; defaults to exhaustive collection with a 2500-person window")
14808
14862
  .option("--complete", "Split oversized searches by company headcount and deduplicate the complete result", false)
14809
14863
  .option("--checkpoint <path>", "Optional resume checkpoint; exhaustive runs use a per-query checkpoint by default")
14864
+ .option("--defer-email-enrichment", "Store leads without starting email enrichment", false)
14810
14865
  .option("--curl-file <path>", "Optional copied Sales Navigator Lead Search curl request")
14811
14866
  .option("--browser-relay-port <number>", "Use the signed-in browser through a loopback relay")
14812
14867
  .option("--page-size <number>", "Direct Sales Navigator page size", "100")
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "salesprompter-cli",
3
- "version": "0.1.65",
3
+ "version": "0.1.66",
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",