@signaliz/cli 1.0.56 → 1.0.58
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 +40 -13
- package/dist/bin.js +510 -55
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -40,6 +40,9 @@ signaliz signal-to-copy --company-domain example.com --person-name "Jane Doe" \
|
|
|
40
40
|
# Signals Everything returns distinct companies with a source-backed signal.
|
|
41
41
|
# It is asynchronous by default; add --wait or resume the returned run ID.
|
|
42
42
|
signaliz signals "companies that just got funded" --limit 100 --sources web,news
|
|
43
|
+
signaliz signals "companies showing signals they need account-signal automation" \
|
|
44
|
+
--icp-context "B2B SaaS with outbound teams and manual account research" \
|
|
45
|
+
--limit 100 --sources web,news
|
|
43
46
|
signaliz signals --signal-search-run-id run_... --wait --json
|
|
44
47
|
```
|
|
45
48
|
|
|
@@ -52,8 +55,20 @@ signaliz find-email --input contacts.jsonl --concurrency 20 --json
|
|
|
52
55
|
signaliz verify-email --input emails.json --concurrency 20 --json
|
|
53
56
|
signaliz company-signals --input companies.jsonl --concurrency 5 --json
|
|
54
57
|
signaliz signal-to-copy --input copy-requests.jsonl --concurrency 5 --json
|
|
58
|
+
|
|
59
|
+
# Yield an agent turn immediately for a durable >25-row batch.
|
|
60
|
+
# The response includes both job_id and the actual idempotency_key.
|
|
61
|
+
signaliz company-signals --input companies.jsonl --no-wait --json
|
|
62
|
+
signaliz company-signals --job-id job_... --idempotency-key key_... --json
|
|
63
|
+
signaliz signal-to-copy --input copy-requests.jsonl --no-wait --json
|
|
64
|
+
signaliz signal-to-copy --job-id job_... --idempotency-key key_... --json
|
|
55
65
|
```
|
|
56
66
|
|
|
67
|
+
Retrieve completed Company/Copy result pages within seven days. If retention
|
|
68
|
+
expires, the CLI returns non-retryable `BATCH_RESULTS_EXPIRED`; do not
|
|
69
|
+
automatically rerun the submission because provider work may already have been
|
|
70
|
+
performed.
|
|
71
|
+
|
|
57
72
|
The CLI compacts exact duplicate successes by default: the first row contains
|
|
58
73
|
the full result and repeats contain `duplicateOf` pointing to its zero-based
|
|
59
74
|
input index. Use `--expand-duplicates` only when every repeated row must contain
|
|
@@ -94,26 +109,38 @@ they reduce the ceiling.
|
|
|
94
109
|
|
|
95
110
|
Batch input is capped at 5,000 records, concurrency is bounded to `1-50`, input
|
|
96
111
|
order is preserved, and a failed item does not abort successful items.
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
112
|
+
Synchronous exact duplicates are single-flighted. Durable Company/Copy
|
|
113
|
+
submissions preserve every row and index so idempotency stays stable across
|
|
114
|
+
REST, MCP, SDK, and CLI recovery. The four row-oriented core products use one
|
|
115
|
+
recoverable REST job for inputs larger than 25; the CLI waits by default or
|
|
116
|
+
returns `job_id`, `idempotency_key`, and an exact resume command with
|
|
117
|
+
`--no-wait`. Find Email, Verify Email, and Company Signal Enrichment use 25-row
|
|
118
|
+
pages; Signal to Copy uses 100-row pages. Durable Signal to Copy jobs use
|
|
119
|
+
persisted, eligible company-signal data only and never start live providers or
|
|
120
|
+
deep research. Large Copy inputs with fresh, deep, skip-cache, per-row
|
|
121
|
+
idempotency, or synchronous-only controls fail locally before HTTP; split those
|
|
122
|
+
requests into batches of at most 25. Company Signal `signal_run_id` recovery
|
|
123
|
+
rows likewise stay in direct batches of at most 25. Large Company Signal jobs
|
|
124
|
+
persist bounded result pages and exclude rejected candidate-evidence
|
|
125
|
+
diagnostics; use `--include-candidate-evidence` only with batches of at most 25.
|
|
126
|
+
|
|
127
|
+
Every command uses the authenticated workspace. Find Email and Verify Email
|
|
128
|
+
honor that workspace's configured cache window; eligible hits cost 0 credits,
|
|
129
|
+
while fresh discovery or verification is charged on every plan. Builder, Team,
|
|
130
|
+
Agency, and Pay-As-You-Go include fresh Company Signals and Signal to Copy. Those plans
|
|
131
|
+
still check eligible workspace signal data first even when Company Signals is
|
|
132
|
+
set to No cache. `--lookback-days` remains a hard evidence-age bound for both
|
|
133
|
+
cached and fresh Company Signals and Signal to Copy results.
|
|
108
134
|
|
|
109
135
|
Signals Everything is a query-first company discovery command rather than a
|
|
110
136
|
row batch. It returns only dated, source-linked signals that contain both a
|
|
111
137
|
company name and company domain. The default request is 100 companies and the
|
|
112
|
-
maximum is 1,000. It is included on Team and Agency plans, is resumable with
|
|
138
|
+
maximum is 1,000. It is included on Builder, Team, and Agency plans, is resumable with
|
|
113
139
|
`signal_search_run_id`, and reports a strict source-cost guardrail: results are
|
|
114
140
|
only returned when source cost is below $0.01 per qualified company. A request
|
|
115
141
|
is a ceiling, not a promise: it can return fewer results when public evidence
|
|
116
|
-
cannot verify both a date and company identity.
|
|
142
|
+
cannot verify both a date and company identity. Use `--icp-context` to qualify
|
|
143
|
+
an open-ended signal request against a target market.
|
|
117
144
|
|
|
118
145
|
Connection commands are limited to access infrastructure:
|
|
119
146
|
|
package/dist/bin.js
CHANGED
|
@@ -13,14 +13,21 @@ var HELP = `Signaliz CLI
|
|
|
13
13
|
|
|
14
14
|
Five product commands:
|
|
15
15
|
signaliz find-email --company-domain example.com --full-name "Jane Doe" [--skip-cache]
|
|
16
|
+
signaliz find-email --run-id run_... Resume an existing Find Email run without duplicate spend
|
|
17
|
+
signaliz find-email --job-id job_... Resume an existing Find Email batch without redispatch
|
|
16
18
|
signaliz verify-email [jane@example.com] [--verification-run-id run_...] [--no-wait] [--max-wait-ms 600000]
|
|
19
|
+
signaliz verify-email --job-id job_... Resume an existing Verify Email batch without redispatch
|
|
17
20
|
signaliz company-signals --domain example.com [--research-prompt "..."] [--search-mode fast|balanced|coverage] [--include-candidate-evidence] [--no-summary] [--max-wait-ms 120000]
|
|
21
|
+
signaliz company-signals --input companies.json --no-wait Submit a durable >25-row batch and return its job
|
|
22
|
+
signaliz company-signals --job-id job_... Resume an existing Company Signals batch without redispatch
|
|
18
23
|
signaliz company-signals --signal-run-id run_... Read a completed run without duplicate spend
|
|
19
24
|
signaliz signal-to-copy --company-domain example.com --person-name "Jane Doe" \\
|
|
20
25
|
--title "VP Sales" --campaign-offer "pipeline intelligence" [--signal-run-id run_...] \\
|
|
21
|
-
[--skip-cache] [--deep-search] [--max-wait-ms 120000]
|
|
26
|
+
[--lookback-days 90] [--skip-cache] [--deep-search] [--max-wait-ms 120000]
|
|
22
27
|
signaliz signal-to-copy --signal-run-id copy_... Reuse a completed signal run
|
|
23
|
-
signaliz
|
|
28
|
+
signaliz signal-to-copy --input recipients.json --no-wait Submit an available-data-only >25-row batch
|
|
29
|
+
signaliz signal-to-copy --job-id job_... Resume an existing Signal to Copy batch without redispatch
|
|
30
|
+
signaliz signals "companies that just got funded" [--icp-context "..."] [--limit 100] [--sources web,news] [--wait]
|
|
24
31
|
signaliz signals --signal-search-run-id run_... Resume an existing company-attributed signal search
|
|
25
32
|
|
|
26
33
|
Access and connection:
|
|
@@ -42,9 +49,12 @@ Common flags:
|
|
|
42
49
|
--input - Read a JSON array or JSONL batch from stdin
|
|
43
50
|
--concurrency <1-50> Simultaneous API requests for batch input (default: 10)
|
|
44
51
|
--idempotency-key <key> Reuse after an ambiguous response to recover the same request or job
|
|
52
|
+
--run-id <run_...> Resume one existing Find Email run without dispatching new work
|
|
53
|
+
--job-id <job_...> Resume an existing core-product batch without dispatching new work
|
|
54
|
+
--icp-context <text> Optional ICP, product, or pain context that returned signals must support
|
|
45
55
|
--dry-run Return a no-spend plan without provider calls, jobs, or writes
|
|
46
56
|
|
|
47
|
-
Company Signals and Signal to Copy
|
|
57
|
+
Company Signals and Signal to Copy wait by default. For a durable batch larger than 25 rows, use --no-wait to return job_id + idempotency_key immediately, then resume with --job-id. Large Company Signal batches exclude candidate-evidence diagnostics; split --include-candidate-evidence requests into batches of at most 25. Large Signal to Copy batches are available-data-only. Signals Everything is asynchronous by default; use --wait to poll until it completes.
|
|
48
58
|
`;
|
|
49
59
|
function loadConfig() {
|
|
50
60
|
if (!(0, import_node_fs.existsSync)(CONFIG_FILE)) return {};
|
|
@@ -124,37 +134,203 @@ function readBatchInput() {
|
|
|
124
134
|
function record(value) {
|
|
125
135
|
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
126
136
|
}
|
|
127
|
-
function
|
|
137
|
+
function safeNonNegativeNumber(value) {
|
|
138
|
+
if (value === null || value === void 0 || value === "") return null;
|
|
139
|
+
const numeric = Number(value);
|
|
140
|
+
return Number.isFinite(numeric) && numeric >= 0 ? numeric : null;
|
|
141
|
+
}
|
|
142
|
+
function customerEmailBillingMetadata(value) {
|
|
143
|
+
const source = record(value);
|
|
144
|
+
const safe = {};
|
|
145
|
+
if (typeof source.result_returned_from === "string") safe.result_returned_from = source.result_returned_from;
|
|
146
|
+
for (const field of ["cache_hit", "fresh_enrichment_used", "live_provider_called", "openrouter_called", "byo_openrouter_used"]) {
|
|
147
|
+
if (typeof source[field] === "boolean") safe[field] = source[field];
|
|
148
|
+
}
|
|
149
|
+
for (const field of ["credits_used", "credits_charged", "estimated_external_cost_usd"]) {
|
|
150
|
+
const numeric = safeNonNegativeNumber(source[field]);
|
|
151
|
+
if (numeric !== null) safe[field] = numeric;
|
|
152
|
+
}
|
|
153
|
+
return safe;
|
|
154
|
+
}
|
|
155
|
+
function customerEmailResult(value, kind) {
|
|
128
156
|
const result = record(value);
|
|
129
|
-
if (String(result.status || "").toLowerCase() === "processing") return result;
|
|
130
157
|
const raw = record(result.raw);
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
158
|
+
if (String(result.status || "").toLowerCase() === "processing") {
|
|
159
|
+
const verificationRunId2 = result.verificationRunId ?? raw.verification_run_id;
|
|
160
|
+
const runId2 = result.runId ?? raw.run_id ?? verificationRunId2;
|
|
161
|
+
return {
|
|
162
|
+
success: true,
|
|
163
|
+
status: "processing",
|
|
164
|
+
email: null,
|
|
165
|
+
run_id: runId2,
|
|
166
|
+
...verificationRunId2 ? { verification_run_id: verificationRunId2 } : {},
|
|
167
|
+
next_poll_after_seconds: result.nextPollAfterSeconds ?? raw.next_poll_after_seconds,
|
|
168
|
+
suggested_action: result.suggestedAction ?? raw.suggested_action ?? (verificationRunId2 ? "retry_with_verification_run_id" : "retry_with_run_id")
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
const creditsUsed = Number(result.creditsUsed ?? raw.credits_used ?? 0);
|
|
172
|
+
const base = {
|
|
137
173
|
email: typeof result.email === "string" && result.email.trim() ? result.email.trim() : null,
|
|
138
174
|
status: "completed",
|
|
139
175
|
success: result.success === true,
|
|
140
|
-
is_valid:
|
|
141
|
-
credits_used: Number(
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
176
|
+
is_valid: typeof result.isValid === "boolean" ? result.isValid : typeof raw.is_valid === "boolean" ? raw.is_valid : result.isVerified ?? false,
|
|
177
|
+
credits_used: Number.isFinite(creditsUsed) && creditsUsed >= 0 ? creditsUsed : 0
|
|
178
|
+
};
|
|
179
|
+
if (kind === "find") {
|
|
180
|
+
const verifiedForSending2 = typeof result.isVerifiedForSending === "boolean" ? result.isVerifiedForSending : raw.verified_for_sending === true;
|
|
181
|
+
const isCatchAll2 = typeof result.isCatchAll === "boolean" ? result.isCatchAll : raw.email_is_catch_all === true || raw.is_catch_all === true;
|
|
182
|
+
const verificationStatus2 = String(
|
|
183
|
+
result.verificationStatus ?? raw.verification_status ?? raw.deliverability_status ?? (verifiedForSending2 ? "valid" : "unknown")
|
|
184
|
+
).toLowerCase();
|
|
185
|
+
const deliverabilityStatus2 = String(
|
|
186
|
+
result.deliverabilityStatus ?? raw.deliverability_status ?? verificationStatus2
|
|
187
|
+
).toLowerCase();
|
|
188
|
+
const verificationVerdict2 = String(
|
|
189
|
+
result.verificationVerdict ?? raw.verification_verdict ?? verificationStatus2
|
|
190
|
+
).toLowerCase();
|
|
191
|
+
const billingMetadata2 = customerEmailBillingMetadata(
|
|
192
|
+
Object.keys(record(result.billingMetadata)).length > 0 ? result.billingMetadata : raw.billing_metadata
|
|
193
|
+
);
|
|
194
|
+
const historicalBillingMetadata2 = customerEmailBillingMetadata(
|
|
195
|
+
Object.keys(record(result.historicalBillingMetadata)).length > 0 ? result.historicalBillingMetadata : raw.historical_billing_metadata
|
|
196
|
+
);
|
|
197
|
+
const cacheHit2 = typeof raw.cache_hit === "boolean" ? raw.cache_hit : result.cacheHit === true;
|
|
198
|
+
const negativeCacheHit2 = typeof raw.negative_cache_hit === "boolean" ? raw.negative_cache_hit : result.negativeCacheHit === true;
|
|
199
|
+
const resultReturnedFrom = result.resultReturnedFrom ?? raw.result_returned_from ?? null;
|
|
200
|
+
const freshEnrichmentUsed = typeof raw.fresh_enrichment_used === "boolean" ? raw.fresh_enrichment_used : result.freshEnrichmentUsed === true;
|
|
201
|
+
const liveProviderCalled = typeof raw.live_provider_called === "boolean" ? raw.live_provider_called : result.liveProviderCalled === true;
|
|
202
|
+
const normalizedBillingMetadata = {
|
|
203
|
+
...billingMetadata2,
|
|
204
|
+
...resultReturnedFrom ? { result_returned_from: resultReturnedFrom } : {},
|
|
205
|
+
cache_hit: cacheHit2,
|
|
206
|
+
fresh_enrichment_used: freshEnrichmentUsed,
|
|
207
|
+
credits_used: base.credits_used,
|
|
208
|
+
live_provider_called: liveProviderCalled
|
|
209
|
+
};
|
|
210
|
+
const estimatedExternalCostUsd2 = safeNonNegativeNumber(
|
|
211
|
+
result.estimatedExternalCostUsd ?? raw.estimated_external_cost_usd
|
|
212
|
+
);
|
|
213
|
+
return {
|
|
214
|
+
...base,
|
|
215
|
+
is_valid: verifiedForSending2,
|
|
216
|
+
verification_status: verificationStatus2,
|
|
217
|
+
deliverability_status: deliverabilityStatus2,
|
|
218
|
+
verification_verdict: verificationVerdict2,
|
|
219
|
+
verified_for_sending: verifiedForSending2,
|
|
220
|
+
is_deliverable: typeof result.isDeliverable === "boolean" ? result.isDeliverable && verifiedForSending2 : verifiedForSending2,
|
|
221
|
+
email_is_catch_all: isCatchAll2,
|
|
222
|
+
is_catch_all: isCatchAll2,
|
|
223
|
+
verification_source: result.verificationSource ?? raw.verification_source ?? result.providerUsed ?? "email_finder",
|
|
224
|
+
...result.verificationObservedAt ?? raw.verification_observed_at ? { verification_observed_at: result.verificationObservedAt ?? raw.verification_observed_at } : {},
|
|
225
|
+
failure_reason: result.failureReason ?? raw.failure_reason ?? (verifiedForSending2 ? "none" : "no_verified_email_found"),
|
|
226
|
+
cache_hit: cacheHit2,
|
|
227
|
+
negative_cache_hit: negativeCacheHit2,
|
|
228
|
+
negative_cache_ttl_seconds: safeNonNegativeNumber(
|
|
229
|
+
result.negativeCacheTtlSeconds ?? raw.negative_cache_ttl_seconds
|
|
230
|
+
),
|
|
231
|
+
cache_tier: result.cacheTier ?? raw.cache_tier ?? null,
|
|
232
|
+
result_returned_from: resultReturnedFrom,
|
|
233
|
+
fresh_enrichment_used: freshEnrichmentUsed,
|
|
234
|
+
live_provider_called: liveProviderCalled,
|
|
235
|
+
billing_metadata: normalizedBillingMetadata,
|
|
236
|
+
...Object.keys(historicalBillingMetadata2).length > 0 ? { historical_billing_metadata: historicalBillingMetadata2 } : {},
|
|
237
|
+
...estimatedExternalCostUsd2 !== null ? { estimated_external_cost_usd: estimatedExternalCostUsd2 } : {},
|
|
238
|
+
...result.runId ?? raw.run_id ? { run_id: result.runId ?? raw.run_id } : {},
|
|
239
|
+
...raw.cache_publication_status ? {
|
|
240
|
+
cache_publication_status: raw.cache_publication_status,
|
|
241
|
+
cache_publication_retry_eligible: raw.cache_publication_retry_eligible === true,
|
|
242
|
+
...raw.suggested_action ? { suggested_action: raw.suggested_action } : {}
|
|
243
|
+
} : {}
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
const verificationStatus = String(
|
|
247
|
+
result.verificationStatus ?? raw.verification_status ?? result.verificationVerdict ?? raw.verification_verdict ?? raw.deliverability_status ?? "unknown"
|
|
248
|
+
).toLowerCase();
|
|
249
|
+
const deliverabilityStatus = String(
|
|
250
|
+
result.deliverabilityStatus ?? raw.deliverability_status ?? verificationStatus
|
|
251
|
+
).toLowerCase();
|
|
252
|
+
const verificationVerdict = String(
|
|
253
|
+
result.verificationVerdict ?? raw.verification_verdict ?? verificationStatus
|
|
254
|
+
).toLowerCase();
|
|
255
|
+
const verifiedForSending = typeof result.verifiedForSending === "boolean" ? result.verifiedForSending : raw.verified_for_sending === true;
|
|
256
|
+
const isDeliverable = typeof result.isDeliverable === "boolean" ? result.isDeliverable : raw.is_deliverable === true;
|
|
257
|
+
const isCatchAll = typeof result.isCatchAll === "boolean" ? result.isCatchAll : raw.email_is_catch_all === true || raw.is_catch_all === true;
|
|
258
|
+
const isRoleAccount = typeof result.isRoleAccount === "boolean" ? result.isRoleAccount : raw.is_role_account === true;
|
|
259
|
+
const isMalformed = typeof result.isMalformed === "boolean" ? result.isMalformed : raw.is_malformed === true;
|
|
260
|
+
const quality = typeof result.quality === "string" ? result.quality : typeof raw.quality === "string" ? raw.quality : null;
|
|
261
|
+
const recommendation = typeof result.recommendation === "string" ? result.recommendation : typeof raw.recommendation === "string" ? raw.recommendation : verifiedForSending ? "send" : ["unknown", "verifier_error"].includes(verificationStatus) ? "manual_review" : "do_not_send";
|
|
262
|
+
const cacheHit = typeof raw.cache_hit === "boolean" ? raw.cache_hit : result.cacheHit === true;
|
|
263
|
+
const negativeCacheHit = typeof raw.negative_cache_hit === "boolean" ? raw.negative_cache_hit : result.negativeCacheHit === true;
|
|
264
|
+
const negativeCacheTtlSeconds = safeNonNegativeNumber(
|
|
265
|
+
result.negativeCacheTtlSeconds ?? raw.negative_cache_ttl_seconds
|
|
266
|
+
);
|
|
267
|
+
const billingMetadata = customerEmailBillingMetadata(
|
|
268
|
+
Object.keys(record(result.billingMetadata)).length > 0 ? result.billingMetadata : raw.billing_metadata
|
|
269
|
+
);
|
|
270
|
+
const historicalBillingMetadata = customerEmailBillingMetadata(
|
|
271
|
+
Object.keys(record(result.historicalBillingMetadata)).length > 0 ? result.historicalBillingMetadata : raw.historical_billing_metadata
|
|
272
|
+
);
|
|
273
|
+
const verificationRunId = result.verificationRunId ?? raw.verification_run_id ?? raw.run_id;
|
|
274
|
+
const runId = result.runId ?? raw.run_id ?? verificationRunId;
|
|
275
|
+
const estimatedExternalCostUsd = safeNonNegativeNumber(
|
|
276
|
+
result.estimatedExternalCostUsd ?? raw.estimated_external_cost_usd
|
|
277
|
+
);
|
|
278
|
+
return {
|
|
279
|
+
...base,
|
|
280
|
+
verification_status: verificationStatus,
|
|
281
|
+
deliverability_status: deliverabilityStatus,
|
|
282
|
+
verification_verdict: verificationVerdict,
|
|
283
|
+
verified_for_sending: verifiedForSending,
|
|
284
|
+
is_deliverable: isDeliverable,
|
|
285
|
+
email_is_catch_all: isCatchAll,
|
|
286
|
+
is_catch_all: isCatchAll,
|
|
287
|
+
is_role_account: isRoleAccount,
|
|
288
|
+
is_malformed: isMalformed,
|
|
289
|
+
quality,
|
|
290
|
+
recommendation,
|
|
291
|
+
smtp_status: result.smtpStatus ?? raw.smtp_status ?? (isCatchAll ? "accept_all" : verifiedForSending ? "accepted" : "not_checked"),
|
|
292
|
+
provider_status: result.providerStatus ?? raw.provider_status ?? verificationStatus,
|
|
293
|
+
verification_source: result.verificationSource ?? raw.verification_source ?? "email_verification",
|
|
294
|
+
...result.verificationObservedAt ?? raw.verification_observed_at ? { verification_observed_at: result.verificationObservedAt ?? raw.verification_observed_at } : {},
|
|
295
|
+
failure_reason: result.failureReason ?? raw.failure_reason ?? (verifiedForSending ? "none" : isCatchAll ? "catch_all_domain_not_send_safe" : "verification_not_send_safe"),
|
|
296
|
+
cache_hit: cacheHit,
|
|
297
|
+
negative_cache_hit: negativeCacheHit,
|
|
298
|
+
negative_cache_ttl_seconds: negativeCacheTtlSeconds,
|
|
299
|
+
cache_tier: result.cacheTier ?? raw.cache_tier ?? null,
|
|
300
|
+
result_returned_from: result.resultReturnedFrom ?? raw.result_returned_from ?? null,
|
|
301
|
+
fresh_enrichment_used: typeof raw.fresh_enrichment_used === "boolean" ? raw.fresh_enrichment_used : result.freshEnrichmentUsed === true,
|
|
302
|
+
live_provider_called: typeof raw.live_provider_called === "boolean" ? raw.live_provider_called : result.liveProviderCalled === true,
|
|
303
|
+
billing_replayed: typeof raw.billing_replayed === "boolean" ? raw.billing_replayed : result.billingReplayed === true,
|
|
304
|
+
billing_metadata: billingMetadata,
|
|
305
|
+
...Object.keys(historicalBillingMetadata).length > 0 ? { historical_billing_metadata: historicalBillingMetadata } : {},
|
|
306
|
+
...estimatedExternalCostUsd !== null ? { estimated_external_cost_usd: estimatedExternalCostUsd } : {},
|
|
307
|
+
...verificationRunId ? { verification_run_id: verificationRunId } : {},
|
|
308
|
+
...runId ? { run_id: runId } : {},
|
|
309
|
+
...raw.cache_publication_status ? {
|
|
310
|
+
cache_publication_status: raw.cache_publication_status,
|
|
311
|
+
cache_publication_retry_eligible: raw.cache_publication_retry_eligible === true,
|
|
312
|
+
...raw.suggested_action ? { suggested_action: raw.suggested_action } : {}
|
|
313
|
+
} : {}
|
|
147
314
|
};
|
|
148
315
|
}
|
|
149
|
-
function customerEmailBatch(value) {
|
|
316
|
+
function customerEmailBatch(value, kind) {
|
|
150
317
|
return {
|
|
151
318
|
...value,
|
|
152
319
|
results: value.results.map((item) => {
|
|
153
320
|
const row = record(item);
|
|
154
|
-
return row.data ? { ...row, data: customerEmailResult(row.data) } : row;
|
|
321
|
+
return promoteBatchRecoveryMetadata(row.data ? { ...row, data: customerEmailResult(row.data, kind) } : row);
|
|
155
322
|
})
|
|
156
323
|
};
|
|
157
324
|
}
|
|
325
|
+
function promoteBatchRecoveryMetadata(row) {
|
|
326
|
+
return {
|
|
327
|
+
...row,
|
|
328
|
+
...typeof row.runId === "string" ? { run_id: row.runId } : {},
|
|
329
|
+
...typeof row.verificationRunId === "string" ? { verification_run_id: row.verificationRunId } : {},
|
|
330
|
+
...typeof row.jobId === "string" ? { job_id: row.jobId } : {},
|
|
331
|
+
...typeof row.suggestedAction === "string" ? { suggested_action: row.suggestedAction } : {}
|
|
332
|
+
};
|
|
333
|
+
}
|
|
158
334
|
function customerSignalResult(value) {
|
|
159
335
|
const result = record(value);
|
|
160
336
|
const raw = record(result.raw);
|
|
@@ -176,7 +352,7 @@ function customerSignalBatch(value) {
|
|
|
176
352
|
...value,
|
|
177
353
|
results: value.results.map((item) => {
|
|
178
354
|
const row = record(item);
|
|
179
|
-
return row.data ? { ...row, data: customerSignalResult(row.data) } : row;
|
|
355
|
+
return promoteBatchRecoveryMetadata(row.data ? { ...row, data: customerSignalResult(row.data) } : row);
|
|
180
356
|
})
|
|
181
357
|
};
|
|
182
358
|
}
|
|
@@ -184,14 +360,40 @@ function batchOptions() {
|
|
|
184
360
|
return {
|
|
185
361
|
concurrency: numberFlag("concurrency"),
|
|
186
362
|
idempotencyKey: flag("idempotency-key"),
|
|
187
|
-
compactDuplicates: !hasFlag("expand-duplicates")
|
|
363
|
+
compactDuplicates: !hasFlag("expand-duplicates"),
|
|
364
|
+
maxWaitMs: numberFlag("max-wait-ms"),
|
|
365
|
+
pollIntervalMs: numberFlag("poll-interval-ms")
|
|
188
366
|
};
|
|
189
367
|
}
|
|
190
|
-
function
|
|
191
|
-
if (hasFlag("no-wait")
|
|
192
|
-
|
|
368
|
+
function rejectDurableNoWaitPollingFlags(label) {
|
|
369
|
+
if (!hasFlag("no-wait")) return;
|
|
370
|
+
const conflicts = ["max-wait-ms", "poll-interval-ms", "concurrency"].filter(hasFlag);
|
|
371
|
+
if (conflicts.length > 0) {
|
|
372
|
+
fail(`${label} --no-wait cannot be combined with ${conflicts.map((name) => `--${name}`).join(", ")}.`);
|
|
193
373
|
}
|
|
194
374
|
}
|
|
375
|
+
function recoverableJobOutput(value, command) {
|
|
376
|
+
const resumeCommand = `signaliz ${command} --job-id ${value.jobId} --idempotency-key ${value.idempotencyKey}`;
|
|
377
|
+
const payload = {
|
|
378
|
+
success: true,
|
|
379
|
+
status: value.status,
|
|
380
|
+
capability: value.capability,
|
|
381
|
+
job_id: value.jobId,
|
|
382
|
+
idempotency_key: value.idempotencyKey,
|
|
383
|
+
total: value.total,
|
|
384
|
+
next_poll_after_seconds: value.nextPollAfterSeconds,
|
|
385
|
+
suggested_action: "check_job_status",
|
|
386
|
+
resume_command: resumeCommand
|
|
387
|
+
};
|
|
388
|
+
output(payload, () => {
|
|
389
|
+
process.stdout.write(`Durable ${command} batch submitted: ${value.jobId}
|
|
390
|
+
`);
|
|
391
|
+
process.stdout.write(`Idempotency key: ${value.idempotencyKey}
|
|
392
|
+
`);
|
|
393
|
+
process.stdout.write(`Resume with: ${resumeCommand}
|
|
394
|
+
`);
|
|
395
|
+
});
|
|
396
|
+
}
|
|
195
397
|
function batchOutput(value) {
|
|
196
398
|
if (hasFlag("jsonl")) {
|
|
197
399
|
for (const result of value.results) {
|
|
@@ -256,6 +458,9 @@ function numberValue(value) {
|
|
|
256
458
|
const number = Number(value);
|
|
257
459
|
return Number.isFinite(number) ? number : void 0;
|
|
258
460
|
}
|
|
461
|
+
function nullableNumberValue(value) {
|
|
462
|
+
return value === null ? null : numberValue(value);
|
|
463
|
+
}
|
|
259
464
|
function stringValue(value) {
|
|
260
465
|
return typeof value === "string" ? value.trim() : "";
|
|
261
466
|
}
|
|
@@ -311,6 +516,7 @@ function normalizeSignalsEverythingResult(request, value) {
|
|
|
311
516
|
const status = statuses.has(statusValue) ? statusValue : signals2.length > 0 ? "completed" : "failed";
|
|
312
517
|
const guardrail = record(value.cost_guardrail);
|
|
313
518
|
const maxCost = numberValue(guardrail.max_cost_per_qualified_signal_usd);
|
|
519
|
+
const identityCostSource = stringValue(guardrail.identity_cost_source);
|
|
314
520
|
const warnings = Array.isArray(value.warnings) ? value.warnings.filter((warning) => typeof warning === "string") : void 0;
|
|
315
521
|
return {
|
|
316
522
|
success: value.success !== false,
|
|
@@ -327,7 +533,15 @@ function normalizeSignalsEverythingResult(request, value) {
|
|
|
327
533
|
...maxCost === void 0 ? {} : {
|
|
328
534
|
costGuardrail: {
|
|
329
535
|
maxCostPerQualifiedSignalUsd: maxCost,
|
|
330
|
-
costPerQualifiedSignalUsd:
|
|
536
|
+
costPerQualifiedSignalUsd: nullableNumberValue(guardrail.cost_per_qualified_signal_usd),
|
|
537
|
+
identityResolutionCostUsd: nullableNumberValue(guardrail.identity_resolution_cost_usd),
|
|
538
|
+
identityCostSource: [
|
|
539
|
+
"observed_zero_credit_receipts",
|
|
540
|
+
"contractual_zero_cost",
|
|
541
|
+
"not_used",
|
|
542
|
+
"unavailable"
|
|
543
|
+
].includes(identityCostSource) ? identityCostSource : void 0,
|
|
544
|
+
identityProviderRequests: numberValue(guardrail.identity_provider_requests)
|
|
331
545
|
}
|
|
332
546
|
},
|
|
333
547
|
...warnings?.length ? { warnings } : {},
|
|
@@ -342,6 +556,7 @@ async function discoverSignalsEverything(request) {
|
|
|
342
556
|
...request.idempotencyKey ? { idempotency_key: request.idempotencyKey } : {}
|
|
343
557
|
} : {
|
|
344
558
|
query: request.query,
|
|
559
|
+
...request.icpContext ? { icp_context: request.icpContext } : {},
|
|
345
560
|
limit: request.limit ?? 100,
|
|
346
561
|
...request.sources ? { sources: request.sources } : {},
|
|
347
562
|
...request.lookbackDays === void 0 ? {} : { lookback_days: request.lookbackDays },
|
|
@@ -465,10 +680,41 @@ async function auth(args) {
|
|
|
465
680
|
fail("Usage: signaliz auth <login|set-key|workspace|status>");
|
|
466
681
|
}
|
|
467
682
|
async function findEmail() {
|
|
683
|
+
const jobId = flag("job-id");
|
|
684
|
+
if (jobId) {
|
|
685
|
+
const conflicts = [
|
|
686
|
+
"input",
|
|
687
|
+
"dry-run",
|
|
688
|
+
"company-domain",
|
|
689
|
+
"domain",
|
|
690
|
+
"full-name",
|
|
691
|
+
"first-name",
|
|
692
|
+
"last-name",
|
|
693
|
+
"linkedin-url",
|
|
694
|
+
"company-name",
|
|
695
|
+
"run-id",
|
|
696
|
+
"skip-cache",
|
|
697
|
+
"idempotency-key"
|
|
698
|
+
].filter(hasFlag);
|
|
699
|
+
if (conflicts.length > 0) {
|
|
700
|
+
fail(`Find Email --job-id cannot be combined with ${conflicts.map((name) => `--${name}`).join(", ")}.`);
|
|
701
|
+
}
|
|
702
|
+
const result2 = await createClient().resumeFindEmailBatch(jobId, {
|
|
703
|
+
pageSize: numberFlag("page-size"),
|
|
704
|
+
maxWaitMs: numberFlag("max-wait-ms"),
|
|
705
|
+
pollIntervalMs: numberFlag("poll-interval-ms"),
|
|
706
|
+
compactDuplicates: !hasFlag("expand-duplicates")
|
|
707
|
+
});
|
|
708
|
+
batchOutput(customerEmailBatch(result2, "find"));
|
|
709
|
+
return;
|
|
710
|
+
}
|
|
468
711
|
const rows = readBatchInput();
|
|
469
712
|
if (rows) {
|
|
470
713
|
const requests = rows.map((value) => {
|
|
471
714
|
const row = record(value);
|
|
715
|
+
const runId2 = row.runId || row.run_id;
|
|
716
|
+
const idempotencyKey = row.idempotencyKey || row.idempotency_key;
|
|
717
|
+
if (runId2) return { runId: runId2, idempotencyKey };
|
|
472
718
|
return {
|
|
473
719
|
companyDomain: row.companyDomain || row.company_domain || row.domain,
|
|
474
720
|
fullName: row.fullName || row.full_name || row.name,
|
|
@@ -476,7 +722,8 @@ async function findEmail() {
|
|
|
476
722
|
lastName: row.lastName || row.last_name,
|
|
477
723
|
linkedinUrl: row.linkedinUrl || row.linkedin_url || row.linkedin,
|
|
478
724
|
companyName: row.companyName || row.company_name,
|
|
479
|
-
skipCache: row.skipCache ?? row.skip_cache ?? hasFlag("skip-cache")
|
|
725
|
+
skipCache: row.skipCache ?? row.skip_cache ?? hasFlag("skip-cache"),
|
|
726
|
+
idempotencyKey
|
|
480
727
|
};
|
|
481
728
|
});
|
|
482
729
|
if (hasFlag("dry-run")) {
|
|
@@ -485,18 +732,19 @@ async function findEmail() {
|
|
|
485
732
|
return;
|
|
486
733
|
}
|
|
487
734
|
const result2 = await createClient().findEmails(requests, batchOptions());
|
|
488
|
-
batchOutput(customerEmailBatch(result2));
|
|
735
|
+
batchOutput(customerEmailBatch(result2, "find"));
|
|
489
736
|
return;
|
|
490
737
|
}
|
|
491
738
|
const companyDomain = flag("company-domain") || flag("domain");
|
|
739
|
+
const runId = flag("run-id");
|
|
492
740
|
const fullName = flag("full-name");
|
|
493
741
|
const firstName = flag("first-name");
|
|
494
742
|
const lastName = flag("last-name");
|
|
495
743
|
const linkedinUrl = flag("linkedin-url");
|
|
496
|
-
if (!companyDomain || !fullName && !(firstName && lastName)) {
|
|
497
|
-
fail('Usage: signaliz find-email --company-domain example.com --full-name "Jane Doe"');
|
|
744
|
+
if (!runId && (!companyDomain || !fullName && !(firstName && lastName))) {
|
|
745
|
+
fail('Usage: signaliz find-email --company-domain example.com --full-name "Jane Doe", or signaliz find-email --run-id run_...');
|
|
498
746
|
}
|
|
499
|
-
const request = {
|
|
747
|
+
const request = runId ? { runId } : {
|
|
500
748
|
companyDomain,
|
|
501
749
|
fullName,
|
|
502
750
|
firstName,
|
|
@@ -512,30 +760,68 @@ async function findEmail() {
|
|
|
512
760
|
return;
|
|
513
761
|
}
|
|
514
762
|
const result = await createClient().findEmail(request);
|
|
515
|
-
const publicResult = customerEmailResult(result);
|
|
763
|
+
const publicResult = customerEmailResult(result, "find");
|
|
516
764
|
if (outputFailedCoreProduct(publicResult, "No verified email found")) return;
|
|
517
765
|
output(publicResult, () => {
|
|
518
|
-
|
|
766
|
+
if (result.status === "processing" && result.runId) {
|
|
767
|
+
process.stdout.write(`Find Email processing: ${result.runId}
|
|
519
768
|
`);
|
|
520
|
-
|
|
769
|
+
process.stdout.write(`Retry after ${result.nextPollAfterSeconds || 3} seconds with --run-id ${result.runId}
|
|
521
770
|
`);
|
|
522
|
-
|
|
771
|
+
return;
|
|
772
|
+
}
|
|
773
|
+
process.stdout.write(`Email: ${publicResult.email ?? "Not found"}
|
|
523
774
|
`);
|
|
524
|
-
process.stdout.write(`
|
|
775
|
+
process.stdout.write(`Status: ${publicResult.status}
|
|
525
776
|
`);
|
|
526
|
-
process.stdout.write(`
|
|
777
|
+
process.stdout.write(`Success: ${publicResult.success}
|
|
527
778
|
`);
|
|
528
|
-
process.stdout.write(`
|
|
779
|
+
process.stdout.write(`Valid: ${publicResult.is_valid}
|
|
780
|
+
`);
|
|
781
|
+
process.stdout.write(`Credits used: ${publicResult.credits_used}
|
|
529
782
|
`);
|
|
530
|
-
if (result.needsReverification) {
|
|
531
|
-
process.stdout.write("Send safety: stale verification; rerun with --skip-cache before sending\n");
|
|
532
|
-
}
|
|
533
783
|
});
|
|
534
784
|
}
|
|
535
785
|
async function verifyEmail(args) {
|
|
786
|
+
const jobId = flag("job-id");
|
|
787
|
+
if (jobId) {
|
|
788
|
+
const positionalEmail = args[0] && !args[0].startsWith("--");
|
|
789
|
+
const conflicts = [
|
|
790
|
+
"input",
|
|
791
|
+
"dry-run",
|
|
792
|
+
"email",
|
|
793
|
+
"verification-run-id",
|
|
794
|
+
"skip-cache",
|
|
795
|
+
"idempotency-key",
|
|
796
|
+
"no-wait"
|
|
797
|
+
].filter(hasFlag);
|
|
798
|
+
if (positionalEmail || conflicts.length > 0) {
|
|
799
|
+
const conflictList = [
|
|
800
|
+
...positionalEmail ? ["email argument"] : [],
|
|
801
|
+
...conflicts.map((name) => `--${name}`)
|
|
802
|
+
];
|
|
803
|
+
fail(`Verify Email --job-id cannot be combined with ${conflictList.join(", ")}.`);
|
|
804
|
+
}
|
|
805
|
+
const result2 = await createClient().resumeVerifyEmailBatch(jobId, {
|
|
806
|
+
pageSize: numberFlag("page-size"),
|
|
807
|
+
maxWaitMs: numberFlag("max-wait-ms"),
|
|
808
|
+
pollIntervalMs: numberFlag("poll-interval-ms"),
|
|
809
|
+
compactDuplicates: !hasFlag("expand-duplicates")
|
|
810
|
+
});
|
|
811
|
+
batchOutput(customerEmailBatch(result2, "verify"));
|
|
812
|
+
return;
|
|
813
|
+
}
|
|
536
814
|
const rows = readBatchInput();
|
|
537
815
|
if (rows) {
|
|
538
|
-
const emails = rows.map((value) =>
|
|
816
|
+
const emails = rows.map((value) => {
|
|
817
|
+
if (typeof value === "string") return value;
|
|
818
|
+
const row = record(value);
|
|
819
|
+
return {
|
|
820
|
+
email: String(row.email || ""),
|
|
821
|
+
skipCache: row.skipCache ?? row.skip_cache ?? hasFlag("skip-cache"),
|
|
822
|
+
idempotencyKey: row.idempotencyKey || row.idempotency_key
|
|
823
|
+
};
|
|
824
|
+
});
|
|
539
825
|
if (hasFlag("dry-run")) {
|
|
540
826
|
const result3 = await createClient().verifyEmails(emails, {
|
|
541
827
|
...batchOptions(),
|
|
@@ -549,7 +835,7 @@ async function verifyEmail(args) {
|
|
|
549
835
|
...batchOptions(),
|
|
550
836
|
skipCache: hasFlag("skip-cache")
|
|
551
837
|
});
|
|
552
|
-
batchOutput(customerEmailBatch(result2));
|
|
838
|
+
batchOutput(customerEmailBatch(result2, "verify"));
|
|
553
839
|
return;
|
|
554
840
|
}
|
|
555
841
|
const email = args[0] && !args[0].startsWith("--") ? args[0] : flag("email");
|
|
@@ -569,7 +855,7 @@ async function verifyEmail(args) {
|
|
|
569
855
|
return;
|
|
570
856
|
}
|
|
571
857
|
const result = await createClient().verifyEmail(email, options);
|
|
572
|
-
const publicResult = customerEmailResult(result);
|
|
858
|
+
const publicResult = customerEmailResult(result, "verify");
|
|
573
859
|
if (outputFailedCoreProduct(publicResult, "Email verification failed")) return;
|
|
574
860
|
output(publicResult, () => {
|
|
575
861
|
if (result.status === "processing") {
|
|
@@ -579,22 +865,69 @@ async function verifyEmail(args) {
|
|
|
579
865
|
`);
|
|
580
866
|
return;
|
|
581
867
|
}
|
|
582
|
-
process.stdout.write(
|
|
868
|
+
process.stdout.write(`Email: ${publicResult.email ?? "Not found"}
|
|
869
|
+
`);
|
|
870
|
+
process.stdout.write(`Status: ${publicResult.status}
|
|
871
|
+
`);
|
|
872
|
+
process.stdout.write(`Success: ${publicResult.success}
|
|
873
|
+
`);
|
|
874
|
+
process.stdout.write(`Valid: ${publicResult.is_valid}
|
|
583
875
|
`);
|
|
584
|
-
process.stdout.write(`
|
|
585
|
-
Deliverable: ${result.isDeliverable}
|
|
586
|
-
Catch-all: ${result.isCatchAll}
|
|
587
|
-
Verdict: ${result.verificationVerdict}
|
|
876
|
+
process.stdout.write(`Verdict: ${publicResult.verification_status}
|
|
588
877
|
`);
|
|
589
|
-
|
|
590
|
-
|
|
878
|
+
process.stdout.write(`Recommendation: ${publicResult.recommendation}
|
|
879
|
+
`);
|
|
880
|
+
process.stdout.write(`Source: ${publicResult.verification_source}${publicResult.cache_hit === true ? " (cache)" : ""}
|
|
881
|
+
`);
|
|
882
|
+
if (publicResult.run_id) process.stdout.write(`Run ID: ${publicResult.run_id}
|
|
883
|
+
`);
|
|
884
|
+
process.stdout.write(`Credits used: ${publicResult.credits_used}
|
|
591
885
|
`);
|
|
592
886
|
});
|
|
593
887
|
}
|
|
594
888
|
async function companySignals() {
|
|
595
|
-
|
|
889
|
+
const jobId = flag("job-id");
|
|
890
|
+
if (jobId) {
|
|
891
|
+
const conflicts = [
|
|
892
|
+
"input",
|
|
893
|
+
"dry-run",
|
|
894
|
+
"domain",
|
|
895
|
+
"company-domain",
|
|
896
|
+
"company-name",
|
|
897
|
+
"signal-run-id",
|
|
898
|
+
"research-prompt",
|
|
899
|
+
"prompt",
|
|
900
|
+
"signal-types",
|
|
901
|
+
"target-signal-count",
|
|
902
|
+
"lookback-days",
|
|
903
|
+
"offline",
|
|
904
|
+
"shallow",
|
|
905
|
+
"search-mode",
|
|
906
|
+
"no-summary",
|
|
907
|
+
"outreach-intelligence",
|
|
908
|
+
"predictive-intelligence",
|
|
909
|
+
"skip-cache",
|
|
910
|
+
"include-candidate-evidence",
|
|
911
|
+
"no-wait",
|
|
912
|
+
"concurrency"
|
|
913
|
+
].filter(hasFlag);
|
|
914
|
+
if (conflicts.length > 0) {
|
|
915
|
+
fail(`Company Signals --job-id cannot be combined with ${conflicts.map((name) => `--${name}`).join(", ")}.`);
|
|
916
|
+
}
|
|
917
|
+
const result2 = await createClient().resumeCompanySignalBatch(jobId, {
|
|
918
|
+
pageSize: numberFlag("page-size"),
|
|
919
|
+
maxWaitMs: numberFlag("max-wait-ms"),
|
|
920
|
+
pollIntervalMs: numberFlag("poll-interval-ms"),
|
|
921
|
+
idempotencyKey: flag("idempotency-key"),
|
|
922
|
+
compactDuplicates: !hasFlag("expand-duplicates")
|
|
923
|
+
});
|
|
924
|
+
batchOutput(customerSignalBatch(result2));
|
|
925
|
+
return;
|
|
926
|
+
}
|
|
927
|
+
rejectDurableNoWaitPollingFlags("Company Signals");
|
|
596
928
|
const rows = readBatchInput();
|
|
597
929
|
if (rows) {
|
|
930
|
+
const durableBatch = rows.length > 25;
|
|
598
931
|
const requests = rows.map((value) => {
|
|
599
932
|
const row = record(value);
|
|
600
933
|
const online = row.online ?? !hasFlag("offline");
|
|
@@ -614,18 +947,30 @@ async function companySignals() {
|
|
|
614
947
|
enablePredictiveIntelligence: row.enablePredictiveIntelligence ?? row.enable_predictive_intelligence,
|
|
615
948
|
skipCache: row.skipCache ?? row.skip_cache ?? hasFlag("skip-cache"),
|
|
616
949
|
includeCandidateEvidence: row.includeCandidateEvidence ?? row.include_candidate_evidence ?? hasFlag("include-candidate-evidence"),
|
|
617
|
-
maxWaitMs: row.maxWaitMs || row.max_wait_ms || numberFlag("max-wait-ms")
|
|
950
|
+
maxWaitMs: row.maxWaitMs || row.max_wait_ms || (durableBatch ? void 0 : numberFlag("max-wait-ms")),
|
|
951
|
+
idempotencyKey: row.idempotencyKey || row.idempotency_key
|
|
618
952
|
};
|
|
619
953
|
});
|
|
620
954
|
if (hasFlag("dry-run")) {
|
|
955
|
+
if (hasFlag("no-wait")) fail("Company Signals --no-wait cannot be combined with --dry-run.");
|
|
621
956
|
const result3 = await createClient().enrichCompanies(requests, { ...batchOptions(), dryRun: true });
|
|
622
957
|
dryRunOutput(result3);
|
|
623
958
|
return;
|
|
624
959
|
}
|
|
960
|
+
if (hasFlag("no-wait")) {
|
|
961
|
+
const job = await createClient().enrichCompanies(requests, {
|
|
962
|
+
...batchOptions(),
|
|
963
|
+
waitForResult: false
|
|
964
|
+
});
|
|
965
|
+
recoverableJobOutput(job, "company-signals");
|
|
966
|
+
return;
|
|
967
|
+
}
|
|
625
968
|
const result2 = await createClient().enrichCompanies(requests, batchOptions());
|
|
626
969
|
batchOutput(customerSignalBatch(result2));
|
|
627
970
|
return;
|
|
628
971
|
}
|
|
972
|
+
if (hasFlag("no-wait")) fail("Company Signals --no-wait requires --input with more than 25 homogeneous rows.");
|
|
973
|
+
if (hasFlag("poll-interval-ms")) fail("Company Signals --poll-interval-ms requires --input or --job-id.");
|
|
629
974
|
const companyDomain = flag("domain") || flag("company-domain");
|
|
630
975
|
const companyName = flag("company-name");
|
|
631
976
|
const signalRunId = flag("signal-run-id");
|
|
@@ -676,6 +1021,7 @@ function signalQueryFromArgs(args) {
|
|
|
676
1021
|
"--limit",
|
|
677
1022
|
"--sources",
|
|
678
1023
|
"--lookback-days",
|
|
1024
|
+
"--icp-context",
|
|
679
1025
|
"--signal-search-run-id",
|
|
680
1026
|
"--idempotency-key",
|
|
681
1027
|
"--max-wait-ms",
|
|
@@ -709,8 +1055,13 @@ async function signals(args) {
|
|
|
709
1055
|
if (sourceValues?.some((source) => !["web", "news"].includes(source))) {
|
|
710
1056
|
fail("--sources must contain only web or news");
|
|
711
1057
|
}
|
|
1058
|
+
const icpContext = flag("icp-context");
|
|
1059
|
+
if (hasFlag("icp-context") && (typeof icpContext !== "string" || icpContext.trim().length < 2 || icpContext.trim().length > 4e3)) {
|
|
1060
|
+
fail("--icp-context must contain between 2 and 4000 characters");
|
|
1061
|
+
}
|
|
712
1062
|
const request = {
|
|
713
1063
|
query,
|
|
1064
|
+
icpContext: icpContext?.trim(),
|
|
714
1065
|
signalSearchRunId,
|
|
715
1066
|
limit: numberFlag("limit"),
|
|
716
1067
|
sources: sourceValues,
|
|
@@ -764,6 +1115,14 @@ async function signals(args) {
|
|
|
764
1115
|
`Source cost per qualified company: ${guardrail.costPerQualifiedSignalUsd ?? "n/a"} (must be < $${guardrail.maxCostPerQualifiedSignalUsd})
|
|
765
1116
|
`
|
|
766
1117
|
);
|
|
1118
|
+
if (guardrail.identityResolutionCostUsd !== void 0) {
|
|
1119
|
+
const identityCost = guardrail.identityResolutionCostUsd === null ? "unavailable" : `$${guardrail.identityResolutionCostUsd}`;
|
|
1120
|
+
const identityRequests = guardrail.identityProviderRequests;
|
|
1121
|
+
process.stdout.write(
|
|
1122
|
+
`Identity resolution cost: ${identityCost}${identityRequests === void 0 ? "" : ` across ${identityRequests} identity lookup request${identityRequests === 1 ? "" : "s"}`}${guardrail.identityCostSource ? ` (${guardrail.identityCostSource})` : ""}.
|
|
1123
|
+
`
|
|
1124
|
+
);
|
|
1125
|
+
}
|
|
767
1126
|
}
|
|
768
1127
|
for (const signal of result.signals.slice(0, 20)) {
|
|
769
1128
|
process.stdout.write(`- ${signal.company.name} (${signal.company.domain}) \u2014 ${signal.eventDate} \u2014 ${signal.title}
|
|
@@ -777,9 +1136,43 @@ async function signals(args) {
|
|
|
777
1136
|
});
|
|
778
1137
|
}
|
|
779
1138
|
async function signalToCopy() {
|
|
780
|
-
|
|
1139
|
+
const jobId = flag("job-id");
|
|
1140
|
+
if (jobId) {
|
|
1141
|
+
const conflicts = [
|
|
1142
|
+
"input",
|
|
1143
|
+
"dry-run",
|
|
1144
|
+
"company-domain",
|
|
1145
|
+
"domain",
|
|
1146
|
+
"person-name",
|
|
1147
|
+
"title",
|
|
1148
|
+
"campaign-offer",
|
|
1149
|
+
"offer",
|
|
1150
|
+
"research-prompt",
|
|
1151
|
+
"prompt",
|
|
1152
|
+
"lookback-days",
|
|
1153
|
+
"signal-run-id",
|
|
1154
|
+
"skip-cache",
|
|
1155
|
+
"deep-search",
|
|
1156
|
+
"no-wait",
|
|
1157
|
+
"concurrency"
|
|
1158
|
+
].filter(hasFlag);
|
|
1159
|
+
if (conflicts.length > 0) {
|
|
1160
|
+
fail(`Signal to Copy --job-id cannot be combined with ${conflicts.map((name) => `--${name}`).join(", ")}.`);
|
|
1161
|
+
}
|
|
1162
|
+
const result2 = await createClient().resumeSignalCopyBatch(jobId, {
|
|
1163
|
+
pageSize: numberFlag("page-size"),
|
|
1164
|
+
maxWaitMs: numberFlag("max-wait-ms"),
|
|
1165
|
+
pollIntervalMs: numberFlag("poll-interval-ms"),
|
|
1166
|
+
idempotencyKey: flag("idempotency-key"),
|
|
1167
|
+
compactDuplicates: !hasFlag("expand-duplicates")
|
|
1168
|
+
});
|
|
1169
|
+
batchOutput(customerSignalBatch(result2));
|
|
1170
|
+
return;
|
|
1171
|
+
}
|
|
1172
|
+
rejectDurableNoWaitPollingFlags("Signal to Copy");
|
|
781
1173
|
const rows = readBatchInput();
|
|
782
1174
|
if (rows) {
|
|
1175
|
+
const durableBatch = rows.length > 25;
|
|
783
1176
|
const requests = rows.map((value) => {
|
|
784
1177
|
const row = record(value);
|
|
785
1178
|
return {
|
|
@@ -788,21 +1181,34 @@ async function signalToCopy() {
|
|
|
788
1181
|
title: row.title,
|
|
789
1182
|
campaignOffer: row.campaignOffer || row.campaign_offer || row.offer,
|
|
790
1183
|
researchPrompt: row.researchPrompt || row.research_prompt || flag("research-prompt") || flag("prompt"),
|
|
1184
|
+
lookbackDays: row.lookbackDays || row.lookback_days || numberFlag("lookback-days"),
|
|
791
1185
|
signalRunId: row.signalRunId || row.signal_run_id,
|
|
792
1186
|
skipCache: row.skipCache ?? row.skip_cache ?? hasFlag("skip-cache"),
|
|
793
1187
|
enableDeepSearch: row.enableDeepSearch ?? row.enable_deep_search ?? hasFlag("deep-search"),
|
|
794
|
-
maxWaitMs: row.maxWaitMs || row.max_wait_ms || numberFlag("max-wait-ms")
|
|
1188
|
+
maxWaitMs: row.maxWaitMs || row.max_wait_ms || (durableBatch ? void 0 : numberFlag("max-wait-ms")),
|
|
1189
|
+
idempotencyKey: row.idempotencyKey || row.idempotency_key
|
|
795
1190
|
};
|
|
796
1191
|
});
|
|
797
1192
|
if (hasFlag("dry-run")) {
|
|
1193
|
+
if (hasFlag("no-wait")) fail("Signal to Copy --no-wait cannot be combined with --dry-run.");
|
|
798
1194
|
const result3 = await createClient().createSignalCopyBatch(requests, { ...batchOptions(), dryRun: true });
|
|
799
1195
|
dryRunOutput(result3);
|
|
800
1196
|
return;
|
|
801
1197
|
}
|
|
1198
|
+
if (hasFlag("no-wait")) {
|
|
1199
|
+
const job = await createClient().createSignalCopyBatch(requests, {
|
|
1200
|
+
...batchOptions(),
|
|
1201
|
+
waitForResult: false
|
|
1202
|
+
});
|
|
1203
|
+
recoverableJobOutput(job, "signal-to-copy");
|
|
1204
|
+
return;
|
|
1205
|
+
}
|
|
802
1206
|
const result2 = await createClient().createSignalCopyBatch(requests, batchOptions());
|
|
803
1207
|
batchOutput(customerSignalBatch(result2));
|
|
804
1208
|
return;
|
|
805
1209
|
}
|
|
1210
|
+
if (hasFlag("no-wait")) fail("Signal to Copy --no-wait requires --input with more than 25 available-data-only rows.");
|
|
1211
|
+
if (hasFlag("poll-interval-ms")) fail("Signal to Copy --poll-interval-ms requires --input or --job-id.");
|
|
806
1212
|
const companyDomain = flag("company-domain") || flag("domain");
|
|
807
1213
|
const personName = flag("person-name");
|
|
808
1214
|
const title = flag("title");
|
|
@@ -814,6 +1220,7 @@ async function signalToCopy() {
|
|
|
814
1220
|
title,
|
|
815
1221
|
campaignOffer,
|
|
816
1222
|
researchPrompt: flag("research-prompt") || flag("prompt"),
|
|
1223
|
+
lookbackDays: numberFlag("lookback-days"),
|
|
817
1224
|
signalRunId,
|
|
818
1225
|
skipCache: hasFlag("skip-cache"),
|
|
819
1226
|
enableDeepSearch: hasFlag("deep-search"),
|
|
@@ -933,6 +1340,14 @@ main().catch((error) => {
|
|
|
933
1340
|
retry_after: signalizError.retryAfter,
|
|
934
1341
|
retry_after_seconds: signalizError.retryAfter
|
|
935
1342
|
} : {},
|
|
1343
|
+
...signalizError?.details?.retry_strategy === "new_idempotency_key" ? { retry_strategy: "new_idempotency_key" } : {},
|
|
1344
|
+
...signalizError?.details?.credits_used === 0 ? { credits_used: 0 } : {},
|
|
1345
|
+
...signalizError?.details?.credits_charged === 0 ? { credits_charged: 0 } : {},
|
|
1346
|
+
...typeof signalizError?.details?.run_id === "string" ? { run_id: signalizError.details.run_id } : {},
|
|
1347
|
+
...typeof signalizError?.details?.verification_run_id === "string" ? { verification_run_id: signalizError.details.verification_run_id } : {},
|
|
1348
|
+
...typeof signalizError?.details?.job_id === "string" ? { job_id: signalizError.details.job_id } : {},
|
|
1349
|
+
...typeof signalizError?.details?.idempotency_key === "string" ? { idempotency_key: signalizError.details.idempotency_key } : {},
|
|
1350
|
+
...typeof signalizError?.details?.suggested_action === "string" ? { suggested_action: signalizError.details.suggested_action } : {},
|
|
936
1351
|
...signalizError?.errorType ? { error_type: signalizError.errorType } : {},
|
|
937
1352
|
...signalizError?.details ? { details: signalizError.details } : {}
|
|
938
1353
|
}, hasFlag("json") ? 2 : void 0)}
|
|
@@ -946,6 +1361,46 @@ main().catch((error) => {
|
|
|
946
1361
|
fail(`${message}
|
|
947
1362
|
Recoverable signal run: ${signalRunId}
|
|
948
1363
|
Retry the same signaliz ${command} command with --signal-run-id ${signalRunId}, or use --json for structured recovery metadata.`);
|
|
1364
|
+
}
|
|
1365
|
+
if (error instanceof import_sdk.SignalizError && typeof error.details?.verification_run_id === "string") {
|
|
1366
|
+
const verificationRunId = error.details.verification_run_id;
|
|
1367
|
+
if (error.details.suggested_action === "retry_with_verification_run_id") {
|
|
1368
|
+
fail(`${message}
|
|
1369
|
+
Recoverable verification run: ${verificationRunId}
|
|
1370
|
+
Retry with: signaliz verify-email --verification-run-id ${verificationRunId}`);
|
|
1371
|
+
}
|
|
1372
|
+
fail(`${message}
|
|
1373
|
+
Terminal verification run: ${verificationRunId}
|
|
1374
|
+
No automatic retry is recommended; review the provider outcome or contact support.`);
|
|
1375
|
+
}
|
|
1376
|
+
if (error instanceof import_sdk.SignalizError && typeof error.details?.run_id === "string" && process.argv[2] === "find-email") {
|
|
1377
|
+
const runId = error.details.run_id;
|
|
1378
|
+
if (error.details.suggested_action === "retry_with_run_id") {
|
|
1379
|
+
fail(`${message}
|
|
1380
|
+
Recoverable Find Email run: ${runId}
|
|
1381
|
+
Retry with: signaliz find-email --run-id ${runId}`);
|
|
1382
|
+
}
|
|
1383
|
+
fail(`${message}
|
|
1384
|
+
Terminal Find Email run: ${runId}
|
|
1385
|
+
No automatic retry is recommended; review the provider outcome or contact support.`);
|
|
1386
|
+
}
|
|
1387
|
+
if (error instanceof import_sdk.SignalizError && typeof error.details?.job_id === "string") {
|
|
1388
|
+
const jobId = error.details.job_id;
|
|
1389
|
+
if (error.details.suggested_action === "check_job_status") {
|
|
1390
|
+
if (["find-email", "verify-email", "company-signals", "signal-to-copy"].includes(process.argv[2])) {
|
|
1391
|
+
const idempotencyKey = typeof error.details.idempotency_key === "string" ? error.details.idempotency_key : "";
|
|
1392
|
+
const keyFlag = idempotencyKey ? ` --idempotency-key ${idempotencyKey}` : "";
|
|
1393
|
+
fail(`${message}
|
|
1394
|
+
Recoverable job: ${jobId}
|
|
1395
|
+
Retry with: signaliz ${process.argv[2]} --job-id ${jobId}${keyFlag}`);
|
|
1396
|
+
}
|
|
1397
|
+
fail(`${message}
|
|
1398
|
+
Recoverable job: ${jobId}
|
|
1399
|
+
Resume this job through the REST API or SDK using job_id ${jobId}, or use --json for structured recovery metadata.`);
|
|
1400
|
+
}
|
|
1401
|
+
fail(`${message}
|
|
1402
|
+
Terminal job: ${jobId}
|
|
1403
|
+
No automatic retry is recommended; review the provider outcome or contact support.`);
|
|
949
1404
|
}
|
|
950
1405
|
fail(message);
|
|
951
1406
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@signaliz/cli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.58",
|
|
4
4
|
"description": "Signaliz CLI for Signals Everything, Find Email, Verify Email, Company Signal Enrichment, and Signal to Copy AI.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"signaliz": "dist/bin.js"
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"node": ">=18"
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@signaliz/sdk": "^1.0.
|
|
32
|
+
"@signaliz/sdk": "^1.0.62"
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
35
35
|
"tsup": "^8.0.0",
|