@signaliz/sdk 1.0.27 → 1.0.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.
- package/README.md +2 -0
- package/dist/{chunk-TQZFKFPI.mjs → chunk-R4DIAP7P.mjs} +153 -32
- package/dist/index.d.mts +8 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +153 -32
- package/dist/index.mjs +1 -1
- package/dist/mcp-config.js +153 -32
- package/dist/mcp-config.mjs +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -77,6 +77,8 @@ resumes long-running research through the same `/company-signals` endpoint;
|
|
|
77
77
|
callers never need an internal Trigger.dev status URL. The SDK follows the
|
|
78
78
|
server's adaptive polling hints by default and still honors `pollIntervalMs`
|
|
79
79
|
when a caller explicitly overrides the cadence.
|
|
80
|
+
Signal to Copy batches are transported in 25-row chunks; exact duplicates and
|
|
81
|
+
shared company research are single-flighted without changing input order.
|
|
80
82
|
|
|
81
83
|
Company Signal Enrichment defaults `online` and `enableDeepSearch` to `true`
|
|
82
84
|
unless the caller explicitly disables them. Signal to Copy AI returns three
|
|
@@ -365,7 +365,12 @@ var Signaliz = class {
|
|
|
365
365
|
const email = typeof data.email === "string" && data.email.trim() ? data.email.trim() : null;
|
|
366
366
|
const found = Boolean(email) && data.found !== false;
|
|
367
367
|
const verificationStatus = data.verification_status ?? data.status ?? "unknown";
|
|
368
|
-
const
|
|
368
|
+
const rawFreshness = String(data.verification_freshness ?? "").toLowerCase();
|
|
369
|
+
const verificationFreshness = rawFreshness === "fresh" || rawFreshness === "stale" ? rawFreshness : "unknown";
|
|
370
|
+
const needsReverification = data.needs_reverification === true || verificationFreshness === "stale";
|
|
371
|
+
const explicitlyNotSendSafe = data.verified_for_sending === false || needsReverification;
|
|
372
|
+
const verified = !explicitlyNotSendSafe && (data.email_verified === true || data.verified_for_sending === true || ["valid", "deliverable"].includes(String(verificationStatus).toLowerCase()));
|
|
373
|
+
const verificationAgeDays = typeof data.verification_age_days === "number" && Number.isFinite(data.verification_age_days) ? data.verification_age_days : null;
|
|
369
374
|
return {
|
|
370
375
|
success: data.success !== false && found,
|
|
371
376
|
found,
|
|
@@ -374,6 +379,13 @@ var Signaliz = class {
|
|
|
374
379
|
lastName: data.last_name,
|
|
375
380
|
confidence: data.confidence ?? data.confidence_score ?? (verified ? 1 : 0),
|
|
376
381
|
verificationStatus,
|
|
382
|
+
isVerified: verified,
|
|
383
|
+
isVerifiedForSending: !needsReverification && data.verified_for_sending === true,
|
|
384
|
+
isCatchAll: data.email_is_catch_all === true || data.is_catch_all === true,
|
|
385
|
+
verificationFreshness,
|
|
386
|
+
verificationAgeDays,
|
|
387
|
+
verifiedAt: typeof data.verified_at === "string" ? data.verified_at : null,
|
|
388
|
+
needsReverification,
|
|
377
389
|
providerUsed: data.provider_used ?? data.provider ?? data.email_source ?? data.source ?? "unknown",
|
|
378
390
|
error: found ? void 0 : data.error ?? "No verified email found",
|
|
379
391
|
errorCode: found ? void 0 : data.error_code ?? "EMAIL_NOT_FOUND",
|
|
@@ -494,16 +506,7 @@ var Signaliz = class {
|
|
|
494
506
|
return runBatch(companies, (company) => this.enrichCompanySignals(company), options);
|
|
495
507
|
}
|
|
496
508
|
async signalToCopy(params) {
|
|
497
|
-
const request =
|
|
498
|
-
company_domain: params.companyDomain,
|
|
499
|
-
person_name: params.personName,
|
|
500
|
-
title: params.title,
|
|
501
|
-
campaign_offer: params.campaignOffer,
|
|
502
|
-
research_prompt: params.researchPrompt,
|
|
503
|
-
signal_run_id: params.signalRunId,
|
|
504
|
-
skip_cache: params.skipCache,
|
|
505
|
-
enable_deep_search: params.enableDeepSearch
|
|
506
|
-
});
|
|
509
|
+
const request = signalToCopyRequestBody(params);
|
|
507
510
|
let data = await this.client.post("api/v1/signal-to-copy", request);
|
|
508
511
|
if (params.waitForResult !== false && data.run_id && isPendingSignalResponse(data)) {
|
|
509
512
|
const maxWaitMs = Math.max(1e3, params.maxWaitMs ?? 20 * 6e4);
|
|
@@ -520,30 +523,113 @@ var Signaliz = class {
|
|
|
520
523
|
throw new Error(`Signal Copy ${data.run_id} did not complete within ${maxWaitMs}ms`);
|
|
521
524
|
}
|
|
522
525
|
}
|
|
526
|
+
return normalizeSignalToCopyResult(data);
|
|
527
|
+
}
|
|
528
|
+
async createSignalCopyBatch(requests, options) {
|
|
529
|
+
if (!Array.isArray(requests) || requests.length === 0) {
|
|
530
|
+
throw new Error("Signaliz batch requests require at least one item");
|
|
531
|
+
}
|
|
532
|
+
if (requests.length > 5e3) {
|
|
533
|
+
throw new Error(`Signaliz batch requests support at most 5,000 items; received ${requests.length}`);
|
|
534
|
+
}
|
|
535
|
+
const startedAt = Date.now();
|
|
536
|
+
const chunks = [];
|
|
537
|
+
for (let offset = 0; offset < requests.length; offset += 25) {
|
|
538
|
+
chunks.push({ offset, requests: requests.slice(offset, offset + 25) });
|
|
539
|
+
}
|
|
540
|
+
const serverConcurrency = Math.min(10, options?.concurrency ?? 10);
|
|
541
|
+
const chunkBatch = await runBatch(
|
|
542
|
+
chunks,
|
|
543
|
+
(chunk) => this.runSignalCopyBatchChunk(chunk.requests, serverConcurrency),
|
|
544
|
+
options
|
|
545
|
+
);
|
|
546
|
+
const results = new Array(requests.length);
|
|
547
|
+
for (const chunkResult of chunkBatch.results) {
|
|
548
|
+
const chunk = chunks[chunkResult.index];
|
|
549
|
+
if (!chunkResult.success || !chunkResult.data) {
|
|
550
|
+
for (let index = 0; index < chunk.requests.length; index += 1) {
|
|
551
|
+
results[chunk.offset + index] = {
|
|
552
|
+
index: chunk.offset + index,
|
|
553
|
+
success: false,
|
|
554
|
+
error: chunkResult.error || "Signal to Copy batch request failed"
|
|
555
|
+
};
|
|
556
|
+
}
|
|
557
|
+
continue;
|
|
558
|
+
}
|
|
559
|
+
for (const item of chunkResult.data) {
|
|
560
|
+
results[chunk.offset + item.index] = { ...item, index: chunk.offset + item.index };
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
const succeeded = results.filter((item) => item.success).length;
|
|
523
564
|
return {
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
subjectLine: data.subject_line ?? "",
|
|
530
|
-
openingLine: data.opening_line ?? "",
|
|
531
|
-
confidence: data.confidence ?? 0,
|
|
532
|
-
evidenceUrl: data.evidence_url ?? "",
|
|
533
|
-
researchPrompt: data.research_prompt,
|
|
534
|
-
variations: (data.variations ?? []).map((variation) => ({
|
|
535
|
-
style: variation.style,
|
|
536
|
-
subjectLine: variation.subject_line ?? "",
|
|
537
|
-
openingLine: variation.opening_line ?? "",
|
|
538
|
-
cta: variation.cta ?? "",
|
|
539
|
-
prediction: variation.prediction,
|
|
540
|
-
email: variation.email
|
|
541
|
-
})),
|
|
542
|
-
raw: data
|
|
565
|
+
total: requests.length,
|
|
566
|
+
succeeded,
|
|
567
|
+
failed: requests.length - succeeded,
|
|
568
|
+
durationMs: Date.now() - startedAt,
|
|
569
|
+
results
|
|
543
570
|
};
|
|
544
571
|
}
|
|
545
|
-
async
|
|
546
|
-
|
|
572
|
+
async runSignalCopyBatchChunk(requests, concurrency) {
|
|
573
|
+
const startedAt = Date.now();
|
|
574
|
+
const results = new Array(requests.length);
|
|
575
|
+
let pending = requests.map((params, index) => ({
|
|
576
|
+
index,
|
|
577
|
+
params,
|
|
578
|
+
request: signalToCopyRequestBody(params)
|
|
579
|
+
}));
|
|
580
|
+
while (pending.length > 0) {
|
|
581
|
+
const data = await this.client.post("api/v1/signal-to-copy", {
|
|
582
|
+
requests: pending.map((item) => item.request),
|
|
583
|
+
concurrency
|
|
584
|
+
});
|
|
585
|
+
if (!Array.isArray(data.results) || data.results.length !== pending.length) {
|
|
586
|
+
throw new Error("Signal to Copy batch returned an invalid result count");
|
|
587
|
+
}
|
|
588
|
+
const nextPending = [];
|
|
589
|
+
let nextDelayMs = 6e4;
|
|
590
|
+
for (let index = 0; index < pending.length; index += 1) {
|
|
591
|
+
const task = pending[index];
|
|
592
|
+
const item = data.results[index];
|
|
593
|
+
if (item.success === false) {
|
|
594
|
+
results[task.index] = { index: task.index, success: false, error: item.error || item.message || "Signal to Copy failed" };
|
|
595
|
+
continue;
|
|
596
|
+
}
|
|
597
|
+
if (!isPendingSignalResponse(item)) {
|
|
598
|
+
results[task.index] = { index: task.index, success: true, data: normalizeSignalToCopyResult(item) };
|
|
599
|
+
continue;
|
|
600
|
+
}
|
|
601
|
+
if (task.params.waitForResult === false) {
|
|
602
|
+
results[task.index] = { index: task.index, success: true, data: normalizeSignalToCopyResult(item) };
|
|
603
|
+
continue;
|
|
604
|
+
}
|
|
605
|
+
const maxWaitMs = Math.max(1e3, task.params.maxWaitMs ?? 20 * 6e4);
|
|
606
|
+
if (Date.now() - startedAt >= maxWaitMs) {
|
|
607
|
+
results[task.index] = {
|
|
608
|
+
index: task.index,
|
|
609
|
+
success: false,
|
|
610
|
+
error: `Signal Copy ${item.signal_run_id || item.run_id || ""} did not complete within ${maxWaitMs}ms`
|
|
611
|
+
};
|
|
612
|
+
continue;
|
|
613
|
+
}
|
|
614
|
+
const signalRunId = item.signal_run_id || item.run_id;
|
|
615
|
+
if (!signalRunId) {
|
|
616
|
+
results[task.index] = { index: task.index, success: false, error: "Signal Copy is processing without a signal_run_id" };
|
|
617
|
+
continue;
|
|
618
|
+
}
|
|
619
|
+
nextDelayMs = Math.min(
|
|
620
|
+
nextDelayMs,
|
|
621
|
+
signalPollDelayMs(item, task.params.pollIntervalMs),
|
|
622
|
+
maxWaitMs - (Date.now() - startedAt)
|
|
623
|
+
);
|
|
624
|
+
nextPending.push({
|
|
625
|
+
...task,
|
|
626
|
+
request: { ...task.request, signal_run_id: signalRunId }
|
|
627
|
+
});
|
|
628
|
+
}
|
|
629
|
+
pending = nextPending;
|
|
630
|
+
if (pending.length > 0) await sleep2(nextDelayMs);
|
|
631
|
+
}
|
|
632
|
+
return results;
|
|
547
633
|
}
|
|
548
634
|
async listTools() {
|
|
549
635
|
const result = await this.client.mcp("tools/list");
|
|
@@ -560,6 +646,41 @@ var Signaliz = class {
|
|
|
560
646
|
function compact(input) {
|
|
561
647
|
return Object.fromEntries(Object.entries(input).filter(([, value]) => value !== void 0));
|
|
562
648
|
}
|
|
649
|
+
function signalToCopyRequestBody(params) {
|
|
650
|
+
return compact({
|
|
651
|
+
company_domain: params.companyDomain,
|
|
652
|
+
person_name: params.personName,
|
|
653
|
+
title: params.title,
|
|
654
|
+
campaign_offer: params.campaignOffer,
|
|
655
|
+
research_prompt: params.researchPrompt,
|
|
656
|
+
signal_run_id: params.signalRunId,
|
|
657
|
+
skip_cache: params.skipCache,
|
|
658
|
+
enable_deep_search: params.enableDeepSearch
|
|
659
|
+
});
|
|
660
|
+
}
|
|
661
|
+
function normalizeSignalToCopyResult(data) {
|
|
662
|
+
return {
|
|
663
|
+
success: data.success ?? true,
|
|
664
|
+
status: data.status,
|
|
665
|
+
runId: data.signal_run_id ?? data.run_id,
|
|
666
|
+
strongestSignal: data.strongest_signal ?? "",
|
|
667
|
+
whyNow: data.why_now ?? "",
|
|
668
|
+
subjectLine: data.subject_line ?? "",
|
|
669
|
+
openingLine: data.opening_line ?? "",
|
|
670
|
+
confidence: data.confidence ?? 0,
|
|
671
|
+
evidenceUrl: data.evidence_url ?? "",
|
|
672
|
+
researchPrompt: data.research_prompt,
|
|
673
|
+
variations: (data.variations ?? []).map((variation) => ({
|
|
674
|
+
style: variation.style,
|
|
675
|
+
subjectLine: variation.subject_line ?? "",
|
|
676
|
+
openingLine: variation.opening_line ?? "",
|
|
677
|
+
cta: variation.cta ?? "",
|
|
678
|
+
prediction: variation.prediction,
|
|
679
|
+
email: variation.email
|
|
680
|
+
})),
|
|
681
|
+
raw: data
|
|
682
|
+
};
|
|
683
|
+
}
|
|
563
684
|
function isPendingSignalResponse(data) {
|
|
564
685
|
return ["triggered", "queued", "processing", "pending", "pending_version", "waiting_for_deploy"].includes(String(data.status || "").toLowerCase());
|
|
565
686
|
}
|
package/dist/index.d.mts
CHANGED
|
@@ -49,6 +49,13 @@ interface FindEmailResult {
|
|
|
49
49
|
lastName?: string;
|
|
50
50
|
confidence: number;
|
|
51
51
|
verificationStatus: string;
|
|
52
|
+
isVerified: boolean;
|
|
53
|
+
isVerifiedForSending: boolean;
|
|
54
|
+
isCatchAll: boolean;
|
|
55
|
+
verificationFreshness: 'fresh' | 'stale' | 'unknown';
|
|
56
|
+
verificationAgeDays: number | null;
|
|
57
|
+
verifiedAt: string | null;
|
|
58
|
+
needsReverification: boolean;
|
|
52
59
|
providerUsed: string;
|
|
53
60
|
error?: string;
|
|
54
61
|
errorCode?: string;
|
|
@@ -196,6 +203,7 @@ declare class Signaliz {
|
|
|
196
203
|
enrichCompanies(companies: CompanySignalEnrichmentParams[], options?: BatchOptions): Promise<BatchResult<CompanySignalEnrichmentResult>>;
|
|
197
204
|
signalToCopy(params: SignalToCopyParams): Promise<SignalToCopyResult>;
|
|
198
205
|
createSignalCopyBatch(requests: SignalToCopyParams[], options?: BatchOptions): Promise<BatchResult<SignalToCopyResult>>;
|
|
206
|
+
private runSignalCopyBatchChunk;
|
|
199
207
|
listTools(): Promise<MCPToolInfo[]>;
|
|
200
208
|
health(): Promise<PlatformHealth>;
|
|
201
209
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -49,6 +49,13 @@ interface FindEmailResult {
|
|
|
49
49
|
lastName?: string;
|
|
50
50
|
confidence: number;
|
|
51
51
|
verificationStatus: string;
|
|
52
|
+
isVerified: boolean;
|
|
53
|
+
isVerifiedForSending: boolean;
|
|
54
|
+
isCatchAll: boolean;
|
|
55
|
+
verificationFreshness: 'fresh' | 'stale' | 'unknown';
|
|
56
|
+
verificationAgeDays: number | null;
|
|
57
|
+
verifiedAt: string | null;
|
|
58
|
+
needsReverification: boolean;
|
|
52
59
|
providerUsed: string;
|
|
53
60
|
error?: string;
|
|
54
61
|
errorCode?: string;
|
|
@@ -196,6 +203,7 @@ declare class Signaliz {
|
|
|
196
203
|
enrichCompanies(companies: CompanySignalEnrichmentParams[], options?: BatchOptions): Promise<BatchResult<CompanySignalEnrichmentResult>>;
|
|
197
204
|
signalToCopy(params: SignalToCopyParams): Promise<SignalToCopyResult>;
|
|
198
205
|
createSignalCopyBatch(requests: SignalToCopyParams[], options?: BatchOptions): Promise<BatchResult<SignalToCopyResult>>;
|
|
206
|
+
private runSignalCopyBatchChunk;
|
|
199
207
|
listTools(): Promise<MCPToolInfo[]>;
|
|
200
208
|
health(): Promise<PlatformHealth>;
|
|
201
209
|
}
|
package/dist/index.js
CHANGED
|
@@ -392,7 +392,12 @@ var Signaliz = class {
|
|
|
392
392
|
const email = typeof data.email === "string" && data.email.trim() ? data.email.trim() : null;
|
|
393
393
|
const found = Boolean(email) && data.found !== false;
|
|
394
394
|
const verificationStatus = data.verification_status ?? data.status ?? "unknown";
|
|
395
|
-
const
|
|
395
|
+
const rawFreshness = String(data.verification_freshness ?? "").toLowerCase();
|
|
396
|
+
const verificationFreshness = rawFreshness === "fresh" || rawFreshness === "stale" ? rawFreshness : "unknown";
|
|
397
|
+
const needsReverification = data.needs_reverification === true || verificationFreshness === "stale";
|
|
398
|
+
const explicitlyNotSendSafe = data.verified_for_sending === false || needsReverification;
|
|
399
|
+
const verified = !explicitlyNotSendSafe && (data.email_verified === true || data.verified_for_sending === true || ["valid", "deliverable"].includes(String(verificationStatus).toLowerCase()));
|
|
400
|
+
const verificationAgeDays = typeof data.verification_age_days === "number" && Number.isFinite(data.verification_age_days) ? data.verification_age_days : null;
|
|
396
401
|
return {
|
|
397
402
|
success: data.success !== false && found,
|
|
398
403
|
found,
|
|
@@ -401,6 +406,13 @@ var Signaliz = class {
|
|
|
401
406
|
lastName: data.last_name,
|
|
402
407
|
confidence: data.confidence ?? data.confidence_score ?? (verified ? 1 : 0),
|
|
403
408
|
verificationStatus,
|
|
409
|
+
isVerified: verified,
|
|
410
|
+
isVerifiedForSending: !needsReverification && data.verified_for_sending === true,
|
|
411
|
+
isCatchAll: data.email_is_catch_all === true || data.is_catch_all === true,
|
|
412
|
+
verificationFreshness,
|
|
413
|
+
verificationAgeDays,
|
|
414
|
+
verifiedAt: typeof data.verified_at === "string" ? data.verified_at : null,
|
|
415
|
+
needsReverification,
|
|
404
416
|
providerUsed: data.provider_used ?? data.provider ?? data.email_source ?? data.source ?? "unknown",
|
|
405
417
|
error: found ? void 0 : data.error ?? "No verified email found",
|
|
406
418
|
errorCode: found ? void 0 : data.error_code ?? "EMAIL_NOT_FOUND",
|
|
@@ -521,16 +533,7 @@ var Signaliz = class {
|
|
|
521
533
|
return runBatch(companies, (company) => this.enrichCompanySignals(company), options);
|
|
522
534
|
}
|
|
523
535
|
async signalToCopy(params) {
|
|
524
|
-
const request =
|
|
525
|
-
company_domain: params.companyDomain,
|
|
526
|
-
person_name: params.personName,
|
|
527
|
-
title: params.title,
|
|
528
|
-
campaign_offer: params.campaignOffer,
|
|
529
|
-
research_prompt: params.researchPrompt,
|
|
530
|
-
signal_run_id: params.signalRunId,
|
|
531
|
-
skip_cache: params.skipCache,
|
|
532
|
-
enable_deep_search: params.enableDeepSearch
|
|
533
|
-
});
|
|
536
|
+
const request = signalToCopyRequestBody(params);
|
|
534
537
|
let data = await this.client.post("api/v1/signal-to-copy", request);
|
|
535
538
|
if (params.waitForResult !== false && data.run_id && isPendingSignalResponse(data)) {
|
|
536
539
|
const maxWaitMs = Math.max(1e3, params.maxWaitMs ?? 20 * 6e4);
|
|
@@ -547,30 +550,113 @@ var Signaliz = class {
|
|
|
547
550
|
throw new Error(`Signal Copy ${data.run_id} did not complete within ${maxWaitMs}ms`);
|
|
548
551
|
}
|
|
549
552
|
}
|
|
553
|
+
return normalizeSignalToCopyResult(data);
|
|
554
|
+
}
|
|
555
|
+
async createSignalCopyBatch(requests, options) {
|
|
556
|
+
if (!Array.isArray(requests) || requests.length === 0) {
|
|
557
|
+
throw new Error("Signaliz batch requests require at least one item");
|
|
558
|
+
}
|
|
559
|
+
if (requests.length > 5e3) {
|
|
560
|
+
throw new Error(`Signaliz batch requests support at most 5,000 items; received ${requests.length}`);
|
|
561
|
+
}
|
|
562
|
+
const startedAt = Date.now();
|
|
563
|
+
const chunks = [];
|
|
564
|
+
for (let offset = 0; offset < requests.length; offset += 25) {
|
|
565
|
+
chunks.push({ offset, requests: requests.slice(offset, offset + 25) });
|
|
566
|
+
}
|
|
567
|
+
const serverConcurrency = Math.min(10, options?.concurrency ?? 10);
|
|
568
|
+
const chunkBatch = await runBatch(
|
|
569
|
+
chunks,
|
|
570
|
+
(chunk) => this.runSignalCopyBatchChunk(chunk.requests, serverConcurrency),
|
|
571
|
+
options
|
|
572
|
+
);
|
|
573
|
+
const results = new Array(requests.length);
|
|
574
|
+
for (const chunkResult of chunkBatch.results) {
|
|
575
|
+
const chunk = chunks[chunkResult.index];
|
|
576
|
+
if (!chunkResult.success || !chunkResult.data) {
|
|
577
|
+
for (let index = 0; index < chunk.requests.length; index += 1) {
|
|
578
|
+
results[chunk.offset + index] = {
|
|
579
|
+
index: chunk.offset + index,
|
|
580
|
+
success: false,
|
|
581
|
+
error: chunkResult.error || "Signal to Copy batch request failed"
|
|
582
|
+
};
|
|
583
|
+
}
|
|
584
|
+
continue;
|
|
585
|
+
}
|
|
586
|
+
for (const item of chunkResult.data) {
|
|
587
|
+
results[chunk.offset + item.index] = { ...item, index: chunk.offset + item.index };
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
const succeeded = results.filter((item) => item.success).length;
|
|
550
591
|
return {
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
subjectLine: data.subject_line ?? "",
|
|
557
|
-
openingLine: data.opening_line ?? "",
|
|
558
|
-
confidence: data.confidence ?? 0,
|
|
559
|
-
evidenceUrl: data.evidence_url ?? "",
|
|
560
|
-
researchPrompt: data.research_prompt,
|
|
561
|
-
variations: (data.variations ?? []).map((variation) => ({
|
|
562
|
-
style: variation.style,
|
|
563
|
-
subjectLine: variation.subject_line ?? "",
|
|
564
|
-
openingLine: variation.opening_line ?? "",
|
|
565
|
-
cta: variation.cta ?? "",
|
|
566
|
-
prediction: variation.prediction,
|
|
567
|
-
email: variation.email
|
|
568
|
-
})),
|
|
569
|
-
raw: data
|
|
592
|
+
total: requests.length,
|
|
593
|
+
succeeded,
|
|
594
|
+
failed: requests.length - succeeded,
|
|
595
|
+
durationMs: Date.now() - startedAt,
|
|
596
|
+
results
|
|
570
597
|
};
|
|
571
598
|
}
|
|
572
|
-
async
|
|
573
|
-
|
|
599
|
+
async runSignalCopyBatchChunk(requests, concurrency) {
|
|
600
|
+
const startedAt = Date.now();
|
|
601
|
+
const results = new Array(requests.length);
|
|
602
|
+
let pending = requests.map((params, index) => ({
|
|
603
|
+
index,
|
|
604
|
+
params,
|
|
605
|
+
request: signalToCopyRequestBody(params)
|
|
606
|
+
}));
|
|
607
|
+
while (pending.length > 0) {
|
|
608
|
+
const data = await this.client.post("api/v1/signal-to-copy", {
|
|
609
|
+
requests: pending.map((item) => item.request),
|
|
610
|
+
concurrency
|
|
611
|
+
});
|
|
612
|
+
if (!Array.isArray(data.results) || data.results.length !== pending.length) {
|
|
613
|
+
throw new Error("Signal to Copy batch returned an invalid result count");
|
|
614
|
+
}
|
|
615
|
+
const nextPending = [];
|
|
616
|
+
let nextDelayMs = 6e4;
|
|
617
|
+
for (let index = 0; index < pending.length; index += 1) {
|
|
618
|
+
const task = pending[index];
|
|
619
|
+
const item = data.results[index];
|
|
620
|
+
if (item.success === false) {
|
|
621
|
+
results[task.index] = { index: task.index, success: false, error: item.error || item.message || "Signal to Copy failed" };
|
|
622
|
+
continue;
|
|
623
|
+
}
|
|
624
|
+
if (!isPendingSignalResponse(item)) {
|
|
625
|
+
results[task.index] = { index: task.index, success: true, data: normalizeSignalToCopyResult(item) };
|
|
626
|
+
continue;
|
|
627
|
+
}
|
|
628
|
+
if (task.params.waitForResult === false) {
|
|
629
|
+
results[task.index] = { index: task.index, success: true, data: normalizeSignalToCopyResult(item) };
|
|
630
|
+
continue;
|
|
631
|
+
}
|
|
632
|
+
const maxWaitMs = Math.max(1e3, task.params.maxWaitMs ?? 20 * 6e4);
|
|
633
|
+
if (Date.now() - startedAt >= maxWaitMs) {
|
|
634
|
+
results[task.index] = {
|
|
635
|
+
index: task.index,
|
|
636
|
+
success: false,
|
|
637
|
+
error: `Signal Copy ${item.signal_run_id || item.run_id || ""} did not complete within ${maxWaitMs}ms`
|
|
638
|
+
};
|
|
639
|
+
continue;
|
|
640
|
+
}
|
|
641
|
+
const signalRunId = item.signal_run_id || item.run_id;
|
|
642
|
+
if (!signalRunId) {
|
|
643
|
+
results[task.index] = { index: task.index, success: false, error: "Signal Copy is processing without a signal_run_id" };
|
|
644
|
+
continue;
|
|
645
|
+
}
|
|
646
|
+
nextDelayMs = Math.min(
|
|
647
|
+
nextDelayMs,
|
|
648
|
+
signalPollDelayMs(item, task.params.pollIntervalMs),
|
|
649
|
+
maxWaitMs - (Date.now() - startedAt)
|
|
650
|
+
);
|
|
651
|
+
nextPending.push({
|
|
652
|
+
...task,
|
|
653
|
+
request: { ...task.request, signal_run_id: signalRunId }
|
|
654
|
+
});
|
|
655
|
+
}
|
|
656
|
+
pending = nextPending;
|
|
657
|
+
if (pending.length > 0) await sleep2(nextDelayMs);
|
|
658
|
+
}
|
|
659
|
+
return results;
|
|
574
660
|
}
|
|
575
661
|
async listTools() {
|
|
576
662
|
const result = await this.client.mcp("tools/list");
|
|
@@ -587,6 +673,41 @@ var Signaliz = class {
|
|
|
587
673
|
function compact(input) {
|
|
588
674
|
return Object.fromEntries(Object.entries(input).filter(([, value]) => value !== void 0));
|
|
589
675
|
}
|
|
676
|
+
function signalToCopyRequestBody(params) {
|
|
677
|
+
return compact({
|
|
678
|
+
company_domain: params.companyDomain,
|
|
679
|
+
person_name: params.personName,
|
|
680
|
+
title: params.title,
|
|
681
|
+
campaign_offer: params.campaignOffer,
|
|
682
|
+
research_prompt: params.researchPrompt,
|
|
683
|
+
signal_run_id: params.signalRunId,
|
|
684
|
+
skip_cache: params.skipCache,
|
|
685
|
+
enable_deep_search: params.enableDeepSearch
|
|
686
|
+
});
|
|
687
|
+
}
|
|
688
|
+
function normalizeSignalToCopyResult(data) {
|
|
689
|
+
return {
|
|
690
|
+
success: data.success ?? true,
|
|
691
|
+
status: data.status,
|
|
692
|
+
runId: data.signal_run_id ?? data.run_id,
|
|
693
|
+
strongestSignal: data.strongest_signal ?? "",
|
|
694
|
+
whyNow: data.why_now ?? "",
|
|
695
|
+
subjectLine: data.subject_line ?? "",
|
|
696
|
+
openingLine: data.opening_line ?? "",
|
|
697
|
+
confidence: data.confidence ?? 0,
|
|
698
|
+
evidenceUrl: data.evidence_url ?? "",
|
|
699
|
+
researchPrompt: data.research_prompt,
|
|
700
|
+
variations: (data.variations ?? []).map((variation) => ({
|
|
701
|
+
style: variation.style,
|
|
702
|
+
subjectLine: variation.subject_line ?? "",
|
|
703
|
+
openingLine: variation.opening_line ?? "",
|
|
704
|
+
cta: variation.cta ?? "",
|
|
705
|
+
prediction: variation.prediction,
|
|
706
|
+
email: variation.email
|
|
707
|
+
})),
|
|
708
|
+
raw: data
|
|
709
|
+
};
|
|
710
|
+
}
|
|
590
711
|
function isPendingSignalResponse(data) {
|
|
591
712
|
return ["triggered", "queued", "processing", "pending", "pending_version", "waiting_for_deploy"].includes(String(data.status || "").toLowerCase());
|
|
592
713
|
}
|
package/dist/index.mjs
CHANGED
package/dist/mcp-config.js
CHANGED
|
@@ -396,7 +396,12 @@ var Signaliz = class {
|
|
|
396
396
|
const email = typeof data.email === "string" && data.email.trim() ? data.email.trim() : null;
|
|
397
397
|
const found = Boolean(email) && data.found !== false;
|
|
398
398
|
const verificationStatus = data.verification_status ?? data.status ?? "unknown";
|
|
399
|
-
const
|
|
399
|
+
const rawFreshness = String(data.verification_freshness ?? "").toLowerCase();
|
|
400
|
+
const verificationFreshness = rawFreshness === "fresh" || rawFreshness === "stale" ? rawFreshness : "unknown";
|
|
401
|
+
const needsReverification = data.needs_reverification === true || verificationFreshness === "stale";
|
|
402
|
+
const explicitlyNotSendSafe = data.verified_for_sending === false || needsReverification;
|
|
403
|
+
const verified = !explicitlyNotSendSafe && (data.email_verified === true || data.verified_for_sending === true || ["valid", "deliverable"].includes(String(verificationStatus).toLowerCase()));
|
|
404
|
+
const verificationAgeDays = typeof data.verification_age_days === "number" && Number.isFinite(data.verification_age_days) ? data.verification_age_days : null;
|
|
400
405
|
return {
|
|
401
406
|
success: data.success !== false && found,
|
|
402
407
|
found,
|
|
@@ -405,6 +410,13 @@ var Signaliz = class {
|
|
|
405
410
|
lastName: data.last_name,
|
|
406
411
|
confidence: data.confidence ?? data.confidence_score ?? (verified ? 1 : 0),
|
|
407
412
|
verificationStatus,
|
|
413
|
+
isVerified: verified,
|
|
414
|
+
isVerifiedForSending: !needsReverification && data.verified_for_sending === true,
|
|
415
|
+
isCatchAll: data.email_is_catch_all === true || data.is_catch_all === true,
|
|
416
|
+
verificationFreshness,
|
|
417
|
+
verificationAgeDays,
|
|
418
|
+
verifiedAt: typeof data.verified_at === "string" ? data.verified_at : null,
|
|
419
|
+
needsReverification,
|
|
408
420
|
providerUsed: data.provider_used ?? data.provider ?? data.email_source ?? data.source ?? "unknown",
|
|
409
421
|
error: found ? void 0 : data.error ?? "No verified email found",
|
|
410
422
|
errorCode: found ? void 0 : data.error_code ?? "EMAIL_NOT_FOUND",
|
|
@@ -525,16 +537,7 @@ var Signaliz = class {
|
|
|
525
537
|
return runBatch(companies, (company) => this.enrichCompanySignals(company), options);
|
|
526
538
|
}
|
|
527
539
|
async signalToCopy(params) {
|
|
528
|
-
const request =
|
|
529
|
-
company_domain: params.companyDomain,
|
|
530
|
-
person_name: params.personName,
|
|
531
|
-
title: params.title,
|
|
532
|
-
campaign_offer: params.campaignOffer,
|
|
533
|
-
research_prompt: params.researchPrompt,
|
|
534
|
-
signal_run_id: params.signalRunId,
|
|
535
|
-
skip_cache: params.skipCache,
|
|
536
|
-
enable_deep_search: params.enableDeepSearch
|
|
537
|
-
});
|
|
540
|
+
const request = signalToCopyRequestBody(params);
|
|
538
541
|
let data = await this.client.post("api/v1/signal-to-copy", request);
|
|
539
542
|
if (params.waitForResult !== false && data.run_id && isPendingSignalResponse(data)) {
|
|
540
543
|
const maxWaitMs = Math.max(1e3, params.maxWaitMs ?? 20 * 6e4);
|
|
@@ -551,30 +554,113 @@ var Signaliz = class {
|
|
|
551
554
|
throw new Error(`Signal Copy ${data.run_id} did not complete within ${maxWaitMs}ms`);
|
|
552
555
|
}
|
|
553
556
|
}
|
|
557
|
+
return normalizeSignalToCopyResult(data);
|
|
558
|
+
}
|
|
559
|
+
async createSignalCopyBatch(requests, options) {
|
|
560
|
+
if (!Array.isArray(requests) || requests.length === 0) {
|
|
561
|
+
throw new Error("Signaliz batch requests require at least one item");
|
|
562
|
+
}
|
|
563
|
+
if (requests.length > 5e3) {
|
|
564
|
+
throw new Error(`Signaliz batch requests support at most 5,000 items; received ${requests.length}`);
|
|
565
|
+
}
|
|
566
|
+
const startedAt = Date.now();
|
|
567
|
+
const chunks = [];
|
|
568
|
+
for (let offset = 0; offset < requests.length; offset += 25) {
|
|
569
|
+
chunks.push({ offset, requests: requests.slice(offset, offset + 25) });
|
|
570
|
+
}
|
|
571
|
+
const serverConcurrency = Math.min(10, options?.concurrency ?? 10);
|
|
572
|
+
const chunkBatch = await runBatch(
|
|
573
|
+
chunks,
|
|
574
|
+
(chunk) => this.runSignalCopyBatchChunk(chunk.requests, serverConcurrency),
|
|
575
|
+
options
|
|
576
|
+
);
|
|
577
|
+
const results = new Array(requests.length);
|
|
578
|
+
for (const chunkResult of chunkBatch.results) {
|
|
579
|
+
const chunk = chunks[chunkResult.index];
|
|
580
|
+
if (!chunkResult.success || !chunkResult.data) {
|
|
581
|
+
for (let index = 0; index < chunk.requests.length; index += 1) {
|
|
582
|
+
results[chunk.offset + index] = {
|
|
583
|
+
index: chunk.offset + index,
|
|
584
|
+
success: false,
|
|
585
|
+
error: chunkResult.error || "Signal to Copy batch request failed"
|
|
586
|
+
};
|
|
587
|
+
}
|
|
588
|
+
continue;
|
|
589
|
+
}
|
|
590
|
+
for (const item of chunkResult.data) {
|
|
591
|
+
results[chunk.offset + item.index] = { ...item, index: chunk.offset + item.index };
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
const succeeded = results.filter((item) => item.success).length;
|
|
554
595
|
return {
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
subjectLine: data.subject_line ?? "",
|
|
561
|
-
openingLine: data.opening_line ?? "",
|
|
562
|
-
confidence: data.confidence ?? 0,
|
|
563
|
-
evidenceUrl: data.evidence_url ?? "",
|
|
564
|
-
researchPrompt: data.research_prompt,
|
|
565
|
-
variations: (data.variations ?? []).map((variation) => ({
|
|
566
|
-
style: variation.style,
|
|
567
|
-
subjectLine: variation.subject_line ?? "",
|
|
568
|
-
openingLine: variation.opening_line ?? "",
|
|
569
|
-
cta: variation.cta ?? "",
|
|
570
|
-
prediction: variation.prediction,
|
|
571
|
-
email: variation.email
|
|
572
|
-
})),
|
|
573
|
-
raw: data
|
|
596
|
+
total: requests.length,
|
|
597
|
+
succeeded,
|
|
598
|
+
failed: requests.length - succeeded,
|
|
599
|
+
durationMs: Date.now() - startedAt,
|
|
600
|
+
results
|
|
574
601
|
};
|
|
575
602
|
}
|
|
576
|
-
async
|
|
577
|
-
|
|
603
|
+
async runSignalCopyBatchChunk(requests, concurrency) {
|
|
604
|
+
const startedAt = Date.now();
|
|
605
|
+
const results = new Array(requests.length);
|
|
606
|
+
let pending = requests.map((params, index) => ({
|
|
607
|
+
index,
|
|
608
|
+
params,
|
|
609
|
+
request: signalToCopyRequestBody(params)
|
|
610
|
+
}));
|
|
611
|
+
while (pending.length > 0) {
|
|
612
|
+
const data = await this.client.post("api/v1/signal-to-copy", {
|
|
613
|
+
requests: pending.map((item) => item.request),
|
|
614
|
+
concurrency
|
|
615
|
+
});
|
|
616
|
+
if (!Array.isArray(data.results) || data.results.length !== pending.length) {
|
|
617
|
+
throw new Error("Signal to Copy batch returned an invalid result count");
|
|
618
|
+
}
|
|
619
|
+
const nextPending = [];
|
|
620
|
+
let nextDelayMs = 6e4;
|
|
621
|
+
for (let index = 0; index < pending.length; index += 1) {
|
|
622
|
+
const task = pending[index];
|
|
623
|
+
const item = data.results[index];
|
|
624
|
+
if (item.success === false) {
|
|
625
|
+
results[task.index] = { index: task.index, success: false, error: item.error || item.message || "Signal to Copy failed" };
|
|
626
|
+
continue;
|
|
627
|
+
}
|
|
628
|
+
if (!isPendingSignalResponse(item)) {
|
|
629
|
+
results[task.index] = { index: task.index, success: true, data: normalizeSignalToCopyResult(item) };
|
|
630
|
+
continue;
|
|
631
|
+
}
|
|
632
|
+
if (task.params.waitForResult === false) {
|
|
633
|
+
results[task.index] = { index: task.index, success: true, data: normalizeSignalToCopyResult(item) };
|
|
634
|
+
continue;
|
|
635
|
+
}
|
|
636
|
+
const maxWaitMs = Math.max(1e3, task.params.maxWaitMs ?? 20 * 6e4);
|
|
637
|
+
if (Date.now() - startedAt >= maxWaitMs) {
|
|
638
|
+
results[task.index] = {
|
|
639
|
+
index: task.index,
|
|
640
|
+
success: false,
|
|
641
|
+
error: `Signal Copy ${item.signal_run_id || item.run_id || ""} did not complete within ${maxWaitMs}ms`
|
|
642
|
+
};
|
|
643
|
+
continue;
|
|
644
|
+
}
|
|
645
|
+
const signalRunId = item.signal_run_id || item.run_id;
|
|
646
|
+
if (!signalRunId) {
|
|
647
|
+
results[task.index] = { index: task.index, success: false, error: "Signal Copy is processing without a signal_run_id" };
|
|
648
|
+
continue;
|
|
649
|
+
}
|
|
650
|
+
nextDelayMs = Math.min(
|
|
651
|
+
nextDelayMs,
|
|
652
|
+
signalPollDelayMs(item, task.params.pollIntervalMs),
|
|
653
|
+
maxWaitMs - (Date.now() - startedAt)
|
|
654
|
+
);
|
|
655
|
+
nextPending.push({
|
|
656
|
+
...task,
|
|
657
|
+
request: { ...task.request, signal_run_id: signalRunId }
|
|
658
|
+
});
|
|
659
|
+
}
|
|
660
|
+
pending = nextPending;
|
|
661
|
+
if (pending.length > 0) await sleep2(nextDelayMs);
|
|
662
|
+
}
|
|
663
|
+
return results;
|
|
578
664
|
}
|
|
579
665
|
async listTools() {
|
|
580
666
|
const result = await this.client.mcp("tools/list");
|
|
@@ -591,6 +677,41 @@ var Signaliz = class {
|
|
|
591
677
|
function compact(input) {
|
|
592
678
|
return Object.fromEntries(Object.entries(input).filter(([, value]) => value !== void 0));
|
|
593
679
|
}
|
|
680
|
+
function signalToCopyRequestBody(params) {
|
|
681
|
+
return compact({
|
|
682
|
+
company_domain: params.companyDomain,
|
|
683
|
+
person_name: params.personName,
|
|
684
|
+
title: params.title,
|
|
685
|
+
campaign_offer: params.campaignOffer,
|
|
686
|
+
research_prompt: params.researchPrompt,
|
|
687
|
+
signal_run_id: params.signalRunId,
|
|
688
|
+
skip_cache: params.skipCache,
|
|
689
|
+
enable_deep_search: params.enableDeepSearch
|
|
690
|
+
});
|
|
691
|
+
}
|
|
692
|
+
function normalizeSignalToCopyResult(data) {
|
|
693
|
+
return {
|
|
694
|
+
success: data.success ?? true,
|
|
695
|
+
status: data.status,
|
|
696
|
+
runId: data.signal_run_id ?? data.run_id,
|
|
697
|
+
strongestSignal: data.strongest_signal ?? "",
|
|
698
|
+
whyNow: data.why_now ?? "",
|
|
699
|
+
subjectLine: data.subject_line ?? "",
|
|
700
|
+
openingLine: data.opening_line ?? "",
|
|
701
|
+
confidence: data.confidence ?? 0,
|
|
702
|
+
evidenceUrl: data.evidence_url ?? "",
|
|
703
|
+
researchPrompt: data.research_prompt,
|
|
704
|
+
variations: (data.variations ?? []).map((variation) => ({
|
|
705
|
+
style: variation.style,
|
|
706
|
+
subjectLine: variation.subject_line ?? "",
|
|
707
|
+
openingLine: variation.opening_line ?? "",
|
|
708
|
+
cta: variation.cta ?? "",
|
|
709
|
+
prediction: variation.prediction,
|
|
710
|
+
email: variation.email
|
|
711
|
+
})),
|
|
712
|
+
raw: data
|
|
713
|
+
};
|
|
714
|
+
}
|
|
594
715
|
function isPendingSignalResponse(data) {
|
|
595
716
|
return ["triggered", "queued", "processing", "pending", "pending_version", "waiting_for_deploy"].includes(String(data.status || "").toLowerCase());
|
|
596
717
|
}
|
package/dist/mcp-config.mjs
CHANGED
package/package.json
CHANGED