@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.
Files changed (87) hide show
  1. package/.claude/agents/general-free.md +39 -0
  2. package/.claude/agents/general-paid.md +20 -0
  3. package/.claude/agents/glm-minimal.md +9 -0
  4. package/.claude/iso-route.resolved.json +21 -0
  5. package/.claude/settings.json +3 -0
  6. package/.codex/config.toml +24 -0
  7. package/.cursor/iso-route.md +17 -0
  8. package/.cursor/mcp.json +19 -0
  9. package/.cursor/rules/agent-general-free.mdc +38 -0
  10. package/.cursor/rules/agent-general-paid.mdc +19 -0
  11. package/.cursor/rules/agent-glm-minimal.mdc +8 -0
  12. package/.cursor/rules/main.mdc +61 -0
  13. package/.mcp.json +19 -0
  14. package/.opencode/agents/general-free.md +48 -0
  15. package/.opencode/agents/general-paid.md +29 -0
  16. package/.opencode/agents/glm-minimal.md +19 -0
  17. package/.opencode/instructions.md +3 -0
  18. package/.opencode/skills/lead-harness.md +56 -0
  19. package/.opencode/skills/public-leads.md +56 -0
  20. package/.pi/prompts/lead-harness.md +54 -0
  21. package/.pi/prompts/public-leads.md +54 -0
  22. package/.pi/skills/general-free/SKILL.md +38 -0
  23. package/.pi/skills/general-paid/SKILL.md +19 -0
  24. package/.pi/skills/glm-minimal/SKILL.md +8 -0
  25. package/AGENTS.md +56 -0
  26. package/CLAUDE.md +56 -0
  27. package/LICENSE +21 -0
  28. package/README.md +61 -0
  29. package/batch/README.md +37 -0
  30. package/batch/batch-prompt.md +19 -0
  31. package/batch/batch-runner.sh +18 -0
  32. package/bin/create-leads-harness.mjs +178 -0
  33. package/bin/lead-harness.mjs +201 -0
  34. package/bin/sync.mjs +150 -0
  35. package/config/profile.example.yml +34 -0
  36. package/docs/ARCHITECTURE.md +85 -0
  37. package/docs/CONSTRUCTION.md +40 -0
  38. package/docs/README.md +5 -0
  39. package/docs/SETUP.md +89 -0
  40. package/examples/README.md +9 -0
  41. package/examples/sample-leads.json +57 -0
  42. package/iso/agents/general-free.md +50 -0
  43. package/iso/agents/general-paid.md +31 -0
  44. package/iso/agents/glm-minimal.md +21 -0
  45. package/iso/commands/lead-harness.md +59 -0
  46. package/iso/commands/public-leads.md +59 -0
  47. package/iso/config.json +11 -0
  48. package/iso/instructions.md +56 -0
  49. package/iso/instructions.opencode.md +3 -0
  50. package/iso/mcp.json +16 -0
  51. package/lib/leadharness-crawler.mjs +911 -0
  52. package/lib/leadharness-ingest.mjs +157 -0
  53. package/lib/leadharness-leads.mjs +574 -0
  54. package/models.yaml +32 -0
  55. package/modes/_shared.md +50 -0
  56. package/modes/batch.md +34 -0
  57. package/modes/crawl.md +25 -0
  58. package/modes/ingest.md +33 -0
  59. package/modes/pipeline.md +27 -0
  60. package/modes/reference-local-helpers.md +13 -0
  61. package/modes/review.md +15 -0
  62. package/modes/setup.md +25 -0
  63. package/opencode.json +43 -0
  64. package/package.json +186 -0
  65. package/scripts/batch-orchestrator.mjs +558 -0
  66. package/scripts/crawl.mjs +129 -0
  67. package/scripts/ingest.mjs +48 -0
  68. package/scripts/manifest.mjs +71 -0
  69. package/scripts/pipeline.mjs +118 -0
  70. package/scripts/validate-leads.mjs +69 -0
  71. package/templates/canon.json +26 -0
  72. package/templates/capabilities.json +23 -0
  73. package/templates/context.json +20 -0
  74. package/templates/contracts.json +72 -0
  75. package/templates/facts.json +13 -0
  76. package/templates/index.json +13 -0
  77. package/templates/lead-schema.json +143 -0
  78. package/templates/lineage.json +10 -0
  79. package/templates/migrations.json +4 -0
  80. package/templates/postflight.json +11 -0
  81. package/templates/preflight.json +19 -0
  82. package/templates/prioritize.json +10 -0
  83. package/templates/redact.json +15 -0
  84. package/templates/score.json +18 -0
  85. package/templates/states.yml +25 -0
  86. package/templates/timeline.json +10 -0
  87. package/verify-pipeline.mjs +123 -0
@@ -0,0 +1,157 @@
1
+ import { existsSync, readFileSync } from 'fs';
2
+ import { resolve } from 'path';
3
+ import {
4
+ loadProfileConfig,
5
+ readLeadArtifact,
6
+ validatePayload,
7
+ writeJson,
8
+ } from './leadharness-leads.mjs';
9
+
10
+ export function resolveIngestConfig(opts = {}, profile = loadProfileConfig()) {
11
+ const targetEnv = loadTargetProjectEnv(opts.targetProject || profile.targetProject);
12
+ const env = { ...process.env, ...targetEnv };
13
+ const legacyOperatorEmail = opts.adminEmail || env.PUBLIC_LEADS_ADMIN_EMAIL || env.LEAD_HARNESS_ADMIN_EMAIL || profile.adminEmail;
14
+ const configuredOperatorEmail = opts.operatorEmail || env.PUBLIC_LEADS_OPERATOR_EMAIL || env.LEAD_HARNESS_OPERATOR_EMAIL || profile.operatorEmail;
15
+ const adminEmails = splitList(env.ADMIN_EMAILS);
16
+ const operatorEmail = configuredOperatorEmail || legacyOperatorEmail || adminEmails[0] || '';
17
+ const tokenEnv = opts.tokenEnv || profile.authTokenEnv || profile.adminTokenEnv || 'ADMIN_API_TOKEN';
18
+
19
+ return {
20
+ endpoint: `${stripTrailingSlash(opts.api || env.PUBLIC_LEADS_API || env.LEAD_HARNESS_API || profile.baseUrl || 'http://localhost:8080')}${normalizeEndpointPath(opts.ingestPath || env.PUBLIC_LEADS_INGEST_PATH || env.LEAD_HARNESS_INGEST_PATH || profile.ingestPath || '/api/lead-ingests')}`,
21
+ operatorEmail,
22
+ operatorEmailHeader: opts.operatorEmailHeader
23
+ || opts.adminEmailHeader
24
+ || env.PUBLIC_LEADS_OPERATOR_EMAIL_HEADER
25
+ || env.LEAD_HARNESS_OPERATOR_EMAIL_HEADER
26
+ || profile.operatorEmailHeader
27
+ || profile.adminEmailHeader
28
+ || (legacyOperatorEmail || adminEmails.length > 0 ? 'X-Admin-Email' : '')
29
+ || (operatorEmail ? 'X-Operator-Email' : ''),
30
+ authHeader: opts.authHeader || env.PUBLIC_LEADS_AUTH_HEADER || env.LEAD_HARNESS_AUTH_HEADER || profile.authHeader || 'Authorization',
31
+ authScheme: opts.authScheme || env.PUBLIC_LEADS_AUTH_SCHEME || env.LEAD_HARNESS_AUTH_SCHEME || profile.authScheme || 'Bearer',
32
+ tokenEnv,
33
+ token: opts.token || env[tokenEnv] || env.PUBLIC_LEADS_API_TOKEN || env.LEAD_HARNESS_API_TOKEN || env.ADMIN_API_TOKEN,
34
+ };
35
+ }
36
+
37
+ export async function ingestArtifact(inputPath, opts = {}) {
38
+ const payload = readLeadArtifact(inputPath);
39
+ if (opts.jobId) payload.jobId = opts.jobId;
40
+ return ingestPayload(payload, { ...opts, input: inputPath });
41
+ }
42
+
43
+ export async function ingestPayload(payload, opts = {}) {
44
+ const validation = validatePayload(payload);
45
+ if (!validation.ok) {
46
+ const errors = validation.issues.filter((issue) => issue.severity === 'error');
47
+ throw new Error(errors.map((item) => `${item.path}: ${item.code}: ${item.message}`).join('\n'));
48
+ }
49
+
50
+ const config = resolveIngestConfig(opts);
51
+ const ingestPayload = {
52
+ ...(payload.jobId ? { jobId: payload.jobId } : {}),
53
+ domains: payload.domains,
54
+ leads: payload.leads,
55
+ results: payload.results,
56
+ errors: payload.errors,
57
+ };
58
+
59
+ if (opts.dryRun) {
60
+ const output = {
61
+ status: 'DRY RUN',
62
+ endpoint: config.endpoint,
63
+ input: opts.input || '',
64
+ payload: ingestPayload,
65
+ validation,
66
+ };
67
+ if (opts.out) writeJson(opts.out, output);
68
+ return output;
69
+ }
70
+
71
+ if (config.operatorEmailHeader && !config.operatorEmail) {
72
+ throw new Error('operator email is required when an operator email header is configured');
73
+ }
74
+ if (!config.token) {
75
+ throw new Error(`API token is required (--token or $${config.tokenEnv})`);
76
+ }
77
+
78
+ const headers = {
79
+ [config.authHeader]: formatAuthHeaderValue(config.token, config.authScheme),
80
+ 'Content-Type': 'application/json',
81
+ };
82
+ if (config.operatorEmail && config.operatorEmailHeader) {
83
+ headers[config.operatorEmailHeader] = config.operatorEmail;
84
+ }
85
+
86
+ const response = await fetch(config.endpoint, {
87
+ method: 'POST',
88
+ headers,
89
+ body: JSON.stringify(ingestPayload),
90
+ });
91
+ const text = await response.text();
92
+ const body = parseResponseBody(text);
93
+ const output = {
94
+ status: response.ok ? 'INGESTED' : 'INGEST FAILED',
95
+ httpStatus: response.status,
96
+ endpoint: config.endpoint,
97
+ input: opts.input || '',
98
+ leadCount: ingestPayload.leads.length,
99
+ response: body,
100
+ };
101
+ if (opts.out) writeJson(opts.out, output);
102
+ if (!response.ok) {
103
+ throw new Error(`ingest failed HTTP ${response.status}`);
104
+ }
105
+ return output;
106
+ }
107
+
108
+ function loadTargetProjectEnv(targetProject) {
109
+ const dir = String(targetProject || '').trim();
110
+ if (!dir) return {};
111
+ const envPath = resolve(dir, '.env');
112
+ if (!existsSync(envPath)) return {};
113
+ return parseEnv(readFileSync(envPath, 'utf8'));
114
+ }
115
+
116
+ function parseEnv(text) {
117
+ const env = {};
118
+ for (const rawLine of String(text || '').split(/\r?\n/)) {
119
+ const line = rawLine.trim();
120
+ if (!line || line.startsWith('#')) continue;
121
+ const index = line.indexOf('=');
122
+ if (index === -1) continue;
123
+ const key = line.slice(0, index).trim();
124
+ let value = line.slice(index + 1).trim();
125
+ value = value.replace(/^['"]|['"]$/g, '');
126
+ if (key) env[key] = value;
127
+ }
128
+ return env;
129
+ }
130
+
131
+ function splitList(value) {
132
+ return String(value || '').split(',').map((item) => item.trim()).filter(Boolean);
133
+ }
134
+
135
+ function stripTrailingSlash(value) {
136
+ return String(value || '').replace(/\/+$/, '');
137
+ }
138
+
139
+ function normalizeEndpointPath(value) {
140
+ const raw = String(value || '').trim();
141
+ if (!raw) return '/api/lead-ingests';
142
+ return raw.startsWith('/') ? raw : `/${raw}`;
143
+ }
144
+
145
+ function formatAuthHeaderValue(token, scheme) {
146
+ const cleanToken = String(token || '').trim();
147
+ const cleanScheme = String(scheme || '').trim();
148
+ return cleanScheme ? `${cleanScheme} ${cleanToken}` : cleanToken;
149
+ }
150
+
151
+ function parseResponseBody(text) {
152
+ try {
153
+ return text ? JSON.parse(text) : {};
154
+ } catch {
155
+ return { raw: text };
156
+ }
157
+ }