careerclaw-js 0.11.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/CHANGELOG.md +362 -0
- package/README.md +348 -0
- package/SECURITY.md +156 -0
- package/SKILL.md +463 -0
- package/dist/adapters/hackernews.d.ts +36 -0
- package/dist/adapters/hackernews.d.ts.map +1 -0
- package/dist/adapters/hackernews.js +164 -0
- package/dist/adapters/hackernews.js.map +1 -0
- package/dist/adapters/index.d.ts +10 -0
- package/dist/adapters/index.d.ts.map +1 -0
- package/dist/adapters/index.js +9 -0
- package/dist/adapters/index.js.map +1 -0
- package/dist/adapters/remoteok.d.ts +35 -0
- package/dist/adapters/remoteok.d.ts.map +1 -0
- package/dist/adapters/remoteok.js +212 -0
- package/dist/adapters/remoteok.js.map +1 -0
- package/dist/briefing.d.ts +81 -0
- package/dist/briefing.d.ts.map +1 -0
- package/dist/briefing.js +152 -0
- package/dist/briefing.js.map +1 -0
- package/dist/cli.d.ts +22 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +235 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +91 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +126 -0
- package/dist/config.js.map +1 -0
- package/dist/core/text-processing.d.ts +62 -0
- package/dist/core/text-processing.d.ts.map +1 -0
- package/dist/core/text-processing.js +187 -0
- package/dist/core/text-processing.js.map +1 -0
- package/dist/drafting.d.ts +28 -0
- package/dist/drafting.d.ts.map +1 -0
- package/dist/drafting.js +116 -0
- package/dist/drafting.js.map +1 -0
- package/dist/gap.d.ts +27 -0
- package/dist/gap.d.ts.map +1 -0
- package/dist/gap.js +90 -0
- package/dist/gap.js.map +1 -0
- package/dist/license.d.ts +40 -0
- package/dist/license.d.ts.map +1 -0
- package/dist/license.js +122 -0
- package/dist/license.js.map +1 -0
- package/dist/llm-enhance.d.ts +69 -0
- package/dist/llm-enhance.d.ts.map +1 -0
- package/dist/llm-enhance.js +376 -0
- package/dist/llm-enhance.js.map +1 -0
- package/dist/matching/engine.d.ts +31 -0
- package/dist/matching/engine.d.ts.map +1 -0
- package/dist/matching/engine.js +51 -0
- package/dist/matching/engine.js.map +1 -0
- package/dist/matching/index.d.ts +8 -0
- package/dist/matching/index.d.ts.map +1 -0
- package/dist/matching/index.js +8 -0
- package/dist/matching/index.js.map +1 -0
- package/dist/matching/scoring.d.ts +84 -0
- package/dist/matching/scoring.d.ts.map +1 -0
- package/dist/matching/scoring.js +184 -0
- package/dist/matching/scoring.js.map +1 -0
- package/dist/models.d.ts +221 -0
- package/dist/models.d.ts.map +1 -0
- package/dist/models.js +28 -0
- package/dist/models.js.map +1 -0
- package/dist/requirements.d.ts +22 -0
- package/dist/requirements.d.ts.map +1 -0
- package/dist/requirements.js +30 -0
- package/dist/requirements.js.map +1 -0
- package/dist/resume-intel.d.ts +40 -0
- package/dist/resume-intel.d.ts.map +1 -0
- package/dist/resume-intel.js +111 -0
- package/dist/resume-intel.js.map +1 -0
- package/dist/sources.d.ts +32 -0
- package/dist/sources.d.ts.map +1 -0
- package/dist/sources.js +72 -0
- package/dist/sources.js.map +1 -0
- package/dist/tracking.d.ts +68 -0
- package/dist/tracking.d.ts.map +1 -0
- package/dist/tracking.js +140 -0
- package/dist/tracking.js.map +1 -0
- package/package.json +58 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/adapters/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,iBAAiB,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACjF,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC5D,YAAY,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* adapters/index.ts — Public adapter API.
|
|
3
|
+
*
|
|
4
|
+
* Import adapters from here rather than individual files.
|
|
5
|
+
* sources.ts (Phase 3) will use fetchAll() to merge results.
|
|
6
|
+
*/
|
|
7
|
+
export { fetchRemoteOkJobs, parseRss, stripHtml, stableId } from "./remoteok.js";
|
|
8
|
+
export { fetchHnJobs, parseComment } from "./hackernews.js";
|
|
9
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/adapters/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,iBAAiB,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACjF,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* adapters/remoteok.ts — RemoteOK RSS adapter.
|
|
3
|
+
*
|
|
4
|
+
* Fetches the RemoteOK RSS feed and normalises each <item> into a
|
|
5
|
+
* NormalizedJob. Parsing is split from fetching so contract tests can
|
|
6
|
+
* call parseRss() directly with fixture XML — no network mocking needed.
|
|
7
|
+
*
|
|
8
|
+
* RemoteOK RSS structure (abridged):
|
|
9
|
+
* <item>
|
|
10
|
+
* <title><![CDATA[Senior Engineer at Acme]]></title>
|
|
11
|
+
* <link>https://remoteok.com/remote-jobs/123</link>
|
|
12
|
+
* <pubDate>Mon, 03 Mar 2026 10:00:00 +0000</pubDate>
|
|
13
|
+
* <description><![CDATA[<p>HTML description...</p>]]></description>
|
|
14
|
+
* <location>Worldwide</location> <!-- optional -->
|
|
15
|
+
* <salary>$120,000 - $180,000</salary> <!-- optional -->
|
|
16
|
+
* <company><![CDATA[Acme Corp]]></company> <!-- optional -->
|
|
17
|
+
* <tag>typescript</tag> <!-- 0-n tags -->
|
|
18
|
+
* </item>
|
|
19
|
+
*/
|
|
20
|
+
import type { NormalizedJob } from "../models.js";
|
|
21
|
+
/** Fetch the RemoteOK RSS feed and return normalised jobs. */
|
|
22
|
+
export declare function fetchRemoteOkJobs(): Promise<NormalizedJob[]>;
|
|
23
|
+
/**
|
|
24
|
+
* Parse a RemoteOK RSS XML string into NormalizedJob[].
|
|
25
|
+
* Exported for offline contract testing.
|
|
26
|
+
*/
|
|
27
|
+
export declare function parseRss(xml: string): NormalizedJob[];
|
|
28
|
+
/**
|
|
29
|
+
* Strip HTML tags and decode common entities.
|
|
30
|
+
* Intentionally minimal — avoids a DOM/cheerio dependency.
|
|
31
|
+
*/
|
|
32
|
+
export declare function stripHtml(html: string): string;
|
|
33
|
+
/** SHA-256(text) → first 16 hex chars (matches Python job_id convention). */
|
|
34
|
+
export declare function stableId(text: string): string;
|
|
35
|
+
//# sourceMappingURL=remoteok.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"remoteok.d.ts","sourceRoot":"","sources":["../../src/adapters/remoteok.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAKH,OAAO,KAAK,EAAE,aAAa,EAAY,MAAM,cAAc,CAAC;AA+B5D,8DAA8D;AAC9D,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,aAAa,EAAE,CAAC,CAiBlE;AAED;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,aAAa,EAAE,CAqBrD;AAkHD;;;GAGG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAoB9C;AAED,6EAA6E;AAC7E,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE7C"}
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* adapters/remoteok.ts — RemoteOK RSS adapter.
|
|
3
|
+
*
|
|
4
|
+
* Fetches the RemoteOK RSS feed and normalises each <item> into a
|
|
5
|
+
* NormalizedJob. Parsing is split from fetching so contract tests can
|
|
6
|
+
* call parseRss() directly with fixture XML — no network mocking needed.
|
|
7
|
+
*
|
|
8
|
+
* RemoteOK RSS structure (abridged):
|
|
9
|
+
* <item>
|
|
10
|
+
* <title><![CDATA[Senior Engineer at Acme]]></title>
|
|
11
|
+
* <link>https://remoteok.com/remote-jobs/123</link>
|
|
12
|
+
* <pubDate>Mon, 03 Mar 2026 10:00:00 +0000</pubDate>
|
|
13
|
+
* <description><![CDATA[<p>HTML description...</p>]]></description>
|
|
14
|
+
* <location>Worldwide</location> <!-- optional -->
|
|
15
|
+
* <salary>$120,000 - $180,000</salary> <!-- optional -->
|
|
16
|
+
* <company><![CDATA[Acme Corp]]></company> <!-- optional -->
|
|
17
|
+
* <tag>typescript</tag> <!-- 0-n tags -->
|
|
18
|
+
* </item>
|
|
19
|
+
*/
|
|
20
|
+
import { createHash } from "node:crypto";
|
|
21
|
+
import { XMLParser } from "fast-xml-parser";
|
|
22
|
+
import { utcNow } from "../models.js";
|
|
23
|
+
import { HTTP_TIMEOUT_MS, REMOTEOK_RSS_URL, USER_AGENT, } from "../config.js";
|
|
24
|
+
// ---------------------------------------------------------------------------
|
|
25
|
+
// Public API
|
|
26
|
+
// ---------------------------------------------------------------------------
|
|
27
|
+
/** Fetch the RemoteOK RSS feed and return normalised jobs. */
|
|
28
|
+
export async function fetchRemoteOkJobs() {
|
|
29
|
+
const controller = new AbortController();
|
|
30
|
+
const timer = setTimeout(() => controller.abort(), HTTP_TIMEOUT_MS);
|
|
31
|
+
try {
|
|
32
|
+
const res = await fetch(REMOTEOK_RSS_URL, {
|
|
33
|
+
signal: controller.signal,
|
|
34
|
+
headers: { "User-Agent": USER_AGENT },
|
|
35
|
+
});
|
|
36
|
+
if (!res.ok) {
|
|
37
|
+
throw new Error(`RemoteOK RSS returned HTTP ${res.status}`);
|
|
38
|
+
}
|
|
39
|
+
const xml = await res.text();
|
|
40
|
+
return parseRss(xml);
|
|
41
|
+
}
|
|
42
|
+
finally {
|
|
43
|
+
clearTimeout(timer);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Parse a RemoteOK RSS XML string into NormalizedJob[].
|
|
48
|
+
* Exported for offline contract testing.
|
|
49
|
+
*/
|
|
50
|
+
export function parseRss(xml) {
|
|
51
|
+
const parser = new XMLParser({
|
|
52
|
+
ignoreAttributes: false,
|
|
53
|
+
cdataPropName: "__cdata",
|
|
54
|
+
isArray: (_name, jpath) => jpath === "rss.channel.item",
|
|
55
|
+
});
|
|
56
|
+
const feed = parser.parse(xml);
|
|
57
|
+
const rawItems = feed?.rss?.channel?.item;
|
|
58
|
+
if (!rawItems)
|
|
59
|
+
return [];
|
|
60
|
+
const items = Array.isArray(rawItems) ? rawItems : [rawItems];
|
|
61
|
+
const fetched_at = utcNow();
|
|
62
|
+
return items.flatMap((item) => {
|
|
63
|
+
try {
|
|
64
|
+
return [normaliseItem(item, fetched_at)];
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
// Skip malformed items rather than aborting the whole feed
|
|
68
|
+
return [];
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
// ---------------------------------------------------------------------------
|
|
73
|
+
// Internal helpers
|
|
74
|
+
// ---------------------------------------------------------------------------
|
|
75
|
+
function normaliseItem(item, fetched_at) {
|
|
76
|
+
const rawTitle = coerceString(item.title);
|
|
77
|
+
const url = coerceString(item.link);
|
|
78
|
+
const rawDescription = stripHtml(coerceString(item.description));
|
|
79
|
+
const rawLocation = coerceString(item.location);
|
|
80
|
+
const rawSalary = coerceString(item.salary);
|
|
81
|
+
const rawCompany = coerceString(item.company);
|
|
82
|
+
const { title, company } = parseTitle(rawTitle, rawCompany);
|
|
83
|
+
const { salary_min, salary_max } = parseSalary(rawSalary, rawDescription);
|
|
84
|
+
const work_mode = inferWorkMode(rawLocation, rawDescription);
|
|
85
|
+
const experience_years = inferExperienceYears(rawDescription);
|
|
86
|
+
// Combine location tag with description inference
|
|
87
|
+
const location = rawLocation || inferLocationLabel(rawDescription);
|
|
88
|
+
return {
|
|
89
|
+
job_id: stableId(url || `${company}:${title}`),
|
|
90
|
+
title,
|
|
91
|
+
company,
|
|
92
|
+
location,
|
|
93
|
+
description: rawDescription,
|
|
94
|
+
url,
|
|
95
|
+
source: "remoteok",
|
|
96
|
+
salary_min,
|
|
97
|
+
salary_max,
|
|
98
|
+
work_mode,
|
|
99
|
+
experience_years,
|
|
100
|
+
posted_at: parsePubDate(coerceString(item.pubDate)),
|
|
101
|
+
fetched_at,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Split "Senior Engineer at Acme Corp" → { title, company }.
|
|
106
|
+
* Falls back to raw company field if present, then empty string.
|
|
107
|
+
*/
|
|
108
|
+
function parseTitle(raw, companyField) {
|
|
109
|
+
const atIdx = raw.lastIndexOf(" at ");
|
|
110
|
+
if (atIdx > 0) {
|
|
111
|
+
return {
|
|
112
|
+
title: raw.slice(0, atIdx).trim(),
|
|
113
|
+
company: raw.slice(atIdx + 4).trim(),
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
return { title: raw.trim(), company: companyField.trim() };
|
|
117
|
+
}
|
|
118
|
+
/** Parse "$120,000 - $180,000" or "$120k - $180k" into annualised USD. */
|
|
119
|
+
function parseSalary(salaryField, description) {
|
|
120
|
+
const src = salaryField || description;
|
|
121
|
+
// Match patterns: $120,000-$180,000 | $120k-$180k | 120000-180000
|
|
122
|
+
const m = src.match(/\$?([\d,]+)\s*k?\s*[-–to]+\s*\$?([\d,]+)\s*k?/i);
|
|
123
|
+
if (!m)
|
|
124
|
+
return { salary_min: null, salary_max: null };
|
|
125
|
+
const parse = (raw, isK) => {
|
|
126
|
+
const n = parseInt(raw.replace(/,/g, ""), 10);
|
|
127
|
+
return isK || n < 1_000 ? n * 1_000 : n;
|
|
128
|
+
};
|
|
129
|
+
const rawMin = m[1] ?? "";
|
|
130
|
+
const rawMax = m[2] ?? "";
|
|
131
|
+
const isK = /k/i.test(salaryField || "");
|
|
132
|
+
return {
|
|
133
|
+
salary_min: parse(rawMin, isK),
|
|
134
|
+
salary_max: parse(rawMax, isK),
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
function inferWorkMode(location, description) {
|
|
138
|
+
const text = `${location} ${description}`.toLowerCase();
|
|
139
|
+
if (/\bhybrid\b/.test(text))
|
|
140
|
+
return "hybrid";
|
|
141
|
+
if (/\bremote\b/.test(text))
|
|
142
|
+
return "remote";
|
|
143
|
+
if (/\bon-?site\b|\bin-?office\b/.test(text))
|
|
144
|
+
return "onsite";
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
function inferLocationLabel(description) {
|
|
148
|
+
if (/\bworldwide\b|\banywhere\b/i.test(description))
|
|
149
|
+
return "Worldwide";
|
|
150
|
+
if (/\bremote\b/i.test(description))
|
|
151
|
+
return "Remote";
|
|
152
|
+
return "";
|
|
153
|
+
}
|
|
154
|
+
function inferExperienceYears(text) {
|
|
155
|
+
const m = text.match(/(\d+)\+?\s*(?:or more\s+)?years?\s+(?:of\s+)?experience/i);
|
|
156
|
+
return m && m[1] ? parseInt(m[1], 10) : null;
|
|
157
|
+
}
|
|
158
|
+
function parsePubDate(raw) {
|
|
159
|
+
if (!raw)
|
|
160
|
+
return null;
|
|
161
|
+
try {
|
|
162
|
+
return new Date(raw).toISOString();
|
|
163
|
+
}
|
|
164
|
+
catch {
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
// ---------------------------------------------------------------------------
|
|
169
|
+
// Shared utilities
|
|
170
|
+
// ---------------------------------------------------------------------------
|
|
171
|
+
/**
|
|
172
|
+
* Strip HTML tags and decode common entities.
|
|
173
|
+
* Intentionally minimal — avoids a DOM/cheerio dependency.
|
|
174
|
+
*/
|
|
175
|
+
export function stripHtml(html) {
|
|
176
|
+
return html
|
|
177
|
+
.replace(/<br\s*\/?>/gi, "\n")
|
|
178
|
+
.replace(/<p[^>]*>/gi, "\n") // opening <p> → newline (creates split point)
|
|
179
|
+
.replace(/<\/p>/gi, "")
|
|
180
|
+
.replace(/<[^>]+>/g, "")
|
|
181
|
+
.replace(/&/g, "&")
|
|
182
|
+
.replace(/</g, "<")
|
|
183
|
+
.replace(/>/g, ">")
|
|
184
|
+
.replace(/"/g, '"')
|
|
185
|
+
.replace(/'/g, "'")
|
|
186
|
+
.replace(/'/g, "'")
|
|
187
|
+
.replace(/'/g, "'")
|
|
188
|
+
.replace(///gi, "/")
|
|
189
|
+
.replace(/&#x([0-9a-f]{1,6});/gi, (_, hex) => String.fromCodePoint(parseInt(hex, 16)))
|
|
190
|
+
.replace(/ /g, " ")
|
|
191
|
+
.replace(/\n{3,}/g, "\n\n")
|
|
192
|
+
.trim();
|
|
193
|
+
}
|
|
194
|
+
/** SHA-256(text) → first 16 hex chars (matches Python job_id convention). */
|
|
195
|
+
export function stableId(text) {
|
|
196
|
+
return createHash("sha256").update(text).digest("hex").slice(0, 16);
|
|
197
|
+
}
|
|
198
|
+
/** Safely coerce a parsed value to string (fast-xml-parser can return numbers). */
|
|
199
|
+
function coerceString(val) {
|
|
200
|
+
if (val === null || val === undefined)
|
|
201
|
+
return "";
|
|
202
|
+
if (typeof val === "string")
|
|
203
|
+
return val;
|
|
204
|
+
if (typeof val === "number")
|
|
205
|
+
return String(val);
|
|
206
|
+
// CDATA objects from fast-xml-parser
|
|
207
|
+
if (typeof val === "object" && val !== null && "__cdata" in val) {
|
|
208
|
+
return String(val["__cdata"] ?? "");
|
|
209
|
+
}
|
|
210
|
+
return "";
|
|
211
|
+
}
|
|
212
|
+
//# sourceMappingURL=remoteok.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"remoteok.js","sourceRoot":"","sources":["../../src/adapters/remoteok.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAEtC,OAAO,EACL,eAAe,EACf,gBAAgB,EAChB,UAAU,GACX,MAAM,cAAc,CAAC;AAsBtB,8EAA8E;AAC9E,aAAa;AACb,8EAA8E;AAE9E,8DAA8D;AAC9D,MAAM,CAAC,KAAK,UAAU,iBAAiB;IACrC,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;IACzC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,eAAe,CAAC,CAAC;IAEpE,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,gBAAgB,EAAE;YACxC,MAAM,EAAE,UAAU,CAAC,MAAM;YACzB,OAAO,EAAE,EAAE,YAAY,EAAE,UAAU,EAAE;SACtC,CAAC,CAAC;QACH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;YACZ,MAAM,IAAI,KAAK,CAAC,8BAA8B,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;QAC9D,CAAC;QACD,MAAM,GAAG,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;QAC7B,OAAO,QAAQ,CAAC,GAAG,CAAC,CAAC;IACvB,CAAC;YAAS,CAAC;QACT,YAAY,CAAC,KAAK,CAAC,CAAC;IACtB,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,QAAQ,CAAC,GAAW;IAClC,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC;QAC3B,gBAAgB,EAAE,KAAK;QACvB,aAAa,EAAE,SAAS;QACxB,OAAO,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,KAAK,kBAAkB;KACxD,CAAC,CAAC;IAEH,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,CAAY,CAAC;IAC1C,MAAM,QAAQ,GAAG,IAAI,EAAE,GAAG,EAAE,OAAO,EAAE,IAAI,CAAC;IAC1C,IAAI,CAAC,QAAQ;QAAE,OAAO,EAAE,CAAC;IACzB,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;IAC9D,MAAM,UAAU,GAAG,MAAM,EAAE,CAAC;IAE5B,OAAO,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE;QAC5B,IAAI,CAAC;YACH,OAAO,CAAC,aAAa,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC;QAC3C,CAAC;QAAC,MAAM,CAAC;YACP,2DAA2D;YAC3D,OAAO,EAAE,CAAC;QACZ,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC;AAED,8EAA8E;AAC9E,mBAAmB;AACnB,8EAA8E;AAE9E,SAAS,aAAa,CAAC,IAAa,EAAE,UAAkB;IACtD,MAAM,QAAQ,GAAG,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC1C,MAAM,GAAG,GAAG,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACpC,MAAM,cAAc,GAAG,SAAS,CAAC,YAAY,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC;IACjE,MAAM,WAAW,GAAG,YAAY,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAChD,MAAM,SAAS,GAAG,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC5C,MAAM,UAAU,GAAG,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAE9C,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,UAAU,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;IAC5D,MAAM,EAAE,UAAU,EAAE,UAAU,EAAE,GAAG,WAAW,CAAC,SAAS,EAAE,cAAc,CAAC,CAAC;IAC1E,MAAM,SAAS,GAAG,aAAa,CAAC,WAAW,EAAE,cAAc,CAAC,CAAC;IAC7D,MAAM,gBAAgB,GAAG,oBAAoB,CAAC,cAAc,CAAC,CAAC;IAE9D,kDAAkD;IAClD,MAAM,QAAQ,GAAG,WAAW,IAAI,kBAAkB,CAAC,cAAc,CAAC,CAAC;IAEnE,OAAO;QACL,MAAM,EAAE,QAAQ,CAAC,GAAG,IAAI,GAAG,OAAO,IAAI,KAAK,EAAE,CAAC;QAC9C,KAAK;QACL,OAAO;QACP,QAAQ;QACR,WAAW,EAAE,cAAc;QAC3B,GAAG;QACH,MAAM,EAAE,UAAU;QAClB,UAAU;QACV,UAAU;QACV,SAAS;QACT,gBAAgB;QAChB,SAAS,EAAE,YAAY,CAAC,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACnD,UAAU;KACX,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,SAAS,UAAU,CACjB,GAAW,EACX,YAAoB;IAEpB,MAAM,KAAK,GAAG,GAAG,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;IACtC,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;QACd,OAAO;YACL,KAAK,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,IAAI,EAAE;YACjC,OAAO,EAAE,GAAG,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE;SACrC,CAAC;IACJ,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,GAAG,CAAC,IAAI,EAAE,EAAE,OAAO,EAAE,YAAY,CAAC,IAAI,EAAE,EAAE,CAAC;AAC7D,CAAC;AAED,0EAA0E;AAC1E,SAAS,WAAW,CAClB,WAAmB,EACnB,WAAmB;IAEnB,MAAM,GAAG,GAAG,WAAW,IAAI,WAAW,CAAC;IACvC,kEAAkE;IAClE,MAAM,CAAC,GAAG,GAAG,CAAC,KAAK,CAAC,gDAAgD,CAAC,CAAC;IACtE,IAAI,CAAC,CAAC;QAAE,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC;IAEtD,MAAM,KAAK,GAAG,CAAC,GAAW,EAAE,GAAY,EAAU,EAAE;QAClD,MAAM,CAAC,GAAG,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;QAC9C,OAAO,GAAG,IAAI,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAC1C,CAAC,CAAC;IAEF,MAAM,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IAC1B,MAAM,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IAC1B,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC;IAEzC,OAAO;QACL,UAAU,EAAE,KAAK,CAAC,MAAM,EAAE,GAAG,CAAC;QAC9B,UAAU,EAAE,KAAK,CAAC,MAAM,EAAE,GAAG,CAAC;KAC/B,CAAC;AACJ,CAAC;AAED,SAAS,aAAa,CAAC,QAAgB,EAAE,WAAmB;IAC1D,MAAM,IAAI,GAAG,GAAG,QAAQ,IAAI,WAAW,EAAE,CAAC,WAAW,EAAE,CAAC;IACxD,IAAI,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,QAAQ,CAAC;IAC7C,IAAI,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,QAAQ,CAAC;IAC7C,IAAI,6BAA6B,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,QAAQ,CAAC;IAC9D,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,kBAAkB,CAAC,WAAmB;IAC7C,IAAI,6BAA6B,CAAC,IAAI,CAAC,WAAW,CAAC;QAAE,OAAO,WAAW,CAAC;IACxE,IAAI,aAAa,CAAC,IAAI,CAAC,WAAW,CAAC;QAAE,OAAO,QAAQ,CAAC;IACrD,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,SAAS,oBAAoB,CAAC,IAAY;IACxC,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,0DAA0D,CAAC,CAAC;IACjF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AAC/C,CAAC;AAED,SAAS,YAAY,CAAC,GAAW;IAC/B,IAAI,CAAC,GAAG;QAAE,OAAO,IAAI,CAAC;IACtB,IAAI,CAAC;QACH,OAAO,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,WAAW,EAAE,CAAC;IACrC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,8EAA8E;AAC9E,mBAAmB;AACnB,8EAA8E;AAE9E;;;GAGG;AACH,MAAM,UAAU,SAAS,CAAC,IAAY;IACpC,OAAO,IAAI;SACR,OAAO,CAAC,cAAc,EAAE,IAAI,CAAC;SAC7B,OAAO,CAAC,YAAY,EAAE,IAAI,CAAC,CAAG,8CAA8C;SAC5E,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC;SACtB,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC;SACvB,OAAO,CAAC,QAAQ,EAAE,GAAG,CAAC;SACtB,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC;SACrB,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC;SACrB,OAAO,CAAC,SAAS,EAAE,GAAG,CAAC;SACvB,OAAO,CAAC,SAAS,EAAE,GAAG,CAAC;SACvB,OAAO,CAAC,QAAQ,EAAE,GAAG,CAAC;SACtB,OAAO,CAAC,SAAS,EAAE,GAAG,CAAC;SACvB,OAAO,CAAC,UAAU,EAAE,GAAG,CAAC;SACxB,OAAO,CAAC,uBAAuB,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,EAAE,CAC3C,MAAM,CAAC,aAAa,CAAC,QAAQ,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,CACxC;SACA,OAAO,CAAC,SAAS,EAAE,GAAG,CAAC;SACvB,OAAO,CAAC,SAAS,EAAE,MAAM,CAAC;SAC1B,IAAI,EAAE,CAAC;AACZ,CAAC;AAED,6EAA6E;AAC7E,MAAM,UAAU,QAAQ,CAAC,IAAY;IACnC,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AACtE,CAAC;AAED,mFAAmF;AACnF,SAAS,YAAY,CAAC,GAAY;IAChC,IAAI,GAAG,KAAK,IAAI,IAAI,GAAG,KAAK,SAAS;QAAE,OAAO,EAAE,CAAC;IACjD,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,OAAO,GAAG,CAAC;IACxC,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC;IAChD,qCAAqC;IACrC,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI,IAAI,SAAS,IAAI,GAAG,EAAE,CAAC;QAChE,OAAO,MAAM,CAAE,GAA+B,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC,CAAC;IACnE,CAAC;IACD,OAAO,EAAE,CAAC;AACZ,CAAC"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* briefing.ts — Daily briefing pipeline orchestrator.
|
|
3
|
+
*
|
|
4
|
+
* `runBriefing()` is the single entry point that wires every module
|
|
5
|
+
* into the complete end-to-end workflow:
|
|
6
|
+
*
|
|
7
|
+
* fetch → deduplicate → rank → draft → persist → return bundle
|
|
8
|
+
*
|
|
9
|
+
* Design principles (from Phase 4 architecture doc):
|
|
10
|
+
* - Skill-first: accepts UserProfile as a parameter, not a file path.
|
|
11
|
+
* The caller (CLI or OpenClaw agent) is responsible for loading the
|
|
12
|
+
* profile; the orchestrator never touches the filesystem for input.
|
|
13
|
+
* - Dual-mode output: structured BriefingResult JSON for agents +
|
|
14
|
+
* per-stage timings for observability.
|
|
15
|
+
* - Dry-run: suppresses all writes; counts are still accurate.
|
|
16
|
+
* - Testable: fetchFn and repo are injectable; no live network calls
|
|
17
|
+
* required in tests.
|
|
18
|
+
* - Graceful degradation: if the fetch stage returns zero jobs (e.g.
|
|
19
|
+
* both adapters failed), the pipeline short-circuits cleanly with
|
|
20
|
+
* an empty result rather than crashing.
|
|
21
|
+
*/
|
|
22
|
+
import type { UserProfile, BriefingResult, ResumeIntelligence } from "./models.js";
|
|
23
|
+
import { type FetchResult } from "./sources.js";
|
|
24
|
+
import { type EnhanceOptions } from "./llm-enhance.js";
|
|
25
|
+
import { type CheckLicenseOptions } from "./license.js";
|
|
26
|
+
import { TrackingRepository } from "./tracking.js";
|
|
27
|
+
export interface BriefingOptions {
|
|
28
|
+
/** Number of top matches to return (default: DEFAULT_TOP_K = 3). */
|
|
29
|
+
topK?: number;
|
|
30
|
+
/** Suppress all file writes when true. Counts remain accurate. */
|
|
31
|
+
dryRun?: boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Injectable fetch function — defaults to the real fetchAllJobs().
|
|
34
|
+
* Pass a stub in tests to avoid live network calls.
|
|
35
|
+
*/
|
|
36
|
+
fetchFn?: () => Promise<FetchResult>;
|
|
37
|
+
/**
|
|
38
|
+
* Injectable TrackingRepository — defaults to a new instance with
|
|
39
|
+
* standard paths. Pass a tmpdir-backed instance in tests.
|
|
40
|
+
*/
|
|
41
|
+
repo?: TrackingRepository;
|
|
42
|
+
/**
|
|
43
|
+
* Resume intelligence from buildResumeIntelligence().
|
|
44
|
+
* Required for LLM draft enhancement — ignored when proKey is absent.
|
|
45
|
+
*/
|
|
46
|
+
resumeIntel?: ResumeIntelligence;
|
|
47
|
+
/**
|
|
48
|
+
* CareerClaw Pro license key (CAREERCLAW_PRO_KEY).
|
|
49
|
+
* Validated against Gumroad before enabling LLM-enhanced drafts.
|
|
50
|
+
* Falls back to deterministic draft if validation fails.
|
|
51
|
+
*/
|
|
52
|
+
proKey?: string;
|
|
53
|
+
/**
|
|
54
|
+
* Injectable fetch for the LLM API calls — passed through to enhanceDraft().
|
|
55
|
+
* Defaults to global fetch. Pass a stub in tests.
|
|
56
|
+
*/
|
|
57
|
+
enhanceFetchFn?: EnhanceOptions["fetchFn"];
|
|
58
|
+
/**
|
|
59
|
+
* Injectable fetch for the Gumroad license API call.
|
|
60
|
+
* Defaults to global fetch. Pass a stub in tests.
|
|
61
|
+
*/
|
|
62
|
+
licenseFetchFn?: CheckLicenseOptions["fetchFn"];
|
|
63
|
+
/**
|
|
64
|
+
* Override the license cache file path — for tests using a tmpdir.
|
|
65
|
+
*/
|
|
66
|
+
licenseCachePath?: string;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Run a full briefing pipeline for the given user profile.
|
|
70
|
+
*
|
|
71
|
+
* Stages and timings:
|
|
72
|
+
* 1. fetch_ms — fetch + deduplicate jobs from all sources
|
|
73
|
+
* 2. rank_ms — score and rank jobs against profile
|
|
74
|
+
* 3. draft_ms — generate one OutreachDraft per top match
|
|
75
|
+
* 4. persist_ms — upsert tracking entries + append run record
|
|
76
|
+
*
|
|
77
|
+
* @param profile - User profile (passed in by caller, not loaded here)
|
|
78
|
+
* @param options - Injection points and run flags
|
|
79
|
+
*/
|
|
80
|
+
export declare function runBriefing(profile: UserProfile, options?: BriefingOptions): Promise<BriefingResult>;
|
|
81
|
+
//# sourceMappingURL=briefing.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"briefing.d.ts","sourceRoot":"","sources":["../src/briefing.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAIH,OAAO,KAAK,EACV,WAAW,EAIX,cAAc,EACd,kBAAkB,EACnB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAgB,KAAK,WAAW,EAAE,MAAM,cAAc,CAAC;AAG9D,OAAO,EAAgB,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACrE,OAAO,EAAgB,KAAK,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACtE,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAOnD,MAAM,WAAW,eAAe;IAC9B,oEAAoE;IACpE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,kEAAkE;IAClE,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,OAAO,CAAC,WAAW,CAAC,CAAC;IACrC;;;OAGG;IACH,IAAI,CAAC,EAAE,kBAAkB,CAAC;IAC1B;;;OAGG;IACH,WAAW,CAAC,EAAE,kBAAkB,CAAC;IACjC;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,cAAc,CAAC,EAAE,cAAc,CAAC,SAAS,CAAC,CAAC;IAC3C;;;OAGG;IACH,cAAc,CAAC,EAAE,mBAAmB,CAAC,SAAS,CAAC,CAAC;IAChD;;OAEG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAMD;;;;;;;;;;;GAWG;AACH,wBAAsB,WAAW,CAC/B,OAAO,EAAE,WAAW,EACpB,OAAO,GAAE,eAAoB,GAC5B,OAAO,CAAC,cAAc,CAAC,CAwHzB"}
|
package/dist/briefing.js
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* briefing.ts — Daily briefing pipeline orchestrator.
|
|
3
|
+
*
|
|
4
|
+
* `runBriefing()` is the single entry point that wires every module
|
|
5
|
+
* into the complete end-to-end workflow:
|
|
6
|
+
*
|
|
7
|
+
* fetch → deduplicate → rank → draft → persist → return bundle
|
|
8
|
+
*
|
|
9
|
+
* Design principles (from Phase 4 architecture doc):
|
|
10
|
+
* - Skill-first: accepts UserProfile as a parameter, not a file path.
|
|
11
|
+
* The caller (CLI or OpenClaw agent) is responsible for loading the
|
|
12
|
+
* profile; the orchestrator never touches the filesystem for input.
|
|
13
|
+
* - Dual-mode output: structured BriefingResult JSON for agents +
|
|
14
|
+
* per-stage timings for observability.
|
|
15
|
+
* - Dry-run: suppresses all writes; counts are still accurate.
|
|
16
|
+
* - Testable: fetchFn and repo are injectable; no live network calls
|
|
17
|
+
* required in tests.
|
|
18
|
+
* - Graceful degradation: if the fetch stage returns zero jobs (e.g.
|
|
19
|
+
* both adapters failed), the pipeline short-circuits cleanly with
|
|
20
|
+
* an empty result rather than crashing.
|
|
21
|
+
*/
|
|
22
|
+
import { randomUUID } from "crypto";
|
|
23
|
+
import { createRequire } from "module";
|
|
24
|
+
import { fetchAllJobs } from "./sources.js";
|
|
25
|
+
import { rankJobs } from "./matching/index.js";
|
|
26
|
+
import { draftOutreach } from "./drafting.js";
|
|
27
|
+
import { enhanceDraft } from "./llm-enhance.js";
|
|
28
|
+
import { checkLicense } from "./license.js";
|
|
29
|
+
import { TrackingRepository } from "./tracking.js";
|
|
30
|
+
import { DEFAULT_TOP_K } from "./config.js";
|
|
31
|
+
// ---------------------------------------------------------------------------
|
|
32
|
+
// Public API
|
|
33
|
+
// ---------------------------------------------------------------------------
|
|
34
|
+
/**
|
|
35
|
+
* Run a full briefing pipeline for the given user profile.
|
|
36
|
+
*
|
|
37
|
+
* Stages and timings:
|
|
38
|
+
* 1. fetch_ms — fetch + deduplicate jobs from all sources
|
|
39
|
+
* 2. rank_ms — score and rank jobs against profile
|
|
40
|
+
* 3. draft_ms — generate one OutreachDraft per top match
|
|
41
|
+
* 4. persist_ms — upsert tracking entries + append run record
|
|
42
|
+
*
|
|
43
|
+
* @param profile - User profile (passed in by caller, not loaded here)
|
|
44
|
+
* @param options - Injection points and run flags
|
|
45
|
+
*/
|
|
46
|
+
export async function runBriefing(profile, options = {}) {
|
|
47
|
+
const { topK = DEFAULT_TOP_K, dryRun = false, fetchFn = fetchAllJobs, repo = new TrackingRepository({ dryRun }), resumeIntel, proKey, enhanceFetchFn, licenseFetchFn, licenseCachePath, } = options;
|
|
48
|
+
// Validate Pro license before enabling LLM enhancement.
|
|
49
|
+
// Runs only when proKey is present; result degrades gracefully on network
|
|
50
|
+
// failure (cached result used for up to LICENSE_CACHE_TTL_MS = 7 days).
|
|
51
|
+
let isProActive = false;
|
|
52
|
+
if (proKey && proKey.trim().length > 0 && resumeIntel) {
|
|
53
|
+
const licenseOptions = {};
|
|
54
|
+
if (licenseFetchFn !== undefined)
|
|
55
|
+
licenseOptions.fetchFn = licenseFetchFn;
|
|
56
|
+
if (licenseCachePath !== undefined)
|
|
57
|
+
licenseOptions.cachePath = licenseCachePath;
|
|
58
|
+
const licenseResult = await checkLicense(proKey, licenseOptions);
|
|
59
|
+
isProActive = licenseResult.valid;
|
|
60
|
+
}
|
|
61
|
+
const runAt = new Date().toISOString();
|
|
62
|
+
const runId = randomUUID();
|
|
63
|
+
const version = readPackageVersion();
|
|
64
|
+
// -------------------------------------------------------------------------
|
|
65
|
+
// Stage 1: Fetch + deduplicate
|
|
66
|
+
// -------------------------------------------------------------------------
|
|
67
|
+
const fetchStart = Date.now();
|
|
68
|
+
let fetchResult;
|
|
69
|
+
try {
|
|
70
|
+
fetchResult = await fetchFn();
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
// Catastrophic fetch failure — return an empty result rather than throwing
|
|
74
|
+
fetchResult = { jobs: [], counts: {}, errors: {} };
|
|
75
|
+
}
|
|
76
|
+
const fetchMs = Date.now() - fetchStart;
|
|
77
|
+
const { jobs, counts: sourceCounts } = fetchResult;
|
|
78
|
+
// -------------------------------------------------------------------------
|
|
79
|
+
// Stage 2: Rank
|
|
80
|
+
// -------------------------------------------------------------------------
|
|
81
|
+
const rankStart = Date.now();
|
|
82
|
+
const matches = jobs.length > 0 ? rankJobs(jobs, profile, topK) : [];
|
|
83
|
+
const rankMs = Date.now() - rankStart;
|
|
84
|
+
// -------------------------------------------------------------------------
|
|
85
|
+
// Stage 3: Draft
|
|
86
|
+
// -------------------------------------------------------------------------
|
|
87
|
+
const draftStart = Date.now();
|
|
88
|
+
const drafts = await Promise.all(matches.map(async (scored) => {
|
|
89
|
+
const baseline = draftOutreach(scored.job, profile, scored.matched_keywords);
|
|
90
|
+
if (isProActive) {
|
|
91
|
+
return enhanceDraft(scored.job, profile, resumeIntel, baseline, scored.gap_keywords, enhanceFetchFn !== undefined ? { fetchFn: enhanceFetchFn } : {});
|
|
92
|
+
}
|
|
93
|
+
return baseline;
|
|
94
|
+
}));
|
|
95
|
+
const draftMs = Date.now() - draftStart;
|
|
96
|
+
// -------------------------------------------------------------------------
|
|
97
|
+
// Stage 4: Persist
|
|
98
|
+
// -------------------------------------------------------------------------
|
|
99
|
+
const persistStart = Date.now();
|
|
100
|
+
const trackingResult = repo.upsertEntries(matches.map((s) => s.job), matches);
|
|
101
|
+
const run = {
|
|
102
|
+
run_id: runId,
|
|
103
|
+
run_at: runAt,
|
|
104
|
+
dry_run: dryRun,
|
|
105
|
+
jobs_fetched: jobs.length,
|
|
106
|
+
jobs_ranked: jobs.length,
|
|
107
|
+
jobs_matched: matches.length,
|
|
108
|
+
sources: sourceCounts,
|
|
109
|
+
timings: {
|
|
110
|
+
fetch_ms: fetchMs,
|
|
111
|
+
rank_ms: rankMs,
|
|
112
|
+
draft_ms: draftMs,
|
|
113
|
+
persist_ms: null, // filled below after appendRun
|
|
114
|
+
},
|
|
115
|
+
version,
|
|
116
|
+
};
|
|
117
|
+
repo.appendRun(run);
|
|
118
|
+
const persistMs = Date.now() - persistStart;
|
|
119
|
+
// Back-fill persist_ms (the field exists for observability; the run
|
|
120
|
+
// record on disk will have null for persist_ms — that is acceptable
|
|
121
|
+
// and consistent with the Python implementation)
|
|
122
|
+
run.timings.persist_ms = persistMs;
|
|
123
|
+
// -------------------------------------------------------------------------
|
|
124
|
+
// Result bundle
|
|
125
|
+
// -------------------------------------------------------------------------
|
|
126
|
+
return {
|
|
127
|
+
run,
|
|
128
|
+
matches,
|
|
129
|
+
drafts,
|
|
130
|
+
tracking: {
|
|
131
|
+
created: trackingResult.created,
|
|
132
|
+
already_present: trackingResult.already_present,
|
|
133
|
+
},
|
|
134
|
+
dry_run: dryRun,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
// ---------------------------------------------------------------------------
|
|
138
|
+
// Helpers
|
|
139
|
+
// ---------------------------------------------------------------------------
|
|
140
|
+
/** Read the package version from package.json at runtime. */
|
|
141
|
+
function readPackageVersion() {
|
|
142
|
+
try {
|
|
143
|
+
const require = createRequire(import.meta.url);
|
|
144
|
+
// Walk up from src/ to find package.json
|
|
145
|
+
const pkg = require("../package.json");
|
|
146
|
+
return pkg.version;
|
|
147
|
+
}
|
|
148
|
+
catch {
|
|
149
|
+
return "unknown";
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
//# sourceMappingURL=briefing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"briefing.js","sourceRoot":"","sources":["../src/briefing.ts"],"names":[],"mappings":"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"}
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* cli.ts — CareerClaw command-line entry point.
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* npx careerclaw-js [options]
|
|
7
|
+
* node --env-file=.env dist/cli.js [options]
|
|
8
|
+
*
|
|
9
|
+
* Options:
|
|
10
|
+
* --profile PATH Path to profile.json (default: ~/.careerclaw/profile.json)
|
|
11
|
+
* --resume-txt PATH Plain-text resume file to enhance matching
|
|
12
|
+
* --top-k INT Number of top matches to return (default: 3)
|
|
13
|
+
* --dry-run Run without writing tracking or run log
|
|
14
|
+
* --json Print JSON output only (machine-readable; no colour)
|
|
15
|
+
* --help Print this help message and exit
|
|
16
|
+
*
|
|
17
|
+
* Exit codes:
|
|
18
|
+
* 0 — briefing completed (even if 0 matches — not an error)
|
|
19
|
+
* 1 — fatal error (profile not found, unreadable, or invalid JSON)
|
|
20
|
+
*/
|
|
21
|
+
export {};
|
|
22
|
+
//# sourceMappingURL=cli.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;GAkBG"}
|