@sellable/mcp 0.1.255 → 0.1.256
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/api.d.ts +3 -0
- package/dist/api.js +24 -13
- package/dist/index-dev.js +0 -0
- package/dist/index.js +0 -0
- package/dist/server.js +13 -7
- package/dist/tools/campaigns.d.ts +41 -0
- package/dist/tools/campaigns.js +22 -3
- package/dist/tools/csv-dnc.d.ts +0 -36
- package/dist/tools/csv-dnc.js +2 -94
- package/dist/tools/leads.d.ts +16 -214
- package/dist/tools/leads.js +1 -39
- package/dist/tools/registry.d.ts +506 -606
- package/dist/tools/tables.d.ts +53 -2
- package/dist/tools/tables.js +78 -0
- package/package.json +1 -1
- package/skills/create-campaign/SKILL.md +0 -6
- package/skills/create-campaign-v2/references/filter-leads.md +0 -2
- package/skills/create-campaign-v2/references/lead-validation-preview.md +0 -2
- package/skills/create-campaign-v2/references/step-13-import-leads.md +1 -3
- package/skills/create-post/SKILL.md +52 -17
- package/skills/create-post/references/gold-standard-post-pack.md +11 -0
- package/skills/create-post/references/hook-research-playbook.md +57 -14
- package/skills/create-post/references/linkedin-preview-rendering.md +163 -0
- package/skills/create-post/references/post-file-contract.md +7 -0
- package/skills/create-post/references/post-validation.md +68 -15
- package/skills/find-leads/SKILL.md +0 -6
- package/skills/research/config.json +9 -0
package/dist/api.d.ts
CHANGED
|
@@ -10,8 +10,11 @@ export declare class SellableApiError extends Error {
|
|
|
10
10
|
constructor(status: number, body: string, guidance?: string);
|
|
11
11
|
}
|
|
12
12
|
export declare class SellableApi {
|
|
13
|
+
private buildError;
|
|
14
|
+
private requestResponse;
|
|
13
15
|
private request;
|
|
14
16
|
get<T>(path: string): Promise<T>;
|
|
17
|
+
getText(path: string): Promise<string>;
|
|
15
18
|
post<T>(path: string, body?: object): Promise<T>;
|
|
16
19
|
put<T>(path: string, body?: object): Promise<T>;
|
|
17
20
|
patch<T>(path: string, body?: object): Promise<T>;
|
package/dist/api.js
CHANGED
|
@@ -14,7 +14,21 @@ export class SellableApiError extends Error {
|
|
|
14
14
|
}
|
|
15
15
|
}
|
|
16
16
|
export class SellableApi {
|
|
17
|
-
|
|
17
|
+
buildError(status, errorText) {
|
|
18
|
+
const isAuthError = status === 401 || status === 403;
|
|
19
|
+
const missingWorkspace = status === 400 && errorText.includes("Workspace");
|
|
20
|
+
const guidance = isAuthError
|
|
21
|
+
? "Sellable authentication failed.\n\n" +
|
|
22
|
+
`Update ${getConfigPath()} with a valid token from Sellable Settings -> Integrations, then retry.\n\n` +
|
|
23
|
+
"NOTE: If the token was just updated via the LLM (editing the config file), " +
|
|
24
|
+
"the change should take effect immediately. If it still fails, restart Claude Code to restart the MCP server."
|
|
25
|
+
: missingWorkspace
|
|
26
|
+
? "No active workspace selected.\n\n" +
|
|
27
|
+
"Run list_workspaces then set_active_workspace to choose a workspace."
|
|
28
|
+
: undefined;
|
|
29
|
+
return new SellableApiError(status, errorText, guidance);
|
|
30
|
+
}
|
|
31
|
+
async requestResponse(method, path, body) {
|
|
18
32
|
// Re-read config on every request so workspace switches take effect immediately
|
|
19
33
|
const config = getConfig();
|
|
20
34
|
const url = `${config.apiUrl}${path}`;
|
|
@@ -30,24 +44,21 @@ export class SellableApi {
|
|
|
30
44
|
});
|
|
31
45
|
if (!response.ok) {
|
|
32
46
|
const errorText = await response.text();
|
|
33
|
-
|
|
34
|
-
const missingWorkspace = response.status === 400 && errorText.includes("Workspace");
|
|
35
|
-
const guidance = isAuthError
|
|
36
|
-
? "Sellable authentication failed.\n\n" +
|
|
37
|
-
`Update ${getConfigPath()} with a valid token from Sellable Settings -> Integrations, then retry.\n\n` +
|
|
38
|
-
"NOTE: If the token was just updated via the LLM (editing the config file), " +
|
|
39
|
-
"the change should take effect immediately. If it still fails, restart Claude Code to restart the MCP server."
|
|
40
|
-
: missingWorkspace
|
|
41
|
-
? "No active workspace selected.\n\n" +
|
|
42
|
-
"Run list_workspaces then set_active_workspace to choose a workspace."
|
|
43
|
-
: undefined;
|
|
44
|
-
throw new SellableApiError(response.status, errorText, guidance);
|
|
47
|
+
throw this.buildError(response.status, errorText);
|
|
45
48
|
}
|
|
49
|
+
return response;
|
|
50
|
+
}
|
|
51
|
+
async request(method, path, body) {
|
|
52
|
+
const response = await this.requestResponse(method, path, body);
|
|
46
53
|
return response.json();
|
|
47
54
|
}
|
|
48
55
|
async get(path) {
|
|
49
56
|
return this.request("GET", path);
|
|
50
57
|
}
|
|
58
|
+
async getText(path) {
|
|
59
|
+
const response = await this.requestResponse("GET", path);
|
|
60
|
+
return response.text();
|
|
61
|
+
}
|
|
51
62
|
async post(path, body) {
|
|
52
63
|
return this.request("POST", path, body);
|
|
53
64
|
}
|
package/dist/index-dev.js
CHANGED
|
File without changes
|
package/dist/index.js
CHANGED
|
File without changes
|
package/dist/server.js
CHANGED
|
@@ -5,12 +5,12 @@ import { getSkillByName, listSkills } from "./skills.js";
|
|
|
5
5
|
import { getAuthStatus } from "./tools/auth.js";
|
|
6
6
|
import { handleAddColumn, handleCommitBlueprint, } from "./tools/blueprint-commit.js";
|
|
7
7
|
import { bootstrapCreateCampaign } from "./tools/bootstrap.js";
|
|
8
|
-
import { createCampaign, getCampaign, getCampaignMessagesPreview, getCampaigns, pauseCampaign, startCampaign, updateCampaign, updateCampaignBrief, } from "./tools/campaigns.js";
|
|
9
8
|
import { getCampaignTableSchema, queueCampaignCells, reviseMessageTemplateAndRerun, selectCampaignCells, waitForCampaignProcessing, } from "./tools/campaign-processing.js";
|
|
9
|
+
import { createCampaign, duplicateCampaign, getCampaign, getCampaignMessagesPreview, getCampaigns, pauseCampaign, startCampaign, updateCampaign, updateCampaignBrief, } from "./tools/campaigns.js";
|
|
10
10
|
import { queueCells, updateCell } from "./tools/cells.js";
|
|
11
11
|
import { handleStartCliLogin, handleWaitForCliLogin, } from "./tools/cli-login.js";
|
|
12
|
+
import { capturePostIdeaTool, getPostDraftTool, getPostIdeaTool, getPublishedPostTool, listPostDraftIterationsTool, listPostDraftsTool, listPostIdeasTool, listPublishedPostsTool, markPostPublishedTool, saveHookResearchTool, savePostDraftTool, updatePostDraftTool, updatePublishedPostMetricsTool, } from "./tools/content-posts.js";
|
|
12
13
|
import { getCampaignContext, hydrateCampaignContextFromCampaign, markCampaignContextDirty, } from "./tools/context.js";
|
|
13
|
-
import { capturePostIdeaTool, getPublishedPostTool, getPostDraftTool, getPostIdeaTool, listPostDraftIterationsTool, listPostDraftsTool, listPostIdeasTool, listPublishedPostsTool, markPostPublishedTool, saveHookResearchTool, savePostDraftTool, updatePostDraftTool, updatePublishedPostMetricsTool, } from "./tools/content-posts.js";
|
|
14
14
|
import { addToCommentCampaign, addToConnectionCampaign, addToInmailCampaign, getEngagedPosts, getOrCreateDirectCampaignTable, pauseDirectCampaign, startDirectCampaign, } from "./tools/direct-campaigns.js";
|
|
15
15
|
import { bootstrapEngage, bootstrapEngageMulti, } from "./tools/engage-bootstrap.js";
|
|
16
16
|
import { searchEngagementPosts } from "./tools/engage-discovery.js";
|
|
@@ -18,7 +18,7 @@ import { copySenderConfigTool, getEngageMemoryTool, migrateFlatConfigsTool, reco
|
|
|
18
18
|
import { getEngageStateTool, setEngageStateTool, } from "./tools/engage-state.js";
|
|
19
19
|
import { bulkEnrichWithProspeo, enrichWithProspeo, getProspeoCredits, } from "./tools/enrichment.js";
|
|
20
20
|
import { getCampaignFramework } from "./tools/framework.js";
|
|
21
|
-
import { cancelLeadImport,
|
|
21
|
+
import { cancelLeadImport, confirmLeadList, confirmProspeoCompanyAccounts, getProviderPrompt, importLeads, loadCsvDncEntriesTool, loadCsvDomains, loadCsvLinkedinLeads, lookupSalesNavFilter, saveDomainFilters, searchApollo, searchProspeo, searchProspeoCompanies, searchSalesNav, searchSignals, selectPromisingPosts, setHeadlineICPCriteria, } from "./tools/leads.js";
|
|
22
22
|
import { fetchCompany, fetchCompanyPosts, fetchLinkedInPosts, fetchLinkedInProfile, fetchPostEngagers, getLinkedInProfile, getUserPosts, } from "./tools/linkedin.js";
|
|
23
23
|
import { getCampaignNavigationState } from "./tools/navigation.js";
|
|
24
24
|
import { addOnDemandLeads, createOnDemandCampaign, createOnDemandTable, initOnDemandSequence, pauseOnDemandCampaign, startOnDemandCampaign, } from "./tools/one-off.js";
|
|
@@ -30,7 +30,7 @@ import { getRows, getTableRows, getTableRowsMinimal } from "./tools/rows.js";
|
|
|
30
30
|
import { addRubricItem, checkRubric, deleteRubricItem, draftRubrics, saveRubrics, selectNecessaryRubrics, updateRubricItem, waitForRubricResults, } from "./tools/rubrics.js";
|
|
31
31
|
import { getSender, listSenders } from "./tools/senders.js";
|
|
32
32
|
import { attachRecommendedSequence, attachSequence, createWorkflowTable, } from "./tools/sequencer.js";
|
|
33
|
-
import { listTables } from "./tools/tables.js";
|
|
33
|
+
import { exportTableCsv, listTables } from "./tools/tables.js";
|
|
34
34
|
import { handleVerifyTableRow } from "./tools/verify-row.js";
|
|
35
35
|
import { sanitizeWatchUrlsForMcpResult } from "./tools/watch-url-security.js";
|
|
36
36
|
import { addTeammate, createWorkspace, getActiveWorkspace, listWorkspaces, setActiveWorkspace, } from "./tools/workspaces.js";
|
|
@@ -208,6 +208,12 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
208
208
|
case "pause_campaign":
|
|
209
209
|
result = await pauseCampaign(args?.campaignId);
|
|
210
210
|
break;
|
|
211
|
+
case "duplicate_campaign":
|
|
212
|
+
result = await duplicateCampaign(args?.campaignId);
|
|
213
|
+
if (result?.campaignOfferId) {
|
|
214
|
+
markCampaignContextDirty(result.campaignOfferId, "duplicate_campaign");
|
|
215
|
+
}
|
|
216
|
+
break;
|
|
211
217
|
case "update_campaign_brief":
|
|
212
218
|
result = await updateCampaignBrief(args?.campaignId, args?.campaignBrief);
|
|
213
219
|
if (args?.campaignId) {
|
|
@@ -277,6 +283,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
277
283
|
case "list_tables":
|
|
278
284
|
result = await listTables(args);
|
|
279
285
|
break;
|
|
286
|
+
case "export_table_csv":
|
|
287
|
+
result = await exportTableCsv(args);
|
|
288
|
+
break;
|
|
280
289
|
case "commit_blueprint":
|
|
281
290
|
result = await handleCommitBlueprint(args);
|
|
282
291
|
break;
|
|
@@ -366,9 +375,6 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
366
375
|
case "load_csv_dnc_entries":
|
|
367
376
|
result = await loadCsvDncEntriesTool(args);
|
|
368
377
|
break;
|
|
369
|
-
case "list_dnc_entries":
|
|
370
|
-
result = await listDncEntriesTool(args);
|
|
371
|
-
break;
|
|
372
378
|
case "save_domain_filters":
|
|
373
379
|
result = await saveDomainFilters(args);
|
|
374
380
|
break;
|
|
@@ -150,6 +150,42 @@ export interface UpdateCampaignInput {
|
|
|
150
150
|
rubric?: unknown[];
|
|
151
151
|
}
|
|
152
152
|
export declare const campaignToolDefinitions: ({
|
|
153
|
+
name: string;
|
|
154
|
+
description: string;
|
|
155
|
+
inputSchema: {
|
|
156
|
+
type: string;
|
|
157
|
+
properties: {
|
|
158
|
+
campaignId: {
|
|
159
|
+
type: string;
|
|
160
|
+
description: string;
|
|
161
|
+
};
|
|
162
|
+
limit?: undefined;
|
|
163
|
+
tableId?: undefined;
|
|
164
|
+
leadLimit?: undefined;
|
|
165
|
+
page?: undefined;
|
|
166
|
+
filters?: undefined;
|
|
167
|
+
name?: undefined;
|
|
168
|
+
clientProspectId?: undefined;
|
|
169
|
+
senderLinkedinUrl?: undefined;
|
|
170
|
+
offerPositioning?: undefined;
|
|
171
|
+
campaignBrief?: undefined;
|
|
172
|
+
messageGenerationMode?: undefined;
|
|
173
|
+
currentStep?: undefined;
|
|
174
|
+
watchNarration?: undefined;
|
|
175
|
+
leadSourceType?: undefined;
|
|
176
|
+
leadSourceProvider?: undefined;
|
|
177
|
+
selectedLeadListId?: undefined;
|
|
178
|
+
senderIds?: undefined;
|
|
179
|
+
interactionMode?: undefined;
|
|
180
|
+
enableICPFilters?: undefined;
|
|
181
|
+
useMessagingTemplate?: undefined;
|
|
182
|
+
rubric?: undefined;
|
|
183
|
+
flowVersion?: undefined;
|
|
184
|
+
};
|
|
185
|
+
required: string[];
|
|
186
|
+
additionalProperties: boolean;
|
|
187
|
+
};
|
|
188
|
+
} | {
|
|
153
189
|
name: string;
|
|
154
190
|
description: string;
|
|
155
191
|
inputSchema: {
|
|
@@ -600,6 +636,11 @@ export declare function startCampaign(campaignId: string): Promise<{
|
|
|
600
636
|
export declare function pauseCampaign(campaignId: string): Promise<{
|
|
601
637
|
success: boolean;
|
|
602
638
|
}>;
|
|
639
|
+
export declare function duplicateCampaign(campaignId: string): Promise<{
|
|
640
|
+
campaignOfferId: string;
|
|
641
|
+
campaignName: string;
|
|
642
|
+
workflowTableId: string | null;
|
|
643
|
+
}>;
|
|
603
644
|
export declare function updateCampaignBrief(campaignId: string, campaignBrief: string): Promise<{
|
|
604
645
|
success: boolean;
|
|
605
646
|
campaignBrief: string;
|
package/dist/tools/campaigns.js
CHANGED
|
@@ -49,8 +49,7 @@ function getCampaignBuilderWatchModeFromUrl(watchUrl) {
|
|
|
49
49
|
export function getCampaignBuilderWatchModeDriverLabel(mode = getCampaignBuilderWatchModeParam()) {
|
|
50
50
|
return mode === "codex" ? "Codex" : "Claude Code";
|
|
51
51
|
}
|
|
52
|
-
export function buildCampaignWatchHandoffMarkdown(watchUrl, mode = getCampaignBuilderWatchModeFromUrl(watchUrl) ??
|
|
53
|
-
getCampaignBuilderWatchModeParam()) {
|
|
52
|
+
export function buildCampaignWatchHandoffMarkdown(watchUrl, mode = getCampaignBuilderWatchModeFromUrl(watchUrl) ?? getCampaignBuilderWatchModeParam()) {
|
|
54
53
|
const driverLabel = getCampaignBuilderWatchModeDriverLabel(mode);
|
|
55
54
|
const headline = `WATCH ${driverLabel.toUpperCase()} BUILD THE CAMPAIGN LIVE`;
|
|
56
55
|
return [
|
|
@@ -85,6 +84,21 @@ function assertBriefHandoffWatchUrl(watchUrl, campaignId) {
|
|
|
85
84
|
"with create_campaign({ campaignId }) or get_campaign before asking for approval.");
|
|
86
85
|
}
|
|
87
86
|
export const campaignToolDefinitions = [
|
|
87
|
+
{
|
|
88
|
+
name: "duplicate_campaign",
|
|
89
|
+
description: "Duplicate a campaign by calling Sellable's existing duplicate endpoint. Returns campaignOfferId, campaignName, and workflowTableId for the new copy.",
|
|
90
|
+
inputSchema: {
|
|
91
|
+
type: "object",
|
|
92
|
+
properties: {
|
|
93
|
+
campaignId: {
|
|
94
|
+
type: "string",
|
|
95
|
+
description: "Source campaign ID to duplicate.",
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
required: ["campaignId"],
|
|
99
|
+
additionalProperties: false,
|
|
100
|
+
},
|
|
101
|
+
},
|
|
88
102
|
{
|
|
89
103
|
name: "get_campaigns",
|
|
90
104
|
description: "List campaigns for the authenticated user. Returns id, name, createdAt. Ordered by most recent first.",
|
|
@@ -845,7 +859,8 @@ export async function createCampaign(input) {
|
|
|
845
859
|
name,
|
|
846
860
|
clientProspectId,
|
|
847
861
|
offerPositioning,
|
|
848
|
-
...(shouldPersistSourceStateOnCreate &&
|
|
862
|
+
...(shouldPersistSourceStateOnCreate &&
|
|
863
|
+
input.leadSourceProvider !== undefined
|
|
849
864
|
? { leadSourceProvider: normalizedLeadSourceProvider }
|
|
850
865
|
: {}),
|
|
851
866
|
currentStep: effectiveCurrentStep,
|
|
@@ -950,6 +965,10 @@ export async function pauseCampaign(campaignId) {
|
|
|
950
965
|
const api = getApi();
|
|
951
966
|
return api.post(`/api/v3/campaigns/${campaignId}/pause`);
|
|
952
967
|
}
|
|
968
|
+
export async function duplicateCampaign(campaignId) {
|
|
969
|
+
const api = getApi();
|
|
970
|
+
return api.post(`/api/v3/campaigns/${campaignId}/duplicate`);
|
|
971
|
+
}
|
|
953
972
|
export async function updateCampaignBrief(campaignId, campaignBrief) {
|
|
954
973
|
const api = getApi();
|
|
955
974
|
return api.patch(`/api/v3/mcp/campaigns/${campaignId}`, { campaignBrief });
|
package/dist/tools/csv-dnc.d.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
type DncSourceType = "all" | "manual" | "hubspot";
|
|
2
1
|
type InvalidDncRow = {
|
|
3
2
|
row: number;
|
|
4
3
|
column: string | null;
|
|
@@ -15,41 +14,6 @@ export type LoadCsvDncEntriesInput = {
|
|
|
15
14
|
confirmationToken?: string;
|
|
16
15
|
confirmed?: boolean;
|
|
17
16
|
};
|
|
18
|
-
export type ListDncEntriesInput = {
|
|
19
|
-
page?: number;
|
|
20
|
-
limit?: number;
|
|
21
|
-
search?: string;
|
|
22
|
-
listName?: string;
|
|
23
|
-
sourceType?: DncSourceType;
|
|
24
|
-
includeDeleted?: boolean;
|
|
25
|
-
};
|
|
26
|
-
export declare function listDncEntries(input?: ListDncEntriesInput): Promise<{
|
|
27
|
-
ok: boolean;
|
|
28
|
-
workspaceId: string;
|
|
29
|
-
workspaceName: string;
|
|
30
|
-
pagination: {
|
|
31
|
-
total: number;
|
|
32
|
-
page: number;
|
|
33
|
-
limit: number;
|
|
34
|
-
totalPages: number;
|
|
35
|
-
};
|
|
36
|
-
listNames: string[];
|
|
37
|
-
entries: {
|
|
38
|
-
id: string | null;
|
|
39
|
-
type: string;
|
|
40
|
-
domain: string | null;
|
|
41
|
-
linkedin: string | null;
|
|
42
|
-
linkedinUsername: string | null;
|
|
43
|
-
name: string | null;
|
|
44
|
-
source: {
|
|
45
|
-
type: string;
|
|
46
|
-
label: string;
|
|
47
|
-
listName: string | null;
|
|
48
|
-
};
|
|
49
|
-
createdAt: string | null;
|
|
50
|
-
}[];
|
|
51
|
-
guidance: string;
|
|
52
|
-
}>;
|
|
53
17
|
export declare function loadCsvDncEntries(input: LoadCsvDncEntriesInput): Promise<{
|
|
54
18
|
guidance: string;
|
|
55
19
|
createdCount: number;
|
package/dist/tools/csv-dnc.js
CHANGED
|
@@ -14,7 +14,6 @@ const CONFIRMATION_TOKEN_VERSION = "csv-dnc-preview-v1";
|
|
|
14
14
|
const confirmationSecret = randomBytes(32).toString("hex");
|
|
15
15
|
const MAX_DNC_UPLOAD_BYTES = 5 * 1024 * 1024;
|
|
16
16
|
const MAX_DNC_CANDIDATES = 7500;
|
|
17
|
-
const MAX_DNC_LIST_LIMIT = 100;
|
|
18
17
|
const STRONG_DOMAIN_HEADER_KEYS = new Set([
|
|
19
18
|
"domain",
|
|
20
19
|
"website",
|
|
@@ -326,84 +325,16 @@ async function getVerifiedActiveWorkspace() {
|
|
|
326
325
|
const config = getConfig();
|
|
327
326
|
const activeWorkspaceId = config.activeWorkspaceId || config.workspaceId;
|
|
328
327
|
if (!activeWorkspaceId) {
|
|
329
|
-
throw new Error("No active workspace selected. Run list_workspaces then set_active_workspace
|
|
328
|
+
throw new Error("No active workspace selected. Run list_workspaces then set_active_workspace before importing DNC entries.");
|
|
330
329
|
}
|
|
331
330
|
const api = getApi();
|
|
332
331
|
const { workspaces } = await api.get("/api/v3/workspaces");
|
|
333
332
|
const match = workspaces.find((workspace) => workspace.id === activeWorkspaceId);
|
|
334
333
|
if (!match) {
|
|
335
|
-
throw new Error(`Active workspace ${activeWorkspaceId} is not in the server access list. Run list_workspaces then set_active_workspace for the
|
|
334
|
+
throw new Error(`Active workspace ${activeWorkspaceId} is not in the server access list. Run list_workspaces then set_active_workspace for the workspace that should receive the DNC import.`);
|
|
336
335
|
}
|
|
337
336
|
return match;
|
|
338
337
|
}
|
|
339
|
-
function clampListPositiveInt(value, fallback, max) {
|
|
340
|
-
const parsed = typeof value === "number"
|
|
341
|
-
? value
|
|
342
|
-
: Number.parseInt(String(value ?? ""), 10);
|
|
343
|
-
if (!Number.isFinite(parsed) || parsed < 1)
|
|
344
|
-
return fallback;
|
|
345
|
-
return Math.min(Math.floor(parsed), max);
|
|
346
|
-
}
|
|
347
|
-
function normalizeDncSourceType(value) {
|
|
348
|
-
if (value === undefined || value === null || value === "")
|
|
349
|
-
return "all";
|
|
350
|
-
if (value === "all" || value === "manual" || value === "hubspot") {
|
|
351
|
-
return value;
|
|
352
|
-
}
|
|
353
|
-
throw new Error("sourceType must be one of all, manual, or hubspot.");
|
|
354
|
-
}
|
|
355
|
-
function optionalTrimmed(value) {
|
|
356
|
-
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
357
|
-
}
|
|
358
|
-
function normalizeCreatedAt(value) {
|
|
359
|
-
if (value instanceof Date)
|
|
360
|
-
return value.toISOString();
|
|
361
|
-
return optionalTrimmed(value);
|
|
362
|
-
}
|
|
363
|
-
function compactDncListEntry(entry) {
|
|
364
|
-
const domain = optionalTrimmed(entry.domain);
|
|
365
|
-
const linkedin = optionalTrimmed(entry.normalizedLinkedinUrl);
|
|
366
|
-
const sourceType = optionalTrimmed(entry.sourceType) ?? "manual";
|
|
367
|
-
return {
|
|
368
|
-
id: optionalTrimmed(entry.id),
|
|
369
|
-
type: domain ? "domain" : "linkedin",
|
|
370
|
-
domain,
|
|
371
|
-
linkedin,
|
|
372
|
-
linkedinUsername: optionalTrimmed(entry.linkedinUsername),
|
|
373
|
-
name: optionalTrimmed(entry.name),
|
|
374
|
-
source: {
|
|
375
|
-
type: sourceType,
|
|
376
|
-
label: optionalTrimmed(entry.sourceLabel) ??
|
|
377
|
-
(sourceType === "hubspot" ? "HubSpot" : "Manual"),
|
|
378
|
-
listName: optionalTrimmed(entry.sourceListName),
|
|
379
|
-
},
|
|
380
|
-
createdAt: normalizeCreatedAt(entry.createdAt),
|
|
381
|
-
};
|
|
382
|
-
}
|
|
383
|
-
function buildDncListPath(input) {
|
|
384
|
-
const page = clampListPositiveInt(input.page, 1, Number.MAX_SAFE_INTEGER);
|
|
385
|
-
const limit = clampListPositiveInt(input.limit, 25, MAX_DNC_LIST_LIMIT);
|
|
386
|
-
const sourceType = normalizeDncSourceType(input.sourceType);
|
|
387
|
-
const params = new URLSearchParams({
|
|
388
|
-
page: String(page),
|
|
389
|
-
limit: String(limit),
|
|
390
|
-
});
|
|
391
|
-
const search = optionalTrimmed(input.search);
|
|
392
|
-
const listName = optionalTrimmed(input.listName);
|
|
393
|
-
if (search)
|
|
394
|
-
params.set("search", search);
|
|
395
|
-
if (listName)
|
|
396
|
-
params.set("listName", listName);
|
|
397
|
-
if (sourceType !== "all")
|
|
398
|
-
params.set("sourceType", sourceType);
|
|
399
|
-
if (input.includeDeleted === true)
|
|
400
|
-
params.set("includeDeleted", "true");
|
|
401
|
-
return {
|
|
402
|
-
path: `/api/v3/dnc-list?${params.toString()}`,
|
|
403
|
-
page,
|
|
404
|
-
limit,
|
|
405
|
-
};
|
|
406
|
-
}
|
|
407
338
|
function makeSignature(payload) {
|
|
408
339
|
return createHmac("sha256", confirmationSecret)
|
|
409
340
|
.update(JSON.stringify(payload))
|
|
@@ -625,29 +556,6 @@ async function runDncSpotChecks(entries) {
|
|
|
625
556
|
}
|
|
626
557
|
return checks;
|
|
627
558
|
}
|
|
628
|
-
export async function listDncEntries(input = {}) {
|
|
629
|
-
const workspace = await getVerifiedActiveWorkspace();
|
|
630
|
-
const api = getApi();
|
|
631
|
-
const request = buildDncListPath(input);
|
|
632
|
-
const response = await api.get(request.path);
|
|
633
|
-
const pagination = response.pagination ?? {};
|
|
634
|
-
return {
|
|
635
|
-
ok: true,
|
|
636
|
-
workspaceId: workspace.id,
|
|
637
|
-
workspaceName: workspace.name,
|
|
638
|
-
pagination: {
|
|
639
|
-
total: pagination.total ?? 0,
|
|
640
|
-
page: pagination.page ?? request.page,
|
|
641
|
-
limit: pagination.limit ?? request.limit,
|
|
642
|
-
totalPages: pagination.totalPages ?? 0,
|
|
643
|
-
},
|
|
644
|
-
listNames: Array.isArray(response.listNames) ? response.listNames : [],
|
|
645
|
-
entries: Array.isArray(response.entries)
|
|
646
|
-
? response.entries.map(compactDncListEntry)
|
|
647
|
-
: [],
|
|
648
|
-
guidance: "This is Sellable's workspace DNC list used by DNC Check.",
|
|
649
|
-
};
|
|
650
|
-
}
|
|
651
559
|
export async function loadCsvDncEntries(input) {
|
|
652
560
|
const workspace = await getVerifiedActiveWorkspace();
|
|
653
561
|
const api = getApi();
|