hunter-mate 0.1.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.
Files changed (57) hide show
  1. package/dist/cache/db.d.ts +7 -0
  2. package/dist/cache/db.js +105 -0
  3. package/dist/commands/addons.d.ts +4 -0
  4. package/dist/commands/addons.js +378 -0
  5. package/dist/commands/cache.d.ts +8 -0
  6. package/dist/commands/cache.js +58 -0
  7. package/dist/commands/config.d.ts +12 -0
  8. package/dist/commands/config.js +27 -0
  9. package/dist/commands/doctor.d.ts +21 -0
  10. package/dist/commands/doctor.js +332 -0
  11. package/dist/commands/extension.d.ts +5 -0
  12. package/dist/commands/extension.js +438 -0
  13. package/dist/commands/extras.d.ts +10 -0
  14. package/dist/commands/extras.js +111 -0
  15. package/dist/commands/init.d.ts +10 -0
  16. package/dist/commands/init.js +87 -0
  17. package/dist/commands/install.d.ts +9 -0
  18. package/dist/commands/install.js +126 -0
  19. package/dist/commands/job.d.ts +9 -0
  20. package/dist/commands/job.js +114 -0
  21. package/dist/commands/login.d.ts +3 -0
  22. package/dist/commands/login.js +51 -0
  23. package/dist/commands/logout.d.ts +3 -0
  24. package/dist/commands/logout.js +16 -0
  25. package/dist/commands/native-check.d.ts +22 -0
  26. package/dist/commands/native-check.js +93 -0
  27. package/dist/commands/ops.d.ts +6 -0
  28. package/dist/commands/ops.js +330 -0
  29. package/dist/commands/record.d.ts +12 -0
  30. package/dist/commands/record.js +96 -0
  31. package/dist/commands/report.d.ts +7 -0
  32. package/dist/commands/report.js +54 -0
  33. package/dist/commands/search.d.ts +14 -0
  34. package/dist/commands/search.js +46 -0
  35. package/dist/commands/server.d.ts +23 -0
  36. package/dist/commands/server.js +232 -0
  37. package/dist/commands/status.d.ts +20 -0
  38. package/dist/commands/status.js +92 -0
  39. package/dist/commands/update.d.ts +8 -0
  40. package/dist/commands/update.js +161 -0
  41. package/dist/config.d.ts +12 -0
  42. package/dist/config.js +65 -0
  43. package/dist/i18n/index.d.ts +7 -0
  44. package/dist/i18n/index.js +24 -0
  45. package/dist/i18n/locales/en-US.json +35 -0
  46. package/dist/i18n/locales/zh-CN.json +35 -0
  47. package/dist/index.d.ts +2 -0
  48. package/dist/index.js +133 -0
  49. package/dist/keychain.d.ts +3 -0
  50. package/dist/keychain.js +117 -0
  51. package/dist/sentry.d.ts +3 -0
  52. package/dist/sentry.js +62 -0
  53. package/dist/updater.d.ts +23 -0
  54. package/dist/updater.js +161 -0
  55. package/dist/version.d.ts +16 -0
  56. package/dist/version.js +249 -0
  57. package/package.json +50 -0
@@ -0,0 +1,249 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.readInstalledVersions = readInstalledVersions;
40
+ exports.readRuntimeStateFile = readRuntimeStateFile;
41
+ exports.fetchLiveVersions = fetchLiveVersions;
42
+ exports.decideConsistencyActions = decideConsistencyActions;
43
+ exports.ensureRuntimeConsistency = ensureRuntimeConsistency;
44
+ exports.shouldCheckNow = shouldCheckNow;
45
+ const fs = __importStar(require("fs"));
46
+ const path = __importStar(require("path"));
47
+ const axios_1 = __importDefault(require("axios"));
48
+ const chalk_1 = __importDefault(require("chalk"));
49
+ const config_1 = require("./config");
50
+ const server_1 = require("./commands/server");
51
+ const extras_1 = require("./commands/extras");
52
+ const HOME = () => process.env.HUNTER_MATE_HOME || process.env.HOME || process.env.USERPROFILE || '.';
53
+ const ROOT = () => path.join(HOME(), '.hunter-mate');
54
+ const PLUGINS_DIR = () => path.join(ROOT(), 'plugins');
55
+ const EXTENSION_MANIFEST = () => path.join(ROOT(), 'extension', 'manifest.json');
56
+ const RUNTIME_FILE = () => path.join(ROOT(), 'runtime.json');
57
+ const LAST_CHECK_FILE = () => path.join(ROOT(), '.last-consistency-check');
58
+ const CHECK_THROTTLE_MS = 60_000;
59
+ // ---------------------------------------------------------------------------
60
+ // 磁盘版本读取(SSOT)
61
+ // ---------------------------------------------------------------------------
62
+ function readJson(file) {
63
+ try {
64
+ return JSON.parse(fs.readFileSync(file, 'utf-8'));
65
+ }
66
+ catch {
67
+ return null;
68
+ }
69
+ }
70
+ function readInstalledVersions() {
71
+ // CLI 自身: src/version.ts 与 dist/version.js 的上一级都是包根
72
+ const cliPkg = readJson(path.join(__dirname, '..', 'package.json'));
73
+ const cli = cliPkg?.version ?? '0.0.0';
74
+ // Server: 定位 packages/server 包根(与 start 命令同一套候选逻辑)
75
+ let server = null;
76
+ const serverRoot = (0, server_1.findServerRoot)();
77
+ if (serverRoot) {
78
+ const pkg = readJson(path.join(serverRoot, 'package.json'));
79
+ server = pkg?.version ?? null;
80
+ }
81
+ // 浏览器扩展(init/install 写入 ~/.hunter-mate/extension)
82
+ const manifest = readJson(EXTENSION_MANIFEST());
83
+ const extension = manifest?.version;
84
+ // 插件
85
+ const plugins = {};
86
+ const dir = PLUGINS_DIR();
87
+ if (fs.existsSync(dir)) {
88
+ for (const name of fs.readdirSync(dir)) {
89
+ const pkg = readJson(path.join(dir, name, 'package.json'));
90
+ if (pkg?.version)
91
+ plugins[name] = pkg.version;
92
+ }
93
+ }
94
+ return { cli, server, extension, plugins };
95
+ }
96
+ function readRuntimeStateFile() {
97
+ return readJson(RUNTIME_FILE());
98
+ }
99
+ // ---------------------------------------------------------------------------
100
+ // live 运行中版本(来自 server /system/versions)
101
+ // ---------------------------------------------------------------------------
102
+ async function fetchLiveVersions(timeoutMs = 3000) {
103
+ const config = (0, config_1.loadConfig)();
104
+ try {
105
+ const res = await axios_1.default.get(`${config.serverUrl}/system/versions`, {
106
+ timeout: timeoutMs,
107
+ proxy: false,
108
+ });
109
+ return res.data;
110
+ }
111
+ catch {
112
+ return null;
113
+ }
114
+ }
115
+ // ---------------------------------------------------------------------------
116
+ // 一致性决策(纯函数,便于测试)
117
+ // ---------------------------------------------------------------------------
118
+ function decideConsistencyActions(installed, live) {
119
+ const actions = {
120
+ serverRestart: false,
121
+ pluginsReload: [],
122
+ extensionReload: false,
123
+ };
124
+ if (!live)
125
+ return actions;
126
+ // server: 磁盘版本 vs 运行版本(磁盘未知时不强制)
127
+ if (installed.server !== null &&
128
+ live.server.version !== installed.server) {
129
+ actions.serverRestart = true;
130
+ actions.serverReason = `running=${live.server.version} disk=${installed.server}`;
131
+ }
132
+ else if (live.server.needsRestart) {
133
+ actions.serverRestart = true;
134
+ actions.serverReason = 'server self-check marked needsRestart';
135
+ }
136
+ // 插件: 磁盘版本 vs 已加载版本
137
+ const names = new Set([
138
+ ...Object.keys(installed.plugins),
139
+ ...Object.keys(live.plugins),
140
+ ]);
141
+ for (const name of names) {
142
+ if (installed.plugins[name] !== live.plugins[name]) {
143
+ actions.pluginsReload.push(name);
144
+ }
145
+ }
146
+ // 浏览器扩展: 磁盘 manifest 版本 vs WS handshake 上报版本
147
+ if (installed.extension) {
148
+ actions.extensionReload = live.extensions.some((e) => e.version !== installed.extension);
149
+ }
150
+ return actions;
151
+ }
152
+ async function ensureRuntimeConsistency(opts = {}) {
153
+ const performed = [];
154
+ const noOp = (reason) => ({
155
+ checked: false,
156
+ reason,
157
+ actions: { serverRestart: false, pluginsReload: [], extensionReload: false },
158
+ performed,
159
+ });
160
+ // 节流: 60s 内不重复检测(force 跳过)
161
+ if (!opts.force && !shouldCheckNow())
162
+ return noOp('throttled');
163
+ // server 是否在跑的判定:
164
+ // 1) CLI 管理的 pid 文件存活 → 走完整检测
165
+ // 2) pid 缺失(用户手动 node dist/main.js 起的 / 旧版 CLI 起的)→
166
+ // 本地地址做快速 HTTP 探测(ECONNREFUSED 即时返回,不拖慢命令);远程地址维持 pid 闸门
167
+ let live = null;
168
+ const config = (0, config_1.loadConfig)();
169
+ const isLocal = /\/\/(localhost|127\.0\.0\.1|\[::1\])/i.test(config.serverUrl);
170
+ if ((0, server_1.tryReadPid)()) {
171
+ live = await fetchLiveVersions();
172
+ }
173
+ else if (isLocal) {
174
+ live = await fetchLiveVersions(1200);
175
+ }
176
+ else {
177
+ return noOp('server not running (no pid file)');
178
+ }
179
+ if (!live) {
180
+ markChecked();
181
+ return noOp('server not running');
182
+ }
183
+ const installed = readInstalledVersions();
184
+ const actions = decideConsistencyActions(installed, live);
185
+ // 1) 插件热重载
186
+ if (actions.pluginsReload.length) {
187
+ try {
188
+ await axios_1.default.post(`${config.serverUrl}/system/reload-plugins`, { reason: `cli-consistency: ${actions.pluginsReload.join(',')}` }, { timeout: 5000, proxy: false });
189
+ performed.push(`plugins reloaded: ${actions.pluginsReload
190
+ .map((n) => `${n} ${live.plugins[n] ?? '-'}→${installed.plugins[n] ?? '?'}`)
191
+ .join(', ')}`);
192
+ }
193
+ catch (err) {
194
+ performed.push(`plugins reload FAILED: ${err.message}`);
195
+ }
196
+ }
197
+ // 2) server 重启(stop + start)
198
+ if (actions.serverRestart) {
199
+ try {
200
+ (0, server_1.runStop)();
201
+ const r = await (0, server_1.runStart)({});
202
+ performed.push(r.started
203
+ ? `server restarted (${actions.serverReason}) — new pid=${r.pid}`
204
+ : `server restart FAILED: ${r.msg}`);
205
+ }
206
+ catch (err) {
207
+ performed.push(`server restart FAILED: ${err.message}`);
208
+ }
209
+ }
210
+ // 3) 浏览器扩展 reload 广播
211
+ if (actions.extensionReload) {
212
+ try {
213
+ await (0, extras_1.runReload)(3000);
214
+ performed.push(`extension reload broadcast (disk=${installed.extension}, running=${[
215
+ ...new Set(live.extensions.map((e) => e.version)),
216
+ ].join('/')})`);
217
+ }
218
+ catch {
219
+ performed.push('extension reload broadcast skipped (no ws clients)');
220
+ }
221
+ }
222
+ markChecked();
223
+ if (opts.verbose && performed.length) {
224
+ for (const line of performed)
225
+ console.log(chalk_1.default.gray(` [auto-update] ${line}`));
226
+ }
227
+ return { checked: true, actions, performed };
228
+ }
229
+ // ---------------------------------------------------------------------------
230
+ // 节流
231
+ // ---------------------------------------------------------------------------
232
+ function shouldCheckNow() {
233
+ try {
234
+ const stat = fs.statSync(LAST_CHECK_FILE());
235
+ return Date.now() - stat.mtimeMs > CHECK_THROTTLE_MS;
236
+ }
237
+ catch {
238
+ return true;
239
+ }
240
+ }
241
+ function markChecked() {
242
+ try {
243
+ fs.mkdirSync(ROOT(), { recursive: true });
244
+ fs.writeFileSync(LAST_CHECK_FILE(), String(Date.now()));
245
+ }
246
+ catch {
247
+ /* 尽力 */
248
+ }
249
+ }
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "hunter-mate",
3
+ "version": "0.1.0",
4
+ "description": "HiredChina 猎头 Agent 数据助手 CLI:人才库/客户/职位数据访问,浏览器录制解析简历,server/扩展一致性自愈",
5
+ "license": "UNLICENSED",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/hiredchina-com/hunter-mate.git",
9
+ "directory": "packages/cli"
10
+ },
11
+ "engines": {
12
+ "node": ">=20"
13
+ },
14
+ "bin": {
15
+ "hunter-mate": "dist/index.js"
16
+ },
17
+ "files": [
18
+ "dist"
19
+ ],
20
+ "dependencies": {
21
+ "@sentry/node": "^10.70.0",
22
+ "@types/ws": "^8.18.1",
23
+ "axios": "^1.6.0",
24
+ "better-sqlite3": "^13.0.3",
25
+ "chalk": "^4.1.2",
26
+ "commander": "^12.0.0",
27
+ "dotenv": "^17.4.2",
28
+ "keytar": "^7.9.0",
29
+ "open": "^8.4.0",
30
+ "uuid": "^9.0.0",
31
+ "ws": "^8.21.3",
32
+ "@hunter-mate/shared": "0.1.0"
33
+ },
34
+ "devDependencies": {
35
+ "@types/better-sqlite3": "^7.6.0",
36
+ "@types/node": "^20.0.0",
37
+ "@types/uuid": "^9.0.0",
38
+ "@vitest/coverage-v8": "^1.6.1",
39
+ "ts-node": "^10.9.0",
40
+ "typescript": "^5.4.0",
41
+ "vitest": "^1.3.0"
42
+ },
43
+ "scripts": {
44
+ "build": "tsc",
45
+ "dev": "ts-node src/index.ts",
46
+ "lint": "eslint .",
47
+ "test": "vitest run",
48
+ "typecheck": "tsc --noEmit"
49
+ }
50
+ }