job_ops-mcp 0.3.0
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/.env.example +33 -0
- package/LICENSE +21 -0
- package/README.md +400 -0
- package/config/profile.example.yml +67 -0
- package/cv.example.md +53 -0
- package/dist/cli.js +385 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.js +63 -0
- package/dist/config.js.map +1 -0
- package/dist/core/browser.js +27 -0
- package/dist/core/browser.js.map +1 -0
- package/dist/core/content_hash.js +11 -0
- package/dist/core/content_hash.js.map +1 -0
- package/dist/core/csv.js +107 -0
- package/dist/core/csv.js.map +1 -0
- package/dist/core/cv_parse.js +201 -0
- package/dist/core/cv_parse.js.map +1 -0
- package/dist/core/html.js +10 -0
- package/dist/core/html.js.map +1 -0
- package/dist/core/jd_normalize.js +99 -0
- package/dist/core/jd_normalize.js.map +1 -0
- package/dist/core/jobs.js +106 -0
- package/dist/core/jobs.js.map +1 -0
- package/dist/core/llm.js +227 -0
- package/dist/core/llm.js.map +1 -0
- package/dist/core/modes.js +55 -0
- package/dist/core/modes.js.map +1 -0
- package/dist/core/outreach_safety.js +77 -0
- package/dist/core/outreach_safety.js.map +1 -0
- package/dist/core/profile.js +88 -0
- package/dist/core/profile.js.map +1 -0
- package/dist/core/providers/amazon.js +36 -0
- package/dist/core/providers/amazon.js.map +1 -0
- package/dist/core/providers/ashby.js +31 -0
- package/dist/core/providers/ashby.js.map +1 -0
- package/dist/core/providers/google.js +46 -0
- package/dist/core/providers/google.js.map +1 -0
- package/dist/core/providers/greenhouse.js +55 -0
- package/dist/core/providers/greenhouse.js.map +1 -0
- package/dist/core/providers/http.js +36 -0
- package/dist/core/providers/http.js.map +1 -0
- package/dist/core/providers/index.js +53 -0
- package/dist/core/providers/index.js.map +1 -0
- package/dist/core/providers/lever.js +32 -0
- package/dist/core/providers/lever.js.map +1 -0
- package/dist/core/providers/playwright_generic.js +53 -0
- package/dist/core/providers/playwright_generic.js.map +1 -0
- package/dist/core/providers/types.js +2 -0
- package/dist/core/providers/types.js.map +1 -0
- package/dist/core/providers/workday.js +44 -0
- package/dist/core/providers/workday.js.map +1 -0
- package/dist/core/render.js +253 -0
- package/dist/core/render.js.map +1 -0
- package/dist/core/reports.js +257 -0
- package/dist/core/reports.js.map +1 -0
- package/dist/core/resources.js +40 -0
- package/dist/core/resources.js.map +1 -0
- package/dist/core/scan_engine.js +164 -0
- package/dist/core/scan_engine.js.map +1 -0
- package/dist/core/scheduler.js +117 -0
- package/dist/core/scheduler.js.map +1 -0
- package/dist/db.js +60 -0
- package/dist/db.js.map +1 -0
- package/dist/http/app.js +35 -0
- package/dist/http/app.js.map +1 -0
- package/dist/http/dashboard.js +131 -0
- package/dist/http/dashboard.js.map +1 -0
- package/dist/mcp/define.js +35 -0
- package/dist/mcp/define.js.map +1 -0
- package/dist/mcp/server.js +103 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/tools/apply_prefill.js +167 -0
- package/dist/mcp/tools/apply_prefill.js.map +1 -0
- package/dist/mcp/tools/batch_evaluate.js +143 -0
- package/dist/mcp/tools/batch_evaluate.js.map +1 -0
- package/dist/mcp/tools/evaluate_job.js +181 -0
- package/dist/mcp/tools/evaluate_job.js.map +1 -0
- package/dist/mcp/tools/generate_materials.js +126 -0
- package/dist/mcp/tools/generate_materials.js.map +1 -0
- package/dist/mcp/tools/get_report.js +24 -0
- package/dist/mcp/tools/get_report.js.map +1 -0
- package/dist/mcp/tools/ops.js +321 -0
- package/dist/mcp/tools/ops.js.map +1 -0
- package/dist/mcp/tools/outreach.js +481 -0
- package/dist/mcp/tools/outreach.js.map +1 -0
- package/dist/mcp/tools/render_pdf.js +27 -0
- package/dist/mcp/tools/render_pdf.js.map +1 -0
- package/dist/mcp/tools/scan_portals.js +35 -0
- package/dist/mcp/tools/scan_portals.js.map +1 -0
- package/dist/mcp/tools/scheduler.js +32 -0
- package/dist/mcp/tools/scheduler.js.map +1 -0
- package/dist/mcp/tools/stories.js +172 -0
- package/dist/mcp/tools/stories.js.map +1 -0
- package/dist/mcp/tools/tracker.js +183 -0
- package/dist/mcp/tools/tracker.js.map +1 -0
- package/dist/mcp/tools/visa.js +219 -0
- package/dist/mcp/tools/visa.js.map +1 -0
- package/dist/migrations/001_initial.sql +505 -0
- package/dist/migrations/002_llm_and_digest.sql +42 -0
- package/dist/server.js +55 -0
- package/dist/server.js.map +1 -0
- package/fonts/dm-sans-latin-ext.woff2 +0 -0
- package/fonts/dm-sans-latin.woff2 +0 -0
- package/fonts/space-grotesk-latin-ext.woff2 +0 -0
- package/fonts/space-grotesk-latin.woff2 +0 -0
- package/modes/career_packet.md +91 -0
- package/modes/negotiation_playbook.md +64 -0
- package/modes/outreach_tone.md +80 -0
- package/modes/report_format.md +83 -0
- package/modes/rubric.md +119 -0
- package/modes/tailoring_rules.md +102 -0
- package/package.json +67 -0
- package/portals.example.yml +95 -0
- package/templates/cover-template.html +64 -0
- package/templates/cv-template.html +421 -0
- package/templates/cv-template.tex +123 -0
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
const ALLOWED_HOSTS = new Set([
|
|
2
|
+
'boards-api.greenhouse.io',
|
|
3
|
+
'boards.greenhouse.io',
|
|
4
|
+
'job-boards.greenhouse.io',
|
|
5
|
+
'job-boards.eu.greenhouse.io',
|
|
6
|
+
]);
|
|
7
|
+
function assertHost(url) {
|
|
8
|
+
const p = new URL(url);
|
|
9
|
+
if (p.protocol !== 'https:')
|
|
10
|
+
throw new Error(`greenhouse: must be HTTPS: ${url}`);
|
|
11
|
+
if (!ALLOWED_HOSTS.has(p.hostname)) {
|
|
12
|
+
throw new Error(`greenhouse: untrusted host ${p.hostname}`);
|
|
13
|
+
}
|
|
14
|
+
return url;
|
|
15
|
+
}
|
|
16
|
+
function resolveApi(entry) {
|
|
17
|
+
if (entry.api) {
|
|
18
|
+
try {
|
|
19
|
+
return assertHost(entry.api);
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
if (entry.greenhouse_slug)
|
|
26
|
+
return `https://boards-api.greenhouse.io/v1/boards/${entry.greenhouse_slug}/jobs`;
|
|
27
|
+
const url = entry.careers_url || '';
|
|
28
|
+
const m = url.match(/job-boards(?:\.eu)?\.greenhouse\.io\/([^/?#]+)/);
|
|
29
|
+
if (m)
|
|
30
|
+
return `https://boards-api.greenhouse.io/v1/boards/${m[1]}/jobs`;
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
const greenhouse = {
|
|
34
|
+
id: 'greenhouse',
|
|
35
|
+
detect(entry) {
|
|
36
|
+
const url = resolveApi(entry);
|
|
37
|
+
return url ? { url } : null;
|
|
38
|
+
},
|
|
39
|
+
async fetch(entry, ctx) {
|
|
40
|
+
const url = resolveApi(entry);
|
|
41
|
+
if (!url)
|
|
42
|
+
throw new Error(`greenhouse: cannot derive API URL for ${entry.name}`);
|
|
43
|
+
assertHost(url);
|
|
44
|
+
const json = await ctx.fetchJson(url, { redirect: 'error' });
|
|
45
|
+
const jobs = Array.isArray(json?.jobs) ? json.jobs : [];
|
|
46
|
+
return jobs.filter((j) => j.absolute_url).map((j) => ({
|
|
47
|
+
title: j.title || '',
|
|
48
|
+
url: j.absolute_url,
|
|
49
|
+
company: entry.name,
|
|
50
|
+
location: j.location?.name || '',
|
|
51
|
+
}));
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
export default greenhouse;
|
|
55
|
+
//# sourceMappingURL=greenhouse.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"greenhouse.js","sourceRoot":"","sources":["../../../src/core/providers/greenhouse.ts"],"names":[],"mappings":"AAGA,MAAM,aAAa,GAAG,IAAI,GAAG,CAAC;IAC5B,0BAA0B;IAC1B,sBAAsB;IACtB,0BAA0B;IAC1B,6BAA6B;CAC9B,CAAC,CAAC;AAEH,SAAS,UAAU,CAAC,GAAW;IAC7B,MAAM,CAAC,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;IACvB,IAAI,CAAC,CAAC,QAAQ,KAAK,QAAQ;QAAE,MAAM,IAAI,KAAK,CAAC,8BAA8B,GAAG,EAAE,CAAC,CAAC;IAClF,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC;QACnC,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC;IAC9D,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,UAAU,CAAC,KAA0B;IAC5C,IAAI,KAAK,CAAC,GAAG,EAAE,CAAC;QAAC,IAAI,CAAC;YAAC,OAAO,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAAC,CAAC;QAAC,MAAM,CAAC;YAAC,OAAO,IAAI,CAAC;QAAC,CAAC;IAAC,CAAC;IAC/E,IAAI,KAAK,CAAC,eAAe;QAAE,OAAO,8CAA8C,KAAK,CAAC,eAAe,OAAO,CAAC;IAC7G,MAAM,GAAG,GAAG,KAAK,CAAC,WAAW,IAAI,EAAE,CAAC;IACpC,MAAM,CAAC,GAAG,GAAG,CAAC,KAAK,CAAC,gDAAgD,CAAC,CAAC;IACtE,IAAI,CAAC;QAAE,OAAO,8CAA8C,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;IACxE,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,GAAa;IAC3B,EAAE,EAAE,YAAY;IAChB,MAAM,CAAC,KAAK;QACV,MAAM,GAAG,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC;QAC9B,OAAO,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;IAC9B,CAAC;IACD,KAAK,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG;QACpB,MAAM,GAAG,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC;QAC9B,IAAI,CAAC,GAAG;YAAE,MAAM,IAAI,KAAK,CAAC,yCAAyC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;QACjF,UAAU,CAAC,GAAG,CAAC,CAAC;QAChB,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC;QAC7D,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;QACxD,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,GAAG,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC;YAC9D,KAAK,EAAK,CAAC,CAAC,KAAK,IAAI,EAAE;YACvB,GAAG,EAAO,CAAC,CAAC,YAAY;YACxB,OAAO,EAAG,KAAK,CAAC,IAAI;YACpB,QAAQ,EAAE,CAAC,CAAC,QAAQ,EAAE,IAAI,IAAI,EAAE;SACjC,CAAC,CAAC,CAAC;IACN,CAAC;CACF,CAAC;AACF,eAAe,UAAU,CAAC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// HTTP transport for providers (ported from career-ops/providers/_http.mjs).
|
|
2
|
+
const DEFAULT_TIMEOUT_MS = 12_000;
|
|
3
|
+
const DEFAULT_USER_AGENT = 'Mozilla/5.0 (compatible; mcp-jsa/0.2)';
|
|
4
|
+
export async function fetchWithTimeout(url, opts = {}) {
|
|
5
|
+
const controller = new AbortController();
|
|
6
|
+
const timer = setTimeout(() => controller.abort(), opts.timeoutMs ?? DEFAULT_TIMEOUT_MS);
|
|
7
|
+
try {
|
|
8
|
+
const res = await fetch(url, {
|
|
9
|
+
method: opts.method ?? 'GET',
|
|
10
|
+
headers: { 'user-agent': DEFAULT_USER_AGENT, ...opts.headers },
|
|
11
|
+
body: opts.body,
|
|
12
|
+
redirect: opts.redirect ?? 'follow',
|
|
13
|
+
signal: controller.signal,
|
|
14
|
+
});
|
|
15
|
+
if (!res.ok) {
|
|
16
|
+
const text = await res.text().catch(() => '');
|
|
17
|
+
const snip = text.replace(/\s+/g, ' ').trim().slice(0, 300);
|
|
18
|
+
const err = new Error(snip ? `HTTP ${res.status}: ${snip}` : `HTTP ${res.status}`);
|
|
19
|
+
err.status = res.status;
|
|
20
|
+
throw err;
|
|
21
|
+
}
|
|
22
|
+
return res;
|
|
23
|
+
}
|
|
24
|
+
finally {
|
|
25
|
+
clearTimeout(timer);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
export async function fetchJson(url, opts = {}) {
|
|
29
|
+
const res = await fetchWithTimeout(url, opts);
|
|
30
|
+
return res.json();
|
|
31
|
+
}
|
|
32
|
+
export async function fetchText(url, opts = {}) {
|
|
33
|
+
const res = await fetchWithTimeout(url, opts);
|
|
34
|
+
return res.text();
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=http.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.js","sourceRoot":"","sources":["../../../src/core/providers/http.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,MAAM,kBAAkB,GAAG,MAAM,CAAC;AAClC,MAAM,kBAAkB,GAAG,uCAAuC,CAAC;AAEnE,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,GAAW,EAAE,OAAY,EAAE;IAChE,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;IACzC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,IAAI,CAAC,SAAS,IAAI,kBAAkB,CAAC,CAAC;IACzF,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;YAC3B,MAAM,EAAG,IAAI,CAAC,MAAM,IAAK,KAAK;YAC9B,OAAO,EAAE,EAAE,YAAY,EAAE,kBAAkB,EAAE,GAAG,IAAI,CAAC,OAAO,EAAE;YAC9D,IAAI,EAAK,IAAI,CAAC,IAAI;YAClB,QAAQ,EAAE,IAAI,CAAC,QAAQ,IAAI,QAAQ;YACnC,MAAM,EAAG,UAAU,CAAC,MAAM;SAC3B,CAAC,CAAC;QACH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;YACZ,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;YAC9C,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;YAC5D,MAAM,GAAG,GAAQ,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,GAAG,CAAC,MAAM,KAAK,IAAI,EAAE,CAAC,CAAC,CAAC,QAAQ,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;YACxF,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC;YACxB,MAAM,GAAG,CAAC;QACZ,CAAC;QACD,OAAO,GAAG,CAAC;IACb,CAAC;YAAS,CAAC;QACT,YAAY,CAAC,KAAK,CAAC,CAAC;IACtB,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,GAAW,EAAE,OAAY,EAAE;IACzD,MAAM,GAAG,GAAG,MAAM,gBAAgB,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IAC9C,OAAO,GAAG,CAAC,IAAI,EAAE,CAAC;AACpB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,GAAW,EAAE,OAAY,EAAE;IACzD,MAAM,GAAG,GAAG,MAAM,gBAAgB,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IAC9C,OAAO,GAAG,CAAC,IAAI,EAAE,CAAC;AACpB,CAAC"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// Provider registry. Explicit imports so the build is deterministic and the IDE shows
|
|
2
|
+
// provider sources at a glance.
|
|
3
|
+
import { fetchJson, fetchText } from './http.js';
|
|
4
|
+
import { getSharedBrowser, closeSharedBrowser } from '../browser.js';
|
|
5
|
+
import greenhouse from './greenhouse.js';
|
|
6
|
+
import ashby from './ashby.js';
|
|
7
|
+
import lever from './lever.js';
|
|
8
|
+
import workday from './workday.js';
|
|
9
|
+
import amazon from './amazon.js';
|
|
10
|
+
import google from './google.js';
|
|
11
|
+
import playwrightGeneric from './playwright_generic.js';
|
|
12
|
+
export const PROVIDERS = new Map([
|
|
13
|
+
// Order matters for detect() fallback chain — most specific first (insertion order =
|
|
14
|
+
// detect-priority). Greenhouse/Ashby/Lever are the most specific (exact host match);
|
|
15
|
+
// Workday matches a tenant URL pattern; Amazon matches a domain substring; Google is
|
|
16
|
+
// hostname-based; playwright_generic only runs when explicitly opted-in.
|
|
17
|
+
greenhouse,
|
|
18
|
+
ashby,
|
|
19
|
+
lever,
|
|
20
|
+
workday,
|
|
21
|
+
amazon,
|
|
22
|
+
google,
|
|
23
|
+
playwrightGeneric,
|
|
24
|
+
].map(p => [p.id, p]));
|
|
25
|
+
export function resolveProvider(entry, opts = {}) {
|
|
26
|
+
if (entry.provider) {
|
|
27
|
+
const p = PROVIDERS.get(entry.provider);
|
|
28
|
+
if (!p)
|
|
29
|
+
return null;
|
|
30
|
+
return p;
|
|
31
|
+
}
|
|
32
|
+
for (const p of PROVIDERS.values()) {
|
|
33
|
+
if (opts.skip?.has(p.id))
|
|
34
|
+
continue;
|
|
35
|
+
try {
|
|
36
|
+
if (p.detect?.(entry))
|
|
37
|
+
return p;
|
|
38
|
+
}
|
|
39
|
+
catch { /* ignore detect failures */ }
|
|
40
|
+
}
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
// The process-wide Chromium lives in core/browser.ts; render_pdf, apply_prefill, and
|
|
44
|
+
// closed-board providers all share it.
|
|
45
|
+
export async function closeProviderBrowser() { await closeSharedBrowser(); }
|
|
46
|
+
export function makeProviderCtx() {
|
|
47
|
+
return {
|
|
48
|
+
fetchJson,
|
|
49
|
+
fetchText,
|
|
50
|
+
withBrowser: async (fn) => fn(await getSharedBrowser()),
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/core/providers/index.ts"],"names":[],"mappings":"AAAA,sFAAsF;AACtF,gCAAgC;AAGhC,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAErE,OAAO,UAAU,MAAa,iBAAiB,CAAC;AAChD,OAAO,KAAK,MAAkB,YAAY,CAAC;AAC3C,OAAO,KAAK,MAAkB,YAAY,CAAC;AAC3C,OAAO,OAAO,MAAgB,cAAc,CAAC;AAC7C,OAAO,MAAM,MAAiB,aAAa,CAAC;AAC5C,OAAO,MAAM,MAAiB,aAAa,CAAC;AAC5C,OAAO,iBAAiB,MAAM,yBAAyB,CAAC;AAExD,MAAM,CAAC,MAAM,SAAS,GAA0B,IAAI,GAAG,CAAC;IACtD,qFAAqF;IACrF,qFAAqF;IACrF,qFAAqF;IACrF,yEAAyE;IACzE,UAAU;IACV,KAAK;IACL,KAAK;IACL,OAAO;IACP,MAAM;IACN,MAAM;IACN,iBAAiB;CAClB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAU,CAAC,CAAC,CAAC;AAMhC,MAAM,UAAU,eAAe,CAAC,KAA0B,EAAE,OAAuB,EAAE;IACnF,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;QACnB,MAAM,CAAC,GAAG,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QACxC,IAAI,CAAC,CAAC;YAAE,OAAO,IAAI,CAAC;QACpB,OAAO,CAAC,CAAC;IACX,CAAC;IACD,KAAK,MAAM,CAAC,IAAI,SAAS,CAAC,MAAM,EAAE,EAAE,CAAC;QACnC,IAAI,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;YAAE,SAAS;QACnC,IAAI,CAAC;YACH,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC;gBAAE,OAAO,CAAC,CAAC;QAClC,CAAC;QAAC,MAAM,CAAC,CAAC,4BAA4B,CAAC,CAAC;IAC1C,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,qFAAqF;AACrF,uCAAuC;AACvC,MAAM,CAAC,KAAK,UAAU,oBAAoB,KAAoB,MAAM,kBAAkB,EAAE,CAAC,CAAC,CAAC;AAE3F,MAAM,UAAU,eAAe;IAC7B,OAAO;QACL,SAAS;QACT,SAAS;QACT,WAAW,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,MAAM,gBAAgB,EAAE,CAAC;KACxD,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
function resolveApi(entry) {
|
|
2
|
+
if (entry.lever_slug)
|
|
3
|
+
return `https://api.lever.co/v0/postings/${entry.lever_slug}`;
|
|
4
|
+
const url = entry.careers_url || '';
|
|
5
|
+
const m = url.match(/jobs\.lever\.co\/([^/?#]+)/);
|
|
6
|
+
if (!m)
|
|
7
|
+
return null;
|
|
8
|
+
return `https://api.lever.co/v0/postings/${m[1]}`;
|
|
9
|
+
}
|
|
10
|
+
const lever = {
|
|
11
|
+
id: 'lever',
|
|
12
|
+
detect(entry) {
|
|
13
|
+
const url = resolveApi(entry);
|
|
14
|
+
return url ? { url } : null;
|
|
15
|
+
},
|
|
16
|
+
async fetch(entry, ctx) {
|
|
17
|
+
const url = resolveApi(entry);
|
|
18
|
+
if (!url)
|
|
19
|
+
throw new Error(`lever: cannot derive API URL for ${entry.name}`);
|
|
20
|
+
const json = await ctx.fetchJson(url);
|
|
21
|
+
if (!Array.isArray(json))
|
|
22
|
+
return [];
|
|
23
|
+
return json.map((j) => ({
|
|
24
|
+
title: j.text || '',
|
|
25
|
+
url: j.hostedUrl || '',
|
|
26
|
+
company: entry.name,
|
|
27
|
+
location: j.categories?.location || '',
|
|
28
|
+
}));
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
export default lever;
|
|
32
|
+
//# sourceMappingURL=lever.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lever.js","sourceRoot":"","sources":["../../../src/core/providers/lever.ts"],"names":[],"mappings":"AAGA,SAAS,UAAU,CAAC,KAA0B;IAC5C,IAAI,KAAK,CAAC,UAAU;QAAE,OAAO,oCAAoC,KAAK,CAAC,UAAU,EAAE,CAAC;IACpF,MAAM,GAAG,GAAG,KAAK,CAAC,WAAW,IAAI,EAAE,CAAC;IACpC,MAAM,CAAC,GAAG,GAAG,CAAC,KAAK,CAAC,4BAA4B,CAAC,CAAC;IAClD,IAAI,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IACpB,OAAO,oCAAoC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AACpD,CAAC;AAED,MAAM,KAAK,GAAa;IACtB,EAAE,EAAE,OAAO;IACX,MAAM,CAAC,KAAK;QACV,MAAM,GAAG,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC;QAC9B,OAAO,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;IAC9B,CAAC;IACD,KAAK,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG;QACpB,MAAM,GAAG,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC;QAC9B,IAAI,CAAC,GAAG;YAAE,MAAM,IAAI,KAAK,CAAC,oCAAoC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;QAC5E,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;QACtC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC;YAAE,OAAO,EAAE,CAAC;QACpC,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC;YAC3B,KAAK,EAAK,CAAC,CAAC,IAAI,IAAI,EAAE;YACtB,GAAG,EAAO,CAAC,CAAC,SAAS,IAAI,EAAE;YAC3B,OAAO,EAAG,KAAK,CAAC,IAAI;YACpB,QAAQ,EAAE,CAAC,CAAC,UAAU,EAAE,QAAQ,IAAI,EAAE;SACvC,CAAC,CAAC,CAAC;IACN,CAAC;CACF,CAAC;AACF,eAAe,KAAK,CAAC"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
const provider = {
|
|
2
|
+
id: 'playwright_generic',
|
|
3
|
+
detect(entry) {
|
|
4
|
+
return (entry.provider ?? '') === 'playwright_generic' ? { url: entry.careers_url ?? '' } : null;
|
|
5
|
+
},
|
|
6
|
+
async fetch(entry, ctx) {
|
|
7
|
+
const e = entry;
|
|
8
|
+
if (!e.careers_url)
|
|
9
|
+
throw new Error(`playwright_generic: careers_url required for ${entry.name}`);
|
|
10
|
+
if (!e.selectors?.item)
|
|
11
|
+
throw new Error(`playwright_generic: selectors.item required for ${entry.name}`);
|
|
12
|
+
if (!ctx.withBrowser)
|
|
13
|
+
return [];
|
|
14
|
+
const sel = e.selectors;
|
|
15
|
+
return ctx.withBrowser(async (browser) => {
|
|
16
|
+
const page = await browser.newPage();
|
|
17
|
+
try {
|
|
18
|
+
await page.goto(e.careers_url, { waitUntil: 'networkidle', timeout: 30_000 });
|
|
19
|
+
if (sel.wait_for)
|
|
20
|
+
await page.waitForSelector(sel.wait_for, { timeout: 15_000 }).catch(() => undefined);
|
|
21
|
+
else
|
|
22
|
+
await page.waitForTimeout(sel.wait_ms ?? 1500);
|
|
23
|
+
const items = await page.$$eval(sel.item, (els, cfg) => {
|
|
24
|
+
return els.map(el => {
|
|
25
|
+
const a = el.matches?.('a') ? el : el.querySelector?.('a');
|
|
26
|
+
const href = a?.[cfg.href_attr ?? 'href'] ?? a?.getAttribute?.(cfg.href_attr ?? 'href') ?? '';
|
|
27
|
+
const titleEl = cfg.title ? el.querySelector(cfg.title) : el;
|
|
28
|
+
const title = cfg.title_attr
|
|
29
|
+
? (titleEl?.getAttribute?.(cfg.title_attr) ?? '')
|
|
30
|
+
: (titleEl?.innerText ?? '').trim();
|
|
31
|
+
const locEl = cfg.location ? el.querySelector(cfg.location) : null;
|
|
32
|
+
const location = locEl ? (locEl.innerText ?? '').trim() : '';
|
|
33
|
+
return { title, href, location };
|
|
34
|
+
});
|
|
35
|
+
}, sel);
|
|
36
|
+
const base = new URL(e.careers_url);
|
|
37
|
+
return items
|
|
38
|
+
.filter((j) => j.title && j.href)
|
|
39
|
+
.map((j) => ({
|
|
40
|
+
title: j.title,
|
|
41
|
+
url: new URL(j.href, base).toString(),
|
|
42
|
+
company: entry.name,
|
|
43
|
+
location: j.location,
|
|
44
|
+
}));
|
|
45
|
+
}
|
|
46
|
+
finally {
|
|
47
|
+
await page.close();
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
export default provider;
|
|
53
|
+
//# sourceMappingURL=playwright_generic.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"playwright_generic.js","sourceRoot":"","sources":["../../../src/core/providers/playwright_generic.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/core/providers/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
function resolveEndpoints(entry) {
|
|
2
|
+
const candidate = entry.workday_url || entry.careers_url || '';
|
|
3
|
+
// Patterns we accept:
|
|
4
|
+
// https://acme.wd5.myworkdayjobs.com/external_career_site
|
|
5
|
+
// https://acme.wd5.myworkdayjobs.com/en-US/external_career_site
|
|
6
|
+
const m = candidate.match(/^(https?:\/\/([^.]+)\.wd\d+\.myworkdayjobs\.com)\/(?:[^/]+\/)?([^/?#]+)/i);
|
|
7
|
+
if (!m)
|
|
8
|
+
return null;
|
|
9
|
+
const origin = m[1];
|
|
10
|
+
const tenant = m[2];
|
|
11
|
+
const site = m[4];
|
|
12
|
+
return {
|
|
13
|
+
url: `${origin}/wday/cxs/${tenant}/${site}/jobs`,
|
|
14
|
+
tenant, site, origin,
|
|
15
|
+
pageBase: `${origin}/${site}/job`,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
const workday = {
|
|
19
|
+
id: 'workday',
|
|
20
|
+
detect(entry) {
|
|
21
|
+
const r = resolveEndpoints(entry);
|
|
22
|
+
return r ? { url: r.url } : null;
|
|
23
|
+
},
|
|
24
|
+
async fetch(entry, ctx) {
|
|
25
|
+
const r = resolveEndpoints(entry);
|
|
26
|
+
if (!r)
|
|
27
|
+
throw new Error(`workday: cannot derive endpoints for ${entry.name}`);
|
|
28
|
+
const body = JSON.stringify({ appliedFacets: {}, limit: 50, offset: 0, searchText: '' });
|
|
29
|
+
const json = await ctx.fetchJson(r.url, {
|
|
30
|
+
method: 'POST',
|
|
31
|
+
headers: { 'content-type': 'application/json', accept: 'application/json' },
|
|
32
|
+
body,
|
|
33
|
+
});
|
|
34
|
+
const postings = Array.isArray(json?.jobPostings) ? json.jobPostings : [];
|
|
35
|
+
return postings.map((j) => ({
|
|
36
|
+
title: j.title || '',
|
|
37
|
+
url: j.externalPath ? `${r.origin}${j.externalPath}` : (j.externalUrl || ''),
|
|
38
|
+
company: entry.name,
|
|
39
|
+
location: j.locationsText || '',
|
|
40
|
+
})).filter((j) => j.url);
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
export default workday;
|
|
44
|
+
//# sourceMappingURL=workday.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workday.js","sourceRoot":"","sources":["../../../src/core/providers/workday.ts"],"names":[],"mappings":"AAWA,SAAS,gBAAgB,CAAC,KAA0B;IAElD,MAAM,SAAS,GAAG,KAAK,CAAC,WAAW,IAAI,KAAK,CAAC,WAAW,IAAI,EAAE,CAAC;IAC/D,sBAAsB;IACtB,4DAA4D;IAC5D,kEAAkE;IAClE,MAAM,CAAC,GAAG,SAAS,CAAC,KAAK,CAAC,0EAA0E,CAAC,CAAC;IACtG,IAAI,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IACpB,MAAM,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IACpB,MAAM,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IACpB,MAAM,IAAI,GAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IACpB,OAAO;QACL,GAAG,EAAO,GAAG,MAAM,aAAa,MAAM,IAAI,IAAI,OAAO;QACrD,MAAM,EAAE,IAAI,EAAE,MAAM;QACpB,QAAQ,EAAE,GAAG,MAAM,IAAI,IAAI,MAAM;KAClC,CAAC;AACJ,CAAC;AAED,MAAM,OAAO,GAAa;IACxB,EAAE,EAAE,SAAS;IACb,MAAM,CAAC,KAAK;QACV,MAAM,CAAC,GAAG,gBAAgB,CAAC,KAAK,CAAC,CAAC;QAClC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;IACnC,CAAC;IACD,KAAK,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG;QACpB,MAAM,CAAC,GAAG,gBAAgB,CAAC,KAAK,CAAC,CAAC;QAClC,IAAI,CAAC,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,wCAAwC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;QAC9E,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,aAAa,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE,UAAU,EAAE,EAAE,EAAE,CAAC,CAAC;QACzF,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,EAAE;YACtC,MAAM,EAAG,MAAM;YACf,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,EAAE,kBAAkB,EAAE;YAC3E,IAAI;SACL,CAAC,CAAC;QACH,MAAM,QAAQ,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC;QAC1E,OAAO,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC;YAC/B,KAAK,EAAK,CAAC,CAAC,KAAK,IAAI,EAAE;YACvB,GAAG,EAAO,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,IAAI,EAAE,CAAC;YACjF,OAAO,EAAG,KAAK,CAAC,IAAI;YACpB,QAAQ,EAAE,CAAC,CAAC,aAAa,IAAI,EAAE;SAChC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IACnC,CAAC;CACF,CAAC;AACF,eAAe,OAAO,CAAC"}
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
// HTML → PDF renderer. Port of career-ops/generate-pdf.mjs with two changes:
|
|
2
|
+
// - the source HTML is built here from the parsed CV + the career-ops template
|
|
3
|
+
// - ATS unicode normalization stays (em-dash → -, smart quotes → ASCII, NBSP → space, ...)
|
|
4
|
+
//
|
|
5
|
+
// The CV template lives in templates/cv-template.html. Fonts live in fonts/ and are
|
|
6
|
+
// referenced from the template via ./fonts/*; we resolve them to absolute file:// URLs
|
|
7
|
+
// before letting Chromium render.
|
|
8
|
+
import { readFileSync, mkdirSync, writeFileSync } from 'node:fs';
|
|
9
|
+
import { resolve } from 'node:path';
|
|
10
|
+
import { randomUUID } from 'node:crypto';
|
|
11
|
+
import { config } from '../config.js';
|
|
12
|
+
import { parseCV } from './cv_parse.js';
|
|
13
|
+
import { getJob } from './jobs.js';
|
|
14
|
+
import { escapeHtml } from './html.js';
|
|
15
|
+
import { scanForVisaLeakage } from './outreach_safety.js';
|
|
16
|
+
import { getSharedBrowser, closeSharedBrowser } from './browser.js';
|
|
17
|
+
// Templates are static — load once.
|
|
18
|
+
let _cvTplCache = null;
|
|
19
|
+
let _coverTplCache = null;
|
|
20
|
+
function loadCvTemplate() {
|
|
21
|
+
if (_cvTplCache)
|
|
22
|
+
return _cvTplCache;
|
|
23
|
+
return _cvTplCache = readFileSync(resolve(config.templatesDir, 'cv-template.html'), 'utf-8');
|
|
24
|
+
}
|
|
25
|
+
function loadCoverTemplate() {
|
|
26
|
+
if (_coverTplCache)
|
|
27
|
+
return _coverTplCache;
|
|
28
|
+
return _coverTplCache = readFileSync(resolve(config.templatesDir, 'cover-template.html'), 'utf-8');
|
|
29
|
+
}
|
|
30
|
+
// Re-exported for `src/server.ts` shutdown, which still passes through this module.
|
|
31
|
+
export async function closeBrowser() { await closeSharedBrowser(); }
|
|
32
|
+
export async function renderPdf(args) {
|
|
33
|
+
const job = getJob(args.job_id);
|
|
34
|
+
if (!job)
|
|
35
|
+
throw new Error(`renderPdf: no job ${args.job_id}`);
|
|
36
|
+
// Hard rail: visa data must never enter a resume or cover letter. The materials
|
|
37
|
+
// generator scans tailored content before persisting; this catches anything the
|
|
38
|
+
// chat hand-types into cover_body.
|
|
39
|
+
if (args.cover_body) {
|
|
40
|
+
const leaks = scanForVisaLeakage(args.cover_body);
|
|
41
|
+
if (leaks.length)
|
|
42
|
+
throw new Error(`render_pdf: cover_body failed visa rail — ${JSON.stringify(leaks)}`);
|
|
43
|
+
}
|
|
44
|
+
const format = args.page_format ?? 'letter';
|
|
45
|
+
const cv = parseCV();
|
|
46
|
+
const outputs = [];
|
|
47
|
+
const browser = await getSharedBrowser();
|
|
48
|
+
if (args.kind === 'resume' || args.kind === 'both') {
|
|
49
|
+
const html = renderResumeHtml(cv);
|
|
50
|
+
const file = await writeAndPdf(browser, html, `resume-${slug(job.title)}-${args.job_id.slice(0, 8)}.pdf`, format);
|
|
51
|
+
outputs.push({ kind: 'resume', ...file });
|
|
52
|
+
}
|
|
53
|
+
if (args.kind === 'cover' || args.kind === 'both') {
|
|
54
|
+
if (!args.cover_body)
|
|
55
|
+
throw new Error('render_pdf: kind=cover|both requires cover_body');
|
|
56
|
+
const html = renderCoverHtml(cv, {
|
|
57
|
+
company: job.company_name_raw,
|
|
58
|
+
location: job.location_raw ?? '',
|
|
59
|
+
body: args.cover_body,
|
|
60
|
+
});
|
|
61
|
+
const file = await writeAndPdf(browser, html, `cover-${slug(job.title)}-${args.job_id.slice(0, 8)}.pdf`, format);
|
|
62
|
+
outputs.push({ kind: 'cover', ...file });
|
|
63
|
+
}
|
|
64
|
+
return outputs;
|
|
65
|
+
}
|
|
66
|
+
// ── Template fill ────────────────────────────────────────────────────────────
|
|
67
|
+
function renderResumeHtml(cv) {
|
|
68
|
+
const tpl = loadCvTemplate();
|
|
69
|
+
const replacements = {
|
|
70
|
+
LANG: 'en',
|
|
71
|
+
NAME: cv.name,
|
|
72
|
+
PHONE: cv.phone,
|
|
73
|
+
EMAIL: cv.email,
|
|
74
|
+
LINKEDIN_URL: cv.linkedin_url,
|
|
75
|
+
LINKEDIN_DISPLAY: cv.linkedin_display,
|
|
76
|
+
PORTFOLIO_URL: cv.portfolio_url,
|
|
77
|
+
PORTFOLIO_DISPLAY: cv.portfolio_display,
|
|
78
|
+
LOCATION: cv.location,
|
|
79
|
+
PAGE_WIDTH: '7.4in',
|
|
80
|
+
SECTION_SUMMARY: 'Professional Summary',
|
|
81
|
+
SECTION_COMPETENCIES: 'Core Competencies',
|
|
82
|
+
SECTION_EXPERIENCE: 'Work Experience',
|
|
83
|
+
SECTION_PROJECTS: 'Projects',
|
|
84
|
+
SECTION_EDUCATION: 'Education',
|
|
85
|
+
SECTION_CERTIFICATIONS: 'Certifications',
|
|
86
|
+
SECTION_SKILLS: 'Skills',
|
|
87
|
+
SUMMARY_TEXT: escapeHtml(cv.summary),
|
|
88
|
+
COMPETENCIES: cv.competencies.map(c => `<span class="competency-tag">${escapeHtml(c)}</span>`).join('\n '),
|
|
89
|
+
EXPERIENCE: cv.experiences.map(renderExperience).join('\n '),
|
|
90
|
+
PROJECTS: cv.projects.map(renderProject).join('\n '),
|
|
91
|
+
EDUCATION: cv.education.map(renderEducation).join('\n '),
|
|
92
|
+
CERTIFICATIONS: cv.certifications.length
|
|
93
|
+
? cv.certifications.map(renderCert).join('\n ')
|
|
94
|
+
: '<div class="cert-item"><span class="cert-title muted">—</span></div>',
|
|
95
|
+
SKILLS: renderSkills(cv.skills),
|
|
96
|
+
};
|
|
97
|
+
let html = tpl;
|
|
98
|
+
for (const [k, v] of Object.entries(replacements)) {
|
|
99
|
+
html = html.replaceAll(`{{${k}}}`, v ?? '');
|
|
100
|
+
}
|
|
101
|
+
return html;
|
|
102
|
+
}
|
|
103
|
+
function renderExperience(e) {
|
|
104
|
+
return `
|
|
105
|
+
<div class="job">
|
|
106
|
+
<div class="job-header">
|
|
107
|
+
<span class="job-company">${escapeHtml(e.company)}</span>
|
|
108
|
+
<span class="job-period">${escapeHtml(e.period)}</span>
|
|
109
|
+
</div>
|
|
110
|
+
<div class="job-role">${escapeHtml(e.role)}${e.location ? ` <span class="job-location">· ${escapeHtml(e.location)}</span>` : ''}</div>
|
|
111
|
+
<ul>
|
|
112
|
+
${(e.bullets ?? []).map((b) => `<li>${inlineBold(escapeHtml(b))}</li>`).join('\n ')}
|
|
113
|
+
</ul>
|
|
114
|
+
</div>`;
|
|
115
|
+
}
|
|
116
|
+
function renderProject(p) {
|
|
117
|
+
return `
|
|
118
|
+
<div class="project">
|
|
119
|
+
<span class="project-title">${escapeHtml(p.title)}</span>${p.badge ? ` <span class="project-badge">${escapeHtml(p.badge)}</span>` : ''}
|
|
120
|
+
<div class="project-desc">${inlineBold(escapeHtml(p.description))}</div>
|
|
121
|
+
${p.tech ? `<div class="project-tech">${escapeHtml(p.tech)}</div>` : ''}
|
|
122
|
+
</div>`;
|
|
123
|
+
}
|
|
124
|
+
function renderEducation(e) {
|
|
125
|
+
return `
|
|
126
|
+
<div class="edu-item">
|
|
127
|
+
<div class="edu-header">
|
|
128
|
+
<span class="edu-title">${escapeHtml(e.title)}${e.org ? `, <span class="edu-org">${escapeHtml(e.org)}</span>` : ''}</span>
|
|
129
|
+
<span class="edu-year">${escapeHtml(e.year)}</span>
|
|
130
|
+
</div>
|
|
131
|
+
${e.desc ? `<div class="edu-desc">${escapeHtml(e.desc)}</div>` : ''}
|
|
132
|
+
</div>`;
|
|
133
|
+
}
|
|
134
|
+
function renderCert(c) {
|
|
135
|
+
return `
|
|
136
|
+
<div class="cert-item">
|
|
137
|
+
<span class="cert-title">${escapeHtml(c.title)}</span>
|
|
138
|
+
<span class="cert-org">${escapeHtml(c.org)}</span>
|
|
139
|
+
<span class="cert-year">${escapeHtml(c.year)}</span>
|
|
140
|
+
</div>`;
|
|
141
|
+
}
|
|
142
|
+
function renderSkills(skills) {
|
|
143
|
+
if (!skills.length)
|
|
144
|
+
return '<div class="skill-item muted">—</div>';
|
|
145
|
+
return `<div class="skills-grid">
|
|
146
|
+
${skills.map(s => `<div class="skill-item"><span class="skill-category">${escapeHtml(s.category)}:</span> ${escapeHtml(s.items)}</div>`).join('\n ')}
|
|
147
|
+
</div>`;
|
|
148
|
+
}
|
|
149
|
+
function renderCoverHtml(cv, args) {
|
|
150
|
+
const tpl = loadCoverTemplate();
|
|
151
|
+
const contactBits = [cv.phone, cv.email, cv.linkedin_display, cv.portfolio_display].filter(Boolean);
|
|
152
|
+
const body = args.body
|
|
153
|
+
.split(/\n{2,}/)
|
|
154
|
+
.map(p => `<p>${escapeHtml(p.trim())}</p>`)
|
|
155
|
+
.join('\n');
|
|
156
|
+
const replacements = {
|
|
157
|
+
NAME: cv.name,
|
|
158
|
+
CONTACT_LINE: contactBits.map(escapeHtml).join(' · '),
|
|
159
|
+
DATE: new Date().toISOString().slice(0, 10),
|
|
160
|
+
COMPANY: escapeHtml(args.company),
|
|
161
|
+
COMPANY_LOCATION: args.location ? `, ${escapeHtml(args.location)}` : '',
|
|
162
|
+
BODY: body,
|
|
163
|
+
};
|
|
164
|
+
let html = tpl;
|
|
165
|
+
for (const [k, v] of Object.entries(replacements)) {
|
|
166
|
+
html = html.replaceAll(`{{${k}}}`, v ?? '');
|
|
167
|
+
}
|
|
168
|
+
return html;
|
|
169
|
+
}
|
|
170
|
+
// ── PDF write path (Playwright) ──────────────────────────────────────────────
|
|
171
|
+
async function writeAndPdf(browser, html, filename, format) {
|
|
172
|
+
// Rewrite font paths to absolute file:// URLs so Chromium can resolve them.
|
|
173
|
+
const withFonts = html.replace(/url\(['"]?\.\/fonts\//g, `url('file://${config.fontsDir}/`);
|
|
174
|
+
const normalized = normalizeAtsUnicode(withFonts);
|
|
175
|
+
// Stage HTML next to the PDF so the chat can debug a render visually if needed.
|
|
176
|
+
const subdir = 'pdfs';
|
|
177
|
+
const outDir = resolve(config.outputDir, subdir);
|
|
178
|
+
mkdirSync(outDir, { recursive: true });
|
|
179
|
+
const htmlPath = resolve(outDir, filename.replace(/\.pdf$/, '.html'));
|
|
180
|
+
writeFileSync(htmlPath, normalized, 'utf-8');
|
|
181
|
+
const page = await browser.newPage();
|
|
182
|
+
try {
|
|
183
|
+
// Navigate to the staged HTML on disk so relative font URLs and fragment links work.
|
|
184
|
+
await page.goto(`file://${htmlPath}`, { waitUntil: 'networkidle' });
|
|
185
|
+
await page.evaluate(() => document.fonts?.ready);
|
|
186
|
+
const pdfPath = resolve(outDir, filename);
|
|
187
|
+
const buf = await page.pdf({
|
|
188
|
+
format,
|
|
189
|
+
printBackground: true,
|
|
190
|
+
margin: { top: '0.6in', right: '0.6in', bottom: '0.6in', left: '0.6in' },
|
|
191
|
+
preferCSSPageSize: false,
|
|
192
|
+
});
|
|
193
|
+
writeFileSync(pdfPath, buf);
|
|
194
|
+
const relative = `${subdir}/${filename}`;
|
|
195
|
+
return {
|
|
196
|
+
path: relative,
|
|
197
|
+
abs: pdfPath,
|
|
198
|
+
url: `${config.baseUrl}/files/${relative}`,
|
|
199
|
+
bytes: buf.length,
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
finally {
|
|
203
|
+
await page.close();
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
// ── Helpers ──────────────────────────────────────────────────────────────────
|
|
207
|
+
function slug(s) {
|
|
208
|
+
return s.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '').slice(0, 40) || randomUUID().slice(0, 8);
|
|
209
|
+
}
|
|
210
|
+
// Re-bold **text** inside bullet bodies — escapeHtml() escapes the asterisks, but we want
|
|
211
|
+
// proof points to render with weight in the template.
|
|
212
|
+
function inlineBold(s) {
|
|
213
|
+
return s.replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>');
|
|
214
|
+
}
|
|
215
|
+
// Port of career-ops/generate-pdf.mjs ATS normalizer. Only touches body text — leaves
|
|
216
|
+
// style/script tags and URLs alone.
|
|
217
|
+
function normalizeAtsUnicode(html) {
|
|
218
|
+
const masks = [];
|
|
219
|
+
const masked = html.replace(/<(style|script)\b[^>]*>[\s\S]*?<\/\1>/gi, (m) => {
|
|
220
|
+
const tok = `MASK${masks.length}`;
|
|
221
|
+
masks.push(m);
|
|
222
|
+
return tok;
|
|
223
|
+
});
|
|
224
|
+
let out = '';
|
|
225
|
+
let i = 0;
|
|
226
|
+
while (i < masked.length) {
|
|
227
|
+
const lt = masked.indexOf('<', i);
|
|
228
|
+
if (lt === -1) {
|
|
229
|
+
out += sanitize(masked.slice(i));
|
|
230
|
+
break;
|
|
231
|
+
}
|
|
232
|
+
out += sanitize(masked.slice(i, lt));
|
|
233
|
+
const gt = masked.indexOf('>', lt);
|
|
234
|
+
if (gt === -1) {
|
|
235
|
+
out += masked.slice(lt);
|
|
236
|
+
break;
|
|
237
|
+
}
|
|
238
|
+
out += masked.slice(lt, gt + 1);
|
|
239
|
+
i = gt + 1;
|
|
240
|
+
}
|
|
241
|
+
return out.replace(/MASK(\d+)/g, (_, n) => masks[Number(n)]);
|
|
242
|
+
function sanitize(t) {
|
|
243
|
+
return t
|
|
244
|
+
.replace(/—/g, '-') // em-dash
|
|
245
|
+
.replace(/–/g, '-') // en-dash
|
|
246
|
+
.replace(/[“”„‟]/g, '"')
|
|
247
|
+
.replace(/[‘’‚‛]/g, "'")
|
|
248
|
+
.replace(/…/g, '...')
|
|
249
|
+
.replace(/[]/g, '')
|
|
250
|
+
.replace(/ /g, ' ');
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
//# sourceMappingURL=render.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render.js","sourceRoot":"","sources":["../../src/core/render.ts"],"names":[],"mappings":"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"}
|