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,438 @@
|
|
|
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.extensionCommand = void 0;
|
|
40
|
+
exports.ensureEnvReady = ensureEnvReady;
|
|
41
|
+
exports.runExtensionCheck = runExtensionCheck;
|
|
42
|
+
const commander_1 = require("commander");
|
|
43
|
+
const axios_1 = __importDefault(require("axios"));
|
|
44
|
+
const ws_1 = __importDefault(require("ws"));
|
|
45
|
+
const config_1 = require("../config");
|
|
46
|
+
const i18n_1 = require("../i18n");
|
|
47
|
+
const install_1 = require("./install");
|
|
48
|
+
const B = {
|
|
49
|
+
reset: '\x1b[0m', bold: '\x1b[1m',
|
|
50
|
+
green: '\x1b[32m', yellow: '\x1b[33m', red: '\x1b[31m', cyan: '\x1b[36m', magenta: '\x1b[35m',
|
|
51
|
+
};
|
|
52
|
+
// ----------------------------------------------------------------------------
|
|
53
|
+
// 共用: 构建 HTTP client(baseURL = hunter-mate config serverUrl)
|
|
54
|
+
// ----------------------------------------------------------------------------
|
|
55
|
+
function http(baseUrl) {
|
|
56
|
+
return axios_1.default.create({
|
|
57
|
+
baseURL: baseUrl,
|
|
58
|
+
timeout: 60_000,
|
|
59
|
+
headers: { 'Content-Type': 'application/json' },
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
function requireServerUrl() {
|
|
63
|
+
const config = (0, config_1.loadConfig)();
|
|
64
|
+
if (!config.serverUrl) {
|
|
65
|
+
console.error(`${B.red}✗ hunter-mate: serverUrl 未配置${B.reset}\n 请先 ${B.bold}hunter-mate config${B.reset} 设置 serverUrl`);
|
|
66
|
+
process.exit(1);
|
|
67
|
+
}
|
|
68
|
+
return config.serverUrl;
|
|
69
|
+
}
|
|
70
|
+
function printResult(data, format) {
|
|
71
|
+
const fmt = (format ?? 'json').toLowerCase();
|
|
72
|
+
if (fmt === 'json') {
|
|
73
|
+
console.log(typeof data === 'string' ? data : JSON.stringify(data, null, 2));
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
if (fmt === 'yaml' || fmt === 'yml') {
|
|
77
|
+
console.log(JSON2YAML(data));
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
if (fmt === 'table') {
|
|
81
|
+
if (Array.isArray(data))
|
|
82
|
+
console.table(data);
|
|
83
|
+
else if (data && typeof data === 'object')
|
|
84
|
+
console.table([data]);
|
|
85
|
+
else
|
|
86
|
+
console.log(String(data));
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
console.log(typeof data === 'string' ? data : JSON.stringify(data, null, 2));
|
|
90
|
+
}
|
|
91
|
+
function JSON2YAML(input, indent = 0) {
|
|
92
|
+
const pad = ' '.repeat(indent);
|
|
93
|
+
if (input === null)
|
|
94
|
+
return 'null';
|
|
95
|
+
if (typeof input !== 'object')
|
|
96
|
+
return JSON.stringify(input);
|
|
97
|
+
if (Array.isArray(input)) {
|
|
98
|
+
if (!input.length)
|
|
99
|
+
return '[]';
|
|
100
|
+
return input.map((v) => `${pad}- ${JSON2YAML(v, indent + 1).replace(/^ {2}/, '')}`).join('\n');
|
|
101
|
+
}
|
|
102
|
+
const entries = Object.entries(input);
|
|
103
|
+
if (!entries.length)
|
|
104
|
+
return '{}';
|
|
105
|
+
return entries
|
|
106
|
+
.map(([k, v]) => {
|
|
107
|
+
const rendered = v !== null && typeof v === 'object'
|
|
108
|
+
? `\n${JSON2YAML(v, indent + 1)}`
|
|
109
|
+
: ` ${JSON2YAML(v, 0)}`;
|
|
110
|
+
return `${pad}${k}:${rendered}`;
|
|
111
|
+
})
|
|
112
|
+
.join('\n');
|
|
113
|
+
}
|
|
114
|
+
// ----------------------------------------------------------------------------
|
|
115
|
+
// 前置环境准备: 所有 extension 命令前自动调用
|
|
116
|
+
// 对应 ChromePilot: 任何 control/run 操作前自动确保 server 运行 + 扩展就绪
|
|
117
|
+
// ----------------------------------------------------------------------------
|
|
118
|
+
let envReadyPromise = null;
|
|
119
|
+
/** 确保环境就绪(自动启动 server、自动安装扩展、打印引导) */
|
|
120
|
+
function ensureEnvReady() {
|
|
121
|
+
if (envReadyPromise)
|
|
122
|
+
return envReadyPromise;
|
|
123
|
+
envReadyPromise = (async () => {
|
|
124
|
+
const config = (0, config_1.loadConfig)();
|
|
125
|
+
const serverUrl = config.serverUrl;
|
|
126
|
+
// 1) 检查 server 是否运行
|
|
127
|
+
let serverUp = false;
|
|
128
|
+
try {
|
|
129
|
+
await axios_1.default.get(`${serverUrl}/health`, { timeout: 2000 });
|
|
130
|
+
serverUp = true;
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
// server 没启动, 自动拉起
|
|
134
|
+
if (!process.env.HUNTER_MATE_SILENT_START) {
|
|
135
|
+
console.log(`${B.yellow}⚠ HunterMate Server 未运行, 正在自动启动...${B.reset}`);
|
|
136
|
+
}
|
|
137
|
+
try {
|
|
138
|
+
const { runStart } = await Promise.resolve().then(() => __importStar(require('./server')));
|
|
139
|
+
runStart({ foreground: false });
|
|
140
|
+
// 等待 server 就绪(最多 8s)
|
|
141
|
+
for (let i = 0; i < 16; i++) {
|
|
142
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
143
|
+
try {
|
|
144
|
+
await axios_1.default.get(`${serverUrl}/health`, { timeout: 1500 });
|
|
145
|
+
serverUp = true;
|
|
146
|
+
break;
|
|
147
|
+
}
|
|
148
|
+
catch { /* keep trying */ }
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
catch { /* ignore */ }
|
|
152
|
+
}
|
|
153
|
+
if (!serverUp) {
|
|
154
|
+
console.error(`${B.red}✗ HunterMate Server 启动失败${B.reset}\n 请手动执行: ${B.bold}hunter-mate start -f${B.reset}`);
|
|
155
|
+
process.exit(1);
|
|
156
|
+
}
|
|
157
|
+
// 2) 检查扩展是否安装
|
|
158
|
+
const fs = require('fs');
|
|
159
|
+
const path = require('path');
|
|
160
|
+
const { runInit } = await Promise.resolve().then(() => __importStar(require('./init')));
|
|
161
|
+
const init = runInit();
|
|
162
|
+
const manifestPath = path.join(init.extensionDir, 'manifest.json');
|
|
163
|
+
if (!fs.existsSync(manifestPath)) {
|
|
164
|
+
// 扩展未安装, 自动执行 install
|
|
165
|
+
if (!process.env.HUNTER_MATE_SILENT_START) {
|
|
166
|
+
console.log(`${B.yellow}⚠ Hunter Mate 扩展未安装, 正在自动安装...${B.reset}`);
|
|
167
|
+
}
|
|
168
|
+
const r = (0, install_1.runInstall)(init.extensionDir);
|
|
169
|
+
if (!r.source) {
|
|
170
|
+
// 内置 dist 不存在, 无法自动安装
|
|
171
|
+
console.error(`${B.red}✗ 无法自动安装扩展(内置 extension dist 未构建)${B.reset}\n` +
|
|
172
|
+
` 请在 hunter-mate monorepo 执行:\n` +
|
|
173
|
+
` ${B.bold}pnpm --filter @hunter-mate/extension build${B.reset}\n` +
|
|
174
|
+
` 或手动安装: ${B.bold}hunter-mate install${B.reset}`);
|
|
175
|
+
process.exit(1);
|
|
176
|
+
}
|
|
177
|
+
console.log(`${B.green}✓ 扩展已自动安装到 ${r.target}${B.reset}`);
|
|
178
|
+
// 打开目录引导
|
|
179
|
+
try {
|
|
180
|
+
const { default: open } = await Promise.resolve().then(() => __importStar(require('open')));
|
|
181
|
+
open(init.extensionDir).catch(() => { });
|
|
182
|
+
}
|
|
183
|
+
catch { }
|
|
184
|
+
(0, install_1.showInstallGuide)(init.extensionDir);
|
|
185
|
+
// 打开 chrome://extensions
|
|
186
|
+
try {
|
|
187
|
+
const { default: open } = await Promise.resolve().then(() => __importStar(require('open')));
|
|
188
|
+
open('chrome://extensions').catch(() => { });
|
|
189
|
+
}
|
|
190
|
+
catch { }
|
|
191
|
+
console.log(`${B.yellow}⚠ 扩展已安装, 但需要你在 Chrome 中手动加载(见上方引导)。加载完成后重新执行命令即可。${B.reset}`);
|
|
192
|
+
process.exit(1);
|
|
193
|
+
}
|
|
194
|
+
// 3) 检查扩展是否连接
|
|
195
|
+
try {
|
|
196
|
+
const r = await http(serverUrl).get('/extensions', { timeout: 3000 });
|
|
197
|
+
const count = Array.isArray(r.data) ? r.data.length : 0;
|
|
198
|
+
if (count === 0) {
|
|
199
|
+
// 扩展已安装但未连接
|
|
200
|
+
console.log(`${B.yellow}⚠ Hunter Mate 扩展已安装但尚未连接到 Chrome${B.reset}\n`);
|
|
201
|
+
(0, install_1.showInstallGuide)(init.extensionDir);
|
|
202
|
+
console.log(`${B.bold}请确认:${B.reset}\n` +
|
|
203
|
+
` ${B.magenta}①${B.reset} Chrome 工具栏的 Hunter Mate 图标是否已显示?\n` +
|
|
204
|
+
` ${B.magenta}②${B.reset} 点击图标, 侧栏是否显示「已连接」?\n` +
|
|
205
|
+
` ${B.magenta}③${B.reset} 如果仍无连接, 点击扩展详情的「重新加载」按钮\n\n` +
|
|
206
|
+
`${B.cyan}完成上述步骤后, 重新执行你的命令即可。${B.reset}`);
|
|
207
|
+
process.exit(1);
|
|
208
|
+
}
|
|
209
|
+
// 扩展已连接, 打印实例信息
|
|
210
|
+
if (!process.env.HUNTER_MATE_SILENT_START) {
|
|
211
|
+
console.log(`${B.green}✓ Hunter Mate 环境就绪: server 运行中, ${count} 个扩展实例已连接${B.reset}`);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
catch {
|
|
215
|
+
// /extensions 端点不可达
|
|
216
|
+
console.error(`${B.red}✗ 无法检查扩展连接状态${B.reset}\n Server 可能未正确启动, 请执行: ${B.bold}hunter-mate start -f${B.reset}`);
|
|
217
|
+
process.exit(1);
|
|
218
|
+
}
|
|
219
|
+
})();
|
|
220
|
+
// 完成后清除缓存, 下次调用重新检查
|
|
221
|
+
envReadyPromise.finally(() => { envReadyPromise = null; });
|
|
222
|
+
return envReadyPromise;
|
|
223
|
+
}
|
|
224
|
+
// ----------------------------------------------------------------------------
|
|
225
|
+
// 原 check 命令(增强: 自动修复 + 友好引导)
|
|
226
|
+
// ----------------------------------------------------------------------------
|
|
227
|
+
async function runExtensionCheck(timeoutMs = 5000) {
|
|
228
|
+
const config = (0, config_1.loadConfig)();
|
|
229
|
+
const wsUrl = config.serverUrl.replace(/^http/, 'ws') + '/ws';
|
|
230
|
+
const ws = new ws_1.default(wsUrl);
|
|
231
|
+
return new Promise((resolve, reject) => {
|
|
232
|
+
const timeout = setTimeout(() => {
|
|
233
|
+
ws.terminate();
|
|
234
|
+
reject(new Error('extension check timeout'));
|
|
235
|
+
}, timeoutMs);
|
|
236
|
+
let registered = false;
|
|
237
|
+
ws.on('open', () => {
|
|
238
|
+
ws.send(JSON.stringify({ type: 'ping' }));
|
|
239
|
+
});
|
|
240
|
+
ws.on('message', (data) => {
|
|
241
|
+
const msg = JSON.parse(data.toString());
|
|
242
|
+
if (msg.type === 'pong') {
|
|
243
|
+
http(config.serverUrl)
|
|
244
|
+
.get('/extensions')
|
|
245
|
+
.then((r) => {
|
|
246
|
+
clearTimeout(timeout);
|
|
247
|
+
const count = Array.isArray(r.data) ? r.data.length : 0;
|
|
248
|
+
if (!count) {
|
|
249
|
+
ws.close();
|
|
250
|
+
reject(new Error('server reachable but no Hunter Mate Chrome extension registered.'));
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
registered = true;
|
|
254
|
+
ws.close();
|
|
255
|
+
globalThis.__extensionCount = count;
|
|
256
|
+
resolve();
|
|
257
|
+
})
|
|
258
|
+
.catch((err) => {
|
|
259
|
+
clearTimeout(timeout);
|
|
260
|
+
ws.close();
|
|
261
|
+
reject(new Error(`server reachable but /extensions HTTP failed: ${err.message}`));
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
});
|
|
265
|
+
ws.on('error', (err) => {
|
|
266
|
+
clearTimeout(timeout);
|
|
267
|
+
reject(err);
|
|
268
|
+
});
|
|
269
|
+
void registered;
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
// ----------------------------------------------------------------------------
|
|
273
|
+
// 新命令: list / control / goto / eval / screenshot / run
|
|
274
|
+
// ----------------------------------------------------------------------------
|
|
275
|
+
async function withEnvCheck(fn) {
|
|
276
|
+
try {
|
|
277
|
+
await ensureEnvReady();
|
|
278
|
+
await fn();
|
|
279
|
+
}
|
|
280
|
+
catch (err) {
|
|
281
|
+
const msg = err.response?.data?.message ?? err.response?.data?.error ?? err.message;
|
|
282
|
+
console.error(`${B.red}✗ hunter-mate extension 执行失败:${B.reset} ${msg}`);
|
|
283
|
+
if (msg.includes('no Hunter Mate Chrome extension registered') || msg.includes('no Hunter Mate')) {
|
|
284
|
+
const init = await Promise.resolve().then(() => __importStar(require('./init'))).then((m) => m.runInit());
|
|
285
|
+
(0, install_1.showInstallGuide)(init.extensionDir);
|
|
286
|
+
}
|
|
287
|
+
process.exit(1);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
async function cmdList(opts) {
|
|
291
|
+
return withEnvCheck(async () => {
|
|
292
|
+
const serverUrl = requireServerUrl();
|
|
293
|
+
const r = await http(serverUrl).get('/extensions');
|
|
294
|
+
const data = Array.isArray(r.data) && r.data.length > 0
|
|
295
|
+
? r.data
|
|
296
|
+
: [{ extensionId: '(none connected)', hint: '请在 Chrome 中加载 Hunter Mate 扩展' }];
|
|
297
|
+
printResult(data, opts.format);
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
async function cmdControl(command, argsJson, opts) {
|
|
301
|
+
return withEnvCheck(async () => {
|
|
302
|
+
const serverUrl = requireServerUrl();
|
|
303
|
+
let params;
|
|
304
|
+
if (argsJson) {
|
|
305
|
+
try {
|
|
306
|
+
params = JSON.parse(argsJson);
|
|
307
|
+
}
|
|
308
|
+
catch {
|
|
309
|
+
console.error('extension control: args 必须是合法 JSON 字符串');
|
|
310
|
+
process.exit(1);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
const r = await http(serverUrl).post('/extensions/control', {
|
|
314
|
+
command,
|
|
315
|
+
params,
|
|
316
|
+
timeoutMs: opts.timeout ? Number(opts.timeout) : undefined,
|
|
317
|
+
extensionId: opts.extensionId,
|
|
318
|
+
});
|
|
319
|
+
printResult(r.data, opts.format);
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
async function cmdGoto(url, opts) {
|
|
323
|
+
return withEnvCheck(async () => {
|
|
324
|
+
const serverUrl = requireServerUrl();
|
|
325
|
+
const r = await http(serverUrl).post('/extensions/goto', {
|
|
326
|
+
url,
|
|
327
|
+
waitUntil: opts.waitUntil,
|
|
328
|
+
timeoutMs: opts.timeout ? Number(opts.timeout) : undefined,
|
|
329
|
+
extensionId: opts.extensionId,
|
|
330
|
+
});
|
|
331
|
+
printResult(r.data, opts.format);
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
async function cmdEval(expression, opts) {
|
|
335
|
+
return withEnvCheck(async () => {
|
|
336
|
+
const serverUrl = requireServerUrl();
|
|
337
|
+
const r = await http(serverUrl).post('/extensions/eval', {
|
|
338
|
+
expression,
|
|
339
|
+
timeoutMs: opts.timeout ? Number(opts.timeout) : undefined,
|
|
340
|
+
extensionId: opts.extensionId,
|
|
341
|
+
});
|
|
342
|
+
printResult(r.data, opts.format);
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
async function cmdScreenshot(opts) {
|
|
346
|
+
return withEnvCheck(async () => {
|
|
347
|
+
const serverUrl = requireServerUrl();
|
|
348
|
+
const r = await http(serverUrl).post('/extensions/screenshot', {
|
|
349
|
+
fullPage: opts.full ? true : undefined,
|
|
350
|
+
selector: opts.selector,
|
|
351
|
+
format: opts.imageFormat ?? 'png',
|
|
352
|
+
timeoutMs: opts.timeout ? Number(opts.timeout) : undefined,
|
|
353
|
+
extensionId: opts.extensionId,
|
|
354
|
+
});
|
|
355
|
+
if (opts.out) {
|
|
356
|
+
const fs = await Promise.resolve().then(() => __importStar(require('fs')));
|
|
357
|
+
const body = r.data.result;
|
|
358
|
+
const base64 = (body ?? '').replace(/^data:image\/(png|jpeg);base64,/, '');
|
|
359
|
+
fs.writeFileSync(opts.out, Buffer.from(base64, 'base64'));
|
|
360
|
+
console.log(`saved screenshot: ${opts.out}`);
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
printResult(r.data, opts.format);
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
async function cmdRun(site, command, opts) {
|
|
367
|
+
return withEnvCheck(async () => {
|
|
368
|
+
const serverUrl = requireServerUrl();
|
|
369
|
+
const endpoint = `/extensions/run/${site}/${command}`;
|
|
370
|
+
let params = {};
|
|
371
|
+
if (opts.args) {
|
|
372
|
+
try {
|
|
373
|
+
params = JSON.parse(opts.args);
|
|
374
|
+
}
|
|
375
|
+
catch {
|
|
376
|
+
console.error('extension run --args 必须是合法 JSON 字符串');
|
|
377
|
+
process.exit(1);
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
const payload = {
|
|
381
|
+
...params,
|
|
382
|
+
timeoutMs: opts.timeout ? Number(opts.timeout) : undefined,
|
|
383
|
+
extensionId: opts.extensionId,
|
|
384
|
+
};
|
|
385
|
+
const r = await http(serverUrl).post(endpoint, payload);
|
|
386
|
+
printResult(r.data, opts.format);
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
// ----------------------------------------------------------------------------
|
|
390
|
+
// commander 注册
|
|
391
|
+
// ----------------------------------------------------------------------------
|
|
392
|
+
exports.extensionCommand = new commander_1.Command('extension')
|
|
393
|
+
.description(i18n_1.i18n.t('extension.description') + ' — 自动确保 server 运行 + 扩展就绪, 无感启动');
|
|
394
|
+
const common = (cmd) => cmd
|
|
395
|
+
.option('-f, --format <format>', '输出格式 json|yaml|table|md|csv', 'json')
|
|
396
|
+
.option('-e, --extension-id <id>', '指定扩展实例(多浏览器时必填)')
|
|
397
|
+
.option('-t, --timeout <ms>', '命令超时(ms),默认 15000');
|
|
398
|
+
exports.extensionCommand
|
|
399
|
+
.command('check')
|
|
400
|
+
.description('检查扩展连接状态(自动启动 server + 自动安装扩展 + 中文引导)')
|
|
401
|
+
.action(async () => {
|
|
402
|
+
try {
|
|
403
|
+
await ensureEnvReady();
|
|
404
|
+
const count = globalThis.__extensionCount ?? 0;
|
|
405
|
+
console.log(`\n${B.green}✓ Hunter Mate 扩展已连接 (${count} 个浏览器实例)${B.reset}\n`);
|
|
406
|
+
}
|
|
407
|
+
catch (err) {
|
|
408
|
+
console.error(`${B.red}✗ 扩展检查失败: ${err.message}${B.reset}`);
|
|
409
|
+
process.exit(1);
|
|
410
|
+
}
|
|
411
|
+
});
|
|
412
|
+
common(exports.extensionCommand
|
|
413
|
+
.command('list')
|
|
414
|
+
.description('列出已连接的 Hunter Mate 浏览器扩展实例')).action(async (opts) => cmdList(opts));
|
|
415
|
+
common(exports.extensionCommand
|
|
416
|
+
.command('control <command> [argsJson]')
|
|
417
|
+
.description('通用页面控制(OpenCLI 风格, 对应 chromepilot control):cp.state/cp.find/cp.click/cp.fill/cp.select/cp.scroll/cp.eval/cp.screenshot/cp.goto/cp.waitFor')).action(async (command, argsJson, opts) => cmdControl(command, argsJson, opts));
|
|
418
|
+
common(exports.extensionCommand
|
|
419
|
+
.command('goto <url>')
|
|
420
|
+
.description('导航到指定 URL (对应 chromepilot control cp.goto)'))
|
|
421
|
+
.option('--wait-until <mode>', 'domcontentloaded|load|networkidle0|networkidle2')
|
|
422
|
+
.action(async (url, opts) => cmdGoto(url, opts));
|
|
423
|
+
common(exports.extensionCommand
|
|
424
|
+
.command('eval <expression>')
|
|
425
|
+
.description('在当前页面执行 JS 表达式 (对应 chromepilot control cp.eval)')).action(async (expression, opts) => cmdEval(expression, opts));
|
|
426
|
+
common(exports.extensionCommand
|
|
427
|
+
.command('screenshot')
|
|
428
|
+
.description('当前页面截图 (对应 chromepilot control cp.screenshot)'))
|
|
429
|
+
.option('--full', '整页截图')
|
|
430
|
+
.option('--selector <sel>', '仅截取某个元素')
|
|
431
|
+
.option('--image-format <fmt>', '图片格式 png|jpeg', 'png')
|
|
432
|
+
.option('--out <file>', '保存到文件(否则输出 base64 JSON)')
|
|
433
|
+
.action(async (opts) => cmdScreenshot(opts));
|
|
434
|
+
common(exports.extensionCommand
|
|
435
|
+
.command('run <site> <command>')
|
|
436
|
+
.description('站点适配器命令 (对应 chromepilot run): linkedin search|extract|invite 或 resume extract'))
|
|
437
|
+
.option('--args <json>', '命令参数 JSON 字符串')
|
|
438
|
+
.action(async (site, command, opts) => cmdRun(site, command, opts));
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
/** 对应 ChromePilot reload: 通过 WS 向所有已连接扩展广播 {type: 'reload'} */
|
|
3
|
+
export declare function runReload(timeoutMs?: number): Promise<{
|
|
4
|
+
broadcast: number;
|
|
5
|
+
msg: string;
|
|
6
|
+
}>;
|
|
7
|
+
/** 对应 ChromePilot open-ext / open-extension-folder: 打开 ~/.hunter-mate/extension */
|
|
8
|
+
export declare function runOpenExt(target?: string): Promise<string>;
|
|
9
|
+
export declare const reloadCommand: Command;
|
|
10
|
+
export declare const openExtCommand: Command;
|
|
@@ -0,0 +1,111 @@
|
|
|
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.openExtCommand = exports.reloadCommand = void 0;
|
|
40
|
+
exports.runReload = runReload;
|
|
41
|
+
exports.runOpenExt = runOpenExt;
|
|
42
|
+
const commander_1 = require("commander");
|
|
43
|
+
const ws_1 = __importDefault(require("ws"));
|
|
44
|
+
const config_1 = require("../config");
|
|
45
|
+
const fs = __importStar(require("fs"));
|
|
46
|
+
const init_1 = require("./init");
|
|
47
|
+
/** 对应 ChromePilot reload: 通过 WS 向所有已连接扩展广播 {type: 'reload'} */
|
|
48
|
+
function runReload(timeoutMs = 5000) {
|
|
49
|
+
const config = (0, config_1.loadConfig)();
|
|
50
|
+
const wsUrl = config.serverUrl.replace(/^http/, 'ws') + '/ws';
|
|
51
|
+
const ws = new ws_1.default(wsUrl);
|
|
52
|
+
return new Promise((resolve, reject) => {
|
|
53
|
+
const timer = setTimeout(() => {
|
|
54
|
+
ws.terminate();
|
|
55
|
+
reject(new Error('reload timeout — is server running? Try `hunter-mate start`.'));
|
|
56
|
+
}, timeoutMs);
|
|
57
|
+
ws.on('open', () => {
|
|
58
|
+
// 以特殊 client 身份发广播消息
|
|
59
|
+
ws.send(JSON.stringify({ type: 'broadcast', payload: { type: 'reload' } }));
|
|
60
|
+
// 服务器未实现 broadcast 路由时也当作 OK(由扩展端自己实现 ws.onmessage 监听),
|
|
61
|
+
// 给用户一个可预期的输出,说明下一步
|
|
62
|
+
setTimeout(() => {
|
|
63
|
+
clearTimeout(timer);
|
|
64
|
+
ws.close();
|
|
65
|
+
resolve({
|
|
66
|
+
broadcast: 1,
|
|
67
|
+
msg: 'reload 广播已发送。若扩展端已实现 reload 处理器,所有 Chrome 实例中的 Hunter Mate 扩展会自动重载;否则请在 chrome://extensions 点击刷新按钮。',
|
|
68
|
+
});
|
|
69
|
+
}, 300);
|
|
70
|
+
});
|
|
71
|
+
ws.on('error', (e) => {
|
|
72
|
+
clearTimeout(timer);
|
|
73
|
+
reject(e);
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
/** 对应 ChromePilot open-ext / open-extension-folder: 打开 ~/.hunter-mate/extension */
|
|
78
|
+
async function runOpenExt(target) {
|
|
79
|
+
const init = (0, init_1.runInit)();
|
|
80
|
+
const dir = target ?? init.extensionDir;
|
|
81
|
+
if (!fs.existsSync(dir))
|
|
82
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
83
|
+
try {
|
|
84
|
+
const { default: open } = await Promise.resolve().then(() => __importStar(require('open')));
|
|
85
|
+
await open(dir);
|
|
86
|
+
}
|
|
87
|
+
catch (e) {
|
|
88
|
+
console.warn(`open(3) not available: ${e.message}. 请手动打开: ${dir}`);
|
|
89
|
+
}
|
|
90
|
+
return dir;
|
|
91
|
+
}
|
|
92
|
+
exports.reloadCommand = new commander_1.Command('reload')
|
|
93
|
+
.description('通知所有已连接的 Chrome 扩展重新加载(对应 chromepilot reload)')
|
|
94
|
+
.action(async () => {
|
|
95
|
+
try {
|
|
96
|
+
const r = await runReload();
|
|
97
|
+
console.log(r.msg);
|
|
98
|
+
}
|
|
99
|
+
catch (e) {
|
|
100
|
+
console.error(`reload error: ${e.message}`);
|
|
101
|
+
process.exit(1);
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
exports.openExtCommand = new commander_1.Command('open-extension-folder')
|
|
105
|
+
.alias('open-ext')
|
|
106
|
+
.description('打开 ~/.hunter-mate/extension 文件夹,方便拖到 chrome://extensions 加载(对应 chromepilot open-ext)')
|
|
107
|
+
.option('-t, --target <dir>', '指定自定义扩展目录')
|
|
108
|
+
.action(async (opts) => {
|
|
109
|
+
const dir = await runOpenExt(opts.target);
|
|
110
|
+
console.log(`extension folder: ${dir}`);
|
|
111
|
+
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
/** 对应 ChromePilot init: 创建 ~/.hunter-mate 目录、SQLite、初始 token */
|
|
3
|
+
export declare function runInit(force?: boolean): {
|
|
4
|
+
dir: string;
|
|
5
|
+
extensionDir: string;
|
|
6
|
+
dbFile: string;
|
|
7
|
+
sessionsDir: string;
|
|
8
|
+
token: string;
|
|
9
|
+
};
|
|
10
|
+
export declare const initCommand: Command;
|
|
@@ -0,0 +1,87 @@
|
|
|
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.initCommand = void 0;
|
|
37
|
+
exports.runInit = runInit;
|
|
38
|
+
const commander_1 = require("commander");
|
|
39
|
+
const fs = __importStar(require("fs"));
|
|
40
|
+
const path = __importStar(require("path"));
|
|
41
|
+
const config_1 = require("../config");
|
|
42
|
+
/** 对应 ChromePilot init: 创建 ~/.hunter-mate 目录、SQLite、初始 token */
|
|
43
|
+
function runInit(force = false) {
|
|
44
|
+
const HOME = process.env.HUNTER_MATE_HOME || process.env.HOME || process.env.USERPROFILE || '.';
|
|
45
|
+
const ROOT = path.join(HOME, '.hunter-mate');
|
|
46
|
+
const EXT_DIR = path.join(ROOT, 'extension');
|
|
47
|
+
const DB_FILE = path.join(ROOT, 'hunter-mate.db');
|
|
48
|
+
const SESSIONS_DIR = path.join(ROOT, 'sessions');
|
|
49
|
+
const TOKEN_FILE = path.join(ROOT, 'token');
|
|
50
|
+
(0, config_1.ensureConfigDir)();
|
|
51
|
+
for (const d of [ROOT, EXT_DIR, SESSIONS_DIR]) {
|
|
52
|
+
if (!fs.existsSync(d))
|
|
53
|
+
fs.mkdirSync(d, { recursive: true });
|
|
54
|
+
}
|
|
55
|
+
if (!fs.existsSync(DB_FILE)) {
|
|
56
|
+
// 空 SQLite 文件交给 better-sqlite3 首次连接时初始化 schema,这里只 touch
|
|
57
|
+
fs.writeFileSync(DB_FILE, '');
|
|
58
|
+
}
|
|
59
|
+
let token;
|
|
60
|
+
if (!fs.existsSync(TOKEN_FILE) || force) {
|
|
61
|
+
token =
|
|
62
|
+
'hm_' +
|
|
63
|
+
Math.random().toString(36).slice(2, 10) +
|
|
64
|
+
Math.random().toString(36).slice(2, 10) +
|
|
65
|
+
Date.now().toString(36);
|
|
66
|
+
fs.writeFileSync(TOKEN_FILE, token, { mode: 0o600 });
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
token = fs.readFileSync(TOKEN_FILE, 'utf-8').trim();
|
|
70
|
+
}
|
|
71
|
+
// 保证 serverUrl 默认值落到 config(首次)
|
|
72
|
+
(0, config_1.loadConfig)();
|
|
73
|
+
void config_1.saveConfig;
|
|
74
|
+
return { dir: ROOT, extensionDir: EXT_DIR, dbFile: DB_FILE, sessionsDir: SESSIONS_DIR, token };
|
|
75
|
+
}
|
|
76
|
+
exports.initCommand = new commander_1.Command('init')
|
|
77
|
+
.description('创建 ~/.hunter-mate 目录、SQLite 数据库文件、初始 token(对应 chromepilot init)')
|
|
78
|
+
.option('-f, --force', '强制重建 token')
|
|
79
|
+
.action((opts) => {
|
|
80
|
+
const r = runInit(opts.force);
|
|
81
|
+
console.log(`hunter-mate init OK:`);
|
|
82
|
+
console.log(` dir: ${r.dir}`);
|
|
83
|
+
console.log(` extension: ${r.extensionDir} (用 \`hunter-mate install\` 写入扩展 dist)`);
|
|
84
|
+
console.log(` db: ${r.dbFile}`);
|
|
85
|
+
console.log(` sessions: ${r.sessionsDir}`);
|
|
86
|
+
console.log(` token(head): ${r.token.slice(0, 8)}...`);
|
|
87
|
+
});
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
export declare function runInstall(target?: string): {
|
|
3
|
+
source: string | null;
|
|
4
|
+
target: string;
|
|
5
|
+
copied: number;
|
|
6
|
+
};
|
|
7
|
+
/** 友好引导: 告诉用户如何在 Chrome 中加载 Hunter Mate 扩展 */
|
|
8
|
+
export declare function showInstallGuide(extDir: string): void;
|
|
9
|
+
export declare const installCommand: Command;
|