salesprompter-cli 0.1.32 → 0.1.34
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/dist/cli.js +490 -46
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -42,6 +42,32 @@ const runtimeOutputOptions = {
|
|
|
42
42
|
quiet: false
|
|
43
43
|
};
|
|
44
44
|
const nullableOptionalString = z.string().min(1).nullish().transform((value) => value ?? undefined);
|
|
45
|
+
const CliWorkspaceSchema = z.object({
|
|
46
|
+
id: z.string().min(1),
|
|
47
|
+
name: nullableOptionalString,
|
|
48
|
+
slug: nullableOptionalString,
|
|
49
|
+
workspaceClientId: nullableOptionalString,
|
|
50
|
+
workspaceClientName: nullableOptionalString
|
|
51
|
+
});
|
|
52
|
+
const CliWorkspaceListResponseSchema = z.object({
|
|
53
|
+
workspaces: z.array(CliWorkspaceSchema),
|
|
54
|
+
currentOrgId: nullableOptionalString
|
|
55
|
+
});
|
|
56
|
+
const CliAuthUserSchema = z.object({
|
|
57
|
+
id: z.string().min(1),
|
|
58
|
+
email: z.string().email(),
|
|
59
|
+
name: nullableOptionalString,
|
|
60
|
+
orgId: nullableOptionalString,
|
|
61
|
+
orgName: nullableOptionalString,
|
|
62
|
+
orgSlug: nullableOptionalString,
|
|
63
|
+
workspaceClientId: nullableOptionalString,
|
|
64
|
+
workspaceClientName: nullableOptionalString
|
|
65
|
+
});
|
|
66
|
+
const CliWorkspaceSwitchResponseSchema = z.object({
|
|
67
|
+
token: z.string().min(1),
|
|
68
|
+
expiresAt: z.string().datetime().optional(),
|
|
69
|
+
user: CliAuthUserSchema
|
|
70
|
+
});
|
|
45
71
|
const LinkedInCompanyBackfillClientIdStateSchema = z
|
|
46
72
|
.object({
|
|
47
73
|
clientId: z.number().int().positive(),
|
|
@@ -311,6 +337,7 @@ const helpVisibleCommandNames = new Set([
|
|
|
311
337
|
"packs:add",
|
|
312
338
|
"upgrade",
|
|
313
339
|
"auth:login",
|
|
340
|
+
"auth:workspace",
|
|
314
341
|
"wizard",
|
|
315
342
|
"auth:whoami",
|
|
316
343
|
"llm:ready",
|
|
@@ -2563,6 +2590,37 @@ function resolveSessionOrgId(session) {
|
|
|
2563
2590
|
const orgId = session.user.orgId?.trim();
|
|
2564
2591
|
return orgId && orgId.length > 0 ? orgId : null;
|
|
2565
2592
|
}
|
|
2593
|
+
function normalizeCliApiBaseUrl(value) {
|
|
2594
|
+
return value.trim().replace(/\/+$/, "");
|
|
2595
|
+
}
|
|
2596
|
+
function getWorkspaceDisplayName(workspace) {
|
|
2597
|
+
return (compactOptionalText(workspace.name) ??
|
|
2598
|
+
compactOptionalText(workspace.workspaceClientName) ??
|
|
2599
|
+
compactOptionalText(workspace.slug) ??
|
|
2600
|
+
workspace.id);
|
|
2601
|
+
}
|
|
2602
|
+
function formatCliWorkspaceLabel(workspace) {
|
|
2603
|
+
const name = getWorkspaceDisplayName(workspace);
|
|
2604
|
+
const details = [
|
|
2605
|
+
compactOptionalText(workspace.slug),
|
|
2606
|
+
compactOptionalText(workspace.workspaceClientId),
|
|
2607
|
+
workspace.id
|
|
2608
|
+
].filter((value) => Boolean(value));
|
|
2609
|
+
return details.length > 0 ? `${name} (${details.join(", ")})` : name;
|
|
2610
|
+
}
|
|
2611
|
+
function workspaceMatchesInput(workspace, value) {
|
|
2612
|
+
const normalized = normalizeChoiceText(value);
|
|
2613
|
+
return [
|
|
2614
|
+
workspace.id,
|
|
2615
|
+
workspace.name,
|
|
2616
|
+
workspace.slug,
|
|
2617
|
+
workspace.workspaceClientId,
|
|
2618
|
+
workspace.workspaceClientName
|
|
2619
|
+
].some((candidate) => {
|
|
2620
|
+
const compacted = compactOptionalText(candidate);
|
|
2621
|
+
return compacted ? normalizeChoiceText(compacted) === normalized : false;
|
|
2622
|
+
});
|
|
2623
|
+
}
|
|
2566
2624
|
function writeSessionSummary(session) {
|
|
2567
2625
|
const identity = session.user.name?.trim()
|
|
2568
2626
|
? `${session.user.name} (${session.user.email})`
|
|
@@ -2747,6 +2805,21 @@ async function promptChoice(rl, prompt, options, defaultValue) {
|
|
|
2747
2805
|
writeWizardLine();
|
|
2748
2806
|
}
|
|
2749
2807
|
}
|
|
2808
|
+
async function withPromptReader(existingReader, run) {
|
|
2809
|
+
if (existingReader) {
|
|
2810
|
+
return await run(existingReader);
|
|
2811
|
+
}
|
|
2812
|
+
const rl = createInterface({
|
|
2813
|
+
input: process.stdin,
|
|
2814
|
+
output: process.stdout
|
|
2815
|
+
});
|
|
2816
|
+
try {
|
|
2817
|
+
return await run(rl);
|
|
2818
|
+
}
|
|
2819
|
+
finally {
|
|
2820
|
+
rl.close();
|
|
2821
|
+
}
|
|
2822
|
+
}
|
|
2750
2823
|
async function promptText(rl, prompt, options = {}) {
|
|
2751
2824
|
while (true) {
|
|
2752
2825
|
const suffix = options.defaultValue !== undefined ? ` [${options.defaultValue}]` : "";
|
|
@@ -2827,7 +2900,7 @@ async function confirmWizardWorkspace(rl, session, options) {
|
|
|
2827
2900
|
? hasNamedOrg
|
|
2828
2901
|
? "Current cached CLI workspace"
|
|
2829
2902
|
: "Workspace name is not available in this cached token"
|
|
2830
|
-
: "
|
|
2903
|
+
: "Select from your Salesprompter organizations in this terminal";
|
|
2831
2904
|
const workspaceChoice = await promptChoice(rl, "Which workspace should I use?", [
|
|
2832
2905
|
{
|
|
2833
2906
|
value: "current",
|
|
@@ -2837,8 +2910,8 @@ async function confirmWizardWorkspace(rl, session, options) {
|
|
|
2837
2910
|
},
|
|
2838
2911
|
{
|
|
2839
2912
|
value: "browser",
|
|
2840
|
-
label: "Choose another workspace
|
|
2841
|
-
description: "
|
|
2913
|
+
label: "Choose another workspace",
|
|
2914
|
+
description: "Select from your Salesprompter organizations in this terminal",
|
|
2842
2915
|
aliases: ["browser", "choose another", "switch workspace", "select organization"]
|
|
2843
2916
|
}
|
|
2844
2917
|
], "current");
|
|
@@ -2847,17 +2920,150 @@ async function confirmWizardWorkspace(rl, session, options) {
|
|
|
2847
2920
|
return session;
|
|
2848
2921
|
}
|
|
2849
2922
|
writeWizardLine();
|
|
2850
|
-
|
|
2851
|
-
writeWizardLine();
|
|
2852
|
-
await clearAuthSession();
|
|
2853
|
-
const result = await performLogin({
|
|
2923
|
+
const result = await switchWorkspaceInCli({
|
|
2854
2924
|
apiUrl: options?.apiUrl ?? session.apiBaseUrl,
|
|
2855
|
-
timeoutSeconds: options?.timeoutSeconds
|
|
2925
|
+
timeoutSeconds: options?.timeoutSeconds,
|
|
2926
|
+
rl
|
|
2856
2927
|
});
|
|
2857
2928
|
writeSessionSummary(result.session);
|
|
2858
2929
|
writeWizardLine();
|
|
2859
2930
|
return result.session;
|
|
2860
2931
|
}
|
|
2932
|
+
async function switchWorkspaceWithBrowser(options) {
|
|
2933
|
+
await clearAuthSession();
|
|
2934
|
+
return (await performLogin({
|
|
2935
|
+
apiUrl: options?.apiUrl,
|
|
2936
|
+
timeoutSeconds: options?.timeoutSeconds ?? 180
|
|
2937
|
+
})).session;
|
|
2938
|
+
}
|
|
2939
|
+
async function listCliWorkspaces(session) {
|
|
2940
|
+
const { session: refreshedSession, value } = await fetchCliJson(session, async (currentSession) => await fetch(`${normalizeCliApiBaseUrl(currentSession.apiBaseUrl)}/api/cli/auth/workspaces`, {
|
|
2941
|
+
method: "GET",
|
|
2942
|
+
headers: {
|
|
2943
|
+
Authorization: `Bearer ${currentSession.accessToken}`,
|
|
2944
|
+
"X-Salesprompter-Client": "salesprompter-cli/0.2"
|
|
2945
|
+
}
|
|
2946
|
+
}), CliWorkspaceListResponseSchema);
|
|
2947
|
+
return {
|
|
2948
|
+
session: refreshedSession,
|
|
2949
|
+
workspaces: value.workspaces,
|
|
2950
|
+
currentOrgId: value.currentOrgId ?? null
|
|
2951
|
+
};
|
|
2952
|
+
}
|
|
2953
|
+
async function selectCliWorkspace(session, orgId) {
|
|
2954
|
+
const { value } = await fetchCliJson(session, async (currentSession) => await fetch(`${normalizeCliApiBaseUrl(currentSession.apiBaseUrl)}/api/cli/auth/workspaces`, {
|
|
2955
|
+
method: "POST",
|
|
2956
|
+
headers: {
|
|
2957
|
+
Authorization: `Bearer ${currentSession.accessToken}`,
|
|
2958
|
+
"Content-Type": "application/json",
|
|
2959
|
+
"X-Salesprompter-Client": "salesprompter-cli/0.2"
|
|
2960
|
+
},
|
|
2961
|
+
body: JSON.stringify({ orgId })
|
|
2962
|
+
}), CliWorkspaceSwitchResponseSchema);
|
|
2963
|
+
const nextSession = {
|
|
2964
|
+
accessToken: value.token,
|
|
2965
|
+
refreshToken: session.refreshToken,
|
|
2966
|
+
apiBaseUrl: session.apiBaseUrl,
|
|
2967
|
+
user: value.user,
|
|
2968
|
+
expiresAt: value.expiresAt,
|
|
2969
|
+
createdAt: new Date().toISOString()
|
|
2970
|
+
};
|
|
2971
|
+
await writeAuthSession(nextSession);
|
|
2972
|
+
return nextSession;
|
|
2973
|
+
}
|
|
2974
|
+
async function promptForCliWorkspace(rl, workspaces, currentOrgId, requestedWorkspace) {
|
|
2975
|
+
const requested = compactOptionalText(requestedWorkspace);
|
|
2976
|
+
if (requested) {
|
|
2977
|
+
const matched = workspaces.find((workspace) => workspaceMatchesInput(workspace, requested));
|
|
2978
|
+
if (!matched) {
|
|
2979
|
+
throw new Error(`workspace not found for this account: ${requested}`);
|
|
2980
|
+
}
|
|
2981
|
+
return matched.id;
|
|
2982
|
+
}
|
|
2983
|
+
const current = currentOrgId ? workspaces.find((workspace) => workspace.id === currentOrgId) : undefined;
|
|
2984
|
+
const options = workspaces.map((workspace) => ({
|
|
2985
|
+
value: workspace.id,
|
|
2986
|
+
label: formatCliWorkspaceLabel(workspace),
|
|
2987
|
+
description: workspace.id === currentOrgId ? "Current cached CLI workspace" : undefined,
|
|
2988
|
+
aliases: [workspace.name, workspace.slug, workspace.workspaceClientId, workspace.workspaceClientName].filter((value) => Boolean(compactOptionalText(value)))
|
|
2989
|
+
}));
|
|
2990
|
+
options.push({
|
|
2991
|
+
value: "browser",
|
|
2992
|
+
label: "Use browser chooser",
|
|
2993
|
+
description: "Fallback if this terminal list is missing a workspace",
|
|
2994
|
+
aliases: ["browser", "open browser", "web"]
|
|
2995
|
+
});
|
|
2996
|
+
return await promptChoice(rl, "Which workspace should I use?", options, current?.id ?? options[0]?.value ?? "browser");
|
|
2997
|
+
}
|
|
2998
|
+
async function switchWorkspaceInCli(options = {}) {
|
|
2999
|
+
if (options.browser) {
|
|
3000
|
+
return {
|
|
3001
|
+
session: await switchWorkspaceWithBrowser(options),
|
|
3002
|
+
method: "browser"
|
|
3003
|
+
};
|
|
3004
|
+
}
|
|
3005
|
+
let session = null;
|
|
3006
|
+
try {
|
|
3007
|
+
session = await requireAuthSession();
|
|
3008
|
+
if (options.apiUrl) {
|
|
3009
|
+
session = {
|
|
3010
|
+
...session,
|
|
3011
|
+
apiBaseUrl: normalizeCliApiBaseUrl(options.apiUrl)
|
|
3012
|
+
};
|
|
3013
|
+
}
|
|
3014
|
+
}
|
|
3015
|
+
catch {
|
|
3016
|
+
if (!runtimeOutputOptions.quiet) {
|
|
3017
|
+
writeWizardLine("No cached CLI session found. Starting browser login flow.");
|
|
3018
|
+
writeWizardLine();
|
|
3019
|
+
}
|
|
3020
|
+
return {
|
|
3021
|
+
session: await switchWorkspaceWithBrowser(options),
|
|
3022
|
+
method: "browser"
|
|
3023
|
+
};
|
|
3024
|
+
}
|
|
3025
|
+
let workspaces;
|
|
3026
|
+
let currentOrgId;
|
|
3027
|
+
try {
|
|
3028
|
+
const listed = await listCliWorkspaces(session);
|
|
3029
|
+
session = listed.session;
|
|
3030
|
+
workspaces = listed.workspaces;
|
|
3031
|
+
currentOrgId = listed.currentOrgId;
|
|
3032
|
+
}
|
|
3033
|
+
catch (error) {
|
|
3034
|
+
if (!runtimeOutputOptions.quiet) {
|
|
3035
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
3036
|
+
writeWizardLine(`Terminal workspace selection is unavailable (${message}). Starting browser chooser.`);
|
|
3037
|
+
writeWizardLine();
|
|
3038
|
+
}
|
|
3039
|
+
return {
|
|
3040
|
+
session: await switchWorkspaceWithBrowser(options),
|
|
3041
|
+
method: "browser"
|
|
3042
|
+
};
|
|
3043
|
+
}
|
|
3044
|
+
if (workspaces.length === 0) {
|
|
3045
|
+
if (!runtimeOutputOptions.quiet) {
|
|
3046
|
+
writeWizardLine("No Salesprompter workspaces were returned for this account. Starting browser chooser.");
|
|
3047
|
+
writeWizardLine();
|
|
3048
|
+
}
|
|
3049
|
+
return {
|
|
3050
|
+
session: await switchWorkspaceWithBrowser(options),
|
|
3051
|
+
method: "browser"
|
|
3052
|
+
};
|
|
3053
|
+
}
|
|
3054
|
+
const selectedOrgId = options.orgId ?? (await withPromptReader(options.rl, async (rl) => await promptForCliWorkspace(rl, workspaces, currentOrgId, options.workspace)));
|
|
3055
|
+
if (selectedOrgId === "browser") {
|
|
3056
|
+
return {
|
|
3057
|
+
session: await switchWorkspaceWithBrowser(options),
|
|
3058
|
+
method: "browser"
|
|
3059
|
+
};
|
|
3060
|
+
}
|
|
3061
|
+
const nextSession = await selectCliWorkspace(session, selectedOrgId);
|
|
3062
|
+
return {
|
|
3063
|
+
session: nextSession,
|
|
3064
|
+
method: "terminal"
|
|
3065
|
+
};
|
|
3066
|
+
}
|
|
2861
3067
|
async function resolveLlmAuthReadiness() {
|
|
2862
3068
|
const apiBaseUrl = process.env.SALESPROMPTER_API_BASE_URL?.trim() || "https://salesprompter.ai";
|
|
2863
3069
|
const envToken = resolveNonInteractiveAuthToken(process.env);
|
|
@@ -2942,6 +3148,19 @@ function buildSalesNavigatorCrawlLogPath(input) {
|
|
|
2942
3148
|
const slug = slugify(input) || "salesnav-crawl";
|
|
2943
3149
|
return `./data/${slug}-crawl.log.jsonl`;
|
|
2944
3150
|
}
|
|
3151
|
+
function buildSalesNavigatorCrawlOutputPath(input) {
|
|
3152
|
+
const slug = slugify(input) || "salesnav-crawl";
|
|
3153
|
+
return `./data/${slug}-crawl.json`;
|
|
3154
|
+
}
|
|
3155
|
+
function isSalesNavigatorPeopleSearchUrl(input) {
|
|
3156
|
+
try {
|
|
3157
|
+
const parsed = new URL(input);
|
|
3158
|
+
return /(^|\.)linkedin\.com$/i.test(parsed.hostname) && parsed.pathname.replace(/\/+$/, "") === "/sales/search/people";
|
|
3159
|
+
}
|
|
3160
|
+
catch {
|
|
3161
|
+
return false;
|
|
3162
|
+
}
|
|
3163
|
+
}
|
|
2945
3164
|
function decodeSalesNavigatorQueryParam(url) {
|
|
2946
3165
|
try {
|
|
2947
3166
|
const encoded = new URL(url).searchParams.get("query");
|
|
@@ -5035,11 +5254,192 @@ async function searchTargetCompanyLeads(reference, limit) {
|
|
|
5035
5254
|
limit
|
|
5036
5255
|
});
|
|
5037
5256
|
}
|
|
5257
|
+
async function runDirectSalesNavigatorSearchWizard(input) {
|
|
5258
|
+
const maxResultsPerSearch = 2500;
|
|
5259
|
+
const numberOfProfiles = 2500;
|
|
5260
|
+
const slicePreset = "wizard-salesnav-search";
|
|
5261
|
+
const maxSplitDepth = DEFAULT_SALES_NAVIGATOR_CRAWL_DIMENSIONS.length;
|
|
5262
|
+
const maxSlices = 1000;
|
|
5263
|
+
const maxRetries = 3;
|
|
5264
|
+
const probeProfiles = 100;
|
|
5265
|
+
const agentBusyWaitSeconds = 30;
|
|
5266
|
+
const agentBusyMaxWaits = 20;
|
|
5267
|
+
const idlePollSeconds = 10;
|
|
5268
|
+
const idleMaxPolls = 180;
|
|
5269
|
+
const parallelExports = 3;
|
|
5270
|
+
const outPath = buildSalesNavigatorCrawlOutputPath(input);
|
|
5271
|
+
const dryRun = shouldBypassAuth();
|
|
5272
|
+
const logger = await createWorkflowLogger({
|
|
5273
|
+
logPath: buildSalesNavigatorCrawlLogPath(input)
|
|
5274
|
+
});
|
|
5275
|
+
writeWizardLine("Detected a Sales Navigator people search. I will process this search directly.");
|
|
5276
|
+
if (dryRun) {
|
|
5277
|
+
writeWizardLine("Auth bypass is enabled, so I will preview the crawl plan instead of launching it.");
|
|
5278
|
+
}
|
|
5279
|
+
writeWizardLine();
|
|
5280
|
+
await logger.log("salesnav.crawl.command.started", {
|
|
5281
|
+
queryUrl: input,
|
|
5282
|
+
jobId: null,
|
|
5283
|
+
maxResultsPerSearch,
|
|
5284
|
+
numberOfProfiles,
|
|
5285
|
+
slicePreset,
|
|
5286
|
+
maxSplitDepth,
|
|
5287
|
+
maxSlices,
|
|
5288
|
+
maxRetries,
|
|
5289
|
+
probeProfiles,
|
|
5290
|
+
agentBusyWaitSeconds,
|
|
5291
|
+
agentBusyMaxWaits,
|
|
5292
|
+
idlePollSeconds,
|
|
5293
|
+
idleMaxPolls,
|
|
5294
|
+
parallelExports,
|
|
5295
|
+
dryRun
|
|
5296
|
+
});
|
|
5297
|
+
if (dryRun) {
|
|
5298
|
+
const preview = buildSalesNavigatorCrawlPreview({
|
|
5299
|
+
sourceQueryUrl: input,
|
|
5300
|
+
maxResultsPerSearch,
|
|
5301
|
+
numberOfProfiles,
|
|
5302
|
+
slicePreset
|
|
5303
|
+
});
|
|
5304
|
+
const payload = {
|
|
5305
|
+
status: "ok",
|
|
5306
|
+
dryRun: true,
|
|
5307
|
+
mode: "adaptive",
|
|
5308
|
+
traceId: logger.traceId,
|
|
5309
|
+
logPath: logger.logPath,
|
|
5310
|
+
sourceQueryUrl: input,
|
|
5311
|
+
rootQueryUrl: preview.root.slicedQueryUrl,
|
|
5312
|
+
rootAppliedFilters: preview.root.appliedFilters,
|
|
5313
|
+
dimensionOrder: preview.dimensions.map((dimension) => ({
|
|
5314
|
+
key: dimension.key,
|
|
5315
|
+
filterType: dimension.filterType,
|
|
5316
|
+
valueCount: dimension.values.length
|
|
5317
|
+
})),
|
|
5318
|
+
firstSplitQueries: preview.firstSplit.map((attempt) => ({
|
|
5319
|
+
slicedQueryUrl: attempt.slicedQueryUrl,
|
|
5320
|
+
appliedFilters: attempt.appliedFilters,
|
|
5321
|
+
splitTrail: attempt.splitTrail.map((entry) => ({
|
|
5322
|
+
key: entry.key,
|
|
5323
|
+
filterType: entry.filterType,
|
|
5324
|
+
valueText: entry.value.text
|
|
5325
|
+
}))
|
|
5326
|
+
}))
|
|
5327
|
+
};
|
|
5328
|
+
await logger.log("salesnav.crawl.dry-run.preview", {
|
|
5329
|
+
sourceQueryUrl: input,
|
|
5330
|
+
root: summarizeSalesNavigatorQuery(payload.rootQueryUrl, payload.rootAppliedFilters),
|
|
5331
|
+
dimensionOrder: payload.dimensionOrder,
|
|
5332
|
+
firstSplitQueries: payload.firstSplitQueries.map((attempt) => ({
|
|
5333
|
+
splitTrail: attempt.splitTrail,
|
|
5334
|
+
...summarizeSalesNavigatorQuery(attempt.slicedQueryUrl, attempt.appliedFilters)
|
|
5335
|
+
}))
|
|
5336
|
+
});
|
|
5337
|
+
await writeJsonFile(outPath, payload);
|
|
5338
|
+
writeWizardLine(`Saved Sales Navigator crawl preview to ${outPath}.`);
|
|
5339
|
+
writeWizardLine(`Saved logs to ${logger.logPath}.`);
|
|
5340
|
+
return;
|
|
5341
|
+
}
|
|
5342
|
+
let session = await requireAuthSession();
|
|
5343
|
+
const sessionOrgId = resolveSessionOrgId(session);
|
|
5344
|
+
if (sessionOrgId) {
|
|
5345
|
+
logger.setEventStore(await createSalesNavigatorCrawlEventStore({ orgId: sessionOrgId }));
|
|
5346
|
+
}
|
|
5347
|
+
const seed = createSalesNavigatorCrawlSeed({
|
|
5348
|
+
sourceQueryUrl: input,
|
|
5349
|
+
maxResultsPerSearch,
|
|
5350
|
+
numberOfProfiles,
|
|
5351
|
+
slicePreset
|
|
5352
|
+
});
|
|
5353
|
+
const created = await createOrResumeSalesNavigatorCrawlJob(session, {
|
|
5354
|
+
sourceQueryUrl: input,
|
|
5355
|
+
slicePreset,
|
|
5356
|
+
maxResultsPerSearch,
|
|
5357
|
+
numberOfProfiles,
|
|
5358
|
+
rawPayload: {
|
|
5359
|
+
workflow: "wizard:salesnav-search",
|
|
5360
|
+
traceId: logger.traceId,
|
|
5361
|
+
command: {
|
|
5362
|
+
sourceQueryUrl: input,
|
|
5363
|
+
slicePreset,
|
|
5364
|
+
maxResultsPerSearch,
|
|
5365
|
+
numberOfProfiles,
|
|
5366
|
+
maxSplitDepth,
|
|
5367
|
+
maxSlices,
|
|
5368
|
+
maxRetries,
|
|
5369
|
+
probeProfiles,
|
|
5370
|
+
agentBusyWaitSeconds,
|
|
5371
|
+
agentBusyMaxWaits,
|
|
5372
|
+
idlePollSeconds,
|
|
5373
|
+
idleMaxPolls,
|
|
5374
|
+
parallelExports
|
|
5375
|
+
}
|
|
5376
|
+
},
|
|
5377
|
+
rootSlice: {
|
|
5378
|
+
slicedQueryUrl: seed.slicedQueryUrl,
|
|
5379
|
+
appliedFilters: seed.appliedFilters,
|
|
5380
|
+
depth: seed.depth,
|
|
5381
|
+
splitTrail: seed.splitTrail,
|
|
5382
|
+
rawPayload: {
|
|
5383
|
+
workflow: "wizard:salesnav-search",
|
|
5384
|
+
traceId: logger.traceId
|
|
5385
|
+
}
|
|
5386
|
+
}
|
|
5387
|
+
}, logger.traceId);
|
|
5388
|
+
session = created.session;
|
|
5389
|
+
const jobId = created.value.job.id;
|
|
5390
|
+
writeWizardLine(`${created.value.resumed ? "Resumed" : "Started"} Sales Navigator crawl ${jobId}. Processing the search now...`);
|
|
5391
|
+
const crawl = await executeSalesNavigatorCrawlJob(session, jobId, {
|
|
5392
|
+
maxSplitDepth,
|
|
5393
|
+
maxSlices,
|
|
5394
|
+
maxRetries,
|
|
5395
|
+
probeProfiles,
|
|
5396
|
+
agentBusyWaitSeconds,
|
|
5397
|
+
agentBusyMaxWaits,
|
|
5398
|
+
idlePollSeconds,
|
|
5399
|
+
idleMaxPolls,
|
|
5400
|
+
parallelExports,
|
|
5401
|
+
traceId: logger.traceId,
|
|
5402
|
+
logger
|
|
5403
|
+
});
|
|
5404
|
+
const payload = {
|
|
5405
|
+
status: "ok",
|
|
5406
|
+
dryRun: false,
|
|
5407
|
+
mode: "durable",
|
|
5408
|
+
traceId: logger.traceId,
|
|
5409
|
+
logPath: logger.logPath,
|
|
5410
|
+
jobId,
|
|
5411
|
+
resumed: created.value.resumed,
|
|
5412
|
+
sourceQueryUrl: crawl.job.sourceQueryUrl,
|
|
5413
|
+
slicePreset: crawl.job.slicePreset,
|
|
5414
|
+
maxResultsPerSearch: crawl.job.maxResultsPerSearch,
|
|
5415
|
+
numberOfProfiles: crawl.job.numberOfProfiles,
|
|
5416
|
+
claimedSlices: crawl.claimedSlices,
|
|
5417
|
+
truncated: crawl.truncated,
|
|
5418
|
+
job: crawl.job,
|
|
5419
|
+
lastOutcome: crawl.lastOutcome
|
|
5420
|
+
};
|
|
5421
|
+
await writeJsonFile(outPath, payload);
|
|
5422
|
+
writeWizardLine(`Sales Navigator crawl status: ${crawl.job.status}.`);
|
|
5423
|
+
writeWizardLine(`Imported ${crawl.job.importedPeople} people across ${crawl.job.exportedSlices} exported slice${crawl.job.exportedSlices === 1 ? "" : "s"}.`);
|
|
5424
|
+
if (crawl.job.failedSlices > 0 || crawl.truncated) {
|
|
5425
|
+
writeWizardLine(`Some work still needs attention: ${crawl.job.failedSlices} failed slice${crawl.job.failedSlices === 1 ? "" : "s"}, truncated=${crawl.truncated}.`);
|
|
5426
|
+
}
|
|
5427
|
+
writeWizardLine(`Saved crawl summary to ${outPath}.`);
|
|
5428
|
+
writeWizardLine(`Saved logs to ${logger.logPath}.`);
|
|
5429
|
+
writeWizardLine();
|
|
5430
|
+
writeWizardLine("Equivalent raw command:");
|
|
5431
|
+
writeWizardLine(` ${buildCommandLine(["salesprompter", "salesnav:crawl", "--query-url", input])}`);
|
|
5432
|
+
}
|
|
5038
5433
|
async function runProductMarketWizard(rl) {
|
|
5039
5434
|
writeWizardSection("Find leads from a product market", "Start from a company website, LinkedIn company page, product page, or category page. I will turn that into intended job titles and durable Sales Navigator crawls.");
|
|
5040
5435
|
const input = await promptText(rl, "What company website or LinkedIn page should I start from?", {
|
|
5041
5436
|
required: true
|
|
5042
5437
|
});
|
|
5438
|
+
if (isSalesNavigatorPeopleSearchUrl(input)) {
|
|
5439
|
+
writeWizardLine();
|
|
5440
|
+
await runDirectSalesNavigatorSearchWizard(input);
|
|
5441
|
+
return;
|
|
5442
|
+
}
|
|
5043
5443
|
const productLimit = z.coerce.number().int().min(1).max(5000).parse(await promptText(rl, "How many products should I inspect?", { defaultValue: "25", required: true }));
|
|
5044
5444
|
const titleLimit = z.coerce.number().int().min(1).max(1000).parse(await promptText(rl, "How many job titles should I turn into Sales Navigator crawls?", {
|
|
5045
5445
|
defaultValue: "5",
|
|
@@ -5237,7 +5637,7 @@ async function runWizard(options) {
|
|
|
5237
5637
|
throw new Error("wizard does not support --json or --quiet.");
|
|
5238
5638
|
}
|
|
5239
5639
|
writeWizardLine("Salesprompter");
|
|
5240
|
-
writeWizardLine("Start with a company website, LinkedIn product page, or
|
|
5640
|
+
writeWizardLine("Start with a company website, LinkedIn product page, category URL, or Sales Navigator search. I will guide you from there.");
|
|
5241
5641
|
writeWizardLine();
|
|
5242
5642
|
const rl = createInterface({
|
|
5243
5643
|
input: process.stdin,
|
|
@@ -5248,46 +5648,64 @@ async function runWizard(options) {
|
|
|
5248
5648
|
if (wizardSession.session && wizardSession.restoredFromCache) {
|
|
5249
5649
|
await confirmWizardWorkspace(rl, wizardSession.session, options);
|
|
5250
5650
|
}
|
|
5251
|
-
|
|
5252
|
-
|
|
5253
|
-
|
|
5254
|
-
|
|
5255
|
-
|
|
5256
|
-
|
|
5257
|
-
|
|
5258
|
-
|
|
5259
|
-
|
|
5260
|
-
|
|
5261
|
-
|
|
5262
|
-
|
|
5263
|
-
|
|
5264
|
-
|
|
5265
|
-
|
|
5266
|
-
|
|
5267
|
-
|
|
5268
|
-
|
|
5269
|
-
|
|
5270
|
-
|
|
5271
|
-
|
|
5272
|
-
|
|
5273
|
-
|
|
5274
|
-
|
|
5651
|
+
for (;;) {
|
|
5652
|
+
const flow = await promptChoice(rl, "What do you want help with?", [
|
|
5653
|
+
{
|
|
5654
|
+
value: "product-market",
|
|
5655
|
+
label: "Find leads from a product market",
|
|
5656
|
+
description: "Start from a company, product, or LinkedIn category and crawl Sales Navigator",
|
|
5657
|
+
aliases: ["product market", "linkedin products", "category", "sales navigator", "crawl"]
|
|
5658
|
+
},
|
|
5659
|
+
{
|
|
5660
|
+
value: "reference-company",
|
|
5661
|
+
label: "Use a built-in vendor shortcut",
|
|
5662
|
+
description: "Generate the saved vendor ICP and search workspace leads",
|
|
5663
|
+
aliases: ["vendor", "shortcut", "vendor template", "quick template"]
|
|
5664
|
+
},
|
|
5665
|
+
{
|
|
5666
|
+
value: "target-company",
|
|
5667
|
+
label: "Find people at a specific company",
|
|
5668
|
+
description: "Example: find people at company.com",
|
|
5669
|
+
aliases: ["target company", "company", "find people", "people at a company", "lead generation"]
|
|
5670
|
+
},
|
|
5671
|
+
{
|
|
5672
|
+
value: "switch-workspace",
|
|
5673
|
+
label: "Switch workspace",
|
|
5674
|
+
description: "Choose Gojiberry, SelectLine, or another Salesprompter workspace",
|
|
5675
|
+
aliases: ["switch workspace", "change workspace", "gojiberry", "organization", "org"]
|
|
5676
|
+
},
|
|
5677
|
+
{
|
|
5678
|
+
value: "outreach-sync",
|
|
5679
|
+
label: "Push qualified leads to Instantly",
|
|
5680
|
+
description: "Use a saved leads file to fill an Instantly campaign",
|
|
5681
|
+
aliases: ["instantly", "outreach", "send leads", "campaign"]
|
|
5682
|
+
}
|
|
5683
|
+
], "product-market");
|
|
5684
|
+
writeWizardLine();
|
|
5685
|
+
if (flow === "switch-workspace") {
|
|
5686
|
+
const result = await switchWorkspaceInCli({
|
|
5687
|
+
...options,
|
|
5688
|
+
rl
|
|
5689
|
+
});
|
|
5690
|
+
writeSessionSummary(result.session);
|
|
5691
|
+
writeWizardLine();
|
|
5692
|
+
continue;
|
|
5275
5693
|
}
|
|
5276
|
-
|
|
5277
|
-
|
|
5278
|
-
|
|
5279
|
-
|
|
5280
|
-
|
|
5281
|
-
|
|
5282
|
-
|
|
5283
|
-
|
|
5284
|
-
|
|
5285
|
-
|
|
5286
|
-
|
|
5287
|
-
|
|
5694
|
+
if (flow === "product-market") {
|
|
5695
|
+
await runProductMarketWizard(rl);
|
|
5696
|
+
return;
|
|
5697
|
+
}
|
|
5698
|
+
if (flow === "reference-company") {
|
|
5699
|
+
await runVendorShortcutWizard(rl);
|
|
5700
|
+
return;
|
|
5701
|
+
}
|
|
5702
|
+
if (flow === "target-company") {
|
|
5703
|
+
await runTargetCompanyWizard(rl);
|
|
5704
|
+
return;
|
|
5705
|
+
}
|
|
5706
|
+
await runOutreachSyncWizard(rl);
|
|
5288
5707
|
return;
|
|
5289
5708
|
}
|
|
5290
|
-
await runOutreachSyncWizard(rl);
|
|
5291
5709
|
}
|
|
5292
5710
|
finally {
|
|
5293
5711
|
rl.close();
|
|
@@ -5605,6 +6023,32 @@ program
|
|
|
5605
6023
|
expiresAt: result.session.expiresAt ?? null
|
|
5606
6024
|
});
|
|
5607
6025
|
});
|
|
6026
|
+
program
|
|
6027
|
+
.command("auth:workspace")
|
|
6028
|
+
.alias("auth:switch")
|
|
6029
|
+
.description("Switch the active Salesprompter workspace for this CLI session.")
|
|
6030
|
+
.option("--api-url <url>", "Salesprompter API base URL, defaults to SALESPROMPTER_API_BASE_URL or salesprompter.ai")
|
|
6031
|
+
.option("--timeout-seconds <number>", "Browser fallback login timeout in seconds", "180")
|
|
6032
|
+
.option("--org-id <id>", "Switch directly to a Clerk organization id")
|
|
6033
|
+
.option("--workspace <nameOrSlug>", "Switch directly to a workspace by name, slug, client id, or org id")
|
|
6034
|
+
.option("--browser", "Use the browser chooser instead of terminal workspace selection")
|
|
6035
|
+
.action(async (options) => {
|
|
6036
|
+
const timeoutSeconds = z.coerce.number().int().min(30).max(1800).parse(options.timeoutSeconds);
|
|
6037
|
+
const result = await switchWorkspaceInCli({
|
|
6038
|
+
apiUrl: options.apiUrl,
|
|
6039
|
+
timeoutSeconds,
|
|
6040
|
+
orgId: options.orgId,
|
|
6041
|
+
workspace: options.workspace,
|
|
6042
|
+
browser: Boolean(options.browser)
|
|
6043
|
+
});
|
|
6044
|
+
printOutput({
|
|
6045
|
+
status: "ok",
|
|
6046
|
+
method: result.method,
|
|
6047
|
+
apiBaseUrl: result.session.apiBaseUrl,
|
|
6048
|
+
user: result.session.user,
|
|
6049
|
+
expiresAt: result.session.expiresAt ?? null
|
|
6050
|
+
});
|
|
6051
|
+
});
|
|
5608
6052
|
program
|
|
5609
6053
|
.command("wizard")
|
|
5610
6054
|
.alias("start")
|
package/package.json
CHANGED