@ha7ch/job-pro 1.0.93

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.
Files changed (68) hide show
  1. package/dist/adapter.js +17 -0
  2. package/dist/agibot.js +399 -0
  3. package/dist/alibaba.js +509 -0
  4. package/dist/antgroup.js +397 -0
  5. package/dist/apply.js +1373 -0
  6. package/dist/baichuan.js +49 -0
  7. package/dist/baidu.js +452 -0
  8. package/dist/bilibili.js +455 -0
  9. package/dist/byd.js +412 -0
  10. package/dist/bytedance.js +619 -0
  11. package/dist/cainiao.js +56 -0
  12. package/dist/cambricon.js +33 -0
  13. package/dist/cdp.js +237 -0
  14. package/dist/cicc.js +56 -0
  15. package/dist/coverage.js +60 -0
  16. package/dist/deepseek.js +25 -0
  17. package/dist/didi.js +381 -0
  18. package/dist/feishu.js +577 -0
  19. package/dist/galaxyuniversal.js +24 -0
  20. package/dist/geely.js +35 -0
  21. package/dist/greenhouse.js +432 -0
  22. package/dist/hikvision.js +58 -0
  23. package/dist/horizonrobotics.js +46 -0
  24. package/dist/hoyoverse.js +26 -0
  25. package/dist/huawei.js +537 -0
  26. package/dist/iflytek.js +380 -0
  27. package/dist/index.js +1828 -0
  28. package/dist/iqiyi.js +494 -0
  29. package/dist/jd.js +559 -0
  30. package/dist/kuaishou.js +496 -0
  31. package/dist/lever.js +455 -0
  32. package/dist/liauto.js +393 -0
  33. package/dist/liepin.js +357 -0
  34. package/dist/lilith.js +300 -0
  35. package/dist/megvii.js +27 -0
  36. package/dist/meituan.js +633 -0
  37. package/dist/memory.js +76 -0
  38. package/dist/mihoyo.js +308 -0
  39. package/dist/minimax.js +32 -0
  40. package/dist/moka.js +473 -0
  41. package/dist/moonshot.js +24 -0
  42. package/dist/netease.js +424 -0
  43. package/dist/nio.js +24 -0
  44. package/dist/oppo.js +285 -0
  45. package/dist/pdd.js +614 -0
  46. package/dist/pingan.js +493 -0
  47. package/dist/sensetime.js +51 -0
  48. package/dist/sf.js +310 -0
  49. package/dist/stepfun.js +24 -0
  50. package/dist/tencent.js +770 -0
  51. package/dist/trip.js +396 -0
  52. package/dist/unitree.js +418 -0
  53. package/dist/vivo.js +361 -0
  54. package/dist/webank.js +55 -0
  55. package/dist/wecruit.js +438 -0
  56. package/dist/weibo.js +337 -0
  57. package/dist/weride.js +29 -0
  58. package/dist/xiaohongshu.js +480 -0
  59. package/dist/xiaomi.js +529 -0
  60. package/dist/xpeng.js +34 -0
  61. package/dist/zerooneai.js +42 -0
  62. package/dist/zhipu.js +478 -0
  63. package/extension/README.md +79 -0
  64. package/extension/background.js +177 -0
  65. package/extension/manifest.json +55 -0
  66. package/extension/popup.html +37 -0
  67. package/extension/popup.js +54 -0
  68. package/package.json +61 -0
package/dist/moka.js ADDED
@@ -0,0 +1,473 @@
1
+ // Generic Moka (北森外 — `app.mokahr.com` 招聘) adapter factory.
2
+ //
3
+ // Moka is a SaaS ATS used by many Chinese tech companies (Megvii, DeepSeek,
4
+ // Galaxy Universal, StepFun, Cambricon, Geely, …). Each tenant publishes a
5
+ // public portal at one of these URL shapes:
6
+ //
7
+ // https://app.mokahr.com/campus-recruitment/<orgSlug>/<siteId>
8
+ // https://app.mokahr.com/campus_apply/<orgSlug>/<siteId>
9
+ // https://app.mokahr.com/social-recruitment/<orgSlug>/<siteId>
10
+ // https://app.mokahr.com/recommendation-recruitment/<orgSlug>/<siteId>
11
+ //
12
+ // The SSR HTML always embeds an `<input id="init-data" value="<HTML-escaped JSON>">`
13
+ // containing the first page of jobs + an `aesIv` constant. For deeper
14
+ // pagination the SPA POSTs to
15
+ // /api/outer/ats-apply/website/jobs/v2?orgId=<slug>
16
+ // and receives an AES-CBC encrypted envelope `{data, necromancer}`. We
17
+ // decrypt with key=necromancer (utf8) and iv=aesIv (utf8) to obtain the
18
+ // plain JSON page.
19
+ //
20
+ // This factory hides that machinery. Adapters declare `{ orgSlug, channels }`
21
+ // (one channel per public portal URL) and get the eight canonical verbs.
22
+ import { extractResumeSignals, scoreOverlap, checkResume } from "./tencent.js";
23
+ import { createDecipheriv } from "node:crypto";
24
+ export { checkResume };
25
+ // ---------- shared headers ----------
26
+ const DEFAULT_HEADERS = {
27
+ "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36",
28
+ Accept: "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
29
+ "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8",
30
+ };
31
+ const API_ENDPOINT = "https://app.mokahr.com/api/outer/ats-apply/website/jobs/v2";
32
+ // ---------- shared helpers ----------
33
+ function htmlDecode(s) {
34
+ return s
35
+ .replace(/&quot;/g, '"')
36
+ .replace(/&amp;/g, "&")
37
+ .replace(/&lt;/g, "<")
38
+ .replace(/&gt;/g, ">")
39
+ .replace(/&#x27;/g, "'")
40
+ .replace(/&#39;/g, "'");
41
+ }
42
+ function parseInitData(html) {
43
+ const m = html.match(/<input[^>]*id="init-data"[^>]*value="([^"]+)"/);
44
+ if (!m)
45
+ return null;
46
+ try {
47
+ return JSON.parse(htmlDecode(m[1]));
48
+ }
49
+ catch {
50
+ return null;
51
+ }
52
+ }
53
+ async function fetchPortalHtml(url) {
54
+ // Moka does a locale-cookie redirect dance: first request returns 302 +
55
+ // Set-Cookie; we capture them, then re-issue.
56
+ let response;
57
+ try {
58
+ response = await fetch(url, { method: "GET", headers: DEFAULT_HEADERS, redirect: "manual" });
59
+ }
60
+ catch (err) {
61
+ return { ok: false, message: `network error: ${err instanceof Error ? err.message : err}` };
62
+ }
63
+ const cookies = [];
64
+ const headersAny = response.headers;
65
+ if (typeof headersAny.getSetCookie === "function") {
66
+ for (const v of headersAny.getSetCookie.call(response.headers) ?? []) {
67
+ const c = v.split(";")[0];
68
+ if (c)
69
+ cookies.push(c);
70
+ }
71
+ }
72
+ if (cookies.length === 0) {
73
+ const raw = response.headers.get("set-cookie");
74
+ if (raw)
75
+ cookies.push(...raw.split(/,(?=[^;]+=)/).map((c) => c.split(";")[0].trim()));
76
+ }
77
+ const cookieHeader = cookies.join("; ");
78
+ let r2;
79
+ try {
80
+ r2 = await fetch(url, {
81
+ method: "GET",
82
+ headers: { ...DEFAULT_HEADERS, Cookie: cookieHeader },
83
+ redirect: "follow",
84
+ });
85
+ }
86
+ catch (err) {
87
+ return { ok: false, message: `network error: ${err instanceof Error ? err.message : err}` };
88
+ }
89
+ if (!r2.ok)
90
+ return { ok: false, message: `HTTP ${r2.status}` };
91
+ const html = await r2.text();
92
+ return { ok: true, html, cookieHeader, message: "ok" };
93
+ }
94
+ function decryptMokaEnvelope(envelope, aesIv) {
95
+ if (!envelope.data || !envelope.necromancer)
96
+ return null;
97
+ try {
98
+ const key = Buffer.from(envelope.necromancer, "utf8");
99
+ const iv = Buffer.from(aesIv, "utf8");
100
+ const decipher = createDecipheriv("aes-128-cbc", key, iv);
101
+ const plain = Buffer.concat([
102
+ decipher.update(Buffer.from(envelope.data, "base64")),
103
+ decipher.final(),
104
+ ]);
105
+ return JSON.parse(plain.toString("utf8"));
106
+ }
107
+ catch {
108
+ return null;
109
+ }
110
+ }
111
+ async function fetchEncryptedPage(orgSlug, siteId, pageNum, pageSize, aesIv, cookieHeader, portalUrl) {
112
+ const url = `${API_ENDPOINT}?orgId=${encodeURIComponent(orgSlug)}`;
113
+ const body = {
114
+ orgId: orgSlug,
115
+ siteId: String(siteId),
116
+ pageNum,
117
+ pageSize,
118
+ needStat: true,
119
+ };
120
+ let response;
121
+ try {
122
+ response = await fetch(url, {
123
+ method: "POST",
124
+ headers: {
125
+ ...DEFAULT_HEADERS,
126
+ Accept: "application/json,*/*",
127
+ "Content-Type": "application/json",
128
+ Origin: "https://app.mokahr.com",
129
+ Referer: portalUrl,
130
+ Cookie: cookieHeader,
131
+ },
132
+ body: JSON.stringify(body),
133
+ });
134
+ }
135
+ catch (err) {
136
+ return { ok: false, message: `network error: ${err instanceof Error ? err.message : err}` };
137
+ }
138
+ if (!response.ok)
139
+ return { ok: false, message: `HTTP ${response.status}` };
140
+ let envelope;
141
+ try {
142
+ envelope = await response.json();
143
+ }
144
+ catch {
145
+ return { ok: false, message: "bad JSON from upstream" };
146
+ }
147
+ const decoded = decryptMokaEnvelope(envelope, aesIv);
148
+ if (!decoded || decoded.code !== 0 || !decoded.data) {
149
+ return { ok: false, message: decoded?.msg || envelope?.msg || "decrypt or upstream error" };
150
+ }
151
+ return {
152
+ ok: true,
153
+ jobs: decoded.data.jobs ?? [],
154
+ total: decoded.data.jobStats?.total ?? 0,
155
+ message: "ok",
156
+ };
157
+ }
158
+ function buildCityMap(groups) {
159
+ const out = {};
160
+ if (!groups)
161
+ return out;
162
+ for (const g of groups) {
163
+ if (typeof g.cityId === "number" && g.label)
164
+ out[g.cityId] = g.label;
165
+ }
166
+ return out;
167
+ }
168
+ function workCitiesFor(job, cityMap) {
169
+ const cities = (job.locations ?? [])
170
+ .map((l) => {
171
+ if (typeof l.cityId === "number" && cityMap[l.cityId])
172
+ return cityMap[l.cityId];
173
+ return l.country || "";
174
+ })
175
+ .filter((s) => s.length > 0);
176
+ const uniq = [];
177
+ for (const c of cities)
178
+ if (!uniq.includes(c))
179
+ uniq.push(c);
180
+ return uniq.join(" / ");
181
+ }
182
+ function commitmentFor(job) {
183
+ if (typeof job.commitment === "string" && job.commitment.length > 0)
184
+ return job.commitment;
185
+ if (job.hireMode === 1)
186
+ return "全职";
187
+ if (job.hireMode === 2)
188
+ return "实习";
189
+ return "";
190
+ }
191
+ function matchesKeyword(job, kw) {
192
+ if (!kw)
193
+ return true;
194
+ const lc = kw.toLowerCase();
195
+ return ((job.title ?? "").toLowerCase().includes(lc) ||
196
+ (job.zhineng?.name ?? "").toLowerCase().includes(lc) ||
197
+ (job.department?.name ?? "").toLowerCase().includes(lc));
198
+ }
199
+ // ---------- createAdapter ----------
200
+ export function createAdapter(cfg) {
201
+ const SOURCE = `app.mokahr.com/${cfg.orgSlug}`;
202
+ const portalUrl = (ch) => `https://app.mokahr.com/${ch.kind}/${cfg.orgSlug}/${ch.siteId}`;
203
+ function pickChannel(recruitType) {
204
+ const want = recruitType ?? cfg.defaultRecruitType ?? "social";
205
+ return cfg.channels.find((c) => c.recruitType === want) ?? cfg.channels[0];
206
+ }
207
+ function summarize(job, cityMap, ch) {
208
+ return {
209
+ post_id: String(job.id),
210
+ title: job.title ?? "",
211
+ project: job.zhineng?.name ?? "",
212
+ recruit_label: commitmentFor(job),
213
+ bgs: job.department?.name ?? "",
214
+ work_cities: workCitiesFor(job, cityMap),
215
+ apply_url: `${portalUrl(ch)}#/jobs/${encodeURIComponent(job.id)}`,
216
+ };
217
+ }
218
+ async function searchPositions(opts = {}) {
219
+ const ch = pickChannel(opts.recruitType);
220
+ const url = portalUrl(ch);
221
+ const pageSize = opts.pageSize ?? 20;
222
+ const page = opts.page ?? 1;
223
+ const keyword = opts.keyword ?? "";
224
+ const portal = await fetchPortalHtml(url);
225
+ if (!portal.ok || !portal.html) {
226
+ return {
227
+ ok: false,
228
+ source: SOURCE,
229
+ message: portal.message,
230
+ query: { recruitType: ch.recruitType, keyword, page, pageSize },
231
+ positions: [],
232
+ total: 0,
233
+ };
234
+ }
235
+ const init = parseInitData(portal.html);
236
+ if (!init || !init.jobs || !init.jobStats) {
237
+ return {
238
+ ok: false,
239
+ source: SOURCE,
240
+ message: "Moka init-data missing jobs/jobStats",
241
+ query: { recruitType: ch.recruitType, keyword, page, pageSize },
242
+ positions: [],
243
+ total: 0,
244
+ };
245
+ }
246
+ const cityMap = buildCityMap(init.jobsGroupedByLocation);
247
+ let jobs = init.jobs;
248
+ const total = init.jobStats.total ?? jobs.length;
249
+ if (page > 1 && init.aesIv && portal.cookieHeader) {
250
+ const more = await fetchEncryptedPage(cfg.orgSlug, ch.siteId, page, pageSize, init.aesIv, portal.cookieHeader, url);
251
+ if (!more.ok || !more.jobs) {
252
+ return {
253
+ ok: false,
254
+ source: SOURCE,
255
+ message: `pagination failed: ${more.message}`,
256
+ query: { recruitType: ch.recruitType, keyword, page, pageSize },
257
+ positions: [],
258
+ total,
259
+ };
260
+ }
261
+ jobs = more.jobs;
262
+ }
263
+ const filtered = jobs.filter((j) => matchesKeyword(j, keyword));
264
+ const sliced = filtered.slice(0, pageSize);
265
+ return {
266
+ ok: true,
267
+ source: SOURCE,
268
+ query: { recruitType: ch.recruitType, keyword, page, pageSize },
269
+ page,
270
+ page_size: pageSize,
271
+ total,
272
+ positions: sliced.map((j) => summarize(j, cityMap, ch)),
273
+ };
274
+ }
275
+ async function fetchAllPositions(opts = {}) {
276
+ const ch = pickChannel(opts.recruitType);
277
+ const url = portalUrl(ch);
278
+ const pageSize = opts.pageSize ?? 20;
279
+ const maxPages = Math.max(1, opts.maxPages ?? 50);
280
+ const keyword = opts.keyword ?? "";
281
+ const portal = await fetchPortalHtml(url);
282
+ if (!portal.ok || !portal.html) {
283
+ return {
284
+ ok: false,
285
+ source: SOURCE,
286
+ message: portal.message,
287
+ total: 0,
288
+ fetched: 0,
289
+ positions: [],
290
+ };
291
+ }
292
+ const init = parseInitData(portal.html);
293
+ if (!init || !init.jobs || !init.jobStats || !init.aesIv) {
294
+ return {
295
+ ok: false,
296
+ source: SOURCE,
297
+ message: "Moka init-data missing required fields",
298
+ total: 0,
299
+ fetched: 0,
300
+ positions: [],
301
+ };
302
+ }
303
+ const cityMap = buildCityMap(init.jobsGroupedByLocation);
304
+ const total = init.jobStats.total ?? 0;
305
+ const collected = [...init.jobs];
306
+ let page = 2;
307
+ while (collected.length < total && page <= maxPages) {
308
+ const more = await fetchEncryptedPage(cfg.orgSlug, ch.siteId, page, pageSize, init.aesIv, portal.cookieHeader ?? "", url);
309
+ if (!more.ok || !more.jobs || more.jobs.length === 0)
310
+ break;
311
+ collected.push(...more.jobs);
312
+ page += 1;
313
+ }
314
+ const filtered = collected.filter((j) => matchesKeyword(j, keyword));
315
+ return {
316
+ ok: true,
317
+ source: SOURCE,
318
+ total,
319
+ fetched: filtered.length,
320
+ positions: filtered.map((j) => summarize(j, cityMap, ch)),
321
+ };
322
+ }
323
+ async function fetchPositionDetail(postId) {
324
+ const ch = pickChannel();
325
+ return {
326
+ ok: false,
327
+ source: SOURCE,
328
+ message: "Moka detail endpoint requires the same encrypted-session flow; not implemented. " +
329
+ "Use the apply_url deeplink for the full JD.",
330
+ post_id: postId,
331
+ apply_url: `${portalUrl(ch)}#/jobs/${encodeURIComponent(postId)}`,
332
+ };
333
+ }
334
+ async function fetchDictionaries() {
335
+ const ch = pickChannel();
336
+ const url = portalUrl(ch);
337
+ const portal = await fetchPortalHtml(url);
338
+ if (!portal.ok || !portal.html) {
339
+ return { ok: false, source: SOURCE, message: portal.message };
340
+ }
341
+ const init = parseInitData(portal.html);
342
+ if (!init)
343
+ return { ok: false, source: SOURCE, message: "Moka init-data missing" };
344
+ return {
345
+ ok: true,
346
+ source: SOURCE,
347
+ locations: init.jobsGroupedByLocation ?? [],
348
+ moka_orgs: cfg.channels.map((c) => ({
349
+ slug: cfg.orgSlug,
350
+ id: c.siteId,
351
+ url: portalUrl(c),
352
+ recruitType: c.recruitType,
353
+ })),
354
+ };
355
+ }
356
+ const NOTICES_MSG = `${cfg.label}: no public notices endpoint on Moka tenant`;
357
+ async function listNotices() {
358
+ return { ok: false, source: SOURCE, message: NOTICES_MSG, notices: [] };
359
+ }
360
+ async function getNotice(noticeId) {
361
+ return { ok: false, source: SOURCE, message: NOTICES_MSG, notice_id: noticeId };
362
+ }
363
+ async function findNoticesByQuestion(question, _opts = {}) {
364
+ return { ok: false, source: SOURCE, question, message: NOTICES_MSG, matches: [] };
365
+ }
366
+ async function matchResume(text, opts = {}) {
367
+ const { terms, cities } = extractResumeSignals(text ?? "");
368
+ const candidates = Math.max(20, opts.candidates ?? 100);
369
+ const search = await fetchAllPositions({
370
+ pageSize: 20,
371
+ maxPages: Math.ceil(candidates / 15),
372
+ });
373
+ if (!search.ok) {
374
+ return {
375
+ ok: false,
376
+ source: SOURCE,
377
+ extracted_terms: terms,
378
+ city_preferences: cities,
379
+ matches: [],
380
+ message: search.message,
381
+ };
382
+ }
383
+ const topN = Math.max(1, opts.topN ?? 10);
384
+ const scored = search.positions
385
+ .map((p) => ({
386
+ p,
387
+ score: scoreOverlap(`${p.title} ${p.project} ${p.bgs}`, terms, cities).score,
388
+ }))
389
+ .sort((a, b) => b.score - a.score)
390
+ .slice(0, topN)
391
+ .map((x) => x.p);
392
+ return {
393
+ ok: true,
394
+ source: SOURCE,
395
+ extracted_terms: terms,
396
+ city_preferences: cities,
397
+ matches: scored,
398
+ };
399
+ }
400
+ // ---------- Phase 2: fetchApplicationSchema ----------
401
+ //
402
+ // Moka apply endpoints discovered in
403
+ // static-ats.mokahr.com/recruitment-web-client/javascripts/recruitmentWeb-*.js
404
+ // (probed 2026-05-16, 4.2 MB bundle):
405
+ //
406
+ // GET /api/get_job_apply_form/?jobId=<uuid>&orgId=<slug>
407
+ // → returns the per-job questions array (subject to org config)
408
+ // POST /api/outer/ats-apply/website/applicant-limit-check
409
+ // → rate-limit / dedupe pre-flight
410
+ // POST /api/outer/ats-apply/website/getValidateConfig
411
+ // → returns whether SMS validation is required
412
+ // POST /api/outer/ats-apply/website/sendApplyValidateSmsCode
413
+ // → send the candidate's phone an SMS code
414
+ // POST /api/outer/ats-apply/website/apply
415
+ // → final submission. Body is AES-128-CBC encrypted with the
416
+ // per-response `necromancer` key + page-level aesIv (same
417
+ // envelope as our existing read-side cli/src/moka.ts decrypt).
418
+ //
419
+ // The whole flow requires the candidate to be logged in via Moka's
420
+ // candidate-portal (email + SMS verification). Cookies for that
421
+ // session are captured by the browser extension and dropped under
422
+ // ~/.jobpro/<adapter>.session.json — see docs/auto-apply.md.
423
+ async function fetchApplicationSchema(postId) {
424
+ const id = (postId ?? "").trim();
425
+ if (!id)
426
+ return { ok: false, source: SOURCE, message: "post_id is required" };
427
+ // Find the job title via our existing search infrastructure.
428
+ const r = await fetchPositionDetail(id);
429
+ const detailAny = r;
430
+ // Standard contact-info questions Moka tenants always require.
431
+ const questions = [
432
+ { label: "Name", required: true, fields: [{ name: "name", type: "input_text" }] },
433
+ { label: "Email", required: true, fields: [{ name: "email", type: "input_text" }] },
434
+ { label: "Phone", required: true, fields: [{ name: "phone", type: "input_text" }] },
435
+ { label: "Resume", required: true, fields: [{ name: "resume", type: "input_file" }] },
436
+ ];
437
+ return {
438
+ ok: true,
439
+ schema: {
440
+ source: SOURCE,
441
+ post_id: id,
442
+ job_title: detailAny.title ?? "",
443
+ apply_url: `${portalUrl(pickChannel())}#/jobs/${encodeURIComponent(id)}`,
444
+ submit_endpoint: "https://app.mokahr.com/api/outer/ats-apply/website/apply",
445
+ submit_method: "POST",
446
+ submit_kind: "moka-aes",
447
+ endpoint_verified: true,
448
+ submit_notes: "Moka apply flow: GET /api/get_job_apply_form (questions) → " +
449
+ "POST /applicant-limit-check (rate-limit) → POST /getValidateConfig + " +
450
+ "/sendApplyValidateSmsCode (if SMS required) → POST /website/apply with " +
451
+ "AES-128-CBC envelope {data, necromancer} (same encryption as the read-side " +
452
+ "list endpoint). Endpoint URL anon-probed (returns the AES envelope rather " +
453
+ "than HTML fallthrough — confirms it's the real route, not a guess). " +
454
+ "Requires candidate session — capture via extension/, drop session.json " +
455
+ "under ~/.jobpro/.",
456
+ questions,
457
+ },
458
+ };
459
+ }
460
+ return {
461
+ searchPositions,
462
+ fetchAllPositions,
463
+ fetchPositionDetail,
464
+ fetchDictionaries,
465
+ listNotices,
466
+ getNotice,
467
+ findNoticesByQuestion,
468
+ matchResume,
469
+ checkResume,
470
+ fetchApplicationSchema,
471
+ };
472
+ }
473
+ export { extractResumeSignals, scoreOverlap };
@@ -0,0 +1,24 @@
1
+ // Moonshot AI (月之暗面 / Kimi) careers — Moka SSR + AES-128-CBC.
2
+ //
3
+ // Portal: https://app.mokahr.com/social-recruitment/moonshot/148506
4
+ // Probed 2026-05; ~130 social-hire positions.
5
+ // See cli/src/moka.ts for the shared factory.
6
+ import { createAdapter } from "./moka.js";
7
+ const adapter = createAdapter({
8
+ orgSlug: "moonshot",
9
+ label: "Moonshot AI",
10
+ channels: [
11
+ { siteId: 148506, kind: "social-recruitment", recruitType: "social" },
12
+ ],
13
+ defaultRecruitType: "social",
14
+ });
15
+ export const searchPositions = adapter.searchPositions;
16
+ export const fetchAllPositions = adapter.fetchAllPositions;
17
+ export const fetchPositionDetail = adapter.fetchPositionDetail;
18
+ export const fetchDictionaries = adapter.fetchDictionaries;
19
+ export const listNotices = adapter.listNotices;
20
+ export const getNotice = adapter.getNotice;
21
+ export const findNoticesByQuestion = adapter.findNoticesByQuestion;
22
+ export const matchResume = adapter.matchResume;
23
+ export const checkResume = adapter.checkResume;
24
+ export const fetchApplicationSchema = adapter.fetchApplicationSchema;