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.
- package/dist/cache/db.d.ts +7 -0
- package/dist/cache/db.js +105 -0
- package/dist/commands/addons.d.ts +4 -0
- package/dist/commands/addons.js +378 -0
- package/dist/commands/cache.d.ts +8 -0
- package/dist/commands/cache.js +58 -0
- package/dist/commands/config.d.ts +12 -0
- package/dist/commands/config.js +27 -0
- package/dist/commands/doctor.d.ts +21 -0
- package/dist/commands/doctor.js +332 -0
- package/dist/commands/extension.d.ts +5 -0
- package/dist/commands/extension.js +438 -0
- package/dist/commands/extras.d.ts +10 -0
- package/dist/commands/extras.js +111 -0
- package/dist/commands/init.d.ts +10 -0
- package/dist/commands/init.js +87 -0
- package/dist/commands/install.d.ts +9 -0
- package/dist/commands/install.js +126 -0
- package/dist/commands/job.d.ts +9 -0
- package/dist/commands/job.js +114 -0
- package/dist/commands/login.d.ts +3 -0
- package/dist/commands/login.js +51 -0
- package/dist/commands/logout.d.ts +3 -0
- package/dist/commands/logout.js +16 -0
- package/dist/commands/native-check.d.ts +22 -0
- package/dist/commands/native-check.js +93 -0
- package/dist/commands/ops.d.ts +6 -0
- package/dist/commands/ops.js +330 -0
- package/dist/commands/record.d.ts +12 -0
- package/dist/commands/record.js +96 -0
- package/dist/commands/report.d.ts +7 -0
- package/dist/commands/report.js +54 -0
- package/dist/commands/search.d.ts +14 -0
- package/dist/commands/search.js +46 -0
- package/dist/commands/server.d.ts +23 -0
- package/dist/commands/server.js +232 -0
- package/dist/commands/status.d.ts +20 -0
- package/dist/commands/status.js +92 -0
- package/dist/commands/update.d.ts +8 -0
- package/dist/commands/update.js +161 -0
- package/dist/config.d.ts +12 -0
- package/dist/config.js +65 -0
- package/dist/i18n/index.d.ts +7 -0
- package/dist/i18n/index.js +24 -0
- package/dist/i18n/locales/en-US.json +35 -0
- package/dist/i18n/locales/zh-CN.json +35 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +133 -0
- package/dist/keychain.d.ts +3 -0
- package/dist/keychain.js +117 -0
- package/dist/sentry.d.ts +3 -0
- package/dist/sentry.js +62 -0
- package/dist/updater.d.ts +23 -0
- package/dist/updater.js +161 -0
- package/dist/version.d.ts +16 -0
- package/dist/version.js +249 -0
- package/package.json +50 -0
|
@@ -0,0 +1,232 @@
|
|
|
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
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.stopCommand = exports.startCommand = void 0;
|
|
37
|
+
exports.tryReadPid = tryReadPid;
|
|
38
|
+
exports.findServerRoot = findServerRoot;
|
|
39
|
+
exports.runStart = runStart;
|
|
40
|
+
exports.runStop = runStop;
|
|
41
|
+
const commander_1 = require("commander");
|
|
42
|
+
const fs = __importStar(require("fs"));
|
|
43
|
+
const path = __importStar(require("path"));
|
|
44
|
+
const child_process_1 = require("child_process");
|
|
45
|
+
const config_1 = require("../config");
|
|
46
|
+
const PID_FILE = () => path.join(process.env.HUNTER_MATE_HOME || process.env.HOME || process.env.USERPROFILE || '.', '.hunter-mate', 'server.pid');
|
|
47
|
+
const LOG_FILE = () => PID_FILE().replace(/server\.pid$/, 'server.log');
|
|
48
|
+
function tryReadPid() {
|
|
49
|
+
try {
|
|
50
|
+
const raw = fs.readFileSync(PID_FILE(), 'utf-8').trim();
|
|
51
|
+
const pid = Number(raw);
|
|
52
|
+
if (!pid || !Number.isInteger(pid))
|
|
53
|
+
return null;
|
|
54
|
+
// 探测进程是否存活
|
|
55
|
+
process.kill(pid, 0);
|
|
56
|
+
return pid;
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/** 定位 packages/server 包根(dist 优先,ts-node 开发模式兜底);找不到返回 null */
|
|
63
|
+
function findServerRoot() {
|
|
64
|
+
const candidates = [
|
|
65
|
+
// 1) 已构建: CLI 包定位到并列 server 包
|
|
66
|
+
path.resolve(__dirname, '../../../server/dist/main.js'),
|
|
67
|
+
// 2) ts-node 开发模式
|
|
68
|
+
path.resolve(__dirname, '../../../server/src/main.ts'),
|
|
69
|
+
];
|
|
70
|
+
for (const c of candidates) {
|
|
71
|
+
if (fs.existsSync(c))
|
|
72
|
+
return path.dirname(path.dirname(c));
|
|
73
|
+
}
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
/** 对应 ChromePilot start: 启动本地 NestJS Server(提供 REST + WebSocket + SPA) */
|
|
77
|
+
async function runStart(opts) {
|
|
78
|
+
const config = (0, config_1.loadConfig)();
|
|
79
|
+
const defaultPort = new URL(config.serverUrl).port
|
|
80
|
+
? Number(new URL(config.serverUrl).port)
|
|
81
|
+
: 34567;
|
|
82
|
+
const port = opts.port ?? defaultPort;
|
|
83
|
+
const host = opts.host ?? '127.0.0.1';
|
|
84
|
+
const url = `http://${host}:${port}`;
|
|
85
|
+
const existing = tryReadPid();
|
|
86
|
+
if (existing) {
|
|
87
|
+
// 已在运行: 先做版本一致性检测(磁盘版本 vs 运行版本,server 不一致则自动重启)
|
|
88
|
+
// 动态 import 避免与 version.ts 的循环依赖(version.ts 静态引入本文件的 runStart/runStop)
|
|
89
|
+
const { ensureRuntimeConsistency } = await Promise.resolve().then(() => __importStar(require('../version')));
|
|
90
|
+
const report = await ensureRuntimeConsistency({ force: true });
|
|
91
|
+
if (report.actions.serverRestart && report.performed.length) {
|
|
92
|
+
return { started: true, pid: null, url, msg: report.performed.join('; ') };
|
|
93
|
+
}
|
|
94
|
+
return { started: false, pid: existing, url, msg: `server already running (pid=${existing})` };
|
|
95
|
+
}
|
|
96
|
+
// server 入口: monorepo 下 packages/server/dist/main.js 或 src/main.ts(开发模式 ts-node)
|
|
97
|
+
const serverRoot = findServerRoot();
|
|
98
|
+
let entry = null;
|
|
99
|
+
if (serverRoot) {
|
|
100
|
+
const built = path.join(serverRoot, 'dist', 'main.js');
|
|
101
|
+
const dev = path.join(serverRoot, 'src', 'main.ts');
|
|
102
|
+
if (fs.existsSync(built))
|
|
103
|
+
entry = built;
|
|
104
|
+
else if (fs.existsSync(dev))
|
|
105
|
+
entry = dev;
|
|
106
|
+
}
|
|
107
|
+
if (!entry) {
|
|
108
|
+
return {
|
|
109
|
+
started: false,
|
|
110
|
+
pid: null,
|
|
111
|
+
url,
|
|
112
|
+
msg: `未找到 hunter-mate server 入口 (packages/server/dist|src)。` +
|
|
113
|
+
`请先在 hunter-mate monorepo 执行 pnpm --filter @hunter-mate/server build,或单独启动 NestJS server 并把 serverUrl 指向它(hunter-mate config serverUrl ${url})。`,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
const env = {
|
|
117
|
+
...process.env,
|
|
118
|
+
PORT: String(port),
|
|
119
|
+
HOST: host,
|
|
120
|
+
};
|
|
121
|
+
const nodeArgs = [];
|
|
122
|
+
if (entry.endsWith('.ts')) {
|
|
123
|
+
// ts-node 模式
|
|
124
|
+
nodeArgs.push('--require', 'ts-node/register');
|
|
125
|
+
}
|
|
126
|
+
let child;
|
|
127
|
+
if (opts.foreground) {
|
|
128
|
+
child = (0, child_process_1.spawn)(process.execPath, [...nodeArgs, entry], {
|
|
129
|
+
stdio: 'inherit',
|
|
130
|
+
env,
|
|
131
|
+
});
|
|
132
|
+
return { started: true, pid: child.pid ?? null, url, msg: `server running in foreground (pid=${child.pid}) on ${url}` };
|
|
133
|
+
}
|
|
134
|
+
const logDir = path.dirname(LOG_FILE());
|
|
135
|
+
if (!fs.existsSync(logDir))
|
|
136
|
+
fs.mkdirSync(logDir, { recursive: true });
|
|
137
|
+
const logStream = fs.createWriteStream(LOG_FILE(), { flags: 'a' });
|
|
138
|
+
child = (0, child_process_1.spawn)(process.execPath, [...nodeArgs, entry], {
|
|
139
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
140
|
+
env,
|
|
141
|
+
detached: true,
|
|
142
|
+
});
|
|
143
|
+
child.stdout?.pipe(logStream);
|
|
144
|
+
child.stderr?.pipe(logStream);
|
|
145
|
+
child.unref();
|
|
146
|
+
if (child.pid)
|
|
147
|
+
fs.writeFileSync(PID_FILE(), String(child.pid));
|
|
148
|
+
// 秒死检测:server 若因原生模块 ABI 不匹配/依赖缺失等问题起来即崩,
|
|
149
|
+
// 写完 pid 即返回会表现假成功("started" 但实际已死)。
|
|
150
|
+
// 轮询最长 4.2s:进程退出 = 读 server.log 尾部输出真实报错;health 可达 = 存活实锤提前放行。
|
|
151
|
+
// (NestJS 冷启动可能较慢,进程仍活但 health 未达不算失败,交由上层轮询)
|
|
152
|
+
const deadline = Date.now() + 4200;
|
|
153
|
+
let died = false;
|
|
154
|
+
while (Date.now() < deadline) {
|
|
155
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
156
|
+
try {
|
|
157
|
+
process.kill(child.pid, 0);
|
|
158
|
+
}
|
|
159
|
+
catch {
|
|
160
|
+
died = true;
|
|
161
|
+
break;
|
|
162
|
+
}
|
|
163
|
+
try {
|
|
164
|
+
const res = await fetch(`${url}/health`, { signal: AbortSignal.timeout(800) });
|
|
165
|
+
if (res.ok)
|
|
166
|
+
break;
|
|
167
|
+
}
|
|
168
|
+
catch { /* server 尚未监听,继续轮询 */ }
|
|
169
|
+
}
|
|
170
|
+
if (died) {
|
|
171
|
+
let tail = '';
|
|
172
|
+
try {
|
|
173
|
+
const log = fs.readFileSync(LOG_FILE(), 'utf-8');
|
|
174
|
+
tail = log.split('\n').slice(-30).join('\n').trim();
|
|
175
|
+
}
|
|
176
|
+
catch { /* ignore */ }
|
|
177
|
+
try {
|
|
178
|
+
fs.unlinkSync(PID_FILE());
|
|
179
|
+
}
|
|
180
|
+
catch { /* ignore */ }
|
|
181
|
+
return {
|
|
182
|
+
started: false,
|
|
183
|
+
pid: null,
|
|
184
|
+
url,
|
|
185
|
+
msg: `✗ server 启动后立即退出(pid=${child.pid}),日志尾部:\n${tail || '(server.log 为空)'}\n` +
|
|
186
|
+
`排障: hunter-mate doctor(含原生模块 ABI 自检)或 TROUBLESHOOTING.md#server-启动即崩abi`,
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
// 持久化: 如果 port 变了就写回 config(避免下次 serverUrl 不对)
|
|
190
|
+
try {
|
|
191
|
+
const u = new URL(config.serverUrl);
|
|
192
|
+
if (Number(u.port) !== port) {
|
|
193
|
+
u.port = String(port);
|
|
194
|
+
u.hostname = host;
|
|
195
|
+
(0, config_1.saveConfig)({ serverUrl: u.origin });
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
catch { /* ignore */ }
|
|
199
|
+
return { started: true, pid: child.pid ?? null, url, msg: `server started (pid=${child.pid}) on ${url} — logs: ${LOG_FILE()}` };
|
|
200
|
+
}
|
|
201
|
+
function runStop() {
|
|
202
|
+
const pid = tryReadPid();
|
|
203
|
+
if (!pid)
|
|
204
|
+
return { stopped: false, pid: null, msg: 'no server.pid found or process dead' };
|
|
205
|
+
try {
|
|
206
|
+
process.kill(pid, 'SIGTERM');
|
|
207
|
+
fs.unlinkSync(PID_FILE());
|
|
208
|
+
return { stopped: true, pid, msg: `server stopped (pid=${pid})` };
|
|
209
|
+
}
|
|
210
|
+
catch (err) {
|
|
211
|
+
return { stopped: false, pid, msg: `failed to kill ${pid}: ${err.message}` };
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
exports.startCommand = new commander_1.Command('start')
|
|
215
|
+
.description('启动本地 NestJS server(REST + WebSocket + SPA),对应 chromepilot start')
|
|
216
|
+
.option('-p, --port <port>', 'server 端口,默认从 config.serverUrl 解析或 34567')
|
|
217
|
+
.option('-h, --host <host>', '绑定 host,默认 127.0.0.1', '127.0.0.1')
|
|
218
|
+
.option('-f, --foreground', '前台运行(默认后台 daemon,写 server.pid + server.log)')
|
|
219
|
+
.action(async (opts) => {
|
|
220
|
+
const r = await runStart({ port: opts.port ? Number(opts.port) : undefined, host: opts.host, foreground: opts.foreground });
|
|
221
|
+
console.log(r.msg);
|
|
222
|
+
if (!r.started && !r.pid)
|
|
223
|
+
process.exit(1);
|
|
224
|
+
});
|
|
225
|
+
exports.stopCommand = new commander_1.Command('stop')
|
|
226
|
+
.description('停止本地 server(读 ~/.hunter-mate/server.pid 并 SIGTERM)')
|
|
227
|
+
.action(() => {
|
|
228
|
+
const r = runStop();
|
|
229
|
+
console.log(r.msg);
|
|
230
|
+
if (!r.stopped && r.pid)
|
|
231
|
+
process.exit(1);
|
|
232
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { InstalledVersions, SystemVersions } from '@hunter-mate/shared';
|
|
3
|
+
export interface StatusResult {
|
|
4
|
+
version: string;
|
|
5
|
+
loggedIn: boolean;
|
|
6
|
+
server: 'running' | 'not running' | 'unknown';
|
|
7
|
+
serverUrl: string;
|
|
8
|
+
extensionCount: number;
|
|
9
|
+
extensions: Array<{
|
|
10
|
+
extensionId: string;
|
|
11
|
+
userId?: string;
|
|
12
|
+
version: string;
|
|
13
|
+
capabilities: string[];
|
|
14
|
+
connectedAt: number;
|
|
15
|
+
}>;
|
|
16
|
+
installed?: InstalledVersions;
|
|
17
|
+
live?: SystemVersions | null;
|
|
18
|
+
}
|
|
19
|
+
export declare function runStatus(): Promise<StatusResult>;
|
|
20
|
+
export declare const statusCommand: Command;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.statusCommand = void 0;
|
|
7
|
+
exports.runStatus = runStatus;
|
|
8
|
+
const commander_1 = require("commander");
|
|
9
|
+
const axios_1 = __importDefault(require("axios"));
|
|
10
|
+
const i18n_1 = require("../i18n");
|
|
11
|
+
const config_1 = require("../config");
|
|
12
|
+
const keychain_1 = require("../keychain");
|
|
13
|
+
const version_1 = require("../version");
|
|
14
|
+
async function runStatus() {
|
|
15
|
+
const config = (0, config_1.loadConfig)();
|
|
16
|
+
const token = await (0, keychain_1.getToken)();
|
|
17
|
+
let serverStatus = 'unknown';
|
|
18
|
+
let extensions = [];
|
|
19
|
+
let live = null;
|
|
20
|
+
try {
|
|
21
|
+
await axios_1.default.get(`${config.serverUrl}/health`, { timeout: 3000 });
|
|
22
|
+
serverStatus = 'running';
|
|
23
|
+
try {
|
|
24
|
+
const r = await axios_1.default.get(`${config.serverUrl}/extensions`, { timeout: 3000 });
|
|
25
|
+
if (Array.isArray(r.data))
|
|
26
|
+
extensions = r.data;
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
/* extensions 端点暂未暴露时保持 [] */
|
|
30
|
+
}
|
|
31
|
+
live = await (0, version_1.fetchLiveVersions)();
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
serverStatus = 'not running';
|
|
35
|
+
}
|
|
36
|
+
return {
|
|
37
|
+
version: '0.1.0',
|
|
38
|
+
loggedIn: !!token,
|
|
39
|
+
server: serverStatus,
|
|
40
|
+
serverUrl: config.serverUrl,
|
|
41
|
+
extensionCount: extensions.length,
|
|
42
|
+
extensions,
|
|
43
|
+
installed: (0, version_1.readInstalledVersions)(),
|
|
44
|
+
live,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
exports.statusCommand = new commander_1.Command('status')
|
|
48
|
+
.description(i18n_1.i18n.t('status.description'))
|
|
49
|
+
.option('-f, --format <fmt>', '输出格式: text|json', 'text')
|
|
50
|
+
.action(async (opts) => {
|
|
51
|
+
const status = await runStatus();
|
|
52
|
+
if (opts.format === 'json') {
|
|
53
|
+
console.log(JSON.stringify(status, null, 2));
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
console.log(`${i18n_1.i18n.t('status.cli')}: ${status.version}`);
|
|
57
|
+
console.log(`${i18n_1.i18n.t('status.loggedIn')}: ${status.loggedIn ? 'yes' : 'no'}`);
|
|
58
|
+
console.log(`${i18n_1.i18n.t('status.server')}: ${status.server} (${status.serverUrl})`);
|
|
59
|
+
// 版本一致性: 磁盘(期望) vs 运行中
|
|
60
|
+
if (status.installed) {
|
|
61
|
+
const inst = status.installed;
|
|
62
|
+
const liveServer = status.live?.server;
|
|
63
|
+
const serverVersion = liveServer
|
|
64
|
+
? liveServer.version === (inst.server ?? liveServer.version)
|
|
65
|
+
? `${liveServer.version} (一致)`
|
|
66
|
+
: `${liveServer.version} (磁盘 ${inst.server},不一致 → 将自动重启)`
|
|
67
|
+
: inst.server ?? '未知';
|
|
68
|
+
console.log(` server 版本: ${serverVersion}`);
|
|
69
|
+
const pluginNames = Object.keys({ ...inst.plugins, ...(status.live?.plugins ?? {}) });
|
|
70
|
+
if (pluginNames.length) {
|
|
71
|
+
console.log(` plugins:`);
|
|
72
|
+
for (const name of pluginNames) {
|
|
73
|
+
const disk = inst.plugins[name] ?? '(未安装)';
|
|
74
|
+
const run = status.live?.plugins?.[name] ?? '(未加载)';
|
|
75
|
+
// server 在跑才谈得上 reload;未运行时只展示磁盘版本
|
|
76
|
+
const mark = status.server === 'running' && disk !== run ? ' ← 不一致,将 reload' : '';
|
|
77
|
+
console.log(` · ${name}: disk=${disk} running=${run}${mark}`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
if (status.extensionCount === 0) {
|
|
82
|
+
console.log(`${i18n_1.i18n.t('status.extension')}: not connected — 请在 Chrome 中启动并加载扩展 (\`hunter-mate install && hunter-mate open-ext\`)`);
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
console.log(`${i18n_1.i18n.t('status.extension')}: connected (${status.extensionCount} instance${status.extensionCount === 1 ? '' : 's'})`);
|
|
86
|
+
for (const ext of status.extensions) {
|
|
87
|
+
const disk = status.installed?.extension;
|
|
88
|
+
const mark = disk && ext.version !== disk ? ` (磁盘 ${disk},不一致 → 将广播 reload)` : '';
|
|
89
|
+
console.log(` · extId=${ext.extensionId} version=${ext.version}${mark} userId=${ext.userId ?? 'anonymous'} caps=[${ext.capabilities.join(',') || '-'}]`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
});
|
|
@@ -0,0 +1,161 @@
|
|
|
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.updateCommand = void 0;
|
|
40
|
+
exports.runUpdate = runUpdate;
|
|
41
|
+
const commander_1 = require("commander");
|
|
42
|
+
const axios_1 = __importDefault(require("axios"));
|
|
43
|
+
const fs = __importStar(require("fs"));
|
|
44
|
+
const path = __importStar(require("path"));
|
|
45
|
+
const config_1 = require("../config");
|
|
46
|
+
const keychain_1 = require("../keychain");
|
|
47
|
+
const i18n_1 = require("../i18n");
|
|
48
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
49
|
+
const version_1 = require("../version");
|
|
50
|
+
const updater_1 = require("../updater");
|
|
51
|
+
const HOME = process.env.HUNTER_MATE_HOME || process.env.HOME || process.env.USERPROFILE || '.';
|
|
52
|
+
const RULES_DIR = path.join(HOME, '.hunter-mate', 'rules');
|
|
53
|
+
const VERSION_FILE = path.join(RULES_DIR, 'version.json');
|
|
54
|
+
function loadLocalVersion() {
|
|
55
|
+
if (!fs.existsSync(VERSION_FILE))
|
|
56
|
+
return null;
|
|
57
|
+
try {
|
|
58
|
+
return JSON.parse(fs.readFileSync(VERSION_FILE, 'utf-8'));
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
function saveLocalVersion(version) {
|
|
65
|
+
fs.mkdirSync(RULES_DIR, { recursive: true });
|
|
66
|
+
fs.writeFileSync(VERSION_FILE, JSON.stringify({ version, updatedAt: new Date().toISOString() }, null, 2));
|
|
67
|
+
}
|
|
68
|
+
async function runUpdate() {
|
|
69
|
+
const config = (0, config_1.loadConfig)();
|
|
70
|
+
const token = await (0, keychain_1.getToken)();
|
|
71
|
+
const headers = token ? { Authorization: `UTOKEN ${token}` } : {};
|
|
72
|
+
const result = { ruleUpdated: false };
|
|
73
|
+
try {
|
|
74
|
+
const res = await axios_1.default.get('https://api.github.com/repos/hiredchina-com/hunter-mate/releases/latest', { timeout: 5000 });
|
|
75
|
+
result.cliVersion = res.data.tag_name;
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
// 网络异常不影响规则检查
|
|
79
|
+
}
|
|
80
|
+
try {
|
|
81
|
+
const res = await axios_1.default.get(`${config.serverUrl}/rules/latest`, {
|
|
82
|
+
headers,
|
|
83
|
+
proxy: false,
|
|
84
|
+
});
|
|
85
|
+
const remote = res.data;
|
|
86
|
+
const local = loadLocalVersion();
|
|
87
|
+
if (!local || local.version !== remote.version) {
|
|
88
|
+
saveLocalVersion(remote.version);
|
|
89
|
+
result.ruleUpdated = true;
|
|
90
|
+
}
|
|
91
|
+
result.ruleVersion = remote.version;
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
// 规则检查失败时静默
|
|
95
|
+
}
|
|
96
|
+
return result;
|
|
97
|
+
}
|
|
98
|
+
exports.updateCommand = new commander_1.Command('update')
|
|
99
|
+
.description(i18n_1.i18n.t('update.description'))
|
|
100
|
+
.option('--apply', '应用更新: CLI 自更新(npm) + server/插件/扩展 一致性修复(reload/重启)')
|
|
101
|
+
.action(async (opts) => {
|
|
102
|
+
console.log(i18n_1.i18n.t('update.checking'));
|
|
103
|
+
const result = await runUpdate();
|
|
104
|
+
if (result.cliVersion) {
|
|
105
|
+
console.log(chalk_1.default.gray(`CLI/Server latest version: ${result.cliVersion}`));
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
console.error(chalk_1.default.yellow('Failed to check CLI/Server updates'));
|
|
109
|
+
}
|
|
110
|
+
if (result.ruleUpdated) {
|
|
111
|
+
console.log(chalk_1.default.green(`New rule package available: ${result.ruleVersion}`));
|
|
112
|
+
}
|
|
113
|
+
else if (result.ruleVersion) {
|
|
114
|
+
console.log(chalk_1.default.gray(`Rule package is up to date: ${result.ruleVersion}`));
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
console.error(chalk_1.default.yellow('Failed to check rule updates'));
|
|
118
|
+
}
|
|
119
|
+
if (opts.apply) {
|
|
120
|
+
// ---- CLI 自更新(npm registry) ----
|
|
121
|
+
const config = (0, config_1.loadConfig)();
|
|
122
|
+
const updateCfg = config.update ?? {};
|
|
123
|
+
const channel = updateCfg.channel ?? 'npm';
|
|
124
|
+
const npmPackage = updateCfg.npmPackage ?? 'hunter-mate';
|
|
125
|
+
const installed = (0, version_1.readInstalledVersions)();
|
|
126
|
+
if (channel === 'off') {
|
|
127
|
+
console.log(chalk_1.default.gray(`update.channel=off,跳过 CLI 自更新`));
|
|
128
|
+
}
|
|
129
|
+
else if ((0, updater_1.isDevLinked)()) {
|
|
130
|
+
// dev 软链模式(wrapper 指向 hc-hw worktree dist):不 npm 覆盖开发链,保留一致性修复
|
|
131
|
+
console.log(chalk_1.default.gray(`dev 软链模式,跳过 CLI 自更新(改代码请 pnpm build);继续执行一致性修复`));
|
|
132
|
+
}
|
|
133
|
+
else if (channel === 'npm') {
|
|
134
|
+
const latest = (0, updater_1.npmLatestVersion)(npmPackage);
|
|
135
|
+
if (!latest) {
|
|
136
|
+
console.log(chalk_1.default.yellow(`npm registry 查询 ${npmPackage} 失败(未发布或网络问题),跳过自更新`));
|
|
137
|
+
}
|
|
138
|
+
else if (latest === installed.cli) {
|
|
139
|
+
console.log(chalk_1.default.gray(`CLI 已是最新: ${installed.cli}`));
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
console.log(chalk_1.default.cyan(`CLI 自更新: ${installed.cli} → ${latest}`));
|
|
143
|
+
const r = (0, updater_1.npmSelfUpdate)(npmPackage);
|
|
144
|
+
console.log(r.ok ? chalk_1.default.green(r.msg) : chalk_1.default.yellow(r.msg));
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
// ---- server / 插件 / 扩展 一致性修复(插件 reload / server 重启 / 扩展广播 reload) ----
|
|
148
|
+
const report = await (0, version_1.ensureRuntimeConsistency)({ force: true, verbose: true });
|
|
149
|
+
if (!report.checked) {
|
|
150
|
+
console.log(chalk_1.default.gray(`一致性检测跳过: ${report.reason ?? 'server not running'}`));
|
|
151
|
+
}
|
|
152
|
+
else if (report.performed.length) {
|
|
153
|
+
for (const line of report.performed)
|
|
154
|
+
console.log(chalk_1.default.green(` ✓ ${line}`));
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
console.log(chalk_1.default.gray('运行中版本与磁盘一致,无需 reload/重启'));
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
console.log(i18n_1.i18n.t('update.latest'));
|
|
161
|
+
});
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface HunterMateConfig {
|
|
2
|
+
serverUrl: string;
|
|
3
|
+
lang: string;
|
|
4
|
+
watchFolder?: string;
|
|
5
|
+
}
|
|
6
|
+
export declare function ensureConfigDir(): void;
|
|
7
|
+
export declare function loadConfig(): HunterMateConfig;
|
|
8
|
+
export declare function saveConfig(config: Partial<HunterMateConfig>): {
|
|
9
|
+
serverUrl: string;
|
|
10
|
+
lang: string;
|
|
11
|
+
watchFolder?: string;
|
|
12
|
+
};
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
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
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.ensureConfigDir = ensureConfigDir;
|
|
37
|
+
exports.loadConfig = loadConfig;
|
|
38
|
+
exports.saveConfig = saveConfig;
|
|
39
|
+
const fs = __importStar(require("fs"));
|
|
40
|
+
const path = __importStar(require("path"));
|
|
41
|
+
const HOME = process.env.HUNTER_MATE_HOME || process.env.HOME || process.env.USERPROFILE || '.';
|
|
42
|
+
const CONFIG_DIR = path.join(HOME, '.hunter-mate');
|
|
43
|
+
const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
|
|
44
|
+
function ensureConfigDir() {
|
|
45
|
+
if (!fs.existsSync(CONFIG_DIR)) {
|
|
46
|
+
fs.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
function loadConfig() {
|
|
50
|
+
ensureConfigDir();
|
|
51
|
+
if (fs.existsSync(CONFIG_FILE)) {
|
|
52
|
+
return JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf-8'));
|
|
53
|
+
}
|
|
54
|
+
return {
|
|
55
|
+
serverUrl: 'http://localhost:34567',
|
|
56
|
+
lang: 'zh-CN',
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
function saveConfig(config) {
|
|
60
|
+
ensureConfigDir();
|
|
61
|
+
const current = loadConfig();
|
|
62
|
+
const next = { ...current, ...config };
|
|
63
|
+
fs.writeFileSync(CONFIG_FILE, JSON.stringify(next, null, 2));
|
|
64
|
+
return next;
|
|
65
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.i18n = void 0;
|
|
7
|
+
const zh_CN_json_1 = __importDefault(require("./locales/zh-CN.json"));
|
|
8
|
+
const en_US_json_1 = __importDefault(require("./locales/en-US.json"));
|
|
9
|
+
const locales = {
|
|
10
|
+
'zh-CN': zh_CN_json_1.default,
|
|
11
|
+
'en-US': en_US_json_1.default,
|
|
12
|
+
};
|
|
13
|
+
class I18n {
|
|
14
|
+
locale = 'zh-CN';
|
|
15
|
+
setLocale(locale) {
|
|
16
|
+
if (locales[locale]) {
|
|
17
|
+
this.locale = locale;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
t(key) {
|
|
21
|
+
return locales[this.locale]?.[key] || key;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
exports.i18n = new I18n();
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"cli.description": "HiredChina headhunter Agent data assistant",
|
|
3
|
+
"cli.langOption": "Output language (zh-CN/en-US)",
|
|
4
|
+
"login.description": "Login with OAuth device code",
|
|
5
|
+
"login.start": "Opening browser to complete login...",
|
|
6
|
+
"login.userCode": "User code:",
|
|
7
|
+
"login.verifyUrl": "Verification URL:",
|
|
8
|
+
"login.success": "Login successful",
|
|
9
|
+
"login.failed": "Login failed:",
|
|
10
|
+
"login.expired": "Device code expired, please try again",
|
|
11
|
+
"logout.description": "Logout and delete local token",
|
|
12
|
+
"logout.success": "Logged out",
|
|
13
|
+
"status.description": "Check service, login, and extension status",
|
|
14
|
+
"status.cli": "CLI version",
|
|
15
|
+
"status.loggedIn": "Logged in",
|
|
16
|
+
"status.loggedOut": "Not logged in",
|
|
17
|
+
"status.server": "Server status",
|
|
18
|
+
"status.extension": "Extension status",
|
|
19
|
+
"config.description": "Configure server address, watch folder, language",
|
|
20
|
+
"config.saved": "Configuration saved",
|
|
21
|
+
"search.description": "Talent search",
|
|
22
|
+
"search.filter": "Structured filter conditions",
|
|
23
|
+
"search.source": "Data sources, comma separated",
|
|
24
|
+
"search.top": "Return count",
|
|
25
|
+
"job.description": "Job case management",
|
|
26
|
+
"job.create": "Create JobCase from folder",
|
|
27
|
+
"update.description": "Check and update CLI/Server/Extension/Rule",
|
|
28
|
+
"update.checking": "Checking for updates...",
|
|
29
|
+
"update.latest": "Already up to date",
|
|
30
|
+
"extension.description": "Browser extension management",
|
|
31
|
+
"extension.checkDescription": "Check WebSocket connection between extension and local server",
|
|
32
|
+
"extension.connected": "WebSocket connection is healthy",
|
|
33
|
+
"extension.timeout": "Connection timed out, please make sure the server is running",
|
|
34
|
+
"extension.error": "Connection failed: {{message}}"
|
|
35
|
+
}
|