salesprompter-cli 0.1.26 → 0.1.29

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.
@@ -12,6 +12,19 @@ function marketCountries(market) {
12
12
  function sqlStringList(values) {
13
13
  return values.map((value) => `'${value.replaceAll("'", "\\'")}'`).join(", ");
14
14
  }
15
+ function companySizeEmployeeCountSql(field) {
16
+ return `CASE TRIM(${field})
17
+ WHEN '1-10' THEN 10
18
+ WHEN '11-50' THEN 30
19
+ WHEN '51-200' THEN 125
20
+ WHEN '201-500' THEN 350
21
+ WHEN '501-1000' THEN 750
22
+ WHEN '1001-5000' THEN 2500
23
+ WHEN '5001-10.000' THEN 7500
24
+ WHEN '10.000+' THEN 10000
25
+ ELSE NULL
26
+ END`;
27
+ }
15
28
  function broadTitlePredicateSql(field) {
16
29
  return [
17
30
  `LOWER(${field}) LIKE '%head of hr%'`,
@@ -54,6 +67,7 @@ function titleExclusionPredicateSql(field) {
54
67
  export function buildDeelOutreachExportSql(options) {
55
68
  const countries = marketCountries(options.market);
56
69
  const countryClause = countries.length > 0 ? `UPPER(CAST(p.company_countryCode AS STRING)) IN (${sqlStringList(countries)})` : "TRUE";
70
+ const companySizeEmployeeCount = companySizeEmployeeCountSql("CAST(p.companySize AS STRING)");
57
71
  return `WITH hr_leadlists AS (
58
72
  SELECT
59
73
  CAST(leadListId AS STRING) AS leadListId,
@@ -120,7 +134,8 @@ WHERE CAST(p.functionId AS STRING) = '12'
120
134
  AND CAST(p.firstName_cleaned AS STRING) IS NOT NULL
121
135
  AND CAST(p.lastName_cleaned AS STRING) IS NOT NULL
122
136
  AND SAFE_CAST(p.emailScore AS INT64) >= ${Math.trunc(options.minEmailScore)}
123
- AND CAST(p.companySize AS STRING) IN ('200-499', '501-1000', '1001-5000', '5001-10.000', '10.000+')
137
+ AND ${companySizeEmployeeCount} >= 100
138
+ AND ${companySizeEmployeeCount} <= 2500
124
139
  AND (
125
140
  ${broadTitlePredicateSql("CAST(p.jobTitle AS STRING)")}
126
141
  )
@@ -11,6 +11,19 @@ function marketCountries(market) {
11
11
  function sqlStringList(values) {
12
12
  return values.map((value) => `'${value.replaceAll("'", "\\'")}'`).join(", ");
13
13
  }
14
+ function companySizeEmployeeCountSql(field) {
15
+ return `CASE TRIM(${field})
16
+ WHEN '1-10' THEN 10
17
+ WHEN '11-50' THEN 30
18
+ WHEN '51-200' THEN 125
19
+ WHEN '201-500' THEN 350
20
+ WHEN '501-1000' THEN 750
21
+ WHEN '1001-5000' THEN 2500
22
+ WHEN '5001-10.000' THEN 7500
23
+ WHEN '10.000+' THEN 10000
24
+ ELSE NULL
25
+ END`;
26
+ }
14
27
  function broadTitlePredicateSql() {
15
28
  return [
16
29
  "LOWER(c.jobTitle) LIKE '%head of hr%'",
@@ -58,6 +71,7 @@ export function buildDirectPathLeadExportSql(vendor, market, limit) {
58
71
  const countryClause = countries.length > 0 ? `co.countryCode IN (${sqlStringList(countries)})` : "TRUE";
59
72
  const broadTitlePredicate = broadTitlePredicateSql();
60
73
  const titleExclusions = titleExclusionPredicateSql();
74
+ const companySizeEmployeeCount = companySizeEmployeeCountSql("co.companySize");
61
75
  return `WITH hr_leadlists AS (
62
76
  SELECT
63
77
  CAST(leadListId AS STRING) AS leadListId,
@@ -132,7 +146,8 @@ JOIN hr_leadlists l ON c.leadListId = l.leadListId
132
146
  LEFT JOIN companies co ON c.companyId = co.companyId
133
147
  WHERE ${countryClause}
134
148
  AND co.domain IS NOT NULL
135
- AND co.companySize IN ('200-499', '501-1000', '1001-5000', '5001-10.000', '10.000+')
149
+ AND ${companySizeEmployeeCount} >= 100
150
+ AND ${companySizeEmployeeCount} <= 2500
136
151
  AND (
137
152
  ${broadTitlePredicate}
138
153
  )
@@ -1,4 +1,24 @@
1
1
  const DOMAIN_BLACKLIST = new Set(["linkedin.com", "bit.ly", "linktr.ee", "facebook.com"]);
2
+ const COMPANY_NAME_STOPWORDS = new Set([
3
+ "group",
4
+ "gmbh",
5
+ "ag",
6
+ "kg",
7
+ "co",
8
+ "company",
9
+ "holding",
10
+ "holdings",
11
+ "solutions",
12
+ "systems",
13
+ "services",
14
+ "international",
15
+ "global",
16
+ "the",
17
+ "und",
18
+ "and",
19
+ "de",
20
+ "of"
21
+ ]);
2
22
  function marketCountries(market) {
3
23
  if (market === "dach") {
4
24
  return ["DE", "AT", "CH"];
@@ -39,6 +59,90 @@ function rootDomain(value) {
39
59
  }
40
60
  return parts.slice(-2).join(".");
41
61
  }
62
+ function domainLabel(value) {
63
+ const root = rootDomain(value);
64
+ if (!root) {
65
+ return null;
66
+ }
67
+ return root.split(".")[0] ?? null;
68
+ }
69
+ function companyNameTokens(companyName) {
70
+ return (companyName ?? "")
71
+ .toLowerCase()
72
+ .replace(/[^a-z0-9]+/g, " ")
73
+ .split(/\s+/)
74
+ .map((token) => token.trim())
75
+ .filter((token) => token.length >= 3 && !COMPANY_NAME_STOPWORDS.has(token));
76
+ }
77
+ function domainCompanyMatchScore(domain, companyName) {
78
+ const label = domainLabel(domain);
79
+ if (!label) {
80
+ return -1;
81
+ }
82
+ const normalizedLabel = label.replace(/[^a-z0-9]/g, "");
83
+ if (normalizedLabel.length === 0) {
84
+ return -1;
85
+ }
86
+ const tokens = companyNameTokens(companyName);
87
+ if (tokens.length === 0) {
88
+ return normalizedLabel.length;
89
+ }
90
+ let score = 0;
91
+ for (const token of tokens) {
92
+ const normalizedToken = token.replace(/[^a-z0-9]/g, "");
93
+ if (normalizedToken.length === 0) {
94
+ continue;
95
+ }
96
+ if (normalizedLabel === normalizedToken) {
97
+ score += 100 + normalizedToken.length;
98
+ continue;
99
+ }
100
+ if (normalizedLabel.startsWith(normalizedToken)) {
101
+ score += 60 + normalizedToken.length;
102
+ continue;
103
+ }
104
+ if (normalizedLabel.includes(normalizedToken)) {
105
+ score += 40 + normalizedToken.length;
106
+ continue;
107
+ }
108
+ if (normalizedToken.startsWith(normalizedLabel) && normalizedLabel.length >= 4) {
109
+ score += 20 + normalizedLabel.length;
110
+ }
111
+ }
112
+ return score;
113
+ }
114
+ function chooseBetterCompanyMatchCandidate(candidates, baselineDomain) {
115
+ const baselineScore = domainCompanyMatchScore(baselineDomain, candidates[0]?.companyName);
116
+ const ranked = [...candidates]
117
+ .filter((candidate) => normalizeDomain(candidate.domain) !== null)
118
+ .filter((candidate) => !isBlacklistedDomain(candidate.domain))
119
+ .filter((candidate) => candidate.source !== "linkedin")
120
+ .sort((a, b) => {
121
+ const scoreDelta = domainCompanyMatchScore(b.domain, b.companyName) -
122
+ domainCompanyMatchScore(a.domain, a.companyName);
123
+ if (scoreDelta !== 0) {
124
+ return scoreDelta;
125
+ }
126
+ const hunterDelta = (b.hunterEmailCount ?? -1) - (a.hunterEmailCount ?? -1);
127
+ if (hunterDelta !== 0) {
128
+ return hunterDelta;
129
+ }
130
+ return (a.source ?? "").localeCompare(b.source ?? "");
131
+ });
132
+ const best = ranked[0] ?? null;
133
+ if (!best) {
134
+ return null;
135
+ }
136
+ const bestScore = domainCompanyMatchScore(best.domain, best.companyName);
137
+ const hasPositiveHunterSignal = (best.hunterEmailCount ?? 0) > 0;
138
+ if (hasPositiveHunterSignal && bestScore > baselineScore) {
139
+ return {
140
+ ...best,
141
+ domain: normalizeDomain(best.domain)
142
+ };
143
+ }
144
+ return null;
145
+ }
42
146
  function isBlacklistedDomain(value) {
43
147
  const normalized = normalizeDomain(value);
44
148
  if (!normalized) {
@@ -109,6 +213,20 @@ export function chooseBestDomain(candidates) {
109
213
  const nonNullCandidates = candidates.filter((candidate) => normalizeDomain(candidate.domain) !== null);
110
214
  const linkedinDomain = normalizeDomain(candidates[0].linkedinDomain);
111
215
  const linkedinWebsite = normalizeDomain(candidates[0].linkedinWebsite);
216
+ const betterThanLinkedinDomain = linkedinDomain
217
+ ? chooseBetterCompanyMatchCandidate(nonNullCandidates, linkedinDomain)
218
+ : null;
219
+ const betterThanLinkedinWebsite = !linkedinDomain && linkedinWebsite
220
+ ? chooseBetterCompanyMatchCandidate(nonNullCandidates, linkedinWebsite)
221
+ : null;
222
+ if (betterThanLinkedinDomain) {
223
+ return {
224
+ companyKey,
225
+ selected: betterThanLinkedinDomain,
226
+ reason: "better-company-match",
227
+ candidates
228
+ };
229
+ }
112
230
  if (linkedinDomain && !isBlacklistedDomain(linkedinDomain)) {
113
231
  const selectedCandidate = nonNullCandidates.find((candidate) => normalizeDomain(candidate.domain) === linkedinDomain) ?? {
114
232
  ...candidates[0],
@@ -128,6 +246,14 @@ export function chooseBestDomain(candidates) {
128
246
  candidates
129
247
  };
130
248
  }
249
+ if (betterThanLinkedinWebsite) {
250
+ return {
251
+ companyKey,
252
+ selected: betterThanLinkedinWebsite,
253
+ reason: "better-company-match",
254
+ candidates
255
+ };
256
+ }
131
257
  if (linkedinWebsite && !isBlacklistedDomain(linkedinWebsite)) {
132
258
  const websiteRoot = rootDomain(linkedinWebsite);
133
259
  const selectedCandidate = nonNullCandidates.find((candidate) => rootDomain(candidate.domain) === websiteRoot) ?? {
@@ -234,6 +360,9 @@ export function compareDomainSelectionStrategies(candidates) {
234
360
  if (improved.reason === "linkedin-domain" || improved.reason === "linkedin-website") {
235
361
  flags.push("preferred-linkedin");
236
362
  }
363
+ if (improved.reason === "better-company-match") {
364
+ flags.push("preferred-hunter-company-match");
365
+ }
237
366
  for (const flag of flags) {
238
367
  changeFlags[flag] = (changeFlags[flag] ?? 0) + 1;
239
368
  }
@@ -310,6 +439,9 @@ export function auditDomainDecisions(decisions) {
310
439
  if (decision.reason === "fallback-first-non-null") {
311
440
  flags.push("fallback-selected");
312
441
  }
442
+ if (decision.reason === "better-company-match") {
443
+ flags.push("better-company-match-selected");
444
+ }
313
445
  if (decision.reason === "highest-hunter-count") {
314
446
  flags.push("hunter-selected");
315
447
  }
@@ -1,7 +1,7 @@
1
1
  import { load } from "cheerio";
2
2
  import { BigQuery } from "@google-cloud/bigquery";
3
+ import { DEFAULT_LINKEDIN_SCRAPER_USER_AGENT } from "./linkedin-session-contracts.js";
3
4
  const DEFAULT_LINKEDIN_BASE_URL = "https://www.linkedin.com";
4
- const DEFAULT_USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36";
5
5
  const DEFAULT_RAPIDAPI_LINKEDIN_COMPANY_HOST = "web-scraping-api2.p.rapidapi.com";
6
6
  const DEFAULT_RAPIDAPI_LINKEDIN_COMPANY_ENDPOINT = "https://web-scraping-api2.p.rapidapi.com/get-company-by-linkedinurl";
7
7
  const DEFAULT_BIGQUERY_PROJECT_ID = process.env.BQ_PROJECT_ID ?? process.env.GOOGLE_CLOUD_PROJECT ?? process.env.GCLOUD_PROJECT ?? "icpidentifier";
@@ -429,7 +429,7 @@ export async function fetchLinkedInCompanyBackfillCandidates(bigQuery, clientId,
429
429
  async function fetchHtml(url) {
430
430
  const response = await fetch(url, {
431
431
  headers: {
432
- "User-Agent": DEFAULT_USER_AGENT
432
+ "User-Agent": DEFAULT_LINKEDIN_SCRAPER_USER_AGENT
433
433
  }
434
434
  });
435
435
  return {
@@ -446,7 +446,7 @@ export async function scrapeLinkedInCompany(candidate) {
446
446
  headers: {
447
447
  "x-rapidapi-key": rapidApiConfig.apiKey,
448
448
  "x-rapidapi-host": rapidApiConfig.host,
449
- "User-Agent": DEFAULT_USER_AGENT
449
+ "User-Agent": DEFAULT_LINKEDIN_SCRAPER_USER_AGENT
450
450
  }
451
451
  });
452
452
  const text = await response.text();
@@ -1,6 +1,6 @@
1
1
  import { load } from "cheerio";
2
+ import { DEFAULT_LINKEDIN_SCRAPER_USER_AGENT } from "./linkedin-session-contracts.js";
2
3
  const DEFAULT_LINKEDIN_BASE_URL = "https://www.linkedin.com";
3
- const DEFAULT_USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36";
4
4
  function normalizeWhitespace(value) {
5
5
  return (value ?? "").replace(/\s+/g, " ").trim();
6
6
  }
@@ -525,7 +525,7 @@ export function createLinkedInHtmlFetcher(fetchImpl = fetch) {
525
525
  return async (url) => {
526
526
  const response = await fetchImpl(url, {
527
527
  headers: {
528
- "User-Agent": DEFAULT_USER_AGENT,
528
+ "User-Agent": DEFAULT_LINKEDIN_SCRAPER_USER_AGENT,
529
529
  Accept: "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
530
530
  },
531
531
  redirect: "follow"
@@ -0,0 +1,3 @@
1
+ export * from "./vendor/salesprompter-shared/linkedin-session.js";
2
+ export { DEFAULT_LINKEDIN_USER_AGENT as DEFAULT_LINKEDIN_SESSION_USER_AGENT } from "./vendor/salesprompter-shared/linkedin-session.js";
3
+ export const DEFAULT_LINKEDIN_SCRAPER_USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36";
@@ -3,10 +3,10 @@ import { existsSync, readFileSync } from "node:fs";
3
3
  import { dirname, resolve } from "node:path";
4
4
  import { fileURLToPath } from "node:url";
5
5
  import { createClient } from "@supabase/supabase-js";
6
+ import { DEFAULT_LINKEDIN_SESSION_USER_AGENT, formatLiAtCookieHeader } from "./linkedin-session-contracts.js";
6
7
  const COOKIE_VAULT_KEY_CONTEXT = "salesprompter:linkedin-session-cookie:v1";
7
8
  const AES_256_KEY_BYTES = 32;
8
9
  const REQUEST_TIMEOUT_MS = 20_000;
9
- const DEFAULT_USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/145.0.0.0 Safari/537.36";
10
10
  const MAX_COOKIE_VALIDATION_ATTEMPTS = 8;
11
11
  const COOKIE_VALIDATION_TIMEOUT_MS = 75_000;
12
12
  const COOKIE_PROBE_TIMEOUT_MS = 8_000;
@@ -68,7 +68,7 @@ function parseDotEnvFile(filePath) {
68
68
  envFileCache.set(filePath, parsed);
69
69
  return parsed;
70
70
  }
71
- function resolveConfiguredEnvValue(env, key) {
71
+ export function resolveConfiguredEnvValue(env, key) {
72
72
  const directValue = env[key]?.trim() || "";
73
73
  if (directValue.length > 0) {
74
74
  return directValue;
@@ -230,6 +230,7 @@ export async function recordLinkedInSessionCookieAudit(supabase, input) {
230
230
  last_sales_navigator_status: input.salesNavigatorStatus,
231
231
  last_recruiter_status: input.recruiterStatus,
232
232
  last_product_class: input.productClass,
233
+ last_ingested_source: input.source,
233
234
  last_validation_error: input.validationError ?? null,
234
235
  last_validation_details: input.details ?? {}
235
236
  })
@@ -270,6 +271,7 @@ async function listLinkedInSessionCookieCandidates(supabase, options) {
270
271
  "session_cookie_auth_tag",
271
272
  "last_user_email",
272
273
  "last_user_handle",
274
+ "last_ingested_source",
273
275
  "is_active",
274
276
  "inactive_reason",
275
277
  "last_validation_at",
@@ -338,6 +340,7 @@ async function buildClaimedLinkedInSessionCookieFromRow(supabase, row, source, e
338
340
  sessionCookie,
339
341
  userEmail: claimedIdentity.userEmail,
340
342
  userHandle: claimedIdentity.userHandle,
343
+ lastIngestedSource: row.last_ingested_source ?? null,
341
344
  claimStrategy: options?.claimStrategy,
342
345
  previousInactiveReason: row.inactive_reason ?? null
343
346
  };
@@ -513,10 +516,6 @@ function extractTitle(body) {
513
516
  const match = body.match(/<title[^>]*>([^<]+)<\/title>/i);
514
517
  return match ? match[1].trim().replace(/\s+/g, " ") : null;
515
518
  }
516
- function normalizeLiAtCookie(sessionCookie) {
517
- const trimmed = sessionCookie.trim();
518
- return trimmed.startsWith("li_at=") ? trimmed.slice("li_at=".length) : trimmed;
519
- }
520
519
  function includesAny(value, patterns) {
521
520
  const normalized = value ?? "";
522
521
  return patterns.some((pattern) => pattern.test(normalized));
@@ -558,7 +557,7 @@ function hasSearchEntitlementSignals(result) {
558
557
  ]));
559
558
  }
560
559
  export async function fetchHtmlWithSessionCookie(url, sessionCookie, options) {
561
- const normalizedSessionCookie = normalizeLiAtCookie(sessionCookie);
560
+ const cookieHeader = formatLiAtCookieHeader(sessionCookie);
562
561
  const redirectChain = [];
563
562
  let currentUrl = url;
564
563
  const seenUrls = new Set([url]);
@@ -571,8 +570,8 @@ export async function fetchHtmlWithSessionCookie(url, sessionCookie, options) {
571
570
  redirect: "manual",
572
571
  signal: controller.signal,
573
572
  headers: {
574
- Cookie: `li_at=${normalizedSessionCookie}`,
575
- "User-Agent": options?.userAgent ?? DEFAULT_USER_AGENT,
573
+ Cookie: cookieHeader ?? "",
574
+ "User-Agent": options?.userAgent ?? DEFAULT_LINKEDIN_SESSION_USER_AGENT,
576
575
  Accept: "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
577
576
  "Accept-Language": "en-US,en;q=0.9",
578
577
  "Cache-Control": "no-cache",
@@ -0,0 +1,29 @@
1
+ import { z } from 'zod';
2
+ export const linkedInCapturedSessionSchema = z.object({
3
+ sessionCookie: z.string().min(1),
4
+ csrfToken: z.string().min(1),
5
+ linkedInIdentity: z.string().min(1),
6
+ userAgent: z.string().min(1),
7
+ requestId: z.string().min(1).nullable().optional(),
8
+ timestamp: z.string().min(1).nullable().optional(),
9
+ extractedFrom: z.string().min(1).nullable().optional(),
10
+ });
11
+ export const extensionSessionSyncPayloadSchema = z.object({
12
+ sessionCookie: z.string().min(1),
13
+ csrfToken: z.string().min(1),
14
+ linkedInIdentity: z.string().min(1),
15
+ userAgent: z.string().min(1),
16
+ requestId: z.string().min(1).nullable().optional(),
17
+ userEmail: z.string().email().nullable().optional(),
18
+ userFullName: z.string().min(1).nullable().optional(),
19
+ userHandle: z.string().min(1).nullable().optional(),
20
+ });
21
+ export const extensionSessionSyncSuccessSchema = z.object({
22
+ success: z.literal(true),
23
+ result: z.unknown().nullable().optional(),
24
+ persisted: z.boolean().nullable().optional(),
25
+ storageReady: z.boolean().nullable().optional(),
26
+ });
27
+ export const extensionSessionSyncErrorSchema = z.object({
28
+ error: z.string().min(1),
29
+ });
@@ -0,0 +1,22 @@
1
+ export const DEFAULT_LINKEDIN_USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/145.0.0.0 Safari/537.36';
2
+ function normalizeWhitespace(value) {
3
+ return (value ?? '').replace(/\s+/g, ' ').trim();
4
+ }
5
+ export function extractLiAtCookieValue(sessionCookie) {
6
+ const trimmed = normalizeWhitespace(sessionCookie);
7
+ if (!trimmed) {
8
+ return null;
9
+ }
10
+ const liAtMatch = trimmed.match(/(?:^|[;\s])li_at="?([^";]+)"?/i);
11
+ if (liAtMatch?.[1]) {
12
+ return liAtMatch[1];
13
+ }
14
+ return trimmed.startsWith('li_at=') ? trimmed.slice('li_at='.length) : trimmed;
15
+ }
16
+ export function normalizeLiAtCookie(sessionCookie) {
17
+ return extractLiAtCookieValue(sessionCookie);
18
+ }
19
+ export function formatLiAtCookieHeader(sessionCookie) {
20
+ const normalized = normalizeLiAtCookie(sessionCookie);
21
+ return normalized ? `li_at=${normalized}` : null;
22
+ }
@@ -0,0 +1,16 @@
1
+ import { z } from 'zod';
2
+ export const DEFAULT_PHANTOMBUSTER_API_BASE = 'https://api.phantombuster.com/api/v2';
3
+ export const DEFAULT_LINKEDIN_COMPANY_SCRAPER_AGENT_ID = '3415615142546934';
4
+ export const LINKEDIN_COMPANY_SCRAPER_COLUMN_NAME = 'Url';
5
+ export const linkedInCompanyScraperLaunchOptionsSchema = z.object({
6
+ spreadsheetUrl: z.string().url(),
7
+ columnName: z.string().min(1).nullable().optional(),
8
+ webhookUrl: z.string().url().nullable().optional(),
9
+ sessionCookie: z.string().min(1).nullable().optional(),
10
+ userAgent: z.string().min(1).nullable().optional(),
11
+ companiesPerLaunch: z.number().int().positive().nullable().optional(),
12
+ delayBetween: z.number().nonnegative().nullable().optional(),
13
+ });
14
+ export const phantombusterLaunchResponseSchema = z.object({
15
+ containerId: z.string().min(1),
16
+ });
@@ -0,0 +1,17 @@
1
+ import { z } from 'zod';
2
+ export const linkedInSessionSelectionSourceSchema = z.enum([
3
+ 'cli_linkedin_company_backfill',
4
+ 'cli_linkedin_company_backfill_preflight',
5
+ 'cli_linkedin_company_backfill_invalid_session',
6
+ 'cli_linkedin_company_backfill_decrypt_failure',
7
+ 'extension_user_profile',
8
+ 'extension_user_profile_li_at_fallback',
9
+ 'operator_env_seed',
10
+ 'pipedream_raw_manual_seed',
11
+ ]);
12
+ export const linkedInSessionInactiveReasonSchema = z.enum([
13
+ 'linkedin_session_invalid',
14
+ 'decrypt_failed',
15
+ 'excluded_linkedin_session_identity',
16
+ 'quarantined_browser_unverified',
17
+ ]);
package/package.json CHANGED
@@ -1,19 +1,27 @@
1
1
  {
2
2
  "name": "salesprompter-cli",
3
- "version": "0.1.26",
3
+ "version": "0.1.29",
4
4
  "description": "Sales workflow CLI for guided lead generation, enrichment, scoring, and sync.",
5
+ "author": "Daniel Sinewe <hello@danielsinewe.com>",
5
6
  "type": "module",
6
7
  "bin": {
7
8
  "salesprompter": "dist/cli.js"
8
9
  },
9
10
  "files": [
10
11
  "dist",
11
- "README.md"
12
+ "README.md",
13
+ "CONTRIBUTING.md",
14
+ "CODE_OF_CONDUCT.md",
15
+ "SECURITY.md"
12
16
  ],
13
17
  "scripts": {
14
18
  "build": "tsc -p tsconfig.json",
19
+ "benchmark:linkedin:direct": "node ./scripts/benchmark-linkedin-direct-strategies.mjs",
15
20
  "build:docs:site": "node ./scripts/build-docs-site.mjs",
16
21
  "check": "tsc --noEmit -p tsconfig.json",
22
+ "supabase:migrations:check": "node ./scripts/check-app-supabase-migrations.mjs",
23
+ "supabase:migrations:sync": "node ./scripts/sync-app-supabase-migrations.mjs",
24
+ "shared:sync": "node ./scripts/sync-shared.mjs",
17
25
  "docs:dev": "mint dev",
18
26
  "docs:broken-links": "mint broken-links",
19
27
  "docs:a11y": "mint a11y",
@@ -42,12 +50,17 @@
42
50
  "tooling"
43
51
  ],
44
52
  "homepage": "https://salesprompter.ai/docs",
53
+ "funding": {
54
+ "type": "other",
55
+ "url": "https://salesprompter.ai"
56
+ },
45
57
  "repository": {
46
58
  "type": "git",
47
59
  "url": "git+https://github.com/danielsinewe/salesprompter-cli.git"
48
60
  },
49
61
  "bugs": {
50
- "url": "https://github.com/danielsinewe/salesprompter-cli/issues"
62
+ "url": "https://github.com/danielsinewe/salesprompter-cli/issues",
63
+ "email": "hello@danielsinewe.com"
51
64
  },
52
65
  "license": "MIT",
53
66
  "dependencies": {
@@ -64,7 +77,7 @@
64
77
  "@types/node": "^24.3.0",
65
78
  "gray-matter": "^4.0.3",
66
79
  "marked": "^16.3.0",
67
- "mint": "^4.2.420",
80
+ "mint": "^4.2.216",
68
81
  "typescript": "^5.9.2"
69
82
  }
70
83
  }