job-pro 0.5.0 → 0.7.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.
@@ -0,0 +1,133 @@
1
+ // 百川智能 (Baichuan AI) — stub adapter for `job-pro`.
2
+ //
3
+ // STATUS: stub-only. No public unauthenticated job API was found.
4
+ //
5
+ // ============================================================
6
+ // RECONNAISSANCE RESULTS (probed 2026-05):
7
+ //
8
+ // www.baichuan-ai.com/careers → 404 (Next.js SPA, page removed)
9
+ // www.baichuan-ai.com/join → 404
10
+ // www.baichuan-ai.com/ → React SPA; no inline job data;
11
+ // JS bundles contain no ATS references
12
+ //
13
+ // Feishu ATSX tenants probed:
14
+ // baichuan.jobs.feishu.cn → HTTP 400 empty body for all channels
15
+ // (TLS cert resolves — wildcard *.jobs.feishu.cn —
16
+ // but no portal is configured on the ATSX backend)
17
+ // baichuan-ai.jobs.feishu.cn → HTTP 404 (DNS only, no TLS/host)
18
+ // baichuan-inc.jobs.feishu.cn → HTTP 404
19
+ // baichuanai.jobs.feishu.cn → HTTP 404
20
+ //
21
+ // HTTP 400 + empty body is Feishu's "site not exist / tenant has no portal"
22
+ // response (documented in feishu.ts discovery notes). The subdomain is
23
+ // registered at the CDN level but has no active recruiting portal behind it.
24
+ //
25
+ // Moka ATS:
26
+ // app.mokahr.com/campus-recruitment/baichuan → "您访问的页面不存在" (error page)
27
+ // app.mokahr.com/social-recruitment/baichuan → same error
28
+ // Moka orgId probes (numeric range) — all return the Moka SPA error shell;
29
+ // no slug "baichuan" maps to a live Moka org.
30
+ //
31
+ // Greenhouse:
32
+ // boards.greenhouse.io/baichuan → 404 "job board no longer active"
33
+ // (Greenhouse board existed historically but has been deactivated.)
34
+ //
35
+ // Lever, BOSS Zhipin, Lagou, Liepin — no unauthenticated public API found.
36
+ //
37
+ // Conclusion: Baichuan currently posts positions through internal/gated channels
38
+ // only. When a public JSON endpoint becomes available this adapter can be
39
+ // upgraded to a thin wrapper around feishu.ts (createAdapter) or a bespoke
40
+ // client in a single pass.
41
+ //
42
+ // ============================================================
43
+ // PositionSummary field mapping (canonical — matches all other adapters):
44
+ // post_id — position identifier
45
+ // title — position title
46
+ // project — job category / department
47
+ // recruit_label — recruit type (e.g. "实习" / "社招")
48
+ // bgs — business group (Baichuan has no public BG dimension)
49
+ // work_cities — work location(s)
50
+ // apply_url — deep-link to the position detail page
51
+ import { extractResumeSignals, scoreOverlap, checkResume } from "./tencent.js";
52
+ export { checkResume };
53
+ const SOURCE = "www.baichuan-ai.com";
54
+ const STUB_MESSAGE = "Baichuan (百川智能): no public job API — Feishu ATSX subdomain " +
55
+ "baichuan.jobs.feishu.cn is registered but has no portal configured " +
56
+ "(HTTP 400 empty body); Moka slug 'baichuan' returns a page-not-found " +
57
+ "error; Greenhouse board has been deactivated. " +
58
+ "Visit https://www.baichuan-ai.com/ for any current career links.";
59
+ // ---------- stub functions ----------
60
+ export async function searchPositions(_opts = {}) {
61
+ return {
62
+ ok: false,
63
+ source: SOURCE,
64
+ message: STUB_MESSAGE,
65
+ query: {},
66
+ positions: [],
67
+ };
68
+ }
69
+ export async function fetchAllPositions(_opts = {}) {
70
+ return {
71
+ ok: false,
72
+ source: SOURCE,
73
+ message: STUB_MESSAGE,
74
+ total: 0,
75
+ fetched: 0,
76
+ positions: [],
77
+ };
78
+ }
79
+ export async function fetchPositionDetail(postId) {
80
+ return {
81
+ ok: false,
82
+ source: SOURCE,
83
+ message: STUB_MESSAGE,
84
+ post_id: postId,
85
+ };
86
+ }
87
+ export async function fetchDictionaries() {
88
+ return {
89
+ ok: false,
90
+ source: SOURCE,
91
+ message: STUB_MESSAGE,
92
+ };
93
+ }
94
+ export async function listNotices() {
95
+ return {
96
+ ok: false,
97
+ source: SOURCE,
98
+ message: STUB_MESSAGE,
99
+ notices: [],
100
+ };
101
+ }
102
+ export async function getNotice(noticeId) {
103
+ return {
104
+ ok: false,
105
+ source: SOURCE,
106
+ message: STUB_MESSAGE,
107
+ notice_id: noticeId,
108
+ };
109
+ }
110
+ export async function findNoticesByQuestion(question, _opts = {}) {
111
+ return {
112
+ ok: false,
113
+ source: SOURCE,
114
+ question,
115
+ message: STUB_MESSAGE,
116
+ matches: [],
117
+ };
118
+ }
119
+ // ---------- matchResume ----------
120
+ // Extracts signals so callers can see what terms were parsed, even though
121
+ // no live positions are available to score against.
122
+ export async function matchResume(text, _opts = {}) {
123
+ const { terms, cities } = extractResumeSignals(text ?? "");
124
+ return {
125
+ ok: false,
126
+ source: SOURCE,
127
+ extracted_terms: terms,
128
+ city_preferences: cities,
129
+ matches: [],
130
+ message: STUB_MESSAGE,
131
+ };
132
+ }
133
+ export { extractResumeSignals, scoreOverlap };
package/dist/byd.js ADDED
@@ -0,0 +1,160 @@
1
+ // Thin client stub for BYD (比亚迪) campus-recruiting portal at job.byd.com.
2
+ //
3
+ // ============================================================
4
+ // Endpoint discovery (probed 2026-05, JS bundle app.e46eb97b.js +
5
+ // chunk-e8fe.d262cda1.js, chunk-ac75.7dee0692.js, chunk-a7e5.62aed375.js,
6
+ // chunk-76ac.cedb4013.js, chunk-dbeb.0075e53e.js):
7
+ //
8
+ // Portal entry:
9
+ // https://job.byd.com/ → redirects to https://job.byd.com/portal/pc/
10
+ // https://careers.byd.com/ → Vite/Vue marketing page (static, no job listings)
11
+ // https://job.byd.com/portal/pc/ → main Vue SPA (webpack, ElementUI)
12
+ //
13
+ // Axios instance (t3Un module in app.e46eb97b.js):
14
+ // baseURL = "/portal/api"
15
+ // Interceptor: adds header Authorization: "bearer <token>" from Vuex store.
16
+ // Code 4001 → "Token无效或已过期: Not Authenticated" (auto-redirect to login).
17
+ //
18
+ // Campus-related API endpoints found in JS bundles:
19
+ // POST /portal/api/school/queryJobList → campus job list
20
+ // POST /portal/api/position/queryList → position list (also skiller/social)
21
+ // POST /portal/api/position/queryDetail → position detail
22
+ // POST /portal/api/other-info/notice/query-list → campus notices
23
+ // POST /portal/api/position/schedule/query-list → campus schedule / timeline
24
+ // GET /portal/api/siteInfo/faq → FAQ
25
+ // POST /portal/api/common/queryCodeTree → code dictionary
26
+ //
27
+ // All endpoints probed 2026-05: EVERY request returns:
28
+ // HTTP 200, body: {"code":4001,"timestamp":...,"msg":"Token无效或已过期: Not Authenticated"}
29
+ //
30
+ // Auth model:
31
+ // Requires a JWT bearer token obtained through BYD account login
32
+ // (POST /portal/api/account/login, then GET /portal/api/account/user-info).
33
+ // There is NO public/anonymous browsing API — even the FAQ and code-tree
34
+ // endpoints are gated behind a valid token.
35
+ //
36
+ // careers.byd.com investigation:
37
+ // careers.byd.com is an internationalised marketing SPA (Vite + Vue 3).
38
+ // Its BydPage-6104aa3e.js uses baseURL "/global-portal/api" with two
39
+ // known endpoints:
40
+ // GET /global-portal-api/global-material/getGlobalMaterial → 404
41
+ // GET /global-portal-api/global-country/getCountryNetwork → 404
42
+ // The site is a static landing page that links to job.byd.com; it does
43
+ // not expose any independent job-search API.
44
+ //
45
+ // ============================================================
46
+ // Summary: BYD has no publicly accessible campus-job search API.
47
+ // All API calls require a logged-in user JWT.
48
+ // This adapter is an honest stub — every function returns ok:false with
49
+ // an informative message. It will be upgraded once an authenticated
50
+ // (scrape-friendly) path is identified.
51
+ //
52
+ // ============================================================
53
+ // PositionSummary field mapping (BYD → canonical, documented for future use):
54
+ // post_id ← item.positionId or item.id (string)
55
+ // title ← item.positionName (e.g. "校招-软件开发工程师")
56
+ // project ← item.positionTypeName (职位类型, e.g. "研发")
57
+ // recruit_label ← item.recruitTypeName (e.g. "应届生" / "实习生")
58
+ // bgs ← "" (not exposed in API)
59
+ // work_cities ← item.workPlace or item.city (free-text Chinese city string)
60
+ // apply_url ← https://job.byd.com/portal/pc/school/schoolPositionApply
61
+ // ?positionId={id}
62
+ //
63
+ // ============================================================
64
+ import { extractResumeSignals, scoreOverlap, checkResume } from "./tencent.js";
65
+ export { checkResume };
66
+ const SOURCE = "job.byd.com";
67
+ const CAMPUS_PAGE = "https://job.byd.com/portal/pc/school/home";
68
+ // ---- stub reason ----
69
+ const STUB_REASON = "BYD job.byd.com: all API endpoints require a valid JWT bearer token " +
70
+ "(code 4001 — Token无效或已过期). No public/anonymous job search API exists. " +
71
+ "Visit https://job.byd.com/portal/pc/school/home to browse positions after login.";
72
+ // ---- searchPositions ----
73
+ export async function searchPositions(_opts = {}) {
74
+ return {
75
+ ok: false,
76
+ source: SOURCE,
77
+ message: STUB_REASON,
78
+ campus_page: CAMPUS_PAGE,
79
+ positions: [],
80
+ };
81
+ }
82
+ // ---- fetchAllPositions ----
83
+ export async function fetchAllPositions(_opts = {}) {
84
+ return {
85
+ ok: false,
86
+ source: SOURCE,
87
+ message: STUB_REASON,
88
+ campus_page: CAMPUS_PAGE,
89
+ fetched: 0,
90
+ positions: [],
91
+ };
92
+ }
93
+ // ---- fetchPositionDetail ----
94
+ export async function fetchPositionDetail(_postId) {
95
+ return { ok: false, source: SOURCE, message: STUB_REASON };
96
+ }
97
+ // ---- fetchDictionaries ----
98
+ export async function fetchDictionaries() {
99
+ return {
100
+ ok: false,
101
+ source: SOURCE,
102
+ message: STUB_REASON,
103
+ note: "BYD: no public filter-taxonomy endpoint. " +
104
+ "POST /portal/api/common/queryCodeTree returns 4001 without a token.",
105
+ known_endpoints: [
106
+ "POST /portal/api/school/queryJobList (campus job list — auth required)",
107
+ "POST /portal/api/position/queryList (position list — auth required)",
108
+ "POST /portal/api/position/queryDetail (position detail — auth required)",
109
+ "POST /portal/api/other-info/notice/query-list (notices — auth required)",
110
+ "POST /portal/api/position/schedule/query-list (campus schedule — auth required)",
111
+ "GET /portal/api/siteInfo/faq (FAQ — auth required)",
112
+ "POST /portal/api/common/queryCodeTree (code tree — auth required)",
113
+ ],
114
+ };
115
+ }
116
+ // ---- listNotices ----
117
+ export async function listNotices() {
118
+ return {
119
+ ok: false,
120
+ source: SOURCE,
121
+ message: "BYD: notices endpoint (POST /portal/api/other-info/notice/query-list) requires authentication.",
122
+ };
123
+ }
124
+ // ---- getNotice ----
125
+ export async function getNotice(_id) {
126
+ return {
127
+ ok: false,
128
+ source: SOURCE,
129
+ message: "BYD: no public notices endpoint (auth required).",
130
+ };
131
+ }
132
+ // ---- findNoticesByQuestion ----
133
+ export async function findNoticesByQuestion(_question, _opts = {}) {
134
+ return {
135
+ ok: false,
136
+ source: SOURCE,
137
+ message: "BYD: no public notices endpoint (auth required).",
138
+ };
139
+ }
140
+ // ---- matchResume ----
141
+ // Resume matching is best-effort using extractResumeSignals/scoreOverlap from
142
+ // tencent.ts, but since the position listing API is gated, we can only return
143
+ // a stub with the extracted signals and a pointer to the campus page.
144
+ export async function matchResume(text, opts = {}) {
145
+ // Extract signals so the caller knows what was parsed from the resume
146
+ const { terms, cities } = extractResumeSignals(text ?? "");
147
+ void opts; // unused until API becomes accessible
148
+ return {
149
+ ok: false,
150
+ source: SOURCE,
151
+ message: "BYD: cannot search positions — API requires authentication. " +
152
+ `Extracted resume signals: [${terms.slice(0, 10).join(", ")}]. ` +
153
+ "Visit the campus page to search manually.",
154
+ campus_page: CAMPUS_PAGE,
155
+ extracted_terms: terms,
156
+ city_preferences: cities,
157
+ };
158
+ }
159
+ // ---- re-export helpers so the tencent resume signals are accessible ----
160
+ export { extractResumeSignals, scoreOverlap };
@@ -0,0 +1,63 @@
1
+ // 菜鸟 (Cainiao Network) — stub adapter for `job-pro`.
2
+ //
3
+ // STATUS: stub-only. Both campus and social recruiting are routed through
4
+ // Alibaba's unified careers infrastructure, which is hosted on subdomains
5
+ // that fail to resolve over public DNS (likely group-network-only A records).
6
+ //
7
+ // ============================================================
8
+ // RECONNAISSANCE RESULTS (probed 2026-05):
9
+ //
10
+ // https://campus.cainiao.com — 000 (no public DNS / unreachable)
11
+ // https://recruit.cainiao.com — 000 (no public DNS / unreachable)
12
+ // https://job.cainiao.com — 000 (no public DNS / unreachable)
13
+ //
14
+ // The corporate careers blurb on www.cainiao.com links out to
15
+ // "campus-talent.alibaba.com" (already covered by the `alibaba` adapter),
16
+ // suggesting cainiao postings are merged into the Alibaba Group careers feed
17
+ // when they go public. The dedicated Cainiao SPA is internal-only.
18
+ //
19
+ // Feishu ATSX: cainiao.jobs.feishu.cn — HTTP 400 (no portal configured)
20
+ // Greenhouse / Lever / Moka: no `cainiao` slug found on any of them.
21
+ //
22
+ // Conclusion: no unauthenticated public API outside of the Alibaba Group
23
+ // feed. Use `job-pro alibaba search "菜鸟"` to surface group-listed roles, or
24
+ // visit https://www.cainiao.com/ for direct contact info.
25
+ import { extractResumeSignals, scoreOverlap, checkResume } from "./tencent.js";
26
+ export { checkResume };
27
+ const SOURCE = "cainiao.com";
28
+ const STUB_MESSAGE = "Cainiao (菜鸟): dedicated careers subdomains (campus / recruit / job.cainiao.com) fail to resolve " +
29
+ "over public DNS. Public-facing roles are surfaced through the Alibaba Group careers feed " +
30
+ "(use `job-pro alibaba search \"菜鸟\"`). No standalone unauthenticated public API.";
31
+ export async function searchPositions(_opts = {}) {
32
+ return { ok: false, source: SOURCE, message: STUB_MESSAGE, query: {}, positions: [] };
33
+ }
34
+ export async function fetchAllPositions(_opts = {}) {
35
+ return { ok: false, source: SOURCE, message: STUB_MESSAGE, total: 0, fetched: 0, positions: [] };
36
+ }
37
+ export async function fetchPositionDetail(postId) {
38
+ return { ok: false, source: SOURCE, message: STUB_MESSAGE, post_id: postId };
39
+ }
40
+ export async function fetchDictionaries() {
41
+ return { ok: false, source: SOURCE, message: STUB_MESSAGE };
42
+ }
43
+ export async function listNotices() {
44
+ return { ok: false, source: SOURCE, message: STUB_MESSAGE, notices: [] };
45
+ }
46
+ export async function getNotice(noticeId) {
47
+ return { ok: false, source: SOURCE, message: STUB_MESSAGE, notice_id: noticeId };
48
+ }
49
+ export async function findNoticesByQuestion(question, _opts = {}) {
50
+ return { ok: false, source: SOURCE, question, message: STUB_MESSAGE, matches: [] };
51
+ }
52
+ export async function matchResume(text, _opts = {}) {
53
+ const { terms, cities } = extractResumeSignals(text ?? "");
54
+ return {
55
+ ok: false,
56
+ source: SOURCE,
57
+ extracted_terms: terms,
58
+ city_preferences: cities,
59
+ matches: [],
60
+ message: STUB_MESSAGE,
61
+ };
62
+ }
63
+ export { extractResumeSignals, scoreOverlap };
@@ -0,0 +1,64 @@
1
+ // 寒武纪 (Cambricon) — stub adapter for `job-pro`.
2
+ //
3
+ // STATUS: stub-only. Cambricon's careers domains do not resolve over public
4
+ // DNS, and no third-party ATS tenant (Feishu, Moka, Greenhouse, Lever) is
5
+ // provisioned for the company. Recruiting runs through internal channels.
6
+ //
7
+ // ============================================================
8
+ // RECONNAISSANCE RESULTS (probed 2026-05):
9
+ //
10
+ // https://hr.cambricon.com — 000 (no public DNS / unreachable)
11
+ // https://careers.cambricon.com — 000 (no public DNS / unreachable)
12
+ // https://campus.cambricon.com — 000 (no public DNS / unreachable)
13
+ //
14
+ // Feishu ATSX: cambricon.jobs.feishu.cn — HTTP 400 (no portal)
15
+ // Greenhouse: cambricon — HTTP 404 (no board)
16
+ // Lever: cambricon — HTTP 404 (no posting)
17
+ // Moka: app.mokahr.com/social-recruitment/cambricon → 302 (unprovisioned)
18
+ //
19
+ // Cambricon's official careers blurb on cambricon.com points to the
20
+ // public WeChat 寒武纪招聘 official account, which posts openings as
21
+ // articles and routes applications to internal HR contacts.
22
+ //
23
+ // Conclusion: no unauthenticated public API. Visit the 寒武纪招聘 WeChat
24
+ // official account, or send a resume to the careers email listed at
25
+ // https://www.cambricon.com/.
26
+ import { extractResumeSignals, scoreOverlap, checkResume } from "./tencent.js";
27
+ export { checkResume };
28
+ const SOURCE = "cambricon.com";
29
+ const STUB_MESSAGE = "Cambricon (寒武纪): hr / careers / campus.cambricon.com all fail to resolve over public DNS. " +
30
+ "No Greenhouse / Lever / Feishu / Moka tenant provisioned. Recruiting runs through the " +
31
+ "WeChat 寒武纪招聘 official account. No unauthenticated public API available.";
32
+ export async function searchPositions(_opts = {}) {
33
+ return { ok: false, source: SOURCE, message: STUB_MESSAGE, query: {}, positions: [] };
34
+ }
35
+ export async function fetchAllPositions(_opts = {}) {
36
+ return { ok: false, source: SOURCE, message: STUB_MESSAGE, total: 0, fetched: 0, positions: [] };
37
+ }
38
+ export async function fetchPositionDetail(postId) {
39
+ return { ok: false, source: SOURCE, message: STUB_MESSAGE, post_id: postId };
40
+ }
41
+ export async function fetchDictionaries() {
42
+ return { ok: false, source: SOURCE, message: STUB_MESSAGE };
43
+ }
44
+ export async function listNotices() {
45
+ return { ok: false, source: SOURCE, message: STUB_MESSAGE, notices: [] };
46
+ }
47
+ export async function getNotice(noticeId) {
48
+ return { ok: false, source: SOURCE, message: STUB_MESSAGE, notice_id: noticeId };
49
+ }
50
+ export async function findNoticesByQuestion(question, _opts = {}) {
51
+ return { ok: false, source: SOURCE, question, message: STUB_MESSAGE, matches: [] };
52
+ }
53
+ export async function matchResume(text, _opts = {}) {
54
+ const { terms, cities } = extractResumeSignals(text ?? "");
55
+ return {
56
+ ok: false,
57
+ source: SOURCE,
58
+ extracted_terms: terms,
59
+ city_preferences: cities,
60
+ matches: [],
61
+ message: STUB_MESSAGE,
62
+ };
63
+ }
64
+ export { extractResumeSignals, scoreOverlap };
package/dist/cicc.js ADDED
@@ -0,0 +1,152 @@
1
+ // Thin client for CICC / 中金公司 (China International Capital Corporation) campus recruiting.
2
+ //
3
+ // ============================================================
4
+ // API DISCOVERY (probed 2026-05-14)
5
+ //
6
+ // Five potential portals were investigated:
7
+ //
8
+ // 1. careers.cicc.com (CICC dedicated careers subdomain)
9
+ // DNS resolves to 198.18.1.66 (IANA RFC 2544 benchmarking range — unreachable
10
+ // from public internet; SSL handshake fails at TCP level).
11
+ // This hostname is dead from outside the CICC intranet.
12
+ //
13
+ // 2. hr.cicc.com (HR portal)
14
+ // DNS resolves to 198.18.1.212 — same IANA-reserved range.
15
+ // Unreachable: SSL handshake fails.
16
+ //
17
+ // 3. www.cicc.com/career (main site career section)
18
+ // Returns HTTP 521 (Cloudflare "Web server is down"). Cloudflare can reach
19
+ // the origin but the origin is not responding. No API endpoints discoverable.
20
+ //
21
+ // 4. app.mokahr.com — Moka ATS, orgId 28961 (reconnaissance-flagged candidate)
22
+ // The Moka platform (app.mokahr.com) is reachable (resolves to Aliyun WAF
23
+ // 47.93.92.61 via authoritative DNS). However:
24
+ // - /campus-recruitment/cicc/28961 → HTTP 302 self-redirect (infinite loop)
25
+ // - /campus-recruitment/cicc-career/28961 → HTTP 200 HTML, but init-data
26
+ // contains {"message":"您访问的页面不存在"} — org page does not exist
27
+ // - /social-recruitment/cicc-career/28961 → same "page not found" response
28
+ // - All /api/campus/jobs?organizationId=28961 variants → {"code":-1,"message":"您访问的页面不存在"}
29
+ // Conclusion: orgId 28961 is either wrong or the CICC Moka tenant is fully
30
+ // auth-gated behind enterprise SSO. No public JSON feed is accessible.
31
+ //
32
+ // 5. Alternative slugs tried on Moka: "cicc", "cicc-career", "zhongjin", numeric
33
+ // variants of orgId (28960–28965) — all return either the same "not found"
34
+ // response or an infinite self-redirect.
35
+ //
36
+ // VERDICT: No public unauthenticated API exists for CICC campus recruiting as of
37
+ // 2026-05-14. All externally-facing hostnames resolve to IANA-reserved IPs (intranet)
38
+ // or return infrastructure errors (521). The Moka tenant, if it exists, is fully
39
+ // auth-gated with no discoverable public endpoints.
40
+ //
41
+ // The canonical path for candidates is the CICC official career portal:
42
+ // https://www.cicc.com/career (may load once the origin is healthy)
43
+ // Or the known Moka URL patterns (require an active employee/candidate session):
44
+ // https://app.mokahr.com/campus-recruitment/cicc-career/28961
45
+ //
46
+ // ============================================================
47
+ // PositionSummary field mapping (canonical keys, matches all other adapters)
48
+ // post_id — string job identifier
49
+ // title — position title
50
+ // project — job category / department (e.g. "投行" / "研究" / "固收")
51
+ // recruit_label — recruit type label (e.g. "校园招聘" / "实习")
52
+ // bgs — business group / division (not exposed without auth, always "")
53
+ // work_cities — work location string
54
+ // apply_url — deep link to the job posting
55
+ // ============================================================
56
+ import { extractResumeSignals, scoreOverlap, checkResume } from "./tencent.js";
57
+ export { checkResume };
58
+ const SOURCE = "www.cicc.com";
59
+ const CAMPUS_PAGE = "https://www.cicc.com/career";
60
+ // Moka tenant URL kept for reference — requires auth
61
+ const MOKA_PAGE = "https://app.mokahr.com/campus-recruitment/cicc-career/28961";
62
+ // ---------- stub message ----------
63
+ const STUB_MSG = "CICC (中金公司) campus recruiting has no publicly accessible API as of 2026-05-14. " +
64
+ "careers.cicc.com and hr.cicc.com resolve to IANA-reserved IPs (198.18.x.x, " +
65
+ "unreachable outside the CICC intranet; SSL handshake fails). " +
66
+ "www.cicc.com returns HTTP 521 (Cloudflare origin-down). " +
67
+ "The Moka ATS tenant (orgId 28961) is fully auth-gated: all public API paths " +
68
+ 'return {"code":-1,"message":"您访问的页面不存在"}. ' +
69
+ `Apply directly at ${CAMPUS_PAGE} or via the Moka portal (login required): ${MOKA_PAGE}`;
70
+ // ---------- searchPositions ----------
71
+ export async function searchPositions(_opts = {}) {
72
+ return {
73
+ ok: false,
74
+ source: SOURCE,
75
+ message: STUB_MSG,
76
+ apply_url: CAMPUS_PAGE,
77
+ moka_url: MOKA_PAGE,
78
+ positions: [],
79
+ };
80
+ }
81
+ // ---------- fetchAllPositions ----------
82
+ export async function fetchAllPositions(_opts = {}) {
83
+ return {
84
+ ok: false,
85
+ source: SOURCE,
86
+ message: STUB_MSG,
87
+ apply_url: CAMPUS_PAGE,
88
+ moka_url: MOKA_PAGE,
89
+ fetched: 0,
90
+ positions: [],
91
+ };
92
+ }
93
+ // ---------- fetchPositionDetail ----------
94
+ export async function fetchPositionDetail(_postId) {
95
+ return {
96
+ ok: false,
97
+ source: SOURCE,
98
+ message: STUB_MSG,
99
+ apply_url: CAMPUS_PAGE,
100
+ moka_url: MOKA_PAGE,
101
+ };
102
+ }
103
+ // ---------- fetchDictionaries ----------
104
+ export async function fetchDictionaries() {
105
+ return {
106
+ ok: false,
107
+ source: SOURCE,
108
+ message: STUB_MSG,
109
+ apply_url: CAMPUS_PAGE,
110
+ moka_url: MOKA_PAGE,
111
+ };
112
+ }
113
+ // ---------- notices (no public endpoint) ----------
114
+ export async function listNotices() {
115
+ return {
116
+ ok: false,
117
+ source: SOURCE,
118
+ message: "CICC: no public notices endpoint",
119
+ };
120
+ }
121
+ export async function getNotice(_id) {
122
+ return {
123
+ ok: false,
124
+ source: SOURCE,
125
+ message: "CICC: no public notices endpoint",
126
+ };
127
+ }
128
+ export async function findNoticesByQuestion(_question, _opts = {}) {
129
+ return {
130
+ ok: false,
131
+ source: SOURCE,
132
+ message: "CICC: no public notices endpoint",
133
+ };
134
+ }
135
+ // ---------- matchResume ----------
136
+ // Resume matching cannot fetch live position data without auth.
137
+ // We extract signals from the resume and direct the user to the CICC career portal.
138
+ export async function matchResume(text, opts = {}) {
139
+ void opts;
140
+ const { terms, cities } = extractResumeSignals(text ?? "");
141
+ return {
142
+ ok: false,
143
+ source: SOURCE,
144
+ message: STUB_MSG,
145
+ apply_url: CAMPUS_PAGE,
146
+ moka_url: MOKA_PAGE,
147
+ extracted_terms: terms,
148
+ city_preferences: cities,
149
+ };
150
+ }
151
+ // Export helpers so callers that import from this module can use them.
152
+ export { extractResumeSignals, scoreOverlap };
@@ -0,0 +1,39 @@
1
+ // DeepSeek (深度求索) — stub adapter for `job-pro`.
2
+ //
3
+ // STATUS: stub-only. DeepSeek is part of High-Flyer (幻方量化) and lists
4
+ // careers via the parent company on Moka social-recruitment. Probe results:
5
+ // www.deepseek.com/careers → 200 HTML, no inline job data / API path
6
+ // careers.deepseek.com → DNS resolves but TLS rejects from non-CN IPs
7
+ // app.mokahr.com/social-recruitment/high-flyer/140576/ → Moka SPA, auth-gated
8
+ // Moka public anonymous API is gated (confirmed; see Moka probe in repo
9
+ // history). When DeepSeek opens a public JSON endpoint we rewrite in one pass.
10
+ import { extractResumeSignals, checkResume } from "./tencent.js";
11
+ export { checkResume };
12
+ const SOURCE = "www.deepseek.com";
13
+ const STUB_MESSAGE = "DeepSeek: no public job API — careers route through Moka social-recruitment " +
14
+ "(high-flyer/140576), which requires session auth. corporate careers page is HTML only.";
15
+ export async function searchPositions(_opts = {}) {
16
+ return { ok: false, source: SOURCE, message: STUB_MESSAGE, query: {}, positions: [] };
17
+ }
18
+ export async function fetchAllPositions(_opts = {}) {
19
+ return { ok: false, source: SOURCE, message: STUB_MESSAGE, total: 0, fetched: 0, positions: [] };
20
+ }
21
+ export async function fetchPositionDetail(postId) {
22
+ return { ok: false, source: SOURCE, message: STUB_MESSAGE, post_id: postId };
23
+ }
24
+ export async function fetchDictionaries() {
25
+ return { ok: false, source: SOURCE, message: STUB_MESSAGE };
26
+ }
27
+ export async function listNotices() {
28
+ return { ok: false, source: SOURCE, message: STUB_MESSAGE, notices: [] };
29
+ }
30
+ export async function getNotice(noticeId) {
31
+ return { ok: false, source: SOURCE, message: STUB_MESSAGE, notice_id: noticeId };
32
+ }
33
+ export async function findNoticesByQuestion(question, _opts = {}) {
34
+ return { ok: false, source: SOURCE, question, message: STUB_MESSAGE, matches: [] };
35
+ }
36
+ export async function matchResume(text, _opts = {}) {
37
+ const { terms, cities } = extractResumeSignals(text ?? "");
38
+ return { ok: false, source: SOURCE, extracted_terms: terms, city_preferences: cities, matches: [], message: STUB_MESSAGE };
39
+ }
@@ -0,0 +1,37 @@
1
+ // 银河通用 / Galaxy Universal (Galbot — embodied AI robotics) — stub for `job-pro`.
2
+ //
3
+ // STATUS: stub-only. Galaxy Universal lists careers via Moka social-recruitment
4
+ // (orgId 165930, slug yinhetongyong), which is auth-gated for anonymous access.
5
+ // Probe results:
6
+ // www.galbot.com/careers, galaxyuniversal.com/careers → no public API discoverable
7
+ // app.mokahr.com/social-recruitment/yinhetongyong/165930 → Moka SPA, session auth required
8
+ import { extractResumeSignals, checkResume } from "./tencent.js";
9
+ export { checkResume };
10
+ const SOURCE = "galbot.com";
11
+ const STUB_MESSAGE = "Galaxy Universal / 银河通用: no public job API — Moka social-recruitment portal " +
12
+ "(yinhetongyong/165930) requires session auth (verified Moka anon path is gated).";
13
+ export async function searchPositions(_opts = {}) {
14
+ return { ok: false, source: SOURCE, message: STUB_MESSAGE, query: {}, positions: [] };
15
+ }
16
+ export async function fetchAllPositions(_opts = {}) {
17
+ return { ok: false, source: SOURCE, message: STUB_MESSAGE, total: 0, fetched: 0, positions: [] };
18
+ }
19
+ export async function fetchPositionDetail(postId) {
20
+ return { ok: false, source: SOURCE, message: STUB_MESSAGE, post_id: postId };
21
+ }
22
+ export async function fetchDictionaries() {
23
+ return { ok: false, source: SOURCE, message: STUB_MESSAGE };
24
+ }
25
+ export async function listNotices() {
26
+ return { ok: false, source: SOURCE, message: STUB_MESSAGE, notices: [] };
27
+ }
28
+ export async function getNotice(noticeId) {
29
+ return { ok: false, source: SOURCE, message: STUB_MESSAGE, notice_id: noticeId };
30
+ }
31
+ export async function findNoticesByQuestion(question, _opts = {}) {
32
+ return { ok: false, source: SOURCE, question, message: STUB_MESSAGE, matches: [] };
33
+ }
34
+ export async function matchResume(text, _opts = {}) {
35
+ const { terms, cities } = extractResumeSignals(text ?? "");
36
+ return { ok: false, source: SOURCE, extracted_terms: terms, city_preferences: cities, matches: [], message: STUB_MESSAGE };
37
+ }