@agent-pattern-labs/leads-rig 0.1.3
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/.claude/agents/general-free.md +39 -0
- package/.claude/agents/general-paid.md +20 -0
- package/.claude/agents/glm-minimal.md +9 -0
- package/.claude/iso-route.resolved.json +21 -0
- package/.claude/settings.json +3 -0
- package/.codex/config.toml +24 -0
- package/.cursor/iso-route.md +17 -0
- package/.cursor/mcp.json +19 -0
- package/.cursor/rules/agent-general-free.mdc +38 -0
- package/.cursor/rules/agent-general-paid.mdc +19 -0
- package/.cursor/rules/agent-glm-minimal.mdc +8 -0
- package/.cursor/rules/main.mdc +61 -0
- package/.mcp.json +19 -0
- package/.opencode/agents/general-free.md +48 -0
- package/.opencode/agents/general-paid.md +29 -0
- package/.opencode/agents/glm-minimal.md +19 -0
- package/.opencode/instructions.md +3 -0
- package/.opencode/skills/lead-harness.md +56 -0
- package/.opencode/skills/public-leads.md +56 -0
- package/.pi/prompts/lead-harness.md +54 -0
- package/.pi/prompts/public-leads.md +54 -0
- package/.pi/skills/general-free/SKILL.md +38 -0
- package/.pi/skills/general-paid/SKILL.md +19 -0
- package/.pi/skills/glm-minimal/SKILL.md +8 -0
- package/AGENTS.md +56 -0
- package/CLAUDE.md +56 -0
- package/LICENSE +21 -0
- package/README.md +61 -0
- package/batch/README.md +37 -0
- package/batch/batch-prompt.md +19 -0
- package/batch/batch-runner.sh +18 -0
- package/bin/create-leads-harness.mjs +178 -0
- package/bin/lead-harness.mjs +201 -0
- package/bin/sync.mjs +150 -0
- package/config/profile.example.yml +34 -0
- package/docs/ARCHITECTURE.md +85 -0
- package/docs/CONSTRUCTION.md +40 -0
- package/docs/README.md +5 -0
- package/docs/SETUP.md +89 -0
- package/examples/README.md +9 -0
- package/examples/sample-leads.json +57 -0
- package/iso/agents/general-free.md +50 -0
- package/iso/agents/general-paid.md +31 -0
- package/iso/agents/glm-minimal.md +21 -0
- package/iso/commands/lead-harness.md +59 -0
- package/iso/commands/public-leads.md +59 -0
- package/iso/config.json +11 -0
- package/iso/instructions.md +56 -0
- package/iso/instructions.opencode.md +3 -0
- package/iso/mcp.json +16 -0
- package/lib/leadharness-crawler.mjs +911 -0
- package/lib/leadharness-ingest.mjs +157 -0
- package/lib/leadharness-leads.mjs +574 -0
- package/models.yaml +32 -0
- package/modes/_shared.md +50 -0
- package/modes/batch.md +34 -0
- package/modes/crawl.md +25 -0
- package/modes/ingest.md +33 -0
- package/modes/pipeline.md +27 -0
- package/modes/reference-local-helpers.md +13 -0
- package/modes/review.md +15 -0
- package/modes/setup.md +25 -0
- package/opencode.json +43 -0
- package/package.json +186 -0
- package/scripts/batch-orchestrator.mjs +558 -0
- package/scripts/crawl.mjs +129 -0
- package/scripts/ingest.mjs +48 -0
- package/scripts/manifest.mjs +71 -0
- package/scripts/pipeline.mjs +118 -0
- package/scripts/validate-leads.mjs +69 -0
- package/templates/canon.json +26 -0
- package/templates/capabilities.json +23 -0
- package/templates/context.json +20 -0
- package/templates/contracts.json +72 -0
- package/templates/facts.json +13 -0
- package/templates/index.json +13 -0
- package/templates/lead-schema.json +143 -0
- package/templates/lineage.json +10 -0
- package/templates/migrations.json +4 -0
- package/templates/postflight.json +11 -0
- package/templates/preflight.json +19 -0
- package/templates/prioritize.json +10 -0
- package/templates/redact.json +15 -0
- package/templates/score.json +18 -0
- package/templates/states.yml +25 -0
- package/templates/timeline.json +10 -0
- package/verify-pipeline.mjs +123 -0
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { ingestArtifact } from '../lib/leadharness-ingest.mjs';
|
|
4
|
+
import { parseArgs, relativeProjectPath } from '../lib/leadharness-leads.mjs';
|
|
5
|
+
|
|
6
|
+
const USAGE = `public-leads ingest -- submit validated leads to an ingest API
|
|
7
|
+
|
|
8
|
+
Usage:
|
|
9
|
+
public-leads ingest --input <file> [--api <base-url>] [--ingest-path </path>]
|
|
10
|
+
[--operator-email <email>] [--operator-email-header <header>]
|
|
11
|
+
[--auth-header <header>] [--auth-scheme <scheme>]
|
|
12
|
+
[--token <token>] [--token-env ADMIN_API_TOKEN]
|
|
13
|
+
[--target-project /path/to/cold-agent-leads]
|
|
14
|
+
[--job-id <id>] [--out data/ingest-response.json] [--dry-run]
|
|
15
|
+
|
|
16
|
+
Defaults are read from config/profile.yml when present:
|
|
17
|
+
api.base_url, api.ingest_path, api.operator_email, api.operator_email_header,
|
|
18
|
+
api.auth_header, api.auth_scheme, api.auth_token_env, api.target_project
|
|
19
|
+
|
|
20
|
+
Compatibility fallbacks are still accepted:
|
|
21
|
+
--admin-email, api.admin_email, api.admin_token_env, $PUBLIC_LEADS_API_TOKEN
|
|
22
|
+
`;
|
|
23
|
+
|
|
24
|
+
const opts = parseArgs(process.argv.slice(2));
|
|
25
|
+
|
|
26
|
+
if (opts.help || !opts.input) {
|
|
27
|
+
console.log(USAGE);
|
|
28
|
+
process.exit(opts.help ? 0 : 2);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
try {
|
|
32
|
+
const outputPath = opts.out || 'data/ingest-response.json';
|
|
33
|
+
const output = await ingestArtifact(opts.input, {
|
|
34
|
+
...opts,
|
|
35
|
+
out: outputPath,
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
if (opts.dryRun) {
|
|
39
|
+
console.log(`dry run: wrote ${relativeProjectPath(outputPath)} (${output.payload.leads.length} leads)`);
|
|
40
|
+
process.exit(0);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const jobId = output.response?.job?.id || opts.jobId || '';
|
|
44
|
+
console.log(`ingested ${output.leadCount} leads${jobId ? ` as job ${jobId}` : ''}; wrote ${relativeProjectPath(outputPath)}`);
|
|
45
|
+
} catch (error) {
|
|
46
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
47
|
+
process.exit(1);
|
|
48
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { existsSync } from 'fs';
|
|
4
|
+
import {
|
|
5
|
+
createManifestRecord,
|
|
6
|
+
parseArgs,
|
|
7
|
+
readJson,
|
|
8
|
+
readLeadArtifact,
|
|
9
|
+
relativeProjectPath,
|
|
10
|
+
resolveProjectPath,
|
|
11
|
+
validatePayload,
|
|
12
|
+
writeJson,
|
|
13
|
+
} from '../lib/leadharness-leads.mjs';
|
|
14
|
+
|
|
15
|
+
const USAGE = `public-leads manifest -- build/update the lead artifact manifest
|
|
16
|
+
|
|
17
|
+
Usage:
|
|
18
|
+
public-leads manifest --input <file> [--manifest data/lead-manifest.json] [--json]
|
|
19
|
+
`;
|
|
20
|
+
|
|
21
|
+
const opts = parseArgs(process.argv.slice(2));
|
|
22
|
+
|
|
23
|
+
if (opts.help || !opts.input) {
|
|
24
|
+
console.log(USAGE);
|
|
25
|
+
process.exit(opts.help ? 0 : 2);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
try {
|
|
29
|
+
const manifestPath = resolveProjectPath(opts.manifest || 'data/lead-manifest.json');
|
|
30
|
+
const payload = readLeadArtifact(opts.input);
|
|
31
|
+
const validation = validatePayload(payload);
|
|
32
|
+
const record = createManifestRecord({
|
|
33
|
+
inputPath: opts.input,
|
|
34
|
+
payload,
|
|
35
|
+
validation,
|
|
36
|
+
});
|
|
37
|
+
const manifest = existsSync(manifestPath)
|
|
38
|
+
? readJson(manifestPath)
|
|
39
|
+
: { version: 1, generatedAt: new Date().toISOString(), batches: [] };
|
|
40
|
+
|
|
41
|
+
manifest.version = 1;
|
|
42
|
+
manifest.generatedAt = new Date().toISOString();
|
|
43
|
+
manifest.batches = Array.isArray(manifest.batches) ? manifest.batches : [];
|
|
44
|
+
|
|
45
|
+
const existingIndex = manifest.batches.findIndex((item) => item.input === record.input);
|
|
46
|
+
if (existingIndex === -1) manifest.batches.push(record);
|
|
47
|
+
else manifest.batches[existingIndex] = record;
|
|
48
|
+
|
|
49
|
+
manifest.summary = {
|
|
50
|
+
batchCount: manifest.batches.length,
|
|
51
|
+
readyForIngest: manifest.batches.filter((item) => item.readyForIngest).length,
|
|
52
|
+
leadCount: manifest.batches.reduce((sum, item) => sum + item.leadCount, 0),
|
|
53
|
+
errorCount: manifest.batches.reduce((sum, item) => sum + item.validation.errors, 0),
|
|
54
|
+
warningCount: manifest.batches.reduce((sum, item) => sum + item.validation.warnings, 0),
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
writeJson(manifestPath, manifest);
|
|
58
|
+
|
|
59
|
+
if (opts.json) {
|
|
60
|
+
console.log(JSON.stringify({ manifest: relativeProjectPath(manifestPath), record, summary: manifest.summary }, null, 2));
|
|
61
|
+
} else {
|
|
62
|
+
console.log(`manifest: ${relativeProjectPath(manifestPath)}`);
|
|
63
|
+
console.log(`batch: ${record.id} leads=${record.leadCount} ready=${record.readyForIngest}`);
|
|
64
|
+
console.log(`summary: batches=${manifest.summary.batchCount} ready=${manifest.summary.readyForIngest} leads=${manifest.summary.leadCount}`);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
process.exit(validation.ok ? 0 : 1);
|
|
68
|
+
} catch (error) {
|
|
69
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
70
|
+
process.exit(1);
|
|
71
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { existsSync } from 'fs';
|
|
4
|
+
import { crawlDomains } from '../lib/leadharness-crawler.mjs';
|
|
5
|
+
import { ingestPayload } from '../lib/leadharness-ingest.mjs';
|
|
6
|
+
import {
|
|
7
|
+
createManifestRecord,
|
|
8
|
+
loadProfileConfig,
|
|
9
|
+
parseArgs,
|
|
10
|
+
readJson,
|
|
11
|
+
relativeProjectPath,
|
|
12
|
+
resolveProjectPath,
|
|
13
|
+
validatePayload,
|
|
14
|
+
writeJson,
|
|
15
|
+
} from '../lib/leadharness-leads.mjs';
|
|
16
|
+
import { readRequestedDomains } from './crawl.mjs';
|
|
17
|
+
|
|
18
|
+
const USAGE = `public-leads pipeline -- crawl, validate, manifest, and optionally ingest leads
|
|
19
|
+
|
|
20
|
+
Usage:
|
|
21
|
+
public-leads pipeline [--domain example.com | --domains example.com,example.org]
|
|
22
|
+
[--input data/domains.tsv] [--out data/lead-results.json]
|
|
23
|
+
[--manifest data/lead-manifest.json]
|
|
24
|
+
[--max-pages 10] [--min-confidence 30]
|
|
25
|
+
[--ingest | --upload] [--dry-run]
|
|
26
|
+
[--target-project /path/to/cold-agent-leads]
|
|
27
|
+
|
|
28
|
+
Examples:
|
|
29
|
+
public-leads pipeline --domains example.com,example.org
|
|
30
|
+
public-leads pipeline --input data/domains.tsv --ingest --target-project /path/to/cold-agent-leads
|
|
31
|
+
`;
|
|
32
|
+
|
|
33
|
+
const opts = parseArgs(process.argv.slice(2));
|
|
34
|
+
|
|
35
|
+
if (opts.help) {
|
|
36
|
+
console.log(USAGE);
|
|
37
|
+
process.exit(0);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
try {
|
|
41
|
+
const profile = loadProfileConfig();
|
|
42
|
+
const domains = readRequestedDomains(opts);
|
|
43
|
+
const out = opts.out || 'data/lead-results.json';
|
|
44
|
+
const manifestPath = opts.manifest || 'data/lead-manifest.json';
|
|
45
|
+
const ingestOut = opts.ingestOut || opts.ingestResponse || 'data/ingest-response.json';
|
|
46
|
+
|
|
47
|
+
const payload = await crawlDomains(domains, {
|
|
48
|
+
maxPages: opts.maxPages || profile.maxPages,
|
|
49
|
+
minConfidence: opts.minConfidence || profile.minConfidence,
|
|
50
|
+
userAgent: opts.userAgent || profile.userAgent,
|
|
51
|
+
includeBlocked: Boolean(opts.includeBlocked),
|
|
52
|
+
delayMs: opts.delayMs,
|
|
53
|
+
timeoutMs: opts.timeoutMs,
|
|
54
|
+
jobId: opts.jobId,
|
|
55
|
+
});
|
|
56
|
+
writeJson(resolveProjectPath(out), payload);
|
|
57
|
+
|
|
58
|
+
const validation = validatePayload(payload, { allowEmpty: Boolean(opts.allowEmpty) });
|
|
59
|
+
if (!validation.ok) {
|
|
60
|
+
for (const item of validation.issues.filter((issue) => issue.severity === 'error')) {
|
|
61
|
+
console.error(`error: ${item.path}: ${item.code}: ${item.message}`);
|
|
62
|
+
}
|
|
63
|
+
process.exit(1);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const manifest = upsertManifest(manifestPath, out, payload, validation);
|
|
67
|
+
console.log(`pipeline: wrote ${relativeProjectPath(resolveProjectPath(out))}`);
|
|
68
|
+
console.log(`manifest: ${relativeProjectPath(resolveProjectPath(manifestPath))}`);
|
|
69
|
+
console.log(`domains=${validation.summary.domainCount} leads=${validation.summary.leadCount} results=${validation.summary.resultCount} errors=${payload.errors.length}`);
|
|
70
|
+
|
|
71
|
+
if (opts.ingest || opts.upload) {
|
|
72
|
+
const ingestResult = await ingestPayload(payload, {
|
|
73
|
+
...opts,
|
|
74
|
+
input: out,
|
|
75
|
+
out: ingestOut,
|
|
76
|
+
dryRun: Boolean(opts.dryRun),
|
|
77
|
+
});
|
|
78
|
+
if (opts.dryRun) {
|
|
79
|
+
console.log(`dry-run ingest: wrote ${relativeProjectPath(resolveProjectPath(ingestOut))}`);
|
|
80
|
+
} else {
|
|
81
|
+
const jobId = ingestResult.response?.job?.id || payload.jobId || '';
|
|
82
|
+
console.log(`ingested ${ingestResult.leadCount} leads${jobId ? ` as job ${jobId}` : ''}; wrote ${relativeProjectPath(resolveProjectPath(ingestOut))}`);
|
|
83
|
+
}
|
|
84
|
+
} else {
|
|
85
|
+
console.log('next: public-leads ingest --input ' + out);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (opts.json) {
|
|
89
|
+
console.log(JSON.stringify({ artifact: out, manifest: manifestPath, summary: manifest.summary }, null, 2));
|
|
90
|
+
}
|
|
91
|
+
} catch (error) {
|
|
92
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
93
|
+
process.exit(1);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function upsertManifest(manifestPath, inputPath, payload, validation) {
|
|
97
|
+
const abs = resolveProjectPath(manifestPath);
|
|
98
|
+
const record = createManifestRecord({ inputPath, payload, validation });
|
|
99
|
+
const manifest = existsSync(abs)
|
|
100
|
+
? readJson(abs)
|
|
101
|
+
: { version: 1, generatedAt: new Date().toISOString(), batches: [] };
|
|
102
|
+
|
|
103
|
+
manifest.version = 1;
|
|
104
|
+
manifest.generatedAt = new Date().toISOString();
|
|
105
|
+
manifest.batches = Array.isArray(manifest.batches) ? manifest.batches : [];
|
|
106
|
+
const index = manifest.batches.findIndex((item) => item.input === record.input);
|
|
107
|
+
if (index === -1) manifest.batches.push(record);
|
|
108
|
+
else manifest.batches[index] = record;
|
|
109
|
+
manifest.summary = {
|
|
110
|
+
batchCount: manifest.batches.length,
|
|
111
|
+
readyForIngest: manifest.batches.filter((item) => item.readyForIngest).length,
|
|
112
|
+
leadCount: manifest.batches.reduce((sum, item) => sum + item.leadCount, 0),
|
|
113
|
+
errorCount: manifest.batches.reduce((sum, item) => sum + item.validation.errors, 0),
|
|
114
|
+
warningCount: manifest.batches.reduce((sum, item) => sum + item.validation.warnings, 0),
|
|
115
|
+
};
|
|
116
|
+
writeJson(abs, manifest);
|
|
117
|
+
return manifest;
|
|
118
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
parseArgs,
|
|
5
|
+
readLeadArtifact,
|
|
6
|
+
relativeProjectPath,
|
|
7
|
+
validatePayload,
|
|
8
|
+
writeJson,
|
|
9
|
+
} from '../lib/leadharness-leads.mjs';
|
|
10
|
+
|
|
11
|
+
const USAGE = `public-leads validate -- validate lead artifacts
|
|
12
|
+
|
|
13
|
+
Usage:
|
|
14
|
+
public-leads validate --input <file> [--json] [--out <file>] [--allow-empty]
|
|
15
|
+
|
|
16
|
+
Accepted input:
|
|
17
|
+
- JSON object with { jobId?, domains?, leads, results?, errors? }
|
|
18
|
+
- JSON array of lead records
|
|
19
|
+
- JSONL lead records
|
|
20
|
+
`;
|
|
21
|
+
|
|
22
|
+
const opts = parseArgs(process.argv.slice(2));
|
|
23
|
+
|
|
24
|
+
if (opts.help || !opts.input) {
|
|
25
|
+
console.log(USAGE);
|
|
26
|
+
process.exit(opts.help ? 0 : 2);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
try {
|
|
30
|
+
const payload = readLeadArtifact(opts.input);
|
|
31
|
+
const result = validatePayload(payload, { allowEmpty: Boolean(opts.allowEmpty) });
|
|
32
|
+
const report = {
|
|
33
|
+
input: opts.input,
|
|
34
|
+
ok: result.ok,
|
|
35
|
+
summary: result.summary,
|
|
36
|
+
errors: result.errors,
|
|
37
|
+
warnings: result.warnings,
|
|
38
|
+
issues: result.issues,
|
|
39
|
+
payload,
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
if (opts.out) {
|
|
43
|
+
writeJson(opts.out, report);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (opts.json) {
|
|
47
|
+
console.log(JSON.stringify(report, null, 2));
|
|
48
|
+
} else {
|
|
49
|
+
printReport(report);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
process.exit(result.ok ? 0 : 1);
|
|
53
|
+
} catch (error) {
|
|
54
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
55
|
+
process.exit(1);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function printReport(report) {
|
|
59
|
+
const rel = relativeProjectPath(report.input);
|
|
60
|
+
console.log(`${report.ok ? 'OK' : 'ERROR'} ${rel}`);
|
|
61
|
+
console.log(`domains=${report.summary.domainCount} leads=${report.summary.leadCount} results=${report.summary.resultCount} avgConfidence=${report.summary.averageConfidence}`);
|
|
62
|
+
if (Object.keys(report.summary.byType).length > 0) {
|
|
63
|
+
console.log(`types=${Object.entries(report.summary.byType).map(([type, count]) => `${type}:${count}`).join(', ')}`);
|
|
64
|
+
}
|
|
65
|
+
for (const item of report.issues) {
|
|
66
|
+
const prefix = item.severity === 'error' ? 'error' : 'warn';
|
|
67
|
+
console.log(`${prefix}: ${item.path}: ${item.code}: ${item.message}`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 1,
|
|
3
|
+
"profiles": [
|
|
4
|
+
{
|
|
5
|
+
"name": "publicleads",
|
|
6
|
+
"entities": {
|
|
7
|
+
"domain": {
|
|
8
|
+
"case": "lower",
|
|
9
|
+
"stripPrefixes": ["http://", "https://", "www."],
|
|
10
|
+
"stripSuffixes": ["/"],
|
|
11
|
+
"collapseWhitespace": true
|
|
12
|
+
},
|
|
13
|
+
"company": {
|
|
14
|
+
"case": "lower",
|
|
15
|
+
"collapseWhitespace": true,
|
|
16
|
+
"stripPunctuation": true,
|
|
17
|
+
"stripSuffixes": [" inc", " llc", " ltd", " co", " corp", " corporation"]
|
|
18
|
+
},
|
|
19
|
+
"lead": {
|
|
20
|
+
"case": "lower",
|
|
21
|
+
"join": ["domain", "email", "contactName", "title"]
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
]
|
|
26
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 1,
|
|
3
|
+
"roles": {
|
|
4
|
+
"general-free": {
|
|
5
|
+
"description": "Procedural crawl, extract, validate, and ingest helper.",
|
|
6
|
+
"tools": ["geometra", "filesystem", "bash"],
|
|
7
|
+
"filesystem": ["read", "write"],
|
|
8
|
+
"network": ["public-web", "lead-ingest-api"]
|
|
9
|
+
},
|
|
10
|
+
"general-paid": {
|
|
11
|
+
"description": "Quality-sensitive lead fit and evidence review helper.",
|
|
12
|
+
"tools": ["geometra", "filesystem", "bash"],
|
|
13
|
+
"filesystem": ["read", "write"],
|
|
14
|
+
"network": ["public-web"]
|
|
15
|
+
},
|
|
16
|
+
"glm-minimal": {
|
|
17
|
+
"description": "Narrow JSON transform helper.",
|
|
18
|
+
"tools": [],
|
|
19
|
+
"filesystem": [],
|
|
20
|
+
"network": []
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 1,
|
|
3
|
+
"bundles": [
|
|
4
|
+
{
|
|
5
|
+
"name": "crawl",
|
|
6
|
+
"budget": 24000,
|
|
7
|
+
"files": ["modes/_shared.md", "modes/crawl.md", "templates/lead-schema.json"]
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
"name": "pipeline",
|
|
11
|
+
"budget": 26000,
|
|
12
|
+
"files": ["modes/_shared.md", "modes/pipeline.md", "templates/lead-schema.json", "data/pipeline.md"]
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"name": "ingest",
|
|
16
|
+
"budget": 18000,
|
|
17
|
+
"files": ["modes/_shared.md", "modes/ingest.md", "templates/lead-schema.json", "config/profile.yml"]
|
|
18
|
+
}
|
|
19
|
+
]
|
|
20
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
{
|
|
2
|
+
"contracts": [
|
|
3
|
+
{
|
|
4
|
+
"name": "publicleads.lead",
|
|
5
|
+
"version": "1.0.0",
|
|
6
|
+
"description": "Source-backed lead record produced by the agentic harness.",
|
|
7
|
+
"fields": [
|
|
8
|
+
{ "name": "id", "type": "string" },
|
|
9
|
+
{ "name": "company", "type": "string" },
|
|
10
|
+
{ "name": "domain", "type": "string", "required": true },
|
|
11
|
+
{ "name": "websiteUrl", "type": "url" },
|
|
12
|
+
{ "name": "contactName", "type": "string" },
|
|
13
|
+
{ "name": "title", "type": "string" },
|
|
14
|
+
{ "name": "email", "type": "string" },
|
|
15
|
+
{
|
|
16
|
+
"name": "emailType",
|
|
17
|
+
"type": "enum",
|
|
18
|
+
"required": true,
|
|
19
|
+
"values": ["person", "role", "blocked", "contact_path", "unknown"]
|
|
20
|
+
},
|
|
21
|
+
{ "name": "sourceUrl", "type": "url", "required": true },
|
|
22
|
+
{ "name": "sourceLabel", "type": "string" },
|
|
23
|
+
{ "name": "evidence", "type": "string", "required": true },
|
|
24
|
+
{ "name": "extractionMethod", "type": "string", "required": true },
|
|
25
|
+
{
|
|
26
|
+
"name": "verificationStatus",
|
|
27
|
+
"type": "enum",
|
|
28
|
+
"required": true,
|
|
29
|
+
"values": ["verified", "mx_verified", "unverified", "not_applicable", "blocked", "unknown"]
|
|
30
|
+
},
|
|
31
|
+
{ "name": "confidence", "type": "integer", "required": true, "min": 0, "max": 100 },
|
|
32
|
+
{ "name": "warnings", "type": "json" },
|
|
33
|
+
{ "name": "phone", "type": "string" },
|
|
34
|
+
{ "name": "socialUrls", "type": "json" },
|
|
35
|
+
{ "name": "contactUrls", "type": "json" },
|
|
36
|
+
{ "name": "foundAt", "type": "datetime" }
|
|
37
|
+
],
|
|
38
|
+
"formats": {
|
|
39
|
+
"tsv": {
|
|
40
|
+
"style": "delimited",
|
|
41
|
+
"delimiter": "tab",
|
|
42
|
+
"fields": [
|
|
43
|
+
"company",
|
|
44
|
+
"domain",
|
|
45
|
+
"contactName",
|
|
46
|
+
"title",
|
|
47
|
+
"email",
|
|
48
|
+
"emailType",
|
|
49
|
+
"verificationStatus",
|
|
50
|
+
"confidence",
|
|
51
|
+
"sourceUrl",
|
|
52
|
+
"evidence"
|
|
53
|
+
]
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
"name": "publicleads.ingest-outcome",
|
|
59
|
+
"version": "1.0.0",
|
|
60
|
+
"description": "Structured outcome from submitting an artifact to an ingest API.",
|
|
61
|
+
"fields": [
|
|
62
|
+
{ "name": "status", "type": "enum", "required": true, "values": ["INGESTED", "INGEST FAILED", "DRY RUN"] },
|
|
63
|
+
{ "name": "input", "type": "string", "required": true },
|
|
64
|
+
{ "name": "jobId", "type": "string" },
|
|
65
|
+
{ "name": "leadCount", "type": "integer", "required": true },
|
|
66
|
+
{ "name": "api", "type": "url" },
|
|
67
|
+
{ "name": "responsePath", "type": "string" },
|
|
68
|
+
{ "name": "reason", "type": "string" }
|
|
69
|
+
]
|
|
70
|
+
}
|
|
71
|
+
]
|
|
72
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "urn:public-leads-harness:templates:lead-schema",
|
|
4
|
+
"title": "Lead ingest payload",
|
|
5
|
+
"description": "Portable payload for lead review or ingest APIs.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"additionalProperties": true,
|
|
8
|
+
"required": ["leads"],
|
|
9
|
+
"properties": {
|
|
10
|
+
"jobId": {
|
|
11
|
+
"type": "string",
|
|
12
|
+
"description": "Optional stable crawl job ID. The app generates one when omitted."
|
|
13
|
+
},
|
|
14
|
+
"domains": {
|
|
15
|
+
"type": "array",
|
|
16
|
+
"items": { "type": "string" },
|
|
17
|
+
"default": []
|
|
18
|
+
},
|
|
19
|
+
"leads": {
|
|
20
|
+
"type": "array",
|
|
21
|
+
"items": { "$ref": "#/$defs/lead" }
|
|
22
|
+
},
|
|
23
|
+
"results": {
|
|
24
|
+
"type": "array",
|
|
25
|
+
"items": { "$ref": "#/$defs/result" },
|
|
26
|
+
"default": []
|
|
27
|
+
},
|
|
28
|
+
"errors": {
|
|
29
|
+
"type": "array",
|
|
30
|
+
"items": { "type": "string" },
|
|
31
|
+
"default": []
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
"$defs": {
|
|
35
|
+
"lead": {
|
|
36
|
+
"type": "object",
|
|
37
|
+
"additionalProperties": true,
|
|
38
|
+
"required": [
|
|
39
|
+
"domain",
|
|
40
|
+
"emailType",
|
|
41
|
+
"sourceUrl",
|
|
42
|
+
"evidence",
|
|
43
|
+
"extractionMethod",
|
|
44
|
+
"verificationStatus",
|
|
45
|
+
"confidence"
|
|
46
|
+
],
|
|
47
|
+
"properties": {
|
|
48
|
+
"id": { "type": "string" },
|
|
49
|
+
"company": { "type": "string" },
|
|
50
|
+
"domain": { "type": "string" },
|
|
51
|
+
"websiteUrl": { "type": "string" },
|
|
52
|
+
"contactName": { "type": "string" },
|
|
53
|
+
"title": { "type": "string" },
|
|
54
|
+
"email": { "type": "string" },
|
|
55
|
+
"emailType": {
|
|
56
|
+
"type": "string",
|
|
57
|
+
"enum": ["person", "role", "blocked", "contact_path", "unknown"]
|
|
58
|
+
},
|
|
59
|
+
"sourceUrl": { "type": "string" },
|
|
60
|
+
"sourceLabel": { "type": "string" },
|
|
61
|
+
"evidence": { "type": "string" },
|
|
62
|
+
"extractionMethod": { "type": "string" },
|
|
63
|
+
"verificationStatus": {
|
|
64
|
+
"type": "string",
|
|
65
|
+
"enum": ["verified", "mx_verified", "unverified", "not_applicable", "blocked", "unknown"]
|
|
66
|
+
},
|
|
67
|
+
"confidence": {
|
|
68
|
+
"type": "integer",
|
|
69
|
+
"minimum": 0,
|
|
70
|
+
"maximum": 100
|
|
71
|
+
},
|
|
72
|
+
"warnings": {
|
|
73
|
+
"type": "array",
|
|
74
|
+
"items": { "type": "string" },
|
|
75
|
+
"default": []
|
|
76
|
+
},
|
|
77
|
+
"phone": {
|
|
78
|
+
"type": "string",
|
|
79
|
+
"description": "Optional normalized phone number found near the public contact evidence."
|
|
80
|
+
},
|
|
81
|
+
"socialUrls": {
|
|
82
|
+
"type": "array",
|
|
83
|
+
"items": { "type": "string" },
|
|
84
|
+
"default": [],
|
|
85
|
+
"description": "Optional public social/profile URLs found on the same source page."
|
|
86
|
+
},
|
|
87
|
+
"contactUrls": {
|
|
88
|
+
"type": "array",
|
|
89
|
+
"items": { "type": "string" },
|
|
90
|
+
"default": [],
|
|
91
|
+
"description": "Optional public scheduling or contact URLs found on the same source page."
|
|
92
|
+
},
|
|
93
|
+
"foundAt": {
|
|
94
|
+
"type": "string",
|
|
95
|
+
"format": "date-time"
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
"result": {
|
|
100
|
+
"type": "object",
|
|
101
|
+
"additionalProperties": true,
|
|
102
|
+
"required": ["domain"],
|
|
103
|
+
"properties": {
|
|
104
|
+
"domain": { "type": "string" },
|
|
105
|
+
"websiteUrl": { "type": "string" },
|
|
106
|
+
"leads": {
|
|
107
|
+
"type": "array",
|
|
108
|
+
"items": { "$ref": "#/$defs/lead" },
|
|
109
|
+
"default": []
|
|
110
|
+
},
|
|
111
|
+
"pages": {
|
|
112
|
+
"type": "array",
|
|
113
|
+
"items": { "$ref": "#/$defs/pageVisit" },
|
|
114
|
+
"default": []
|
|
115
|
+
},
|
|
116
|
+
"warnings": {
|
|
117
|
+
"type": "array",
|
|
118
|
+
"items": { "type": "string" },
|
|
119
|
+
"default": []
|
|
120
|
+
},
|
|
121
|
+
"completedAt": {
|
|
122
|
+
"type": "string",
|
|
123
|
+
"format": "date-time"
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
"pageVisit": {
|
|
128
|
+
"type": "object",
|
|
129
|
+
"additionalProperties": true,
|
|
130
|
+
"required": ["url"],
|
|
131
|
+
"properties": {
|
|
132
|
+
"url": { "type": "string" },
|
|
133
|
+
"title": { "type": "string" },
|
|
134
|
+
"statusCode": { "type": "integer" },
|
|
135
|
+
"emailsFound": { "type": "integer" },
|
|
136
|
+
"phonesFound": { "type": "integer" },
|
|
137
|
+
"socialUrlsFound": { "type": "integer" },
|
|
138
|
+
"contactUrlsFound": { "type": "integer" },
|
|
139
|
+
"error": { "type": "string" }
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 1,
|
|
3
|
+
"workflows": [
|
|
4
|
+
{
|
|
5
|
+
"name": "publicleads.crawl",
|
|
6
|
+
"maxParallel": 2,
|
|
7
|
+
"requiredSourceFields": ["domain"],
|
|
8
|
+
"blockGates": ["duplicate", "robotsBlocked", "privateSource"],
|
|
9
|
+
"warnGates": ["lowSignal", "noCompany"]
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"name": "publicleads.ingest",
|
|
13
|
+
"maxParallel": 1,
|
|
14
|
+
"requiredSourceFields": ["input", "leadCount", "validationPath"],
|
|
15
|
+
"blockGates": ["validationErrors", "missingToken", "missingOperatorEmail"],
|
|
16
|
+
"warnGates": ["validationWarnings"]
|
|
17
|
+
}
|
|
18
|
+
]
|
|
19
|
+
}
|