@signaliz/cli 1.0.17 → 1.0.19

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.
Files changed (3) hide show
  1. package/README.md +8 -1
  2. package/dist/bin.js +413 -14
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -7,6 +7,12 @@ provider routing for Nango-managed customer API destinations, MCP/Ops
7
7
  observability, enrichment primitives, and live email verification at 0.02 fresh
8
8
  enrichment credits when a new verification is needed.
9
9
 
10
+ Monthly plans include unlimited cache search, Campaign Builder, API, MCP, CLI,
11
+ Ops, and integrations. Team is the $499/month public plan and adds unlimited
12
+ live Find People and Find Companies in Signaliz; Agency stays $999/month with
13
+ higher credits and throughput. Fresh credits are used only when Signaliz
14
+ creates, verifies, or fetches new premium data.
15
+
10
16
  Fresh enrichment throughput now allows Free 60/min, Builder 300/min, Team
11
17
  600/min, Agency 1,000/min, and Pay-As-You-Go 1,000/min, with a 5,000/hour
12
18
  workspace safety cap.
@@ -183,7 +189,8 @@ signaliz lead local \
183
189
  # Poll async lead jobs
184
190
  signaliz lead status <job_id>
185
191
 
186
- # Verify or find emails
192
+ # Verify or find emails. `email find` uses the V3 waterfall:
193
+ # primary discovery, secondary enrichment, verified guesses, then paid fallback if needed.
187
194
  signaliz email verify jane@example.com
188
195
  signaliz email verify-batch --emails jane@example.com,sam@example.com
189
196
  signaliz email status <job_id>
package/dist/bin.js CHANGED
@@ -100,7 +100,7 @@ function normalizeCampaignBuildInput(raw) {
100
100
  return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
101
101
  };
102
102
  const policy = nested("policy", "policy");
103
- const signals = nested("signals", "signals");
103
+ const signals2 = nested("signals", "signals");
104
104
  const copy = nested("copy", "copy");
105
105
  const delivery = nested("delivery", "delivery");
106
106
  const learningHoldout = nested("learning_holdout", "learningHoldout");
@@ -131,11 +131,11 @@ function normalizeCampaignBuildInput(raw) {
131
131
  maxCredits: policy.max_credits ?? policy.maxCredits,
132
132
  verifyEmails: policy.verify_emails ?? policy.verifyEmails
133
133
  } : void 0,
134
- signals: signals ? {
135
- ...signals,
136
- customPrompt: signals.custom_prompt ?? signals.customPrompt,
137
- confidenceThreshold: signals.confidence_threshold ?? signals.confidenceThreshold,
138
- lookbackDays: signals.lookback_days ?? signals.lookbackDays
134
+ signals: signals2 ? {
135
+ ...signals2,
136
+ customPrompt: signals2.custom_prompt ?? signals2.customPrompt,
137
+ confidenceThreshold: signals2.confidence_threshold ?? signals2.confidenceThreshold,
138
+ lookbackDays: signals2.lookback_days ?? signals2.lookbackDays
139
139
  } : void 0,
140
140
  qualification: nested("qualification", "qualification"),
141
141
  copy: copy ? {
@@ -431,6 +431,69 @@ function parseJsonObjectFromString(value, label) {
431
431
  }
432
432
  return parsed;
433
433
  }
434
+ var PUBLIC_GROUND_TRUTH_CLI_FILTERS = [
435
+ ["industry", "industry"],
436
+ ["location", "location"],
437
+ ["nativeId", "native-id", "nativeId"],
438
+ ["entityName", "entity-name", "entityName"],
439
+ ["domain", "domain"],
440
+ ["websiteUrl", "website-url", "websiteUrl"],
441
+ ["phone", "phone"],
442
+ ["email", "email"],
443
+ ["city", "city"],
444
+ ["state", "state"],
445
+ ["postalCode", "postal-code", "postalCode"],
446
+ ["country", "country"],
447
+ ["sourceUrl", "source-url", "sourceUrl"],
448
+ ["naics", "naics"],
449
+ ["industryCode", "industry-code", "industryCode"],
450
+ ["industryType", "industry-type", "industryType"],
451
+ ["year", "year"],
452
+ ["quarter", "quarter"],
453
+ ["areaFips", "area-fips", "areaFips"],
454
+ ["geoId", "geo-id", "geoId"],
455
+ ["ownCode", "own-code", "ownCode"],
456
+ ["sourceUpdatedAfter", "source-updated-after", "sourceUpdatedAfter"],
457
+ ["sourceUpdatedBefore", "source-updated-before", "sourceUpdatedBefore"],
458
+ ["observedAfter", "observed-after", "observedAfter"],
459
+ ["observedBefore", "observed-before", "observedBefore"]
460
+ ];
461
+ function commandObjectFromKeyValueFlags(flags, keyValueNames, jsonNames) {
462
+ const object = {};
463
+ for (const jsonName of jsonNames) {
464
+ const parsed = commandJsonObjectFlag(flags, jsonName);
465
+ if (parsed) Object.assign(object, parsed);
466
+ }
467
+ for (const pair of repeatedCommandStringFlag(flags, ...keyValueNames)) {
468
+ const eq = pair.indexOf("=");
469
+ if (eq <= 0) die(`Expected key=value for --${keyValueNames[0]}`);
470
+ const key = pair.slice(0, eq).trim();
471
+ const value = pair.slice(eq + 1).trim();
472
+ if (!key) die(`Expected non-empty key for --${keyValueNames[0]}`);
473
+ object[key] = value;
474
+ }
475
+ return Object.keys(object).length > 0 ? object : void 0;
476
+ }
477
+ function publicGroundTruthFiltersFromCommandFlags(flags) {
478
+ const filters = {};
479
+ for (const [paramName, ...flagNames] of PUBLIC_GROUND_TRUTH_CLI_FILTERS) {
480
+ const value = commandStringFlag(flags, ...flagNames);
481
+ if (value) filters[paramName] = value;
482
+ }
483
+ const joinKeys = commandObjectFromKeyValueFlags(
484
+ flags,
485
+ ["join-key", "joinKey"],
486
+ ["join-keys-json", "joinKeysJson"]
487
+ );
488
+ const rowData = commandObjectFromKeyValueFlags(
489
+ flags,
490
+ ["row-data", "rowData"],
491
+ ["row-data-json", "rowDataJson"]
492
+ );
493
+ if (joinKeys) filters.joinKeys = joinKeys;
494
+ if (rowData) filters.rowData = rowData;
495
+ return filters;
496
+ }
434
497
  function output(data, humanFn) {
435
498
  if (jsonMode()) {
436
499
  console.log(JSON.stringify(data, null, 2));
@@ -479,6 +542,32 @@ function layerCommand(layer) {
479
542
  const normalized = String(layer || "sender").trim() || "sender";
480
543
  return `signaliz connect --layer ${normalized}`;
481
544
  }
545
+ function campaignSourcePlanRecord(result) {
546
+ const root = record(result);
547
+ const plan = record(root.sourcePlan ?? root.source_plan);
548
+ if (Object.keys(plan).length > 0) return plan;
549
+ return record(record(root.plan).source_plan);
550
+ }
551
+ function campaignSourceRouteRecords(result) {
552
+ const root = record(result);
553
+ const plan = campaignSourcePlanRecord(result);
554
+ return firstArray(root.sourceRoutes, root.source_routes, plan.routes).filter((route) => route && typeof route === "object" && !Array.isArray(route)).sort((a, b) => Number(a.priority ?? 0) - Number(b.priority ?? 0));
555
+ }
556
+ function campaignSourceSummary(result) {
557
+ const plan = campaignSourcePlanRecord(result);
558
+ if (Object.keys(plan).length === 0) return void 0;
559
+ const decision = record(plan.route_decision);
560
+ const intent = capitalizeLabel(plan.intent);
561
+ const reason = String(decision.selected_reason || "").trim();
562
+ return [intent, reason].filter(Boolean).join(" - ");
563
+ }
564
+ function campaignSourceRouteLine(route) {
565
+ const role = capitalizeLabel(route.role);
566
+ const tool = labelize(route.tool || "source");
567
+ const providers = firstArray(route.providers).map(labelize).filter(Boolean).join(" + ");
568
+ const fallback = typeof route.fallback_when === "string" && route.fallback_when.trim() ? ` (${route.fallback_when.trim()})` : "";
569
+ return `${role ? `${role}: ` : ""}${tool}${providers ? ` via ${providers}` : ""}${fallback}`;
570
+ }
482
571
  function blockedRouteLayers(result) {
483
572
  const root = record(result);
484
573
  const providerActivation = record(root.provider_activation);
@@ -582,6 +671,7 @@ function inferMcpToolCategory(toolName) {
582
671
  }
583
672
  if (toolName.includes("icp")) return "icp";
584
673
  if (toolName.startsWith("ai_clean_")) return "data_cleaning";
674
+ if (toolName.includes("public_ground_truth")) return "public_data";
585
675
  if (toolName.includes("system") || toolName.includes("workflow")) return "automation";
586
676
  if (toolName.includes("agent") || toolName.includes("platform_health") || toolName === "discover_capabilities") return "observability";
587
677
  return void 0;
@@ -1071,6 +1161,10 @@ Discovery:
1071
1161
  --category NAME Filter by category
1072
1162
  discover --query "..." Find relevant MCP tools/capabilities
1073
1163
  --category NAME Optional category filter, e.g. ops or enrichment
1164
+ public-data search Search public registry/cache records
1165
+ --query "..." Company/name/domain/native ID search text
1166
+ --source-id ID Optional source filter
1167
+ public-data sources List public data source IDs and join-key metadata
1074
1168
 
1075
1169
  AI:
1076
1170
  ai multi-model Run Custom AI Enrichment - Multi Model
@@ -1087,6 +1181,22 @@ AI:
1087
1181
  Record fields containing attachment URLs/arrays
1088
1182
  --max-concurrency N Record concurrency, default 3, maximum 5
1089
1183
  --confirm-spend Required to run spendful AI enrichment
1184
+ ai fusion Run AI Enrichment Fusion with OpenRouter Fusion
1185
+ --prompt "..." Prompt/template for each record (required)
1186
+ --preset general-budget|general-high
1187
+ --records-file FILE JSON array, or object with records/inputs/data
1188
+ --output-fields a:text,b:number
1189
+ --dry-run Default. Return a no-spend Fusion plan
1190
+ --confirm-spend Run a live experimental probe with dry_run=false
1191
+
1192
+ Signals:
1193
+ signals enrich Run canonical web-grounded company signal enrichment (up to 15 signals)
1194
+ signals fusion Run Signal Fusion for company signal intelligence
1195
+ --domain example.com Company domain, or use --company-name
1196
+ --preset general-budget|general-high
1197
+ --signal-types hiring,funding
1198
+ --dry-run Default. Return a no-spend Fusion plan
1199
+ --confirm-spend Run a live experimental probe with dry_run=false
1090
1200
 
1091
1201
  Lead Generation:
1092
1202
  lead generate Start a B2B lead-generation job
@@ -2691,6 +2801,111 @@ async function discover(rest) {
2691
2801
  }
2692
2802
  );
2693
2803
  }
2804
+ function publicDataPositionalText(rest) {
2805
+ const parts = [];
2806
+ for (let i = 0; i < rest.length; i += 1) {
2807
+ const part = rest[i];
2808
+ if (part.startsWith("--")) {
2809
+ if (!part.includes("=") && rest[i + 1] && !rest[i + 1].startsWith("--")) i += 1;
2810
+ continue;
2811
+ }
2812
+ parts.push(part);
2813
+ }
2814
+ return parts.join(" ").trim() || void 0;
2815
+ }
2816
+ function publicDataCell(value, width) {
2817
+ const text = String(value ?? "").replace(/\s+/g, " ").trim();
2818
+ return text.length > width ? `${text.slice(0, Math.max(width - 3, 0))}...` : text;
2819
+ }
2820
+ function printPublicDataSearchResult(result) {
2821
+ const records = Array.isArray(result.records) ? result.records : [];
2822
+ console.log(`Public data search: ${result.total_count ?? 0} matches (${records.length} returned)`);
2823
+ if (result.query) console.log(`Query: ${result.query}`);
2824
+ if (result.source_id) console.log(`Source: ${result.source_id}`);
2825
+ if (result.entity_type) console.log(`Entity type: ${result.entity_type}`);
2826
+ if (!records.length) {
2827
+ console.log("No records matched.");
2828
+ return;
2829
+ }
2830
+ console.log("");
2831
+ console.log("Source".padEnd(22) + "Native ID".padEnd(22) + "Name".padEnd(42) + "Industry".padEnd(28) + "Domain".padEnd(24) + "Loc".padEnd(12) + "Match");
2832
+ console.log("-".repeat(160));
2833
+ for (const record2 of records) {
2834
+ const loc = [record2.city, record2.state].filter(Boolean).join(", ");
2835
+ const industry = [record2.industry_code, record2.industry_name].filter(Boolean).join(" ");
2836
+ console.log(
2837
+ publicDataCell(record2.source_id, 21).padEnd(22) + publicDataCell(record2.native_id, 21).padEnd(22) + publicDataCell(record2.entity_name, 41).padEnd(42) + publicDataCell(industry, 27).padEnd(28) + publicDataCell(record2.domain, 23).padEnd(24) + publicDataCell(loc, 11).padEnd(12) + publicDataCell(record2.match_type, 16)
2838
+ );
2839
+ }
2840
+ if (result.next_offset !== null && result.next_offset !== void 0) {
2841
+ console.log(`
2842
+ Next page: --offset ${result.next_offset}`);
2843
+ }
2844
+ }
2845
+ function printPublicDataSources(result) {
2846
+ const sources = Array.isArray(result.sources) ? result.sources : [];
2847
+ console.log(`Public data sources: ${sources.length}/${result.total ?? sources.length}`);
2848
+ if (!sources.length) {
2849
+ console.log("No sources matched.");
2850
+ return;
2851
+ }
2852
+ console.log("");
2853
+ console.log("Source ID".padEnd(30) + "Status".padEnd(12) + "Rung".padEnd(7) + "Join key".padEnd(20) + "Cadence".padEnd(16) + "Name");
2854
+ console.log("-".repeat(120));
2855
+ for (const source of sources) {
2856
+ console.log(
2857
+ publicDataCell(source.id, 29).padEnd(30) + publicDataCell(source.ingestion_status, 11).padEnd(12) + String(source.ground_truth_rung ?? "").padEnd(7) + publicDataCell(source.primary_join_key || source.join_key_quality, 19).padEnd(20) + publicDataCell(source.update_cadence, 15).padEnd(16) + publicDataCell(source.name, 40)
2858
+ );
2859
+ }
2860
+ }
2861
+ async function publicData(sub, rest) {
2862
+ if (!sub || sub === "--help" || sub === "-h" || sub === "help") {
2863
+ process.stdout.write(`signaliz public-data <search|sources> [options]
2864
+
2865
+ Search the public-ground-truth cache of public registry and disclosure data.
2866
+
2867
+ Examples:
2868
+ signaliz public-data search --query "swift transportation" --source-id fmcsa_company_census --limit 10
2869
+ signaliz public-data search --industry "software publishers" --location CA --limit 25 --json
2870
+ signaliz public-data search --source-id bls_qcew --naics 541511 --state CA --year 2025 --json
2871
+ signaliz public-data search --domain swifttrans.com --json
2872
+ signaliz public-data search --join-key ein=123456789 --json
2873
+ signaliz public-data sources --status ready --json
2874
+ `);
2875
+ return;
2876
+ }
2877
+ const sdk = getSdk();
2878
+ if (!sdk.publicGroundTruth?.search || !sdk.publicGroundTruth?.listSources) {
2879
+ die("Installed @signaliz/sdk does not expose publicGroundTruth yet. Rebuild or upgrade @signaliz/sdk.");
2880
+ }
2881
+ if (sub === "sources" || sub === "list-sources") {
2882
+ const flags2 = parseCommandFlags(rest);
2883
+ const result2 = await sdk.publicGroundTruth.listSources({
2884
+ query: commandStringFlag(flags2, "query", "q", "search"),
2885
+ status: commandStringFlag(flags2, "status"),
2886
+ limit: commandNumberFlag(flags2, "limit")
2887
+ });
2888
+ return output(result2, () => printPublicDataSources(result2));
2889
+ }
2890
+ if (sub !== "search") die("Usage: signaliz public-data <search|sources>");
2891
+ const flags = parseCommandFlags(rest);
2892
+ const query = commandStringFlag(flags, "query", "q", "search") || publicDataPositionalText(rest);
2893
+ const sourceId = commandStringFlag(flags, "source-id", "sourceId");
2894
+ const entityType = commandStringFlag(flags, "entity-type", "entityType");
2895
+ const filters = publicGroundTruthFiltersFromCommandFlags(flags);
2896
+ if (!query && !sourceId && !entityType && Object.keys(filters).length === 0) {
2897
+ die("Usage: signaliz public-data search [--query TEXT] [--industry TEXT] [--location TEXT] [--domain DOMAIN] [--naics CODE] [--join-key k=v]");
2898
+ }
2899
+ const result = await sdk.publicGroundTruth.search({
2900
+ query,
2901
+ sourceId,
2902
+ entityType,
2903
+ ...filters,
2904
+ limit: commandNumberFlag(flags, "limit"),
2905
+ offset: commandNumberFlag(flags, "offset")
2906
+ });
2907
+ return output(result, () => printPublicDataSearchResult(result));
2908
+ }
2694
2909
  function normalizeAiRecords(value, source) {
2695
2910
  if (Array.isArray(value)) {
2696
2911
  if (value.every((item) => item && typeof item === "object" && !Array.isArray(item))) {
@@ -2776,12 +2991,47 @@ function aiAttachmentsFromFlags() {
2776
2991
  return attachments;
2777
2992
  }
2778
2993
  async function ai(sub, rest) {
2779
- if (sub !== "multi-model" && sub !== "multimodel") {
2780
- die('Usage: signaliz ai multi-model --prompt "..." --confirm-spend');
2781
- }
2782
2994
  if (flagArg("model") !== void 0 || hasFlag("model") || flagArg("analysis-models") !== void 0 || hasFlag("analysis-models") || flagArg("judge-model") !== void 0 || hasFlag("judge-model")) {
2783
2995
  die("Custom AI model selection is no longer supported. Signaliz-hosted custom AI uses the default model and returns model readback metadata.");
2784
2996
  }
2997
+ if (sub === "fusion") {
2998
+ const promptText2 = promptArg(rest);
2999
+ if (!promptText2) die('Usage: signaliz ai fusion --prompt "..." [--dry-run|--confirm-spend]');
3000
+ const dryRun = hasFlag("dry-run") || !hasFlag("confirm-spend");
3001
+ const sdk2 = getSdk();
3002
+ const result2 = await sdk2.ai.fusion({
3003
+ prompt: promptText2,
3004
+ records: aiRecordsFromFlags(),
3005
+ preset: flagArg("preset") || "general-budget",
3006
+ systemPrompt: flagArg("system-prompt"),
3007
+ temperature: numberFlag("temperature"),
3008
+ maxToolCalls: numberFlag("max-tool-calls"),
3009
+ maxTokens: numberFlag("max-tokens"),
3010
+ timeoutMs: numberFlag("timeout-ms"),
3011
+ dryRun,
3012
+ confirmSpend: !dryRun,
3013
+ maxCredits: numberFlag("max-credits"),
3014
+ maxCostUsd: numberFlag("max-cost-usd"),
3015
+ outputFields: aiOutputFieldsFromFlags()
3016
+ });
3017
+ return output(result2, () => {
3018
+ console.log(dryRun || result2.raw?.dry_run ? "AI Enrichment Fusion dry run" : "AI Enrichment Fusion complete");
3019
+ console.log(` Model: ${result2.model || "openrouter/fusion"}`);
3020
+ console.log(` Preset: ${String(result2.fusion?.preset || flagArg("preset") || "general-budget")}`);
3021
+ console.log(` Records: ${result2.results.length}`);
3022
+ console.log(` Cost USD: $${Number(result2.costUsd || 0).toFixed(6)}`);
3023
+ console.log(` Credits: ${result2.creditsUsed}`);
3024
+ console.log(` Tokens: ${result2.tokensUsed}`);
3025
+ const preview = result2.results.slice(0, 3);
3026
+ if (preview.length) {
3027
+ console.log("\nResults preview:");
3028
+ console.log(JSON.stringify(preview, null, 2));
3029
+ }
3030
+ });
3031
+ }
3032
+ if (sub !== "multi-model" && sub !== "multimodel") {
3033
+ die('Usage: signaliz ai <multi-model|fusion> --prompt "..." --confirm-spend');
3034
+ }
2785
3035
  const promptText = promptArg(rest);
2786
3036
  if (!promptText) die('Usage: signaliz ai multi-model --prompt "..." --confirm-spend');
2787
3037
  if (!hasFlag("confirm-spend")) {
@@ -2818,6 +3068,118 @@ async function ai(sub, rest) {
2818
3068
  }
2819
3069
  });
2820
3070
  }
3071
+ function signalCompaniesFromFlags() {
3072
+ const companiesFile = flagArg("companies-file");
3073
+ if (companiesFile) return normalizeAiRecords(readJsonValue(companiesFile), companiesFile);
3074
+ const companiesJson = flagArg("companies-json");
3075
+ if (companiesJson) return normalizeAiRecords(JSON.parse(companiesJson), "--companies-json");
3076
+ return void 0;
3077
+ }
3078
+ async function signals(sub, rest) {
3079
+ if (sub === "enrich") {
3080
+ const domain2 = flagArg("domain") || flagArg("company-domain");
3081
+ const companyName2 = flagArg("company-name") || rest.find((item) => !item.startsWith("--"));
3082
+ if (!domain2 && !companyName2) {
3083
+ die("Usage: signaliz signals enrich --domain example.com [--target-signal-count 15]");
3084
+ }
3085
+ const sdk2 = getSdk();
3086
+ const result2 = await sdk2.signals.enrichV2({
3087
+ domain: domain2,
3088
+ companyName: companyName2,
3089
+ researchPrompt: flagArg("prompt") || flagArg("research-prompt"),
3090
+ signalTypes: csvFlag("signal-types") || ["all"],
3091
+ targetSignalCount: numberFlag("target-signal-count"),
3092
+ lookbackDays: numberFlag("lookback-days"),
3093
+ online: !hasFlag("offline"),
3094
+ enableDeepSearch: !hasFlag("shallow") && !hasFlag("offline"),
3095
+ skipCache: hasFlag("skip-cache") || hasFlag("fresh-only")
3096
+ });
3097
+ return output(result2, () => {
3098
+ console.log("Company signal enrichment complete");
3099
+ console.log(` Company: ${result2.company.name || companyName2 || domain2}`);
3100
+ console.log(` Signals: ${result2.signalCount}`);
3101
+ const preview = result2.signals.slice(0, 5);
3102
+ if (preview.length) {
3103
+ console.log("\nPreview:");
3104
+ console.log(JSON.stringify(preview, null, 2));
3105
+ }
3106
+ });
3107
+ }
3108
+ if (sub === "signal-to-copy" || sub === "personalize") {
3109
+ const companyDomain = flagArg("company-domain") || flagArg("domain");
3110
+ const personName = flagArg("person-name") || flagArg("name");
3111
+ const title = flagArg("title");
3112
+ const campaignOffer = flagArg("campaign-offer") || flagArg("offer");
3113
+ if (!companyDomain || !personName || !title || !campaignOffer) {
3114
+ die('Usage: signaliz signals signal-to-copy --company-domain example.com --person-name "Alex Smith" --title "VP Sales" --campaign-offer "sales intelligence platform" [--research-prompt "..."]');
3115
+ }
3116
+ const result2 = await getSdk().signals.signalToCopy({
3117
+ companyDomain,
3118
+ personName,
3119
+ title,
3120
+ campaignOffer,
3121
+ researchPrompt: flagArg("research-prompt") || flagArg("prompt")
3122
+ });
3123
+ return output(result2, () => {
3124
+ console.log(`Signal Copy: ${result2.strongestSignal}`);
3125
+ console.log(`Confidence: ${result2.confidence}`);
3126
+ console.log(`Evidence: ${result2.evidenceUrl}`);
3127
+ for (const variation of result2.variations) {
3128
+ console.log(`
3129
+ === ${variation.style} ===`);
3130
+ console.log(`Subject: ${variation.subjectLine}`);
3131
+ console.log(variation.email || `${variation.openingLine}
3132
+
3133
+ ${variation.cta}`);
3134
+ }
3135
+ });
3136
+ }
3137
+ if (sub !== "fusion") {
3138
+ die("Usage: signaliz signals <enrich|signal-to-copy|fusion> --domain example.com");
3139
+ }
3140
+ const dryRun = hasFlag("dry-run") || !hasFlag("confirm-spend");
3141
+ const companies = signalCompaniesFromFlags();
3142
+ const domain = flagArg("domain") || flagArg("company-domain");
3143
+ const companyName = flagArg("company-name") || rest.find((item) => !item.startsWith("--"));
3144
+ const linkedinUrl = flagArg("linkedin-url");
3145
+ if (!companies?.length && !domain && !companyName && !linkedinUrl) {
3146
+ die("Usage: signaliz signals fusion --domain example.com [--dry-run|--confirm-spend]");
3147
+ }
3148
+ const sdk = getSdk();
3149
+ const result = await sdk.signals.fusion({
3150
+ companies,
3151
+ domain,
3152
+ companyName,
3153
+ linkedinUrl,
3154
+ preset: flagArg("preset") || "general-budget",
3155
+ researchPrompt: flagArg("prompt") || flagArg("research-prompt"),
3156
+ signalTypes: csvFlag("signal-types"),
3157
+ lookbackDays: numberFlag("lookback-days"),
3158
+ targetSignalCount: numberFlag("target-signal-count"),
3159
+ maxToolCalls: numberFlag("max-tool-calls"),
3160
+ maxTokens: numberFlag("max-tokens"),
3161
+ timeoutMs: numberFlag("timeout-ms"),
3162
+ dryRun,
3163
+ confirmSpend: !dryRun,
3164
+ maxCredits: numberFlag("max-credits"),
3165
+ maxCostUsd: numberFlag("max-cost-usd")
3166
+ });
3167
+ return output(result, () => {
3168
+ console.log(dryRun || result.raw?.dry_run ? "Signal Fusion dry run" : "Signal Fusion complete");
3169
+ console.log(` Model: ${result.model || "openrouter/fusion"}`);
3170
+ console.log(` Preset: ${String(result.fusion?.preset || flagArg("preset") || "general-budget")}`);
3171
+ console.log(` Companies: ${result.results.length || 1}`);
3172
+ console.log(` Signals: ${result.signalCount}`);
3173
+ console.log(` Cost USD: $${Number(result.costUsd || 0).toFixed(6)}`);
3174
+ console.log(` Credits: ${result.creditsUsed}`);
3175
+ console.log(` Tokens: ${result.tokensUsed}`);
3176
+ const preview = result.signals.length ? result.signals.slice(0, 5) : result.results.slice(0, 3);
3177
+ if (preview.length) {
3178
+ console.log("\nPreview:");
3179
+ console.log(JSON.stringify(preview, null, 2));
3180
+ }
3181
+ });
3182
+ }
2821
3183
  async function campaignBuild() {
2822
3184
  if (isHelpRequest(process.argv.slice(4))) {
2823
3185
  process.stdout.write(campaignHelpFor("build"));
@@ -2892,6 +3254,11 @@ async function campaignBuild() {
2892
3254
  console.log("\u2713 Campaign dry-run plan ready");
2893
3255
  console.log(` Requested: ${result.requestedTargetCount ?? config.targetCount ?? 50} leads`);
2894
3256
  console.log(` Planned: ${result.plannedTargetCount ?? result.requestedTargetCount ?? config.targetCount ?? 50} leads`);
3257
+ const sourceSummary = campaignSourceSummary(result);
3258
+ if (sourceSummary) console.log(` Source: ${sourceSummary}`);
3259
+ for (const [index, route] of campaignSourceRouteRecords(result).slice(0, 4).entries()) {
3260
+ console.log(` Route ${index + 1}: ${campaignSourceRouteLine(route)}`);
3261
+ }
2895
3262
  if (result.estimatedCredits !== void 0) console.log(` Credits: ~${result.estimatedCredits}`);
2896
3263
  if (result.estimatedDurationSeconds !== void 0) console.log(` Duration: ~${result.estimatedDurationSeconds}s`);
2897
3264
  if (result.learningHoldout?.enabled) console.log(` Holdout: enabled (${Math.round(Number(result.learningHoldout.control_percentage || 0) * 100)}% control)`);
@@ -3062,7 +3429,10 @@ function printCampaignCustomerRowCounts(status) {
3062
3429
  if (!counts || typeof counts !== "object") return;
3063
3430
  const value = (camel, snake) => counts[camel] ?? counts[snake] ?? "n/a";
3064
3431
  const requested = value("requestedTarget", "requested_target");
3065
- console.log(`Customer rows: accepted ${value("acceptedRows", "accepted_rows")}/${requested}, copied ${value("copiedRows", "copied_rows")}, approval-ready ${value("approvalReadyRows", "approval_ready_rows")}, QA-ready ${value("qaReadyRows", "qa_ready_rows")}, delivered ${value("deliveredRows", "delivered_rows")}`);
3432
+ const copied = value("copiedRows", "copied_rows");
3433
+ const copySkipped = value("copySkippedRows", "copy_skipped_rows");
3434
+ const copyLabel = typeof copySkipped === "number" && copySkipped > 0 && (!copied || copied === "n/a") ? `copy-skipped ${copySkipped}` : `copied ${copied}`;
3435
+ console.log(`Customer rows: accepted ${value("acceptedRows", "accepted_rows")}/${requested}, ${copyLabel}, approval-ready ${value("approvalReadyRows", "approval_ready_rows")}, QA-ready ${value("qaReadyRows", "qa_ready_rows")}, delivered ${value("deliveredRows", "delivered_rows")}`);
3066
3436
  const shortfalls = [
3067
3437
  ["accepted", value("acceptedShortfall", "accepted_shortfall")],
3068
3438
  ["usable", value("usableShortfall", "usable_shortfall")],
@@ -3180,7 +3550,11 @@ function formatCampaignRowsForOperator(rows) {
3180
3550
  disqualifiers: firstArray(data.disqualifiers),
3181
3551
  signal: data.signal_title ? {
3182
3552
  type: data.signal_type || null,
3553
+ family: data.signal_family || data.signal_type || null,
3183
3554
  title: data.signal_title,
3555
+ source_type: data.signal_source_type || data.signal_event_label || data.signal_classification?.label || null,
3556
+ source_provenance: data.signal_source_provenance || data.source_provenance || null,
3557
+ classification: data.signal_classification || null,
3184
3558
  summary: data.signal_summary || null,
3185
3559
  date: data.signal_date || null,
3186
3560
  confidence: data.signal_confidence ?? null,
@@ -3322,10 +3696,10 @@ var CAMPAIGN_AGENT_APPROVAL_TYPES = [
3322
3696
  "launch"
3323
3697
  ];
3324
3698
  var CAMPAIGN_AGENT_TEMPLATE_PLAYBOOKS = {
3325
- "industrial-ot-resilience": ["net-new-suppressed-list", "proof-first-vertical-gate", "signal-led-copy-approval"],
3326
- "non-medical-home-care": ["proof-first-vertical-gate", "domain-first-recovery", "table-workflow-handoff"],
3327
- "agency-founder-led": ["net-new-suppressed-list", "signal-led-copy-approval", "table-workflow-handoff"],
3328
- "cloud-infrastructure-displacement": ["proof-first-vertical-gate", "domain-first-recovery", "signal-led-copy-approval"]
3699
+ "industrial-ot-resilience": ["net-new-suppressed-list", "proof-first-vertical-gate", "signal-led-copy-approval", "icp-persona-segmentation", "buying-signal-prioritization", "multi-channel-sequence-fit", "revops-feedback-loop"],
3700
+ "non-medical-home-care": ["proof-first-vertical-gate", "domain-first-recovery", "table-workflow-handoff", "icp-persona-segmentation", "buying-signal-prioritization", "multi-channel-sequence-fit", "revops-feedback-loop"],
3701
+ "agency-founder-led": ["net-new-suppressed-list", "signal-led-copy-approval", "table-workflow-handoff", "icp-persona-segmentation", "buying-signal-prioritization", "multi-channel-sequence-fit", "revops-feedback-loop"],
3702
+ "cloud-infrastructure-displacement": ["proof-first-vertical-gate", "domain-first-recovery", "signal-led-copy-approval", "icp-persona-segmentation", "buying-signal-prioritization", "multi-channel-sequence-fit", "revops-feedback-loop"]
3329
3703
  };
3330
3704
  var CAMPAIGN_AGENT_HELP = `signaliz campaign-agent <command> [options]
3331
3705
 
@@ -4227,6 +4601,7 @@ function printCampaignAgentBuildStatus(status) {
4227
4601
  if (status.campaignId) console.log(`Campaign ID: ${status.campaignId}`);
4228
4602
  console.log(`Status: ${status.status}`);
4229
4603
  console.log(`Phase: ${status.currentPhase || "N/A"}`);
4604
+ if (status.terminalState?.kind) console.log(`Terminal: ${status.terminalState.kind}`);
4230
4605
  console.log(`Rows: ${status.recordsSucceeded}/${status.recordsTotal} succeeded, ${status.recordsFailed} failed`);
4231
4606
  printCampaignCustomerRowCounts(status);
4232
4607
  console.log(`Artifacts: ${status.artifactCount}`);
@@ -4260,11 +4635,13 @@ function printCampaignAgentBuildReview(review) {
4260
4635
  console.log(`Campaign: ${status.name || review.campaignBuildId}`);
4261
4636
  console.log(`Status: ${summary.status || status.status}`);
4262
4637
  console.log(`Phase: ${summary.phase || status.currentPhase || "N/A"}`);
4638
+ console.log(`Terminal state: ${summary.terminalState || status.terminalState?.kind || "unknown"}`);
4263
4639
  console.log(`Rows: ${summary.sampledRows || 0} sampled, ${summary.availableRows || 0} available`);
4264
4640
  console.log(`Qualified: ${summary.qualifiedRows || 0}`);
4265
4641
  console.log(`Emails: ${summary.rowsWithEmail || 0}`);
4266
4642
  console.log(`Copy ready: ${summary.copyReadyRows || 0}`);
4267
4643
  console.log(`Artifacts: ${summary.artifactCount || 0}`);
4644
+ printCampaignAgentNorthStarScorecard(review.northStarScorecard);
4268
4645
  if (review.campaignBuildId) printCampaignArtifactDownloadHint(status, review.campaignBuildId);
4269
4646
  console.log(`Delivery approval ready: ${summary.readyForDeliveryApproval === true ? "yes" : "no"}`);
4270
4647
  const gates = Array.isArray(review.gates) ? review.gates : [];
@@ -4285,6 +4662,21 @@ function printCampaignAgentBuildReview(review) {
4285
4662
  for (const action of nextActions) console.log(`- ${action}`);
4286
4663
  }
4287
4664
  }
4665
+ function printCampaignAgentNorthStarScorecard(scorecard) {
4666
+ const card = record(scorecard);
4667
+ if (!card.version) return;
4668
+ const score = card.score === null || card.score === void 0 ? "pending" : `${card.score}%`;
4669
+ console.log(`North Star: ${String(card.status || "unknown")} (${score})`);
4670
+ if (card.moatState) console.log(`Moat state: ${card.moatState}`);
4671
+ const metrics = Array.isArray(card.metrics) ? card.metrics : [];
4672
+ if (metrics.length) {
4673
+ console.log("\nNorth Star gates:");
4674
+ for (const metric of metrics.slice(0, 12)) {
4675
+ const item = record(metric);
4676
+ console.log(`- ${String(item.status || "review").toUpperCase()} ${item.label || item.id}: ${item.detail || ""}`);
4677
+ }
4678
+ }
4679
+ }
4288
4680
  function createCampaignAgentBuildReview(status, rows, artifacts) {
4289
4681
  const rowList = Array.isArray(rows?.rows) ? rows.rows : [];
4290
4682
  const sampledRows = rowList.length;
@@ -7113,8 +7505,15 @@ async function main() {
7113
7505
  return tools();
7114
7506
  case "discover":
7115
7507
  return discover(rest);
7508
+ case "public-data":
7509
+ case "ground-truth":
7510
+ case "public-ground-truth":
7511
+ return publicData(rest[0], rest.slice(1));
7116
7512
  case "ai":
7117
7513
  return ai(rest[0], rest.slice(1));
7514
+ case "signal":
7515
+ case "signals":
7516
+ return signals(rest[0], rest.slice(1));
7118
7517
  case "lead":
7119
7518
  case "leads":
7120
7519
  return lead(rest[0], rest.slice(1));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@signaliz/cli",
3
- "version": "1.0.17",
3
+ "version": "1.0.19",
4
4
  "description": "Signaliz CLI — GTM Kernel, Nango routes, MCP, and enrichment operations.",
5
5
  "bin": {
6
6
  "signaliz": "dist/bin.js"