openings 0.1.62 → 0.1.64

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.
@@ -0,0 +1,63 @@
1
+ {
2
+ "version": 1,
3
+ "comment": "Sources that must never enter the catalog, and why. Verification consults this before admitting a board, so a decision taken once is not quietly undone by the next discovery run. Removing a row from data/companies.json does not survive: the lead stays in the registry and the next verification re-admits it.",
4
+ "entries": [
5
+ {
6
+ "ats": "oraclecloud",
7
+ "token": "ejwl.fa.us2.oraclecloud.com/CX",
8
+ "name": "Marriott International",
9
+ "reason": "terms",
10
+ "detail": "The terms page refuses our requests, and being unable to read a contract is not permission to act under it.",
11
+ "excludedAt": "2026-09-17"
12
+ },
13
+ {
14
+ "ats": "oraclecloud",
15
+ "token": "fa-ewjt-saasfaprod1.fa.ocs.oraclecloud.com/CX_2",
16
+ "name": "EXL",
17
+ "reason": "terms",
18
+ "detail": "Terms prohibit automated data gathering.",
19
+ "excludedAt": "2026-09-17"
20
+ },
21
+
22
+ {
23
+ "ats": "workable",
24
+ "token": "eramtalent-1",
25
+ "name": "Eram Talent",
26
+ "reason": "staffing",
27
+ "detail": "A recruiting firm's board lists other companies' vacancies, which is the opposite of the employer's own posting we promise.",
28
+ "excludedAt": "2026-10-07"
29
+ },
30
+ { "ats": "workable", "token": "flextal-staffing-llc", "name": "FlexTal Staffing LLC", "reason": "staffing", "excludedAt": "2026-10-07" },
31
+ { "ats": "workable", "token": "marathon-talent", "name": "Marathon Talent", "reason": "staffing", "excludedAt": "2026-10-07" },
32
+ { "ats": "workable", "token": "next-job-abroad", "name": "Next Job Abroad", "reason": "staffing", "excludedAt": "2026-10-07" },
33
+ { "ats": "workable", "token": "nobox-hr-outsourcing-solutions", "name": "Nobox Aviation Recruitment", "reason": "staffing", "excludedAt": "2026-10-07" },
34
+ { "ats": "workable", "token": "opendatajobs", "name": "OpenDataJobs", "reason": "staffing", "excludedAt": "2026-10-07" },
35
+ { "ats": "workable", "token": "osa", "name": "Outsourcing Advantage", "reason": "staffing", "excludedAt": "2026-10-07" },
36
+ { "ats": "workable", "token": "pairedrecruiting", "name": "Paired", "reason": "staffing", "excludedAt": "2026-10-07" },
37
+ { "ats": "workable", "token": "pearltalent", "name": "Pearl Talent", "reason": "staffing", "excludedAt": "2026-10-07" },
38
+ { "ats": "workable", "token": "remote-recruitment", "name": "Remote Recruitment", "reason": "staffing", "excludedAt": "2026-10-07" },
39
+ { "ats": "workable", "token": "remote-talent-cloud", "name": "Remote Talent Cloud", "reason": "staffing", "excludedAt": "2026-10-07" },
40
+ { "ats": "workable", "token": "remote-talent-latam", "name": "Remote Talent LATAM", "reason": "staffing", "excludedAt": "2026-10-07" },
41
+ { "ats": "workable", "token": "staffing-for-doctors", "name": "Staffing for Doctors", "reason": "staffing", "excludedAt": "2026-10-07" },
42
+ { "ats": "workable", "token": "talent-engine", "name": "Talent Engine", "reason": "staffing", "excludedAt": "2026-10-07" },
43
+ { "ats": "workable", "token": "uptalent-dot-i-o", "name": "Uptalent.io", "reason": "staffing", "excludedAt": "2026-10-07" },
44
+
45
+ {
46
+ "ats": "workable",
47
+ "token": "entaingroup",
48
+ "name": "Entain",
49
+ "reason": "duplicate",
50
+ "detail": "Already crawled on SmartRecruiters as `entain`. The same vacancy reached from two boards is two rows in search, which is the one thing the count of unique posts must not include.",
51
+ "excludedAt": "2026-10-07"
52
+ },
53
+ { "ats": "workable", "token": "rentokil-initial", "name": "Rentokil Initial", "reason": "duplicate", "detail": "Already crawled on SmartRecruiters as `rentokilinitial1`.", "excludedAt": "2026-10-07" },
54
+ {
55
+ "ats": "workday",
56
+ "token": "accenture.wd103.myworkdayjobs.com/accenture/AccentureCareers",
57
+ "name": "Accenture",
58
+ "reason": "duplicate",
59
+ "detail": "Accenture's India roles already arrive through the `accenture-india` portal, which is the single largest source in the catalog at 24,400 live roles. The global Workday board overlaps it.",
60
+ "excludedAt": "2026-10-07"
61
+ }
62
+ ]
63
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openings",
3
- "version": "0.1.62",
3
+ "version": "0.1.64",
4
4
  "description": "A free, candidate-safe job-search substrate for AI agents",
5
5
  "license": "MIT",
6
6
  "repository": { "type": "git", "url": "git+https://github.com/abhay-avagama/hiring-agent.git" },
@@ -13,6 +13,7 @@
13
13
  "files": [
14
14
  "src/",
15
15
  "data/companies.json",
16
+ "data/excluded.json",
16
17
  "skills/openings/",
17
18
  ".codex-plugin/plugin.json",
18
19
  ".mcp.json",
@@ -27,8 +27,12 @@ export function resolveAggregatorUrl(value: string | undefined): string | undefi
27
27
  }
28
28
 
29
29
  /** Posts each successfully crawled partition to the aggregator. Only job data is sent, never resume content. */
30
- export function createCrawlReporter(options: { url: string; fetcher?: Fetch; timeoutMs?: number; attempts?: number; sleep?: (ms: number) => Promise<void> }) {
30
+ export function createCrawlReporter(options: { url: string; fetcher?: Fetch; timeoutMs?: number; attempts?: number; secret?: string; sleep?: (ms: number) => Promise<void> }) {
31
31
  const fetcher = options.fetcher ?? globalThis.fetch;
32
+ // Without this the only thing standing between a stranger and an employer's listings is the slug, ats and
33
+ // token triple — all three of which are published in data/companies.json inside the npm tarball. An empty
34
+ // report for a real slug empties that employer from the site and answers 200.
35
+ const secret = options.secret ?? process.env.OPENINGS_INGEST_SECRET;
32
36
  const target = endpoint(options.url, "v1/crawls");
33
37
  const attempts = Math.max(1, options.attempts ?? 3);
34
38
  const sleep = options.sleep ?? ((ms: number) => new Promise<void>((resolve) => setTimeout(resolve, ms)));
@@ -42,7 +46,8 @@ export function createCrawlReporter(options: { url: string; fetcher?: Fetch; tim
42
46
  try {
43
47
  const response = await fetcher(target, {
44
48
  method: "POST",
45
- headers: { "content-type": "application/json", "content-encoding": "gzip" },
49
+ headers: { "content-type": "application/json", "content-encoding": "gzip",
50
+ ...(secret ? { authorization: `Bearer ${secret}` } : {}) },
46
51
  body,
47
52
  // Big employers send tens of megabytes and the aggregator ingests one report at a time; give them room.
48
53
  signal: AbortSignal.timeout(options.timeoutMs ?? 120_000),
package/src/version.ts CHANGED
@@ -1 +1 @@
1
- export const VERSION = "0.1.62";
1
+ export const VERSION = "0.1.64";