@agent-pattern-labs/leads-rig 0.1.4 → 0.1.5
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/.codex/config.toml +1 -1
- package/.cursor/rules/main.mdc +2 -2
- package/AGENTS.md +2 -2
- package/CLAUDE.md +2 -2
- package/README.md +3 -1
- package/bin/lead-harness.mjs +75 -75
- package/docs/ARCHITECTURE.md +3 -3
- package/docs/CONSTRUCTION.md +1 -1
- package/iso/instructions.md +2 -2
- package/lib/leadharness-crawler.mjs +2 -2
- package/lib/leadharness-leads.mjs +61 -0
- package/modes/_shared.md +2 -1
- package/modes/batch.md +1 -1
- package/package.json +25 -25
- package/scripts/batch-orchestrator.mjs +1 -1
package/.codex/config.toml
CHANGED
package/.cursor/rules/main.mdc
CHANGED
|
@@ -9,7 +9,7 @@ Agentic public-web lead discovery harness. It crawls company domains, extracts r
|
|
|
9
9
|
|
|
10
10
|
## Hard Limits
|
|
11
11
|
|
|
12
|
-
- [H1] Only collect public-source business contact data. Do not infer, guess, brute-force, or enrich private email addresses. Every lead needs a source URL and evidence excerpt.
|
|
12
|
+
- [H1] Only collect public-source business contact data. Do not infer, guess, brute-force, or enrich private email addresses. Every lead needs a source URL and evidence excerpt. The primary goal is named people, not generic company inboxes.
|
|
13
13
|
- [H2] Respect robots.txt, terms, rate limits, and site intent. If a page blocks crawling or is not public, record a warning and move on.
|
|
14
14
|
- [H3] Never send outreach from this harness. The output is reviewable lead records and ingest-ready artifacts, not email campaigns.
|
|
15
15
|
- [H4] Use file-backed artifacts before ingest. A subagent prose summary is not enough; write validated JSON/JSONL first, then ingest.
|
|
@@ -23,7 +23,7 @@ Agentic public-web lead discovery harness. It crawls company domains, extracts r
|
|
|
23
23
|
- [D2] Use deterministic local helpers when they can validate, normalize, manifest, preflight, postflight, or ingest artifacts.
|
|
24
24
|
- [D3] Read the active mode file before executing. Mode files own output shapes and gates.
|
|
25
25
|
- [D4] Prefer official company websites and high-signal pages: home, contact, about, team, leadership, people, press, blog, careers, and legal pages.
|
|
26
|
-
- [D5] Keep artifacts source-backed. Every emitted lead should include `domain`, `sourceUrl`, `evidence`, `emailType`, `extractionMethod`, `verificationStatus`, and `confidence`.
|
|
26
|
+
- [D5] Keep artifacts source-backed. Every emitted lead should include `domain`, `sourceUrl`, `evidence`, `emailType`, `extractionMethod`, `verificationStatus`, and `confidence`. Reject generic catch-all inboxes such as `info@`, `hello@`, `contact@`, `support@`, `team@`, and similar aliases; if no named person is available, prefer a `contact_path` record instead.
|
|
27
27
|
|
|
28
28
|
## Procedure
|
|
29
29
|
|
package/AGENTS.md
CHANGED
|
@@ -4,7 +4,7 @@ Agentic public-web lead discovery harness. It crawls company domains, extracts r
|
|
|
4
4
|
|
|
5
5
|
## Hard Limits
|
|
6
6
|
|
|
7
|
-
- [H1] Only collect public-source business contact data. Do not infer, guess, brute-force, or enrich private email addresses. Every lead needs a source URL and evidence excerpt.
|
|
7
|
+
- [H1] Only collect public-source business contact data. Do not infer, guess, brute-force, or enrich private email addresses. Every lead needs a source URL and evidence excerpt. The primary goal is named people, not generic company inboxes.
|
|
8
8
|
- [H2] Respect robots.txt, terms, rate limits, and site intent. If a page blocks crawling or is not public, record a warning and move on.
|
|
9
9
|
- [H3] Never send outreach from this harness. The output is reviewable lead records and ingest-ready artifacts, not email campaigns.
|
|
10
10
|
- [H4] Use file-backed artifacts before ingest. A subagent prose summary is not enough; write validated JSON/JSONL first, then ingest.
|
|
@@ -18,7 +18,7 @@ Agentic public-web lead discovery harness. It crawls company domains, extracts r
|
|
|
18
18
|
- [D2] Use deterministic local helpers when they can validate, normalize, manifest, preflight, postflight, or ingest artifacts.
|
|
19
19
|
- [D3] Read the active mode file before executing. Mode files own output shapes and gates.
|
|
20
20
|
- [D4] Prefer official company websites and high-signal pages: home, contact, about, team, leadership, people, press, blog, careers, and legal pages.
|
|
21
|
-
- [D5] Keep artifacts source-backed. Every emitted lead should include `domain`, `sourceUrl`, `evidence`, `emailType`, `extractionMethod`, `verificationStatus`, and `confidence`.
|
|
21
|
+
- [D5] Keep artifacts source-backed. Every emitted lead should include `domain`, `sourceUrl`, `evidence`, `emailType`, `extractionMethod`, `verificationStatus`, and `confidence`. Reject generic catch-all inboxes such as `info@`, `hello@`, `contact@`, `support@`, `team@`, and similar aliases; if no named person is available, prefer a `contact_path` record instead.
|
|
22
22
|
|
|
23
23
|
## Procedure
|
|
24
24
|
|
package/CLAUDE.md
CHANGED
|
@@ -4,7 +4,7 @@ Agentic public-web lead discovery harness. It crawls company domains, extracts r
|
|
|
4
4
|
|
|
5
5
|
## Hard Limits
|
|
6
6
|
|
|
7
|
-
- [H1] Only collect public-source business contact data. Do not infer, guess, brute-force, or enrich private email addresses. Every lead needs a source URL and evidence excerpt.
|
|
7
|
+
- [H1] Only collect public-source business contact data. Do not infer, guess, brute-force, or enrich private email addresses. Every lead needs a source URL and evidence excerpt. The primary goal is named people, not generic company inboxes.
|
|
8
8
|
- [H2] Respect robots.txt, terms, rate limits, and site intent. If a page blocks crawling or is not public, record a warning and move on.
|
|
9
9
|
- [H3] Never send outreach from this harness. The output is reviewable lead records and ingest-ready artifacts, not email campaigns.
|
|
10
10
|
- [H4] Use file-backed artifacts before ingest. A subagent prose summary is not enough; write validated JSON/JSONL first, then ingest.
|
|
@@ -18,7 +18,7 @@ Agentic public-web lead discovery harness. It crawls company domains, extracts r
|
|
|
18
18
|
- [D2] Use deterministic local helpers when they can validate, normalize, manifest, preflight, postflight, or ingest artifacts.
|
|
19
19
|
- [D3] Read the active mode file before executing. Mode files own output shapes and gates.
|
|
20
20
|
- [D4] Prefer official company websites and high-signal pages: home, contact, about, team, leadership, people, press, blog, careers, and legal pages.
|
|
21
|
-
- [D5] Keep artifacts source-backed. Every emitted lead should include `domain`, `sourceUrl`, `evidence`, `emailType`, `extractionMethod`, `verificationStatus`, and `confidence`.
|
|
21
|
+
- [D5] Keep artifacts source-backed. Every emitted lead should include `domain`, `sourceUrl`, `evidence`, `emailType`, `extractionMethod`, `verificationStatus`, and `confidence`. Reject generic catch-all inboxes such as `info@`, `hello@`, `contact@`, `support@`, `team@`, and similar aliases; if no named person is available, prefer a `contact_path` record instead.
|
|
22
22
|
|
|
23
23
|
## Procedure
|
|
24
24
|
|
package/README.md
CHANGED
|
@@ -6,11 +6,13 @@ Agentic public-web lead discovery harness for portable, source-backed lead artif
|
|
|
6
6
|
|
|
7
7
|
- Crawls public company websites through agent workflows.
|
|
8
8
|
- Produces source-backed lead artifacts matching the local lead-ingest contract.
|
|
9
|
-
- Validates lead JSON/JSONL before database handoff
|
|
9
|
+
- Validates lead JSON/JSONL before database handoff and rejects generic catch-all inboxes such as `info@` and `hello@`.
|
|
10
10
|
- Builds a local manifest for batch auditability.
|
|
11
11
|
- Submits validated payloads to a configured ingest API.
|
|
12
12
|
- Ships OpenCode, Claude Code, Cursor, Codex, and Pi harness config from one `iso/` source.
|
|
13
13
|
|
|
14
|
+
The harness is optimized to find named people who can actually be contacted. When only a general company contact path exists, it should emit a `contact_path` record rather than a generic organizational inbox.
|
|
15
|
+
|
|
14
16
|
## Core Commands
|
|
15
17
|
|
|
16
18
|
```bash
|
package/bin/lead-harness.mjs
CHANGED
|
@@ -23,80 +23,80 @@ const commands = {
|
|
|
23
23
|
};
|
|
24
24
|
|
|
25
25
|
const helperAliases = {
|
|
26
|
-
'trace:list': ['@
|
|
27
|
-
'trace:stats': ['@
|
|
28
|
-
'trace:show': ['@
|
|
29
|
-
'guard:audit': ['@
|
|
30
|
-
'guard:explain': ['@
|
|
31
|
-
'ledger:status': ['@
|
|
32
|
-
'ledger:rebuild': ['@
|
|
33
|
-
'ledger:verify': ['@
|
|
34
|
-
'capabilities:list': ['@
|
|
35
|
-
'capabilities:explain': ['@
|
|
36
|
-
'capabilities:check': ['@
|
|
37
|
-
'context:list': ['@
|
|
38
|
-
'context:explain': ['@
|
|
39
|
-
'context:plan': ['@
|
|
40
|
-
'context:check': ['@
|
|
41
|
-
'context:render': ['@
|
|
42
|
-
'cache:key': ['@
|
|
43
|
-
'cache:has': ['@
|
|
44
|
-
'cache:get': ['@
|
|
45
|
-
'cache:put': ['@
|
|
46
|
-
'cache:status': ['@
|
|
47
|
-
'cache:list': ['@
|
|
48
|
-
'cache:verify': ['@
|
|
49
|
-
'cache:prune': ['@
|
|
50
|
-
'index:build': ['@
|
|
51
|
-
'index:status': ['@
|
|
52
|
-
'index:query': ['@
|
|
53
|
-
'index:has': ['@
|
|
54
|
-
'index:verify': ['@
|
|
55
|
-
'facts:build': ['@
|
|
56
|
-
'facts:status': ['@
|
|
57
|
-
'facts:query': ['@
|
|
58
|
-
'facts:has': ['@
|
|
59
|
-
'facts:verify': ['@
|
|
60
|
-
'score:compute': ['@
|
|
61
|
-
'score:verify': ['@
|
|
62
|
-
'score:check': ['@
|
|
63
|
-
'score:gate': ['@
|
|
64
|
-
'score:compare': ['@
|
|
65
|
-
'score:explain': ['@
|
|
66
|
-
'canon:normalize': ['@
|
|
67
|
-
'canon:key': ['@
|
|
68
|
-
'canon:compare': ['@
|
|
69
|
-
'canon:explain': ['@
|
|
70
|
-
'preflight:plan': ['@
|
|
71
|
-
'preflight:check': ['@
|
|
72
|
-
'preflight:explain': ['@
|
|
73
|
-
'postflight:status': ['@
|
|
74
|
-
'postflight:check': ['@
|
|
75
|
-
'postflight:explain': ['@
|
|
76
|
-
'timeline:status': ['@
|
|
77
|
-
'timeline:build': ['@
|
|
78
|
-
'timeline:plan': ['@
|
|
79
|
-
'timeline:due': ['@
|
|
80
|
-
'timeline:check': ['@
|
|
81
|
-
'timeline:verify': ['@
|
|
82
|
-
'prioritize:status': ['@
|
|
83
|
-
'prioritize:items': ['@
|
|
84
|
-
'prioritize:build': ['@
|
|
85
|
-
'prioritize:rank': ['@
|
|
86
|
-
'prioritize:select': ['@
|
|
87
|
-
'prioritize:check': ['@
|
|
88
|
-
'prioritize:verify': ['@
|
|
89
|
-
'lineage:status': ['@
|
|
90
|
-
'lineage:record': ['@
|
|
91
|
-
'lineage:check': ['@
|
|
92
|
-
'lineage:stale': ['@
|
|
93
|
-
'lineage:verify': ['@
|
|
94
|
-
'redact:scan': ['@
|
|
95
|
-
'redact:verify': ['@
|
|
96
|
-
'redact:apply': ['@
|
|
97
|
-
'migrate:plan': ['@
|
|
98
|
-
'migrate:apply': ['@
|
|
99
|
-
'migrate:check': ['@
|
|
26
|
+
'trace:list': ['@agent-pattern-labs/iso-trace', 'list'],
|
|
27
|
+
'trace:stats': ['@agent-pattern-labs/iso-trace', 'stats'],
|
|
28
|
+
'trace:show': ['@agent-pattern-labs/iso-trace', 'show'],
|
|
29
|
+
'guard:audit': ['@agent-pattern-labs/iso-guard', 'audit'],
|
|
30
|
+
'guard:explain': ['@agent-pattern-labs/iso-guard', 'explain'],
|
|
31
|
+
'ledger:status': ['@agent-pattern-labs/iso-ledger', 'status'],
|
|
32
|
+
'ledger:rebuild': ['@agent-pattern-labs/iso-ledger', 'rebuild'],
|
|
33
|
+
'ledger:verify': ['@agent-pattern-labs/iso-ledger', 'verify'],
|
|
34
|
+
'capabilities:list': ['@agent-pattern-labs/iso-capabilities', 'list'],
|
|
35
|
+
'capabilities:explain': ['@agent-pattern-labs/iso-capabilities', 'explain'],
|
|
36
|
+
'capabilities:check': ['@agent-pattern-labs/iso-capabilities', 'check'],
|
|
37
|
+
'context:list': ['@agent-pattern-labs/iso-context', 'list'],
|
|
38
|
+
'context:explain': ['@agent-pattern-labs/iso-context', 'explain'],
|
|
39
|
+
'context:plan': ['@agent-pattern-labs/iso-context', 'plan'],
|
|
40
|
+
'context:check': ['@agent-pattern-labs/iso-context', 'check'],
|
|
41
|
+
'context:render': ['@agent-pattern-labs/iso-context', 'render'],
|
|
42
|
+
'cache:key': ['@agent-pattern-labs/iso-cache', 'key'],
|
|
43
|
+
'cache:has': ['@agent-pattern-labs/iso-cache', 'has'],
|
|
44
|
+
'cache:get': ['@agent-pattern-labs/iso-cache', 'get'],
|
|
45
|
+
'cache:put': ['@agent-pattern-labs/iso-cache', 'put'],
|
|
46
|
+
'cache:status': ['@agent-pattern-labs/iso-cache', 'status'],
|
|
47
|
+
'cache:list': ['@agent-pattern-labs/iso-cache', 'list'],
|
|
48
|
+
'cache:verify': ['@agent-pattern-labs/iso-cache', 'verify'],
|
|
49
|
+
'cache:prune': ['@agent-pattern-labs/iso-cache', 'prune'],
|
|
50
|
+
'index:build': ['@agent-pattern-labs/iso-index', 'build'],
|
|
51
|
+
'index:status': ['@agent-pattern-labs/iso-index', 'status'],
|
|
52
|
+
'index:query': ['@agent-pattern-labs/iso-index', 'query'],
|
|
53
|
+
'index:has': ['@agent-pattern-labs/iso-index', 'has'],
|
|
54
|
+
'index:verify': ['@agent-pattern-labs/iso-index', 'verify'],
|
|
55
|
+
'facts:build': ['@agent-pattern-labs/iso-facts', 'build'],
|
|
56
|
+
'facts:status': ['@agent-pattern-labs/iso-facts', 'status'],
|
|
57
|
+
'facts:query': ['@agent-pattern-labs/iso-facts', 'query'],
|
|
58
|
+
'facts:has': ['@agent-pattern-labs/iso-facts', 'has'],
|
|
59
|
+
'facts:verify': ['@agent-pattern-labs/iso-facts', 'verify'],
|
|
60
|
+
'score:compute': ['@agent-pattern-labs/iso-score', 'compute'],
|
|
61
|
+
'score:verify': ['@agent-pattern-labs/iso-score', 'verify'],
|
|
62
|
+
'score:check': ['@agent-pattern-labs/iso-score', 'check'],
|
|
63
|
+
'score:gate': ['@agent-pattern-labs/iso-score', 'gate'],
|
|
64
|
+
'score:compare': ['@agent-pattern-labs/iso-score', 'compare'],
|
|
65
|
+
'score:explain': ['@agent-pattern-labs/iso-score', 'explain'],
|
|
66
|
+
'canon:normalize': ['@agent-pattern-labs/iso-canon', 'normalize'],
|
|
67
|
+
'canon:key': ['@agent-pattern-labs/iso-canon', 'key'],
|
|
68
|
+
'canon:compare': ['@agent-pattern-labs/iso-canon', 'compare'],
|
|
69
|
+
'canon:explain': ['@agent-pattern-labs/iso-canon', 'explain'],
|
|
70
|
+
'preflight:plan': ['@agent-pattern-labs/iso-preflight', 'plan'],
|
|
71
|
+
'preflight:check': ['@agent-pattern-labs/iso-preflight', 'check'],
|
|
72
|
+
'preflight:explain': ['@agent-pattern-labs/iso-preflight', 'explain'],
|
|
73
|
+
'postflight:status': ['@agent-pattern-labs/iso-postflight', 'status'],
|
|
74
|
+
'postflight:check': ['@agent-pattern-labs/iso-postflight', 'check'],
|
|
75
|
+
'postflight:explain': ['@agent-pattern-labs/iso-postflight', 'explain'],
|
|
76
|
+
'timeline:status': ['@agent-pattern-labs/iso-timeline', 'status'],
|
|
77
|
+
'timeline:build': ['@agent-pattern-labs/iso-timeline', 'build'],
|
|
78
|
+
'timeline:plan': ['@agent-pattern-labs/iso-timeline', 'plan'],
|
|
79
|
+
'timeline:due': ['@agent-pattern-labs/iso-timeline', 'due'],
|
|
80
|
+
'timeline:check': ['@agent-pattern-labs/iso-timeline', 'check'],
|
|
81
|
+
'timeline:verify': ['@agent-pattern-labs/iso-timeline', 'verify'],
|
|
82
|
+
'prioritize:status': ['@agent-pattern-labs/iso-prioritize', 'status'],
|
|
83
|
+
'prioritize:items': ['@agent-pattern-labs/iso-prioritize', 'items'],
|
|
84
|
+
'prioritize:build': ['@agent-pattern-labs/iso-prioritize', 'build'],
|
|
85
|
+
'prioritize:rank': ['@agent-pattern-labs/iso-prioritize', 'rank'],
|
|
86
|
+
'prioritize:select': ['@agent-pattern-labs/iso-prioritize', 'select'],
|
|
87
|
+
'prioritize:check': ['@agent-pattern-labs/iso-prioritize', 'check'],
|
|
88
|
+
'prioritize:verify': ['@agent-pattern-labs/iso-prioritize', 'verify'],
|
|
89
|
+
'lineage:status': ['@agent-pattern-labs/iso-lineage', 'status'],
|
|
90
|
+
'lineage:record': ['@agent-pattern-labs/iso-lineage', 'record'],
|
|
91
|
+
'lineage:check': ['@agent-pattern-labs/iso-lineage', 'check'],
|
|
92
|
+
'lineage:stale': ['@agent-pattern-labs/iso-lineage', 'stale'],
|
|
93
|
+
'lineage:verify': ['@agent-pattern-labs/iso-lineage', 'verify'],
|
|
94
|
+
'redact:scan': ['@agent-pattern-labs/iso-redact', 'scan'],
|
|
95
|
+
'redact:verify': ['@agent-pattern-labs/iso-redact', 'verify'],
|
|
96
|
+
'redact:apply': ['@agent-pattern-labs/iso-redact', 'apply'],
|
|
97
|
+
'migrate:plan': ['@agent-pattern-labs/iso-migrate', 'plan'],
|
|
98
|
+
'migrate:apply': ['@agent-pattern-labs/iso-migrate', 'apply'],
|
|
99
|
+
'migrate:check': ['@agent-pattern-labs/iso-migrate', 'check'],
|
|
100
100
|
};
|
|
101
101
|
|
|
102
102
|
const [, , cmd, ...rest] = process.argv;
|
|
@@ -161,7 +161,7 @@ function runIsoCli(pkgName, args) {
|
|
|
161
161
|
|
|
162
162
|
function defaultIsoArgs(pkgName, subcommand, args) {
|
|
163
163
|
const defaults = [];
|
|
164
|
-
if (pkgName === '@
|
|
164
|
+
if (pkgName === '@agent-pattern-labs/iso-trace') {
|
|
165
165
|
if (!hasFlag(args, '--cwd')) defaults.push('--cwd', PROJECT_DIR);
|
|
166
166
|
if ((subcommand === 'list' || subcommand === 'stats') && !hasFlag(args, '--since')) {
|
|
167
167
|
defaults.push('--since', '7d');
|
package/docs/ARCHITECTURE.md
CHANGED
|
@@ -68,10 +68,10 @@ domain input
|
|
|
68
68
|
| `scripts/validate-leads.mjs` | Normalizes and validates JSON/JSONL lead artifacts |
|
|
69
69
|
| `scripts/manifest.mjs` | Records validated batches in `data/lead-manifest.json` |
|
|
70
70
|
| `scripts/ingest.mjs` | Posts to the configured ingest endpoint |
|
|
71
|
-
| `scripts/batch-orchestrator.mjs` | Durable batch runner using `@
|
|
71
|
+
| `scripts/batch-orchestrator.mjs` | Durable batch runner using `@agent-pattern-labs/iso-orchestrator` |
|
|
72
72
|
| `verify-pipeline.mjs` | Repo/consumer health gate |
|
|
73
73
|
|
|
74
|
-
The package also carries the `@
|
|
74
|
+
The package also carries the `@agent-pattern-labs/iso-*` helper ecosystem for trace, guard, ledger, cache, canon, context, preflight, postflight, prioritize, lineage, redact, migrate, score, and timeline commands.
|
|
75
75
|
|
|
76
76
|
## Data Contract
|
|
77
77
|
|
|
@@ -82,4 +82,4 @@ The package also carries the `@razroo/iso-*` helper ecosystem for trace, guard,
|
|
|
82
82
|
- page visits
|
|
83
83
|
- ingest requests
|
|
84
84
|
|
|
85
|
-
The validator intentionally accepts the same defaults the local runtime normalizes, but it fails missing source evidence, invalid URL fields, invalid `emailType`, invalid `verificationStatus`, invalid confidence,
|
|
85
|
+
The validator intentionally accepts the same defaults the local runtime normalizes, but it fails missing source evidence, invalid URL fields, invalid `emailType`, invalid `verificationStatus`, invalid confidence, person/role leads without email, and generic catch-all inboxes such as `info@`, `hello@`, or similar organizational aliases.
|
package/docs/CONSTRUCTION.md
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
| Deterministic helpers | `scripts/crawl.mjs`, `scripts/pipeline.mjs`, `scripts/validate-leads.mjs`, `scripts/manifest.mjs`, `scripts/ingest.mjs`, `scripts/batch-orchestrator.mjs` |
|
|
15
15
|
| Consumer scaffold | `create-public-leads-harness <dir>` writes local config, input, output, and ignore files |
|
|
16
16
|
| Consumer sync | `public-leads sync` symlinks shared harness files from the installed package |
|
|
17
|
-
| Batch orchestration | `batch/batch-runner.sh` delegates to `@
|
|
17
|
+
| Batch orchestration | `batch/batch-runner.sh` delegates to `@agent-pattern-labs/iso-orchestrator` with bounded parallelism |
|
|
18
18
|
| Verification gates | `npm run verify`, `npm run smoke:iso`, sample validate/manifest/ingest, `npm pack` |
|
|
19
19
|
|
|
20
20
|
## Maintainer Rules
|
package/iso/instructions.md
CHANGED
|
@@ -4,7 +4,7 @@ Agentic public-web lead discovery harness. It crawls company domains, extracts r
|
|
|
4
4
|
|
|
5
5
|
## Hard Limits
|
|
6
6
|
|
|
7
|
-
- [H1] Only collect public-source business contact data. Do not infer, guess, brute-force, or enrich private email addresses. Every lead needs a source URL and evidence excerpt.
|
|
7
|
+
- [H1] Only collect public-source business contact data. Do not infer, guess, brute-force, or enrich private email addresses. Every lead needs a source URL and evidence excerpt. The primary goal is named people, not generic company inboxes.
|
|
8
8
|
- [H2] Respect robots.txt, terms, rate limits, and site intent. If a page blocks crawling or is not public, record a warning and move on.
|
|
9
9
|
- [H3] Never send outreach from this harness. The output is reviewable lead records and ingest-ready artifacts, not email campaigns.
|
|
10
10
|
- [H4] Use file-backed artifacts before ingest. A subagent prose summary is not enough; write validated JSON/JSONL first, then ingest.
|
|
@@ -18,7 +18,7 @@ Agentic public-web lead discovery harness. It crawls company domains, extracts r
|
|
|
18
18
|
- [D2] Use deterministic local helpers when they can validate, normalize, manifest, preflight, postflight, or ingest artifacts.
|
|
19
19
|
- [D3] Read the active mode file before executing. Mode files own output shapes and gates.
|
|
20
20
|
- [D4] Prefer official company websites and high-signal pages: home, contact, about, team, leadership, people, press, blog, careers, and legal pages.
|
|
21
|
-
- [D5] Keep artifacts source-backed. Every emitted lead should include `domain`, `sourceUrl`, `evidence`, `emailType`, `extractionMethod`, `verificationStatus`, and `confidence`.
|
|
21
|
+
- [D5] Keep artifacts source-backed. Every emitted lead should include `domain`, `sourceUrl`, `evidence`, `emailType`, `extractionMethod`, `verificationStatus`, and `confidence`. Reject generic catch-all inboxes such as `info@`, `hello@`, `contact@`, `support@`, `team@`, and similar aliases; if no named person is available, prefer a `contact_path` record instead.
|
|
22
22
|
|
|
23
23
|
## Procedure
|
|
24
24
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createHash } from 'crypto';
|
|
2
2
|
import { lookup, resolveMx } from 'dns/promises';
|
|
3
3
|
import { setTimeout as sleep } from 'timers/promises';
|
|
4
|
-
import { cleanDomains, dedupeLeadRecords, mergeLeadRecords, normalizeDomain } from './leadharness-leads.mjs';
|
|
4
|
+
import { cleanDomains, dedupeLeadRecords, isRejectedLeadEmail, mergeLeadRecords, normalizeDomain } from './leadharness-leads.mjs';
|
|
5
5
|
|
|
6
6
|
const DEFAULT_MAX_PAGES = 10;
|
|
7
7
|
const HARD_MAX_PAGES = 25;
|
|
@@ -366,7 +366,7 @@ function extractEmails(html) {
|
|
|
366
366
|
for (const value of values) {
|
|
367
367
|
for (const found of String(value || '').matchAll(EMAIL_RE)) {
|
|
368
368
|
const email = found[0].toLowerCase().replace(/^mailto:/, '').replace(/[.,;:!?()[\]{}<>"']+$/g, '');
|
|
369
|
-
if (email && !isLikelyAssetEmail(email)) emails.add(email);
|
|
369
|
+
if (email && !isLikelyAssetEmail(email) && !isRejectedLeadEmail(email)) emails.add(email);
|
|
370
370
|
}
|
|
371
371
|
}
|
|
372
372
|
return [...emails].sort();
|
|
@@ -28,6 +28,37 @@ export const ALLOWED_VERIFICATION_STATUSES = new Set([
|
|
|
28
28
|
'blocked',
|
|
29
29
|
'unknown',
|
|
30
30
|
]);
|
|
31
|
+
export const REJECTED_EMAIL_LOCAL_PREFIXES = [
|
|
32
|
+
'admin',
|
|
33
|
+
'booking',
|
|
34
|
+
'bookings',
|
|
35
|
+
'business',
|
|
36
|
+
'contact',
|
|
37
|
+
'customerservice',
|
|
38
|
+
'customer-service',
|
|
39
|
+
'customersuccess',
|
|
40
|
+
'customer-success',
|
|
41
|
+
'events',
|
|
42
|
+
'hello',
|
|
43
|
+
'help',
|
|
44
|
+
'hi',
|
|
45
|
+
'info',
|
|
46
|
+
'inquiries',
|
|
47
|
+
'enquiries',
|
|
48
|
+
'media',
|
|
49
|
+
'office',
|
|
50
|
+
'outreach',
|
|
51
|
+
'partners',
|
|
52
|
+
'partnerships',
|
|
53
|
+
'press',
|
|
54
|
+
'sales',
|
|
55
|
+
'service',
|
|
56
|
+
'services',
|
|
57
|
+
'speaking',
|
|
58
|
+
'success',
|
|
59
|
+
'support',
|
|
60
|
+
'team',
|
|
61
|
+
];
|
|
31
62
|
|
|
32
63
|
const EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
33
64
|
|
|
@@ -232,6 +263,9 @@ export function validatePayload(payload, { allowEmpty = false } = {}) {
|
|
|
232
263
|
|
|
233
264
|
results.forEach((result, index) => {
|
|
234
265
|
validateResult(result, `results[${index}]`, issues);
|
|
266
|
+
result.leads.forEach((lead, leadIndex) => {
|
|
267
|
+
validateLead(lead, `results[${index}].leads[${leadIndex}]`, issues);
|
|
268
|
+
});
|
|
235
269
|
});
|
|
236
270
|
|
|
237
271
|
const errors = issues.filter((item) => item.severity === 'error').length;
|
|
@@ -259,6 +293,9 @@ export function validateLead(lead, path, issues) {
|
|
|
259
293
|
if (lead.email && !EMAIL_RE.test(lead.email)) {
|
|
260
294
|
issues.push(issue('error', `${path}.email`, 'invalid_email', 'email must be a valid email address when present'));
|
|
261
295
|
}
|
|
296
|
+
if (lead.email && isRejectedLeadEmail(lead.email)) {
|
|
297
|
+
issues.push(issue('error', `${path}.email`, 'generic_inbox_rejected', 'generic company inboxes such as info@, hello@, or support@ are not accepted; submit named people instead'));
|
|
298
|
+
}
|
|
262
299
|
if (lead.emailType && !ALLOWED_EMAIL_TYPES.has(lead.emailType)) {
|
|
263
300
|
issues.push(issue('error', `${path}.emailType`, 'invalid_email_type', `emailType must be one of ${[...ALLOWED_EMAIL_TYPES].join(', ')}`));
|
|
264
301
|
}
|
|
@@ -685,6 +722,30 @@ export function fileHash(path) {
|
|
|
685
722
|
return createHash('sha256').update(readFileSync(path)).digest('hex');
|
|
686
723
|
}
|
|
687
724
|
|
|
725
|
+
export function isRejectedLeadEmail(email) {
|
|
726
|
+
const local = String(email || '').trim().toLowerCase().split('@')[0] || '';
|
|
727
|
+
return isRejectedLeadLocalPart(local);
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
export function isRejectedLeadLocalPart(local) {
|
|
731
|
+
const value = normalizeRejectedLeadLocalPart(local);
|
|
732
|
+
if (!value) return false;
|
|
733
|
+
return REJECTED_EMAIL_LOCAL_PREFIXES.some((prefix) => {
|
|
734
|
+
if (value === prefix) return true;
|
|
735
|
+
if (!value.startsWith(prefix) || value.length === prefix.length) return false;
|
|
736
|
+
const next = value[prefix.length];
|
|
737
|
+
return next === '.' || next === '_' || next === '-' || next === '+' || /\d/.test(next);
|
|
738
|
+
});
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
function normalizeRejectedLeadLocalPart(local) {
|
|
742
|
+
let value = String(local || '').trim().toLowerCase();
|
|
743
|
+
for (const noise of ['mailto', 'u003e', 'email']) {
|
|
744
|
+
if (value.startsWith(noise)) value = value.slice(noise.length);
|
|
745
|
+
}
|
|
746
|
+
return value.replace(/^[>._+-]+/, '');
|
|
747
|
+
}
|
|
748
|
+
|
|
688
749
|
export function issue(severity, path, code, message) {
|
|
689
750
|
return { severity, path, code, message };
|
|
690
751
|
}
|
package/modes/_shared.md
CHANGED
|
@@ -28,13 +28,14 @@ Required fields:
|
|
|
28
28
|
- Prioritize home, contact, about, team, leadership, people, press, blog, careers, and legal pages.
|
|
29
29
|
- Respect robots.txt, paywalls, login walls, and obvious anti-scraping notices.
|
|
30
30
|
- Do not infer email formats. Do not brute-force guessed addresses.
|
|
31
|
+
- Prefer named people over organizational inboxes. Do not emit generic catch-all emails such as `info@`, `hello@`, `contact@`, `support@`, `team@`, or similar aliases. If only a general contact path exists, emit `contact_path` instead.
|
|
31
32
|
- Keep contact forms as `emailType: "contact_path"` with empty `email`.
|
|
32
33
|
- Mark operational no-contact inboxes such as `noreply`, `abuse`, `security`, and `legal` as `blocked` with confidence `0`.
|
|
33
34
|
|
|
34
35
|
## Quality Heuristics
|
|
35
36
|
|
|
36
37
|
- `person`: named person or person-like email with source context.
|
|
37
|
-
- `role`: public role inbox
|
|
38
|
+
- `role`: public role inbox only when it is clearly role-specific and not a generic catch-all alias.
|
|
38
39
|
- `contact_path`: public form or page where no email is available.
|
|
39
40
|
- `blocked`: public address that should not be used for outbound.
|
|
40
41
|
|
package/modes/batch.md
CHANGED
|
@@ -10,7 +10,7 @@ Prefer:
|
|
|
10
10
|
batch/batch-runner.sh --parallel 2
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
The runner delegates to `scripts/batch-orchestrator.mjs`, which uses `@
|
|
13
|
+
The runner delegates to `scripts/batch-orchestrator.mjs`, which uses `@agent-pattern-labs/iso-orchestrator` for durable workflow records in `.public-leads-runs/`, bounded fan-out, and state updates.
|
|
14
14
|
|
|
15
15
|
## Input
|
|
16
16
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-pattern-labs/leads-rig",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "Agentic public-web lead discovery harness with portable ingest artifacts",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"author": "Razroo",
|
|
@@ -155,32 +155,32 @@
|
|
|
155
155
|
"fast-uri": "3.1.2"
|
|
156
156
|
},
|
|
157
157
|
"dependencies": {
|
|
158
|
-
"@
|
|
159
|
-
"@
|
|
160
|
-
"@
|
|
161
|
-
"@
|
|
162
|
-
"@
|
|
163
|
-
"@
|
|
164
|
-
"@
|
|
165
|
-
"@
|
|
166
|
-
"@
|
|
167
|
-
"@
|
|
168
|
-
"@
|
|
169
|
-
"@
|
|
170
|
-
"@
|
|
171
|
-
"@
|
|
172
|
-
"@
|
|
173
|
-
"@
|
|
174
|
-
"@
|
|
175
|
-
"@
|
|
176
|
-
"@
|
|
158
|
+
"@agent-pattern-labs/iso-cache": "^0.1.1",
|
|
159
|
+
"@agent-pattern-labs/iso-canon": "^0.1.1",
|
|
160
|
+
"@agent-pattern-labs/iso-capabilities": "^0.1.1",
|
|
161
|
+
"@agent-pattern-labs/iso-context": "^0.1.1",
|
|
162
|
+
"@agent-pattern-labs/iso-contract": "^0.1.1",
|
|
163
|
+
"@agent-pattern-labs/iso-facts": "^0.1.1",
|
|
164
|
+
"@agent-pattern-labs/iso-guard": "^0.1.1",
|
|
165
|
+
"@agent-pattern-labs/iso-index": "^0.1.1",
|
|
166
|
+
"@agent-pattern-labs/iso-ledger": "^0.1.1",
|
|
167
|
+
"@agent-pattern-labs/iso-lineage": "^0.1.1",
|
|
168
|
+
"@agent-pattern-labs/iso-migrate": "^0.1.1",
|
|
169
|
+
"@agent-pattern-labs/iso-orchestrator": "^0.2.1",
|
|
170
|
+
"@agent-pattern-labs/iso-postflight": "^0.1.1",
|
|
171
|
+
"@agent-pattern-labs/iso-preflight": "^0.1.1",
|
|
172
|
+
"@agent-pattern-labs/iso-prioritize": "^0.1.1",
|
|
173
|
+
"@agent-pattern-labs/iso-redact": "^0.1.1",
|
|
174
|
+
"@agent-pattern-labs/iso-score": "^0.1.1",
|
|
175
|
+
"@agent-pattern-labs/iso-timeline": "^0.1.1",
|
|
176
|
+
"@agent-pattern-labs/iso-trace": "^0.5.1",
|
|
177
177
|
"playwright": "^1.58.1"
|
|
178
178
|
},
|
|
179
179
|
"devDependencies": {
|
|
180
|
-
"@
|
|
181
|
-
"@
|
|
182
|
-
"@
|
|
183
|
-
"@
|
|
184
|
-
"@
|
|
180
|
+
"@agent-pattern-labs/agentmd": "^0.3.1",
|
|
181
|
+
"@agent-pattern-labs/iso": "^0.3.2",
|
|
182
|
+
"@agent-pattern-labs/iso-eval": "^0.4.1",
|
|
183
|
+
"@agent-pattern-labs/iso-harness": "^0.8.1",
|
|
184
|
+
"@agent-pattern-labs/iso-route": "^0.6.1"
|
|
185
185
|
}
|
|
186
186
|
}
|
|
@@ -6,7 +6,7 @@ import { existsSync } from 'fs';
|
|
|
6
6
|
import { mkdir, readFile, rm, writeFile } from 'fs/promises';
|
|
7
7
|
import { dirname, join, resolve } from 'path';
|
|
8
8
|
import { fileURLToPath } from 'url';
|
|
9
|
-
import { runWorkflow } from '@
|
|
9
|
+
import { runWorkflow } from '@agent-pattern-labs/iso-orchestrator';
|
|
10
10
|
|
|
11
11
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
12
12
|
const PKG_ROOT = resolve(__dirname, '..');
|