openings 0.1.22 → 0.1.24
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-plugin/plugin.json +1 -1
- package/data/companies.json +19624 -1099
- package/package.json +1 -1
- package/src/board-verification.ts +1 -1
- package/src/job-recommendations.ts +16 -10
- package/src/providers.ts +3 -2
- package/src/version.ts +1 -1
package/package.json
CHANGED
|
@@ -157,7 +157,7 @@ function withAttempt(lead: EnrichmentLead, attempt: LeadAttempt): EnrichmentLead
|
|
|
157
157
|
}
|
|
158
158
|
|
|
159
159
|
function uniqueSlug(lead: EnrichmentLead, used: Set<string>): string | null {
|
|
160
|
-
const raw = lead.ats === "workday" ? (lead.token.split("/")[1] ?? lead.token) : lead.token;
|
|
160
|
+
const raw = lead.ats === "workday" ? (lead.token.split("/")[1] ?? lead.token) : lead.ats === "keka" ? lead.token.split("/")[0]! : lead.ats === "zohorecruit" ? lead.token.split(".")[0]! : lead.token;
|
|
161
161
|
const base = raw.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || lead.ats;
|
|
162
162
|
for (const candidate of [base, `${lead.ats}-${base}`]) if (!used.has(candidate)) return candidate;
|
|
163
163
|
return null;
|
|
@@ -22,7 +22,8 @@ export interface RecommendJobsResult {
|
|
|
22
22
|
profile: CandidateProfile;
|
|
23
23
|
matches: JobMatch[];
|
|
24
24
|
exploration: ReturnType<typeof matchJobs>["exploration"];
|
|
25
|
-
|
|
25
|
+
/** Counts by reason plus a small sample: the full list once ran to every job in the index and blew past MCP payload limits. */
|
|
26
|
+
filteredOut: { total: number; byReason: Record<string, number>; sample: FilteredJob[] };
|
|
26
27
|
assumptions: string[];
|
|
27
28
|
ranking: { mode: "evidence" | "keyword"; minimumPercent: number };
|
|
28
29
|
snapshot: SnapshotStatus;
|
|
@@ -143,15 +144,20 @@ function matchSnapshot(profile: CandidateProfile, intent: CandidateIntent, snaps
|
|
|
143
144
|
return matchJobs(profile, intent, jobs, jobs.length, ranking);
|
|
144
145
|
}
|
|
145
146
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
147
|
+
const EXCERPT_CHARS = 280;
|
|
148
|
+
/** Transport shape: descriptions become excerpts (get_job returns the full text), exploration lists carry the same compact matches, filtered jobs are summarised. */
|
|
149
|
+
function limitMatching(matching: ReturnType<typeof matchSnapshot>, limit = 20): Omit<ReturnType<typeof matchSnapshot>, "filteredOut"> & { filteredOut: RecommendJobsResult["filteredOut"] } {
|
|
150
|
+
const compact = (match: JobMatch): JobMatch => ({ ...match, job: { ...match.job, description: match.job.description.length > EXCERPT_CHARS ? `${match.job.description.slice(0, EXCERPT_CHARS).trimEnd()}…` : match.job.description } });
|
|
151
|
+
const matches = matching.matches.slice(0, Math.max(0, limit)).map(compact);
|
|
152
|
+
const byId = new Map(matches.map((match) => [match.job.id, match]));
|
|
153
|
+
const pick = (list: JobMatch[]) => list.filter((match) => byId.has(match.job.id)).map((match) => byId.get(match.job.id)!);
|
|
154
|
+
const byReason: Record<string, number> = {};
|
|
155
|
+
for (const entry of matching.filteredOut) for (const reason of entry.reasons) { const key = reason.split(":")[0]!; byReason[key] = (byReason[key] ?? 0) + 1; }
|
|
156
|
+
return {
|
|
157
|
+
...matching, matches,
|
|
158
|
+
exploration: { ...matching.exploration, directMatches: pick(matching.exploration.directMatches), hiddenMatches: pick(matching.exploration.hiddenMatches), stretchMatches: pick(matching.exploration.stretchMatches) },
|
|
159
|
+
filteredOut: { total: matching.filteredOut.length, byReason, sample: matching.filteredOut.slice(0, 20) },
|
|
160
|
+
};
|
|
155
161
|
}
|
|
156
162
|
|
|
157
163
|
function preCutoffMatchCount(matching: ReturnType<typeof matchSnapshot>): number {
|
package/src/providers.ts
CHANGED
|
@@ -192,10 +192,11 @@ const keka: ProviderSpec = {
|
|
|
192
192
|
resolve(url) {
|
|
193
193
|
const tenant = /^([a-z0-9-]+)\.keka\.com$/i.exec(url.hostname)?.[1]?.toLowerCase();
|
|
194
194
|
if (!tenant || ["www", "app", "hr", "academy", "developers", "help", "cdn", "api"].includes(tenant) && tenant !== "hr") return null;
|
|
195
|
-
const org = /\/(?:careers\/api\/embedjobs
|
|
195
|
+
const org = /\/(?:careers\/api\/embedjobs(?:\/default\/active)?|ats\/documents)\/([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/i.exec(url.pathname)?.[1];
|
|
196
196
|
return org ? `${tenant}/${org.toLowerCase()}` : null;
|
|
197
197
|
},
|
|
198
|
-
|
|
198
|
+
// The embed page is a real listing a person can open and it carries the org id, so the canonical URL round-trips through resolve().
|
|
199
|
+
canonicalUrl: (token) => `https://${token.split("/")[0]}.keka.com/careers/api/embedjobs/${token.split("/")[1] ?? ""}`,
|
|
199
200
|
endpoint: (token) => `https://${token.split("/")[0]}.keka.com/careers/api/embedjobs/default/active/${token.split("/")[1] ?? ""}`,
|
|
200
201
|
jobsFromBody: (body) => asRecords(body),
|
|
201
202
|
providerName: () => "",
|
package/src/version.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const VERSION = "0.1.
|
|
1
|
+
export const VERSION = "0.1.24";
|