@mazjq/zhiro 0.1.2 → 0.2.1
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/README.md +136 -64
- package/README.zh-CN.md +97 -41
- package/agent-skills/zhiro/SKILL.md +8 -2
- package/dist/acp-bridge.js +6 -6
- package/dist/{chunk-YJNBYSVM.js → chunk-2XJ45GUM.js} +141 -84
- package/dist/{chunk-5KCN32HQ.js → chunk-4EPNDXRS.js} +25 -0
- package/dist/{chunk-YYNUPNBH.js → chunk-4UWMDKMH.js} +142 -11
- package/dist/{chunk-F6DLONN7.js → chunk-7IV2NG2D.js} +2 -2
- package/dist/{chunk-J2D7DYGW.js → chunk-A722HITH.js} +2 -2
- package/dist/{chunk-LUKCNS5M.js → chunk-GRZ4VMSO.js} +1 -1
- package/dist/{chunk-NNH4ZG6F.js → chunk-H3EFVYGD.js} +1 -1
- package/dist/{chunk-UMCKC7XP.js → chunk-VOUILWMI.js} +36 -7
- package/dist/{chunk-LH25QWMP.js → chunk-ZKD2BKN3.js} +9 -5
- package/dist/{chunk-ZL2RKXOF.js → chunk-ZUUYRVX6.js} +1 -1
- package/dist/{cli-WO7FHPTE.js → cli-LUWVZ5NG.js} +3 -3
- package/dist/{login-2WAH5KPP.js → login-W5QYFWTY.js} +4 -4
- package/dist/{main-KCG27VLD.js → main-FTWFKWZH.js} +701 -135
- package/dist/research-data.js +7 -7
- package/dist/{vault-7KJ34T7Q.js → vault-JUTREQJK.js} +3 -3
- package/dist/zhiro.js +5 -5
- package/package.json +2 -2
- package/ui/assets/index-B7GrU8uc.js +39 -0
- package/ui/assets/index-Dt8b-A97.css +2 -0
- package/ui/index.html +2 -2
- package/ui/assets/index-DUzC3BlF.js +0 -11
- package/ui/assets/index-y_0yp8U5.css +0 -2
|
@@ -8,25 +8,25 @@ import {
|
|
|
8
8
|
nodeArgs,
|
|
9
9
|
noteModelRefused,
|
|
10
10
|
upsertFact
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-VOUILWMI.js";
|
|
12
12
|
import {
|
|
13
13
|
callingAgent,
|
|
14
14
|
delegateRun,
|
|
15
15
|
isExternal
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-7IV2NG2D.js";
|
|
17
17
|
import {
|
|
18
18
|
DomainError,
|
|
19
19
|
NotFound
|
|
20
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-ZKD2BKN3.js";
|
|
21
21
|
import {
|
|
22
22
|
parseBaseUrl,
|
|
23
23
|
parseCsv,
|
|
24
24
|
readBase
|
|
25
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-4UWMDKMH.js";
|
|
26
26
|
import {
|
|
27
27
|
now,
|
|
28
28
|
tx
|
|
29
|
-
} from "./chunk-
|
|
29
|
+
} from "./chunk-4EPNDXRS.js";
|
|
30
30
|
import {
|
|
31
31
|
__name
|
|
32
32
|
} from "./chunk-2SSYR7VW.js";
|
|
@@ -1256,7 +1256,12 @@ function brief(kind, d) {
|
|
|
1256
1256
|
}
|
|
1257
1257
|
}
|
|
1258
1258
|
__name(brief, "brief");
|
|
1259
|
-
var
|
|
1259
|
+
var EVERY_DAYS = { listing: 180, basic: 90, sectors: 90, finance: 30, dividends: 30, holders: 30, forecast: 7, reports: 7, irm: 7, announcements: 1, news: 1, ipo: 3, sme: 365 };
|
|
1260
|
+
var everyDays = /* @__PURE__ */ __name((kind, data) => kind === "listing" && data?.none ? 30 : kind === "ipo" && !data?.szse ? 30 : EVERY_DAYS[kind] ?? 30, "everyDays");
|
|
1261
|
+
var toRow = /* @__PURE__ */ __name((r, now3 = Date.now()) => {
|
|
1262
|
+
const data = JSON.parse(r.data_json), age = (now3 - Date.parse(r.fetched_at)) / 864e5, every = everyDays(r.kind, data);
|
|
1263
|
+
return { kind: r.kind, data, source: r.source, fetchedAt: r.fetched_at, ageDays: Math.floor(age), everyDays: every, stale: age > every };
|
|
1264
|
+
}, "toRow");
|
|
1260
1265
|
function getIntelDetail(s, companyId, kind) {
|
|
1261
1266
|
const r = s.domain.db.prepare("select * from company_intel where company_id = ? and kind = ?").get(companyId, kind);
|
|
1262
1267
|
if (!r) throw new NotFound(`企业情况 ${companyId} ${kind}`);
|
|
@@ -1271,97 +1276,110 @@ function getIntel(s, companyId) {
|
|
|
1271
1276
|
return JSON.stringify(b) === JSON.stringify(row2.data) ? row2 : { ...row2, data: b, more: true };
|
|
1272
1277
|
});
|
|
1273
1278
|
const sme = smeMatch(s.domain.db, co.name);
|
|
1274
|
-
const
|
|
1275
|
-
|
|
1279
|
+
const listingRow = rows.find((r) => r.kind === "listing");
|
|
1280
|
+
const staleKinds = rows.length === 0 ? ["listing"] : rows.filter((r) => r.stale).map((r) => r.kind);
|
|
1281
|
+
return { companyId, companyName: co.name, listing: listingRow?.data ?? null, rows: rows.filter((r) => r.kind !== "listing"), sme, stale: staleKinds.length > 0, staleKinds, refreshing: running2.has(companyId) };
|
|
1276
1282
|
}
|
|
1277
1283
|
__name(getIntel, "getIntel");
|
|
1284
|
+
function readIntel(s, companyId) {
|
|
1285
|
+
const card = getIntel(s, companyId);
|
|
1286
|
+
const kept = card.listing !== null;
|
|
1287
|
+
if (kept && card.stale && !card.refreshing) void refreshIntel(s, companyId).catch(() => {
|
|
1288
|
+
});
|
|
1289
|
+
return { ...card, refreshing: card.refreshing || kept && card.stale };
|
|
1290
|
+
}
|
|
1291
|
+
__name(readIntel, "readIntel");
|
|
1278
1292
|
var put = /* @__PURE__ */ __name((s, companyId, kind, data, source) => s.domain.db.prepare("insert or replace into company_intel(company_id, kind, data_json, source, fetched_at) values (?,?,?,?,?)").run(companyId, kind, JSON.stringify(data), source, (/* @__PURE__ */ new Date()).toISOString()), "put");
|
|
1279
|
-
|
|
1293
|
+
var running2 = /* @__PURE__ */ new Map();
|
|
1294
|
+
var failed = /* @__PURE__ */ new Map();
|
|
1295
|
+
var mayTry = /* @__PURE__ */ __name((key, now3) => (failed.get(key)?.until ?? 0) <= now3, "mayTry");
|
|
1296
|
+
var noteFailure = /* @__PURE__ */ __name((key, now3) => {
|
|
1297
|
+
const n = (failed.get(key)?.n ?? 0) + 1;
|
|
1298
|
+
failed.set(key, { n, until: now3 + Math.min(24, 2 ** (n - 1)) * 36e5 });
|
|
1299
|
+
}, "noteFailure");
|
|
1300
|
+
function refreshIntel(s, companyId, opts = {}) {
|
|
1301
|
+
const on = running2.get(companyId);
|
|
1302
|
+
if (on) return on;
|
|
1303
|
+
const job = refreshKinds(s, companyId, !!opts.force).finally(() => running2.delete(companyId));
|
|
1304
|
+
running2.set(companyId, job);
|
|
1305
|
+
return job;
|
|
1306
|
+
}
|
|
1307
|
+
__name(refreshIntel, "refreshIntel");
|
|
1308
|
+
async function refreshKinds(s, companyId, force) {
|
|
1280
1309
|
const co = s.domain.db.prepare("select name from company where id = ?").get(companyId);
|
|
1281
1310
|
if (!co) throw new NotFound(`company ${companyId}`);
|
|
1311
|
+
const now3 = Date.now();
|
|
1312
|
+
const have = new Map(s.domain.db.prepare("select * from company_intel where company_id = ?").all(companyId).map((r) => [r.kind, toRow(r, now3)]));
|
|
1282
1313
|
const errors = {};
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
errors
|
|
1292
|
-
|
|
1293
|
-
}
|
|
1294
|
-
via = "eastmoney:codetable";
|
|
1295
|
-
}
|
|
1296
|
-
if (!listing) {
|
|
1314
|
+
const due = /* @__PURE__ */ __name((kind) => force || !have.has(kind) || have.get(kind).stale, "due");
|
|
1315
|
+
const attempt = /* @__PURE__ */ __name(async (kind, fn, source) => {
|
|
1316
|
+
const key = `${companyId}:${kind}`;
|
|
1317
|
+
if (!force && !mayTry(key, now3)) return;
|
|
1318
|
+
try {
|
|
1319
|
+
put(s, companyId, kind, await fn(), source);
|
|
1320
|
+
failed.delete(key);
|
|
1321
|
+
} catch (e) {
|
|
1322
|
+
errors[kind] = String(e?.message ?? e).slice(0, 200);
|
|
1323
|
+
noteFailure(key, now3);
|
|
1324
|
+
}
|
|
1297
1325
|
await sleep(300);
|
|
1298
|
-
|
|
1326
|
+
}, "attempt");
|
|
1327
|
+
let listing = have.get("listing")?.data?.none ? null : have.get("listing")?.data ?? null;
|
|
1328
|
+
let changed = false;
|
|
1329
|
+
if (due("listing")) {
|
|
1330
|
+
const was = have.get("listing")?.data;
|
|
1331
|
+
let found = await resolveListing(co.name).catch((e) => {
|
|
1299
1332
|
errors.listing = String(e?.message ?? e);
|
|
1300
1333
|
return null;
|
|
1301
1334
|
});
|
|
1302
|
-
via = "
|
|
1303
|
-
|
|
1304
|
-
put(s, companyId, "listing", listing ?? { none: true }, listing ? via : "cninfo:topSearch+eastmoney:codetable+eastmoney:search");
|
|
1305
|
-
if (listing && listing.market === "美股") {
|
|
1306
|
-
for (const [kind, fn, source] of [["basic", () => fetchUsBasic(listing.code), "eastmoney:USF10"], ["finance", () => fetchUsFinance(listing.code), "eastmoney:USF10"]]) {
|
|
1307
|
-
try {
|
|
1308
|
-
put(s, companyId, kind, await fn(), source);
|
|
1309
|
-
} catch (e) {
|
|
1310
|
-
errors[kind] = String(e?.message ?? e).slice(0, 200);
|
|
1311
|
-
}
|
|
1335
|
+
let via = "cninfo:topSearch";
|
|
1336
|
+
if (!found) {
|
|
1312
1337
|
await sleep(300);
|
|
1338
|
+
found = await resolveNeeq(co.name, normName(co.name)).catch((e) => {
|
|
1339
|
+
errors.listing = String(e?.message ?? e);
|
|
1340
|
+
return null;
|
|
1341
|
+
});
|
|
1342
|
+
via = "eastmoney:codetable";
|
|
1313
1343
|
}
|
|
1314
|
-
|
|
1315
|
-
if (listing && listing.market === "新三板") {
|
|
1316
|
-
for (const [kind, fn, source] of [["basic", () => fetchNeeqBasic(listing.code), "neeq:detailCompany+年报"], ["finance", () => fetchNeeqFinance(listing.code), "eastmoney:xinsanban"]]) {
|
|
1317
|
-
try {
|
|
1318
|
-
put(s, companyId, kind, await fn(), source);
|
|
1319
|
-
} catch (e) {
|
|
1320
|
-
errors[kind] = String(e?.message ?? e).slice(0, 200);
|
|
1321
|
-
}
|
|
1344
|
+
if (!found) {
|
|
1322
1345
|
await sleep(300);
|
|
1346
|
+
found = await resolveUsListing(co.name).catch((e) => {
|
|
1347
|
+
errors.listing = String(e?.message ?? e);
|
|
1348
|
+
return null;
|
|
1349
|
+
});
|
|
1350
|
+
via = "eastmoney:search";
|
|
1323
1351
|
}
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
} catch (e) {
|
|
1329
|
-
errors.finance = String(e?.message ?? e).slice(0, 200);
|
|
1352
|
+
if (found || !errors.listing) put(s, companyId, "listing", found ?? { none: true }, found ? via : "cninfo:topSearch+eastmoney:codetable+eastmoney:search");
|
|
1353
|
+
if (found || !errors.listing) {
|
|
1354
|
+
changed = !was || (was.none ? !!found : !found || was.code !== found.code || was.market !== found.market);
|
|
1355
|
+
listing = found;
|
|
1330
1356
|
}
|
|
1331
|
-
await sleep(300);
|
|
1332
1357
|
}
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
["
|
|
1342
|
-
["
|
|
1343
|
-
["
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
await sleep(300);
|
|
1352
|
-
}
|
|
1353
|
-
}
|
|
1354
|
-
for (const [kind, fn, source] of [["news", () => fetchNews(co.name), "wallstreetcn:search"], ["ipo", () => fetchIpo(co.name), "szse:projectrends"]]) {
|
|
1355
|
-
try {
|
|
1356
|
-
put(s, companyId, kind, await fn(), source);
|
|
1357
|
-
} catch (e) {
|
|
1358
|
-
errors[kind] = String(e?.message ?? e).slice(0, 200);
|
|
1359
|
-
}
|
|
1360
|
-
await sleep(300);
|
|
1358
|
+
const go = /* @__PURE__ */ __name((kind) => changed || due(kind), "go");
|
|
1359
|
+
const steps = [];
|
|
1360
|
+
if (listing?.market === "美股") steps.push(["basic", () => fetchUsBasic(listing.code), "eastmoney:USF10"], ["finance", () => fetchUsFinance(listing.code), "eastmoney:USF10"]);
|
|
1361
|
+
if (listing?.market === "新三板") steps.push(["basic", () => fetchNeeqBasic(listing.code), "neeq:detailCompany+年报"], ["finance", () => fetchNeeqFinance(listing.code), "eastmoney:xinsanban"]);
|
|
1362
|
+
if (listing?.market === "港股") steps.push(["finance", () => fetchHkFinance(listing.code), "eastmoney:HKF10"]);
|
|
1363
|
+
if (listing?.market === "A股") {
|
|
1364
|
+
const l = listing;
|
|
1365
|
+
steps.push(
|
|
1366
|
+
["basic", () => fetchBasic(l.code), "eastmoney:F10"],
|
|
1367
|
+
["finance", () => fetchFinance(l.code), "eastmoney:ZYZB+sina:lrb"],
|
|
1368
|
+
["dividends", () => fetchDividends(l.code), "eastmoney:datacenter"],
|
|
1369
|
+
["announcements", () => fetchAnnouncements(l.code, l.orgId), "cninfo:hisAnnouncement"],
|
|
1370
|
+
["holders", () => fetchHolders(l.code), "eastmoney:F10-shareholders"],
|
|
1371
|
+
["irm", () => fetchIrm(l.code), "cninfo:irm"],
|
|
1372
|
+
["sectors", () => fetchSectors(l.code), "eastmoney:slist"],
|
|
1373
|
+
["reports", () => fetchReports(l.code), "eastmoney:reportapi"],
|
|
1374
|
+
["forecast", () => fetchForecast(l.code), "eastmoney:datacenter"]
|
|
1375
|
+
);
|
|
1361
1376
|
}
|
|
1377
|
+
steps.push(["news", () => fetchNews(co.name), "wallstreetcn:search"]);
|
|
1378
|
+
if (listing?.market !== "A股") steps.push(["ipo", () => fetchIpo(co.name), "szse:projectrends"]);
|
|
1379
|
+
for (const [kind, fn, source] of steps) if (go(kind)) await attempt(kind, fn, source);
|
|
1362
1380
|
return { ...getIntel(s, companyId), errors };
|
|
1363
1381
|
}
|
|
1364
|
-
__name(
|
|
1382
|
+
__name(refreshKinds, "refreshKinds");
|
|
1365
1383
|
|
|
1366
1384
|
// apps/daemon/src/service/industry.ts
|
|
1367
1385
|
var UA3 = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/128.0 Safari/537.36";
|
|
@@ -2300,6 +2318,7 @@ var tools = {
|
|
|
2300
2318
|
degree: Type.Optional(Type.String({ description: "学历要求含任一词" })),
|
|
2301
2319
|
city: Type.Optional(Type.String()),
|
|
2302
2320
|
watchedOnly: Type.Optional(Type.Boolean()),
|
|
2321
|
+
internship: Type.Optional(Type.Boolean({ description: "只列实习岗:岗位名含「实习」/ intern,或来源把这批标成实习" })),
|
|
2303
2322
|
includeExpired: Type.Optional(Type.Boolean({ description: "默认只列截止日未过的岗位;true 连已截止的也列" })),
|
|
2304
2323
|
limit: Type.Optional(Type.Number({ default: 100 })),
|
|
2305
2324
|
offset: Type.Optional(Type.Number({ default: 0, description: "跳过前几条(翻页);简版结果的 hasMore 说还有没有" })),
|
|
@@ -2596,7 +2615,7 @@ var tools = {
|
|
|
2596
2615
|
"research.company": defineTool({
|
|
2597
2616
|
name: "research.company",
|
|
2598
2617
|
description: "派发一次公司调研:由系统侧 Claude Code worker(有联网搜索)在隔离 run 目录中检索官网/招聘系统/就业网/公众号/牛客,产出带证据等级的中文报告,落 vault/research/。异步,返回 runId;用 research.get 查看。",
|
|
2599
|
-
input: Type.Object({ companyName: Type.String(), question: Type.Optional(Type.String({ description: "具体想知道什么;默认 2027
|
|
2618
|
+
input: Type.Object({ companyName: Type.String(), question: Type.Optional(Type.String({ description: "具体想知道什么;默认 2027 届校招全貌。「企业情况」= 经营情况;「就业评估」= 就业评估报告(D-29:职路先放同行百分位和历年财务,再由调研员写利润为什么变、钱去了哪、激励是否覆盖技术人员、上市进展、和用户的匹配,每条结论带证据、反面说法和限制,不打分)" })) }),
|
|
2600
2619
|
output: Type.Object({ runId: Id, workflowId: Id }),
|
|
2601
2620
|
effects: EGRESS,
|
|
2602
2621
|
execution: "job",
|
|
@@ -2671,6 +2690,31 @@ var tools = {
|
|
|
2671
2690
|
execution: "sync",
|
|
2672
2691
|
errors: ["MISSING_FIELD", "BAD_RUN"]
|
|
2673
2692
|
}),
|
|
2693
|
+
"employer.compare": defineTool({
|
|
2694
|
+
name: "employer.compare",
|
|
2695
|
+
description: "比较几家上市公司作为雇主的位置(D-29,不打总分,不花模型额度)。每家在自己行业的全部 A 股公司里(A 股按申万一级,新三板按证监会行业,港股美股按 peerIndustry 或一起比的 A 股公司最多的那个申万一级;港美股不披露职工现金,待遇信号缺;yardstick 说明怎么量的,要转述给用户),按四项各算百分位:成长(营收三年复合增速)、研发(研发费用占营收)、待遇信号(每名员工对应的职工现金支出,粗指标)、盈利(净利率);再在三种偏好(成长研发优先 / 均衡 / 稳定回报优先,可另给 weights)下排这几家的名次。under 里每种偏好给:place(按该权重的名次)、usually(权重上下浮动时名次的通常范围)、top3Share(进前三的权重占比,是偏好情景的比例,不是现实概率);缺指标的不估值,给 byKnown / best / worst。outdoneBy 列出在四项上都不差且至少一项更好的公司(不叫梯队)。movedTogether 只说同期一起发生了什么,不是原因,原因要读年报或公告核实。labels 带着规则。未上市公司在 notFound 里,不和上市公司混排。第一次查一个行业约 20–30 秒,之后 7 天内用同一份数据(data 里有取数时间)。",
|
|
2696
|
+
input: Type.Object({
|
|
2697
|
+
companies: Type.Array(Type.String({ description: "公司名、简称或 6 位股票代码" }), { minItems: 1, maxItems: 15 }),
|
|
2698
|
+
weights: Type.Optional(Type.Object({ growth: Type.Optional(Type.Number()), rd: Type.Optional(Type.Number()), pay: Type.Optional(Type.Number()), profit: Type.Optional(Type.Number()) }, { description: "用户自己的偏好权重(相对大小即可,会归一)" })),
|
|
2699
|
+
spread: Type.Optional(Type.Number({ description: "权重上下浮动多少(0.02–1,默认 0.1)" })),
|
|
2700
|
+
year: Type.Optional(Type.Number({ description: "用哪一年的年报,默认最近一个已全部披露的年份" })),
|
|
2701
|
+
peerIndustry: Type.Optional(Type.String({ description: "港股、美股公司拿哪个申万一级行业的 A 股公司当同行(如 机械设备、电子、汽车);不给就用一起比的 A 股公司最多的那个行业" }))
|
|
2702
|
+
}),
|
|
2703
|
+
output: Type.Object({
|
|
2704
|
+
year: Type.Number(),
|
|
2705
|
+
peerGroup: Type.String(),
|
|
2706
|
+
data: Type.Unknown(),
|
|
2707
|
+
measures: Type.Array(Type.Unknown()),
|
|
2708
|
+
companies: Type.Array(Type.Unknown()),
|
|
2709
|
+
scenarios: Type.Unknown(),
|
|
2710
|
+
spread: Type.Number(),
|
|
2711
|
+
notFound: Type.Array(Type.Unknown()),
|
|
2712
|
+
method: Type.String()
|
|
2713
|
+
}),
|
|
2714
|
+
effects: READ,
|
|
2715
|
+
execution: "sync",
|
|
2716
|
+
errors: ["MISSING_FIELD"]
|
|
2717
|
+
}),
|
|
2674
2718
|
"labor.us": defineTool({
|
|
2675
2719
|
name: "labor.us",
|
|
2676
2720
|
description: "美国某些职业的就业人数和年薪(均值、中位数):美国劳工统计局 BLS 的 OEWS 官方年度统计,全国、各行业合计(免费)。按 SOC 职业代码查,一次最多 8 个,如 17-2141 机械工程师、17-2071 电气工程师、17-2072 电子工程师、17-2199 其他工程师、15-1252 软件开发、15-2051 数据科学家、15-1221 计算机与信息研究科学家。返回里的 title 是 BLS 对这个代码的叫法,和你要查的职业不一样就是代码给错了。没有密钥,每天大约只能问 25 次,把要查的职业放进一次调用。",
|
|
@@ -3116,9 +3160,9 @@ var tools = {
|
|
|
3116
3160
|
}),
|
|
3117
3161
|
"company.intel": defineTool({
|
|
3118
3162
|
name: "company.intel",
|
|
3119
|
-
description: "企业情况卡(ADR-029):是否上市(A股/港股/新三板/美股)、基本盘(行业/省份/员工数/成立与上市日期)、近三年营收净利与研发费用、分红、近一年公告里的关键词(专精特新/政府补助/IPO/扩产/对外投资/减持等)、专精特新名录匹配;每类各带 fetchedAt
|
|
3163
|
+
description: "企业情况卡(ADR-029):是否上市(A股/港股/新三板/美股)、基本盘(行业/省份/员工数/成立与上市日期)、近三年营收净利与研发费用、分红、近一年公告里的关键词(专精特新/政府补助/IPO/扩产/对外投资/减持等)、专精特新名录匹配;每类各带 fetchedAt、ageDays 和 everyDays(这一类多久更新一次:新闻公告 1 天、问答研报业绩预告 7 天、在审 IPO 3 天、财报分红股东 30 天、行业员工 90 天、上市信息 180 天)。读的时候立即返回已有的;过期的类(staleKinds)会在后台自动更新(refreshing=true),稍后再读就是新的,一般不用调 company.intelRefresh。非上市公司只有名录匹配,其余要 research.company 联网调研。列表是摘要:more=true 的类别(新闻、互动易问答全文、研报、股东、分红、公告命中、行业概念)用 company.intelDetail 取全部。",
|
|
3120
3164
|
input: Type.Object({ companyId: Id }),
|
|
3121
|
-
output: Type.Object({ companyId: Id, companyName: Type.String(), listing: Type.Unknown(), rows: Type.Array(Type.Object({ kind: Type.String(), data: Type.Unknown(), source: Type.String(), fetchedAt: Type.String(), ageDays: Type.Number(), more: Type.Optional(Type.Boolean()) })), sme: Type.Unknown(), stale: Type.Boolean() }),
|
|
3165
|
+
output: Type.Object({ companyId: Id, companyName: Type.String(), listing: Type.Unknown(), rows: Type.Array(Type.Object({ kind: Type.String(), data: Type.Unknown(), source: Type.String(), fetchedAt: Type.String(), ageDays: Type.Number(), everyDays: Type.Number(), stale: Type.Boolean(), more: Type.Optional(Type.Boolean()) })), sme: Type.Unknown(), stale: Type.Boolean(), staleKinds: Type.Array(Type.String()), refreshing: Type.Boolean() }),
|
|
3122
3166
|
effects: READ,
|
|
3123
3167
|
execution: "sync",
|
|
3124
3168
|
errors: ["NOT_FOUND"]
|
|
@@ -3134,8 +3178,8 @@ var tools = {
|
|
|
3134
3178
|
}),
|
|
3135
3179
|
"company.intelRefresh": defineTool({
|
|
3136
3180
|
name: "company.intelRefresh",
|
|
3137
|
-
description: "
|
|
3138
|
-
input: Type.Object({ companyId: Id }),
|
|
3181
|
+
description: "立即重新抓取一家公司的企业情况并等它完成(巨潮、东方财富 F10、新浪财报、巨潮公告;新三板走股转年报 PDF + 东财三板 F10;匿名只读,约 2–30 秒)。默认全部类别重抓;force=false 只抓过期的类。抓不到的类别保留旧值并在 errors 里说明。不花模型额度。",
|
|
3182
|
+
input: Type.Object({ companyId: Id, force: Type.Optional(Type.Boolean({ description: "默认 true:全部重抓;false:只抓过期的类" })) }),
|
|
3139
3183
|
output: Type.Object({ companyId: Id, companyName: Type.String(), listing: Type.Unknown(), rows: Type.Array(Type.Unknown()), sme: Type.Unknown(), stale: Type.Boolean(), errors: Type.Record(Type.String(), Type.String()) }),
|
|
3140
3184
|
effects: NETWORK_READ,
|
|
3141
3185
|
execution: "sync",
|
|
@@ -3613,6 +3657,15 @@ var tools = {
|
|
|
3613
3657
|
execution: "sync",
|
|
3614
3658
|
errors: ["NOT_AVAILABLE"]
|
|
3615
3659
|
}),
|
|
3660
|
+
"agent.model": defineTool({
|
|
3661
|
+
name: "agent.model",
|
|
3662
|
+
description: "Zhiro 里用 Claude Code / Codex 时用哪个模型:model 为空 = 跟随它自己的设置;否则用这里选的(只影响 Zhiro 里的用法,不改它自己的设置)。可选的模型见 config.get 的 chat.models.choices。只能由本人。",
|
|
3663
|
+
input: Type.Object({ backend: Type.Union([Type.Literal("claude"), Type.Literal("codex")]), model: Type.String() }),
|
|
3664
|
+
output: Type.Object({ backend: Type.String(), model: Type.String(), using: Type.String() }),
|
|
3665
|
+
effects: WRITE,
|
|
3666
|
+
execution: "sync",
|
|
3667
|
+
errors: []
|
|
3668
|
+
}),
|
|
3616
3669
|
"agent.harnessConfirm": defineTool({
|
|
3617
3670
|
name: "agent.harnessConfirm",
|
|
3618
3671
|
description: "在你自己的 agent(Claude Code / Codex / WorkBuddy 经 MCP)里用 Zhiro 时,确认和撤销在哪里完成:harness = 在 agent 里(agent 复述、你同意后它来确认);window = 只在 Zhiro 窗口。只能由本人。",
|
|
@@ -3718,12 +3771,16 @@ export {
|
|
|
3718
3771
|
loadSources,
|
|
3719
3772
|
startSmeResearch,
|
|
3720
3773
|
smeRun,
|
|
3774
|
+
resolveNeeq,
|
|
3721
3775
|
INTEL_QUESTION,
|
|
3722
3776
|
PLACE,
|
|
3777
|
+
normName,
|
|
3778
|
+
smeMatch,
|
|
3723
3779
|
importSme,
|
|
3724
3780
|
getIntelDetail,
|
|
3725
|
-
|
|
3781
|
+
readIntel,
|
|
3726
3782
|
refreshIntel,
|
|
3783
|
+
companyFinancials,
|
|
3727
3784
|
DATA_TOOLS,
|
|
3728
3785
|
dataFile,
|
|
3729
3786
|
keepData,
|
|
@@ -399,6 +399,31 @@ var MIGRATIONS = [
|
|
|
399
399
|
// v36: ADR-050 — every upgrade leaves a line here: when, from / to which schema, by which program, where the original went
|
|
400
400
|
`
|
|
401
401
|
create table upgrade_log (at text not null, from_v integer not null, to_v integer not null, app_version text not null, backup text not null default '');
|
|
402
|
+
`,
|
|
403
|
+
// v37: D-27 — BOSS 直聘 visits through the person's agent: one row per one-time permit (issued → used), and the stops
|
|
404
|
+
`
|
|
405
|
+
create table boss_visit (
|
|
406
|
+
id text primary key, session_id text not null, requested_at text not null, not_before text not null, expires_at text not null,
|
|
407
|
+
consumed_at text, target text not null, kind text not null, outcome text not null default 'issued', detail text not null default ''
|
|
408
|
+
);
|
|
409
|
+
create index boss_visit_at on boss_visit(requested_at);
|
|
410
|
+
create index boss_visit_session on boss_visit(session_id);
|
|
411
|
+
`,
|
|
412
|
+
// v38: D-28 — BOSS posts seen on a search page, kept as candidates until their job page is read (then position_id)
|
|
413
|
+
`
|
|
414
|
+
create table boss_candidate (
|
|
415
|
+
job_id text primary key, url text not null, title text not null, salary text not null default '', city text not null default '',
|
|
416
|
+
district text not null default '', experience text not null default '', degree text not null default '', days text not null default '',
|
|
417
|
+
months text not null default '', brand text not null default '', scale text not null default '', stage text not null default '',
|
|
418
|
+
industry text not null default '', skills text not null default '[]', labels text not null default '[]', query text not null default '',
|
|
419
|
+
city_code text not null default '', first_seen text not null, last_seen text not null, position_id text, detail_at text
|
|
420
|
+
);
|
|
421
|
+
create index boss_candidate_seen on boss_candidate(last_seen);
|
|
422
|
+
`,
|
|
423
|
+
// v39: D-29 — the A-share list and one industry's statements for a year, kept with the time they were fetched, so a
|
|
424
|
+
// comparison of employers can say which data it used and be run again on the same data
|
|
425
|
+
`
|
|
426
|
+
create table peer_snapshot (key text primary key, fetched_at text not null, data_json text not null);
|
|
402
427
|
`
|
|
403
428
|
];
|
|
404
429
|
var COUNTS_MAY_CHANGE = {};
|
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
json,
|
|
3
3
|
now,
|
|
4
4
|
tx
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-4EPNDXRS.js";
|
|
6
6
|
import {
|
|
7
7
|
updateConfig
|
|
8
8
|
} from "./chunk-4JJEOOW2.js";
|
|
@@ -832,12 +832,12 @@ var CatalogBatch = class {
|
|
|
832
832
|
};
|
|
833
833
|
|
|
834
834
|
// apps/daemon/src/data/sheet.ts
|
|
835
|
-
function parseCsv(
|
|
835
|
+
function parseCsv(text3) {
|
|
836
836
|
const rows = [];
|
|
837
837
|
let row = [];
|
|
838
838
|
let field = "";
|
|
839
839
|
let inQ = false;
|
|
840
|
-
const s =
|
|
840
|
+
const s = text3.replace(/^/, "");
|
|
841
841
|
for (let i = 0; i < s.length; i++) {
|
|
842
842
|
const c = s[i];
|
|
843
843
|
if (inQ) {
|
|
@@ -866,8 +866,8 @@ function parseCsv(text2) {
|
|
|
866
866
|
return rows.filter((r) => r.some((f) => f.trim() !== ""));
|
|
867
867
|
}
|
|
868
868
|
__name(parseCsv, "parseCsv");
|
|
869
|
-
function parseDeadline(
|
|
870
|
-
const t = (
|
|
869
|
+
function parseDeadline(text3) {
|
|
870
|
+
const t = (text3 ?? "").trim();
|
|
871
871
|
if (!t) return { closesAt: null, kind: "unknown" };
|
|
872
872
|
if (/招满|长期|滚动|持续|尽快|随时/.test(t)) return { closesAt: null, kind: /滚动|持续|尽快|随时/.test(t) ? "rolling" : "until_filled" };
|
|
873
873
|
const m = t.match(/(\d{4})[-/.年](\d{1,2})[-/.月](\d{1,2})/);
|
|
@@ -1005,8 +1005,8 @@ function listingsFromTable(table, cfg, publisher) {
|
|
|
1005
1005
|
return tableToListings(table, columns, cfg, void 0, publisher);
|
|
1006
1006
|
}
|
|
1007
1007
|
__name(listingsFromTable, "listingsFromTable");
|
|
1008
|
-
function csvToTable(
|
|
1009
|
-
const [head = [], ...rows] = parseCsv(
|
|
1008
|
+
function csvToTable(text3) {
|
|
1009
|
+
const [head = [], ...rows] = parseCsv(text3);
|
|
1010
1010
|
const headers = head.map((h) => h.trim());
|
|
1011
1011
|
const at = new Map(headers.map((h, i) => [h, i]));
|
|
1012
1012
|
return { headers, rows: rows.map((r) => ({ id: "", get: /* @__PURE__ */ __name((h) => (r[at.get(h) ?? -1] ?? "").trim(), "get") })) };
|
|
@@ -1244,7 +1244,7 @@ function zhilianUrl(url) {
|
|
|
1244
1244
|
}
|
|
1245
1245
|
__name(zhilianUrl, "zhilianUrl");
|
|
1246
1246
|
var ID = /(?:xiaozhaoId|companyXzId|companyOutId|rootCompanyId|orgNumbers?)["']?[:=]\[?["']?(\d{5,7})\b|globalData\.companyId,[\w$]+="?(\d{5,7})\b/g;
|
|
1247
|
-
var idsIn = /* @__PURE__ */ __name((
|
|
1247
|
+
var idsIn = /* @__PURE__ */ __name((text3) => [...new Set([...text3.matchAll(ID)].map((m) => m[1] || m[2]))], "idsIn");
|
|
1248
1248
|
async function count(ctx, id) {
|
|
1249
1249
|
const r = await ctx.fetchJson(API2, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ orgNumbers: [id], jobSource: 2, pageIndex: 1, pageSize: 1 }) }).catch(() => null);
|
|
1250
1250
|
return r?.data?.pageInfo?.totalNum ?? 0;
|
|
@@ -1818,9 +1818,9 @@ function literalToJson(s) {
|
|
|
1818
1818
|
return o;
|
|
1819
1819
|
}
|
|
1820
1820
|
__name(literalToJson, "literalToJson");
|
|
1821
|
-
function siteLinks(
|
|
1821
|
+
function siteLinks(text3, base) {
|
|
1822
1822
|
const root = new URL(".", base).href, out = [];
|
|
1823
|
-
for (const m of
|
|
1823
|
+
for (const m of text3.matchAll(/["']((?:\.\/)?[\w./-]+\.(?:html?|js))(?:\?[^"']*)?["']/g)) {
|
|
1824
1824
|
const p = new URL(m[1], base).href;
|
|
1825
1825
|
if (p.startsWith(root) && !out.includes(p) && !/jquery|swiper|jweixin|\.min\.js/.test(p)) out.push(p);
|
|
1826
1826
|
}
|
|
@@ -2076,9 +2076,140 @@ var csv = {
|
|
|
2076
2076
|
}
|
|
2077
2077
|
};
|
|
2078
2078
|
|
|
2079
|
+
// apps/daemon/src/data/readers/gaoxiaojob.ts
|
|
2080
|
+
var M = "https://m.gaoxiaojob.com";
|
|
2081
|
+
var W = "https://www.gaoxiaojob.com";
|
|
2082
|
+
var UA3 = { "user-agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 Mobile/15E148" };
|
|
2083
|
+
var DAY = 864e5;
|
|
2084
|
+
var LONG = /长期有效|长期招聘|常年|招满为止|招满即止|额满为止/;
|
|
2085
|
+
var columnOf = /* @__PURE__ */ __name((url) => /^https?:\/\/(?:www\.|m\.)?gaoxiaojob\.com\/column\/(\d+)\.html/.exec(url.trim())?.[1] ?? null, "columnOf");
|
|
2086
|
+
function dayOf(mmdd, today = /* @__PURE__ */ new Date()) {
|
|
2087
|
+
const m = /^(\d{1,2})-(\d{1,2})$/.exec(mmdd.trim());
|
|
2088
|
+
if (!m) return null;
|
|
2089
|
+
const y = today.getUTCFullYear(), mm = m[1].padStart(2, "0"), dd = m[2].padStart(2, "0");
|
|
2090
|
+
const d = `${y}-${mm}-${dd}`;
|
|
2091
|
+
return Date.parse(d) > today.getTime() + DAY ? `${y - 1}-${mm}-${dd}` : d;
|
|
2092
|
+
}
|
|
2093
|
+
__name(dayOf, "dayOf");
|
|
2094
|
+
var text2 = /* @__PURE__ */ __name((html) => html.replace(/<script[\s\S]*?<\/script>|<style[\s\S]*?<\/style>/g, " ").replace(/<br\s*\/?>|<\/p>|<\/div>|<\/li>/g, "\n").replace(/<[^>]+>/g, " ").replace(/ /g, " ").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/[ \t\r]+/g, " ").replace(/\n\s*/g, "\n"), "text");
|
|
2095
|
+
function parseNotice(id, title, html) {
|
|
2096
|
+
const t = text2(html);
|
|
2097
|
+
const flat = t.replace(/\s+/g, " ");
|
|
2098
|
+
const field = /* @__PURE__ */ __name((k) => new RegExp(`${k}[::]\\s*(.*?)\\s+(?:截止日期|学历要求|所属省份|工作地点|报名方式|栏目分类|需求学科|发布时间)`).exec(flat)?.[1]?.trim() ?? "", "field");
|
|
2099
|
+
const posted = /发布时间[::]\s*(\d{4}-\d{2}-\d{2})/.exec(flat)?.[1] ?? null;
|
|
2100
|
+
const deadlineText = field("截止日期");
|
|
2101
|
+
const full = /(\d{4})[-/.年](\d{1,2})[-/.月](\d{1,2})/.exec(deadlineText);
|
|
2102
|
+
const closesAt = full ? `${full[1]}-${full[2].padStart(2, "0")}-${full[3].padStart(2, "0")}` : null;
|
|
2103
|
+
const source = /来源链接[::]\s*(https?:\/\/\S+)/.exec(flat)?.[1] ?? "";
|
|
2104
|
+
const a = t.indexOf("公告详情"), b = t.indexOf("来源于网络", a);
|
|
2105
|
+
const body = (a >= 0 ? t.slice(a + 4, b > a ? b : a + 8e3) : "").trim().slice(0, 8e3);
|
|
2106
|
+
return { id, title, posted, deadlineText, closesAt, place: field("工作地点"), source, body };
|
|
2107
|
+
}
|
|
2108
|
+
__name(parseNotice, "parseNotice");
|
|
2109
|
+
function stillOpen(n, listedOn, today = /* @__PURE__ */ new Date()) {
|
|
2110
|
+
if (n.closesAt) return Date.parse(`${n.closesAt}T23:59:59+08:00`) >= today.getTime();
|
|
2111
|
+
const on = n.posted ?? listedOn;
|
|
2112
|
+
if (!on) return false;
|
|
2113
|
+
const age = (today.getTime() - Date.parse(on)) / DAY;
|
|
2114
|
+
return age <= (LONG.test(n.deadlineText) ? 180 : 90);
|
|
2115
|
+
}
|
|
2116
|
+
__name(stillOpen, "stillOpen");
|
|
2117
|
+
function noticeListings(n, jobs, cfg) {
|
|
2118
|
+
const company = (jobs.find((j) => j.companyName)?.companyName ?? "").replace(/#+$/, "").trim() || unitFrom(n.title);
|
|
2119
|
+
const base = {
|
|
2120
|
+
company,
|
|
2121
|
+
noticeUrl: `${W}/announcement/detail/${n.id}.html`,
|
|
2122
|
+
datePosted: n.posted ?? void 0,
|
|
2123
|
+
validThrough: n.closesAt ?? void 0,
|
|
2124
|
+
deadlineText: n.deadlineText || void 0,
|
|
2125
|
+
cohort: cfg.cohort,
|
|
2126
|
+
batch: n.title.slice(0, 80),
|
|
2127
|
+
// one announcement, one campaign (campaigns group by publisher|company|cohort|batch)
|
|
2128
|
+
publisher: "gaoxiaojob",
|
|
2129
|
+
grade: "聚合",
|
|
2130
|
+
isOriginal: false,
|
|
2131
|
+
confidence: 0.7
|
|
2132
|
+
// a reprint: below the 0.85–0.9 the official readers give
|
|
2133
|
+
};
|
|
2134
|
+
const intern = /* @__PURE__ */ __name((s) => /实习/.test(s) ? "实习" : void 0, "intern");
|
|
2135
|
+
const jd = [n.body, n.source ? `原公告:${n.source}` : ""].filter(Boolean).join("\n\n");
|
|
2136
|
+
if (!jobs.length) return [{ ...base, externalId: `ann:${n.id}`, url: base.noticeUrl, title: n.title, locations: n.place ? [n.place] : [], description: jd, kind: intern(n.title) }];
|
|
2137
|
+
return jobs.map((j) => ({
|
|
2138
|
+
...base,
|
|
2139
|
+
externalId: `job:${j.jobId}`,
|
|
2140
|
+
url: j.url ? `${W}${j.url}` : base.noticeUrl,
|
|
2141
|
+
title: j.jobName,
|
|
2142
|
+
locations: j.city ? [j.city] : n.place ? [n.place] : [],
|
|
2143
|
+
description: jd,
|
|
2144
|
+
degreeReq: j.education || void 0,
|
|
2145
|
+
majorReq: j.major || void 0,
|
|
2146
|
+
kind: intern(`${j.jobName} ${n.title}`)
|
|
2147
|
+
}));
|
|
2148
|
+
}
|
|
2149
|
+
__name(noticeListings, "noticeListings");
|
|
2150
|
+
var unitFrom = /* @__PURE__ */ __name((title) => /^(.+?)(?:\d{4}|诚聘|招聘|公开|引进|招收)/.exec(title)?.[1]?.trim() || title.slice(0, 30), "unitFrom");
|
|
2151
|
+
async function jobsOf(ctx, id) {
|
|
2152
|
+
const out = [];
|
|
2153
|
+
for (let page2 = 1; page2 <= 20; page2++) {
|
|
2154
|
+
const d = await ctx.fetchJson(`${M}/announcement/get-job-list?announcementId=${id}&page=${page2}`, { headers: UA3 });
|
|
2155
|
+
const list = d?.data?.list ?? [];
|
|
2156
|
+
out.push(...list);
|
|
2157
|
+
if (!list.length || out.length >= Number(d?.data?.count ?? 0)) return out;
|
|
2158
|
+
await ctx.sleep(pause());
|
|
2159
|
+
}
|
|
2160
|
+
return out;
|
|
2161
|
+
}
|
|
2162
|
+
__name(jobsOf, "jobsOf");
|
|
2163
|
+
var pause = /* @__PURE__ */ __name(() => 600 + Math.floor(Math.random() * 800), "pause");
|
|
2164
|
+
var gaoxiaojob = {
|
|
2165
|
+
id: "gaoxiaojob",
|
|
2166
|
+
name: "高校人才网",
|
|
2167
|
+
input: "www.gaoxiaojob.com/column/<栏目编号>.html(如武汉 48、武汉科研院所 509、武汉企业 511)",
|
|
2168
|
+
mode: "pull",
|
|
2169
|
+
describe: "高校人才网的一个栏目(研究院、实验室、高校团队、事业单位、企业):读手机版公开列表和每条公告的岗位清单,只收仍有效的;转载来源,记为聚合,零 token",
|
|
2170
|
+
detect: /* @__PURE__ */ __name((url) => !!columnOf(url), "detect"),
|
|
2171
|
+
siteKey: /* @__PURE__ */ __name((url) => `column/${columnOf(url)}`, "siteKey"),
|
|
2172
|
+
async fetch(cfg, ctx) {
|
|
2173
|
+
const col = columnOf(cfg.url ?? "");
|
|
2174
|
+
if (!col) throw new Error("SCHEMA_CHANGED: 不是高校人才网的栏目链接");
|
|
2175
|
+
const maxPages = cfg.maxPages ?? 60;
|
|
2176
|
+
const today = /* @__PURE__ */ new Date();
|
|
2177
|
+
const out = [];
|
|
2178
|
+
let oldPages = 0, page2 = 1;
|
|
2179
|
+
for (; page2 <= maxPages; page2++) {
|
|
2180
|
+
const d = await ctx.fetchJson(`${M}/home/get-announcement-list?columnId=${col}&isEstablishment=0&announcementHeat=0&page=${page2}`, { headers: UA3 });
|
|
2181
|
+
if (d?.code !== 200) throw new Error(`SCHEMA_CHANGED: 列表接口返回 ${d?.code ?? "?"} ${d?.msg ?? ""}`);
|
|
2182
|
+
const items = d?.data ?? [];
|
|
2183
|
+
if (!items.length) break;
|
|
2184
|
+
let fresh = 0;
|
|
2185
|
+
for (const it of items) {
|
|
2186
|
+
const listedOn = dayOf(it.refreshTime ?? "", today);
|
|
2187
|
+
if (listedOn && (today.getTime() - Date.parse(listedOn)) / DAY > 180) continue;
|
|
2188
|
+
fresh++;
|
|
2189
|
+
await ctx.sleep(pause());
|
|
2190
|
+
const n = parseNotice(
|
|
2191
|
+
String(it.id),
|
|
2192
|
+
it.title,
|
|
2193
|
+
await ctx.fetchText(`${W}/announcement/detail/${it.id}.html`)
|
|
2194
|
+
/* the desktop page: its 基本信息 block is what parseNotice reads */
|
|
2195
|
+
);
|
|
2196
|
+
if (!stillOpen(n, listedOn, today)) continue;
|
|
2197
|
+
await ctx.sleep(pause());
|
|
2198
|
+
out.push(...noticeListings(n, await jobsOf(ctx, n.id), cfg));
|
|
2199
|
+
}
|
|
2200
|
+
ctx.log(`gaoxiaojob ${col}: page ${page2} ${items.length} 条,在窗口内 ${fresh}(累计 ${out.length})`);
|
|
2201
|
+
oldPages = fresh ? 0 : oldPages + 1;
|
|
2202
|
+
if (oldPages >= 2) break;
|
|
2203
|
+
await ctx.sleep(pause());
|
|
2204
|
+
}
|
|
2205
|
+
if (page2 > maxPages) throw new Error(`TIMEOUT: 读到第 ${maxPages} 页还没读完这个栏目,这次不算(读一半会把另一半标成下架)`);
|
|
2206
|
+
return out;
|
|
2207
|
+
}
|
|
2208
|
+
};
|
|
2209
|
+
|
|
2079
2210
|
// apps/daemon/src/data/readers/index.ts
|
|
2080
2211
|
var capturePush = { id: "capture", name: "任意页面抓取", mode: "push", describe: "任意招聘页/简章:书签一键捕获,或给 URL 由 daemon 抓取(公众号/就业网/官网),模型抽取岗位" };
|
|
2081
|
-
var sources = Object.fromEntries([moka, beisen, feishuBase, feishuJobs, zhilian, iguopin, chnenergy, chng, dayee, job51, kuaishou, xiaohongshu, csv, capturePush].map((s) => [s.id, s]));
|
|
2212
|
+
var sources = Object.fromEntries([moka, beisen, feishuBase, feishuJobs, zhilian, iguopin, chnenergy, chng, dayee, job51, kuaishou, xiaohongshu, gaoxiaojob, csv, capturePush].map((s) => [s.id, s]));
|
|
2082
2213
|
function sourceFor(url) {
|
|
2083
2214
|
return Object.values(sources).find((s) => s.detect?.(url));
|
|
2084
2215
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
VERSION
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-H3EFVYGD.js";
|
|
4
4
|
import {
|
|
5
5
|
COUNTS_MAY_CHANGE,
|
|
6
6
|
MIGRATIONS,
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
migrate,
|
|
10
10
|
openDb,
|
|
11
11
|
schemaOf
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-4EPNDXRS.js";
|
|
13
13
|
import {
|
|
14
14
|
configFile,
|
|
15
15
|
dbPath,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// apps/daemon/src/version.ts
|
|
2
|
-
var VERSION = true ? "0.1
|
|
2
|
+
var VERSION = true ? "0.2.1" : JSON.parse(readFileSync(new URL("../../../package.json", import.meta.url), "utf8")).version;
|
|
3
3
|
|
|
4
4
|
export {
|
|
5
5
|
VERSION
|